diff --git a/locales/en/translations.json b/locales/en/translations.json index 9efddc2d4123755dcc74cce257b511b58246d4b5..a151a9dbe0929dd36432757eda9bf820c7167d0f 100644 --- a/locales/en/translations.json +++ b/locales/en/translations.json @@ -152,7 +152,7 @@ "acceNumb": "Accession number", "doi": "Accession DOI", "genus": "Accession genus", - "instCode": "Accession institution code" + "instCode": "Accession holder" }, "description": "Description", "descriptor": { @@ -160,13 +160,19 @@ "title": "Descriptor title" }, "owner": { - "uuid": "Owner uuid", - "name": "Owner name", - "shortName": "Owner short name", + "uuid": "Provider", + "shortName": "Provider short name", "wiewsCodes": "Owner WIEWS institute code" }, + "location": { + "country": "Evaluated in", + "latitude": "Latitude", + "longitude": "Longitude" + }, "published": "Published", - "title": "Title" + "title": "Title", + "rights": "License", + "crop": "Crop" }, "partner": { "description": "Description", @@ -210,6 +216,29 @@ "genus": "Genus", "species": "Species", "subtaxa": "Subtaxon" + }, + "sampleStatus": { + "100": "Wild", + "110": "Natural", + "120": "Semi-natural/wild", + "130": "Semi-natural/sown", + "200": "Weedy", + "300": "Traditional cultivar/Landrace", + "400": "Breeding/Research Material", + "410": "Breeders Line", + "411": "Synthetic population", + "412": "Hybrid", + "413": "Founder stock/base population", + "414": "Inbred line", + "415": "Segregating population", + "416": "Clonal selection", + "420": "Genetic stock", + "421": "Mutant", + "422": "Cytogenetic stocks", + "423": "Other genetic stocks", + "500": "Advanced/improved cultivar", + "600": "GMO", + "999": "Other" } } } diff --git a/src/datasets/ui/c/Card.tsx b/src/datasets/ui/c/Card.tsx index 514896475f178e64a5ecdf0e7f86c2e795140d1c..a1ebcadf0f6a3ac857383f5256c95cb655dde2e2 100644 --- a/src/datasets/ui/c/Card.tsx +++ b/src/datasets/ui/c/Card.tsx @@ -8,8 +8,8 @@ import { Link } from 'react-router-dom'; import Markdown from 'ui/catalog/markdown'; import { PartnerLink } from 'ui/catalog/Links'; import { Properties, PropertiesItem } from 'ui/catalog/Properties'; -import PrettyDate from 'ui/common/time/PrettyDate'; import CropChips from 'crops/ui/c/CropChips'; +import McpdDate from 'ui/common/time/McpdDate'; interface IDatasetCardProps extends React.ClassAttributes { className?: string; @@ -37,15 +37,15 @@ class DatasetCard extends React.Component { } /> - + { dataset.crops && dataset.crops.length > 0 && } + { dataset.accessionCount } { dataset.descriptorCount } - diff --git a/src/datasets/ui/c/Filters.tsx b/src/datasets/ui/c/Filters.tsx index fbfb6cafa61ff2dbfb7581e837f5ab72718b4761..a513e223e42925a8a5eeb64c5ef7012296a7df83 100644 --- a/src/datasets/ui/c/Filters.tsx +++ b/src/datasets/ui/c/Filters.tsx @@ -10,36 +10,37 @@ import StringArrFilter from 'ui/common/filter/StringArrFilter'; import TextFilter from 'ui/common/filter/TextFilter'; import CropFilter from 'crops/ui/c/CropFilter'; import PartnerFilter from 'partners/ui/c/PartnerFilter'; +import NumberFilter from 'ui/common/filter/NumberFilter'; +import LicenceFilter from 'ui/common/filter/LicenceFilter'; // const DatasetFilters = ({handleSubmit, initialize, ...other}) => ( - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + ); export default reduxForm({ diff --git a/src/datasets/ui/dataset-stepper/steps/basic-info/BasicInfoForm.tsx b/src/datasets/ui/dataset-stepper/steps/basic-info/BasicInfoForm.tsx index 7074ef7cf7d1d4bbf01547a17fe673e9918088f5..519b66dd7af0a9be9b660d0aff8e41461aac092b 100644 --- a/src/datasets/ui/dataset-stepper/steps/basic-info/BasicInfoForm.tsx +++ b/src/datasets/ui/dataset-stepper/steps/basic-info/BasicInfoForm.tsx @@ -10,7 +10,7 @@ import CropSelector from 'crops/ui/c/CropSelector'; import Validators from 'utilities/Validators'; -import BasicInfoRadioGroup from './BasicInfoRadioGroup'; +import BasicInfoRadioGroup from './LicenceSelector'; import remoteSubmit from './RemoteSubmit'; import VocabularyTermPicker from 'vocabulary/ui/c/VocabularyTermPicker'; diff --git a/src/datasets/ui/dataset-stepper/steps/basic-info/BasicInfoRadioGroup.tsx b/src/datasets/ui/dataset-stepper/steps/basic-info/LicenceSelector.tsx similarity index 92% rename from src/datasets/ui/dataset-stepper/steps/basic-info/BasicInfoRadioGroup.tsx rename to src/datasets/ui/dataset-stepper/steps/basic-info/LicenceSelector.tsx index 2fa6f70bc436e495aa2a0e8624f51a72e0737286..c532c56607877a544e1dcebb06373cbb3bf85252 100644 --- a/src/datasets/ui/dataset-stepper/steps/basic-info/BasicInfoRadioGroup.tsx +++ b/src/datasets/ui/dataset-stepper/steps/basic-info/LicenceSelector.tsx @@ -13,7 +13,7 @@ const styles = { // None }; -const BasicInfoRadioGroup = ({input, meta, classes, ...rest}) => ( +const LicenceSelector = ({input, meta, classes, ...rest}) => ( Rights ( ); -export default withStyles(styles)(BasicInfoRadioGroup); +export default withStyles(styles)(LicenceSelector); diff --git a/src/model/dataset.model.ts b/src/model/dataset.model.ts index 47ab7061a3d3ac5134b39f66b94e9aa82328983b..8c54efa110435c41dcaf35fd29b460c9436a23ef 100644 --- a/src/model/dataset.model.ts +++ b/src/model/dataset.model.ts @@ -66,6 +66,8 @@ export class Dataset extends UuidModel implements IUserPermissions { this._permissions = new CurrentPermissions(obj._permissions); } } + + } diff --git a/src/ui/common/filter/LicenceFilter.tsx b/src/ui/common/filter/LicenceFilter.tsx new file mode 100644 index 0000000000000000000000000000000000000000..2892faa021e265c1cdb41f011fe5e98e2f57d442 --- /dev/null +++ b/src/ui/common/filter/LicenceFilter.tsx @@ -0,0 +1,127 @@ +import * as React from 'react'; +import { Field } from 'redux-form'; +import { translate } from 'react-i18next'; + +import FormControlLabel from '@material-ui/core/FormControlLabel'; +import FormControl from '@material-ui/core/FormControl'; +import FormGroup from '@material-ui/core/FormGroup'; +import {AVAILABLE_LICENSES} from 'model/license.model'; +import { ExternalLink } from 'ui/catalog/Links'; +import Checkbox from '@material-ui/core/Checkbox/Checkbox'; + + +interface ILicenceFilterInternalProps extends React.ClassAttributes { + label: any; + input: any; + t: any; +} + +class LicenceFilterInternal extends React.Component { + + + public state = { + values: [], + }; + + public componentWillMount() { + this.setState({ + values: [ ...this.props.input.value ], + }); + } + + public componentWillReceiveProps(nextProps) { + this.setState({ + values: [ ...nextProps.input.value ], + }); + } + + private maybeAdd = (text: string) => { + const values = [ ...this.state.values ]; + + if (text && text.length > 0) { + if (values.indexOf(text) < 0) { + values.push(text); + } + this.setState({ + values, + }); + } + return values; + } + + private maybeRemove = (text: string) => { + const values = [ ...this.state.values ]; + + if (text && text.length > 0) { + const index: number = values.indexOf(text); + if (index >= 0) { + values.splice(index, 1); + } + this.setState({ + values, + }); + } + return values; + } + + private handleCheckbox = (event) => { + const { input } = this.props; + + const values = event.target.checked ? this.maybeAdd(event.target.value) : this.maybeRemove(event.target.value); + input.onChange(values); + } + + public render() { + + const { values } = this.state; + + return( + + + { AVAILABLE_LICENSES.map((license) => ( + { license.code } + { license.url && +
{ license.title }
+ } + + ) } + control={ + = 0 } + onChange={ this.handleCheckbox } /> + } + /> + )) } +
+
+ ); + } +} + +interface ILicenceFilterProps extends React.ClassAttributes { + name: string; + t?: any; +} + +class LicenceFilter extends React.Component { + + public render() { + const {name, t} = this.props; + + return( + + ); + } +} + + +export default translate()(LicenceFilter); diff --git a/src/ui/common/filter/NumberFilter.tsx b/src/ui/common/filter/NumberFilter.tsx new file mode 100644 index 0000000000000000000000000000000000000000..54c8433bfc5ab6254359e296e76ff166e4b35ef6 --- /dev/null +++ b/src/ui/common/filter/NumberFilter.tsx @@ -0,0 +1,109 @@ +import * as React from 'react'; +import { Field } from 'redux-form'; + +import TextField from '@material-ui/core/TextField'; + +import { INumberFilter as NumberFilterModel } from 'model/filter.model'; + +interface INumberFilterInternal extends React.ClassAttributes { + name: string; + input: any; + placeholder: string; + label: string; +} + +class NumberFilterInternal extends React.Component { + + private constructor(props, context) { + super(props, context); + + const { value }: { value: NumberFilterModel } = props.input; + let textA = value && value.ge !== null ? value.ge : ''; + let textB = value && value.le !== null ? value.le : ''; + if (textA && textB) { + const sorted = [ +textA, +textB ].sort((a, b) => b - a); + textA = sorted[0] === 0 ? '0' : sorted[0]; + textB = sorted[1] === 0 ? '0' : sorted[1]; + } + // console.log('Constructor', textA, textB); + this.state = { textA, textB }; + } + + public componentWillReceiveProps(nextProps) { + const { value }: { value: NumberFilterModel } = nextProps.input; + const textA = value && value.ge !== null ? value.ge : ''; + const textB = value && value.le !== null ? value.le : ''; + // console.log('receive props', textA, textB); + this.setState({ textA, textB }); + } + + private report(filter: NumberFilterModel) { + const { input } = this.props; + if (filter) { + console.log('Reporting', filter); + input.onChange(filter); + } else { + input.onChange(null); + } + } + + private textAChange = (event) => { + this.setState({textA: event.target.value , textB: this.state.textB}); + this.report({ge: event.target.value, le: this.state.textB}); + } + + private textBChange = (event) => { + this.setState({ textA: this.state.textA, textB: event.target.value }); + this.report({ge: this.state.textA, le: event.target.value}); + } + + public render() { + const { label } = this.props; + return ( +
+ + +
+ ); + } +} + +interface INumberFilter extends React.ClassAttributes { + name: string; + label: string; +} + +// NumberFilter supports eq, ge, gt, le, lt +class NumberFilter extends React.Component { + + public constructor(props: any) { + super(props); + } + + public render() { + const { name, label } = this.props; + return ( +
+ +
+ ); + } +} + +export default NumberFilter; diff --git a/src/ui/common/filter/StringArrFilter.tsx b/src/ui/common/filter/StringArrFilter.tsx index 68101dac9bb4cb08ebdac861494f679d7c659eef..d509c7b099f92a1286a3d8cc0d15376c596e4736 100644 --- a/src/ui/common/filter/StringArrFilter.tsx +++ b/src/ui/common/filter/StringArrFilter.tsx @@ -1,5 +1,6 @@ import * as React from 'react'; import { Field } from 'redux-form'; +import { translate } from 'react-i18next'; import FormGroup from '@material-ui/core/FormGroup'; import FormControlLabel from '@material-ui/core/FormControlLabel'; @@ -19,6 +20,7 @@ interface IStringArrFilterInternal extends React.ClassAttributes { placeholder?: string; label?: string; options?: { [key: string]: any; }; + t: any; } class InternalStringArrField extends React.Component { @@ -32,42 +34,51 @@ class InternalStringArrField extends React.Component `${key}`) : this.props.input.value; + this.setState({ + values: [ ...value ], + text: '', + }); + } + public componentWillReceiveProps(nextProps) { + const value = typeof nextProps.input.value[0] === 'number' ? nextProps.input.value.map((key) => `${key}`) : nextProps.input.value; this.setState({ - values: [ ...nextProps.input.value ], + values: [ ...value ], text: '', }); } private maybeAdd = (text: string) => { - const values = [ ...this.state.values ]; - - if (text && text.length > 0) { - if (values.indexOf(text) < 0) { - values.push(text); + const values = [ ...this.state.values ]; + + if (text && text.length > 0) { + if (values.indexOf(text) < 0) { + values.push(text); + } + this.setState({ + text: '', + values, + }); } - this.setState({ - text: '', - values, - }); - } - return values; + return values; } private maybeRemove = (text: string) => { - const values = [ ...this.state.values ]; - - if (text && text.length > 0) { - const index: number = values.indexOf(text); - if (index >= 0) { - values.splice(index, 1); + const values = [ ...this.state.values ]; + + if (text && text.length > 0) { + const index: number = values.indexOf(text); + if (index >= 0) { + values.splice(index, 1); + } + this.setState({ + text: '', + values, + }); } - this.setState({ - text: '', - values, - }); - } - return values; + return values; } private handleKeyPres = (event) => { @@ -75,14 +86,14 @@ class InternalStringArrField extends React.Component 0) { - event.preventDefault(); - const values = this.maybeAdd(text); - input.onChange(values); - } else { - // on blank, submit it - // input.onChange(values); - } + if (text && text.length > 0) { + event.preventDefault(); + const values = this.maybeAdd(text); + input.onChange(values); + } else { + // on blank, submit it + // input.onChange(values); + } } } @@ -115,7 +126,7 @@ class InternalStringArrField extends React.Component { withOptions ? - - { label && { label } } - - { Object.keys(options).map((key) => ( - = 0 } - onChange={ this.handleCheckbox }/> - } /> - )) } - - - : - - { label } - - - - - - } - /> - + + { label && { t(`${label}`) } } + + { Object.keys(options).map((key) => ( + = 0 } + onChange={ this.handleCheckbox } /> + } /> + )) } + + + : + + { t(`${label}`) } + + + + + + } + /> + } { (! withOptions && values && values.length > 0) && - values.map((val, index) => ( -
+ values.map((val, index) => ( +
{ withOptions ? options[val] || val : val } -
X
-
- )) } +
X
+
+ )) } ); } @@ -173,6 +185,7 @@ interface IStringArrFilter extends React.ClassAttributes { placeholder?: string; label?: string; options?: { [key: string]: any; }; + t: any; } class StringArrFilter extends React.Component { @@ -182,7 +195,7 @@ class StringArrFilter extends React.Component { } public render() { - const { name, label, placeholder, options } = this.props; + const { name, label, placeholder, options, t } = this.props; return (
{ label={ label } placeholder={ placeholder } options={ options } + t={ t } />
); } } -export default StringArrFilter; +export default translate()(StringArrFilter); diff --git a/styles/app.styles.scss b/styles/app.styles.scss index dddc2a1950ea6ca57e412eb7282cd6030d9d29f7..c7e4824116355584861366cab93efdaefa7abcc3 100644 --- a/styles/app.styles.scss +++ b/styles/app.styles.scss @@ -88,7 +88,7 @@ $mui-breakpoints: ( > a { margin-right: 2em; - body[dir="rtl"] & { + html[dir="rtl"] & { margin-left: 2em; margin-right: 0; } @@ -105,7 +105,7 @@ $mui-breakpoints: ( display: inline-block; position: absolute; height: 100%; - @media (max-width: 989px) { + @media (max-width: 959px) { display: none; } a { @@ -128,7 +128,7 @@ $mui-breakpoints: ( } .mobile-navigation-block { display: none; - @media (max-width: 989px) { + @media (max-width: 959px) { display: inline-block; } } @@ -270,7 +270,7 @@ $mui-breakpoints: ( color: #2b2924; background: #D4D1C6; padding: 2.14rem 1.429rem; - margin: 2.14rem auto 0; + // margin: 2.14rem auto 0; box-sizing: border-box; width: 100%; float: left; @@ -292,6 +292,10 @@ $mui-breakpoints: ( img:first-child { width: 32px; padding-right: 0.5714rem; + html[dir="rtl"] & { + padding-left: 0.5714rem; + padding-right: 0 !important; + } } } } @@ -390,9 +394,25 @@ $mui-breakpoints: ( filter: drop-shadow(-2px 0 2px rgba(0, 0, 0, 0.2)); } +/** Alignment **/ + +.float-right { + float: right; + html[dir=rtl] & { + float: left !important; + } +} + +.float-left { + float: left; + html[dir=rtl] & { + float: right !important; + } +} + /** Text **/ .font-bold { - font-weight: bold !important; + font-weight: bold; } .font-medium { @@ -408,13 +428,13 @@ $mui-breakpoints: ( } .text-left { text-align: left; - body[dir="rtl"] & { + html[dir="rtl"] & { text-align: right; } } .text-right { text-align: right; - body[dir="rtl"] & { + html[dir="rtl"] & { text-align: left; } }