@@ -21,12 +21,14 @@ class WBCollectionListing extends Component { | |||||
} | } | ||||
prepareRows(items, ownerLookup) { | prepareRows(items, ownerLookup) { | ||||
let { app } = this.props; | |||||
let { app, renderRenameLink } = this.props; | |||||
return items.map(item => [ | return items.map(item => [ | ||||
(<div> | (<div> | ||||
<div> | <div> | ||||
<a href={ urlForObject(item) }>{ item['name'] }</a> | |||||
<a href={ urlForObject(item) }> | |||||
{ item['name'] } | |||||
</a> { renderRenameLink(item, () => this.fetchItems()) } | |||||
</div> | </div> | ||||
<div>{ item['uuid'] }</div> | <div>{ item['uuid'] }</div> | ||||
</div>), | </div>), | ||||
@@ -22,11 +22,14 @@ class WBProcessListing extends Component { | |||||
} | } | ||||
prepareRows(items) { | prepareRows(items) { | ||||
const { renderRenameLink } = this.props; | |||||
return items.map(item => [ | return items.map(item => [ | ||||
(<div> | (<div> | ||||
<div> | <div> | ||||
<a href="#" | <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> | ||||
<div>{ item['uuid'] }</div> | <div>{ item['uuid'] }</div> | ||||
</div>), | </div>), | ||||
@@ -17,13 +17,15 @@ class WBProjectListing extends Component { | |||||
} | } | ||||
prepareRows(items) { | prepareRows(items) { | ||||
let { app } = this.props; | |||||
let { app, renderRenameLink } = this.props; | |||||
return items.map(item => [ | return items.map(item => [ | ||||
(<div> | (<div> | ||||
<div> | <div> | ||||
<a href="#" | <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> | ||||
<div>{ item['uuid'] }</div> | <div>{ item['uuid'] }</div> | ||||
</div>), | </div>), | ||||
@@ -78,7 +80,8 @@ class WBProjectListing extends Component { | |||||
WBProjectListing.defaultProps = { | WBProjectListing.defaultProps = { | ||||
'itemsPerPage': 100, | 'itemsPerPage': 100, | ||||
'ownerUuid': null | |||||
'ownerUuid': null, | |||||
'renderRenameLink': () => null | |||||
}; | }; | ||||
export default WBProjectListing; | export default WBProjectListing; |
@@ -6,6 +6,7 @@ import WBNameAndUuid from 'wb-name-and-uuid'; | |||||
import wbFetchObjects from 'wb-fetch-objects'; | import wbFetchObjects from 'wb-fetch-objects'; | ||||
import wbFormatDate from 'wb-format-date'; | import wbFormatDate from 'wb-format-date'; | ||||
import urlForObject from 'url-for-object'; | import urlForObject from 'url-for-object'; | ||||
import arvadosObjectName from 'arvados-object-name'; | |||||
class WBWorkflowListing extends Component { | class WBWorkflowListing extends Component { | ||||
@@ -20,8 +21,18 @@ class WBWorkflowListing extends Component { | |||||
} | } | ||||
prepareRows(items, ownerLookup) { | prepareRows(items, ownerLookup) { | ||||
const { renderRenameLink } = this.props; | |||||
return items.map(item => [ | 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, | item.description, | ||||
( <WBNameAndUuid uuid={ item.owner_uuid } lookup={ ownerLookup } /> ), | ( <WBNameAndUuid uuid={ item.owner_uuid } lookup={ ownerLookup } /> ), | ||||
wbFormatDate(item.created_at), | 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 { route } from 'preact-router'; | ||||
import WBNavbarCommon from 'wb-navbar-common'; | import WBNavbarCommon from 'wb-navbar-common'; | ||||
import WBProjectListing from 'wb-project-listing'; | import WBProjectListing from 'wb-project-listing'; | ||||
@@ -8,8 +8,14 @@ import WBTabs from 'wb-tabs'; | |||||
import WBProcessListing from 'wb-process-listing'; | import WBProcessListing from 'wb-process-listing'; | ||||
import WBCollectionListing from 'wb-collection-listing'; | import WBCollectionListing from 'wb-collection-listing'; | ||||
import WBWorkflowListing from 'wb-workflow-listing'; | import WBWorkflowListing from 'wb-workflow-listing'; | ||||
import WBRenameDialog from 'wb-rename-dialog'; | |||||
class WBBrowse extends Component { | class WBBrowse extends Component { | ||||
constructor(...args) { | |||||
super(...args); | |||||
this.renameDialogRef = createRef(); | |||||
} | |||||
getUrl(params) { | getUrl(params) { | ||||
let res = '/browse/' + | let res = '/browse/' + | ||||
('ownerUuid' in params ? params.ownerUuid : (this.props.ownerUuid || '')) + '/' + | ('ownerUuid' in params ? params.ownerUuid : (this.props.ownerUuid || '')) + '/' + | ||||
@@ -26,11 +32,26 @@ class WBBrowse extends Component { | |||||
route(this.getUrl(params)); | 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, | render({ ownerUuid, activePage, appState, app, | ||||
objTypeTab, collectionPage, processPage, workflowPage }) { | objTypeTab, collectionPage, processPage, workflowPage }) { | ||||
return ( | return ( | ||||
<div> | <div> | ||||
<WBRenameDialog app={ app } ref={ this.renameDialogRef } /> | |||||
<WBNavbarCommon app={ app } activeItem={ !ownerUuid ? 'all-projects' : | <WBNavbarCommon app={ app } activeItem={ !ownerUuid ? 'all-projects' : | ||||
(ownerUuid === app.state.currentUser.uuid ? 'home' : null) } /> | (ownerUuid === app.state.currentUser.uuid ? 'home' : null) } /> | ||||
@@ -44,7 +65,8 @@ class WBBrowse extends Component { | |||||
ownerUuid={ ownerUuid } | ownerUuid={ ownerUuid } | ||||
itemsPerPage="5" | itemsPerPage="5" | ||||
activePage={ Number(activePage || 0) } | 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={ [ | <WBTabs tabs={ [ | ||||
{ 'id': 'collection', 'name': 'Collections', 'isActive': (!objTypeTab || objTypeTab === 'collection') }, | { 'id': 'collection', 'name': 'Collections', 'isActive': (!objTypeTab || objTypeTab === 'collection') }, | ||||
@@ -58,21 +80,24 @@ class WBBrowse extends Component { | |||||
ownerUuid={ ownerUuid } | ownerUuid={ ownerUuid } | ||||
itemsPerPage="20" | itemsPerPage="20" | ||||
activePage={ Number(collectionPage || 0) } | 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' ? ( | ) : (objTypeTab === 'process' ? ( | ||||
<WBProcessListing appState={ appState } | <WBProcessListing appState={ appState } | ||||
ownerUuid={ ownerUuid } | ownerUuid={ ownerUuid } | ||||
itemsPerPage="20" | itemsPerPage="20" | ||||
activePage={ Number(processPage || 0) } | 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' ? ( | ) : (objTypeTab === 'workflow' ? ( | ||||
<WBWorkflowListing app={ app } | <WBWorkflowListing app={ app } | ||||
ownerUuid={ ownerUuid } | ownerUuid={ ownerUuid } | ||||
itemsPerPage="20" | itemsPerPage="20" | ||||
page={ Number(workflowPage || 0) } | page={ Number(workflowPage || 0) } | ||||
getPageUrl={ i => this.getUrl({ 'workflowPage': i }) } /> | |||||
getPageUrl={ i => this.getUrl({ 'workflowPage': i }) } | |||||
renderRenameLink={ (it, cb) => this.renderRenameLink(it, cb) } /> | |||||
) : null)) | ) : 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; |