| @@ -2,6 +2,9 @@ | |||
| function makeArvadosRequest(arvHost, arvToken, endpoint, method='GET', data=null, | |||
| contentType='application/json;charset=utf-8', responseType='json') { | |||
| if (!arvHost || !arvToken) | |||
| return new Promise((accept, reject) => reject()); | |||
| let xhr = new XMLHttpRequest(); | |||
| xhr.open(method, 'https://' + arvHost + endpoint); | |||
| xhr.setRequestHeader('Authorization', 'OAuth2 ' + arvToken); | |||
| @@ -1,24 +1,36 @@ | |||
| import { h, Component } from 'preact'; | |||
| import { Router, route } from 'preact-router'; | |||
| import WBTabs from 'wb-tabs'; | |||
| import WBTable from 'wb-table'; | |||
| import WBPagination from 'wb-pagination'; | |||
| import WBProjectListing from 'wb-project-listing'; | |||
| import WBNavbar from 'wb-navbar'; | |||
| import WBBrowse from 'wb-browse'; | |||
| import WBSignIn from 'wb-sign-in'; | |||
| import WBLandingPage from 'wb-landing-page'; | |||
| class WBApp extends Component { | |||
| render({}, { activePage }) { | |||
| constructor(...args) { | |||
| super(...args); | |||
| this.state.arvHost = window.localStorage['arvHost']; | |||
| this.state.arvToken = window.localStorage['arvToken']; | |||
| this.appCallbacks = { | |||
| 'navbarItemClicked': this.navbarItemClicked | |||
| }; | |||
| } | |||
| navbarItemClicked(item) { | |||
| if (item['id'] === 'sign-out') { | |||
| delete window.localStorage['arvHost']; | |||
| delete window.localStorage['arvToken']; | |||
| delete window.localStorage['currentUser']; | |||
| route('/sign-in'); | |||
| } | |||
| } | |||
| render({}, { activePage, arvHost, arvToken }) { | |||
| return ( | |||
| <Router> | |||
| <div path="/"> | |||
| Hello, world! | |||
| </div> | |||
| <WBLandingPage path="/" /> | |||
| <WBSignIn path="/sign-in" /> | |||
| <WBBrowse path="/browse/:ownerUuid?" /> | |||
| <WBBrowse path="/browse/:ownerUuid?" appCallbacks={ this.appCallbacks } /> | |||
| </Router> | |||
| ); | |||
| } | |||
| @@ -4,15 +4,16 @@ import WBProjectListing from 'wb-project-listing'; | |||
| import WBInlineSearch from 'wb-inline-search'; | |||
| class WBBrowse extends Component { | |||
| render({ ownerUuid }) { | |||
| render({ ownerUuid, appCallbacks }) { | |||
| return ( | |||
| <div> | |||
| <WBNavbar items={ [ | |||
| { 'name': 'Browse', 'active': true }, | |||
| { 'name': 'User', 'dropdown': [ 'Sign Off' ]} | |||
| { 'name': 'User', 'dropdown': [ { 'id': 'sign-out', 'name': 'Sign Out' } ]} | |||
| ] } rhs={ ( | |||
| <WBInlineSearch /> | |||
| ) } /> | |||
| ) } onItemClicked={ appCallbacks.navbarItemClicked } /> | |||
| <WBProjectListing arvHost="api.arkau.roche.com" | |||
| arvToken="v2/arkau-gj3su-uf4hnu2o2qkvm8j/15kla38mafzq6b31d5t74ynhk6iuy32v1ticslodr0obvvhde9" | |||
| ownerUuid={ ownerUuid } | |||
| @@ -0,0 +1,20 @@ | |||
| import { h, Component } from 'preact'; | |||
| import makeArvadosRequest from 'make-arvados-request'; | |||
| import { route } from 'preact-router'; | |||
| class WBLandingPage extends Component { | |||
| componentDidMount() { | |||
| let { arvHost, arvToken } = window.localStorage; | |||
| let prom = makeArvadosRequest(arvHost, arvToken, '/arvados/v1/users/current'); | |||
| prom = prom.then(xhr => route('/browse/' + xhr.response['uuid'])); | |||
| prom = prom.catch(() => route('/sign-in')); | |||
| } | |||
| render() { | |||
| return ( | |||
| <div>Please wait...</div> | |||
| ); | |||
| } | |||
| } | |||
| export default WBLandingPage; | |||
| @@ -1,24 +1,49 @@ | |||
| import { h, Component } from 'preact'; | |||
| import { route } from 'preact-router'; | |||
| import WBNavbar from 'wb-navbar'; | |||
| import linkState from 'linkstate'; | |||
| import makeArvadosRequest from 'make-arvados-request'; | |||
| class WBLogin extends Component { | |||
| render() { | |||
| class WBSignIn extends Component { | |||
| onSubmit() { | |||
| let { arvHost, arvToken } = this.state; | |||
| window.localStorage['arvHost'] = arvHost; | |||
| window.localStorage['arvToken'] = arvToken; | |||
| let prom = makeArvadosRequest(arvHost, arvToken, '/arvados/v1/users/current'); | |||
| prom = prom.then(xhr => { | |||
| window.localStorage['currentUser'] = JSON.stringify(xhr.response); | |||
| route('/browse/' + xhr.response['uuid']); | |||
| }); | |||
| prom = prom.catch(() => { | |||
| alert('Sign in unsuccessful. Verify your input and try again.') | |||
| }); | |||
| } | |||
| render({}, { arvHost, arvToken }) { | |||
| return ( | |||
| <div> | |||
| <div class="container"> | |||
| <WBNavbar /> | |||
| <div class="container my-3"> | |||
| <div class="row justify-content-center"> | |||
| <div class="col-6"> | |||
| <h1>Sign In</h1> | |||
| <form> | |||
| <div class="form-group"> | |||
| <label for="arvHost">Arvados API Host</label> | |||
| <input type="text" class="form-control" id="arvHost" placeholder="Enter Arvados API Host" /> | |||
| <input type="text" class="form-control" id="arvHost" | |||
| placeholder="Enter Arvados API Host" | |||
| value={ arvHost } | |||
| onInput={ linkState(this, 'arvHost') } /> | |||
| </div> | |||
| <div class="form-group"> | |||
| <label for="arvToken">Token</label> | |||
| <input type="text" class="form-control" id="arvToken" placeholder="Enter Arvados API Token" /> | |||
| <input type="text" class="form-control" id="arvToken" | |||
| placeholder="Enter Arvados API Token" | |||
| value={ arvToken } | |||
| onInput={ linkState(this, 'arvToken') } /> | |||
| </div> | |||
| <button type="submit" class="btn btn-primary">Submit</button> | |||
| <button type="submit" class="btn btn-primary" | |||
| onclick={ e => { e.preventDefault(); this.onSubmit(); } }>Submit</button> | |||
| </form> | |||
| </div> | |||
| </div> | |||
| @@ -28,4 +53,4 @@ class WBLogin extends Component { | |||
| } | |||
| } | |||
| export default WBLogin; | |||
| export default WBSignIn; | |||
| @@ -1,10 +1,10 @@ | |||
| import { h, Component } from 'preact'; | |||
| class WBNavbar extends Component { | |||
| render({ title, items, rhs, onItemClicked }) { | |||
| render({ title, items, rhs, onItemClicked, onTitleClicked }) { | |||
| return ( | |||
| <nav class="navbar navbar-expand-lg navbar-light bg-light"> | |||
| <a class="navbar-brand" href="#">{ title }</a> | |||
| <a class="navbar-brand" href="#" onclick={ e => { e.preventDefault(); onTitleClicked(); } }>{ title }</a> | |||
| <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation"> | |||
| <span class="navbar-toggler-icon"></span> | |||
| @@ -52,6 +52,11 @@ class WBNavbar extends Component { | |||
| return ( | |||
| <div class="dropdown-divider" /> | |||
| ); | |||
| else if (typeof(d) === 'object' && d['name']) | |||
| return ( | |||
| <a class="dropdown-item" href="#" onclick={ e => { e.preventDefault(); onItemClicked(d); } }>{ d['name'] }</a> | |||
| ); | |||
| }) } | |||
| </div> | |||
| </li> | |||
| @@ -81,7 +86,8 @@ WBNavbar.defaultProps = { | |||
| 'title': 'Workbench Advanced', | |||
| 'items': [], | |||
| 'form': null, | |||
| 'onItemClicked': () => {} | |||
| 'onItemClicked': () => {}, | |||
| 'onTitleClicked': () => {} | |||
| } | |||
| export default WBNavbar; | |||