From 7a461e3cea37e5c12c30a3fdd936d618d89051d3 Mon Sep 17 00:00:00 2001 From: pkupczyk <piotr.kupczyk@id.ethz.ch> Date: Tue, 22 Jan 2019 20:09:56 +0100 Subject: [PATCH] Initial version of: - SSDM-7559 : NEW openBIS UI - User/Groups - Navigation Tree - Visualization - SSDM-7566 : NEW openBIS UI - Types - Navigation Tree - Visualization --- openbis_ng_ui/.eslintrc.js | 6 +- openbis_ng_ui/src/components/App.jsx | 120 +++---- openbis_ng_ui/src/components/Browser.jsx | 61 ++-- .../src/components/BrowserButtons.jsx | 50 +-- .../src/components/BrowserFilter.jsx | 38 +-- openbis_ng_ui/src/components/BrowserList.jsx | 110 +++---- openbis_ng_ui/src/components/ErrorDialog.jsx | 92 +++--- openbis_ng_ui/src/components/ModeBar.jsx | 65 ++-- openbis_ng_ui/src/components/TabContainer.jsx | 142 ++++----- openbis_ng_ui/src/components/TabContent.jsx | 16 +- openbis_ng_ui/src/components/TabPanel.jsx | 88 +++--- openbis_ng_ui/src/components/TopBar.jsx | 122 ++++---- openbis_ng_ui/src/components/WithError.jsx | 42 +-- openbis_ng_ui/src/components/WithLoader.jsx | 64 ++-- openbis_ng_ui/src/components/WithLogin.jsx | 200 ++++++------ .../src/components/database/EntityDetails.jsx | 210 ++++++------- .../src/components/database/OpenBISTable.jsx | 173 +++++------ .../components/database/OpenBISTableRow.jsx | 116 +++---- openbis_ng_ui/src/index.js | 48 +-- openbis_ng_ui/src/profile.js | 2 +- openbis_ng_ui/src/reducer/actions.js | 173 ++++++----- openbis_ng_ui/src/reducer/database/reducer.js | 215 ++++++------- openbis_ng_ui/src/reducer/initialstate.js | 108 ++++--- openbis_ng_ui/src/reducer/reducer.js | 292 ++++++++++-------- openbis_ng_ui/src/reducer/sagas.js | 139 ++++++--- openbis_ng_ui/src/reducer/types/reducer.js | 109 +++++++ openbis_ng_ui/src/reducer/users/reducer.js | 161 ++++++++++ openbis_ng_ui/src/services/openbis.js | 169 +++++++--- 28 files changed, 1806 insertions(+), 1325 deletions(-) create mode 100644 openbis_ng_ui/src/reducer/types/reducer.js create mode 100644 openbis_ng_ui/src/reducer/users/reducer.js diff --git a/openbis_ng_ui/.eslintrc.js b/openbis_ng_ui/.eslintrc.js index c7098787dad..1b5c2d8298b 100644 --- a/openbis_ng_ui/.eslintrc.js +++ b/openbis_ng_ui/.eslintrc.js @@ -24,13 +24,13 @@ module.exports = { pragma: "React", version: "16.4.2" }, - propWrapperFunctions: [ "forbidExtraProps" ] + propWrapperFunctions: ["forbidExtraProps"] }, rules: { "react/jsx-uses-react": "error", "react/jsx-uses-vars": "error", - "indent": ["error", 2], + "indent": ["error", 4, {"SwitchCase": 1}], "linebreak-style": ["error", "unix"], "quotes": ["error", "single"], "semi": ["error", "never"], @@ -41,4 +41,4 @@ module.exports = { // override default options for rules from base configurations "no-cond-assign": ["error", "always"], } -} +}; diff --git a/openbis_ng_ui/src/components/App.jsx b/openbis_ng_ui/src/components/App.jsx index bebfcaaf7e7..55a4111599c 100644 --- a/openbis_ng_ui/src/components/App.jsx +++ b/openbis_ng_ui/src/components/App.jsx @@ -1,7 +1,7 @@ import React from 'react' -import { withStyles } from '@material-ui/core/styles' +import {withStyles} from '@material-ui/core/styles' import HTML5Backend from 'react-dnd-html5-backend' -import { DragDropContext } from 'react-dnd' +import {DragDropContext} from 'react-dnd' import flow from 'lodash/flow' import Hidden from '@material-ui/core/Hidden' @@ -17,75 +17,75 @@ import TopBar from './TopBar.jsx' const drawerWidth = 400 const styles = { - right: { - width: `calc(100% - ${drawerWidth + 4 + 4 + 1}px)`, - paddingLeft: 4, - marginLeft: drawerWidth + 5, - }, - - left: { - float: 'left', - width: drawerWidth, - paddingRight: 4, - borderRight: '1px dotted', - borderColor: '#e3e5ea', - height: '100%', - position: 'absolute', - }, + right: { + width: `calc(100% - ${drawerWidth + 4 + 4 + 1}px)`, + paddingLeft: 4, + marginLeft: drawerWidth + 5, + }, - browser: { - height: 'calc(100% - 160px)', - overflow: 'auto' - }, + left: { + float: 'left', + width: drawerWidth, + paddingRight: 4, + borderRight: '1px dotted', + borderColor: '#e3e5ea', + height: '100%', + position: 'absolute', + }, - topMargin: { - marginTop: 8 - }, + browser: { + height: 'calc(100% - 160px)', + overflow: 'auto' + }, + + topMargin: { + marginTop: 8 + }, } class App extends React.Component { - render() { - const classes = this.props.classes + render() { + const classes = this.props.classes - return ( - <div> + return ( + <div> - <Hidden mdUp> - <TopBar/> - <div className={classes.topMargin}> - <ModeBar/> - </div> - <BrowserFilter/> - <Browser/> - <BrowserButtons /> - <div className={classes.topMargin}> - <TabPanel /> - </div> - </Hidden> + <Hidden mdUp> + <TopBar/> + <div className={classes.topMargin}> + <ModeBar/> + </div> + <BrowserFilter/> + <Browser/> + <BrowserButtons/> + <div className={classes.topMargin}> + <TabPanel/> + </div> + </Hidden> - <Hidden smDown> - <div className={classes.left}> - <ModeBar/> - <BrowserFilter/> - <div className={classes.browser}> - <Browser /> - </div> - <BrowserButtons /> - </div> - <div className={classes.right}> - <TopBar /> - <div className={classes.topMargin}> - <TabPanel /> + <Hidden smDown> + <div className={classes.left}> + <ModeBar/> + <BrowserFilter/> + <div className={classes.browser}> + <Browser/> + </div> + <BrowserButtons/> + </div> + <div className={classes.right}> + <TopBar/> + <div className={classes.topMargin}> + <TabPanel/> + </div> + </div> + </Hidden> </div> - </div> - </Hidden> - </div> - ) - } + ) + } } export default flow( - withStyles(styles), - DragDropContext(HTML5Backend) + withStyles(styles), + DragDropContext(HTML5Backend) )(App) diff --git a/openbis_ng_ui/src/components/Browser.jsx b/openbis_ng_ui/src/components/Browser.jsx index 86f0406b139..a34c9f05f51 100644 --- a/openbis_ng_ui/src/components/Browser.jsx +++ b/openbis_ng_ui/src/components/Browser.jsx @@ -1,5 +1,5 @@ import React from 'react' -import { connect } from 'react-redux' +import {connect} from 'react-redux' import ListItemText from '@material-ui/core/ListItemText' import BrowserList from './BrowserList.jsx' @@ -7,34 +7,55 @@ import actions from '../reducer/actions.js' function mapDispatchToProps(dispatch) { - return { - selectEntity: permId => dispatch(actions.selectEntity(permId)), - } + return { + selectNode: permId => dispatch(actions.selectEntity(permId)), + } } function mapStateToProps(state) { - // TODO stack tree nodes here when the final tree model is done - return { - databaseTreeNodes: state.databaseTreeNodes, - selectedEntity: state.openEntities.selectedEntity, - } + // TODO stack tree nodes here when the final tree model is done + if (state.mode === 'DATABASE') { + return { + nodes: state.databaseTreeNodes, + selectedNodeId: state.openEntities.selectedEntity, + } + } else if (state.mode === 'USERS') { + return { + nodes: state.users.browser.nodes, + selectedNodeId: state.users.browser.selectedNodeId + } + } else if (state.mode === 'TYPES') { + return { + nodes: state.types.browser.nodes, + selectedNodeId: state.types.browser.selectedNodeId + } + } else { + return { + nodes: [], + selectedNodeId: null + } + } } class Browser extends React.Component { - render() { - return ( - <BrowserList - nodes={ this.props.databaseTreeNodes } - level={ 0 } - selectedNodeId={ this.props.selectedEntity } - onSelect={ node => { if (node.type === 'as.dto.space.Space') this.props.selectEntity(node.id) } } - renderNode={ node => { return (<ListItemText secondary={node.id} />)} } - /> - ) - } + render() { + return ( + <BrowserList + nodes={this.props.nodes} + level={0} + selectedNodeId={this.props.selectedNodeId} + onSelect={node => { + if (node.type === 'as.dto.space.Space') this.props.selectNode(node.id) + }} + renderNode={node => { + return (<ListItemText inset secondary={node.id}/>) + }} + /> + ) + } } export default connect(mapStateToProps, mapDispatchToProps)(Browser) diff --git a/openbis_ng_ui/src/components/BrowserButtons.jsx b/openbis_ng_ui/src/components/BrowserButtons.jsx index 1996ecab692..1834851a8ae 100644 --- a/openbis_ng_ui/src/components/BrowserButtons.jsx +++ b/openbis_ng_ui/src/components/BrowserButtons.jsx @@ -8,31 +8,31 @@ import AddIcon from '@material-ui/icons/Add' class BrowserButtons extends React.Component { - render() { - return ( - <AppBar position='static'> - <Toolbar> - <Grid container alignItems='center'> - <Grid item xs={2}> - <Button - variant="contained" - color="primary"> - <RemoveIcon /> - </Button> - </Grid> - <Grid item xs={8} /> - <Grid item xs={2}> - <Button - variant="contained" - color="primary"> - <AddIcon /> - </Button> - </Grid> - </Grid> - </Toolbar> - </AppBar> - ) - } + render() { + return ( + <AppBar position='static'> + <Toolbar> + <Grid container alignItems='center'> + <Grid item xs={2}> + <Button + variant="contained" + color="primary"> + <RemoveIcon/> + </Button> + </Grid> + <Grid item xs={8}/> + <Grid item xs={2}> + <Button + variant="contained" + color="primary"> + <AddIcon/> + </Button> + </Grid> + </Grid> + </Toolbar> + </AppBar> + ) + } } export default BrowserButtons diff --git a/openbis_ng_ui/src/components/BrowserFilter.jsx b/openbis_ng_ui/src/components/BrowserFilter.jsx index 9ca605b39d7..55771780fda 100644 --- a/openbis_ng_ui/src/components/BrowserFilter.jsx +++ b/openbis_ng_ui/src/components/BrowserFilter.jsx @@ -1,34 +1,34 @@ import React from 'react' -import { withStyles } from '@material-ui/core/styles' +import {withStyles} from '@material-ui/core/styles' import InputAdornment from '@material-ui/core/InputAdornment' import TextField from '@material-ui/core/TextField' import FilterIcon from '@material-ui/icons/FilterList' /*eslint-disable-next-line no-unused-vars*/ const styles = theme => ({ - browserFilter: { - width: '100%' - } + browserFilter: { + width: '100%' + } }) class BrowserFilter extends React.Component { - render() { - const classes = this.props.classes + render() { + const classes = this.props.classes - return ( - <TextField - className = { classes.browserFilter } - placeholder = "Filter" - InputProps={{ - startAdornment: ( - <InputAdornment position="start"> - <FilterIcon /> - </InputAdornment> - ), - }}/> - ) - } + return ( + <TextField + className={classes.browserFilter} + placeholder="Filter" + InputProps={{ + startAdornment: ( + <InputAdornment position="start"> + <FilterIcon/> + </InputAdornment> + ), + }}/> + ) + } } export default withStyles(styles)(BrowserFilter) diff --git a/openbis_ng_ui/src/components/BrowserList.jsx b/openbis_ng_ui/src/components/BrowserList.jsx index 8b17fa4a438..32823f9f5e4 100644 --- a/openbis_ng_ui/src/components/BrowserList.jsx +++ b/openbis_ng_ui/src/components/BrowserList.jsx @@ -1,12 +1,13 @@ import React from 'react' -import { connect } from 'react-redux' -import { withStyles } from '@material-ui/core/styles' +import {connect} from 'react-redux' +import {withStyles} from '@material-ui/core/styles' import List from '@material-ui/core/List' import ListItem from '@material-ui/core/ListItem' import ListItemIcon from '@material-ui/core/ListItemIcon' import ChevronRightIcon from '@material-ui/icons/ChevronRight' import ExpandMoreIcon from '@material-ui/icons/ExpandMore' import HourglassEmptyIcon from '@material-ui/icons/HourglassEmpty' +import Collapse from '@material-ui/core/Collapse'; import PropTypes from 'prop-types' @@ -15,74 +16,73 @@ import actions from '../reducer/actions.js' /*eslint-disable-next-line no-unused-vars*/ const styles = theme => ({ - noPadding: { - paddingTop: '0', - paddingBottom: '0', - }, + noPadding: { + paddingTop: '0', + paddingBottom: '0', + }, }) function mapDispatchToProps(dispatch) { - return { - expandNode: (e, node) => { - e.stopPropagation() - dispatch(actions.expandNode(node)) - }, - collapseNode: (e, node) => { - e.stopPropagation() - dispatch(actions.collapseNode(node)) - }, - } + return { + expandNode: (e, node) => { + e.stopPropagation() + dispatch(actions.expandNode(node)) + }, + collapseNode: (e, node) => { + e.stopPropagation() + dispatch(actions.collapseNode(node)) + }, + } } class BrowserList extends React.Component { - icon(node) { - if (node.expanded) { - return (<ExpandMoreIcon onClick={ (e) => this.props.collapseNode(e, node) }/>) - } else { - return (<ChevronRightIcon onClick={ (e) => this.props.expandNode(e, node) }/>) + icon(node) { + if (node.expanded) { + return (<ListItemIcon><ExpandMoreIcon onClick={(e) => this.props.collapseNode(e, node)}/></ListItemIcon>) + } else if (node.loaded === false || (node.children !== null && node.children.length > 0)) { + return (<ListItemIcon><ChevronRightIcon onClick={(e) => this.props.expandNode(e, node)}/></ListItemIcon>) + } else { + return null + } } - } - render() { - const classes = this.props.classes + render() { + const classes = this.props.classes - return ( - <List className={classes.noPadding}> - { - this.props.nodes.map(node => - <div key={node.id}> - <ListItem - button - selected={this.props.selectedNodeId === node.id} - key={node.id} - onClick = {() => this.props.onSelect(node)} - style={{ paddingLeft: '' + this.props.level * 20 + 'px' }}> - <ListItemIcon> - {this.icon(node)} - </ListItemIcon> - { node.loading && - <ListItemIcon> - <HourglassEmptyIcon/> - </ListItemIcon> + return ( + <List className={classes.noPadding}> + { + this.props.nodes.map(node => + <div key={node.id}> + <ListItem + button + selected={this.props.selectedNodeId === node.id} + key={node.id} + onClick={() => this.props.onSelect(node)} + style={{paddingLeft: '' + this.props.level * 20 + 'px'}}> + {this.icon(node)} + {node.loading && + <ListItemIcon> + <HourglassEmptyIcon/> + </ListItemIcon> + } + {this.props.renderNode(node)} + </ListItem> + <Collapse key={node.id + '-collapse'} in={node.expanded}> + <BrowserList {...this.props} nodes={node.children} level={this.props.level + 1}/> + </Collapse> + </div> + ) } - { this.props.renderNode(node) } - </ListItem> - { - node.expanded && - <BrowserList {...this.props} nodes={node.children} level={this.props.level+1}/> - } - </div> - ) - } - </List> - ) - } + </List> + ) + } } BrowserList.propTypes = { - renderNode: PropTypes.func.isRequired, + renderNode: PropTypes.func.isRequired, } export default connect(null, mapDispatchToProps)(withStyles(styles)(BrowserList)) diff --git a/openbis_ng_ui/src/components/ErrorDialog.jsx b/openbis_ng_ui/src/components/ErrorDialog.jsx index dffa90bee31..539144f5299 100644 --- a/openbis_ng_ui/src/components/ErrorDialog.jsx +++ b/openbis_ng_ui/src/components/ErrorDialog.jsx @@ -1,5 +1,5 @@ import React from 'react' -import { withStyles } from '@material-ui/core/styles' +import {withStyles} from '@material-ui/core/styles' import PropTypes from 'prop-types' import Button from '@material-ui/core/Button' @@ -13,61 +13,61 @@ import profile from '../profile.js' const dialogStyles = { - paper: { - backgroundColor: '#ffd2d2', - }, + paper: { + backgroundColor: '#ffd2d2', + }, } const StyledDialog = withStyles(dialogStyles)(Dialog) class ErrorDialog extends React.Component { - getErrorMailtoHref() { - let report = - 'agent: ' + navigator.userAgent + '%0D%0A' + - 'domain: ' + location.hostname + '%0D%0A' + - 'timestamp: ' + new Date() + '%0D%0A' + - 'href: ' + location.href.replace(new RegExp('&', 'g'), ' - ') + '%0D%0A' + - 'error: ' + JSON.stringify(this.props.exception['data']) - - let href = - 'mailto:' + profile.devEmail + - '?subject=openBIS Error Report [' + location.hostname + ']' + - '&body=' + report - return href - } + getErrorMailtoHref() { + let report = + 'agent: ' + navigator.userAgent + '%0D%0A' + + 'domain: ' + location.hostname + '%0D%0A' + + 'timestamp: ' + new Date() + '%0D%0A' + + 'href: ' + location.href.replace(new RegExp('&', 'g'), ' - ') + '%0D%0A' + + 'error: ' + JSON.stringify(this.props.exception['data']) - render() { - return ( - <StyledDialog - open={ true } - onClose={ this.props.closeError } - scroll="paper" - aria-labelledby="error-dialog-title" - fullWidth={ true } - maxWidth="md" - > - <DialogTitle id="error-dialog-title">Error</DialogTitle> - <DialogContent> - <DialogContentText> - { this.props.exception.message } - </DialogContentText> - </DialogContent> - <DialogActions> - <Button onClick={ this.props.onClose } color="primary"> - Dismiss - </Button> - <Button color="primary" href={this.getErrorMailtoHref()}> - Send error report - </Button> - </DialogActions> - </StyledDialog> - ) - } + let href = + 'mailto:' + profile.devEmail + + '?subject=openBIS Error Report [' + location.hostname + ']' + + '&body=' + report + return href + } + + render() { + return ( + <StyledDialog + open={true} + onClose={this.props.closeError} + scroll="paper" + aria-labelledby="error-dialog-title" + fullWidth={true} + maxWidth="md" + > + <DialogTitle id="error-dialog-title">Error</DialogTitle> + <DialogContent> + <DialogContentText> + {this.props.exception.message} + </DialogContentText> + </DialogContent> + <DialogActions> + <Button onClick={this.props.onClose} color="primary"> + Dismiss + </Button> + <Button color="primary" href={this.getErrorMailtoHref()}> + Send error report + </Button> + </DialogActions> + </StyledDialog> + ) + } } ErrorDialog.propTypes = { - exception: PropTypes.any.isRequired, + exception: PropTypes.any.isRequired, } export default ErrorDialog diff --git a/openbis_ng_ui/src/components/ModeBar.jsx b/openbis_ng_ui/src/components/ModeBar.jsx index 1a198dd0565..f306999d24d 100644 --- a/openbis_ng_ui/src/components/ModeBar.jsx +++ b/openbis_ng_ui/src/components/ModeBar.jsx @@ -1,39 +1,56 @@ import React from 'react' +import {connect} from 'react-redux' import AppBar from '@material-ui/core/AppBar' import Toolbar from '@material-ui/core/Toolbar' import Tabs from '@material-ui/core/Tabs' import Tab from '@material-ui/core/Tab' -import { withStyles } from '@material-ui/core/styles' +import {withStyles} from '@material-ui/core/styles' +import actions from '../reducer/actions.js' /*eslint-disable-next-line no-unused-vars*/ const styles = theme => ({ - browserTabs: { - width: '100%' - }, - browserTab: { - minWidth: '50px' - } + browserTabs: { + width: '100%' + }, + browserTab: { + minWidth: '50px' + } }) +function mapStateToProps(state) { + return { + mode: state.mode + } +} + +function mapDispatchToProps(dispatch) { + return { + setMode: (event, value) => { + dispatch(actions.setMode(value)) + } + } +} + class ModeBar extends React.Component { - render() { - const classes = this.props.classes + render() { + const classes = this.props.classes - return ( - <AppBar position="static"> - <Toolbar> - <Tabs value={0} fullWidth className = { classes.browserTabs }> - <Tab className = { classes.browserTab } label="Database" /> - <Tab className = { classes.browserTab } label="Types" /> - <Tab className = { classes.browserTab } label="Users" /> - <Tab className = { classes.browserTab } label="Favourites" /> - <Tab className = { classes.browserTab } label="Tools" /> - </Tabs> - </Toolbar> - </AppBar> - ) - } + return ( + <AppBar position="static"> + <Toolbar> + <Tabs value={this.props.mode} onChange={this.props.setMode} fullWidth + className={classes.browserTabs}> + <Tab value="DATABASE" className={classes.browserTab} label="Database"/> + <Tab value="TYPES" className={classes.browserTab} label="Types"/> + <Tab value="USERS" className={classes.browserTab} label="Users"/> + <Tab value="FAVOURITES" className={classes.browserTab} label="Favourites"/> + <Tab value="TOOLS" className={classes.browserTab} label="Tools"/> + </Tabs> + </Toolbar> + </AppBar> + ) + } } -export default withStyles(styles)(ModeBar) +export default connect(mapStateToProps, mapDispatchToProps)(withStyles(styles)(ModeBar)) diff --git a/openbis_ng_ui/src/components/TabContainer.jsx b/openbis_ng_ui/src/components/TabContainer.jsx index 30a983e16b4..2104c967099 100644 --- a/openbis_ng_ui/src/components/TabContainer.jsx +++ b/openbis_ng_ui/src/components/TabContainer.jsx @@ -1,5 +1,5 @@ import React from 'react' -import { withStyles } from '@material-ui/core/styles' +import {withStyles} from '@material-ui/core/styles' import AppBar from '@material-ui/core/AppBar' import CloseIcon from '@material-ui/icons/Close' import IconButton from '@material-ui/core/IconButton' @@ -13,84 +13,84 @@ import TabContent from './TabContent.jsx' /* eslint-disable-next-line no-unused-vars */ const styles = theme => ({ - entityTabs: { - width: '100%' - }, - inlineElement: { - display: 'inline-block' - }, - hidden: { - display: 'none', - } + entityTabs: { + width: '100%' + }, + inlineElement: { + display: 'inline-block' + }, + hidden: { + display: 'none', + } }) class TabContainer extends React.Component { - render() { - const classes = this.props.classes - const selectedKey = this.props.selectedKey - const selectedTabIndex = this.props.children.findIndex(child => child.key === selectedKey) - return ( - <div> - <div> - <AppBar position="static"> - <Toolbar> - <Tabs - value={selectedTabIndex} - scrollable={ React.Children.count(this.props.children) > 0 } - scrollButtons="auto" - className = { classes.entityTabs }> - { - React.Children.map(this.props.children, child => - <Tab - component="div" - label={ - <div> - <div className = { classes.inlineElement }>{child.props.name}</div> - { child.props.dirty && - <div className = { classes.inlineElement }>*</div> - } - <div className = { classes.inlineElement }> - <IconButton onClick={ (e) => child.props.onClose(e) } > - <CloseIcon/> - </IconButton> - </div> - </div>} - onClick = {child.props.onSelect}/> - ) - } - </Tabs> - </Toolbar> - </AppBar> - </div> - <div> - { - React.Children.map(this.props.children, child => { - return ( - <div className={ selectedKey === child.key ? {} : classes.hidden }> - {child} + render() { + const classes = this.props.classes + const selectedKey = this.props.selectedKey + const selectedTabIndex = this.props.children.findIndex(child => child.key === selectedKey) + return ( + <div> + <div> + <AppBar position="static"> + <Toolbar> + <Tabs + value={selectedTabIndex} + scrollable={React.Children.count(this.props.children) > 0} + scrollButtons="auto" + className={classes.entityTabs}> + { + React.Children.map(this.props.children, child => + <Tab + component="div" + label={ + <div> + <div className={classes.inlineElement}>{child.props.name}</div> + {child.props.dirty && + <div className={classes.inlineElement}>*</div> + } + <div className={classes.inlineElement}> + <IconButton onClick={(e) => child.props.onClose(e)}> + <CloseIcon/> + </IconButton> + </div> + </div>} + onClick={child.props.onSelect}/> + ) + } + </Tabs> + </Toolbar> + </AppBar> </div> - ) - }) - } - </div> - </div> - ) - } + <div> + { + React.Children.map(this.props.children, child => { + return ( + <div className={selectedKey === child.key ? {} : classes.hidden}> + {child} + </div> + ) + }) + } + </div> + </div> + ) + } } TabContainer.propTypes = { - selectedKey: PropTypes.string.isRequired, - children: function (props, propName, componentName) { - const prop = props[propName] - let error = null - React.Children.forEach(prop, function (child) { - if (child.type !== TabContent) { - error = new Error('`' + componentName + '` children should be of type `TabContent`.') - } - }) - return error - } + selectedKey: PropTypes.string.isRequired, + children: function (props, propName, componentName) { + const prop = props[propName] + let error = null + React.Children.forEach(prop, function (child) { + if (child.type !== TabContent) { + error = new Error('`' + componentName + '` children should be of type `TabContent`.') + } + }) + return error + } } export default withStyles(styles)(TabContainer) diff --git a/openbis_ng_ui/src/components/TabContent.jsx b/openbis_ng_ui/src/components/TabContent.jsx index d1a925010af..34ae06c7915 100644 --- a/openbis_ng_ui/src/components/TabContent.jsx +++ b/openbis_ng_ui/src/components/TabContent.jsx @@ -3,16 +3,16 @@ import PropTypes from 'prop-types' class TabContent extends React.Component { - render() { - return <div>{this.props.children}</div> - } + render() { + return <div>{this.props.children}</div> + } } -TabContent.propTypes ={ - name: PropTypes.string.isRequired, - dirty: PropTypes.bool.isRequired, - onSelect: PropTypes.func.isRequired, - onClose: PropTypes.func.isRequired, +TabContent.propTypes = { + name: PropTypes.string.isRequired, + dirty: PropTypes.bool.isRequired, + onSelect: PropTypes.func.isRequired, + onClose: PropTypes.func.isRequired, } export default TabContent diff --git a/openbis_ng_ui/src/components/TabPanel.jsx b/openbis_ng_ui/src/components/TabPanel.jsx index 970e381d19f..55b700b66b2 100644 --- a/openbis_ng_ui/src/components/TabPanel.jsx +++ b/openbis_ng_ui/src/components/TabPanel.jsx @@ -1,5 +1,5 @@ import React from 'react' -import { connect } from 'react-redux' +import {connect} from 'react-redux' import EntityDetails from './database/EntityDetails.jsx' import TabContainer from './TabContainer.jsx' import TabContent from './TabContent.jsx' @@ -8,62 +8,66 @@ import actions from '../reducer/actions.js' /** * This component at the moment only makes tabs for entities. - * In the future, it should be extended for other kinds of tabs + * In the future, it should be extended for other kinds of tabs * (settings forms etc.). */ function mapDispatchToProps(dispatch) { - return { - selectEntity: (entityPermId) => dispatch(actions.selectEntity(entityPermId)), - closeEntity: (e, entityPermId) => { - e.stopPropagation() - dispatch(actions.closeEntity(entityPermId)) + return { + selectEntity: (entityPermId) => dispatch(actions.selectEntity(entityPermId)), + closeEntity: (e, entityPermId) => { + e.stopPropagation() + dispatch(actions.closeEntity(entityPermId)) + } } - } } function mapStateToProps(state) { - const selectedEntity = state.openEntities.selectedEntity - const spaces = state.database.spaces - return { - openEntities: state.openEntities.entities - .filter(permId => permId in spaces) - .map(permId => spaces[permId]), - selectedEntity: selectedEntity, - dirtyEntities: state.dirtyEntities, - } + const selectedEntity = state.openEntities.selectedEntity + const spaces = state.database.spaces + return { + openEntities: state.openEntities.entities + .filter(permId => permId in spaces) + .map(permId => spaces[permId]), + selectedEntity: selectedEntity, + dirtyEntities: state.dirtyEntities, + } } class TabPanel extends React.Component { - render() { - if (this.props.openEntities.length === 0) { - return null - } - return ( - <TabContainer selectedKey={this.props.selectedEntity}> - { - this.props.openEntities.map(entity => { - return( - <TabContent - key={entity.permId.permId} - name={entity.code} - dirty={this.props.dirtyEntities.indexOf(entity.permId.permId) > -1} - onSelect={() => {this.props.selectEntity(entity.permId.permId)}} - onClose={(e) => {this.props.closeEntity(e, entity.permId.permId)}} - > - <EntityDetails - entity={entity} - /> - </TabContent> - ) - }) + render() { + if (this.props.openEntities.length === 0) { + return null } - </TabContainer> - ) - } + return ( + <TabContainer selectedKey={this.props.selectedEntity}> + { + this.props.openEntities.map(entity => { + return ( + <TabContent + key={entity.permId.permId} + name={entity.code} + dirty={this.props.dirtyEntities.indexOf(entity.permId.permId) > -1} + onSelect={() => { + this.props.selectEntity(entity.permId.permId) + }} + onClose={(e) => { + this.props.closeEntity(e, entity.permId.permId) + }} + > + <EntityDetails + entity={entity} + /> + </TabContent> + ) + }) + } + </TabContainer> + ) + } } export default connect(mapStateToProps, mapDispatchToProps)(TabPanel) diff --git a/openbis_ng_ui/src/components/TopBar.jsx b/openbis_ng_ui/src/components/TopBar.jsx index 86aefdd4bbb..afe525a36a0 100644 --- a/openbis_ng_ui/src/components/TopBar.jsx +++ b/openbis_ng_ui/src/components/TopBar.jsx @@ -1,6 +1,6 @@ import React from 'react' -import { withStyles } from '@material-ui/core/styles' -import { connect } from 'react-redux' +import {withStyles} from '@material-ui/core/styles' +import {connect} from 'react-redux' import flow from 'lodash/flow' import Grid from '@material-ui/core/Grid' @@ -17,79 +17,79 @@ import actions from '../reducer/actions.js' const styles = theme => ({ - searchField: { - backgroundColor: 'white', - width: '100%' - }, - leftIcon: { - marginRight: theme.spacing.unit, - }, - grid: { - minWidth: 0 - } + searchField: { + backgroundColor: 'white', + width: '100%' + }, + leftIcon: { + marginRight: theme.spacing.unit, + }, + grid: { + minWidth: 0 + } }) function mapDispatchToProps(dispatch) { - return { - logout: () => dispatch(actions.logout()), - } + return { + logout: () => dispatch(actions.logout()), + } } class TopBar extends React.Component { - render() { - const classes = this.props.classes + render() { + const classes = this.props.classes - return ( - <AppBar position='static'> - <Toolbar> - <Grid container alignItems='center' spacing={8}> + return ( + <AppBar position='static'> + <Toolbar> + <Grid container alignItems='center' spacing={8}> - <Grid item xs className={ classes.grid }> - <TextField - className = { classes.searchField } - InputProps={{ - startAdornment: ( - <InputAdornment position="start"> - <SearchIcon /> - </InputAdornment> - ), - }}/> - </Grid> + <Grid item xs className={classes.grid}> + <TextField + className={classes.searchField} + InputProps={{ + startAdornment: ( + <InputAdornment position="start"> + <SearchIcon/> + </InputAdornment> + ), + }}/> + </Grid> - <Grid item> - <Button - variant="contained" - color="primary"> - <SearchIcon className={ classes.leftIcon } /> - <Hidden mdUp> - Adv. - </Hidden> - <Hidden smDown> - Adv. search - </Hidden> - </Button> - </Grid> + <Grid item> + <Button + variant="contained" + color="primary"> + <SearchIcon className={classes.leftIcon}/> + <Hidden mdUp> + Adv. + </Hidden> + <Hidden smDown> + Adv. search + </Hidden> + </Button> + </Grid> - <Grid item> - <Button - variant="contained" - color="primary" - onClick={ this.props.logout }> - <LogoutIcon /> - </Button> - </Grid> - - </Grid> - </Toolbar> - </AppBar> - ) - } + <Grid item> + <Button + variant="contained" + color="primary" + onClick={this.props.logout}> + <LogoutIcon/> + </Button> + </Grid> + + </Grid> + </Toolbar> + </AppBar> + ) + } } export default flow( - connect(null, mapDispatchToProps), - withStyles(styles) + connect(null, mapDispatchToProps), + withStyles(styles) )(TopBar) diff --git a/openbis_ng_ui/src/components/WithError.jsx b/openbis_ng_ui/src/components/WithError.jsx index b3ff70d6f09..3f042f8ff1b 100644 --- a/openbis_ng_ui/src/components/WithError.jsx +++ b/openbis_ng_ui/src/components/WithError.jsx @@ -1,5 +1,5 @@ import React from 'react' -import { connect } from 'react-redux' +import {connect} from 'react-redux' import flow from 'lodash/flow' import ErrorDialog from './ErrorDialog.jsx' @@ -7,35 +7,35 @@ import actions from '../reducer/actions.js' function mapStateToProps(state) { - return { - exception: state.exceptions.length > 0 ? state.exceptions[0] : null - } + return { + exception: state.exceptions.length > 0 ? state.exceptions[0] : null + } } function mapDispatchToProps(dispatch) { - return { - closeError: () => dispatch(actions.closeError()), - } + return { + closeError: () => dispatch(actions.closeError()), + } } - + class WithError extends React.Component { - render() { - - return ( - <div> - { - this.props.exception && - <ErrorDialog exception={this.props.exception} onClose={this.props.closeError} /> - } - {this.props.children} - </div> - ) - } + render() { + + return ( + <div> + { + this.props.exception && + <ErrorDialog exception={this.props.exception} onClose={this.props.closeError}/> + } + {this.props.children} + </div> + ) + } } export default flow( - connect(mapStateToProps, mapDispatchToProps), + connect(mapStateToProps, mapDispatchToProps), )(WithError) \ No newline at end of file diff --git a/openbis_ng_ui/src/components/WithLoader.jsx b/openbis_ng_ui/src/components/WithLoader.jsx index 3edfb7855e9..9f8bf234674 100644 --- a/openbis_ng_ui/src/components/WithLoader.jsx +++ b/openbis_ng_ui/src/components/WithLoader.jsx @@ -1,49 +1,49 @@ import React from 'react' -import { withStyles } from '@material-ui/core/styles' -import { connect } from 'react-redux' +import {withStyles} from '@material-ui/core/styles' +import {connect} from 'react-redux' import CircularProgress from '@material-ui/core/CircularProgress' import flow from 'lodash/flow' const styles = { - loader: { - position: 'absolute', - paddingTop: '15%', - width: '100%', - height: '100%', - zIndex: 1000, - backgroundColor: '#000000', - opacity: 0.5, - textAlign: 'center', - } + loader: { + position: 'absolute', + paddingTop: '15%', + width: '100%', + height: '100%', + zIndex: 1000, + backgroundColor: '#000000', + opacity: 0.5, + textAlign: 'center', + } } function mapStateToProps(state) { - return { - loading: state.loading, - exception: state.exceptions.length > 0 ? state.exceptions[0] : null - } + return { + loading: state.loading, + exception: state.exceptions.length > 0 ? state.exceptions[0] : null + } } class WithLoader extends React.Component { - render() { - const classes = this.props.classes + render() { + const classes = this.props.classes - return ( - <div> - { - this.props.loading && - <div className={classes.loader}> - <CircularProgress className={classes.progress} /> - </div> - } - {this.props.children} - </div> - ) - } + return ( + <div> + { + this.props.loading && + <div className={classes.loader}> + <CircularProgress className={classes.progress}/> + </div> + } + {this.props.children} + </div> + ) + } } export default flow( - connect(mapStateToProps), - withStyles(styles), + connect(mapStateToProps), + withStyles(styles), )(WithLoader) diff --git a/openbis_ng_ui/src/components/WithLogin.jsx b/openbis_ng_ui/src/components/WithLogin.jsx index 58310c0c476..5e3a0779bd2 100644 --- a/openbis_ng_ui/src/components/WithLogin.jsx +++ b/openbis_ng_ui/src/components/WithLogin.jsx @@ -1,6 +1,6 @@ import React from 'react' -import { withStyles } from '@material-ui/core/styles' -import { connect } from 'react-redux' +import {withStyles} from '@material-ui/core/styles' +import {connect} from 'react-redux' import flow from 'lodash/flow' import Button from '@material-ui/core/Button' @@ -13,117 +13,121 @@ import actions from '../reducer/actions.js' const styles = { - card: { - marginTop: '10%', - marginBottom: '10em', - width: '30em', - margin: '0 auto', - }, - title: { - fontSize: 24, - }, - textField: { - width: '100%', - }, - button: { - marginTop: '1em', - }, - container: { - width: '100%', - height: '100%', - overflow: 'auto', - }, + card: { + marginTop: '10%', + marginBottom: '10em', + width: '30em', + margin: '0 auto', + }, + title: { + fontSize: 24, + }, + textField: { + width: '100%', + }, + button: { + marginTop: '1em', + }, + container: { + width: '100%', + height: '100%', + overflow: 'auto', + }, } function mapStateToProps(state) { - return { - sessionActive: state.sessionActive, - } + return { + sessionActive: state.sessionActive, + } } function mapDispatchToProps(dispatch) { - return { - login: (user, password) => dispatch(actions.login(user, password)), - } + return { + login: (user, password) => dispatch(actions.login(user, password)), + } } class WithLogin extends React.Component { - state = {} - - handleChange = name => event => { - this.setState({ - [name]: event.target.value, - }) - } + state = {} - keyPress(e){ - if(e.key === 'Enter'){ - this.login() + handleChange = name => event => { + this.setState({ + [name]: event.target.value, + }) } - } - - login = () => { - this.props.login(this.state.user, this.state.password) - } - - render() { - const classes = this.props.classes - - return ( - <div> - { - this.props.sessionActive && - <div> - {this.props.children} - </div> - } - { - !this.props.sessionActive && - <div className={classes.container}> - <Card className={classes.card}> - <CardContent> - <Typography className={classes.title}> - Login - </Typography> - <TextField - id="standard-name" - label="User" - className={classes.textField} - margin="normal" - autoFocus={true} - onKeyPress={ (e) => { this.keyPress(e) }} - onChange={this.handleChange('user')} - /> - <TextField - id="standard-password-input" - label="Password" - className={classes.textField} - type="password" - autoComplete="current-password" - margin="normal" - onKeyPress={ (e) => { this.keyPress(e) }} - onChange={this.handleChange('password')} - /> - <Button - onClick={ this.login } - color="primary" - className={classes.button} - variant="contained"> - Login - </Button> - - </CardContent> - </Card> - </div> + + keyPress(e) { + if (e.key === 'Enter') { + this.login() } + } + + login = () => { + this.props.login(this.state.user, this.state.password) + } - </div> - ) - } + render() { + const classes = this.props.classes + + return ( + <div> + { + this.props.sessionActive && + <div> + {this.props.children} + </div> + } + { + !this.props.sessionActive && + <div className={classes.container}> + <Card className={classes.card}> + <CardContent> + <Typography className={classes.title}> + Login + </Typography> + <TextField + id="standard-name" + label="User" + className={classes.textField} + margin="normal" + autoFocus={true} + onKeyPress={(e) => { + this.keyPress(e) + }} + onChange={this.handleChange('user')} + /> + <TextField + id="standard-password-input" + label="Password" + className={classes.textField} + type="password" + autoComplete="current-password" + margin="normal" + onKeyPress={(e) => { + this.keyPress(e) + }} + onChange={this.handleChange('password')} + /> + <Button + onClick={this.login} + color="primary" + className={classes.button} + variant="contained"> + Login + </Button> + + </CardContent> + </Card> + </div> + } + + </div> + ) + } } export default flow( - connect(mapStateToProps, mapDispatchToProps), - withStyles(styles) + connect(mapStateToProps, mapDispatchToProps), + withStyles(styles) )(WithLogin) diff --git a/openbis_ng_ui/src/components/database/EntityDetails.jsx b/openbis_ng_ui/src/components/database/EntityDetails.jsx index 0a7e97e6419..36c88fe3a76 100644 --- a/openbis_ng_ui/src/components/database/EntityDetails.jsx +++ b/openbis_ng_ui/src/components/database/EntityDetails.jsx @@ -1,6 +1,6 @@ import React from 'react' -import { connect } from 'react-redux' -import { withStyles } from '@material-ui/core/styles' +import {connect} from 'react-redux' +import {withStyles} from '@material-ui/core/styles' import Button from '@material-ui/core/Button' import Card from '@material-ui/core/Card' import CardHeader from '@material-ui/core/CardHeader' @@ -21,128 +21,128 @@ import actions from '../../reducer/actions.js' /* eslint-disable-next-line no-unused-vars */ const styles = theme => ({ - textField: { - width: '100%' - } + textField: { + width: '100%' + } }) function mapStateToProps(state) { - return { - dirtyEntities: state.dirtyEntities, - } + return { + dirtyEntities: state.dirtyEntities, + } } function mapDispatchToProps(dispatch) { - return { - setDirty: (entity, dirty) => dispatch(actions.setDirty(entity.permId.permId, dirty)), - saveEntity: (entity) => dispatch(actions.saveEntity(entity)), - } + return { + setDirty: (entity, dirty) => dispatch(actions.setDirty(entity.permId.permId, dirty)), + saveEntity: (entity) => dispatch(actions.saveEntity(entity)), + } } class EntityDetails extends React.Component { - constructor(props) { - super(props) - this.state = { - description: this.props.entity ? this.props.entity.description : '', - dirty: false + constructor(props) { + super(props) + this.state = { + description: this.props.entity ? this.props.entity.description : '', + dirty: false + } } - } - matches(value1, value2) { - if (value1 === null || value1.length === 0) { - return value2 === null || value2.length === 0 + matches(value1, value2) { + if (value1 === null || value1.length === 0) { + return value2 === null || value2.length === 0 + } + return value1 === value2 } - return value1 === value2 - } - - handleChange(name, e) { - const currentlyDirty = !this.matches(e.target.value, this.props.entity.description) - const stateDirty = this.props.dirtyEntities.indexOf(this.props.entity.permId.permId) > -1 - if (currentlyDirty !== stateDirty) { - this.props.setDirty(this.props.entity, currentlyDirty) + + handleChange(name, e) { + const currentlyDirty = !this.matches(e.target.value, this.props.entity.description) + const stateDirty = this.props.dirtyEntities.indexOf(this.props.entity.permId.permId) > -1 + if (currentlyDirty !== stateDirty) { + this.props.setDirty(this.props.entity, currentlyDirty) + } + this.setState({ + description: e.target.value, + dirty: currentlyDirty + }) } - this.setState({ - description: e.target.value, - dirty: currentlyDirty - }) - } - - handleSave(entity) { - const entityCopy = _.cloneDeep(entity) - entityCopy.description = this.state.description - this.props.saveEntity(entityCopy) - } - - render() { - const { classes } = this.props - if (this.props.entity === null) { - return (<div />) + + handleSave(entity) { + const entityCopy = _.cloneDeep(entity) + entityCopy.description = this.state.description + this.props.saveEntity(entityCopy) } - const entity = this.props.entity - - const options = { - weekday: 'long', - year: 'numeric', - month: 'long', - day: 'numeric', - hour: '2-digit', - minute: '2-digit', - second: '2-digit' + + render() { + const {classes} = this.props + if (this.props.entity === null) { + return (<div/>) + } + const entity = this.props.entity + + const options = { + weekday: 'long', + year: 'numeric', + month: 'long', + day: 'numeric', + hour: '2-digit', + minute: '2-digit', + second: '2-digit' + } + const created = new Date(entity.registrationDate).toLocaleDateString('en-US', options) + + return ( + <Card className={classes.card}> + <CardHeader + avatar={ + <Avatar aria-label="Recipe" className={classes.avatar}> + S + </Avatar> + } + action={ + <Button + color="primary" + disabled={!this.state.dirty} + variant="contained" + onClick={() => this.handleSave(this.props.entity)}> + Save + </Button> + } + title={'Space ' + entity.code} + subheader={'Created on ' + created} + /> + <CardContent> + <TextField + label='Description' + className={classes.textField} + value={this.state.description ? this.state.description : ''} + onChange={(e) => { + this.handleChange('description', e) + }} + margin='normal' + /> + <OpenBISTable/> + + </CardContent> + <CardActions disableActionSpacing> + <IconButton aria-label="Add to favorites"> + <FavoriteIcon/> + </IconButton> + <IconButton aria-label="Share"> + <ShareIcon/> + </IconButton> + <IconButton> + <ExpandMoreIcon/> + </IconButton> + </CardActions> + </Card> + ) } - const created = new Date(entity.registrationDate).toLocaleDateString('en-US', options) - - return( - <Card className={classes.card}> - <CardHeader - avatar={ - <Avatar aria-label="Recipe" className={classes.avatar}> - S - </Avatar> - } - action={ - <Button - color="primary" - disabled={!this.state.dirty} - variant="contained" - onClick={ () => this.handleSave(this.props.entity) }> - Save - </Button> - } - title={ 'Space '+entity.code } - subheader={ 'Created on ' + created } - /> - <CardContent> - <TextField - label='Description' - className={classes.textField} - value={this.state.description ? this.state.description : ''} - onChange={ (e) => { - this.handleChange('description', e) - }} - margin='normal' - /> - <OpenBISTable /> - - </CardContent> - <CardActions disableActionSpacing> - <IconButton aria-label="Add to favorites"> - <FavoriteIcon /> - </IconButton> - <IconButton aria-label="Share"> - <ShareIcon /> - </IconButton> - <IconButton> - <ExpandMoreIcon /> - </IconButton> - </CardActions> - </Card> - ) - } } EntityDetails.propTypes = { - entity: PropTypes.any.isRequired, + entity: PropTypes.any.isRequired, } export default connect(mapStateToProps, mapDispatchToProps)(withStyles(styles)(EntityDetails)) diff --git a/openbis_ng_ui/src/components/database/OpenBISTable.jsx b/openbis_ng_ui/src/components/database/OpenBISTable.jsx index 7dd715bfc41..25a4d87b912 100644 --- a/openbis_ng_ui/src/components/database/OpenBISTable.jsx +++ b/openbis_ng_ui/src/components/database/OpenBISTable.jsx @@ -1,6 +1,6 @@ import React from 'react' -import { connect } from 'react-redux' -import { withStyles } from '@material-ui/core/styles' +import {connect} from 'react-redux' +import {withStyles} from '@material-ui/core/styles' import Table from '@material-ui/core/Table' import TableBody from '@material-ui/core/TableBody' import TableCell from '@material-ui/core/TableCell' @@ -20,100 +20,101 @@ import actions from '../../reducer/actions.js' /* eslint-disable-next-line no-unused-vars */ const styles = theme => ({ - table: { - marginTop: 30, - overflowX: 'auto' - }, - headerCell: { - backgroundColor: theme.palette.grey[300], - } + table: { + marginTop: 30, + overflowX: 'auto' + }, + headerCell: { + backgroundColor: theme.palette.grey[300], + } }) function mapStateToProps(state) { - const table = state.database.table - return { - spaces: state.database.spaces, - columns: table.columns, - data: table.data, - page: table.page, - sortColumn: table.sortColumn, - sortDirection: table.sortDirection, - filter: table.filter - } + const table = state.database.table + return { + spaces: state.database.spaces, + columns: table.columns, + data: table.data, + page: table.page, + sortColumn: table.sortColumn, + sortDirection: table.sortDirection, + filter: table.filter + } } function mapDispatchToProps(dispatch) { - return { - selectEntity: e => dispatch(actions.selectEntity(e)), - changePage: page => dispatch(actions.changePage(page)), - sortBy: column => dispatch(actions.sortBy(column)), - setFilter: filter => dispatch(actions.setFilter(filter)) - } + return { + selectEntity: e => dispatch(actions.selectEntity(e)), + changePage: page => dispatch(actions.changePage(page)), + sortBy: column => dispatch(actions.sortBy(column)), + setFilter: filter => dispatch(actions.setFilter(filter)) + } } class OpenBISTable extends React.Component { - render() { - const { data, columns, classes, page } = this.props - return ( - <Paper className={classes.table}> - <Table padding='checkbox'> - <TableHead> - <TableRow> - <TableCell className={classes.headerCell} variant='head'/> - { Object.entries(columns).map(([column, type]) => - <TableCell key={column} className={classes.headerCell} numeric={type === 'int'} variant='head'> - <TableSortLabel - className={classes.headerCell} - active={ this.props.sortColumn === column } - direction={ this.props.sortDirection } - onClick={() => this.props.sortBy(column)} > - { column } - </TableSortLabel> - </TableCell> - )} - </TableRow> - </TableHead> - <TableBody> - { - data.slice(page * 10, page * 10 + 10).map(row => - <OpenBISTableRow key = { row.Id } row = { row } columns = { columns } /> - ) - } - </TableBody> - <TableFooter className={classes.headerCell}> - <TableRow> - <TableCell> - <div - style = {{ cursor: 'pointer' }}> - <SettingsIcon/> - </div> - </TableCell> - <TableCell colSpan={2}> - <TextField - onChange = { e => this.props.setFilter(e.target.value) } - placeholder = "filter rows by value" - InputProps={{ - startAdornment: ( - <InputAdornment position="start" variant="outlined"> - <FilterIcon /> - </InputAdornment> - ), - }}/> - </TableCell> - <TablePagination - count={data.length} - rowsPerPage={10} - rowsPerPageOptions={[10]} - page={page} - onChangePage={(_, page) => this.props.changePage(page)} - /> - </TableRow> - </TableFooter> - </Table> - </Paper> - ) - } + render() { + const {data, columns, classes, page} = this.props + return ( + <Paper className={classes.table}> + <Table padding='checkbox'> + <TableHead> + <TableRow> + <TableCell className={classes.headerCell} variant='head'/> + {Object.entries(columns).map(([column, type]) => + <TableCell key={column} className={classes.headerCell} numeric={type === 'int'} + variant='head'> + <TableSortLabel + className={classes.headerCell} + active={this.props.sortColumn === column} + direction={this.props.sortDirection} + onClick={() => this.props.sortBy(column)}> + {column} + </TableSortLabel> + </TableCell> + )} + </TableRow> + </TableHead> + <TableBody> + { + data.slice(page * 10, page * 10 + 10).map(row => + <OpenBISTableRow key={row.Id} row={row} columns={columns}/> + ) + } + </TableBody> + <TableFooter className={classes.headerCell}> + <TableRow> + <TableCell> + <div + style={{cursor: 'pointer'}}> + <SettingsIcon/> + </div> + </TableCell> + <TableCell colSpan={2}> + <TextField + onChange={e => this.props.setFilter(e.target.value)} + placeholder="filter rows by value" + InputProps={{ + startAdornment: ( + <InputAdornment position="start" variant="outlined"> + <FilterIcon/> + </InputAdornment> + ), + }}/> + </TableCell> + <TablePagination + count={data.length} + rowsPerPage={10} + rowsPerPageOptions={[10]} + page={page} + onChangePage={(_, page) => this.props.changePage(page)} + /> + </TableRow> + </TableFooter> + </Table> + </Paper> + ) + } } export default connect(mapStateToProps, mapDispatchToProps)(withStyles(styles)(OpenBISTable)) diff --git a/openbis_ng_ui/src/components/database/OpenBISTableRow.jsx b/openbis_ng_ui/src/components/database/OpenBISTableRow.jsx index 57cc32ac4ec..4275cfa8ec1 100644 --- a/openbis_ng_ui/src/components/database/OpenBISTableRow.jsx +++ b/openbis_ng_ui/src/components/database/OpenBISTableRow.jsx @@ -1,93 +1,93 @@ import React from 'react' import ReactDOM from 'react-dom' -import { connect } from 'react-redux' +import {connect} from 'react-redux' import TableCell from '@material-ui/core/TableCell' import TableRow from '@material-ui/core/TableRow' -import { DragSource } from 'react-dnd' -import { DropTarget } from 'react-dnd' +import {DragSource} from 'react-dnd' +import {DropTarget} from 'react-dnd' import DragHandle from '@material-ui/icons/DragHandle' import actions from '../../reducer/actions.js' function targetCollect(connect, monitor) { - return { - connectDropTarget: connect.dropTarget() - } + return { + connectDropTarget: connect.dropTarget() + } } function sourceCollect(connect, monitor) { - return { - connectDragSource: connect.dragSource(), - isDragging: monitor.isDragging(), - connectDragPreview: connect.dragPreview() - } + return { + connectDragSource: connect.dragSource(), + isDragging: monitor.isDragging(), + connectDragPreview: connect.dragPreview() + } } const source = { - beginDrag(props, monitor, component) { - return { - id: props.row.Id + beginDrag(props, monitor, component) { + return { + id: props.row.Id + } + }, + endDrag(props, monitor, component) { + monitor.getDropResult().fire() } - }, - endDrag(props, monitor, component) { - monitor.getDropResult().fire() - } } const target = { - drop(props, monitor, component) { - return { - source: monitor.getItem(), - target: { - id: props.row.Id - }, - fire: () => props.moveEntity(monitor.getItem().id, props.row.Id) + drop(props, monitor, component) { + return { + source: monitor.getItem(), + target: { + id: props.row.Id + }, + fire: () => props.moveEntity(monitor.getItem().id, props.row.Id) + } } - } } function mapStateToProps(state) { - return { - spaces: state.database.spaces, - } + return { + spaces: state.database.spaces, + } } function mapDispatchToProps(dispatch) { - return { - selectEntity: e => dispatch(actions.selectEntity(e)), - moveEntity: (source, target) => dispatch(actions.moveEntity(source, target)) - } + return { + selectEntity: e => dispatch(actions.selectEntity(e)), + moveEntity: (source, target) => dispatch(actions.moveEntity(source, target)) + } } class OpenBISTableRow extends React.Component { - constructor(props) { - super(props) - } + constructor(props) { + super(props) + } - render() { - const row = this.props.row - return ( - <TableRow - key ={ row.Id } - ref = { instance => { - this.props.connectDragPreview(ReactDOM.findDOMNode(instance)) - this.props.connectDropTarget(ReactDOM.findDOMNode(instance)) - }}> - <TableCell> - <div - style = {{ cursor: 'pointer' }} - ref = { instance => this.props.connectDragSource(ReactDOM.findDOMNode(instance)) }> - <DragHandle/> - </div> - </TableCell> - { Object.entries(this.props.columns).map(([col, type]) => - <TableCell key={col} numeric={type === 'int'}> { row[col] }</TableCell> - )} - </TableRow> - ) - } + render() { + const row = this.props.row + return ( + <TableRow + key={row.Id} + ref={instance => { + this.props.connectDragPreview(ReactDOM.findDOMNode(instance)) + this.props.connectDropTarget(ReactDOM.findDOMNode(instance)) + }}> + <TableCell> + <div + style={{cursor: 'pointer'}} + ref={instance => this.props.connectDragSource(ReactDOM.findDOMNode(instance))}> + <DragHandle/> + </div> + </TableCell> + {Object.entries(this.props.columns).map(([col, type]) => + <TableCell key={col} numeric={type === 'int'}> {row[col]}</TableCell> + )} + </TableRow> + ) + } } export default connect(mapStateToProps, mapDispatchToProps)(DropTarget('row', target, targetCollect)(DragSource('row', source, sourceCollect)(OpenBISTableRow))) diff --git a/openbis_ng_ui/src/index.js b/openbis_ng_ui/src/index.js index 2f67930c467..27a9ea7dbc2 100644 --- a/openbis_ng_ui/src/index.js +++ b/openbis_ng_ui/src/index.js @@ -2,11 +2,11 @@ import "regenerator-runtime/runtime" import React from 'react' import ReactDOM from 'react-dom' -import { createStore, applyMiddleware, compose } from 'redux' -import { Provider } from 'react-redux' +import {createStore, applyMiddleware, compose} from 'redux' +import {Provider} from 'react-redux' import createSagaMiddleware from 'redux-saga' import reducer from './reducer/reducer.js' -import { watchActions } from './reducer/sagas' +import {watchActions} from './reducer/sagas' const sagaMiddleware = createSagaMiddleware() const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose; @@ -15,29 +15,29 @@ export const store = createStore(reducer, composeEnhancers(applyMiddleware(sagaM sagaMiddleware.run(watchActions) const render = () => { - const App = require('./components/App.jsx').default - const WithLogin = require('./components/WithLogin.jsx').default - const WithLoader = require('./components/WithLoader.jsx').default - const WithError = require('./components/WithError.jsx').default - ReactDOM.render( - <Provider store = { store }> - <WithLoader> - <WithError> - <WithLogin> - <App /> - </WithLogin> - </WithError> - </WithLoader> - </Provider>, - document.getElementById("app") - ) + const App = require('./components/App.jsx').default + const WithLogin = require('./components/WithLogin.jsx').default + const WithLoader = require('./components/WithLoader.jsx').default + const WithError = require('./components/WithError.jsx').default + ReactDOM.render( + <Provider store={store}> + <WithLoader> + <WithError> + <WithLogin> + <App/> + </WithLogin> + </WithError> + </WithLoader> + </Provider>, + document.getElementById("app") + ) } if (module.hot) { - module.hot.accept('./components/App.jsx', () => setTimeout(render)) - module.hot.accept('./reducer/reducer.js', () => { - const nextRootReducer = require('./reducer/reducer.js').default - store.replaceReducer(nextRootReducer) - }); + module.hot.accept('./components/App.jsx', () => setTimeout(render)) + module.hot.accept('./reducer/reducer.js', () => { + const nextRootReducer = require('./reducer/reducer.js').default + store.replaceReducer(nextRootReducer) + }); } render() \ No newline at end of file diff --git a/openbis_ng_ui/src/profile.js b/openbis_ng_ui/src/profile.js index cc4eef0850e..278d465d8ba 100644 --- a/openbis_ng_ui/src/profile.js +++ b/openbis_ng_ui/src/profile.js @@ -1,5 +1,5 @@ let profile = { - devEmail: 'dummy.address@test.ch' + devEmail: 'dummy.address@test.ch' } export default profile \ No newline at end of file diff --git a/openbis_ng_ui/src/reducer/actions.js b/openbis_ng_ui/src/reducer/actions.js index c7934cad243..67ebe161ea6 100644 --- a/openbis_ng_ui/src/reducer/actions.js +++ b/openbis_ng_ui/src/reducer/actions.js @@ -1,84 +1,93 @@ export default { - init: () => ({ - type: 'INIT', - }), - // TODO setDirty for generic tabs instead of entities - setDirty: (entityPermId, dirty) => ({ - type: 'SET-DIRTY', - entityPermId: entityPermId, - dirty: dirty - }), - expandNode: (node) => ({ - type: 'EXPAND-NODE', - node: node - }), - collapseNode: (node) => ({ - type: 'COLLAPSE-NODE', - node: node - }), - // database stuff - setSpaces: spaces => ({ - type: 'SET-SPACES', - spaces: spaces, - }), - setProjects: (projects, spacePermId) => ({ - type: 'SET-PROJECTS', - projects: projects, - spacePermId: spacePermId - }), - selectEntity: entityPermId => ({ - type: 'SELECT-ENTITY', - entityPermId: entityPermId - }), - closeEntity: entityPermId => ({ - type: 'CLOSE-ENTITY', - entityPermId: entityPermId - }), - changePage: page => ({ - type: 'CHANGE-PAGE', - page: page - }), - sortBy: column => ({ - type: 'SORT-BY', - column: column - }), - setFilter: filter => ({ - type: 'SET-FILTER', - value: filter - }), - moveEntity: (source, target) => ({ - type: 'MOVE-ENTITY', - source: source, - target: target - }), - saveEntity: (entity) => ({ - type: 'SAVE-ENTITY', - entity: entity - }), - savedEntity: (entity) => ({ - type: 'SAVED-ENTITY', - entity: entity - }), - error: (exception) => ({ - type: 'ERROR', - exception: exception - }), - closeError: () => ({ - type: 'CLOSE-ERROR', - }), - // session - login: (username, password) => ({ - type: 'LOGIN', - username: username, - password: password, - }), - loginDone: () => ({ - type: 'LOGIN-DONE', - }), - logout: () => ({ - type: 'LOGOUT' - }), - logoutDone: () => ({ - type: 'LOGOUT-DONE', - }), + init: () => ({ + type: 'INIT', + }), + // TODO setDirty for generic tabs instead of entities + setDirty: (entityPermId, dirty) => ({ + type: 'SET-DIRTY', + entityPermId: entityPermId, + dirty: dirty + }), + expandNode: (node) => ({ + type: 'EXPAND-NODE', + node: node + }), + collapseNode: (node) => ({ + type: 'COLLAPSE-NODE', + node: node + }), + // database stuff + setSpaces: spaces => ({ + type: 'SET-SPACES', + spaces: spaces, + }), + setProjects: (projects, spacePermId) => ({ + type: 'SET-PROJECTS', + projects: projects, + spacePermId: spacePermId + }), + selectEntity: entityPermId => ({ + type: 'SELECT-ENTITY', + entityPermId: entityPermId + }), + closeEntity: entityPermId => ({ + type: 'CLOSE-ENTITY', + entityPermId: entityPermId + }), + changePage: page => ({ + type: 'CHANGE-PAGE', + page: page + }), + sortBy: column => ({ + type: 'SORT-BY', + column: column + }), + setFilter: filter => ({ + type: 'SET-FILTER', + value: filter + }), + setMode: mode => ({ + type: 'SET-MODE', + mode + }), + setModeDone: (mode, data) => ({ + type: 'SET-MODE-DONE', + mode, + data + }), + moveEntity: (source, target) => ({ + type: 'MOVE-ENTITY', + source: source, + target: target + }), + saveEntity: (entity) => ({ + type: 'SAVE-ENTITY', + entity: entity + }), + savedEntity: (entity) => ({ + type: 'SAVED-ENTITY', + entity: entity + }), + error: (exception) => ({ + type: 'ERROR', + exception: exception + }), + closeError: () => ({ + type: 'CLOSE-ERROR', + }), + // session + login: (username, password) => ({ + type: 'LOGIN', + username: username, + password: password, + }), + loginDone: () => ({ + type: 'LOGIN-DONE', + }), + logout: () => ({ + type: 'LOGOUT' + }), + logoutDone: () => ({ + type: 'LOGOUT-DONE', + }), } diff --git a/openbis_ng_ui/src/reducer/database/reducer.js b/openbis_ng_ui/src/reducer/database/reducer.js index 345a5a270a5..61799136a0a 100644 --- a/openbis_ng_ui/src/reducer/database/reducer.js +++ b/openbis_ng_ui/src/reducer/database/reducer.js @@ -2,52 +2,52 @@ import initialState from '../initialstate.js' function filterOf(filter, columns) { - if (filter == null || filter.length === 0) { - return _ => true - } else { - return entity => - Object.keys(columns) - .map(col => entity[col]) - .map(value => value != null && value.toString().includes(filter)) - .includes(true) - } + if (filter == null || filter.length === 0) { + return _ => true + } else { + return entity => + Object.keys(columns) + .map(col => entity[col]) + .map(value => value != null && value.toString().includes(filter)) + .includes(true) + } } function sortOf(orderColumn, direction, columns) { - return (a, b) => { - let valA = a[orderColumn] - let valB = b[orderColumn] - if (valA == null && valB == null) { - return 0 - } - if (valA == null) { - return 1 - } - if (valB == null) { - return -1 + return (a, b) => { + let valA = a[orderColumn] + let valB = b[orderColumn] + if (valA == null && valB == null) { + return 0 + } + if (valA == null) { + return 1 + } + if (valB == null) { + return -1 + } + const type = columns[orderColumn] + if (type === 'int') { + return direction === 'asc' ? valA - valB : valB - valA + } else { + if (valA < valB) { + return direction === 'asc' ? -1 : 1 + } else if (valA > valB) { + return direction === 'asc' ? 1 : -1 + } else { + return 0 + } + } } - const type = columns[orderColumn] - if (type === 'int') { - return direction === 'asc' ? valA - valB : valB - valA - } else { - if (valA < valB){ - return direction === 'asc' ? -1 : 1 - } else if (valA > valB) { - return direction === 'asc' ? 1 : -1 - } else { - return 0 - } - } - } } function transformData(data, columns, filter, orderColumn, direction) { - const filtered = data.filter(filterOf(filter, columns)) - if (orderColumn in columns) { - return filtered.sort(sortOf(orderColumn, direction, columns)) - } else { - return filtered - } + const filtered = data.filter(filterOf(filter, columns)) + if (orderColumn in columns) { + return filtered.sort(sortOf(orderColumn, direction, columns)) + } else { + return filtered + } } const concat = (...arrays) => [].concat(...arrays) @@ -55,91 +55,94 @@ const take = (array, n) => array.slice(0, n) const drop = (array, n) => array.slice(n) const remove = (array, index) => [...take(array, index), ...drop(array, index + 1)] -function move (array, oldIndex, newIndex) { - const value = array[oldIndex] - const removed = remove(array, oldIndex) - return concat(take(removed, newIndex), [value], drop(removed, newIndex)) +function move(array, oldIndex, newIndex) { + const value = array[oldIndex] + const removed = remove(array, oldIndex) + return concat(take(removed, newIndex), [value], drop(removed, newIndex)) } function entitiesByPermId(entities) { - const byPermId = {} - for (let entity of entities) { - byPermId[entity.permId.permId] = entity - } - return byPermId + const byPermId = {} + for (let entity of entities) { + byPermId[entity.permId.permId] = entity + } + return byPermId } function spaces(spaces = initialState.spaces, action) { - switch (action.type) { - case 'SET-SPACES': { - return entitiesByPermId(action.spaces) - } - case 'SAVED-ENTITY': { - const newSpaces = Object.assign({}, spaces) - newSpaces[action.entity.permId.permId] = action.entity - return newSpaces - } - default: { - return spaces - }} + switch (action.type) { + case 'SET-SPACES': { + return entitiesByPermId(action.spaces) + } + case 'SAVED-ENTITY': { + const newSpaces = Object.assign({}, spaces) + newSpaces[action.entity.permId.permId] = action.entity + return newSpaces + } + default: { + return spaces + } + } } function projects(projects = initialState.projects, action) { - switch (action.type) { - case 'SET-PROJECTS': { - return entitiesByPermId(action.projects) - } - default: { - return projects - }} + switch (action.type) { + case 'SET-PROJECTS': { + return entitiesByPermId(action.projects) + } + default: { + return projects + } + } } function table(table = initialState.table, action) { - switch (action.type) { - case 'CHANGE-PAGE': { - return Object.assign({}, table, { page: action.page }) - } - case 'SORT-BY': { - const column = action.column - const direction = - column === table.sortColumn - ? table.sortDirection === 'asc' ? 'desc' : 'asc' - : 'asc' - return Object.assign({}, table, { - sortColumn: column, - sortDirection: direction, - page: 0, - data: transformData(table.initialData, table.columns, table.filter, column, direction) - }) - } - case 'SET-FILTER': { - return Object.assign({}, table, { - page: 0, - filter: action.value, - data: transformData(table.initialData, table.columns, action.value, table.sortColumn, table.sortDirection) - }) - } - case 'MOVE-ENTITY': { - const sourceIndex = table.data.findIndex(e => e.Id === action.source) - const targetIndex = table.data.findIndex(e => e.Id === action.target) - return Object.assign({}, table, { - data: move(table.data, sourceIndex, targetIndex) - }) - } - default: { - return table - }} + switch (action.type) { + case 'CHANGE-PAGE': { + return Object.assign({}, table, {page: action.page}) + } + case 'SORT-BY': { + const column = action.column + const direction = + column === table.sortColumn + ? table.sortDirection === 'asc' ? 'desc' : 'asc' + : 'asc' + return Object.assign({}, table, { + sortColumn: column, + sortDirection: direction, + page: 0, + data: transformData(table.initialData, table.columns, table.filter, column, direction) + }) + } + case 'SET-FILTER': { + return Object.assign({}, table, { + page: 0, + filter: action.value, + data: transformData(table.initialData, table.columns, action.value, table.sortColumn, table.sortDirection) + }) + } + case 'MOVE-ENTITY': { + const sourceIndex = table.data.findIndex(e => e.Id === action.source) + const targetIndex = table.data.findIndex(e => e.Id === action.target) + return Object.assign({}, table, { + data: move(table.data, sourceIndex, targetIndex) + }) + } + default: { + return table + } + } } export default function database(database = initialState.database, action) { - return { - spaces: spaces(database.spaces, action), - projects: projects(database.projects, action), - table: table(database.table, action), - } + return { + spaces: spaces(database.spaces, action), + projects: projects(database.projects, action), + table: table(database.table, action), + } } diff --git a/openbis_ng_ui/src/reducer/initialstate.js b/openbis_ng_ui/src/reducer/initialstate.js index f3e4db2a8e1..76c9cba6841 100644 --- a/openbis_ng_ui/src/reducer/initialstate.js +++ b/openbis_ng_ui/src/reducer/initialstate.js @@ -1,11 +1,10 @@ - function foldLeft(array, acc, reducer) { - if (array.length === 0) { - return acc - } else { - const [head, ...tail] = array - return foldLeft(tail, reducer(acc, head), reducer) - } + if (array.length === 0) { + return acc + } else { + const [head, ...tail] = array + return foldLeft(tail, reducer(acc, head), reducer) + } } const take = (array, n) => array.slice(0, n) @@ -18,60 +17,73 @@ const intColumns = ['Weight', 'Length', 'Width'] function shuffle(arr) { - return arr.slice(0).sort((a, b) => Math.random() * 3 - 1.5) + return arr.slice(0).sort((a, b) => Math.random() * 3 - 1.5) } function randomString() { - return Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15) + return Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15) } function createEntity(id) { - const numStrings = Math.random() * 3 - const numInts = Math.random() * 3 - let entity = { Id: id } - entity = foldLeft(take(shuffle(stringColumns), numStrings), entity, (acc, a) => Object.assign({}, acc, { [a] : randomString() })) - entity = foldLeft(take(shuffle(intColumns), numInts), entity, (acc, a) => Object.assign({}, acc, { [a] : Math.floor(Math.random() * 100) })) - return entity + const numStrings = Math.random() * 3 + const numInts = Math.random() * 3 + let entity = {Id: id} + entity = foldLeft(take(shuffle(stringColumns), numStrings), entity, (acc, a) => Object.assign({}, acc, {[a]: randomString()})) + entity = foldLeft(take(shuffle(intColumns), numInts), entity, (acc, a) => Object.assign({}, acc, {[a]: Math.floor(Math.random() * 100)})) + return entity } const data = [...Array(10000).keys()].map(i => createEntity(i + 1)) -let tableColumns = foldLeft(stringColumns, {}, (acc, a) => Object.assign({}, acc, { [a]: 'string'})) -tableColumns = foldLeft(intColumns, tableColumns, (acc, a) => Object.assign({}, acc, { [a]: 'int'})) -tableColumns = foldLeft(shuffle(Object.entries(tableColumns)), { Id: 'int' }, (acc, a) => Object.assign({}, acc, { [a[0]] : a[1] })) +let tableColumns = foldLeft(stringColumns, {}, (acc, a) => Object.assign({}, acc, {[a]: 'string'})) +tableColumns = foldLeft(intColumns, tableColumns, (acc, a) => Object.assign({}, acc, {[a]: 'int'})) +tableColumns = foldLeft(shuffle(Object.entries(tableColumns)), {Id: 'int'}, (acc, a) => Object.assign({}, acc, {[a[0]]: a[1]})) // TODO split initialstate when it becomes too big export default { - sessionActive: false, - - database: { - spaces: {}, - projects: {}, - table: { - initialData: data, - data: data, - columns: tableColumns, - page: 0, - sortColumn: '', - sortDirection: 'asc' + sessionActive: false, + mode: 'DATABASE', + + database: { + spaces: {}, + projects: {}, + table: { + initialData: data, + data: data, + columns: tableColumns, + page: 0, + sortColumn: '', + sortDirection: 'asc' + }, + }, + + types: { + browser: { + selectedNodeId: null, + nodes: [] + } + }, + + users: { + browser: { + selectedNodeId: null, + nodes: [] + } }, - }, - - types: {}, - users: {}, - favourites: {}, - tools: {}, - - // TODO replace with generic tab description - openEntities: { - entities: [], - selectedEntity: null, - }, - dirtyEntities: [], - - loading: false, - // TODO generic tree - databaseTreeNodes: [], - exceptions: [], + + favourites: {}, + tools: {}, + + // TODO replace with generic tab description + openEntities: { + entities: [], + selectedEntity: null, + }, + dirtyEntities: [], + + loading: false, + // TODO generic tree + databaseTreeNodes: [], + exceptions: [], } \ No newline at end of file diff --git a/openbis_ng_ui/src/reducer/reducer.js b/openbis_ng_ui/src/reducer/reducer.js index d68df384ba4..57109574ac1 100644 --- a/openbis_ng_ui/src/reducer/reducer.js +++ b/openbis_ng_ui/src/reducer/reducer.js @@ -1,171 +1,197 @@ import merge from 'lodash/merge' import initialState from './initialstate.js' import database from './database/reducer.js' +import users from './users/reducer.js' +import types from './types/reducer.js' function replaceNode(nodes, newNode) { - return nodes.map( node => { - if (node.id === newNode.id) { - return newNode - } - return node - }) + return nodes.map(node => { + if (node.id === newNode.id) { + return newNode + } + return node + }) } function asTreeNode(entity) { - return { - id: entity.permId.permId, - type: entity['@type'], - expanded: false, - loading: false, - loaded: false, - children: [], - } + return { + id: entity.permId.permId, + type: entity['@type'], + expanded: false, + loading: false, + loaded: false, + children: [], + } } // reducers function loading(loading = initialState.loading, action) { - switch (action.type) { - case 'SET-SPACES': { - return false - } - case 'SAVE-ENTITY': { - return true - } - case 'SAVED-ENTITY': { - return false - } - case 'ERROR': { - return false - } - case 'LOGIN': { - return true - } - case 'LOGOUT': { - return true - } - case 'LOGOUT-DONE': { - return false - } - default: { - return loading - }} + switch (action.type) { + case 'SET-SPACES': { + return false + } + case 'SAVE-ENTITY': { + return true + } + case 'SAVED-ENTITY': { + return false + } + case 'ERROR': { + return false + } + case 'LOGIN': { + return true + } + case 'LOGOUT': { + return true + } + case 'LOGOUT-DONE': { + return false + } + default: { + return loading + } + } } function databaseTreeNodes(databaseTreeNodes = initialState.databaseTreeNodes, action) { - switch (action.type) { - case 'SET-SPACES': { - return action.spaces.map(asTreeNode) - } - case 'SET-PROJECTS': { - const oldNode = databaseTreeNodes.filter( node => node.id === action.spacePermId )[0] - const projectNodes = action.projects.map(asTreeNode) - const node = merge({}, oldNode, { loading: false, loaded: true, children: projectNodes }) - return replaceNode(databaseTreeNodes, node) - } - case 'EXPAND-NODE': { - const loading = action.node.loaded === false - const node = merge({}, action.node, { expanded: true, loading: loading }) - return replaceNode(databaseTreeNodes, node) - } - case 'COLLAPSE-NODE': { - const node = merge({}, action.node, { expanded: false }) - return replaceNode(databaseTreeNodes, node) - } - default: { - return databaseTreeNodes - }} + switch (action.type) { + case 'SET-SPACES': { + return action.spaces.map(asTreeNode) + } + case 'SET-PROJECTS': { + const oldNode = databaseTreeNodes.filter(node => node.id === action.spacePermId)[0] + const projectNodes = action.projects.map(asTreeNode).map(project => { + return merge(project, {loaded: true}) + }) + const node = merge({}, oldNode, {loading: false, loaded: true, children: projectNodes}) + return replaceNode(databaseTreeNodes, node) + } + case 'EXPAND-NODE': { + const loading = action.node.loaded === false + const node = merge({}, action.node, {expanded: true, loading: loading}) + return replaceNode(databaseTreeNodes, node) + } + case 'COLLAPSE-NODE': { + const node = merge({}, action.node, {expanded: false}) + return replaceNode(databaseTreeNodes, node) + } + default: { + return databaseTreeNodes + } + } } function openEntities(openEntities = initialState.openEntities, action) { - switch (action.type) { - case 'SELECT-ENTITY': { - const entities = openEntities.entities - return { - entities: entities.indexOf(action.entityPermId) > -1 ? entities : [].concat(entities, [action.entityPermId]), - selectedEntity: action.entityPermId, + switch (action.type) { + case 'SELECT-ENTITY': { + const entities = openEntities.entities + return { + entities: entities.indexOf(action.entityPermId) > -1 ? entities : [].concat(entities, [action.entityPermId]), + selectedEntity: action.entityPermId, + } + } + case 'CLOSE-ENTITY': { + const newOpenEntities = openEntities.entities.filter(e => e !== action.entityPermId) + if (openEntities.selectedEntity === action.entityPermId) { + const oldIndex = openEntities.entities.indexOf(action.entityPermId) + const newIndex = oldIndex === newOpenEntities.length ? oldIndex - 1 : oldIndex + const selectedEntity = newIndex > -1 ? newOpenEntities[newIndex] : null + return { + entities: newOpenEntities, + selectedEntity: selectedEntity, + } + } else { + return { + entities: newOpenEntities, + selectedEntity: openEntities.selectedEntity, + } + } + } + default: { + return openEntities + } } - } - case 'CLOSE-ENTITY': { - const newOpenEntities = openEntities.entities.filter(e => e !== action.entityPermId) - if (openEntities.selectedEntity === action.entityPermId) { - const oldIndex = openEntities.entities.indexOf(action.entityPermId) - const newIndex = oldIndex === newOpenEntities.length ? oldIndex - 1 : oldIndex - const selectedEntity = newIndex > -1 ? newOpenEntities[newIndex] : null - return { - entities: newOpenEntities, - selectedEntity: selectedEntity, - } - } else { - return { - entities: newOpenEntities, - selectedEntity: openEntities.selectedEntity, - } - } - } - default: { - return openEntities - }} } function dirtyEntities(dirtyEntities = initialState.dirtyEntities, action) { - switch (action.type) { - case 'SET-DIRTY': { - if (action.dirty) { - return [].concat(dirtyEntities, [action.entityPermId]) - } else { - return dirtyEntities.filter(e => e !== action.entityPermId) + switch (action.type) { + case 'SET-DIRTY': { + if (action.dirty) { + return [].concat(dirtyEntities, [action.entityPermId]) + } else { + return dirtyEntities.filter(e => e !== action.entityPermId) + } + } + case 'SAVED-ENTITY': { + return dirtyEntities.filter(permId => permId !== action.entity.permId.permId) + } + case 'CLOSE-ENTITY': { + return dirtyEntities.filter(permId => permId !== action.entityPermId) + } + default: { + return dirtyEntities + } } - } - case 'SAVED-ENTITY': { - return dirtyEntities.filter(permId => permId !== action.entity.permId.permId) - } - case 'CLOSE-ENTITY': { - return dirtyEntities.filter(permId => permId !== action.entityPermId) - } - default: { - return dirtyEntities - }} } function exceptions(exceptions = initialState.exceptions, action) { - switch (action.type) { - case 'ERROR': { - return [].concat(exceptions, [action.exception]) - } - case 'CLOSE-ERROR': { - return exceptions.slice(1) - } - default: { - return exceptions - }} + switch (action.type) { + case 'ERROR': { + return [].concat(exceptions, [action.exception]) + } + case 'CLOSE-ERROR': { + return exceptions.slice(1) + } + default: { + return exceptions + } + } } function sessionActive(sessionActive = initialState.sessionActive, action) { - switch(action.type) { - case 'LOGIN-DONE': { - return true - } - case 'LOGOUT-DONE': { - return false - } - default: { - return sessionActive - }} + switch (action.type) { + case 'LOGIN-DONE': { + return true + } + case 'LOGOUT-DONE': { + return false + } + default: { + return sessionActive + } + } +} + +function mode(mode = initialState.mode, action) { + switch (action.type) { + case 'SET-MODE-DONE' : { + return action.mode + } + default: { + return mode + } + } } function reducer(state = initialState, action) { - return { - sessionActive: sessionActive(state.sessionActive, action), - database: database(state.database, action), - loading: loading(state.loading, action), - databaseTreeNodes: databaseTreeNodes(state.databaseTreeNodes, action), - openEntities: openEntities(state.openEntities, action), - dirtyEntities: dirtyEntities(state.dirtyEntities, action), - exceptions: exceptions(state.exceptions, action), - } + let newMode = mode(state.mode, action); + + return { + sessionActive: sessionActive(state.sessionActive, action), + mode: newMode, + database: database(state.database, action), + users: newMode === 'USERS' ? users(state.users, action) : state.users, + types: newMode === 'TYPES' ? types(state.types, action) : state.types, + loading: loading(state.loading, action), + databaseTreeNodes: databaseTreeNodes(state.databaseTreeNodes, action), + openEntities: openEntities(state.openEntities, action), + dirtyEntities: dirtyEntities(state.dirtyEntities, action), + exceptions: exceptions(state.exceptions, action), + } } export default reducer diff --git a/openbis_ng_ui/src/reducer/sagas.js b/openbis_ng_ui/src/reducer/sagas.js index 1b2ccb6486d..0c6f21e3550 100644 --- a/openbis_ng_ui/src/reducer/sagas.js +++ b/openbis_ng_ui/src/reducer/sagas.js @@ -1,78 +1,119 @@ -import { put, takeEvery, call } from 'redux-saga/effects' +import {put, takeEvery, call} from 'redux-saga/effects' import openbis from '../services/openbis' import actions from './actions' // TODO split sagas function* init() { - // TODO we want to check if there is an active session here to avoid the login + // TODO we want to check if there is an active session here to avoid the login } function* loginDone() { - try { - const result = yield call(openbis.getSpaces) - yield put(actions.setSpaces(result.getObjects())) - } catch(exception) { - yield put(actions.error(exception)) - } + try { + const result = yield call(openbis.getSpaces) + yield put(actions.setSpaces(result.getObjects())) + } catch (exception) { + yield put(actions.error(exception)) + } } function* logout() { - try { - console.log('logout') - const result = yield call(openbis.logout) - yield put(actions.logoutDone()) - } catch(exception) { - yield put(actions.error(exception)) - } + try { + console.log('logout') + const result = yield call(openbis.logout) + yield put(actions.logoutDone()) + } catch (exception) { + yield put(actions.error(exception)) + } } function* selectSpace(action) { - yield put(actions.selectEntity(action.spaces[0].permId.permId)) + yield put(actions.selectEntity(action.spaces[0].permId.permId)) } function* saveEntity(action) { - try { - yield openbis.updateSpace(action.entity.permId, action.entity.description) - const result = yield call(openbis.getSpaces) - const spaces = result.getObjects() - const space = spaces.filter(space => space.permId.permId === action.entity.permId.permId)[0] - yield put(actions.savedEntity(space)) - } catch(exception) { - yield put(actions.error(exception)) - } + try { + yield openbis.updateSpace(action.entity.permId, action.entity.description) + const result = yield call(openbis.getSpaces) + const spaces = result.getObjects() + const space = spaces.filter(space => space.permId.permId === action.entity.permId.permId)[0] + yield put(actions.savedEntity(space)) + } catch (exception) { + yield put(actions.error(exception)) + } } function* expandNode(action) { - try { - const node = action.node - if (node.loaded === false) { - if (node.type === 'as.dto.space.Space') { - const result = yield openbis.searchProjects(node.id) - const projects = result.getObjects() - yield put(actions.setProjects(projects, node.id)) - } + try { + const node = action.node + if (node.loaded === false) { + if (node.type === 'as.dto.space.Space') { + const result = yield openbis.searchProjects(node.id) + const projects = result.getObjects() + yield put(actions.setProjects(projects, node.id)) + } + } + } catch (exception) { + yield put(actions.error(exception)) } - } catch(exception) { - yield put(actions.error(exception)) - } } export function* login(action) { - try { - const result = yield openbis.login(action.username, action.password) - yield put(actions.loginDone()) - } catch(exception) { - yield put(actions.error(exception)) - } + try { + const result = yield openbis.login(action.username, action.password) + yield put(actions.loginDone()) + } catch (exception) { + yield put(actions.error(exception)) + } +} + +function* setMode(action) { + try { + switch (action.mode) { + case 'USERS': { + let users = yield call(openbis.getUsers) + let groups = yield call(openbis.getGroups) + yield put(actions.setModeDone(action.mode, { + users: users.getObjects(), + groups: groups.getObjects() + })) + break + } + case 'TYPES': { + let objectTypes = yield call(openbis.getObjectTypes) + let collectionTypes = yield call(openbis.getCollectionTypes) + let dataSetTypes = yield call(openbis.getDataSetTypes) + let materialTypes = yield call(openbis.getMaterialTypes) + yield put(actions.setModeDone(action.mode, { + objectTypes: objectTypes.getObjects(), + collectionTypes: collectionTypes.getObjects(), + dataSetTypes: dataSetTypes.getObjects(), + materialTypes: materialTypes.getObjects(), + })) + break + } + default: { + yield put(actions.setModeDone(action.mode, {})) + break + } + } + } catch (exception) { + yield put(actions.error(exception)) + } +} + +function* log(action) { + console.log('action', action); } export function* watchActions() { - yield takeEvery('INIT', init) - yield takeEvery('LOGIN', login) - yield takeEvery('LOGIN-DONE', loginDone) - yield takeEvery('LOGOUT', logout) - yield takeEvery('SAVE-ENTITY', saveEntity) - yield takeEvery('SET-SPACES', selectSpace) - yield takeEvery('EXPAND-NODE', expandNode) + yield takeEvery('*', log) + yield takeEvery('INIT', init) + yield takeEvery('LOGIN', login) + yield takeEvery('LOGIN-DONE', loginDone) + yield takeEvery('LOGOUT', logout) + yield takeEvery('SAVE-ENTITY', saveEntity) + yield takeEvery('SET-SPACES', selectSpace) + yield takeEvery('EXPAND-NODE', expandNode) + yield takeEvery('SET-MODE', setMode) } diff --git a/openbis_ng_ui/src/reducer/types/reducer.js b/openbis_ng_ui/src/reducer/types/reducer.js new file mode 100644 index 00000000000..d8b8acce886 --- /dev/null +++ b/openbis_ng_ui/src/reducer/types/reducer.js @@ -0,0 +1,109 @@ +import initialState from '../initialstate.js' +import _ from 'lodash' + +export default function types(types = initialState.types, action) { + return { + browser: browser(types.browser, action), + } +} + +function browser(browser = initialState.types.browser, action) { + switch (action.type) { + case 'SET-MODE-DONE': + return browser_SetModeDone(browser, action) + case 'EXPAND-NODE': + return browser_ExpandNode(browser, action) + case 'COLLAPSE-NODE': + return browser_CollapseNode(browser, action) + default: + return browser + } +} + +function browser_SetModeDone(browser, action) { + return { + selectedNodeId: browser.selectedNodeId, + nodes: [ + browser_SetModeDone_TypeNodes('Object Types', action.data.objectTypes), + browser_SetModeDone_TypeNodes('Collection Types', action.data.collectionTypes), + browser_SetModeDone_TypeNodes('Data Set Types', action.data.dataSetTypes), + browser_SetModeDone_TypeNodes('Material Types', action.data.materialTypes) + ] + } +} + +function browser_SetModeDone_TypeNodes(groupId, types) { + let typeNodes = [] + + types.forEach(type => { + typeNodes.push({ + id: type.getPermId().getPermId(), + expanded: false, + loading: false, + loaded: true, + children: [] + }) + }) + + _sortById(typeNodes) + + return { + id: groupId, + expanded: false, + loading: false, + loaded: true, + children: typeNodes + } +} + +function browser_ExpandNode(browser, action) { + let newBrowser = _.cloneDeep(browser) + _visitNodes(newBrowser.nodes, node => { + if (node.id === action.node.id) { + node.expanded = true + } + }) + return newBrowser +} + +function browser_CollapseNode(browser, action) { + let newBrowser = _.cloneDeep(browser) + _visitNodes(newBrowser.nodes, node => { + if (node.id === action.node.id) { + node.expanded = false + } + }) + return newBrowser +} + +const _visitNodes = (nodes, visitor) => { + let toVisit = [] + let visited = {} + + toVisit.push(...nodes) + + while (toVisit.length > 0) { + let node = toVisit.shift() + + if (!visited[node.id]) { + visited[node.id] = true + let result = visitor(node) + if (result) { + return result + } + } + + if (node.children !== undefined) { + node.children.forEach((child) => { + toVisit.push(child) + }) + } + } +} + + +const _sortById = (arr) => { + arr.sort((i1, i2) => { + return i1.id.localeCompare(i2.id) + }) +} \ No newline at end of file diff --git a/openbis_ng_ui/src/reducer/users/reducer.js b/openbis_ng_ui/src/reducer/users/reducer.js new file mode 100644 index 00000000000..436a6a780ea --- /dev/null +++ b/openbis_ng_ui/src/reducer/users/reducer.js @@ -0,0 +1,161 @@ +import initialState from '../initialstate.js' +import _ from 'lodash' + +export default function users(users = initialState.users, action) { + return { + browser: browser(users.browser, action), + } +} + +function browser(browser = initialState.users.browser, action) { + switch (action.type) { + case 'SET-MODE-DONE': + return browser_SetModeDone(browser, action) + case 'EXPAND-NODE': + return browser_ExpandNode(browser, action) + case 'COLLAPSE-NODE': + return browser_CollapseNode(browser, action) + default: + return browser + } +} + +function browser_SetModeDone(browser, action) { + return { + selectedNodeId: browser.selectedNodeId, + nodes: [browser_SetModeDone_UserNodes(action.data.users, action.data.groups), browser_SetModeDone_GroupNodes(action.data.groups)] + } +} + +function browser_SetModeDone_UserNodes(users, groups) { + let userGroupsMap = {} + let userNodes = [] + + groups.forEach(group => { + group.getUsers().forEach(user => { + let userGroups = userGroupsMap[user.getPermId().getPermId()] || [] + userGroups.push(group) + userGroupsMap[user.getPermId().getPermId()] = userGroups + }) + }) + + users.forEach(user => { + let userGroups = userGroupsMap[user.getPermId().getPermId()] || [] + let groupNodes = [] + + userGroups.forEach(group => { + groupNodes.push({ + id: group.getPermId().getPermId(), + expanded: false, + loading: false, + loaded: true, + children: [] + }) + }) + + _sortById(groupNodes) + + userNodes.push({ + id: user.getPermId().getPermId(), + expanded: false, + loading: false, + loaded: true, + children: groupNodes + }) + }) + + _sortById(userNodes) + + return { + id: 'Users', + expanded: false, + loading: false, + loaded: true, + children: userNodes + } +} + +function browser_SetModeDone_GroupNodes(groups) { + let groupNodes = [] + + groups.forEach(group => { + let userNodes = [] + group.getUsers().forEach(user => { + userNodes.push({ + id: user.getPermId().getPermId(), + expanded: false, + loading: false, + loaded: true, + children: [] + }) + }) + + groupNodes.push({ + id: group.getPermId().getPermId(), + expanded: false, + loading: false, + loaded: true, + children: userNodes + }) + }) + + return { + id: 'Groups', + expanded: false, + loading: false, + loaded: true, + children: groupNodes + } +} + +function browser_ExpandNode(browser, action) { + let newBrowser = _.cloneDeep(browser) + _visitNodes(newBrowser.nodes, node => { + if (node.id === action.node.id) { + node.expanded = true + } + }) + return newBrowser +} + +function browser_CollapseNode(browser, action) { + let newBrowser = _.cloneDeep(browser) + _visitNodes(newBrowser.nodes, node => { + if (node.id === action.node.id) { + node.expanded = false + } + }) + return newBrowser +} + +const _visitNodes = (nodes, visitor) => { + let toVisit = [] + let visited = {} + + toVisit.push(...nodes) + + while (toVisit.length > 0) { + let node = toVisit.shift() + + if (!visited[node.id]) { + visited[node.id] = true + let result = visitor(node) + if (result) { + return result + } + } + + if (node.children !== undefined) { + node.children.forEach((child) => { + toVisit.push(child) + }) + } + } +} + + +const _sortById = (arr) => { + arr.sort((i1, i2) => { + return i1.id.localeCompare(i2.id) + }) +} \ No newline at end of file diff --git a/openbis_ng_ui/src/services/openbis.js b/openbis_ng_ui/src/services/openbis.js index 4362afe4850..641105f377a 100644 --- a/openbis_ng_ui/src/services/openbis.js +++ b/openbis_ng_ui/src/services/openbis.js @@ -1,72 +1,145 @@ -import { store } from '../index.js' +import {store} from '../index.js' let v3 = null /* eslint-disable-next-line no-undef */ -requirejs([ 'openbis' ], openbis => { - v3 = new openbis() - store.dispatch({type: 'INIT'}) +requirejs(['openbis'], openbis => { + v3 = new openbis() }) function login(user, password) { - return new Promise( (resolve, reject) => { - v3.login(user, password).done(resolve).fail(() => { - reject({ message: 'Login failed' }) + return new Promise((resolve, reject) => { + v3.login(user, password).done(resolve).fail(() => { + reject({message: 'Login failed'}) + }) }) - }) } function logout() { - return new Promise( (resolve, reject) => { - v3.logout().done(resolve).fail(reject) - }) + return new Promise((resolve, reject) => { + v3.logout().done(resolve).fail(reject) + }) } function getSpaces() { - return new Promise( (resolve, reject) => { - /* eslint-disable-next-line no-undef */ - requirejs( - ['as/dto/space/search/SpaceSearchCriteria', 'as/dto/space/fetchoptions/SpaceFetchOptions' ], - (SpaceSearchCriteria, SpaceFetchOptions) => - v3.searchSpaces(new SpaceSearchCriteria(), new SpaceFetchOptions()).done(resolve).fail(reject) - ) - }) + return new Promise((resolve, reject) => { + /* eslint-disable-next-line no-undef */ + requirejs( + ['as/dto/space/search/SpaceSearchCriteria', 'as/dto/space/fetchoptions/SpaceFetchOptions'], + (SpaceSearchCriteria, SpaceFetchOptions) => + v3.searchSpaces(new SpaceSearchCriteria(), new SpaceFetchOptions()).done(resolve).fail(reject) + ) + }) +} + +function getUsers() { + return new Promise((resolve, reject) => { + /* eslint-disable-next-line no-undef */ + requirejs( + ['as/dto/person/search/PersonSearchCriteria', 'as/dto/person/fetchoptions/PersonFetchOptions'], + (PersonSearchCriteria, PersonFetchOptions) => { + v3.searchPersons(new PersonSearchCriteria(), new PersonFetchOptions()).done(resolve).fail(reject) + }) + }) +} + +function getGroups() { + return new Promise((resolve, reject) => { + /* eslint-disable-next-line no-undef */ + requirejs( + ['as/dto/authorizationgroup/search/AuthorizationGroupSearchCriteria', 'as/dto/authorizationgroup/fetchoptions/AuthorizationGroupFetchOptions'], + (AuthorizationGroupSearchCriteria, AuthorizationGroupFetchOptions) => { + let fo = new AuthorizationGroupFetchOptions() + fo.withUsers() + v3.searchAuthorizationGroups(new AuthorizationGroupSearchCriteria(), fo).done(resolve).fail(reject) + }) + }) +} + +function getObjectTypes() { + return new Promise((resolve, reject) => { + /* eslint-disable-next-line no-undef */ + requirejs( + ['as/dto/sample/search/SampleTypeSearchCriteria', 'as/dto/sample/fetchoptions/SampleTypeFetchOptions'], + (SampleTypeSearchCriteria, SampleTypeFetchOptions) => { + v3.searchSampleTypes(new SampleTypeSearchCriteria(), new SampleTypeFetchOptions()).done(resolve).fail(reject) + }) + }) +} + +function getCollectionTypes() { + return new Promise((resolve, reject) => { + /* eslint-disable-next-line no-undef */ + requirejs( + ['as/dto/experiment/search/ExperimentTypeSearchCriteria', 'as/dto/experiment/fetchoptions/ExperimentTypeFetchOptions'], + (ExperimentTypeSearchCriteria, ExperimentTypeFetchOptions) => { + v3.searchExperimentTypes(new ExperimentTypeSearchCriteria(), new ExperimentTypeFetchOptions()).done(resolve).fail(reject) + }) + }) +} + +function getDataSetTypes() { + return new Promise((resolve, reject) => { + /* eslint-disable-next-line no-undef */ + requirejs( + ['as/dto/dataset/search/DataSetTypeSearchCriteria', 'as/dto/dataset/fetchoptions/DataSetTypeFetchOptions'], + (DataSetTypeSearchCriteria, DataSetTypeFetchOptions) => { + v3.searchDataSetTypes(new DataSetTypeSearchCriteria(), new DataSetTypeFetchOptions()).done(resolve).fail(reject) + }) + }) +} + +function getMaterialTypes() { + return new Promise((resolve, reject) => { + /* eslint-disable-next-line no-undef */ + requirejs( + ['as/dto/material/search/MaterialTypeSearchCriteria', 'as/dto/material/fetchoptions/MaterialTypeFetchOptions'], + (MaterialTypeSearchCriteria, MaterialTypeFetchOptions) => { + v3.searchMaterialTypes(new MaterialTypeSearchCriteria(), new MaterialTypeFetchOptions()).done(resolve).fail(reject) + }) + }) } function updateSpace(permId, description) { - return new Promise( (resolve, reject) => { - /* eslint-disable-next-line no-undef */ - requirejs( - ['as/dto/space/update/SpaceUpdate'], - (SpaceUpdate) => { - let spaceUpdate = new SpaceUpdate() - spaceUpdate.setSpaceId(permId) - spaceUpdate.setDescription(description) - v3.updateSpaces([spaceUpdate]).done(resolve).fail(reject) - } - ) - }) + return new Promise((resolve, reject) => { + /* eslint-disable-next-line no-undef */ + requirejs( + ['as/dto/space/update/SpaceUpdate'], + (SpaceUpdate) => { + let spaceUpdate = new SpaceUpdate() + spaceUpdate.setSpaceId(permId) + spaceUpdate.setDescription(description) + v3.updateSpaces([spaceUpdate]).done(resolve).fail(reject) + } + ) + }) } function searchProjects(spacePermId) { - return new Promise( (resolve, reject) => { - /* eslint-disable-next-line no-undef */ - requirejs( - ['as/dto/project/search/ProjectSearchCriteria', 'as/dto/project/fetchoptions/ProjectFetchOptions'], - (ProjectSearchCriteria, ProjectFetchOptions) => { - let searchCriteria = new ProjectSearchCriteria() - searchCriteria.withSpace().withPermId().thatEquals(spacePermId) - let fetchOptions = new ProjectFetchOptions() - v3.searchProjects(searchCriteria, fetchOptions).done(resolve).fail(reject) - } - ) - }) + return new Promise((resolve, reject) => { + /* eslint-disable-next-line no-undef */ + requirejs( + ['as/dto/project/search/ProjectSearchCriteria', 'as/dto/project/fetchoptions/ProjectFetchOptions'], + (ProjectSearchCriteria, ProjectFetchOptions) => { + let searchCriteria = new ProjectSearchCriteria() + searchCriteria.withSpace().withPermId().thatEquals(spacePermId) + let fetchOptions = new ProjectFetchOptions() + v3.searchProjects(searchCriteria, fetchOptions).done(resolve).fail(reject) + } + ) + }) } export default { - login: login, - logout: logout, - getSpaces: getSpaces, - updateSpace: updateSpace, - searchProjects: searchProjects, + login: login, + logout: logout, + getSpaces: getSpaces, + getUsers: getUsers, + getGroups: getGroups, + getObjectTypes: getObjectTypes, + getCollectionTypes: getCollectionTypes, + getDataSetTypes: getDataSetTypes, + getMaterialTypes: getMaterialTypes, + updateSpace: updateSpace, + searchProjects: searchProjects, } -- GitLab