diff --git a/src/js/components/Provenance/DatasetProvenance.tsx b/src/js/components/Provenance/DatasetProvenance.tsx index d1ed9f2a..65949796 100644 --- a/src/js/components/Provenance/DatasetProvenance.tsx +++ b/src/js/components/Provenance/DatasetProvenance.tsx @@ -30,7 +30,7 @@ const DatasetProvenance = ({ metadata, loading }: DatasetProvenanceProps) => { {t(metadata.version)} , ]} - style={{ borderRadius: '11px', maxWidth: '1400px' }} + style={{ borderRadius: '11px' }} loading={loading} > {/* --- DESCRIPTION ---*/} @@ -44,7 +44,7 @@ const DatasetProvenance = ({ metadata, loading }: DatasetProvenanceProps) => { {t(metadata.privacy)} )} - {metadata.licenses.length && ( + {!!metadata.licenses?.length && ( }> {metadata.licenses.map((l, i) => ( @@ -53,11 +53,11 @@ const DatasetProvenance = ({ metadata, loading }: DatasetProvenanceProps) => { ))} )} - {metadata.keywords.length && ( + {!!metadata.keywords?.length && ( }> {metadata.keywords.map((keyword, i) => ( - {t(keyword.value)} + {t(keyword.value.toString())} ))} @@ -67,32 +67,60 @@ const DatasetProvenance = ({ metadata, loading }: DatasetProvenanceProps) => { {/* TableTitle has translation in it*/} {/* --- CREATED BY ---*/} - - + {!!metadata.creators?.length && ( + <> + + + + )} {/* --- DISTRIBUTIONS ---*/} - - + {!!metadata.distributions?.length && ( + <> + + + + )} {/* --- IS ABOUT ---*/} - - + {!!metadata.isAbout?.length && ( + <> + + + + )} {/* --- PUBLICATIONS ---*/} - - + {!!metadata.primaryPublications?.length && ( + <> + + + + )} {/* --- ACKNOWLEDGES ---*/} - - + {!!metadata.acknowledges?.length && ( + <> + + + + )} {/* --- SPATIAL COVERAGE ---*/} - - + {!!metadata.spatialCoverage?.length && ( + <> + + + + )} {/* --- EXTRA PROPERTIES ---*/} - - + {!!metadata.extraProperties?.length && ( + <> + + + + )} {/* --- DOWNLOAD DATS --- */} @@ -108,7 +136,7 @@ export type DatasetProvenanceProps = { export default DatasetProvenance; -const TableTitleWitTranslation = ({ title }: { title: string }) => { +const TableTitleWithTranslation = ({ title }: { title: string }) => { const { t } = useTranslation(DEFAULT_TRANSLATION); return ( diff --git a/src/js/components/Provenance/Tables/AcknowledgesTable.tsx b/src/js/components/Provenance/Tables/AcknowledgesTable.tsx index 165c3f65..92100add 100644 --- a/src/js/components/Provenance/Tables/AcknowledgesTable.tsx +++ b/src/js/components/Provenance/Tables/AcknowledgesTable.tsx @@ -1,5 +1,4 @@ import React from 'react'; -import { Tag } from 'antd'; import BaseProvenanceTable from './BaseProvenanceTable'; import LinkIfUrl from '../../Util/LinkIfUrl'; @@ -24,10 +23,11 @@ const AcknowledgesTable = ({ acknowledges }: AcknowledgesTableProps) => { dataIndex: 'funders', render: (_, { funders }) => funders.map((f, i) => ( - + - {f.abbreviation ? `(${t(f.abbreviation)})` : ''} - + {f.abbreviation ? ` (${t(f.abbreviation)})` : ''} + {i < funders.length - 1 ? '; ' : ''} + )), }, ]} diff --git a/src/js/components/Provenance/Tables/CreatedByTable.tsx b/src/js/components/Provenance/Tables/CreatedByTable.tsx index 30ddb65e..7a837d55 100644 --- a/src/js/components/Provenance/Tables/CreatedByTable.tsx +++ b/src/js/components/Provenance/Tables/CreatedByTable.tsx @@ -33,7 +33,7 @@ const CreatedByTable = ({ creators }: CreatedByTableProps) => { roles && roles.map((r, i) => ( - {t(r.value)} + {t(r.value.toString())} )), }, diff --git a/src/js/components/Provenance/Tables/DistributionsTable.tsx b/src/js/components/Provenance/Tables/DistributionsTable.tsx index e87c0151..aff70350 100644 --- a/src/js/components/Provenance/Tables/DistributionsTable.tsx +++ b/src/js/components/Provenance/Tables/DistributionsTable.tsx @@ -1,19 +1,20 @@ -import React from 'react'; +import React, { useMemo } from 'react'; + import { Tag, Typography } from 'antd'; +import { ColumnsType } from 'antd/es/table'; const { Link } = Typography; import BaseProvenanceTable from './BaseProvenanceTable'; import { useTranslationDefault, useTranslationCustom } from '@/hooks'; -import { ProvenanceStoreDataset } from '@/types/provenance'; +import { Distribution, ProvenanceStoreDataset } from '@/types/provenance'; const DistributionsTable = ({ distributions }: DistributionsTableProps) => { const t = useTranslationCustom(); const td = useTranslationDefault(); - return ( - + [ { title: td('Formats'), dataIndex: 'formats', @@ -27,7 +28,7 @@ const DistributionsTable = ({ distributions }: DistributionsTableProps) => { { title: td('Unit'), dataIndex: 'unit', - render: (_, { unit }) => t(unit.value), + render: (_, { unit }) => t(unit.value.toString()), }, { title: td('Access'), @@ -47,15 +48,17 @@ const DistributionsTable = ({ distributions }: DistributionsTableProps) => { render: (_, { access }) => access.authorizations.map((a, i) => ( - {t(a.value)} + {t(a.value.toString())} )), }, ], }, - ]} - /> + ] as ColumnsType, + [td] ); + + return ; }; export interface DistributionsTableProps { diff --git a/src/js/components/Provenance/Tables/ExtraPropertiesTable.tsx b/src/js/components/Provenance/Tables/ExtraPropertiesTable.tsx index 2032193c..1cecb2ed 100644 --- a/src/js/components/Provenance/Tables/ExtraPropertiesTable.tsx +++ b/src/js/components/Provenance/Tables/ExtraPropertiesTable.tsx @@ -1,33 +1,36 @@ -import React from 'react'; -import { Tag } from 'antd'; +import React, { useMemo } from 'react'; + +import { ColumnsType } from 'antd/es/table'; import BaseProvenanceTable from '@/components/Provenance/Tables/BaseProvenanceTable'; import LinkIfUrl from '../../Util/LinkIfUrl'; import { useTranslationCustom, useTranslationDefault } from '@/hooks'; -import { ProvenanceStoreDataset } from '@/types/provenance'; +import { ExtraProperty, ProvenanceStoreDataset } from '@/types/provenance'; const ExtraPropertiesTable = ({ extraProperties }: ExtraPropertiesTableProps) => { const t = useTranslationCustom(); const td = useTranslationDefault(); - return ( - + [ { title: td('Category'), dataIndex: 'category', render: (text) => t(text) }, { title: td('Values'), dataIndex: 'values', render: (_, { values }) => values.map((v, i) => ( - - - + <> + + {i < values.length - 1 ? '; ' : ''} + )), }, - ]} - /> + ] as ColumnsType, + [td] ); + + return ; }; export interface ExtraPropertiesTableProps { diff --git a/src/js/components/Provenance/Tables/PublicationsTable.tsx b/src/js/components/Provenance/Tables/PublicationsTable.tsx index 59a071e7..772eba28 100644 --- a/src/js/components/Provenance/Tables/PublicationsTable.tsx +++ b/src/js/components/Provenance/Tables/PublicationsTable.tsx @@ -1,70 +1,124 @@ -import React from 'react'; -import { Tag, Typography } from 'antd'; +import React, { ReactNode, useMemo } from 'react'; + +import { Typography } from 'antd'; +import { ColumnsType } from 'antd/es/table'; import BaseProvenanceTable from './BaseProvenanceTable'; import LinkIfUrl from '../../Util/LinkIfUrl'; import { useTranslationCustom, useTranslationDefault } from '@/hooks'; -import { ProvenanceStoreDataset } from '@/types/provenance'; +import { Person, PrimaryPublication, ProvenanceStoreDataset } from '@/types/provenance'; + +import { stringIsDOI, stringIsURL } from '@/utils/strings'; + +const URLLink = ({ url, children }: { url: string; children?: ReactNode }) => ( + + {children || url} + +); + +const DOILink = ({ doi, children }: { doi: string; children?: ReactNode }) => ( + {children || doi} +); + +const formatAuthorList = (authors: Person[]): string => { + const fullNames = authors.map((a) => a.fullName); + if (fullNames.length === 0) { + return ''; + } else if (fullNames.length === 1) { + return `${fullNames[0]}.`; + } else if (fullNames.length === 2) { + return `${fullNames.join(' and ')}.`; + } else { + return `${fullNames.slice(0, -1).join(', ')}, and ${fullNames.at(-1)}.`; + } +}; const PublicationsTable = ({ publications }: PublicationsTableProps) => { const t = useTranslationCustom(); const td = useTranslationDefault(); - return ( - + [ { - title: td('Title'), - dataIndex: 'title', + title: td('Publication'), + key: 'publication', + + render: (_, { title, identifier: { identifier }, authors }) => { + const formattedTitle = `${t(title)}${title.endsWith('.') ? '' : '.'}`; + return ( + <> + {stringIsDOI(identifier) ? ( + {formattedTitle} + ) : stringIsURL(identifier) ? ( + {formattedTitle} + ) : ( + formattedTitle + )} + {!!authors?.length && ( + <> +
+ {formatAuthorList(authors)} + + )} + + ); + }, - render: (_, { title, identifier }) => - identifier.identifier === '' ? ( - t(title) - ) : ( - - {t(title)} - - ), + sorter: (a, b) => a.title.localeCompare(b.title), }, { title: td('Publication Venue'), dataIndex: 'publicationVenue', render: (text) => t(text), + sorter: (a, b) => a.publicationVenue.localeCompare(b.publicationVenue), }, { - title: td('Authors'), - dataIndex: 'authors', - render: (_, { authors }) => - authors.map((author, i) => ( - - {author} - - )), - }, - { - title: td('Dates'), + title: td('Date'), dataIndex: 'dates', - render: (_, { dates }) => - dates.map((date, i) => ( - - {date} - - )), + render: (_, { dates }) => { + const _dates = dates ?? []; + return _dates.map((date, i) => ( + <> + {new Date(Date.parse(date.date)).toLocaleDateString()} + {i < _dates.length - 1 ? '; ' : ''} + + )); + }, + sorter: (a, b) => { + if (!a.dates?.length) { + if (!b.dates?.length) return 0; + return 1; // Sort blank entries after + } else if (!b.dates?.length) { + return -1; // Sort blank entries after + } else { + return Date.parse(a.dates[0].date) - Date.parse(b.dates[0].date); + } + }, + defaultSortOrder: 'descend', }, { title: td('Identifier'), dataIndex: 'identifier.identifier', - render: (_, { identifier }) => , + render: (_, { identifier: { identifier } }) => + stringIsDOI(identifier) ? : , }, { title: td('Identifier Source'), dataIndex: 'identifier.identifierSource', - render: (_, { identifier }) => t(identifier.identifierSource), + render: (_, { identifier: { identifierSource } }) => t(identifierSource), + sorter: (a, b) => a.identifier.identifierSource.localeCompare(b.identifier.identifierSource), + filters: Array.from(new Set(publications.map((p) => p.identifier.identifierSource))).map((v) => ({ + text: v, + value: v, + })), + onFilter: (filterValue, p) => p.identifier.identifierSource === filterValue, }, - ]} - /> + ] as ColumnsType, + [td, publications] ); + + return ; }; export interface PublicationsTableProps { diff --git a/src/js/components/Util/LinkIfUrl.tsx b/src/js/components/Util/LinkIfUrl.tsx new file mode 100644 index 00000000..9d1eb448 --- /dev/null +++ b/src/js/components/Util/LinkIfUrl.tsx @@ -0,0 +1,18 @@ +import React from 'react'; +import { Typography } from 'antd'; + +import { stringIsURL } from '@/utils/strings'; + +// Renders text as link if the text provided is a valid url. +const LinkIfUrl = ({ text }: { text: string }) => { + if (stringIsURL(text)) { + return ( + + {text} + + ); + } + return <>{text}; +}; + +export default LinkIfUrl; diff --git a/src/js/components/Util/LinkIfUrl.js b/src/js/constants/patterns.ts similarity index 51% rename from src/js/components/Util/LinkIfUrl.js rename to src/js/constants/patterns.ts index a05bf25e..2fe9af6e 100644 --- a/src/js/components/Util/LinkIfUrl.js +++ b/src/js/constants/patterns.ts @@ -1,5 +1,4 @@ -import React from 'react'; -import { Typography } from 'antd'; +export const DOI_PATTERN = /^10.\d{4,9}\/[-._;()/:A-Z0-9]+$/i; /* ^ starts with... @@ -14,18 +13,4 @@ import { Typography } from 'antd'; + repeated at least once $ until the end of the string */ -const url_regex = /^(http|https):\/\/[^ "]+$/; - -// Renders text as link if the text provided is a valid url. -const LinkIfUrl = ({ text }) => { - if (text.match(url_regex)) { - return ( - - {text} - - ); - } - return text; -}; - -export default LinkIfUrl; +export const URL_PATTERN = /^(http|https):\/\/[^ "]+$/; diff --git a/src/js/index.tsx b/src/js/index.tsx index a346c0c6..f15ab5cf 100644 --- a/src/js/index.tsx +++ b/src/js/index.tsx @@ -37,7 +37,7 @@ const App = () => { }, [lang, i18n.language, navigate]); return ( - + diff --git a/src/js/types/provenance.ts b/src/js/types/provenance.ts index c44bfef3..d3f7e52b 100644 --- a/src/js/types/provenance.ts +++ b/src/js/types/provenance.ts @@ -21,7 +21,7 @@ export interface DatsFile { distributions: Distribution[]; extraProperties: ExtraProperty[]; isAbout: IsAbout[]; - keywords: Keyword[]; + keywords: Annotation[]; licenses: License[]; primaryPublications: PrimaryPublication[]; privacy: string; @@ -43,29 +43,30 @@ export interface Funder { export interface Creator { name: string; - roles: Keyword[]; + roles: Annotation[]; abbreviation?: string; } -export interface Keyword { - value: string; +export interface Annotation { + value: string | number; + valueIRI?: string; } export interface Distribution { access: Access; formats: string[]; size: number; - unit: Keyword; + unit: Annotation; } export interface Access { - authorizations: Keyword[]; + authorizations: Annotation[]; landingPage: string; } export interface ExtraProperty { category: string; - values: Keyword[]; + values: Annotation[]; } export interface IsAbout { @@ -82,9 +83,18 @@ export interface License { name: string; } +export interface Person { + fullName: string; +} + +export interface DateInfo { + date: string; + type?: Annotation; +} + export interface PrimaryPublication { - authors: string[]; - dates: string[]; + authors?: Person[]; + dates?: DateInfo[]; identifier: Identifier; publicationVenue: string; title: string; @@ -96,5 +106,5 @@ export interface SpatialCoverage { } export interface Type { - information: Keyword; + information: Annotation; } diff --git a/src/js/utils/strings.ts b/src/js/utils/strings.ts new file mode 100644 index 00000000..a30bd27d --- /dev/null +++ b/src/js/utils/strings.ts @@ -0,0 +1,4 @@ +import { DOI_PATTERN, URL_PATTERN } from '@/constants/patterns'; + +export const stringIsDOI = (s: string) => !!s.match(DOI_PATTERN); +export const stringIsURL = (s: string) => !!s.match(URL_PATTERN); diff --git a/src/public/locales/en/default_translation_en.json b/src/public/locales/en/default_translation_en.json index 849a1a71..a78f30bd 100644 --- a/src/public/locales/en/default_translation_en.json +++ b/src/public/locales/en/default_translation_en.json @@ -18,7 +18,8 @@ "Distributions": "Distributions", "Is About": "Is About", "Primary Publications": "Primary Publications", - "Acknowledges": "Acknowledges", + "Publication": "Publication", + "Acknowledgements": "Acknowledgements", "Spatial Coverage": "Spatial Coverage", "Extra Properties": "Extra Properties", "Name": "Name", @@ -38,6 +39,7 @@ "Title": "Title", "Publication Venue": "Publication Venue", "Authors": "Authors", + "Date": "Date", "Dates": "Dates", "Description": "Description", "Manage Charts": "Manage Charts", diff --git a/src/public/locales/fr/default_translation_fr.json b/src/public/locales/fr/default_translation_fr.json index ebe0c518..3060cf3c 100644 --- a/src/public/locales/fr/default_translation_fr.json +++ b/src/public/locales/fr/default_translation_fr.json @@ -18,7 +18,8 @@ "Distributions": "Distributions", "Is About": "Sujet", "Primary Publications": "Publications primaires", - "Acknowledges": "Reconnaît", + "Publication": "Publication", + "Acknowledgements": "Remerciements", "Spatial Coverage": "Couverture spatiale", "Extra Properties": "Propriétés supplémentaires", "Name": "Nom", @@ -38,6 +39,7 @@ "Title": "Titre", "Publication Venue": "Lieu de publication", "Authors": "Auteurs", + "Date": "Date", "Dates": "Dates", "Description": "Description", "Manage Charts": "Gestion des tableaux",