diff --git a/src/accession/AccessionDetailsPage.tsx b/src/accession/AccessionDetailsPage.tsx index bca476c22f47863595e4654cf1008ad99560e1dd..964f87d26d435779c046ae03c66251da4fa0d040 100644 --- a/src/accession/AccessionDetailsPage.tsx +++ b/src/accession/AccessionDetailsPage.tsx @@ -16,10 +16,16 @@ import Loading from 'ui/common/Loading'; import PageTitle from 'ui/common/PageTitle'; import Markdown from 'ui/common/Markdown'; import McpdDate from "ui/common/McpdDate"; +import GenericHit from '@genesys-pgr/client/model/GenericHit'; +import Accession from '@genesys-pgr/client/model/accession/Accession'; +import SimilarAccessionsTable from "accession/c/SimilarAccessionsTable"; interface AccessionDetailsPageState { accession: AccessionDetails; cartItems: string[]; + similar: GenericHit[]; + selectedAccessions: string[]; + loadingSimilar: boolean; } interface AccessionDetailsPageProps { @@ -31,17 +37,18 @@ class AccessionDetailsPage extends React.Component) => { + const { appConfig } = this.props; + const { loadingSimilar } = this.state; + const uuid = e.currentTarget.getAttribute('data-uuid') + if (loadingSimilar) { + return; + } + this.setState({ loadingSimilar: true }) + AccessionService.getSimilarAccessionsForUUID(uuid, appConfig.filter).then((similar) => { + if (similar && similar.length > 0) { + this.setState({ + similar, + loadingSimilar: false + }); + console.log(`Genesys returned ${similar.length} matches.`); + } else { + console.log('No matching accessions were found.'); + } + }).catch((e) => { + this.setState({ loadingSimilar: false }) + console.log(e); + }) + } + private renderAddToCart = () => { const { accession } = this.state; const { t } = this.props; @@ -109,13 +140,26 @@ class AccessionDetailsPage extends React.Component } + private onSelectedAccessions = (selectedAccessions) => { + this.setState({ selectedAccessions }) + } + + private addSelectedToCart = () => { + const { selectedAccessions } = this.state; + LocalStorageCart.addToCart(selectedAccessions); + } + + private closeSimilar = () => { + this.setState({ similar: null }) + } + public render() { - const { accession, cartItems } = this.state; + const { accession, cartItems, selectedAccessions, similar, loadingSimilar } = this.state; const { t, appConfig: { apiUrl, shoppingCart, map }, appConfig } = this.props; let propertyIndex = 0; - if (accession === null) { + if (accession === null || loadingSimilar) { return ( <> @@ -126,27 +170,37 @@ class AccessionDetailsPage extends React.Component + !similar ? <>

{ details.accessionNumber }

- { shoppingCart.enabled && -
- { cartItems.includes(this.props.match.params.uuid) ? - - : - this.renderAddToCart() - } -
- } +
+ + { shoppingCart.enabled && + <> + { cartItems.includes(this.props.match.params.uuid) ? + + : + this.renderAddToCart() + } + + } +
{ details.historic &&
{ t('accession.historicalNote') }
} @@ -262,16 +316,16 @@ class AccessionDetailsPage extends React.Component } - { details.geo && + { (details.latitude || details.longitude || details.coordinateDatum || details.coordinateUncertainty || details.elevation) && <>

{ t('accession.details.geo') }

- - - - - - { map.enabled && details.institute && details.geo.latitude && details.geo.longitude && - + + + + + + { map.enabled && details.institute && details.latitude && details.longitude && + } } @@ -330,6 +384,24 @@ class AccessionDetailsPage extends React.Component } + : + <> + +
+

{ t('accession.details.similarTo', { accNumber: details.accessionNumber }) }

+
+ { selectedAccessions?.length !== 0 && + + } + +
+
+ + ); } } diff --git a/src/accession/c/SimilarAccessionsTable.tsx b/src/accession/c/SimilarAccessionsTable.tsx new file mode 100644 index 0000000000000000000000000000000000000000..a9460797faf86e17d07c898d6e06de3d188fab87 --- /dev/null +++ b/src/accession/c/SimilarAccessionsTable.tsx @@ -0,0 +1,141 @@ +import React from 'react'; +import { Link } from 'react-router-dom'; +import { useTranslation } from 'react-i18next'; + +// hook +import useAccessionCart from 'accession/hook/useAccessionCart'; +// util +import { canAddToCart } from 'utilities'; +// ui +import { CountryName } from 'ui/common/CountryName'; +import Loading from 'ui/common/Loading'; +import GenericHit from '@genesys-pgr/client/model/GenericHit'; +import Accession from '@genesys-pgr/client/model/accession/Accession'; + +interface IAccessionTable { + accessions: GenericHit[]; + onSelectedModified?: (selected: string[]) => void; + noRedirect?: boolean +} + +const AccessionTable = ({ accessions, onSelectedModified, noRedirect }: IAccessionTable) => { + + // util + const { t } = useTranslation(); + // custom + const { shoppingCartEnabled, addToCart, removeFromCart, cartItems } = useAccessionCart(); + + // state + const [{ isAllSelected, selectedUUIDs }, setSelected] + = React.useState<{ isAllSelected: boolean, selectedUUIDs: string[] }>({ isAllSelected: false, selectedUUIDs: [] }); + + const toggleRowSelect = React.useCallback((e: React.ChangeEvent) => { + const { uuid } = (e.currentTarget as HTMLElement).dataset; + const updated = selectedUUIDs.filter((selectedUuid) => selectedUuid !== uuid); + if (updated.length === selectedUUIDs.length) { + updated.push(uuid); + } + setSelected({ selectedUUIDs: updated, isAllSelected: updated.length === accessions?.length }); + }, [accessions?.length, selectedUUIDs]); + + const onToggleAll = React.useCallback(() => { + setSelected({ + selectedUUIDs: isAllSelected + ? [] + : accessions?.filter((a) => canAddToCart(a as any) && a.uuid).map((a) => a.uuid), + isAllSelected: !isAllSelected, + }); + }, [accessions, isAllSelected]); + + React.useEffect(() => { + if (onSelectedModified) { + onSelectedModified(selectedUUIDs); + } + }, [onSelectedModified, selectedUUIDs, isAllSelected]); + + + if (!accessions) { + return ; + } + + return ( + <> + + + + { shoppingCartEnabled && ( + + ) } + + + + + + + { shoppingCartEnabled && () } + + + + { accessions + .map((a) => ({ + ...a, + canAdd: canAddToCart(a as any), + included: selectedUUIDs.includes(a.uuid), + inCart: cartItems.includes(a.uuid) + })) + .map((a, i) => ( + + { shoppingCartEnabled && ( + + ) } + + + + + + + { shoppingCartEnabled && + + } + + )) } + +
+ + { t(['accession.crop', '_.crop']) }{ t('accession.acceNumb') }{ t('accession.accessionName') }{ t('accession.taxonomy.scientificName') }{ t('accession.countryOfOrigin') }{ t('accession.sampStat') }{ t('list.availability') }
+ { a.canAdd && + + } + { a.crop && a.crop.name || a.cropName }{ a.accessionNumber }{ a.accessionName && { a.accessionName } }{ a.countryOfOrigin && ( + || a.countryOfOrigin.name) }{ a.sampStat && t(`accession.sampleStatus.${ a.sampStat }`) } + { a.canAdd && + + } +
+ + ); +}; + +export default AccessionTable; diff --git a/src/locales/en/translations.json b/src/locales/en/translations.json index 984f3841b29a0ad442810fc7de0e0b6aaf280dd2..fa6ba5b8b72d49174a73caa25fe546daadbfcc86 100644 --- a/src/locales/en/translations.json +++ b/src/locales/en/translations.json @@ -14,7 +14,8 @@ }, "action": { "submit": "Submit", - "download": "Download" + "download": "Download", + "close": "Close" }, "pag": { "first": "First", @@ -81,6 +82,8 @@ "datasets": "Datasets", "subsets": "Subsets", "details": { + "findAlternatives": "Find alternatives", + "similarTo": "Similar to {{accNumber}}", "providedTaxonomy": "Taxonomy", "metadata": "Metadata", "collSite": "Collecting site",