| @@ -38,7 +38,7 @@ class WBDeleteDialog extends Component { | |||||
| <div> | <div> | ||||
| <input type="submit" class="btn btn-danger mr-2" value="Delete" | <input type="submit" class="btn btn-danger mr-2" value="Delete" | ||||
| onclick={ e => { e.preventDefault(); | |||||
| onclick={ e => { e.preventDefault(); this.hide(); | |||||
| wbDeleteObject(arvHost, arvToken, item.uuid).then(callback); } } /> | wbDeleteObject(arvHost, arvToken, item.uuid).then(callback); } } /> | ||||
| <button class="btn btn-secondary mr-2" onclick={ e => { e.preventDefault(); | <button class="btn btn-secondary mr-2" onclick={ e => { e.preventDefault(); | ||||
| this.hide(); } }> | this.hide(); } }> | ||||
| @@ -0,0 +1,54 @@ | |||||
| import { h, Component, createRef } from 'preact'; | |||||
| import WBDialog from 'wb-dialog'; | |||||
| import linkState from 'linkstate'; | |||||
| import makeArvadosRequest from 'make-arvados-request'; | |||||
| class WBNewProjectDialog extends Component { | |||||
| constructor(...args) { | |||||
| super(...args); | |||||
| this.dialogRef = createRef(); | |||||
| this.state.inputId = uuid.v4(); | |||||
| } | |||||
| show(ownerUuid, callback) { | |||||
| const { inputId } = this.state; | |||||
| this.setState({ | |||||
| 'ownerUuid': ownerUuid, | |||||
| 'newName': null, | |||||
| 'placeholderName': 'New Project (' + (new Date()).toISOString() + ')', | |||||
| 'callback': callback || (() => {}) | |||||
| }); | |||||
| this.dialogRef.current.show(); | |||||
| $('#' + inputId).focus(); | |||||
| } | |||||
| hide() { | |||||
| this.dialogRef.current.hide(); | |||||
| } | |||||
| render({ app }, { ownerUuid, newName, placeholderName, callback, inputId }) { | |||||
| const { arvHost, arvToken } = app.state; | |||||
| return ( | |||||
| <WBDialog title="New Project" ref={ this.dialogRef } accept={ () => { | |||||
| const group = { | |||||
| 'group_class': 'project', | |||||
| 'name': newName || placeholderName, | |||||
| 'owner_uuid': ownerUuid | |||||
| }; | |||||
| makeArvadosRequest(arvHost, arvToken, | |||||
| '/arvados/v1/groups', { 'method': 'POST', | |||||
| 'data': JSON.stringify(group), | |||||
| 'promiseOrdering': false } | |||||
| ).then(callback); | |||||
| } }> | |||||
| <div> | |||||
| <input type="text" class="form-control" id={ inputId } | |||||
| placeholder={ placeholderName } | |||||
| value={ newName } onChange={ linkState(this, 'newName') } /> | |||||
| </div> | |||||
| </WBDialog> | |||||
| ); | |||||
| } | |||||
| } | |||||
| export default WBNewProjectDialog; | |||||
| @@ -10,13 +10,14 @@ function makeArvadosRequest(arvHost, arvToken, endpoint, params={}) { | |||||
| 'responseType': 'json', | 'responseType': 'json', | ||||
| 'useSsl': true, | 'useSsl': true, | ||||
| 'requireToken': true, | 'requireToken': true, | ||||
| 'onProgress': () => {} | |||||
| 'onProgress': () => {}, | |||||
| 'promiseOrdering': true | |||||
| }; | }; | ||||
| Object.keys(defaultParams).map(k => (params[k] = | Object.keys(defaultParams).map(k => (params[k] = | ||||
| (k in params ? params[k] : defaultParams[k]))); | (k in params ? params[k] : defaultParams[k]))); | ||||
| let { method, data, contentType, responseType, | let { method, data, contentType, responseType, | ||||
| useSsl, requireToken, onProgress } = params; | |||||
| useSsl, requireToken, onProgress, promiseOrdering } = params; | |||||
| if (!(arvHost && (arvToken || !requireToken))) | if (!(arvHost && (arvToken || !requireToken))) | ||||
| return new Promise((accept, reject) => reject()); | return new Promise((accept, reject) => reject()); | ||||
| @@ -43,7 +44,8 @@ function makeArvadosRequest(arvHost, arvToken, endpoint, params={}) { | |||||
| xhr.send(data); | xhr.send(data); | ||||
| }); | }); | ||||
| prom = wbApplyPromiseOrdering(prom, requestPromiseOrdering); | |||||
| if (promiseOrdering) | |||||
| prom = wbApplyPromiseOrdering(prom, requestPromiseOrdering); | |||||
| return prom; | return prom; | ||||
| } | } | ||||
| @@ -5,7 +5,7 @@ function wbDeleteObject(arvHost, arvToken, uuid) { | |||||
| const typeName = arvadosTypeName(uuid); | const typeName = arvadosTypeName(uuid); | ||||
| return makeArvadosRequest(arvHost, arvToken, | return makeArvadosRequest(arvHost, arvToken, | ||||
| '/arvados/v1/' + typeName + 's/' + | '/arvados/v1/' + typeName + 's/' + | ||||
| uuid, { 'method': 'DELETE' }); | |||||
| uuid, { 'method': 'DELETE', 'promiseOrdering': false }); | |||||
| } | } | ||||
| export default wbDeleteObject; | export default wbDeleteObject; | ||||
| @@ -10,12 +10,15 @@ import WBCollectionListing from 'wb-collection-listing'; | |||||
| import WBWorkflowListing from 'wb-workflow-listing'; | import WBWorkflowListing from 'wb-workflow-listing'; | ||||
| import WBRenameDialog from 'wb-rename-dialog'; | import WBRenameDialog from 'wb-rename-dialog'; | ||||
| import WBDeleteDialog from 'wb-delete-dialog'; | import WBDeleteDialog from 'wb-delete-dialog'; | ||||
| import WBNewProjectDialog from 'wb-new-project-dialog'; | |||||
| class WBBrowse extends Component { | class WBBrowse extends Component { | ||||
| constructor(...args) { | constructor(...args) { | ||||
| super(...args); | super(...args); | ||||
| this.renameDialogRef = createRef(); | this.renameDialogRef = createRef(); | ||||
| this.deleteDialogRef = createRef(); | this.deleteDialogRef = createRef(); | ||||
| this.newProjectDialogRef = createRef(); | |||||
| this.projectListingRef = createRef(); | |||||
| } | } | ||||
| getUrl(params) { | getUrl(params) { | ||||
| @@ -65,14 +68,22 @@ class WBBrowse extends Component { | |||||
| <WBDeleteDialog app={ app } ref={ this.deleteDialogRef } /> | <WBDeleteDialog app={ app } ref={ this.deleteDialogRef } /> | ||||
| <WBNewProjectDialog app={ app } ref={ this.newProjectDialogRef } /> | |||||
| <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) } /> | ||||
| <WBArvadosCrumbs uuid={ ownerUuid } app={ app } /> | <WBArvadosCrumbs uuid={ ownerUuid } app={ app } /> | ||||
| <WBTabs tabs={ [ { 'name': 'Projects', 'isActive': true } ] } /> | |||||
| <WBProjectListing app={ app } | |||||
| <WBTabs tabs={ [ | |||||
| { 'name': 'Projects', 'isActive': true }, | |||||
| { 'name': ( <span><i class="fas fa-plus-square text-success"></i> New Project</span> ), | |||||
| 'onClick': () => this.newProjectDialogRef.current.show(ownerUuid, | |||||
| () => this.projectListingRef.current.fetchItems() ) } | |||||
| ] } /> | |||||
| <WBProjectListing ref={ this.projectListingRef } | |||||
| app={ app } | |||||
| arvHost={ appState.arvHost } | arvHost={ appState.arvHost } | ||||
| arvToken={ appState.arvToken } | arvToken={ appState.arvToken } | ||||
| ownerUuid={ ownerUuid } | ownerUuid={ ownerUuid } | ||||
| @@ -1,18 +1,22 @@ | |||||
| import { h, Component } from 'preact'; | |||||
| import { h, Component, VNode } from 'preact'; | |||||
| class WBTabs extends Component { | class WBTabs extends Component { | ||||
| render({ tabs, onTabChanged }) { | render({ tabs, onTabChanged }) { | ||||
| return ( | return ( | ||||
| <ul class="nav nav-tabs"> | <ul class="nav nav-tabs"> | ||||
| { tabs.map((t, idx) => { | { tabs.map((t, idx) => { | ||||
| let name, isActive, isDisabled; | |||||
| let name, isActive, isDisabled, onClick; | |||||
| if (typeof(t) === 'object') { | if (typeof(t) === 'object') { | ||||
| name = t.name; | name = t.name; | ||||
| isActive = t.isActive; | isActive = t.isActive; | ||||
| isDisabled = t.isDisabled; | isDisabled = t.isDisabled; | ||||
| onClick = t.onClick; | |||||
| } else if (typeof(t) === 'string') { | } else if (typeof(t) === 'string') { | ||||
| name = t; | name = t; | ||||
| } | } | ||||
| let cls = ['nav-link']; | let cls = ['nav-link']; | ||||
| if (isActive) | if (isActive) | ||||
| cls.push('active'); | cls.push('active'); | ||||
| @@ -21,7 +25,11 @@ class WBTabs extends Component { | |||||
| cls = cls.join(' '); | cls = cls.join(' '); | ||||
| return ( | return ( | ||||
| <li class="nav-item"> | <li class="nav-item"> | ||||
| <a class={ cls } href="#" onclick={ e => { e.preventDefault(); onTabChanged(t); } }>{ name }</a> | |||||
| <a class={ cls } href="#" | |||||
| onclick={ e => { e.preventDefault(); | |||||
| onClick ? onClick() : onTabChanged(t); } }> | |||||
| { name } | |||||
| </a> | |||||
| </li> | </li> | ||||
| ); | ); | ||||
| }) } | }) } | ||||