| @@ -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; | |||||