@@ -4,4 +4,4 @@ node_modules | |||||
package-lock.json | package-lock.json | ||||
/frontend/dist/ | /frontend/dist/ | ||||
/backend/server.pem | /backend/server.pem | ||||
/testdata/ |
@@ -16,7 +16,7 @@ export default { | |||||
}, | }, | ||||
plugins: [ | plugins: [ | ||||
includePaths({ | includePaths({ | ||||
paths: ['src/js', 'src/js/widget', 'src/js/misc', 'src/js/component', 'src/js/page'] | |||||
paths: ['src/js', 'src/js/widget', 'src/js/misc', 'src/js/component', 'src/js/page', 'src/js/dialog'] | |||||
}), | }), | ||||
copy({ | copy({ | ||||
'src/html/index.html': 'dist/index.html', | 'src/html/index.html': 'dist/index.html', | ||||
@@ -21,6 +21,8 @@ class WBCollectionListing extends Component { | |||||
} | } | ||||
prepareRows(items, ownerLookup) { | prepareRows(items, ownerLookup) { | ||||
let { app } = this.props; | |||||
return items.map(item => [ | return items.map(item => [ | ||||
(<div> | (<div> | ||||
<div> | <div> | ||||
@@ -42,10 +44,16 @@ class WBCollectionListing extends Component { | |||||
item['file_count'], | item['file_count'], | ||||
filesize(item['file_size_total']), | filesize(item['file_size_total']), | ||||
(<div> | (<div> | ||||
<button class="btn btn-outline-warning m-1" title="Add to Toolbox" | |||||
onclick={ () => (app.addToToolbox(item.uuid)) }> | |||||
<i class="fas fa-toolbox"></i> | |||||
</button> | |||||
<a class="btn btn-outline-primary m-1" title="Properties" | <a class="btn btn-outline-primary m-1" title="Properties" | ||||
href={ urlForObject(item, 'properties') }> | href={ urlForObject(item, 'properties') }> | ||||
<i class="fas fa-list-ul"></i> | <i class="fas fa-list-ul"></i> | ||||
</a> | </a> | ||||
<button class="btn btn-outline-danger m-1" title="Delete"> | <button class="btn btn-outline-danger m-1" title="Delete"> | ||||
<i class="fas fa-trash"></i> | <i class="fas fa-trash"></i> | ||||
</button> | </button> | ||||
@@ -17,6 +17,8 @@ class WBProjectListing extends Component { | |||||
} | } | ||||
prepareRows(items) { | prepareRows(items) { | ||||
let { app } = this.props; | |||||
return items.map(item => [ | return items.map(item => [ | ||||
(<div> | (<div> | ||||
<div> | <div> | ||||
@@ -26,7 +28,13 @@ class WBProjectListing extends Component { | |||||
<div>{ item['uuid'] }</div> | <div>{ item['uuid'] }</div> | ||||
</div>), | </div>), | ||||
item['description'], | item['description'], | ||||
item['owner_uuid'] | |||||
item['owner_uuid'], | |||||
(<div> | |||||
<button class="btn btn-outline-warning m-1" title="Add to Toolbox" | |||||
onclick={ () => (app.addToToolbox(item.uuid)) }> | |||||
<i class="fas fa-toolbox"></i> | |||||
</button> | |||||
</div>) | |||||
]); | ]); | ||||
} | } | ||||
@@ -57,8 +65,9 @@ class WBProjectListing extends Component { | |||||
render({ arvHost, arvToken, ownerUuid, activePage, onPageChanged }, { rows, numPages }) { | render({ arvHost, arvToken, ownerUuid, activePage, onPageChanged }, { rows, numPages }) { | ||||
return ( | return ( | ||||
<div> | <div> | ||||
<WBTable columns={ [ 'Name', 'Description', 'Owner' ] } | |||||
<WBTable columns={ [ 'Name', 'Description', 'Owner', 'Actions' ] } | |||||
rows={ rows } /> | rows={ rows } /> | ||||
<WBPagination numPages={ numPages } | <WBPagination numPages={ numPages } | ||||
activePage={ activePage } | activePage={ activePage } | ||||
onPageChanged={ i => onPageChanged(i) } /> | onPageChanged={ i => onPageChanged(i) } /> | ||||
@@ -5,6 +5,7 @@ import WBPagination from 'wb-pagination'; | |||||
import WBNameAndUuid from 'wb-name-and-uuid'; | 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'; | |||||
class WBWorkflowListing extends Component { | class WBWorkflowListing extends Component { | ||||
@@ -25,7 +26,8 @@ class WBWorkflowListing extends Component { | |||||
( <WBNameAndUuid uuid={ item.owner_uuid } lookup={ ownerLookup } /> ), | ( <WBNameAndUuid uuid={ item.owner_uuid } lookup={ ownerLookup } /> ), | ||||
wbFormatDate(item.created_at), | wbFormatDate(item.created_at), | ||||
(<div> | (<div> | ||||
<button class="btn btn-outline-success mx-1 my-1" title="Run"><i class="fas fa-running"></i></button> | |||||
<a class="btn btn-outline-success mx-1 my-1" title="Launch" | |||||
href={ urlForObject(item, 'launch') }><i class="fas fa-running"></i></a> | |||||
<button class="btn btn-outline-primary mx-1 my-1" title="View"><i class="far fa-eye"></i></button> | <button class="btn btn-outline-primary mx-1 my-1" title="View"><i class="far fa-eye"></i></button> | ||||
<button class="btn btn-outline-danger mx-1 my-1" title="Delete"><i class="fas fa-trash"></i></button> | <button class="btn btn-outline-danger mx-1 my-1" title="Delete"><i class="fas fa-trash"></i></button> | ||||
</div>) | </div>) | ||||
@@ -0,0 +1,35 @@ | |||||
import { h, Component } from 'preact'; | |||||
class WBBrowseDialog extends Component { | |||||
constructor(...args) { | |||||
super(...args); | |||||
} | |||||
render({ id }) { | |||||
return ( | |||||
<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">Browse</h5> | |||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close"> | |||||
<span aria-hidden="true">×</span> | |||||
</button> | |||||
</div> | |||||
<div class="modal-body m-0 p-0"> | |||||
<iframe style="width: 100%;" src="/browse" /> | |||||
</div> | |||||
<div class="modal-footer"> | |||||
<button type="button" class="btn btn-primary">Accept</button> | |||||
<button type="button" class="btn btn-secondary" data-dismiss="modal">Cancel</button> | |||||
</div> | |||||
</div> | |||||
</div> | |||||
</div> | |||||
); | |||||
} | |||||
} | |||||
export default WBBrowseDialog; |
@@ -0,0 +1,77 @@ | |||||
import { h, Component } from 'preact'; | |||||
import WBTable from 'wb-table'; | |||||
import WBNameAndUuid from 'wb-name-and-uuid'; | |||||
import wbFetchObjects from 'wb-fetch-objects'; | |||||
import wbFormatDate from 'wb-format-date'; | |||||
class WBToolboxDialog extends Component { | |||||
constructor(...args) { | |||||
super(...args); | |||||
this.state.rows = []; | |||||
} | |||||
componentDidMount() { | |||||
this.fetchRows(); | |||||
} | |||||
componentWillReceiveProps(nextProps) { | |||||
this.props = nextProps; | |||||
this.fetchRows(); | |||||
} | |||||
fetchRows() { | |||||
const { items } = this.props; | |||||
const { arvHost, arvToken } = this.props.app.state; | |||||
let prom = wbFetchObjects(arvHost, arvToken, | |||||
items); | |||||
let lookup; | |||||
prom = prom.then(lkup => (lookup = lkup)); | |||||
prom = prom.then(() => wbFetchObjects(arvHost, arvToken, | |||||
items.map(uuid => lookup[uuid].owner_uuid))); | |||||
let ownerLookup; | |||||
prom = prom.then(lkup => (ownerLookup = lkup)); | |||||
prom = prom.then(() => { | |||||
const rows = items.map(uuid => { | |||||
const it = lookup[uuid]; | |||||
const ow = ownerLookup[it.owner_uuid]; | |||||
return [ | |||||
( <div><input type="checkbox" /></div> ), | |||||
( <WBNameAndUuid uuid={ uuid } lookup={ lookup } /> ), | |||||
it.kind, | |||||
wbFormatDate(it.created_at), | |||||
( <WBNameAndUuid uuid={ it.owner_uuid } lookup={ ownerLookup } /> ) | |||||
]; | |||||
}); | |||||
this.setState({ rows }); | |||||
}); | |||||
} | |||||
render({ id }, { rows }) { | |||||
return ( | |||||
<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">Browse Toolbox</h5> | |||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close"> | |||||
<span aria-hidden="true">×</span> | |||||
</button> | |||||
</div> | |||||
<div class="modal-body"> | |||||
<WBTable columns={ [ '', 'Name', 'Kind', 'Created At', 'Owner' ] } | |||||
rows={ rows } /> | |||||
</div> | |||||
<div class="modal-footer"> | |||||
<button type="button" class="btn btn-primary">Accept</button> | |||||
<button type="button" class="btn btn-secondary" data-dismiss="modal">Cancel</button> | |||||
</div> | |||||
</div> | |||||
</div> | |||||
</div> | |||||
); | |||||
} | |||||
} | |||||
export default WBToolboxDialog; |
@@ -8,9 +8,12 @@ function urlForObject(item, mode='primary') { | |||||
return ('/browse/' + item.uuid); | return ('/browse/' + item.uuid); | ||||
else if (objectType === 'container_request') | else if (objectType === 'container_request') | ||||
return ('/process/' + item.uuid); | return ('/process/' + item.uuid); | ||||
else if (objectType === 'workflow') | |||||
return ('/workflow/' + item.uuid); | |||||
else if (objectType === 'collection') { | |||||
else if (objectType === 'workflow') { | |||||
if (mode === 'launch') | |||||
return ('/workflow-launch/' + item.uuid) | |||||
else | |||||
return ('/workflow/' + item.uuid); | |||||
} else if (objectType === 'collection') { | |||||
if (mode === 'primary' || mode === 'browse') | if (mode === 'primary' || mode === 'browse') | ||||
return ('/collection-browse/' + item.uuid); | return ('/collection-browse/' + item.uuid); | ||||
else | else | ||||
@@ -19,6 +19,8 @@ class WBApp extends Component { | |||||
this.state.arvToken = window.localStorage['arvToken']; | this.state.arvToken = window.localStorage['arvToken']; | ||||
if ('currentUser' in window.localStorage) | if ('currentUser' in window.localStorage) | ||||
this.state.currentUser = JSON.parse(window.localStorage['currentUser']); | this.state.currentUser = JSON.parse(window.localStorage['currentUser']); | ||||
this.state.toolboxItems = ('toolboxItems' in window.localStorage) ? | |||||
JSON.parse(window.localStorage['toolboxItems']) : []; | |||||
} | } | ||||
navbarItemUrl(item) { | navbarItemUrl(item) { | ||||
@@ -43,6 +45,12 @@ class WBApp extends Component { | |||||
route('/process/' + item.uuid) | route('/process/' + item.uuid) | ||||
} | } | ||||
addToToolbox(uuid) { | |||||
this.state.toolboxItems.push(uuid); | |||||
window.localStorage['toolboxItems'] = | |||||
JSON.stringify(this.state.toolboxItems); | |||||
} | |||||
render() { | render() { | ||||
return ( | return ( | ||||
<Router> | <Router> | ||||
@@ -38,7 +38,8 @@ class WBBrowse extends Component { | |||||
<WBTabs tabs={ [ { 'name': 'Projects', 'isActive': true } ] } /> | <WBTabs tabs={ [ { 'name': 'Projects', 'isActive': true } ] } /> | ||||
<WBProjectListing arvHost={ appState.arvHost } | |||||
<WBProjectListing app={ app } | |||||
arvHost={ appState.arvHost } | |||||
arvToken={ appState.arvToken } | arvToken={ appState.arvToken } | ||||
ownerUuid={ ownerUuid } | ownerUuid={ ownerUuid } | ||||
itemsPerPage="5" | itemsPerPage="5" | ||||
@@ -1,6 +1,7 @@ | |||||
import { h, Component } from 'preact'; | import { h, Component } from 'preact'; | ||||
import WBNavbarCommon from 'wb-navbar-common'; | import WBNavbarCommon from 'wb-navbar-common'; | ||||
import WBArvadosCrumbs from 'wb-arvados-crumbs'; | import WBArvadosCrumbs from 'wb-arvados-crumbs'; | ||||
import WBToolboxDialog from 'wb-toolbox-dialog'; | |||||
import makeArvadosRequest from 'make-arvados-request'; | import makeArvadosRequest from 'make-arvados-request'; | ||||
import linkState from 'linkstate'; | import linkState from 'linkstate'; | ||||
@@ -25,6 +26,11 @@ function createInputsTemplate(workflow) { | |||||
} | } | ||||
class WBLaunchWorkflowPage extends Component { | class WBLaunchWorkflowPage extends Component { | ||||
constructor(...args) { | |||||
super(...args); | |||||
this.state.toolboxDialogId = uuid.v4(); | |||||
} | |||||
componentDidMount() { | componentDidMount() { | ||||
let { app, workflowUuid } = this.props; | let { app, workflowUuid } = this.props; | ||||
let { arvHost, arvToken } = app.state; | let { arvHost, arvToken } = app.state; | ||||
@@ -43,12 +49,15 @@ class WBLaunchWorkflowPage extends Component { | |||||
render({ app, projectUuid, workflowUuid }, | render({ app, projectUuid, workflowUuid }, | ||||
{ workflow, processName, processDescription, | { workflow, processName, processDescription, | ||||
inputsFunctionText }) { | |||||
inputsFunctionText, toolboxDialogId }) { | |||||
return ( | return ( | ||||
<div> | <div> | ||||
<WBNavbarCommon app={ app } /> | <WBNavbarCommon app={ app } /> | ||||
<WBToolboxDialog app={ app } id={ toolboxDialogId } | |||||
items={ app.state.toolboxItems } /> | |||||
{ workflow ? | { workflow ? | ||||
(<form class="container-fluid"> | (<form class="container-fluid"> | ||||
<h1>Launch Workflow</h1> | <h1>Launch Workflow</h1> | ||||
@@ -61,6 +70,7 @@ class WBLaunchWorkflowPage extends Component { | |||||
<div class="form-group"> | <div class="form-group"> | ||||
<label for="projectUuid">Project UUID</label> | <label for="projectUuid">Project UUID</label> | ||||
<input type="email" class="form-control" id="projectUuid" placeholder="Enter project uuid" /> | <input type="email" class="form-control" id="projectUuid" placeholder="Enter project uuid" /> | ||||
<button class="btn btn-primary" onclick={ e => { e.preventDefault(); $('#' + toolboxDialogId).modal(); } }>Browse</button> | |||||
</div> | </div> | ||||
<div class="form-check"> | <div class="form-check"> | ||||