@@ -21,12 +21,14 @@ class WBCollectionListing extends Component { | |||
} | |||
prepareRows(items, ownerLookup) { | |||
let { app } = this.props; | |||
let { app, renderRenameLink } = this.props; | |||
return items.map(item => [ | |||
(<div> | |||
<div> | |||
<a href={ urlForObject(item) }>{ item['name'] }</a> | |||
<a href={ urlForObject(item) }> | |||
{ item['name'] } | |||
</a> { renderRenameLink(item, () => this.fetchItems()) } | |||
</div> | |||
<div>{ item['uuid'] }</div> | |||
</div>), | |||
@@ -22,11 +22,14 @@ class WBProcessListing extends Component { | |||
} | |||
prepareRows(items) { | |||
const { renderRenameLink } = this.props; | |||
return items.map(item => [ | |||
(<div> | |||
<div> | |||
<a href="#" | |||
onclick={ e => { e.preventDefault(); route('/process/' + item['uuid']) }}>{ item['name'] }</a> | |||
onclick={ e => { e.preventDefault(); route('/process/' + item['uuid']) }}> | |||
{ item['name'] } | |||
</a> { renderRenameLink(item, () => this.fetchItems()) } | |||
</div> | |||
<div>{ item['uuid'] }</div> | |||
</div>), | |||
@@ -17,13 +17,15 @@ class WBProjectListing extends Component { | |||
} | |||
prepareRows(items) { | |||
let { app } = this.props; | |||
let { app, renderRenameLink } = this.props; | |||
return items.map(item => [ | |||
(<div> | |||
<div> | |||
<a href="#" | |||
onclick={ e => { e.preventDefault(); route('/browse/' + item['uuid']) }}>{ item['name'] }</a> | |||
onclick={ e => { e.preventDefault(); route('/browse/' + item['uuid']) }}> | |||
{ item['name'] } | |||
</a> { renderRenameLink(item, () => this.fetchItems()) } | |||
</div> | |||
<div>{ item['uuid'] }</div> | |||
</div>), | |||
@@ -78,7 +80,8 @@ class WBProjectListing extends Component { | |||
WBProjectListing.defaultProps = { | |||
'itemsPerPage': 100, | |||
'ownerUuid': null | |||
'ownerUuid': null, | |||
'renderRenameLink': () => null | |||
}; | |||
export default WBProjectListing; |
@@ -6,6 +6,7 @@ import WBNameAndUuid from 'wb-name-and-uuid'; | |||
import wbFetchObjects from 'wb-fetch-objects'; | |||
import wbFormatDate from 'wb-format-date'; | |||
import urlForObject from 'url-for-object'; | |||
import arvadosObjectName from 'arvados-object-name'; | |||
class WBWorkflowListing extends Component { | |||
@@ -20,8 +21,18 @@ class WBWorkflowListing extends Component { | |||
} | |||
prepareRows(items, ownerLookup) { | |||
const { renderRenameLink } = this.props; | |||
return items.map(item => [ | |||
( <WBNameAndUuid uuid={ item.uuid } lookup={ { [item.uuid]: item } } /> ), | |||
( | |||
<div> | |||
<div> | |||
<a href={ urlForObject(item) }> | |||
{ arvadosObjectName(item) } | |||
</a> { renderRenameLink(item, () => this.fetchItems()) } | |||
</div> | |||
<div>{ item.uuid }</div> | |||
</div> | |||
), | |||
item.description, | |||
( <WBNameAndUuid uuid={ item.owner_uuid } lookup={ ownerLookup } /> ), | |||
wbFormatDate(item.created_at), | |||
@@ -0,0 +1,45 @@ | |||
import { h, Component, createRef } from 'preact'; | |||
import WBDialog from 'wb-dialog'; | |||
import linkState from 'linkstate'; | |||
import wbRenameObject from 'wb-rename-object'; | |||
class WBRenameDialog extends Component { | |||
constructor(...args) { | |||
super(...args); | |||
this.dialogRef = createRef(); | |||
this.state.inputId = uuid.v4(); | |||
} | |||
show(item, callback) { | |||
const { inputId } = this.state; | |||
this.setState({ | |||
'item': item, | |||
'newName': null, | |||
'callback': callback || (() => {}) | |||
}); | |||
this.dialogRef.current.show(); | |||
$('#' + inputId).focus(); | |||
} | |||
hide() { | |||
this.dialogRef.current.hide(); | |||
} | |||
render({ app }, { item, newName, callback, inputId }) { | |||
const { arvHost, arvToken } = app.state; | |||
return ( | |||
<WBDialog title="Rename" ref={ this.dialogRef } accept={ () => { | |||
if (newName) | |||
wbRenameObject(arvHost, arvToken, item.uuid, newName).then(callback); | |||
} }> | |||
<div> | |||
<input type="text" class="form-control" id={ inputId } | |||
placeholder={ item ? item.name : 'Type new name here' } | |||
value={ newName } onChange={ linkState(this, 'newName') } /> | |||
</div> | |||
</WBDialog> | |||
); | |||
} | |||
} | |||
export default WBRenameDialog; |
@@ -0,0 +1,16 @@ | |||
import makeArvadosRequest from 'make-arvados-request'; | |||
import arvadosTypeName from 'arvados-type-name'; | |||
function wbRenameObject(arvHost, arvToken, uuid, newName) { | |||
const update = { | |||
'name': newName | |||
}; | |||
const typeName = arvadosTypeName(uuid); | |||
return makeArvadosRequest(arvHost, arvToken, | |||
'/arvados/v1/' + typeName + 's/' + | |||
uuid + '?' + typeName + '=' + | |||
encodeURIComponent(JSON.stringify(update)), | |||
{ 'method': 'PUT' }); | |||
} | |||
export default wbRenameObject; |
@@ -1,4 +1,4 @@ | |||
import { h, Component } from 'preact'; | |||
import { h, Component, createRef } from 'preact'; | |||
import { route } from 'preact-router'; | |||
import WBNavbarCommon from 'wb-navbar-common'; | |||
import WBProjectListing from 'wb-project-listing'; | |||
@@ -8,8 +8,14 @@ import WBTabs from 'wb-tabs'; | |||
import WBProcessListing from 'wb-process-listing'; | |||
import WBCollectionListing from 'wb-collection-listing'; | |||
import WBWorkflowListing from 'wb-workflow-listing'; | |||
import WBRenameDialog from 'wb-rename-dialog'; | |||
class WBBrowse extends Component { | |||
constructor(...args) { | |||
super(...args); | |||
this.renameDialogRef = createRef(); | |||
} | |||
getUrl(params) { | |||
let res = '/browse/' + | |||
('ownerUuid' in params ? params.ownerUuid : (this.props.ownerUuid || '')) + '/' + | |||
@@ -26,11 +32,26 @@ class WBBrowse extends Component { | |||
route(this.getUrl(params)); | |||
} | |||
renameDialog(item, callback) { | |||
// throw Error('Not implemented'); | |||
this.renameDialogRef.current.show(item, callback); | |||
} | |||
renderRenameLink(item, callback) { | |||
return ( | |||
<a href="#" title="Rename" onclick={ e => { e.preventDefault(); this.renameDialog(item, callback); } }> | |||
<i class="fas fa-edit text-secondary"></i> | |||
</a> | |||
); | |||
} | |||
render({ ownerUuid, activePage, appState, app, | |||
objTypeTab, collectionPage, processPage, workflowPage }) { | |||
return ( | |||
<div> | |||
<WBRenameDialog app={ app } ref={ this.renameDialogRef } /> | |||
<WBNavbarCommon app={ app } activeItem={ !ownerUuid ? 'all-projects' : | |||
(ownerUuid === app.state.currentUser.uuid ? 'home' : null) } /> | |||
@@ -44,7 +65,8 @@ class WBBrowse extends Component { | |||
ownerUuid={ ownerUuid } | |||
itemsPerPage="5" | |||
activePage={ Number(activePage || 0) } | |||
onPageChanged={ i => route('/browse/' + (ownerUuid || '') + '/' + i)} /> | |||
onPageChanged={ i => route('/browse/' + (ownerUuid || '') + '/' + i) } | |||
renderRenameLink={ (it, cb) => this.renderRenameLink(it, cb) } /> | |||
<WBTabs tabs={ [ | |||
{ 'id': 'collection', 'name': 'Collections', 'isActive': (!objTypeTab || objTypeTab === 'collection') }, | |||
@@ -58,21 +80,24 @@ class WBBrowse extends Component { | |||
ownerUuid={ ownerUuid } | |||
itemsPerPage="20" | |||
activePage={ Number(collectionPage || 0) } | |||
getPageUrl={ i => this.getUrl({ 'collectionPage': i }) } /> | |||
getPageUrl={ i => this.getUrl({ 'collectionPage': i }) } | |||
renderRenameLink={ (it, cb) => this.renderRenameLink(it, cb) } /> | |||
) : (objTypeTab === 'process' ? ( | |||
<WBProcessListing appState={ appState } | |||
ownerUuid={ ownerUuid } | |||
itemsPerPage="20" | |||
activePage={ Number(processPage || 0) } | |||
onPageChanged={ i => this.route({ 'processPage': i }) } /> | |||
onPageChanged={ i => this.route({ 'processPage': i }) } | |||
renderRenameLink={ (it, cb) => this.renderRenameLink(it, cb) } /> | |||
) : (objTypeTab === 'workflow' ? ( | |||
<WBWorkflowListing app={ app } | |||
ownerUuid={ ownerUuid } | |||
itemsPerPage="20" | |||
page={ Number(workflowPage || 0) } | |||
getPageUrl={ i => this.getUrl({ 'workflowPage': i }) } /> | |||
getPageUrl={ i => this.getUrl({ 'workflowPage': i }) } | |||
renderRenameLink={ (it, cb) => this.renderRenameLink(it, cb) } /> | |||
) : null)) | |||
} | |||
@@ -0,0 +1,56 @@ | |||
import { h, Component } from 'preact'; | |||
class WBDialog extends Component { | |||
constructor(...args) { | |||
super(...args); | |||
this.state.id = uuid.v4(); | |||
} | |||
show() { | |||
const { id } = this.state; | |||
$('#' + id).modal(); | |||
} | |||
hide() { | |||
const { id } = this.state; | |||
$('#' + id).modal('hide'); | |||
} | |||
render({ title, children, accept, reject }, { id }) { | |||
return ( | |||
<form class="m-0"> | |||
<div class="modal" id={ id } tabindex="-1" role="dialog"> | |||
<div class="modal-dialog modal-lg" role="document"> | |||
<div class="modal-content"> | |||
<div class="modal-header"> | |||
<h5 class="modal-title">{ title }</h5> | |||
<button type="button" class="close" data-dismiss="modal" aria-label="Close"> | |||
<span aria-hidden="true">×</span> | |||
</button> | |||
</div> | |||
<div class="modal-body"> | |||
{ children[0] } | |||
</div> | |||
<div class="modal-footer"> | |||
<input type="submit" class="btn btn-primary" value="Accept" | |||
onclick={ e => { e.preventDefault(); this.hide(); accept(); } } /> | |||
<button type="button" class="btn btn-secondary" | |||
onclick={ () => { this.hide(); reject(); } }>Cancel</button> | |||
</div> | |||
</div> | |||
</div> | |||
</div> | |||
</form> | |||
); | |||
} | |||
} | |||
WBDialog.defaultProps = { | |||
'title': 'Dialog', | |||
'accept': () => {}, | |||
'reject': () => {} | |||
}; | |||
export default WBDialog; |