Commit c8310d25 authored by Maxym Borodenko's avatar Maxym Borodenko
Browse files

UX/UI for Descriptors and Descriptor Lists

parent b74376e0
...@@ -12365,9 +12365,9 @@ ...@@ -12365,9 +12365,9 @@
} }
}, },
"react": { "react": {
"version": "16.2.0", "version": "16.3.2",
"resolved": "https://registry.npmjs.org/react/-/react-16.2.0.tgz", "resolved": "https://registry.npmjs.org/react/-/react-16.3.2.tgz",
"integrity": "sha512-ZmIomM7EE1DvPEnSFAHZn9Vs9zJl5A9H7el0EGTE6ZbW9FKe/14IYAlPbC8iH25YarEQxZL+E8VW7Mi7kfQrDQ==", "integrity": "sha512-o5GPdkhciQ3cEph6qgvYB7LTOHw/GB0qRI6ZFNugj49qJCFfgHwVNjZ5u+b7nif4vOeMIOuYj3CeYe2IBD74lg==",
"requires": { "requires": {
"fbjs": "0.8.16", "fbjs": "0.8.16",
"loose-envify": "1.3.1", "loose-envify": "1.3.1",
......
...@@ -116,8 +116,6 @@ export const saveDescriptorList = (descriptorList: DescriptorList) => (dispatch, ...@@ -116,8 +116,6 @@ export const saveDescriptorList = (descriptorList: DescriptorList) => (dispatch,
.then((descriptorList) => { .then((descriptorList) => {
// receive the updated descriptor list // receive the updated descriptor list
dispatch(receiveDescriptorList(descriptorList)); dispatch(receiveDescriptorList(descriptorList));
// and redirect to proper edit page
return dispatch(push(`/descriptorlists/${descriptorList.uuid}/edit`));
}).catch((error) => { }).catch((error) => {
log(`Error saving descriptor list`, error, descriptorList); log(`Error saving descriptor list`, error, descriptorList);
}); });
...@@ -129,8 +127,6 @@ export const deleteDescriptorList = (descriptorList: DescriptorList) => (dispatc ...@@ -129,8 +127,6 @@ export const deleteDescriptorList = (descriptorList: DescriptorList) => (dispatc
.then((descriptorList) => { .then((descriptorList) => {
// receive updates // receive updates
dispatch(receiveDescriptorList(descriptorList)); dispatch(receiveDescriptorList(descriptorList));
// go to the published descriptor list page
dispatch(push(`/descriptorlist`));
}); });
}; };
......
...@@ -14,7 +14,8 @@ import {log} from 'utilities/debug'; ...@@ -14,7 +14,8 @@ import {log} from 'utilities/debug';
interface IDescriptorPickerProps extends React.ClassAttributes<any> { interface IDescriptorPickerProps extends React.ClassAttributes<any> {
classes: any; classes: any;
router: any; history: any;
location: any;
loadDescriptors: (page?: number, results?: number, sortBy?: string, filter?: IDescriptorFilter, order?: string) => void; loadDescriptors: (page?: number, results?: number, sortBy?: string, filter?: IDescriptorFilter, order?: string) => void;
matchingDescriptors: Page<Descriptor>; // results from loadDescriptors matchingDescriptors: Page<Descriptor>; // results from loadDescriptors
onAddDescriptor: (descriptor: Descriptor) => void; // event handler onAddDescriptor: (descriptor: Descriptor) => void; // event handler
...@@ -74,14 +75,17 @@ class DescriptorPicker extends React.Component<IDescriptorPickerProps, any> { ...@@ -74,14 +75,17 @@ class DescriptorPicker extends React.Component<IDescriptorPickerProps, any> {
} }
protected onPaginationChange = (page, results, sortBy, dir) => { protected onPaginationChange = (page, results, sortBy, dir) => {
log('onPaginationChange', page, results, sortBy); const { history, location } = this.props;
const {router, router: { location }} = this.props; const params = new URLSearchParams();
params.append('p', page);
location.query.p = page; params.append('l', results);
location.query.l = results; params.append('s', sortBy);
location.query.s = sortBy; if (dir) {
location.query.d = dir; params.append('d', dir);
router.push(location); }
location.search = params.toString();
history.push(location);
} }
protected applyFilters = (newFilters) => { protected applyFilters = (newFilters) => {
......
import * as React from 'react'; import * as React from 'react';
// import {connect} from 'react-redux';
// import {bindActionCreators} from 'redux';
import {log} from 'utilities/debug'; import {log} from 'utilities/debug';
...@@ -8,13 +6,10 @@ import { CSV, ICsvConfiguration } from 'utilities/CSV'; ...@@ -8,13 +6,10 @@ import { CSV, ICsvConfiguration } from 'utilities/CSV';
import { Descriptor } from 'model/descriptor.model'; import { Descriptor } from 'model/descriptor.model';
import { VocabularyTerm } from 'model/vocabulary.model'; import { VocabularyTerm } from 'model/vocabulary.model';
import ContentHeaderWithButton from 'ui/common/heading/ContentHeaderWithButton';
import CSVConfiguration from 'ui/common/csv-configuration/CSVConfiguration'; import CSVConfiguration from 'ui/common/csv-configuration/CSVConfiguration';
import DescriptorCard from 'ui/catalog/descriptor/DescriptorCard'; import DescriptorCard from 'ui/catalog/descriptor/DescriptorCard';
import Grid from 'material-ui/Grid'; import Grid from 'material-ui/Grid';
import Paper from 'material-ui/Paper';
import Button from 'material-ui/Button';
import FormControl from 'material-ui/Form/FormControl'; import FormControl from 'material-ui/Form/FormControl';
import Input from 'material-ui/Input'; import Input from 'material-ui/Input';
import InputLabel from 'material-ui/Input/InputLabel'; import InputLabel from 'material-ui/Input/InputLabel';
...@@ -22,7 +17,7 @@ import InputLabel from 'material-ui/Input/InputLabel'; ...@@ -22,7 +17,7 @@ import InputLabel from 'material-ui/Input/InputLabel';
interface IDescriptorUpload extends React.ClassAttributes<any> { interface IDescriptorUpload extends React.ClassAttributes<any> {
className?: any; className?: any;
onImport: (descriptors: Descriptor[]) => any; onImportDescriptors: any;
} }
// Page to edit a descriptor list // Page to edit a descriptor list
...@@ -133,73 +128,48 @@ class DescriptorUpload extends React.Component<IDescriptorUpload, any> { ...@@ -133,73 +128,48 @@ class DescriptorUpload extends React.Component<IDescriptorUpload, any> {
}).on('end', () => { }).on('end', () => {
log('All CSV parsed'); log('All CSV parsed');
this.setState({ ...this.state, uploader: true, uploadedDescriptors: newDescriptors }); this.setState({ ...this.state, uploader: true, uploadedDescriptors: newDescriptors });
// this.props.onAccessionsUpdated(newIdentifiers); this.props.onImportDescriptors(newDescriptors);
}); });
} }
public clickImport = (e) => {
// log('Sending', this.state.uploadedDescriptors);
if (this.state.uploadedDescriptors && this.state.uploadedDescriptors.length > 0) {
this.props.onImport(this.state.uploadedDescriptors);
} else {
log('Nothing to import');
}
}
public render() { public render() {
return ( return (
<div> <div className={ `${this.props.className} m-20 p-20 even-row` }>
<Paper className={ `${this.props.className} p-20 mb-20` }> <div>
<div className="p-20 even-row"> <h4>INSTRUCTIONS FOR USE</h4>
<h4>INSTRUCTIONS FOR USE</h4> <ul>
<ul> <li>Use "Descriptors" sheet from template: <a href="/templates/genesys-catalog-template.xlsx" target="_blank">Genesys Catalog template</a>.</li>
<li>Use "Descriptors" sheet from template: <a href="/templates/genesys-catalog-template.xlsx" target="_blank">Genesys Catalog template</a>.</li> <li>Do not change header names in the template!</li>
<li>Do not change header names in the template!</li> <li>Fill the template with the descriptor information, save it.</li>
<li>Fill the template with the descriptor information, save it.</li> <li>Copy and paste the table from Excel into the text field below.</li>
<li>Copy and paste the table from Excel into the text field below.</li> </ul>
</ul> </div>
</div>
<CSVConfiguration onChange={ this.onUpdateCsvConfig } config={ this.state.csvConfig } />
<CSVConfiguration onChange={ this.onUpdateCsvConfig } config={ this.state.csvConfig } />
<FormControl fullWidth>
<FormControl fullWidth> <InputLabel>Descriptor definitions</InputLabel>
<InputLabel>Descriptor definitions</InputLabel> <Input onPaste={ this.dataPasted } placeholder="Paste descriptor data here (tab separated)" onBlur={ this.textBlurred }/>
<Input onPaste={ this.dataPasted } placeholder="Paste descriptor data here (tab separated)" onBlur={ this.textBlurred }/> </FormControl>
</FormControl>
</Paper> { this.state.uploadedDescriptors ? (
<div style={ { 'margin-top': '23px' } }>
{ this.state.uploadedDescriptors ? ( <h3>{ `Uploaded ${this.state.uploadedDescriptors.length} descriptor definitions` }</h3>
<div>
<ContentHeaderWithButton title={ `Uploaded ${this.state.uploadedDescriptors.length} descriptor definitions` } buttons={ <Grid container spacing={ 24 }>
<Button raised onClick={ this.clickImport }>Import descriptors</Button> { this.state.uploadedDescriptors.map((d, index) => (
} /> <Grid item key={ index } xs={ 12 } md={ 6 } xl={ 4 }>
<Grid container spacing={ 24 }> <DescriptorCard descriptor={ d } />
{ this.state.uploadedDescriptors.map((d, index) => ( </Grid>
<Grid item key={ index } xs={ 12 } md={ 6 } xl={ 4 }> )) }
<DescriptorCard descriptor={ d } /> </Grid>
</Grid> </div>
)) } ) : (
</Grid> <h3>No descriptors uploaded</h3>
</div> ) }
</div>
) : (
<ContentHeaderWithButton title="No descriptors uploaded" />
) }
</div>
); );
} }
} }
//
// const mapStateToProps = (state, ownProps) => ({
// descriptorList: ownProps.descriptorList,
// });
//
// const mapDispatchToProps = (dispatch) => bindActionCreators({
// addDescriptorToDescriptorList,
// removeDescriptorFromDescriptorList,
// }, dispatch);
//
// export default connect(mapStateToProps, mapDispatchToProps)(DescriptorListEditPage);
export default DescriptorUpload; export default DescriptorUpload;
import * as React from 'react'; import * as React from 'react';
import Paper from 'material-ui/Paper';
import Button from 'material-ui/Button';
import List from 'material-ui/List'; import List from 'material-ui/List';
import { DragDropContext } from 'react-dnd'; import { DragDropContext } from 'react-dnd';
import HTML5Backend from 'react-dnd-html5-backend'; import HTML5Backend from 'react-dnd-html5-backend';
...@@ -11,8 +9,7 @@ import Card from './Card'; ...@@ -11,8 +9,7 @@ import Card from './Card';
interface IDescriptorOrderProps extends React.ClassAttributes<any> { interface IDescriptorOrderProps extends React.ClassAttributes<any> {
descriptors: Descriptor[]; descriptors: Descriptor[];
onDone: (descriptors: Descriptor[]) => void; onRef?: any;
onCancel?: () => void;
} }
class DescriptorOrder extends React.PureComponent<IDescriptorOrderProps, any> { class DescriptorOrder extends React.PureComponent<IDescriptorOrderProps, any> {
...@@ -27,16 +24,17 @@ class DescriptorOrder extends React.PureComponent<IDescriptorOrderProps, any> { ...@@ -27,16 +24,17 @@ class DescriptorOrder extends React.PureComponent<IDescriptorOrderProps, any> {
}; };
} }
public componentDidMount() {
const { onRef } = this.props;
onRef(this);
}
public componentWillReceiveProps(nextProps) { public componentWillReceiveProps(nextProps) {
const { descriptors = [] } = nextProps; const { descriptors = [] } = nextProps;
this.setState(() => ({ descriptors: [ ...descriptors ] })); this.setState(() => ({ descriptors: [ ...descriptors ] }));
} }
protected saveOrder = () => {
this.props.onDone(this.state.descriptors);
}
public moveCard = (dragIndex: number, hoverIndex: number) => { public moveCard = (dragIndex: number, hoverIndex: number) => {
const { descriptors } = this.state; const { descriptors } = this.state;
const dragCard = descriptors[dragIndex]; const dragCard = descriptors[dragIndex];
...@@ -51,10 +49,9 @@ class DescriptorOrder extends React.PureComponent<IDescriptorOrderProps, any> { ...@@ -51,10 +49,9 @@ class DescriptorOrder extends React.PureComponent<IDescriptorOrderProps, any> {
} }
public render() { public render() {
const {onCancel} = this.props;
const {descriptors} = this.state; const {descriptors} = this.state;
return ( return (
<Paper className="p-20"> <div className="p-20">
<List> <List>
{ descriptors.map((descriptor, i) => ( { descriptors.map((descriptor, i) => (
<Card <Card
...@@ -66,9 +63,7 @@ class DescriptorOrder extends React.PureComponent<IDescriptorOrderProps, any> { ...@@ -66,9 +63,7 @@ class DescriptorOrder extends React.PureComponent<IDescriptorOrderProps, any> {
/> />
)) } )) }
</List> </List>
<Button raised onClick={ this.saveOrder }>Save order</Button> </div>
<Button onClick={ onCancel }>Cancel</Button>
</Paper>
); );
} }
} }
......
...@@ -86,8 +86,8 @@ class StepNavigation extends React.Component<IStepNavigationProps, any> { ...@@ -86,8 +86,8 @@ class StepNavigation extends React.Component<IStepNavigationProps, any> {
</h3> </h3>
<div className={ classes.flexGrow }/> <div className={ classes.flexGrow }/>
{ this.state.id === 1 && ( { this.state.id === 1 && (
<Button onClick={ onDelete } className={ classes.btnDelete }> <Button disabled={ disabled } onClick={ onDelete } className={ classes.btnDelete }>
Delete dataset Delete data
</Button> </Button>
) )
} }
......
...@@ -39,7 +39,7 @@ const styles = (theme) => ({ ...@@ -39,7 +39,7 @@ const styles = (theme) => ({
}, },
}); });
const TopSection = ({classes, pageTitle}) => ( const TopSection = ({classes, pageTitle, subTitle}) => (
<Grid container spacing={ 0 } className={ classes.root }> <Grid container spacing={ 0 } className={ classes.root }>
<Grid item xs={ 12 } className={ classes.header }> <Grid item xs={ 12 } className={ classes.header }>
<h1 className="white mb-5 font-bold" style={ { marginBottom: 0, fontSize: '1.714rem'} }> <h1 className="white mb-5 font-bold" style={ { marginBottom: 0, fontSize: '1.714rem'} }>
...@@ -47,13 +47,13 @@ const TopSection = ({classes, pageTitle}) => ( ...@@ -47,13 +47,13 @@ const TopSection = ({classes, pageTitle}) => (
</h1> </h1>
<Hidden implementation="css" smDown> <Hidden implementation="css" smDown>
<h3 className="font-medium white m-0" style={ { marginTop: '.5rem', fontSize: '0.8571rem' } }> <h3 className="font-medium white m-0" style={ { marginTop: '.5rem', fontSize: '0.8571rem' } }>
Publish your datasets { subTitle }
</h3> </h3>
</Hidden> </Hidden>
</Grid> </Grid>
<Grid item xs={ 12 } className={ classes.subHeader }> <Grid item xs={ 12 } className={ classes.subHeader }>
<h4 className="font-bold m-0 lh-35"> <h4 className="font-bold m-0 lh-35">
Dataset publication Data publication
</h4> </h4>
<div className={ classes.flexGrow }/> <div className={ classes.flexGrow }/>
<div className={ classes.guide }> <div className={ classes.guide }>
......
...@@ -6,14 +6,14 @@ import Grid from 'material-ui/Grid'; ...@@ -6,14 +6,14 @@ import Grid from 'material-ui/Grid';
import { log } from 'utilities/debug'; import { log } from 'utilities/debug';
import confirm from 'utilities/confirmAlert'; import confirm from 'utilities/confirmAlert';
import ProgressMenu from './progress-menu'; import ProgressMenu from 'ui/common/stepper/progress-menu';
import TopSection from './TopSection'; import TopSection from 'ui/common/stepper/TopSection';
import BottomSection from './BottomSection'; import BottomSection from 'ui/common/stepper/BottomSection';
import StepNavigation from 'ui/common/stepper/StepNavigation';
import { navigateTo } from 'actions/navigation'; import { navigateTo } from 'actions/navigation';
import { setPageTitle } from 'actions/pageTitle'; import { setPageTitle } from 'actions/pageTitle';
import { loadDataset, publishDataset, deleteDataset } from 'actions/dataset'; import { loadDataset, publishDataset, deleteDataset } from 'actions/dataset';
import { Dataset } from 'model/dataset.model'; import { Dataset } from 'model/dataset.model';
import StepNavigation from './StepNavigation';
import Loading from 'ui/common/Loading'; import Loading from 'ui/common/Loading';
import renderRoutes from 'ui/renderRoutes'; import renderRoutes from 'ui/renderRoutes';
...@@ -148,7 +148,7 @@ class DatasetStepper extends React.Component<IDatasetProps, any> { ...@@ -148,7 +148,7 @@ class DatasetStepper extends React.Component<IDatasetProps, any> {
return ( return (
<Grid container spacing={ 0 }> <Grid container spacing={ 0 }>
<TopSection pageTitle={ pageTitle } /> <TopSection pageTitle={ pageTitle } subTitle="Publish your datasets" />
<Grid item xs={ 12 } md={ 9 } xl={ 10 } className="back-gray p-20"> <Grid item xs={ 12 } md={ 9 } xl={ 10 } className="back-gray p-20">
<Grid container spacing={ 0 } className="back-white"> <Grid container spacing={ 0 } className="back-white">
<StepNavigation disabled={ !(dataset && dataset.uuid) } onGotoStep={ this.gotoStep } onDelete={ this.onDelete } steps={ steps } location={ location } showStepName bottomDivider onPublish={ this.onPublish } /> <StepNavigation disabled={ !(dataset && dataset.uuid) } onGotoStep={ this.gotoStep } onDelete={ this.onDelete } steps={ steps } location={ location } showStepName bottomDivider onPublish={ this.onPublish } />
......
...@@ -42,7 +42,7 @@ class Traits extends React.Component<any, any> { ...@@ -42,7 +42,7 @@ class Traits extends React.Component<any, any> {
} }
public render() { public render() {
const { matchingDescriptors, loadDescriptors, dataset, router, listCrops, pagination} = this.props; const { matchingDescriptors, loadDescriptors, dataset, history, location, listCrops, pagination} = this.props;
return ( return (
<DescriptorPicker <DescriptorPicker
...@@ -51,7 +51,8 @@ class Traits extends React.Component<any, any> { ...@@ -51,7 +51,8 @@ class Traits extends React.Component<any, any> {
currentDescriptors={ dataset.descriptors } currentDescriptors={ dataset.descriptors }
onAddDescriptor={ this.addDescriptor } onAddDescriptor={ this.addDescriptor }
onRemoveDescriptor={ this.removeDescriptor } onRemoveDescriptor={ this.removeDescriptor }
router={ router } history={ history }
location={ location }
pagination={ pagination } pagination={ pagination }
listCrops={ listCrops } listCrops={ listCrops }
classes={ {} } classes={ {} }
......
import * as React from 'react'; import * as React from 'react';
import {connect} from 'react-redux'; import {connect} from 'react-redux';
import {bindActionCreators} from 'redux'; import {bindActionCreators} from 'redux';
import { withStyles } from 'material-ui/styles'; import { withStyles } from 'material-ui/styles';
import {log} from 'utilities/debug';
import { import {
loadDescriptorList, loadDescriptorList,
publishDescriptorList, publishDescriptorList,
...@@ -13,25 +11,12 @@ import { ...@@ -13,25 +11,12 @@ import {
setDescriptorsToDescriptorList, setDescriptorsToDescriptorList,
} from 'actions/descriptorList'; } from 'actions/descriptorList';
import { copyDescriptor } from 'actions/descriptors'; import { copyDescriptor } from 'actions/descriptors';
import { DescriptorList, Descriptor } from 'model/descriptor.model'; import { DescriptorList} from 'model/descriptor.model';
import confirm from 'utilities/confirmAlert';
import Authorize from 'ui/common/authorized/Authorize';
import Loading from 'ui/common/Loading'; import Loading from 'ui/common/Loading';
import Markdown from 'ui/catalog/markdown';
import DescriptorCard from 'ui/catalog/descriptor/DescriptorCard';
import ContentHeaderWithButton from 'ui/common/heading/ContentHeaderWithButton'; import ContentHeaderWithButton from 'ui/common/heading/ContentHeaderWithButton';
import {PartnerLink, DescriptorListLink, CropLink, DescriptorLink, ExternalLink} from 'ui/catalog/Links';
import { Properties, PropertiesItem } from 'ui/catalog/Properties';
import BackButton from 'ui/common/buttons/BackButton'; import BackButton from 'ui/common/buttons/BackButton';
import { ScrollToTopOnMount } from 'ui/common/page/scrollers'; import { ScrollToTopOnMount } from 'ui/common/page/scrollers';
import Permissions from 'ui/common/permission/Permissions'; import DescriptorListDisplay from './c/DescriptorListDisplay';
import Grid from 'material-ui/Grid';
import Card, {CardHeader, CardContent, CardActions } from 'ui/common/Card';
import Section from 'ui/common/layout/Section';
import Divider from 'material-ui/Divider';
import Button from 'material-ui/Button';
interface IDescriptorListPageProps extends React.ClassAttributes<any> { interface IDescriptorListPageProps extends React.ClassAttributes<any> {
classes: any; classes: any;
...@@ -46,225 +31,49 @@ interface IDescriptorListPageProps extends React.ClassAttributes<any> { ...@@ -46,225 +31,49 @@ interface IDescriptorListPageProps extends React.ClassAttributes<any> {
} }
const styles = (theme) => ({ const styles = (theme) => ({
filterSection: theme.leftPanel.root, contentContainer: {
contentContainer: { backgroundColor: '#E8E5E0',
backgroundColor: '#E8E5E0', padding: '1.5rem',
padding: '1.5rem', },
},
card: {
marginBottom: '1.5rem',
},
propertiesContainer: {
marginTop: '20px',
marginBottom: '20px',
},
propertiesRow: {
/* tslint:disable */
'marginTop': '1px',
'marginBottom': '1px',
'& > *:first-child': {
borderRight: 'solid 1px white',
},
'&:nth-child(even)': {
backgroundColor: '#f8f7f5',
},
'&:nth-child(odd)': {
backgroundColor: '#f3f2ee',
},
/* tslint:enable */
},
}); });
// Page to edit a descriptor list
class DescriptorListPage extends React.Component<IDescriptorListPageProps, any> { class DescriptorListPage extends React.Component<IDescriptorListPageProps, any> {
protected static needs = [ protected static needs = [
({ params: { uuid } }) => loadDescriptorList(uuid), ({ params: { uuid } }) => loadDescriptorList(uuid),
]; ];
public componentWillMount() {
const {uuid, loading, loadDescriptorList} = this.props;
if (uuid && (! loading || loading.uuid !== uuid)) {
loadDescriptorList(uuid);
}