diff --git a/packages/doi/package.json b/packages/doi/package.json index 0d6d8265fe84b3da7849dec925ea7ed5936cbaf9..b84df44479b42b41a42acc8b18f43b597cada2bf 100644 --- a/packages/doi/package.json +++ b/packages/doi/package.json @@ -24,7 +24,7 @@ "fix:prettier": "prettier . --write" }, "dependencies": { - "@edata-portal/icat-plus-api": "^1.8.7", + "@edata-portal/icat-plus-api": "^1.8.12", "@fortawesome/free-solid-svg-icons": "^6.6.0", "@fortawesome/fontawesome-svg-core": "^6.6.0", "@fortawesome/react-fontawesome": "^0.2.2", diff --git a/packages/doi/src/components/DOIInfo/DOICItationPublication.tsx b/packages/doi/src/components/DOIInfo/DOICItationPublication.tsx new file mode 100644 index 0000000000000000000000000000000000000000..415c23f2653a96a3829c84e97d2860df225c6b6f --- /dev/null +++ b/packages/doi/src/components/DOIInfo/DOICItationPublication.tsx @@ -0,0 +1,15 @@ +import { CopyValue } from 'components/CopyValue'; +import type { DOI } from 'model/doi'; + +export function DOICitationPublication({ doi }: { doi: DOI }) { + if (!doi.citationPublicationDOI) { + return null; + } + const publicationDOI = `https://doi.org/${doi.citationPublicationDOI}`; + const renderedValue = ( + <a href={publicationDOI} target="_blank" rel="noopener noreferrer"> + {publicationDOI} + </a> + ); + return <CopyValue value={publicationDOI} renderedValue={renderedValue} />; +} diff --git a/packages/doi/src/components/DOIInfo/DOICited.tsx b/packages/doi/src/components/DOIInfo/DOICited.tsx index 0aa0cece720635710d759221541232068ac9c8e2..e4a576debbf3da8df4c40716e51c47899ba7dc16 100644 --- a/packages/doi/src/components/DOIInfo/DOICited.tsx +++ b/packages/doi/src/components/DOIInfo/DOICited.tsx @@ -1,54 +1,17 @@ -import { LoadReference } from 'components/DOIInfo/DOIReference'; -import { Loading } from 'components/Loading'; -import { useLocalGetEndpoint } from 'fetch/fetching'; -import { OPENCITATION_LIST_CITATIONS } from 'fetch/opencitation'; +import { DOICitationPublication } from 'components/DOIInfo/DOICItationPublication'; import type { DOI } from 'model/doi'; -import { Suspense } from 'react'; import { Card } from 'react-bootstrap'; export function DOICited({ doi }: { doi: DOI }) { + if (!doi.citationPublicationDOI) { + return null; + } return ( <Card> <Card.Header className="p-2">Cited by</Card.Header> <Card.Body className="p-0"> - <Suspense fallback={<Loading />}> - <LoadCited doi={doi} /> - </Suspense> + <DOICitationPublication doi={doi} /> </Card.Body> </Card> ); } - -export function LoadCited({ doi }: { doi: DOI }) { - const citations = useLocalGetEndpoint({ - endpoint: OPENCITATION_LIST_CITATIONS, - params: { - doi: doi.doi, - }, - }); - - if (!citations?.length) { - return <p className="p-2 m-0">No citations were found.</p>; - } - - return ( - <> - {citations - .map((value) => value.citing.value) - //remove duplicates - .filter((doi, index, self) => self.indexOf(doi) === index) - .map((doi, index, self) => ( - <div - key={doi} - className={`p-2 ${ - index === self.length - 1 ? '' : 'border-bottom border-black' - }`} - > - <Suspense fallback={<Loading />}> - <LoadReference doi={doi} /> - </Suspense> - </div> - ))} - </> - ); -} diff --git a/packages/doi/src/components/DOIInfo/DOIInfo.tsx b/packages/doi/src/components/DOIInfo/DOIInfo.tsx index d9d7b8474adbb37924531812e21494954e0aca67..2d3c9e696ca45249f3f62cd4c44ef4fee8ed9b72 100644 --- a/packages/doi/src/components/DOIInfo/DOIInfo.tsx +++ b/packages/doi/src/components/DOIInfo/DOIInfo.tsx @@ -11,6 +11,8 @@ import { DOITypes } from 'components/DOIInfo/DOITypes'; import type { DOI } from 'model/doi'; import { Col, Row } from 'react-bootstrap'; import { CONFIG } from 'config/config'; +import { DOIKeywords } from 'components/DOIInfo/DOIKeywords'; +import { DOIReferencesIn } from 'components/DOIInfo/DOIReferencesIn'; export function DOIInfo({ doi }: { doi: DOI }) { return ( @@ -30,6 +32,9 @@ export function DOIInfo({ doi }: { doi: DOI }) { <Col xs={12}> <DOIDescription doi={doi} /> </Col> + <Col xs={12}> + <DOIKeywords doi={doi} /> + </Col> <Col xs={12} sm={CONFIG.EXPERIMENT_REPORT.enabled ? 6 : 12}> <DOIDataAccess doi={doi} /> </Col> @@ -44,6 +49,9 @@ export function DOIInfo({ doi }: { doi: DOI }) { <Col xs={12}> <DOICited doi={doi} /> </Col> + <Col xs={12}> + <DOIReferencesIn doi={doi} /> + </Col> </Row> </Col> <Col xs={12} lg={5} xl={4} xxl={3}> diff --git a/packages/doi/src/components/DOIInfo/DOIKeywords.tsx b/packages/doi/src/components/DOIInfo/DOIKeywords.tsx new file mode 100644 index 0000000000000000000000000000000000000000..16893015af0c2e7e9aef383757cddcd3db385909 --- /dev/null +++ b/packages/doi/src/components/DOIInfo/DOIKeywords.tsx @@ -0,0 +1,21 @@ +import type { DOI } from 'model/doi'; +import { Badge } from 'react-bootstrap'; + +export function DOIKeywords({ doi }: { doi: DOI }) { + if (!doi.keywords) { + return null; + } + return ( + <> + {doi.keywords.map((keyword, index) => ( + <Badge + key={`id=keyword-${index}`} + bg="secondary" + style={{ marginRight: '8px' }} + > + {keyword} + </Badge> + ))} + </> + ); +} diff --git a/packages/doi/src/components/DOIInfo/DOIReferenceURL.tsx b/packages/doi/src/components/DOIInfo/DOIReferenceURL.tsx new file mode 100644 index 0000000000000000000000000000000000000000..5355a40a53b597f0e25af670f8b3fe515aab95a2 --- /dev/null +++ b/packages/doi/src/components/DOIInfo/DOIReferenceURL.tsx @@ -0,0 +1,14 @@ +import { CopyValue } from 'components/CopyValue'; +import type { DOI } from 'model/doi'; + +export function DOIReferenceURL({ doi }: { doi: DOI }) { + if (!doi.referenceURL) { + return null; + } + const renderedValue = ( + <a href={doi.referenceURL} target="_blank" rel="noopener noreferrer"> + {doi.referenceURL} + </a> + ); + return <CopyValue value={doi.referenceURL} renderedValue={renderedValue} />; +} diff --git a/packages/doi/src/components/DOIInfo/DOIReferencesIn.tsx b/packages/doi/src/components/DOIInfo/DOIReferencesIn.tsx new file mode 100644 index 0000000000000000000000000000000000000000..484399ae5b0f19bc3218130ae36b224c7644e943 --- /dev/null +++ b/packages/doi/src/components/DOIInfo/DOIReferencesIn.tsx @@ -0,0 +1,17 @@ +import { DOIReferenceURL } from 'components/DOIInfo/DOIReferenceURL'; +import type { DOI } from 'model/doi'; +import { Card } from 'react-bootstrap'; + +export function DOIReferencesIn({ doi }: { doi: DOI }) { + if (!doi.referenceURL) { + return null; + } + return ( + <Card> + <Card.Header className="p-2">References</Card.Header> + <Card.Body className="p-0"> + <DOIReferenceURL doi={doi} /> + </Card.Body> + </Card> + ); +} diff --git a/packages/doi/src/constants/doi.ts b/packages/doi/src/constants/doi.ts index 7129ab6bea17a864ed190335a67d6aa45e70964e..35de0c6523fa49b64a12747fe97c5b34deec5a13 100644 --- a/packages/doi/src/constants/doi.ts +++ b/packages/doi/src/constants/doi.ts @@ -15,3 +15,9 @@ export const ABSTRACT_DESCRIPTION_TYPE = 'Abstract'; export const ORCID_IDENTIFIER_SCHEME = 'ORCID'; export const COLLECTION_DATE_FORMAT = 'yyyy-MM-dd'; + +export const RELATED_IDENTIFIER_TYPE_RELATED_DOI = 'IsSupplementedBy'; + +export const RELATED_IDENTIFIER_TYPE_IS_CITED_BY = 'IsCitedBy'; + +export const RELATED_IDENTIFIER_TYPE_REFERENCES = 'References'; diff --git a/packages/doi/src/fetch/opencitation.ts b/packages/doi/src/fetch/opencitation.ts deleted file mode 100644 index c236593bcb334a5c71b4ac82f08fb6caae7240a6..0000000000000000000000000000000000000000 --- a/packages/doi/src/fetch/opencitation.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { Endpoint } from 'fetch/fetching'; -import type { OpencitationCitation } from 'model/opencitation'; - -export const OPENCITATION_LIST_CITATIONS = Endpoint({ - server: 'OPENCITATION', - path: 'citations/:doi?json=dict(%22%20=%3E%20%22,citing,source,value).dict(%22%20=%3E%20%22,cited,source,value)', - params: {} as { - doi: string; - }, - schema: {} as OpencitationCitation[], -}); diff --git a/packages/doi/src/model/doi.ts b/packages/doi/src/model/doi.ts index b63061dd75ca400ec36c67dc20c188d4200999dc..f95db7b1b0f598bd3fe2d4888617156e15ca03d4 100644 --- a/packages/doi/src/model/doi.ts +++ b/packages/doi/src/model/doi.ts @@ -12,6 +12,9 @@ export type DOI = { categories?: string[]; relatedDOI?: string[]; resourceType?: string; + keywords?: string[]; + citationPublicationDOI?: string; + referenceURL?: string; }; export type DOIParticipant = { name: string; diff --git a/packages/doi/src/utils/datacite.ts b/packages/doi/src/utils/datacite.ts index c9319a9d3c27b0b7a520f136a3c1da43cc4f0da6..3fbecce8e22de12c8b9712fce9caff2febdaa713 100644 --- a/packages/doi/src/utils/datacite.ts +++ b/packages/doi/src/utils/datacite.ts @@ -5,6 +5,9 @@ import { INSTRUMENT_SUBJECT_SCHEME, ORCID_IDENTIFIER_SCHEME, PROPOSAL_SUBJECT_SCHEME, + RELATED_IDENTIFIER_TYPE_IS_CITED_BY, + RELATED_IDENTIFIER_TYPE_REFERENCES, + RELATED_IDENTIFIER_TYPE_RELATED_DOI, } from 'constants/doi'; import type { DataciteDOIAttributes, @@ -30,15 +33,17 @@ export function convertDataciteToInternal( instruments: doi.subjects .filter( (subject) => + subject.subjectScheme && subject.subjectScheme.toLowerCase() === - INSTRUMENT_SUBJECT_SCHEME.toLowerCase(), + INSTRUMENT_SUBJECT_SCHEME.toLowerCase(), ) .flatMap((subject) => subject.subject.split(',')), proposals: doi.subjects .filter( (subject) => + subject.subjectScheme && subject.subjectScheme.toLowerCase() === - PROPOSAL_SUBJECT_SCHEME.toLowerCase(), + PROPOSAL_SUBJECT_SCHEME.toLowerCase(), ) .flatMap((subject) => subject.subject.split(',')), description: doi.descriptions.find( @@ -54,14 +59,35 @@ export function convertDataciteToInternal( categories: doi.subjects .filter( (subject) => + subject.subjectScheme && subject.subjectScheme.toLowerCase() === - CATEGORY_SUBJECT_SCHEME.toLowerCase(), + CATEGORY_SUBJECT_SCHEME.toLowerCase(), ) .map((subject) => subject.subject), - relatedDOI: doi.relatedIdentifiers?.map( - (relatedIdentifier) => relatedIdentifier.relatedIdentifier, - ), + relatedDOI: doi.relatedIdentifiers + ?.filter( + (relatedIdentifier) => + relatedIdentifier.relatedIdentifierType === + RELATED_IDENTIFIER_TYPE_RELATED_DOI, + ) + .map((relatedIdentifier) => relatedIdentifier.relatedIdentifier), resourceType: doi.types.resourceType, + keywords: doi.subjects + .filter((subject) => !subject.subjectScheme) + .map((subject) => subject.subject), + citationPublicationDOI: doi.relatedIdentifiers + ?.filter( + (relatedIdentifier) => + relatedIdentifier.relationType === + RELATED_IDENTIFIER_TYPE_IS_CITED_BY, + ) + .map((relatedIdentifier) => relatedIdentifier.relatedIdentifier)?.[0], + referenceURL: doi.relatedIdentifiers + ?.filter( + (relatedIdentifier) => + relatedIdentifier.relationType === RELATED_IDENTIFIER_TYPE_REFERENCES, + ) + .map((relatedIdentifier) => relatedIdentifier.relatedIdentifier)?.[0], }; } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 3eaff8d132f2333edb96d395163478e5abc0e6af..6baa1413aac76e5198f2464a089ad329ecabfb9e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -98,8 +98,8 @@ importers: packages/doi: dependencies: '@edata-portal/icat-plus-api': - specifier: ^1.8.7 - version: 1.8.7(@tanstack/react-query@5.59.20(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + specifier: ^1.8.12 + version: 1.8.12(@tanstack/react-query@5.59.20(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@fortawesome/fontawesome-svg-core': specifier: ^6.6.0 version: 6.6.0 @@ -967,6 +967,13 @@ packages: resolution: {integrity: sha512-Z/yiTPj+lDVnF7lWeKCIJzaIkI0vYO87dMpZ4bg4TDrFe4XXLFWL1TbXU27gBP3QccxV9mZICCrnjnYlJjXHOA==} engines: {node: '>=6.9.0'} + '@edata-portal/icat-plus-api@1.8.12': + resolution: {integrity: sha512-ag9ZfXydvKke645K/DdUJdXwalJFoF0d2UkFHup1ZC17AwrhmaK90VIhRotjCTulexAH+x7D00KTJXTQPf5nzw==} + peerDependencies: + '@tanstack/react-query': ^5.28.9 + react: ^18.2.0 + react-dom: ^18.2.0 + '@edata-portal/icat-plus-api@1.8.7': resolution: {integrity: sha512-jrmrV6cIMSNHk2aKrA1POXKaWhanrYirOZERz9aIhpVTDBrS+sxUtHGpPkg/KAH0iAY346pxNKsjVeRTNnnxPA==} peerDependencies: @@ -4261,6 +4268,13 @@ snapshots: '@babel/helper-string-parser': 7.25.9 '@babel/helper-validator-identifier': 7.25.9 + '@edata-portal/icat-plus-api@1.8.12(@tanstack/react-query@5.59.20(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@tanstack/react-query': 5.59.20(react@18.3.1) + date-fns: 3.6.0 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + '@edata-portal/icat-plus-api@1.8.7(@tanstack/react-query@5.59.20(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: '@tanstack/react-query': 5.59.20(react@18.3.1)