From 2f9ddd1d536bb7c08626c56fd6a1a86890317533 Mon Sep 17 00:00:00 2001 From: Pooria Mehregan Date: Mon, 4 Sep 2023 14:22:19 +0200 Subject: [PATCH] refactor(concept): separate Related Concepts from detail page --- .../RelatedConcepts/index.tsx | 322 ++++++++++++++++++ src/components/concept-details-page/index.tsx | 284 ++------------- .../components/details-page/index.tsx | 11 +- src/l10n/nb.json | 3 +- src/types/domain.d.ts | 1 + 5 files changed, 353 insertions(+), 268 deletions(-) create mode 100644 src/components/concept-details-page/RelatedConcepts/index.tsx diff --git a/src/components/concept-details-page/RelatedConcepts/index.tsx b/src/components/concept-details-page/RelatedConcepts/index.tsx new file mode 100644 index 000000000..a8cfd60fa --- /dev/null +++ b/src/components/concept-details-page/RelatedConcepts/index.tsx @@ -0,0 +1,322 @@ +import React, { useEffect } from 'react'; +import Link from '@fellesdatakatalog/link'; +import { Link as RouteLink } from 'react-router-dom'; +import { + ContentSection, + KeyValueList, + KeyValueListItem +} from '../../details-page'; +import translations from '../../../lib/localization'; +import { Entity } from '../../../types/enums'; +import { PATHNAME_CONCEPTS } from '../../../constants/constants'; +import { getTranslateText as translate } from '../../../lib/translateText'; +import { + dateStringToDate, + formatDate, + isDateAfterToday, + isDateBeforeToday +} from '../../../lib/date-utils'; +import { Concept } from '../../../types'; + +interface Props { + concept: Concept | null; + conceptReferences: Concept[]; + getConcepts: (params: any) => void; + getConceptsRelations: (params: any) => void; + getDatasetsRelations: (params: any) => void; + getInformationmodelsRelations: (params: any) => void; + getPublicServicesRelations: (params: any) => void; +} + +const RelatedConcepts = ({ + concept, + conceptReferences, + getConcepts, + getConceptsRelations, + getDatasetsRelations, + getPublicServicesRelations, + getInformationmodelsRelations +}: Props) => { + const title = concept?.prefLabel ?? {}; + const associativeRelations = concept?.associativeRelation ?? []; + const partitiveRelations = concept?.partitiveRelation ?? []; + const genericRelations = concept?.genericRelation ?? []; + const isReplacedBy = concept?.isReplacedBy ?? []; + const memberOf = concept?.memberOf ?? []; + const seeAlso = concept?.seeAlso ?? []; + + const conceptReferencesMap = conceptReferences?.reduce( + (previous, current) => ({ ...previous, [current.identifier]: current }), + {} as Record + ); + + const associativeRelationsUris: string[] = associativeRelations?.map( + ({ related = '' }) => related + ); + + const partitiveRelationsUris: string[] = partitiveRelations?.map( + ({ isPartOf = '', hasPart = '' }) => isPartOf ?? hasPart + ); + + const genericRelationsUris: string[] = genericRelations?.map( + ({ generalizes = '', specializes = '' }) => generalizes ?? specializes + ); + + useEffect(() => { + if (concept?.identifier) { + if ( + (Array.isArray(concept?.seeAlso) && concept?.seeAlso.length > 0) || + (Array.isArray(associativeRelationsUris) && + associativeRelationsUris.length > 0) || + (Array.isArray(partitiveRelationsUris) && + partitiveRelationsUris.length > 0) || + (Array.isArray(genericRelationsUris) && + genericRelationsUris.length > 0) || + (Array.isArray(isReplacedBy) && isReplacedBy.length > 0) || + (Array.isArray(concept?.memberOf) && concept?.memberOf.length > 0) + ) { + getConcepts({ + identifiers: [ + ...(concept?.seeAlso ?? []), + ...associativeRelationsUris, + ...partitiveRelationsUris, + ...genericRelationsUris, + ...isReplacedBy, + ...memberOf + ], + size: 1000 + }); + } + + getConceptsRelations({ seeAlso: concept.identifier }); + getDatasetsRelations({ subject: concept.identifier }); + getInformationmodelsRelations({ + conceptIdentifiers: [concept.identifier] + }); + getPublicServicesRelations({ isClassifiedBy: concept.identifier }); + } + }, [concept?.identifier]); + + return ( + + + {associativeRelations.map( + ({ description: associativeDescription, related = '' }) => + conceptReferencesMap?.[related] && ( + + {translate(conceptReferencesMap[related].prefLabel)} + + } + value={ +
+
+ + {translations.conceptReferences.associative} + .  + +
+
+ {translate(associativeDescription)} +
+
+ } + /> + ) + )} + {partitiveRelations.map( + ({ + description: partitiveDescription, + hasPart = '', + isPartOf = '' + }) => { + const conceptReferenceUri = hasPart ?? isPartOf; + return ( + conceptReferencesMap?.[conceptReferenceUri] && ( + + {translate( + conceptReferencesMap[conceptReferenceUri].prefLabel + )} + + } + value={ +
+
+ + {translations.conceptReferences.partitive} + .  + {isPartOf + ? translations.conceptReferences.isPartOf + : translations.conceptReferences.hasPart} + +
+
+ {translate(partitiveDescription)} +
+
+ } + /> + ) + ); + } + )} + {genericRelations.map( + ({ divisioncriterion, generalizes = '', specializes = '' }) => { + const conceptReferenceUri = generalizes ?? specializes; + return ( + conceptReferencesMap?.[conceptReferenceUri] && ( + + {translate( + conceptReferencesMap[conceptReferenceUri].prefLabel + )} + + } + value={ +
+
+ + {translations.conceptReferences.generic} + .  + {generalizes + ? translations.conceptReferences.generalizes + : translations.conceptReferences.specializes} + +
+
+ {translate(divisioncriterion)} +
+
+ } + /> + ) + ); + } + )} + {seeAlso.map(uri => { + const hasExpired = isDateBeforeToday( + dateStringToDate( + formatDate(dateStringToDate(concept?.validToIncluding)) + ) + ); + const willBeValid = isDateAfterToday( + dateStringToDate( + formatDate(dateStringToDate(concept?.validFromIncluding)) + ) + ); + + return conceptReferencesMap?.[uri] ? ( + + {translate(conceptReferencesMap[uri].prefLabel)} + {hasExpired && <> ({translations.validity.expired})} + {!hasExpired && willBeValid && ( + <> ({translations.validity.willBeValid}) + )} + + } + value={translations.conceptReferences.seeAlso} + /> + ) : ( + + ); + })} + {isReplacedBy.map(uri => + conceptReferencesMap?.[uri] ? ( + + {translate(conceptReferencesMap[uri].prefLabel)} + + } + value={`${ + translations.conceptReferences.isReplacedBy + } ${translate(title)}`} + /> + ) : ( + + ) + )} + {memberOf && + memberOf.map(uri => + conceptReferencesMap?.[uri] ? ( + + {translate(conceptReferencesMap[uri].prefLabel)} + + } + value={`${translations.conceptReferences.memberOf} ${translate( + title + )}`} + /> + ) : ( + + ) + )} +
+
+ ); +}; + +export default RelatedConcepts; diff --git a/src/components/concept-details-page/index.tsx b/src/components/concept-details-page/index.tsx index 3be430e67..66f7d3f90 100755 --- a/src/components/concept-details-page/index.tsx +++ b/src/components/concept-details-page/index.tsx @@ -2,23 +2,15 @@ import type { FC } from 'react'; import React, { memo, useState, useEffect } from 'react'; import { compose } from 'redux'; import type { RouteComponentProps } from 'react-router-dom'; -import { Link as RouteLink } from 'react-router-dom'; import { ThemeProvider } from 'styled-components'; import Link from '@fellesdatakatalog/link'; import translations from '../../lib/localization'; -import { - dateStringToDate, - formatDate, - isDateBeforeToday, - isDateAfterToday -} from '../../lib/date-utils'; +import { dateStringToDate, formatDate } from '../../lib/date-utils'; import { getTranslateText as translate } from '../../lib/translateText'; import { deepKeys } from '../../lib/deep-keys'; import { languageSorter } from '../../lib/languageSorter'; import { themeFDK } from '../../app/theme'; -import { PATHNAME_CONCEPTS } from '../../constants/constants'; - import type { Props as ConceptProps } from '../with-concept'; import withConcept from '../with-concept'; import type { Props as DatasetsProps } from '../with-datasets'; @@ -46,6 +38,7 @@ import SC from './styled'; import type { Theme, Language, TextLanguage } from '../../types'; import { Entity } from '../../types/enums'; import { formatISO } from '../../utils/date'; +import RelatedConcepts from './RelatedConcepts'; interface RouteParams { conceptId: string; @@ -121,57 +114,6 @@ const ConceptDetailsPage: FC = ({ }; }, [conceptId]); - const associativeRelations = concept?.associativeRelation ?? []; - const partitiveRelations = concept?.partitiveRelation ?? []; - const genericRelations = concept?.genericRelation ?? []; - - const associativeRelationsUris: string[] = associativeRelations?.map( - ({ related = '' }) => related - ); - - const partitiveRelationsUris: string[] = partitiveRelations?.map( - ({ isPartOf = '', hasPart = '' }) => isPartOf ?? hasPart - ); - - const genericRelationsUris: string[] = genericRelations?.map( - ({ generalizes = '', specializes = '' }) => generalizes ?? specializes - ); - - const isReplacedBy = concept?.isReplacedBy ?? []; - - useEffect(() => { - if (concept?.identifier) { - if ( - (Array.isArray(concept?.seeAlso) && concept?.seeAlso.length > 0) || - (Array.isArray(associativeRelationsUris) && - associativeRelationsUris.length > 0) || - (Array.isArray(partitiveRelationsUris) && - partitiveRelationsUris.length > 0) || - (Array.isArray(genericRelationsUris) && - genericRelationsUris.length > 0) || - (Array.isArray(isReplacedBy) && isReplacedBy.length > 0) - ) { - getConcepts({ - identifiers: [ - ...(concept?.seeAlso ?? []), - ...associativeRelationsUris, - ...partitiveRelationsUris, - ...genericRelationsUris, - ...isReplacedBy - ], - size: 1000 - }); - } - - getConceptsRelations({ seeAlso: concept.identifier }); - getDatasetsRelations({ subject: concept.identifier }); - getInformationmodelsRelations({ - conceptIdentifiers: [concept.identifier] - }); - getPublicServicesRelations({ isClassifiedBy: concept.identifier }); - } - }, [concept?.identifier]); - const publicServicesRelationsWithRelationType: ItemWithRelationType[] = publicServicesRelations.map(relation => ({ relation, @@ -230,11 +172,6 @@ const ConceptDetailsPage: FC = ({ } }, [concept, translations.getLanguage()]); - const conceptReferencesMap = conceptReferences?.reduce( - (previous, current) => ({ ...previous, [current.identifier]: current }), - {} as Record - ); - const entityId = concept?.id; const entityUri = concept?.uri; const identifier = concept?.identifier; @@ -264,9 +201,16 @@ const ConceptDetailsPage: FC = ({ dateStringToDate(concept?.validToIncluding) ); const contactPoint = concept?.contactPoint; - const seeAlso = concept?.seeAlso ?? []; + const themes: Theme[] = []; const created = concept?.created ?? ''; + const hasRelatedConcepts = + concept?.associativeRelation || + concept?.partitiveRelation || + concept?.genericRelation || + concept?.isReplacedBy || + concept?.memberOf || + concept?.seeAlso; const renderSources = () => { if (sourceRelationship === 'egendefinert') { @@ -332,7 +276,6 @@ const ConceptDetailsPage: FC = ({ )} )} - {description && ( = ({ {identifier} )} - {(associativeRelations.length > 0 || - partitiveRelations.length > 0 || - genericRelations.length > 0 || - seeAlso.length > 0) && ( - - - {associativeRelations.map( - ({ description: associativeDescription, related = '' }) => - conceptReferencesMap?.[related] && ( - - {translate(conceptReferencesMap[related].prefLabel)} - - } - value={ -
-
- - {translations.conceptReferences.associative} - .  - -
-
- {translate(associativeDescription)} -
-
- } - /> - ) - )} - {partitiveRelations.map( - ({ - description: partitiveDescription, - hasPart = '', - isPartOf = '' - }) => { - const conceptReferenceUri = hasPart ?? isPartOf; - return ( - conceptReferencesMap?.[conceptReferenceUri] && ( - - {translate( - conceptReferencesMap[conceptReferenceUri] - .prefLabel - )} - - } - value={ -
-
- - {translations.conceptReferences.partitive} - .  - {isPartOf - ? translations.conceptReferences.isPartOf - : translations.conceptReferences.hasPart} - -
-
- {translate(partitiveDescription)} -
-
- } - /> - ) - ); - } - )} - {genericRelations.map( - ({ divisioncriterion, generalizes = '', specializes = '' }) => { - const conceptReferenceUri = generalizes ?? specializes; - return ( - conceptReferencesMap?.[conceptReferenceUri] && ( - - {translate( - conceptReferencesMap[conceptReferenceUri] - .prefLabel - )} - - } - value={ -
-
- - {translations.conceptReferences.generic} - .  - {generalizes - ? translations.conceptReferences.generalizes - : translations.conceptReferences.specializes} - -
-
- {translate(divisioncriterion)} -
-
- } - /> - ) - ); - } - )} - {seeAlso.map(uri => { - const isExpired = isDateBeforeToday( - dateStringToDate(validToIncluding) - ); - const isWillBeValid = isDateAfterToday( - dateStringToDate(validFromIncluding) - ); - - return conceptReferencesMap?.[uri] ? ( - - {translate(conceptReferencesMap[uri].prefLabel)} - {isExpired && ( - <> ({translations.validity.expired}) - )} - {!isExpired && isWillBeValid && ( - <> ({translations.validity.willBeValid}) - )} - - } - value={translations.conceptReferences.seeAlso} - /> - ) : ( - - ); - })} - {isReplacedBy.map(uri => - conceptReferencesMap?.[uri] ? ( - - {translate(conceptReferencesMap[uri].prefLabel)} - - } - value={`${ - translations.conceptReferences.isReplacedBy - } ${translate(title)}`} - /> - ) : ( - - ) - )} -
-
+ {hasRelatedConcepts && ( + )} {(conceptsRelations.length > 0 || datasetsRelations.length > 0 || diff --git a/src/components/details-page/components/details-page/index.tsx b/src/components/details-page/components/details-page/index.tsx index 679a24b6d..0ee3e95a0 100755 --- a/src/components/details-page/components/details-page/index.tsx +++ b/src/components/details-page/components/details-page/index.tsx @@ -55,11 +55,12 @@ import { Entity } from '../../../../types/enums'; import { calculateRatingPercentage, determineRatingIcon -} from '../../../../pages/organizations/pages/datasets-page/index'; +} from '../../../../pages/organizations/pages/datasets-page'; import withCommunity, { Props as CommunityProps } from '../../../with-community'; import Aside from '../aside'; +import RelationsContainer from '../../../concept-details-page/RelatedConcepts'; interface ExternalProps { entity: Entity; @@ -212,8 +213,12 @@ const DetailsPage: FC> = ({ const renderContentSections = () => contentSections - .map(child => - isValidElement(child) && child.type === ContentSection ? child : null + .map( + child => + isValidElement(child) && + (child.type === ContentSection || child.type === RelationsContainer + ? child + : null) ) ?.filter(Boolean); diff --git a/src/l10n/nb.json b/src/l10n/nb.json index c40cf9ec3..f3de55c94 100644 --- a/src/l10n/nb.json +++ b/src/l10n/nb.json @@ -112,7 +112,8 @@ "generalizes": "Underordnet", "specializes": "Overordnet", "isPartOf": "Er en del av", - "hasPart": "Omfatter" + "hasPart": "Omfatter", + "memberOf": "Medlem av" }, "validity": { "validFromIncluding": "Gyldig fra og med", diff --git a/src/types/domain.d.ts b/src/types/domain.d.ts index 45f374489..818ec234e 100644 --- a/src/types/domain.d.ts +++ b/src/types/domain.d.ts @@ -276,6 +276,7 @@ export interface Concept { partitiveRelation?: Partial[]; genericRelation?: Partial[]; created?: string; + memberOf?: string[]; } export interface ConceptDefinition {