diff --git a/locales/en/common.json b/locales/en/common.json index 2e047da86a0fad1de879833a81d9855770bf62c7..21c5fba328836f2b735acb8e1f5cb748c6cbb0c5 100644 --- a/locales/en/common.json +++ b/locales/en/common.json @@ -66,7 +66,9 @@ "createdDate": "Created on", "lastModifiedDate": "Last modified", "textSearch": "Text search", - "keyword": "Keyword search" + "keyword": "Keyword search", + "noFilters": "No available filters", + "suggestedFilters": "Suggested filters" }, "fileUploader": { "chooseFiles": "Choose files to upload", diff --git a/locales/en/translations.json b/locales/en/translations.json index 766df607bd1f5cd3d4a62852ba5ab1221748ce4d..4f09d3d408b532f0624d04cc36bd3d551b864006 100644 --- a/locales/en/translations.json +++ b/locales/en/translations.json @@ -36,7 +36,8 @@ "gt": "{{what}} >", "accessions": { "crop": "Crop", - "acceNumb": "Accession number", + "crops": "Crops", + "accessionNumber": "Accession number", "seqNo": "Sequential number", "sampStat": "Biological status", "storage": "Storage", @@ -52,14 +53,14 @@ "bio15": "Precipitation seasonality" } }, - "holder": { + "institute": { "code": "Holder", "networks": "Network", "owner": { "uuid": "Data provider" }, "country": { - "iso3": "Holder country", + "code3": "Holder country", "region": "Holder region" } }, @@ -68,8 +69,11 @@ "species": "Species", "subtaxa": "Subtaxa" }, - "origin": { - "iso3": "Provenance" + "owner": { + "uuid": "Data provider" + }, + "countryOfOrigin": { + "code3": "Provenance" }, "taxonomy": { "genus": "Genus", @@ -107,17 +111,20 @@ }, "subsets": { "title": "Title", - "crop": "Crop", + "crops": "Crop", "institutes": "Institute code", "description": "Description", "state": "Status", - "dateCreated": "Date created" + "dateCreated": "Date created", + "owner": { + "uuid": "Data provider" + } }, "wiews": { "code": "Institute code", "accessions": "Accessions in Genesys", "country": { - "iso3": "Country code" + "code3": "Country code" }, "owner": { "uuid": "Data provider" @@ -139,14 +146,14 @@ }, "datasets": { "_text": "Keywords", - "accessionRef": { + "accessionRefs": { "acceNumb": "Accession number", "doi": "Accession DOI", "genus": "Accession genus", "instCode": "Accession holder" }, "description": "Description", - "descriptor": { + "descriptors": { "_text": "Descriptor keywords", "title": "Descriptor title" }, @@ -155,7 +162,7 @@ "shortName": "Provider short name", "wiewsCodes": "Owner WIEWS institute code" }, - "location": { + "locations": { "country": "Evaluated in", "latitude": "Latitude", "longitude": "Longitude" @@ -163,7 +170,7 @@ "published": "Published", "title": "Title", "rights": "License", - "crop": "Crop", + "crops": "Crop", "state": "Status" }, "partner": { @@ -196,7 +203,7 @@ "category": "Category", "columnName": "Column name", "key": "Key crop descriptor", - "list": { + "descriptorLists": { "uuid": "Descriptor list" }, "owner": { diff --git a/src/accessions/actions/public.ts b/src/accessions/actions/public.ts index 9f53c4db3e9fc80f610586e26a033b2759b96a53..6121cb0daf1ed6f8ed20b390a4d7ffd34fbb5a28 100644 --- a/src/accessions/actions/public.ts +++ b/src/accessions/actions/public.ts @@ -16,6 +16,7 @@ import { RECEIVE_ACCESSION_AUDIT_LOG, RECEIVE_TILE_LAYER, APPEND_ACCESSIONS, + APPEND_ACCESSIONS_WITH_SUGGESTIONS, } from 'accessions/constants'; import AccessionService from 'service/genesys/AccessionService'; import ClimateService from 'service/genesys/ClimateService'; @@ -32,6 +33,7 @@ const receiveAccessionMapInfo = (apiCall: ApiCall) => ({ // Wrapped API calls const apiListAccessions = createApiCaller(AccessionService.list, APPEND_ACCESSIONS); +const apiListAccessionSugestions = createApiCaller(AccessionService.listSuggestions, APPEND_ACCESSIONS_WITH_SUGGESTIONS); const apiAccessionsOverview = createApiCaller(AccessionService.listOverview, RECEIVE_ACCESSION_OVERVIEW); const apiAccessionsMapInfo = createApiCaller(AccessionService.mapInfo, RECEIVE_ACCESSION_MAPINFO); @@ -63,7 +65,7 @@ export const applyFilters = (filters: string | AccessionFilter, page: IPageReque console.log('Applying new filter', filters); dispatch(showSnackbar('Applying filters...')); - return dispatch(apiListAccessions(filters, page)) + return dispatch(apiListAccessionSugestions(filters, page)) .then((paged) => { dispatch(updateRoute(paged)); dispatch(showSnackbar(`Filters applied.`)); diff --git a/src/accessions/constants.ts b/src/accessions/constants.ts index a3dce914e900e273ac7ee024362ac7a8958a28de..d9ef4fb950f9da94e21aefc3a4b9abc9429c6f96 100644 --- a/src/accessions/constants.ts +++ b/src/accessions/constants.ts @@ -1,4 +1,5 @@ export const APPEND_ACCESSIONS = 'accessions/APPEND_ACCESSIONS'; +export const APPEND_ACCESSIONS_WITH_SUGGESTIONS = 'accessions/APPEND_ACCESSIONS_WITH_SUGGESTIONS'; export const RECEIVE_ACCESSION_OVERVIEW = 'accessions/RECEIVE_ACCESSION_OVERVIEW'; export const RECEIVE_TILE_LAYER = 'accessions/RECEIVE_TILE_LAYER'; export const RECEIVE_ACCESSION_AUDIT_LOG = 'accessions/RECEIVE_ACCESSION_AUDIT_LOG'; diff --git a/src/accessions/reducers/public.ts b/src/accessions/reducers/public.ts index 1e013fb73a8a7cac3d177d110900a6dd71ab0f37..0de7678292991efce1371ac060baac406479b708 100644 --- a/src/accessions/reducers/public.ts +++ b/src/accessions/reducers/public.ts @@ -7,7 +7,7 @@ import { APPEND_ACCESSIONS, RECEIVE_ACCESSION_MAPINFO, RECEIVE_ACCESSION_AUDIT_LOG, - RECEIVE_TILE_LAYER, + RECEIVE_TILE_LAYER, APPEND_ACCESSIONS_WITH_SUGGESTIONS, } from 'accessions/constants'; import FilteredPage from 'model/FilteredPage'; @@ -22,6 +22,7 @@ const INITIAL_STATE: { accession: ApiCall; auditLog: ApiCall, paged: ApiCall>; + suggestions: any; overview: ApiCall; mapInfo: ApiCall; mapLayers: MapLayer[] @@ -29,6 +30,7 @@ const INITIAL_STATE: { accession: null, auditLog: null, paged: null, + suggestions: null, overview: null, mapInfo: null, mapLayers: AVAILABLE_LAYERS, @@ -96,6 +98,22 @@ function publicAccessions(state = INITIAL_STATE, action: IReducerAction) { // overview: { $set: null }, }); } + case APPEND_ACCESSIONS_WITH_SUGGESTIONS: { + const {apiCall: {loading, error, timestamp, data}} = action.payload; + return update(state, { + paged: { + $set: { + loading, + error, + timestamp, + data: FilteredPage.merge(state.paged && state.paged.data, data), + }, + }, + suggestions: {$set: data ? data.suggestions : state.suggestions}, + // mapInfo: { $set: null }, + // overview: { $set: null }, + }); + } case RECEIVE_ACCESSION_MAPINFO: { const { apiCall } = action.payload; diff --git a/src/accessions/ui/BrowsePage.tsx b/src/accessions/ui/BrowsePage.tsx index d24c384a16b3c24d7407f3441a0b40dee17d97b1..830e49e6ead1c90b27ce11a626f9a75bc5741652 100644 --- a/src/accessions/ui/BrowsePage.tsx +++ b/src/accessions/ui/BrowsePage.tsx @@ -42,15 +42,27 @@ class BrowsePage extends BrowsePageTemplate { } public render() { - const { paged, loadMoreData, filterCode, currentTab, loading, t} = this.props; + const { paged, loadMoreData, filterCode, currentTab, loading, suggestions, crops, t} = this.props; + const slug: string = this.state.authenticated ? 'download-authenticated' : 'download-anonymous'; const renderAccession = (s: Accession, index: number) => { return ; }; + + const suggestionTerms = new Map(); + if (suggestions) { + Object.keys(suggestions).forEach((key) => { + const overviewEl = suggestions[key]; + const terms = new Map(); + overviewEl.terms.forEach((term) => terms.set(term.term, term.count)); + suggestionTerms.set(key, terms); + }); + } + return ( + }> @@ -100,6 +112,8 @@ class BrowsePage extends BrowsePageTemplate { } const mapStateToProps = (state, ownProps) => ({ + suggestions: state.accessions.public.suggestions, + crops: state.crop.public.list ? state.crop.public.list.data : undefined, paged: state.accessions.public.paged ? state.accessions.public.paged.data : undefined, loading: state.accessions.public.paged ? state.accessions.public.paged.loading : false, filterCode: ownProps.match.params.filterCode, diff --git a/src/accessions/ui/c/Filters.tsx b/src/accessions/ui/c/Filters.tsx index e88ad831335bad0253f6a68f080a471f1d4530e2..4b12cc71c1d540592ab3d0e98f6898f1aea2b2c9 100644 --- a/src/accessions/ui/c/Filters.tsx +++ b/src/accessions/ui/c/Filters.tsx @@ -1,4 +1,5 @@ import * as React from 'react'; +import { connect } from 'react-redux'; import { reduxForm } from 'redux-form'; import {translate} from 'react-i18next'; @@ -6,25 +7,27 @@ import { ACCESSION_FILTERFORM } from 'accessions/constants'; import FiltersBlock from 'ui/common/filter/FiltersBlock'; import CollapsibleComponentSearch from 'ui/common/filter/CollapsibleComponentSearch'; -import BooleanFilter from 'ui/common/filter/BooleanFilter'; import NumberFilter from 'ui/common/filter/NumberFilter'; import StringFilter from 'ui/common/filter/StringFilter'; import StringArrFilter from 'ui/common/filter/StringArrFilter'; import Accession from 'model/accession/Accession'; import DateFilter from 'ui/common/filter/DateFilter'; -import CropFilter from 'crop/ui/c/CropFilter'; +import BooleanFilter from 'ui/common/filter/BooleanFilter'; -const AccessionFilters = ({handleSubmit, initialValues, initialize, t, ...other}) => { +const AccessionFilters = ({handleSubmit, initialValues, initialize, terms, crops, t, ...other}) => { // console.log('AccessionFilters', initialValues); return ( - + - - - + + + @@ -32,15 +35,21 @@ const AccessionFilters = ({handleSubmit, initialValues, initialize, t, ...other} - + - - - + + + - + @@ -67,24 +76,55 @@ const AccessionFilters = ({handleSubmit, initialValues, initialize, t, ...other} - + - + - - - - - + + + + ); }; +const mapStateToProps = (state, ownProps) => ({ + crops: state.crop.public.list ? state.crop.public.list.data : undefined, + +}); + export default translate()(reduxForm({ enableReinitialize: true, destroyOnUnmount: false, form: ACCESSION_FILTERFORM, -})(AccessionFilters)); +})(connect(mapStateToProps)(AccessionFilters))); diff --git a/src/crop/ui/c/CropFilter.tsx b/src/crop/ui/c/CropFilter.tsx index 051d2167c754c3699c1b14265e0ae29e704918e6..77fc43535ae4bf9de6bcb037dbf33f17b8807f68 100644 --- a/src/crop/ui/c/CropFilter.tsx +++ b/src/crop/ui/c/CropFilter.tsx @@ -10,31 +10,11 @@ interface IProps extends React.ClassAttributes { } class CropFilter extends React.Component { - private options: object = {}; - - public componentWillMount() { - const { crops } = this.props; - if (crops && crops.length > 0) { - this.options = {}; - crops.sort((a, b) => a.name.localeCompare(b.name)).forEach((crop) => { - this.options[crop.shortName] = crop.name; - }); - } - } - - public componentWillReceiveProps(nextProps) { - const { crops } = nextProps; - if (crops && crops.length > 0) { - this.options = {}; - crops.sort((a, b) => a.name.localeCompare(b.name)).forEach((crop) => { - this.options[crop.shortName] = crop.name; - }); - } - } public render() { + const {crops} = this.props; return ( - + a.name.localeCompare(b.name)) } valueField="shortName" labelField="name" { ...this.props }/> ); } } diff --git a/src/datasets/ui/c/Filters.tsx b/src/datasets/ui/c/Filters.tsx index d5010819bcdef8fb7612e5d262a9ff972f48da6e..656c9780833f8eebdbfeb40993cb51fb1c9b053b 100644 --- a/src/datasets/ui/c/Filters.tsx +++ b/src/datasets/ui/c/Filters.tsx @@ -19,24 +19,24 @@ import LicenceFilter from 'ui/common/filter/LicenceFilter'; const DatasetFilters = ({ handleSubmit, initialize, t, ...other }) => ( - + - - - + + + - + - - - - + + + + diff --git a/src/datasets/ui/dashboard/DashboardPage.tsx b/src/datasets/ui/dashboard/DashboardPage.tsx index 8c38efbe61a7097d386476b719a23cbed2b23609..455cbb93d7d65fca93d4d099afa943fc6a7deb24 100644 --- a/src/datasets/ui/dashboard/DashboardPage.tsx +++ b/src/datasets/ui/dashboard/DashboardPage.tsx @@ -7,6 +7,7 @@ import { approveDataset, deleteDataset, loadMoreDatasets, unpublishDataset, publ import Dataset from 'model/catalog/Dataset'; import { DatasetLink } from 'ui/genesys/Links'; import { PublishState } from 'model/common.model'; +import DashboardDescriptorFilters from 'datasets/ui/dashboard/c/DashboardFilters'; import ActionButton from 'ui/common/buttons/ActionButton'; const renderDataLink = ({ row, children, needCurrent, t }) => ( @@ -32,6 +33,7 @@ const mapStateToProps = (state, ownProps) => ({ tab: 'datasets', dataClassName: Dataset.clazz, filterCode: ownProps.match.params.filterCode, + filterComponent: DashboardDescriptorFilters, renderDataLink, renderActions, }); diff --git a/src/datasets/ui/dashboard/c/DashboardFilters.tsx b/src/datasets/ui/dashboard/c/DashboardFilters.tsx new file mode 100644 index 0000000000000000000000000000000000000000..0c8f883ed8ef717fb0b8082a289dadef9e264aae --- /dev/null +++ b/src/datasets/ui/dashboard/c/DashboardFilters.tsx @@ -0,0 +1,43 @@ +import * as React from 'react'; +import { reduxForm } from 'redux-form'; +import { translate } from 'react-i18next'; + +import { DASHBOARD_FILTERFORM } from 'constants/dashboard'; + +import FiltersBlock from 'ui/common/filter/FiltersBlock'; +import CollapsibleComponentSearch from 'ui/common/filter/CollapsibleComponentSearch'; +import CropFilter from 'crop/ui/c/CropFilter'; +import TextFilter from 'ui/common/filter/TextFilter'; +import Authorize from 'ui/common/authorized/Authorize'; +import PartnerFilter from 'partners/ui/c/PartnerFilter'; +import StatusFilter from 'ui/catalog/dashboard/c/StatusFilter'; // move + +const DashboardFilters = ({ handleSubmit, initialize, t, ...other }) => ( + + + + + + + + + + + + +); + +export default translate()(reduxForm({ + enableReinitialize: true, + destroyOnUnmount: false, + form: DASHBOARD_FILTERFORM, +})(DashboardFilters)); diff --git a/src/descriptors/ui/c/Filters.tsx b/src/descriptors/ui/c/Filters.tsx index b8a3314a8a2ac4163fcc19f935003560bd2cafc0..8d052e489f576c342c24ec7d3966dd69c9d9ac75 100644 --- a/src/descriptors/ui/c/Filters.tsx +++ b/src/descriptors/ui/c/Filters.tsx @@ -18,7 +18,7 @@ const DescriptorFilters = ({ handleSubmit, initialize, t, ...other }) => ( - + diff --git "a/src/institutes/ui/\321\201/Filters.tsx" "b/src/institutes/ui/\321\201/Filters.tsx" index 8883211432fe9592733e3a87dc53c86c9d1de2cd..9b6922c17a4c6cadd1f2e3bd87713efb11520690 100644 --- "a/src/institutes/ui/\321\201/Filters.tsx" +++ "b/src/institutes/ui/\321\201/Filters.tsx" @@ -20,11 +20,11 @@ const AccessionFilters = ({handleSubmit, initialValues, initialize, t, ...other} - + - + ); diff --git a/src/model/accession/AccessionFilter.ts b/src/model/accession/AccessionFilter.ts index ff3175e9114c7cb2d0651e5b24ba4d88e641110f..25f2537123b25a5df3b3b48ed4fa7de384d2d94a 100644 --- a/src/model/accession/AccessionFilter.ts +++ b/src/model/accession/AccessionFilter.ts @@ -4,31 +4,36 @@ import InstituteFilter from 'model/genesys/InstituteFilter'; import NumberFilter from 'model/filter/NumberFilter'; import StringFilter from 'model/filter/StringFilter'; import TaxonomyFilter from 'model/genesys/TaxonomyFilter'; -import ClimateFilter from 'model/genesys/ClimateFilter'; +import AccessionGeoFilter from './AccessionGeoFilter'; /* * Defined in Swagger as '#/definitions/AccessionFilter' */ class AccessionFilter { - public acceNumb?: StringFilter; + public accessionNumber?: StringFilter; + public aliases: StringFilter; public active?: boolean; public available?: boolean; public createdBy?: number[]; + public countryOfOrigin: CountryFilter; public createdDate?: DateFilter; public crop?: string[]; public cropName?: string; public doi?: string[]; public elevation?: NumberFilter; - public geo?: { latitude?: NumberFilter, longitude?: NumberFilter, climate?: ClimateFilter }; + public geo?: AccessionGeoFilter; public historic?: boolean; - public holder?: InstituteFilter; + public images?: boolean; + public inTrust: boolean; + public institute: InstituteFilter; + public lists: string[]; public id?: number[]; public lastModifiedBy?: number[]; public lastModifiedDate?: DateFilter; public latitude?: NumberFilter; public longitude?: NumberFilter; public mlsStatus?: boolean; - public origin?: CountryFilter; + public pdci: NumberFilter; public sampStat?: number[]; public seqNo?: NumberFilter; public taxa?: TaxonomyFilter; @@ -36,7 +41,7 @@ class AccessionFilter { public version?: number[]; public sgsv?: boolean; public storage?: number[]; - public images?: boolean; + public taxonomy: TaxonomyFilter; public NOT?: AccessionFilter; } diff --git a/src/model/accession/AccessionGeoFilter.ts b/src/model/accession/AccessionGeoFilter.ts new file mode 100644 index 0000000000000000000000000000000000000000..e33e660ff260cd808be794a300221f84abf0dd4d --- /dev/null +++ b/src/model/accession/AccessionGeoFilter.ts @@ -0,0 +1,17 @@ +import ClimateFilter from 'model/genesys/ClimateFilter'; +import NumberFilter from 'model/filter/NumberFilter'; + +/* +* Defined in Swagger as '#/definitions/AccessionGeoFilter' +*/ +class AccessionGeoFilter { + public climate: ClimateFilter; + public elevation: NumberFilter; + public latitude: NumberFilter; + public longitude: NumberFilter; + public referenced: boolean; + + +} + +export default AccessionGeoFilter; diff --git a/src/model/accession/AccessionSuggestionPage.ts b/src/model/accession/AccessionSuggestionPage.ts new file mode 100644 index 0000000000000000000000000000000000000000..7fbc69b5d3ec443ae263d77842d4707b62a69bf5 --- /dev/null +++ b/src/model/accession/AccessionSuggestionPage.ts @@ -0,0 +1,11 @@ +import Accession from './Accession'; +import FilteredPage from '../FilteredPage'; + +/* +* Defined in Swagger as '#/definitions/AccessionSuggestionPage' +*/ +class AccessionSuggestionPage extends FilteredPage { + public suggestions: any; +} + +export default AccessionSuggestionPage; diff --git a/src/model/geo/CountryFilter.ts b/src/model/geo/CountryFilter.ts index 787fd7573d87e6a6cdbfe4997c7450feb6fade12..007c361bdf909a4b2bac441761d45418943b338a 100644 --- a/src/model/geo/CountryFilter.ts +++ b/src/model/geo/CountryFilter.ts @@ -3,7 +3,7 @@ * Defined in Swagger as '#/definitions/CountryFilter' */ class CountryFilter { - public iso3: string[]; + public code3: string[]; } diff --git a/src/service/genesys/AccessionService.ts b/src/service/genesys/AccessionService.ts index c108dec75bdd35954b8276f84d394b7fc2eb2069..953632187f69e2832ba3d3858fbd1b49c4788016 100644 --- a/src/service/genesys/AccessionService.ts +++ b/src/service/genesys/AccessionService.ts @@ -10,6 +10,7 @@ import AccessionMapInfo from 'model/accession/AccessionMapInfo'; import FilteredPage, { IPageRequest } from 'model/FilteredPage'; import {AccessionRef} from 'model/accession/AccessionRef'; import AccessionAuditLog from 'model/accession/AccessionAuditLog'; +import AccessionSuggestionPage from 'model/accession/AccessionSuggestionPage'; const URL_GET_BY_DOI = `/api/v1/acn/{doi}`; // UrlTemplate doesn't like the / in DOI const URL_GET_BY_UUID = UrlTemplate.parse(`/api/v1/acn/{uuid}`); @@ -22,6 +23,7 @@ const URL_GET_DETAILS_BY_DOI = `/api/v1/acn/details/{doi}`; // UrlTemplate doesn const URL_GET_DETAILS_BY_UUID = UrlTemplate.parse(`/api/v1/acn/details/{uuid}`); const URL_TO_UUID = `/api/v1/acn/toUUID`; const URL_LIST = `/api/v1/acn/list`; +const URL_LIST_SUGGESTIONS = `/api/v1/acn/list-suggestions`; const URL_LIST_OVERVIEW = `/api/v1/acn/overview`; const URL_MAPINFO = `/api/v1/acn/mapinfo`; @@ -234,6 +236,36 @@ class AccessionService { }).then(({ data }) => data as FilteredPage); } + /** + * listSuggestions at /api/v1/acn/list-suggestions + * + * @param filter filter + * @param f f + * @param page undefined + * @param xhrConfig additional xhr config + */ + public static listSuggestions(filter: string | AccessionFilter, page: IPageRequest, xhrConfig?): Promise { + + const qs = QueryString.stringify({ + f: typeof filter === 'string' ? filter : undefined, + p: page.page || undefined, + l: page.size || 100, + d: page && page.direction ? page.direction : Accession.DEFAULT_SORT.direction, + s: page.properties || Accession.DEFAULT_SORT.property, + }, {}); + const apiUrl = URL_LIST_SUGGESTIONS + (qs ? `?${qs}` : ''); + // console.log(`Fetching from ${apiUrl}`); + const content = { data: typeof filter === 'string' ? null : { ...filter } }; + + + return axiosBackend.request({ + ...xhrConfig, + url: apiUrl, + method: 'POST', + ...content, + }).then(({ data }) => data as AccessionSuggestionPage); + } + public static listAllByUuid(UUIDs: string[], xhrConfig?): Promise { const apiUrl = URL_LIST_BY_UUID; diff --git a/src/subsets/ui/c/Filters.tsx b/src/subsets/ui/c/Filters.tsx index eb3c5f6ff46a664aec7f92dbae13e6e76fd43f75..2d087d091a52f586764a47e5925c6e754fbad4fa 100644 --- a/src/subsets/ui/c/Filters.tsx +++ b/src/subsets/ui/c/Filters.tsx @@ -23,7 +23,7 @@ const SubsetFilters = ({handleSubmit, initialValues, initialize, t, ...other}) = - + ); diff --git a/src/subsets/ui/dashboard/c/SubsetFilters.tsx b/src/subsets/ui/dashboard/c/SubsetFilters.tsx index 141c7898b6468d7883395b099e16d8a6672d8a7f..db47b5cbb757f8732f3217f36a44b4fe2ad6e74e 100644 --- a/src/subsets/ui/dashboard/c/SubsetFilters.tsx +++ b/src/subsets/ui/dashboard/c/SubsetFilters.tsx @@ -28,7 +28,7 @@ const SubsetFilters = ({handleSubmit, initialValues, initialize, t, ...other}) = - + ); diff --git a/src/translations.json b/src/translations.json index 6fb0b029af521af82d3b0c0b47fc38b63c4bb72a..1a5a54a3d8d725e9b7f1603e35d6ada5cc6405c7 100644 --- a/src/translations.json +++ b/src/translations.json @@ -36,7 +36,8 @@ "gt": "{{what}} >", "accessions": { "crop": "Crop", - "acceNumb": "Accession number", + "crops": "Crops", + "accessionNumber": "Accession number", "seqNo": "Sequential number", "sampStat": "Biological status", "storage": "Storage", @@ -52,14 +53,14 @@ "bio15": "Precipitation seasonality" } }, - "holder": { + "institute": { "code": "Holder", "networks": "Network", "owner": { "uuid": "Data provider" }, "country": { - "iso3": "Holder country", + "code3": "Holder country", "region": "Holder region" } }, @@ -68,8 +69,11 @@ "species": "Species", "subtaxa": "Subtaxa" }, - "origin": { - "iso3": "Provenance" + "owner": { + "uuid": "Data provider" + }, + "countryOfOrigin": { + "code3": "Provenance" }, "taxonomy": { "genus": "Genus", @@ -107,17 +111,20 @@ }, "subsets": { "title": "Title", - "crop": "Crop", + "crops": "Crop", "institutes": "Institute code", "description": "Description", "state": "Status", - "dateCreated": "Date created" + "dateCreated": "Date created", + "owner": { + "uuid": "Data provider" + } }, "wiews": { "code": "Institute code", "accessions": "Accessions in Genesys", "country": { - "iso3": "Country code" + "code3": "Country code" }, "owner": { "uuid": "Data provider" @@ -139,14 +146,14 @@ }, "datasets": { "_text": "Keywords", - "accessionRef": { + "accessionRefs": { "acceNumb": "Accession number", "doi": "Accession DOI", "genus": "Accession genus", "instCode": "Accession holder" }, "description": "Description", - "descriptor": { + "descriptors": { "_text": "Descriptor keywords", "title": "Descriptor title" }, @@ -155,7 +162,7 @@ "shortName": "Provider short name", "wiewsCodes": "Owner WIEWS institute code" }, - "location": { + "locations": { "country": "Evaluated in", "latitude": "Latitude", "longitude": "Longitude" @@ -163,7 +170,7 @@ "published": "Published", "title": "Title", "rights": "License", - "crop": "Crop", + "crops": "Crop", "state": "Status" }, "partner": { @@ -196,7 +203,7 @@ "category": "Category", "columnName": "Column name", "key": "Key crop descriptor", - "list": { + "descriptorLists": { "uuid": "Descriptor list" }, "owner": { @@ -343,4 +350,4 @@ "inProgress": "Draft", "published": "Published" } -} \ No newline at end of file +} diff --git a/src/ui/common/Properties.tsx b/src/ui/common/Properties.tsx index ff932ff73f662ff1e5820dc6f37c41821f6691ad..289209070b84e00f0de9ae44bc9ffb1556955ca5 100644 --- a/src/ui/common/Properties.tsx +++ b/src/ui/common/Properties.tsx @@ -69,13 +69,13 @@ interface IItemProps extends React.ClassAttributes { class PropertiesItem1 extends React.Component, any> { public render() { - const { keepEmpty, small, md, numeric, title, classes, className, children, t, style } = this.props; + const { keepEmpty, small, md, numeric, title, classes, className, children, t, style, ...other} = this.props; if (!keepEmpty && ! children) { return null; } const hasTitle: boolean = title !== null && title; return ( - + { hasTitle && ( diff --git a/src/ui/common/filter/BooleanFilter.tsx b/src/ui/common/filter/BooleanFilter.tsx index 0d7bcad69625705694fc645e4ca33b63a5b73787..1940e72b1145179dff7651c4fe9ccdf20464e037 100644 --- a/src/ui/common/filter/BooleanFilter.tsx +++ b/src/ui/common/filter/BooleanFilter.tsx @@ -2,75 +2,44 @@ import * as React from 'react'; import {translate} from 'react-i18next'; import { Field } from 'redux-form'; -import Radio from '@material-ui/core/Radio'; -import RadioGroup from '@material-ui/core/RadioGroup'; -import FormControlLabel from '@material-ui/core/FormControlLabel'; -import FormLabel from '@material-ui/core/FormLabel'; -import FormControl from '@material-ui/core/FormControl'; - -interface IBooleanFilterInternal extends React.ClassAttributes { - input: any; - label: string; - t: any; -} - -class BooleanFilterInternal extends React.Component { - - private constructor(props, context) { - super(props, context); - - this.state = { - value: props.input.value, - }; - } - - private handleChange = (event, value) => { - const { input } = this.props; - this.setState({ value }); - input.onChange(value === '' ? null : value === 'true'); - } - - public componentWillReceiveProps(nextProps) { - this.setState({value: nextProps.input.value}); - } - - public render() { - const { label, t } = this.props; - return ( -
- - { label } - - } label={ t('common:label.yes') } /> - } label={ t('common:label.no') } /> - } label={ t('common:label.either') } /> - - -
- ); - } -} +import Number from 'ui/common/Number'; +import RadioSelection from 'ui/common/forms/RadioSelection'; interface IBooleanFilter extends React.ClassAttributes { name: string; label?: string; + terms: Map; t: any; } class BooleanFilter extends React.Component { + private getLabel = (val) => val === 'true' ? this.props.t(`common:label.yes`) : val === 'false' ? this.props.t(`common:label.no`) : this.props.t(`common:label.either`); + + private getTermsValue = (val) => val === 'true' ? this.props.terms.get('1') : val === 'false' ? this.props.terms.get('0') : (this.props.terms.get('1') + this.props.terms.get('0')) || '0'; + public render() { - const { name , label, t } = this.props; + const {terms, name, label} = this.props; return (
value === '' ? null : value === 'true' } + options={ [true, false, ''] } + classes={ {label: 'full-width'} } + renderOptionLabel={ (stat) => + + { `${this.getLabel(`${stat}`)}` } + { terms && + + + + } + + } />
); diff --git a/src/ui/common/filter/StringArrFilter.tsx b/src/ui/common/filter/StringArrFilter.tsx index c0b78cf1c51aaa88c3cd7716edd88907fd86cd26..bf973a8e569b2a322922825c4b9a67127a44ae25 100644 --- a/src/ui/common/filter/StringArrFilter.tsx +++ b/src/ui/common/filter/StringArrFilter.tsx @@ -1,21 +1,20 @@ import * as React from 'react'; -import { Fields, change } from 'redux-form'; +import { Fields, change, FieldArray } from 'redux-form'; import {connect} from 'react-redux'; import {bindActionCreators} from 'redux'; import { translate } from 'react-i18next'; import * as _ from 'lodash'; -import FormGroup from '@material-ui/core/FormGroup'; -import FormControlLabel from '@material-ui/core/FormControlLabel'; -import FormLabel from '@material-ui/core/FormLabel'; import FormControl from '@material-ui/core/FormControl'; -import Checkbox from '@material-ui/core/Checkbox'; import IconButton from '@material-ui/core/IconButton'; import Input from '@material-ui/core/Input'; import InputLabel from '@material-ui/core/InputLabel'; import InputAdornment from '@material-ui/core/InputAdornment'; import PlusOne from '@material-ui/icons/PlusOne'; +import CheckboxSelection from '../forms/CheckboxSelection'; +import Number from 'ui/common/Number'; +import {Properties, PropertiesItem} from 'ui/common/Properties'; interface IStringListProps extends React.ClassAttributes { input: any; @@ -210,13 +209,6 @@ class InternalStringArrField extends React.Component { - const { input } = _.get(this.props, this.props.names[0]); - - const values = event.target.checked ? this.maybeAdd(event.target.value) : this.maybeRemove(event.target.value); - input.onChange(values); - } - private removeByIndex = (index, isNot) => { const { input } = _.get(this.props, this.props.names[isNot ? 1 : 0]); @@ -239,30 +231,44 @@ class InternalStringArrField extends React.Component +val) : options); + + const filteredOptions = terms ? (fieldOptions && fieldOptions.filter((opt) => terms.get(valueField ? opt[valueField] : `${opt}`) > 0)) : fieldOptions; + return (
{ withOptions ? - - { label && { t(`${label}`) } } - - { Object.keys(options).map((key) => ( - = 0 } - onChange={ this.handleCheckbox } /> - } /> - )) } - - + (!filteredOptions || filteredOptions.length === 0 ? + (
{ t('common:f.noFilters') }
) + : + ( + + { `${t(options[opt]) || (labelField ? opt[labelField] : opt)}` } + { terms && + + + + } + + } + />) + ) : { t(`${label}`) } @@ -286,6 +292,19 @@ class InternalStringArrField extends React.Component 0) || (notValues && notValues.length > 0))) && } + { !withOptions && terms && + +
{ t('common:f.suggestedFilters') }
+ { terms && Array.from(terms).slice(0, 10).map(([key, value]) => ( + input.onChange(this.maybeAdd(key)) } classes={ {propertiesRow: 'cursor-pointer'} }> + + + + + )) } +
+ } +
); } @@ -296,6 +315,10 @@ interface IStringArrFilter extends React.ClassAttributes { placeholder?: string; label?: string; options?: { [key: string]: any; }; + terms?: { [key: string]: any; }; + valueField?: string; + labelField?: string; + byKey?: boolean; t: any; } @@ -303,7 +326,7 @@ class StringArrFilter extends React.Component { public render() { - const { name, label, placeholder, options, t } = this.props; + const { name, label, placeholder, options, terms, valueField, labelField, byKey, t } = this.props; return (
{ label={ t(label) } placeholder={ placeholder } options={ options } + terms={ terms } + valueField={ valueField } + labelField={ labelField } + byKey={ byKey } t={ t } />
diff --git a/src/ui/common/forms/CheckboxSelection.tsx b/src/ui/common/forms/CheckboxSelection.tsx index 97dbb31e77b2f7d4b4778318374ddf812489d891..a12a42b5c4928e332b5a6e688487be3c41b41c2f 100644 --- a/src/ui/common/forms/CheckboxSelection.tsx +++ b/src/ui/common/forms/CheckboxSelection.tsx @@ -29,6 +29,9 @@ const styles = (theme) =>({ }, }, }, + label: { + // just mock for overriding + }, }); /* tslint:enable */ @@ -42,8 +45,8 @@ const handleCheckboxChange = (fields, code) => { } }; -const CheckboxSelection = ({formLabel, singleColumn = false, options, renderOptionLabel, valueField, fields, input, meta, classes, t, ...rest}) => ( - +const CheckboxSelection = ({formLabel, singleColumn = false, options, renderOptionLabel, valueField, fields, input, required = false, meta, classes, t, ...rest}) => ( + { formLabel } handleCheckboxChange(fields,valueField ? option[valueField] : option) } // tslint:disable-line /> } - className={ classes.label } /> )) } diff --git a/src/ui/common/forms/RadioSelection.tsx b/src/ui/common/forms/RadioSelection.tsx index b72acc3159609cead18acfbca8f7218521547f9c..8f3c1d9eb81de2c1312dec307e769e8fd4380ecd 100644 --- a/src/ui/common/forms/RadioSelection.tsx +++ b/src/ui/common/forms/RadioSelection.tsx @@ -26,17 +26,20 @@ const styles = (theme) =>({ }, }, }, + label: { + // just mock for overriding + }, }); /* tslint:enable */ -const RadioSelection = ({formLabel, singleColumn = false, options, renderOptionLabel, valueField, input, meta, classes, t, ...rest}) => ( - +const RadioSelection = ({formLabel, singleColumn = false, options, renderOptionLabel, valueField, input, meta, handleChange, classes, required = false, t, ...rest}) => ( + { formLabel } input.onChange(value) // tslint:disable-line } className={ singleColumn ? classes.radioGroupSingleColumn : classes.radioGroupMultiColumn } @@ -48,7 +51,7 @@ const RadioSelection = ({formLabel, singleColumn = false, options, renderOptionL value={ valueField ? option[valueField] : option } label={ renderOptionLabel ? renderOptionLabel(option) : valueField ? option[valueField] : option } control={ } - className={ classes.label } + classes={ {label: classes.label} } { ...rest } /> )) diff --git a/src/user/ui/admin/c/UserFilter.tsx b/src/user/ui/admin/c/UserFilter.tsx index b8610bb1afe1894910cb56cae68d2cf7f2559d7b..a0f3a7b6aacc620a93dc569aefc4b3e11d9be92e 100644 --- a/src/user/ui/admin/c/UserFilter.tsx +++ b/src/user/ui/admin/c/UserFilter.tsx @@ -22,7 +22,7 @@ const UserFilters = ({handleSubmit, initialValues, initialize, ...other}) => { - + diff --git a/styles/app.styles.scss b/styles/app.styles.scss index b6d2d57eca13e921970bb179c3a7a84c7f73e373..cb78ea45ed03a63436cbfc0d6158882c2d383091 100644 --- a/styles/app.styles.scss +++ b/styles/app.styles.scss @@ -464,6 +464,10 @@ $mui-breakpoints: ( text-transform: uppercase; } +.cursor-pointer { + cursor: pointer; +} + table.simple { width: 100%; thead {