From 78f6e64df899aa859127eb0daf3a619a8b7de324 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kalle=20Kytt=C3=A4l=C3=A4?= Date: Thu, 22 Aug 2024 13:29:17 +0300 Subject: [PATCH 01/35] use api v2 for frontpage: terminology search, organizations, groups, fake login --- common-ui/interfaces/group.interface.ts | 7 ++ .../interfaces/organization.interface.ts | 2 +- common-ui/utils/get-language-version.ts | 32 ++++++ datamodel-ui/next.config.js | 2 +- terminology-ui/.env | 2 +- terminology-ui/jest.setup.ts | 2 +- .../access-request/access-request-modal.tsx | 12 +- .../components/access-request/index.tsx | 4 +- .../common/components/counts/counts.slice.tsx | 6 +- .../fakeable-user/fakeable-user.slice.tsx | 2 +- .../common/components/login/login.slice.tsx | 4 +- .../information-domains-selector.tsx | 8 +- .../organization-selector.tsx | 9 +- .../terminology-search.slice.tsx | 29 ++--- .../src/common/interfaces/interfaces-v2.ts | 75 +++++++++++++ .../src/common/utils/translation-helpers.ts | 10 +- .../src/modules/collection/index.tsx | 2 +- terminology-ui/src/modules/concept/index.tsx | 2 +- .../src/modules/own-information/index.tsx | 16 +-- .../src/modules/terminology-search/index.tsx | 104 +++++++++--------- .../terminology-search/search-page-filter.tsx | 25 +++-- .../src/modules/vocabulary/index.tsx | 2 +- terminology-ui/src/pages/api/auth/callback.ts | 5 +- .../src/pages/api/auth/fake-login.ts | 3 +- .../src/store/api-base-query.test.ts | 2 +- terminology-ui/src/store/api-base-query.ts | 4 +- terminology-ui/src/tests/fake-login.test.ts | 9 +- terminology-ui/src/tests/login.test.ts | 4 +- .../terminology/[terminologyId].test.tsx | 8 +- 29 files changed, 261 insertions(+), 131 deletions(-) create mode 100644 common-ui/interfaces/group.interface.ts create mode 100644 common-ui/utils/get-language-version.ts create mode 100644 terminology-ui/src/common/interfaces/interfaces-v2.ts diff --git a/common-ui/interfaces/group.interface.ts b/common-ui/interfaces/group.interface.ts new file mode 100644 index 000000000..7711dcba6 --- /dev/null +++ b/common-ui/interfaces/group.interface.ts @@ -0,0 +1,7 @@ +export interface Group { + id: string; + label: { + [key: string]: string; + }; + identifier: string; +} diff --git a/common-ui/interfaces/organization.interface.ts b/common-ui/interfaces/organization.interface.ts index cdb133cba..bc4d502e5 100644 --- a/common-ui/interfaces/organization.interface.ts +++ b/common-ui/interfaces/organization.interface.ts @@ -3,5 +3,5 @@ export interface Organization { label: { [key: string]: string; }; - parentId?: string; + parentOrganization?: string; } diff --git a/common-ui/utils/get-language-version.ts b/common-ui/utils/get-language-version.ts new file mode 100644 index 000000000..8498e1107 --- /dev/null +++ b/common-ui/utils/get-language-version.ts @@ -0,0 +1,32 @@ +interface getLanguageVersionProps { + data?: { [key: string]: string }; + lang: string; + appendLocale?: boolean; +} + +export function getLanguageVersion({ + data, + lang, + appendLocale = false, +}: getLanguageVersionProps) { + if (!data) { + return ''; + } + + if (data[lang]) { + return data[lang]; + } + + if (data.fi) { + return appendLocale ? `${data.fi} (fi)` : data.fi; + } else if (data.en) { + return appendLocale ? `${data.en} (en)` : data.en; + } + + if (Object.keys(data).length > 0) { + const localeKey = Object.keys(data)[0]; + return appendLocale ? `${data[localeKey]} (${localeKey})` : data[localeKey]; + } + + return ''; +} diff --git a/datamodel-ui/next.config.js b/datamodel-ui/next.config.js index 6cfe80384..39a97f393 100644 --- a/datamodel-ui/next.config.js +++ b/datamodel-ui/next.config.js @@ -103,7 +103,7 @@ module.exports = () => { }, { source: '/datamodel-api/:path*', - destination: 'http://localhost:9004/datamodel-api/:path*', + destination: 'https://localhost:9004/datamodel-api/:path*', }, { source: '/terminology-api/:path*', diff --git a/terminology-ui/.env b/terminology-ui/.env index 34cd46f5c..996c2f95c 100644 --- a/terminology-ui/.env +++ b/terminology-ui/.env @@ -1,6 +1,6 @@ # curl -v http://yti-terminology-api:9103/terminology-api/actuator/info # API url to be used by SSR -TERMINOLOGY_API_URL=http://yti-terminology-api:9103/terminology-api +TERMINOLOGY_API_URL=http://yti-terminology-api:9103/terminology-api/v2 MESSAGING_API_URL=http://yti-messaging-api:9801/messaging-api AUTH_PROXY_URL=http://yti-auth-proxy diff --git a/terminology-ui/jest.setup.ts b/terminology-ui/jest.setup.ts index 3be50f9c5..53f45637d 100644 --- a/terminology-ui/jest.setup.ts +++ b/terminology-ui/jest.setup.ts @@ -3,7 +3,7 @@ import * as crypto from 'crypto'; import { TextEncoder, TextDecoder } from 'util'; process.env.TERMINOLOGY_API_URL = - 'http://terminology-api.invalid/terminology-api'; + 'http://terminology-api.invalid/terminology-api/v2'; process.env.AUTH_PROXY_URL = 'http://auth-proxy.invalid'; process.env.SECRET_COOKIE_PASSWORD = crypto.randomBytes(16).toString('hex'); diff --git a/terminology-ui/src/common/components/access-request/access-request-modal.tsx b/terminology-ui/src/common/components/access-request/access-request-modal.tsx index 00b54a416..ac7be2d4a 100644 --- a/terminology-ui/src/common/components/access-request/access-request-modal.tsx +++ b/terminology-ui/src/common/components/access-request/access-request-modal.tsx @@ -1,5 +1,4 @@ -import { OrganizationSearchResult } from '@app/common/interfaces/terminology.interface'; -import { useTranslation } from 'next-i18next'; +import { i18n, useTranslation } from 'next-i18next'; import { useState } from 'react'; import { useSelector } from 'react-redux'; import { @@ -21,11 +20,13 @@ import { ModalContentBlock, ModalTitleH1, } from './access-request.styles'; +import { Organization } from 'yti-common-ui/interfaces/organization.interface'; +import { getLanguageVersion } from 'yti-common-ui/utils/get-language-version'; export interface AccessRequestModalProps { visible: boolean; handleClose: () => void; - organizations?: OrganizationSearchResult[]; + organizations?: Organization[]; requests?: AccessRequest[]; postRequest: (value: string) => void; } @@ -127,7 +128,10 @@ export default function AccessRequestModal({ value={organization.id} key={`dropdown-item-${idx}`} > - {organization.properties.prefLabel.value} + {getLanguageVersion({ + data: organization.label, + lang: i18n?.language ?? 'fi', + })} ); })} diff --git a/terminology-ui/src/common/components/access-request/index.tsx b/terminology-ui/src/common/components/access-request/index.tsx index c80a5e8eb..72085faa2 100644 --- a/terminology-ui/src/common/components/access-request/index.tsx +++ b/terminology-ui/src/common/components/access-request/index.tsx @@ -2,7 +2,6 @@ import dynamic from 'next/dynamic'; import { useTranslation } from 'next-i18next'; import { useEffect, useState } from 'react'; import { useStoreDispatch } from '../../../store'; -import { OrganizationSearchResult } from '../../interfaces/terminology.interface'; import { setAlert } from '../alert/alert.slice'; import { useGetRequestsQuery, @@ -15,13 +14,14 @@ import { } from './access-request.styles'; import { AccessRequestModalProps } from './access-request-modal'; import { IconMessage } from 'suomifi-ui-components'; +import { Organization } from 'yti-common-ui/interfaces/organization.interface'; const AccessRequestModal = dynamic(() => import('./access-request-modal').then((module) => module.default) ); interface AccessRequestProps { - organizations?: OrganizationSearchResult[]; + organizations?: Organization[]; } export default function AccessRequest({ organizations }: AccessRequestProps) { diff --git a/terminology-ui/src/common/components/counts/counts.slice.tsx b/terminology-ui/src/common/components/counts/counts.slice.tsx index 7411e0c10..ae72d7b1c 100644 --- a/terminology-ui/src/common/components/counts/counts.slice.tsx +++ b/terminology-ui/src/common/components/counts/counts.slice.tsx @@ -10,13 +10,13 @@ export const countsApi = createApi({ endpoints: (builder) => ({ getCounts: builder.query({ query: () => ({ - url: '/counts?vocabularies=true', + url: '/frontend/counts?vocabularies=true', method: 'GET', }), }), getVocabularyCount: builder.query({ query: (value) => ({ - url: `/conceptCounts?graphId=${value}`, + url: `/frontend/concept-counts?graphId=${value}`, method: 'GET', headers: { 'content-type': 'application/json', @@ -25,7 +25,7 @@ export const countsApi = createApi({ }), getStatusCounts: builder.query({ query: (value) => ({ - url: `/statusCounts?graphId=${value}`, + url: `/frontend/status-counts?graphId=${value}`, method: 'GET', }), }), diff --git a/terminology-ui/src/common/components/fakeable-user/fakeable-user.slice.tsx b/terminology-ui/src/common/components/fakeable-user/fakeable-user.slice.tsx index 91d143b01..02fd36c10 100644 --- a/terminology-ui/src/common/components/fakeable-user/fakeable-user.slice.tsx +++ b/terminology-ui/src/common/components/fakeable-user/fakeable-user.slice.tsx @@ -9,7 +9,7 @@ export const fakeableUsersApi = createApi({ endpoints: (builder) => ({ getFakeableUsers: builder.query({ query: () => ({ - url: '/fakeableUsers', + url: '/fakeable-users', method: 'GET', }), }), diff --git a/terminology-ui/src/common/components/login/login.slice.tsx b/terminology-ui/src/common/components/login/login.slice.tsx index 54c16fbff..182fa481a 100644 --- a/terminology-ui/src/common/components/login/login.slice.tsx +++ b/terminology-ui/src/common/components/login/login.slice.tsx @@ -45,13 +45,13 @@ export const loginApi = createApi({ endpoints: (builder) => ({ getAuthenticatedUser: builder.query({ query: () => ({ - url: '/authenticated-user', + url: '/user', method: 'GET', }), }), getAuthenticatedUserMut: builder.mutation({ query: () => ({ - url: '/authenticated-user', + url: '/user', method: 'GET', }), }), diff --git a/terminology-ui/src/common/components/terminology-components/information-domains-selector.tsx b/terminology-ui/src/common/components/terminology-components/information-domains-selector.tsx index 5a61296a6..74fa4a7aa 100644 --- a/terminology-ui/src/common/components/terminology-components/information-domains-selector.tsx +++ b/terminology-ui/src/common/components/terminology-components/information-domains-selector.tsx @@ -9,6 +9,7 @@ import { } from './terminology-components.styles'; import { UpdateTerminology } from '@app/modules/new-terminology/update-terminology.interface'; import { NewTerminologyInfo } from '@app/common/interfaces/new-terminology-info'; +import { getLanguageVersion } from 'yti-common-ui/utils/get-language-version'; interface InformationDomainsSelectorProps { update: ({ key, data }: UpdateTerminology) => void; @@ -46,14 +47,17 @@ export default function InformationDomainsSelector({ const infoDomains: MultiSelectData[] = informationDomains?.map( (infoDomain) => { - const domainName = infoDomain.properties.prefLabel.value; + const domainName = getLanguageVersion({ + data: infoDomain.label, + lang: i18n.language, + }); if (domainName) { return { name: domainName, labelText: domainName, uniqueItemId: infoDomain.id, - groupId: infoDomain.type.graph.id, + // groupId: infoDomain.type.graph.id, } as MultiSelectData; } } diff --git a/terminology-ui/src/common/components/terminology-components/organization-selector.tsx b/terminology-ui/src/common/components/terminology-components/organization-selector.tsx index ef35ef609..7725474a1 100644 --- a/terminology-ui/src/common/components/terminology-components/organization-selector.tsx +++ b/terminology-ui/src/common/components/terminology-components/organization-selector.tsx @@ -12,6 +12,7 @@ import { useEffect, useMemo, useState } from 'react'; import { checkPermission } from '@app/common/utils/has-permission'; import { useSelector } from 'react-redux'; import { selectLogin } from '../login/login.slice'; +import { getLanguageVersion } from 'yti-common-ui/utils/get-language-version'; export interface OrganizationSelectorProps { update: ({ key, data }: UpdateTerminology) => void; @@ -62,14 +63,18 @@ export default function OrganizationSelector({ targetOrganizations: [org.id.toString()], }) ) { - const orgName = org.properties.prefLabel.value; + const orgName = getLanguageVersion({ + data: org.label, + lang: i18n.language, + }); if (orgName) { return { name: orgName, labelText: orgName, uniqueItemId: org.id, - organizationId: org.type.graph.id, + // TODO + // organizationId: org.type.graph.id, } as MultiSelectData; } } diff --git a/terminology-ui/src/common/components/terminology-search/terminology-search.slice.tsx b/terminology-ui/src/common/components/terminology-search/terminology-search.slice.tsx index 311cf8d90..60d6da400 100644 --- a/terminology-ui/src/common/components/terminology-search/terminology-search.slice.tsx +++ b/terminology-ui/src/common/components/terminology-search/terminology-search.slice.tsx @@ -1,12 +1,13 @@ import { createSlice } from '@reduxjs/toolkit'; import { createApi } from '@reduxjs/toolkit/query/react'; -import { - GroupSearchResult, - OrganizationSearchResult, - TerminologySearchResult, -} from '@app/common/interfaces/terminology.interface'; import { UrlState } from '@app/common/utils/hooks/use-url-state'; import { getTerminologyApiBaseQuery } from '@app/store/api-base-query'; +import { Organization } from 'yti-common-ui/interfaces/organization.interface'; +import { Group } from 'yti-common-ui/interfaces/group.interface'; +import { + SearchResponse, + TerminogyResponseObject, +} from '@app/common/interfaces/interfaces-v2'; export const initialState = {}; @@ -22,15 +23,15 @@ export const terminologySearchApi = createApi({ tagTypes: ['TerminologySearch'], endpoints: (builder) => ({ getSearchResult: builder.query< - TerminologySearchResult, + SearchResponse, { urlState: UrlState; language: string } >({ query: (value) => ({ - url: '/searchTerminology', - method: 'POST', - data: { + url: '/frontend/search-terminologies', + method: 'GET', + params: { query: value.urlState.q, - statuses: value.urlState.status.map((s) => s.toUpperCase()), + status: value.urlState.status.map((s) => s.toUpperCase()), groups: value.urlState.domain, organizations: value.urlState.organization ? [value.urlState.organization] @@ -44,18 +45,18 @@ export const terminologySearchApi = createApi({ }), providesTags: ['TerminologySearch'], }), - getGroups: builder.query({ + getGroups: builder.query({ query: (value) => ({ - url: `/v2/groups?language=${value}`, + url: `/frontend/service-categories?language=${value}`, method: 'GET', }), }), getOrganizations: builder.query< - OrganizationSearchResult[], + Organization[], { language: string; showChildOrganizations?: boolean } >({ query: (value) => ({ - url: `/v2/organizations?language=${ + url: `/frontend/organizations?language=${ value.language }&showChildOrganizations=${value.showChildOrganizations ?? false}`, method: 'GET', diff --git a/terminology-ui/src/common/interfaces/interfaces-v2.ts b/terminology-ui/src/common/interfaces/interfaces-v2.ts new file mode 100644 index 000000000..9a1a921d4 --- /dev/null +++ b/terminology-ui/src/common/interfaces/interfaces-v2.ts @@ -0,0 +1,75 @@ +import { Status } from 'yti-common-ui/interfaces/status.interface'; + +/* build fails for empty interfaces +export interface Terminology {} + +export interface TerminologyInfo {} + +export interface Concept {} + +export interface ConceptInfo {} + +export interface Term {} + +export interface ConceptCollection {} + +export interface ConceptCollectionInfo {} + +export interface ConceptReferenceInfo {} +*/ + +export interface SearchRequest { + query: string; + sortLang: string; + status: Status[]; + pageSize: number; + pageFrom: number; +} + +export interface TerminologySearchRequest extends SearchRequest { + searchconcepts: boolean; + groups: string[]; + organizations: string[]; + languages: string[]; +} + +export interface ConceptSearchRequest extends SearchRequest { + namespace: string; +} + +export interface ResponseObject { + uri: string; + id: string; + label: LocalizedValue; + status: Status; +} + +export interface TerminogyResponseObject extends ResponseObject { + prefix: string; + description: LocalizedValue; + type: TerminologyType; + organizations: string[]; + groups: string[]; + matchingConcepts: ConceptResponseObject[]; +} + +export interface ConceptResponseObject extends ResponseObject { + definition: LocalizedValue; + identifier: string; +} + +export interface SearchResponse { + totalHitCount: number; + pageSize: number; + pageFrom: number; + responseObjects: T[]; +} + +export interface LocalizedValue { + [key: string]: string; +} + +export enum TerminologyType { + TERMINOLOGICAL_VOCABULARY = 'TERMINOLOGICAL_VOCABULARY', + OTHER_VOCABULARY = 'OTHER_VOCABULARY', +} diff --git a/terminology-ui/src/common/utils/translation-helpers.ts b/terminology-ui/src/common/utils/translation-helpers.ts index e7304b24b..47f0de8a5 100644 --- a/terminology-ui/src/common/utils/translation-helpers.ts +++ b/terminology-ui/src/common/utils/translation-helpers.ts @@ -1,4 +1,5 @@ import { TFunction } from 'next-i18next'; +import { TerminologyType } from '../interfaces/interfaces-v2'; export function translateStatus(status: string, t: TFunction) { switch (status) { @@ -66,11 +67,14 @@ export function translateTermType(type: string, t: TFunction) { } } -export function translateTerminologyType(type: string, t: TFunction) { +export function translateTerminologyType( + type: TerminologyType | string, + t: TFunction +) { switch (type) { - case 'TERMINOLOGICAL_VOCABULARY': + case TerminologyType.TERMINOLOGICAL_VOCABULARY: return t('terminology-type.terminologyical-vocabulary', { ns: 'common' }); - case 'OTHER_VOCABULARY': + case TerminologyType.OTHER_VOCABULARY: return t('terminology-type.other-vocabulary', { ns: 'common' }); default: return t('terminology-type.undefined', { ns: 'common' }); diff --git a/terminology-ui/src/modules/collection/index.tsx b/terminology-ui/src/modules/collection/index.tsx index 895cd3798..a1d491fb8 100644 --- a/terminology-ui/src/modules/collection/index.tsx +++ b/terminology-ui/src/modules/collection/index.tsx @@ -79,7 +79,7 @@ export default function Collection({ return []; } return organizations - ?.filter((org) => org.references.parent) + ?.filter((org) => org.parentOrganization) .map((org) => org.id); }, [organizations, isLoading, isError]); diff --git a/terminology-ui/src/modules/concept/index.tsx b/terminology-ui/src/modules/concept/index.tsx index c69f05ebe..e17634146 100644 --- a/terminology-ui/src/modules/concept/index.tsx +++ b/terminology-ui/src/modules/concept/index.tsx @@ -84,7 +84,7 @@ export default function Concept({ terminologyId, conceptId }: ConceptProps) { return []; } return organizations - ?.filter((org) => org.references.parent) + ?.filter((org) => org.parentOrganization) .map((org) => org.id); }, [organizations, isLoading, isError]); diff --git a/terminology-ui/src/modules/own-information/index.tsx b/terminology-ui/src/modules/own-information/index.tsx index c266e66f7..174d1bd29 100644 --- a/terminology-ui/src/modules/own-information/index.tsx +++ b/terminology-ui/src/modules/own-information/index.tsx @@ -24,6 +24,7 @@ import InlineAlert from 'yti-common-ui/inline-alert'; import { useGetRequestsQuery } from '@app/common/components/access-request/access-request.slice'; import { MainTitle } from 'yti-common-ui/title-block'; import { translateRole } from '@app/common/utils/translation-helpers'; +import { getLanguageVersion } from 'yti-common-ui/utils/get-language-version'; export default function OwnInformation() { const user = useSelector(selectLogin()); @@ -181,13 +182,12 @@ export default function OwnInformation() { } function getOrganizationName(id: string): string { - return ( - getPropertyValue({ - property: organizations - ?.filter((organization) => organization.id === id) - .map((organization) => organization.properties.prefLabel), - language: i18n.language, - }) ?? '' - ); + const organization = organizations?.find((org) => org.id === id); + return organization + ? getLanguageVersion({ + data: organization.label, + lang: i18n?.language ?? 'fi', + }) + : ''; } } diff --git a/terminology-ui/src/modules/terminology-search/index.tsx b/terminology-ui/src/modules/terminology-search/index.tsx index 144ef8a56..ecb43735b 100644 --- a/terminology-ui/src/modules/terminology-search/index.tsx +++ b/terminology-ui/src/modules/terminology-search/index.tsx @@ -42,6 +42,7 @@ import { import NewTerminology from '@app/modules/new-terminology'; import { setTitle } from '@app/common/components/title/title.slice'; import Link from 'next/link'; +import { getLanguageVersion } from 'yti-common-ui/utils/get-language-version'; export default function TerminologySearch() { const { t, i18n } = useTranslation(); @@ -65,15 +66,37 @@ export default function TerminologySearch() { const [showLoading, setShowLoading] = useState(true); const previousAlerts = useSelector(selectAlert()); + const organizations = useMemo(() => { + if (!orgsData) { + return []; + } + + return orgsData.map((o) => ({ + id: o.id, + label: getLanguageVersion({ data: o.label, lang: i18n.language }), + })); + }, [orgsData]); + + const groups = useMemo(() => { + if (!groupsData) { + return []; + } + + return groupsData.map((g) => ({ + id: g.identifier, + label: getLanguageVersion({ data: g.label, lang: i18n.language }), + })); + }, [groupsData]); + const results: SearchResultData[] = useMemo(() => { - if (!data || !data.terminologies) { + if (!data || !data.responseObjects) { return []; } - return data.terminologies.map((terminology) => ({ - id: terminology.id, - contributors: terminology.contributors.map((c) => - getPrefLabel({ prefLabels: c.label, lang: i18n.language }) + return data.responseObjects.map((terminology) => ({ + id: terminology.prefix, + contributors: terminology.organizations.map( + (c) => organizations.find((o) => o.id === c)?.label ?? '' ), description: terminology.description ? getPrefLabel({ @@ -83,61 +106,40 @@ export default function TerminologySearch() { : '', icon: , status: terminology.status, - partOf: terminology.informationDomains.map((d) => - getPrefLabel({ prefLabels: d.label, lang: i18n.language }) + partOf: terminology.groups.map( + (d) => groups.find((g) => g.id === d)?.label ?? '' ), title: getPrefLabel({ prefLabels: terminology.label, lang: i18n.language, }), - titleLink: `terminology/${terminology.id}`, - type: translateTerminologyType(terminology.type ?? '', t), + titleLink: `terminology/${terminology.prefix}`, + type: translateTerminologyType(terminology.type, t), })); }, [data, t, i18n.language]); - const deepHits = useMemo(() => { - if (!data || !data.deepHits) { - return {}; - } - - const keys = Object.keys(data.deepHits); - const returnValue: { - [key: string]: { label: string; id: string; uri: string }[]; - } = {}; - - keys.forEach((key) => { - returnValue[key] = data.deepHits[key][0].topHits.map((hit) => ({ - label: getPrefLabel({ prefLabels: hit.label, lang: i18n.language }), - id: hit.id, - uri: `terminology/${key}/concept/${hit.id}`, - })); - }); - - return returnValue; + const extra = useMemo(() => { + return ( + data?.responseObjects.reduce((deepHitsResult, object) => { + const concepts = object.matchingConcepts.map((concept) => { + return { + label: getLanguageVersion({ + data: concept.label, + lang: i18n.language, + }), + id: concept.identifier, + uri: concept.uri, + }; + }); + if (concepts.length > 0) { + deepHitsResult[object.prefix] = concepts; + } + return deepHitsResult; + }, {} as { [key: string]: { label: string; id: string; uri: string }[] }) ?? + {} + ); }, [data, i18n.language]); - const organizations = useMemo(() => { - if (!orgsData) { - return []; - } - - return orgsData.map((o) => ({ - id: o.id, - label: o.properties.prefLabel.value, - })); - }, [orgsData]); - - const groups = useMemo(() => { - if (!groupsData) { - return []; - } - - return groupsData.map((g) => ({ - id: g.id, - label: g.properties.prefLabel.value, - })); - }, [groupsData]); - useEffect(() => { dispatch( setAlert( @@ -264,7 +266,7 @@ export default function TerminologySearch() { expander: { buttonLabel: t('results-with-query-from-terminology'), contentLabel: t('concepts'), - deepHits, + deepHits: extra, }, }} /> diff --git a/terminology-ui/src/modules/terminology-search/search-page-filter.tsx b/terminology-ui/src/modules/terminology-search/search-page-filter.tsx index e1c19c7dd..3fa5811c3 100644 --- a/terminology-ui/src/modules/terminology-search/search-page-filter.tsx +++ b/terminology-ui/src/modules/terminology-search/search-page-filter.tsx @@ -1,9 +1,5 @@ import { useTranslation } from 'next-i18next'; import { Counts } from '@app/common/interfaces/counts.interface'; -import { - GroupSearchResult, - OrganizationSearchResult, -} from '@app/common/interfaces/terminology.interface'; import Separator from 'yti-common-ui/separator'; import Filter, { KeywordFilter, @@ -13,13 +9,16 @@ import Filter, { InformationDomainFilter, } from 'yti-common-ui/filter'; import { FilterTopPartBlock } from './terminology-search.styles'; +import { Organization } from 'yti-common-ui/interfaces/organization.interface'; +import { Group } from 'yti-common-ui/interfaces/group.interface'; +import { getLanguageVersion } from 'yti-common-ui/utils/get-language-version'; export interface SearchPageFilterProps { isModal?: boolean; onModalClose?: () => void; resultCount?: number; - organizations?: OrganizationSearchResult[]; - groups?: GroupSearchResult[]; + organizations?: Organization[]; + groups?: Group[]; counts?: Counts; } @@ -31,7 +30,7 @@ export function SearchPageFilter({ groups, counts, }: SearchPageFilterProps) { - const { t } = useTranslation('common'); + const { t, i18n } = useTranslation('common'); return ( ({ - labelText: org.properties.prefLabel.value, + labelText: getLanguageVersion({ + data: org.label, + lang: i18n.language, + }), uniqueItemId: org.id, })) ?? [] } @@ -82,8 +84,11 @@ export function SearchPageFilter({ title={t('terminology-search-filter-show-by-information-domain')} domains={ groups?.map((group) => ({ - id: group.id, - name: group.properties.prefLabel.value, + id: group.identifier, + name: getLanguageVersion({ + data: group.label, + lang: i18n.language, + }), })) ?? [] } isModal={isModal} diff --git a/terminology-ui/src/modules/vocabulary/index.tsx b/terminology-ui/src/modules/vocabulary/index.tsx index d13817385..dc5ef3c3f 100644 --- a/terminology-ui/src/modules/vocabulary/index.tsx +++ b/terminology-ui/src/modules/vocabulary/index.tsx @@ -147,7 +147,7 @@ export default function Vocabulary({ id }: VocabularyProps) { return []; } return organizationsData - ?.filter((org) => org.references.parent) + ?.filter((org) => org.parentOrganization) .map((org) => org.id); }, [organizationsData, isOrganizationsLoading, organizationsError]); diff --git a/terminology-ui/src/pages/api/auth/callback.ts b/terminology-ui/src/pages/api/auth/callback.ts index 08cca9e2d..54abc248c 100644 --- a/terminology-ui/src/pages/api/auth/callback.ts +++ b/terminology-ui/src/pages/api/auth/callback.ts @@ -36,10 +36,9 @@ export default withIronSessionApiRoute( const proxyUrl = process.env.AUTH_PROXY_URL ?? 'http://yti-auth-proxy'; const apiBase = process.env.TERMINOLOGY_API_URL ?? - 'http://yti-terminology-api:9103/terminology-api'; + 'http://yti-terminology-api:9103/terminology-api/v2'; const apiPath = new URL(apiBase).pathname; - const fetchUrl = - proxyUrl + apiPath + '/api/v1/frontend/authenticated-user'; + const fetchUrl = proxyUrl + apiPath + '/user'; // Shibboleth configuration requires X-Forwarded-For to allow access with // the _shibsession cookie. Otherwise the "consistentAddress" setting will diff --git a/terminology-ui/src/pages/api/auth/fake-login.ts b/terminology-ui/src/pages/api/auth/fake-login.ts index 907ff2621..96b7e2aa6 100644 --- a/terminology-ui/src/pages/api/auth/fake-login.ts +++ b/terminology-ui/src/pages/api/auth/fake-login.ts @@ -19,8 +19,7 @@ export default withIronSessionApiRoute( const email = (req.query['fake.login.mail'] as string) ?? 'admin@localhost'; try { - let fetchUrl: string = - process.env.TERMINOLOGY_API_URL + '/api/v1/frontend/authenticated-user'; + let fetchUrl: string = process.env.TERMINOLOGY_API_URL + '/user'; fetchUrl += '?fake.login.mail=' + encodeURIComponent(email); let authProxyHeaders = {}; diff --git a/terminology-ui/src/store/api-base-query.test.ts b/terminology-ui/src/store/api-base-query.test.ts index 21a076bae..7faa9bb0a 100644 --- a/terminology-ui/src/store/api-base-query.test.ts +++ b/terminology-ui/src/store/api-base-query.test.ts @@ -33,7 +33,7 @@ describe('axios base query', () => { }; // any API call would be fine here - mock.onGet(/\/v1\/frontend\/vocabulary\?graphId=\d+/).reply((config) => { + mock.onGet(/\/vocabulary\?graphId=\d+/).reply((config) => { return [ 200, 'JSESSIONID exists in headers: ' + diff --git a/terminology-ui/src/store/api-base-query.ts b/terminology-ui/src/store/api-base-query.ts index 2fcda3b52..1bd4c3d5e 100644 --- a/terminology-ui/src/store/api-base-query.ts +++ b/terminology-ui/src/store/api-base-query.ts @@ -8,8 +8,8 @@ export const getTerminologyApiBaseQuery = ( ) => axiosBaseQuery({ baseUrl: process.env.TERMINOLOGY_API_URL - ? `${process.env.TERMINOLOGY_API_URL}/api/v1/frontend` - : '/terminology-api/api/v1/frontend', + ? `${process.env.TERMINOLOGY_API_URL}` + : '/terminology-api/v2', // prepareHeaders is used to take the JSESSIONID stored in session // and provide it as a cookie for API calls diff --git a/terminology-ui/src/tests/fake-login.test.ts b/terminology-ui/src/tests/fake-login.test.ts index 8a281dee9..ab43e3800 100644 --- a/terminology-ui/src/tests/fake-login.test.ts +++ b/terminology-ui/src/tests/fake-login.test.ts @@ -46,14 +46,9 @@ describe('api endpoint - fake login', () => { mock .onGet( - 'http://terminology-api.invalid/terminology-api/api/v1/frontend/authenticated-user?fake.login.mail=admin%40localhost' + 'http://terminology-api.invalid/terminology-api/v2/user?fake.login.mail=admin%40localhost' ) .reply(200, fakeUser, { 'set-cookie': ['JSESSIONID=foo'] }); - /* - mock - .onGet('http://test.invalid/api/v1/frontend/authenticated-user') - .reply(200, fakeUser); - */ await fakeLogin(req, res); @@ -89,7 +84,7 @@ describe('api endpoint - fake login', () => { mock .onGet( - 'http://terminology-api.invalid/terminology-api/api/v1/frontend/authenticated-user?fake.login.mail=admin%40localhost' + 'http://terminology-api.invalid/terminology-api/v2/user?fake.login.mail=admin%40localhost' ) .reply(500, { error: 'An error occurred' }); diff --git a/terminology-ui/src/tests/login.test.ts b/terminology-ui/src/tests/login.test.ts index b1f7f6458..f324a0c35 100644 --- a/terminology-ui/src/tests/login.test.ts +++ b/terminology-ui/src/tests/login.test.ts @@ -78,9 +78,7 @@ describe('api endpoint - login', () => { // callback will call authenticated-user for user details mock - .onGet( - 'http://auth-proxy.invalid/terminology-api/api/v1/frontend/authenticated-user' - ) + .onGet('http://auth-proxy.invalid/terminology-api/v2/user') .reply(200, fakeUser, { 'set-cookie': ['JSESSIONID=foo'], }); diff --git a/terminology-ui/src/tests/terminology/[terminologyId].test.tsx b/terminology-ui/src/tests/terminology/[terminologyId].test.tsx index f6287edd2..3279316c5 100644 --- a/terminology-ui/src/tests/terminology/[terminologyId].test.tsx +++ b/terminology-ui/src/tests/terminology/[terminologyId].test.tsx @@ -53,10 +53,10 @@ describe('terminologyId page', () => { const polledUrls = [ '/vocabulary?graphId=1234', - '/conceptCounts?graphId=1234', - '/statusCounts?graphId=1234', - '/authenticated-user', - '/fakeableUsers', + '/concept-counts?graphId=1234', + '/status-counts?graphId=1234', + '/user', + '/fakeable-users', ]; const foundUrls = polledUrls.filter((url) => From f60e613918cde3dcb6c9886727b3860efaca93a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kalle=20Kytt=C3=A4l=C3=A4?= Date: Fri, 23 Aug 2024 09:52:11 +0300 Subject: [PATCH 02/35] terminology page, concept list, rename vocabulary -> terminology --- .../common/components/counts/counts.slice.tsx | 6 +- .../info-dropdown/info-expander.test.tsx | 4 +- .../info-dropdown/info-expander.tsx | 223 ++++++------------ .../render-expander-content.tsx | 13 +- .../common/components/removal-modal/index.tsx | 6 +- .../components/status-mass-edit/index.tsx | 18 +- .../vocabulary/vocabulary.slice.tsx | 35 +-- .../src/common/interfaces/interfaces-v2.ts | 20 +- .../interfaces/status-counts.interface.ts | 6 +- .../src/common/utils/get-store-data.test.tsx | 15 +- .../src/common/utils/has-permission.tsx | 2 +- .../src/modules/collection/index.tsx | 52 ++-- terminology-ui/src/modules/concept/index.tsx | 48 ++-- .../expander-concept-content.tsx | 17 +- .../src/modules/edit-collection/index.tsx | 35 +-- .../generate-form-data-test-variables.tsx | 2 +- .../edit-concept/generate-form-data.tsx | 28 +-- .../src/modules/edit-concept/index.tsx | 45 ++-- .../edit-vocabulary/generate-initial-data.ts | 49 ++-- .../src/modules/edit-vocabulary/index.tsx | 47 ++-- .../src/modules/vocabulary/index.tsx | 48 ++-- .../vocabulary/terminology-list-filter.tsx | 12 +- .../src/pages/terminology/[terminologyId].tsx | 12 +- .../collection/[collectionId].tsx | 8 +- .../collection/[collectionId]/edit.tsx | 4 +- .../[terminologyId]/concept/[conceptId].tsx | 8 +- .../concept/[conceptId]/edit.tsx | 4 +- .../terminology/[terminologyId]/edit.tsx | 4 +- .../[terminologyId]/new-collection.tsx | 4 +- .../[terminologyId]/new-concept.tsx | 4 +- .../src/store/api-base-query.test.ts | 6 +- terminology-ui/src/store/index.tsx | 6 +- 32 files changed, 351 insertions(+), 440 deletions(-) diff --git a/terminology-ui/src/common/components/counts/counts.slice.tsx b/terminology-ui/src/common/components/counts/counts.slice.tsx index ae72d7b1c..0e6c81d4e 100644 --- a/terminology-ui/src/common/components/counts/counts.slice.tsx +++ b/terminology-ui/src/common/components/counts/counts.slice.tsx @@ -10,13 +10,13 @@ export const countsApi = createApi({ endpoints: (builder) => ({ getCounts: builder.query({ query: () => ({ - url: '/frontend/counts?vocabularies=true', + url: '/frontend/counts', method: 'GET', }), }), getVocabularyCount: builder.query({ query: (value) => ({ - url: `/frontend/concept-counts?graphId=${value}`, + url: `/frontend/concept-counts?prefix=${value}`, method: 'GET', headers: { 'content-type': 'application/json', @@ -25,7 +25,7 @@ export const countsApi = createApi({ }), getStatusCounts: builder.query({ query: (value) => ({ - url: `/frontend/status-counts?graphId=${value}`, + url: `/frontend/status-counts?prefix=${value}`, method: 'GET', }), }), diff --git a/terminology-ui/src/common/components/info-dropdown/info-expander.test.tsx b/terminology-ui/src/common/components/info-dropdown/info-expander.test.tsx index b1577c3ba..94898db3b 100644 --- a/terminology-ui/src/common/components/info-dropdown/info-expander.test.tsx +++ b/terminology-ui/src/common/components/info-dropdown/info-expander.test.tsx @@ -10,7 +10,7 @@ import { import mockRouter from 'next-router-mock'; import { v4 } from 'uuid'; import { adminControlsSlice } from '../admin-controls/admin-controls.slice'; -import { vocabularyApi } from '../vocabulary/vocabulary.slice'; +import { terminologyApi } from '../vocabulary/vocabulary.slice'; import { subscriptionApi } from '../subscription/subscription.slice'; import { screen, waitFor } from '@testing-library/react'; import { terminologySearchApi } from '../terminology-search/terminology-search.slice'; @@ -19,7 +19,7 @@ jest.mock('next/dist/client/router', () => require('next-router-mock')); const reducers = [ adminControlsSlice, - vocabularyApi, + terminologyApi, loginApi, subscriptionApi, terminologySearchApi, diff --git a/terminology-ui/src/common/components/info-dropdown/info-expander.tsx b/terminology-ui/src/common/components/info-dropdown/info-expander.tsx index 26d76af7b..561b82233 100644 --- a/terminology-ui/src/common/components/info-dropdown/info-expander.tsx +++ b/terminology-ui/src/common/components/info-dropdown/info-expander.tsx @@ -5,7 +5,6 @@ import { ExpanderContent, ExpanderTitleButton, ExternalLink, - IconDownload, IconEdit, IconPlus, VisuallyHidden, @@ -15,13 +14,12 @@ import { InfoExpanderWrapper, PropertyList, } from './info-expander.styles'; -import { VocabularyInfoDTO } from '@app/common/interfaces/vocabulary.interface'; import Separator from 'yti-common-ui/separator'; -import { BasicBlock, BasicBlockExtraWrapper } from 'yti-common-ui/block'; import { - MultilingualPropertyBlock, - PropertyBlock, -} from '@app/common/components/block'; + BasicBlock, + BasicBlockExtraWrapper, + MultilingualBlock, +} from 'yti-common-ui/block'; import FormattedDate from 'yti-common-ui/formatted-date'; import { useSelector } from 'react-redux'; import { selectLogin } from '@app/common/components/login/login.slice'; @@ -30,21 +28,22 @@ import { translateLanguage, translateTerminologyType, } from '@app/common/utils/translation-helpers'; -import { getPropertyValue } from '../property-value/get-property-value'; -import PropertyValue from '../property-value'; import RemovalModal from '../removal-modal'; import NewConceptModal from '../new-concept-modal'; import ConceptImportModal from '../concept-import'; import { useGetConceptResultQuery } from '../vocabulary/vocabulary.slice'; import useUrlState from '@app/common/utils/hooks/use-url-state'; -import axios from 'axios'; import { useStoreDispatch } from '@app/store'; -import { setAlert } from '../alert/alert.slice'; import UpdateWithFileModal from '../update-with-file-modal'; import StatusMassEdit from '../status-mass-edit'; import isEmail from 'validator/lib/isEmail'; import { useRouter } from 'next/router'; import { compareLocales } from '@app/common/utils/compare-locals'; +import { + Terminology, + TerminologyType, +} from '@app/common/interfaces/interfaces-v2'; +import { getLanguageVersion } from 'yti-common-ui/utils/get-language-version'; const Subscription = dynamic( () => import('@app/common/components/subscription/subscription') @@ -52,7 +51,7 @@ const Subscription = dynamic( const CopyTerminologyModal = dynamic(() => import('../copy-terminology-modal')); interface InfoExpanderProps { - data?: VocabularyInfoDTO; + data?: Terminology; childOrganizations?: string[]; } @@ -64,8 +63,7 @@ export default function InfoExpander({ const { urlState } = useUrlState(); const router = useRouter(); const user = useSelector(selectLogin()); - const terminologyId = - data?.type?.graph.id ?? data?.identifier?.type.graph?.id ?? ''; + const terminologyId = data?.prefix ?? ''; const { refetch: refetchConcepts } = useGetConceptResultQuery({ id: terminologyId, urlState, @@ -78,65 +76,32 @@ export default function InfoExpander({ return null; } - const contact = getPropertyValue({ - property: data.properties.contact, - }).trim(); - - const handleDownloadClick = async () => { - const result = await axios.get( - `/terminology-api/api/v1/export/${data.type.graph.id}?format=xlsx`, - { responseType: 'arraybuffer' } - ); - - if (result.status !== 200) { - dispatch( - setAlert( - [ - { - note: result, - displayText: t('error-occured_download-excel', { ns: 'alert' }), - }, - ], - [] - ) - ); - return; - } - - const url = window.URL.createObjectURL(new Blob([result.data])); - const fileName = result.headers['content-disposition'] - .split('=')[1] - .endsWith('.xlsx') - ? result.headers['content-disposition'].split('=')[1].trim() - : `${getPropertyValue({ - property: data.properties.prefLabel, - language: i18n.language, - })}_export.xlsx`; - const link = document.createElement('a'); - - link.href = url; - link.setAttribute('download', fileName); - document.body.appendChild(link); - link.click(); - link.remove(); - }; - return ( {t('vocabulary-info-terminology')} - - + + compareLocales(a[0], b[0])) + .map((l) => ({ lang: l[0], value: l[1] }))} + /> + + + + {Object.keys(data.description).length > 0 ? ( + compareLocales(a[0], b[0])) + .map((l) => ({ lang: l[0], value: l[1] }))} + /> + ) : ( + <> + )} + + {data.uri} @@ -144,60 +109,54 @@ export default function InfoExpander({ title={t('vocabulary-info-information-domain')} id="information-domains" > - {data.references.inGroup - ?.map((group) => - getPropertyValue({ - property: group.properties.prefLabel, - language: i18n.language, - }) + {data.groups + .map((g) => + getLanguageVersion({ data: g.label, lang: i18n.language }) ) .join(', ')} - + {data.languages ?.slice() - .sort((a, b) => compareLocales(a.value, b.value))} - delimiter=", " - valueAccessor={({ value }) => { - // if no translation found for language, return only language code - const tr = translateLanguage(value, t); - if (tr === value) { - return value; - } - return `${tr} ${value.toUpperCase()}`; - }} - id="languages" - /> + .sort((a, b) => compareLocales(a, b)) + .map((lang) => { + const tr = translateLanguage(lang, t); + if (tr) { + return `${tr} ${lang.toUpperCase()}`; + } + return lang; + }) + .join(', ')} + + {translateTerminologyType( - data.properties?.terminologyType?.[0].value ?? - 'TERMINOLOGICAL_VOCABULARY', + data.type ?? TerminologyType.TERMINOLOGICAL_VOCABULARY, t )} - {contact && ( + {data.contact && ( - {contact} + {data.contact} )} {HasPermission({ actions: 'EDIT_TERMINOLOGY', - targetOrganization: data.references.contributor, + targetOrganization: data.organizations, }) && ( <> @@ -220,16 +179,16 @@ export default function InfoExpander({ @@ -242,7 +201,7 @@ export default function InfoExpander({ {HasPermission({ actions: 'EDIT_TERMINOLOGY', - targetOrganization: data.references.contributor, + targetOrganization: data.organizations, }) && ( <> @@ -253,10 +212,7 @@ export default function InfoExpander({ value) ?? - [] - } + languages={data.languages} /> - - - - } - id="export-terminology-block" - > - {t('vocabulary-info-vocabulary-export-description')} - - - - {!user.anonymous && ( <> - {data.references.contributor - ?.filter( - (c) => - c && - c.properties.prefLabel && - !childOrganizations?.includes(c.id) - ) - .map((contributor) => ( -
  • - + {data.organizations + .filter((org) => !childOrganizations?.includes(org.id)) + .map((org) => ( +
  • + {getLanguageVersion({ data: org.label, lang: i18n.language })}
  • ))}
    - - {data.createdBy && `, ${data.createdBy}`} + + {data.creator.name && `, ${data.creator.name}`} - {data.properties.origin && ( - - {data.properties.origin[0].value} - - )} +
    TODO: origin
    - - {data.lastModifiedBy && `, ${data.lastModifiedBy}`} + + {data.modifier.name && `, ${data.modifier.name}`}
    diff --git a/terminology-ui/src/common/components/relational-information-block/render-expander-content.tsx b/terminology-ui/src/common/components/relational-information-block/render-expander-content.tsx index a8962ed74..0c4cfdf4d 100644 --- a/terminology-ui/src/common/components/relational-information-block/render-expander-content.tsx +++ b/terminology-ui/src/common/components/relational-information-block/render-expander-content.tsx @@ -1,7 +1,7 @@ import { memo } from 'react'; import { useTranslation } from 'next-i18next'; import { useGetConceptQuery } from '../concept/concept.slice'; -import { useGetVocabularyQuery } from '../vocabulary/vocabulary.slice'; +import { useGetTerminologyQuery } from '../vocabulary/vocabulary.slice'; import { ExpanderContent } from 'suomifi-ui-components'; import SaveSpinner from 'yti-common-ui/save-spinner'; import { BasicBlock } from 'yti-common-ui/block'; @@ -28,7 +28,7 @@ function RenderExpanderContent({ }, { skip: !isOpen } ); - const { data: terminology } = useGetVocabularyQuery( + const { data: terminology } = useGetTerminologyQuery( { id: terminologyId, }, @@ -60,14 +60,7 @@ function RenderExpanderContent({ - {terminology && ( - - )} + {terminology &&
    TODO contributor
    } ,{' '} diff --git a/terminology-ui/src/common/components/removal-modal/index.tsx b/terminology-ui/src/common/components/removal-modal/index.tsx index 71033735f..2a7e19523 100644 --- a/terminology-ui/src/common/components/removal-modal/index.tsx +++ b/terminology-ui/src/common/components/removal-modal/index.tsx @@ -1,6 +1,5 @@ import { Collection } from '@app/common/interfaces/collection.interface'; import { Concept } from '@app/common/interfaces/concept.interface'; -import { VocabularyInfoDTO } from '@app/common/interfaces/vocabulary.interface'; import { translateRemovalModalConfirmation, translateRemovalModalDescription, @@ -39,13 +38,14 @@ import { RemoveModalContent, } from './removal-modal.styles'; import { useGetAuthenticatedUserMutMutation } from '../login/login.slice'; +import { Terminology } from '@app/common/interfaces/interfaces-v2'; interface RemovalModalProps { nonDescriptive?: boolean; removalData: | { type: 'terminology'; - data?: VocabularyInfoDTO; + data?: Terminology; } | { type: 'concept'; @@ -118,7 +118,7 @@ export default function RemovalModal({ const handleVisibility = () => { if ( removalData.type === 'terminology' && - removalData.data?.properties.status?.[0].value === 'VALID' + removalData.data?.status === 'VALID' ) { setShowError(true); return; diff --git a/terminology-ui/src/common/components/status-mass-edit/index.tsx b/terminology-ui/src/common/components/status-mass-edit/index.tsx index 53bca00fd..9fabc1666 100644 --- a/terminology-ui/src/common/components/status-mass-edit/index.tsx +++ b/terminology-ui/src/common/components/status-mass-edit/index.tsx @@ -155,8 +155,7 @@ export default function StatusMassEdit({ terminologyId }: StatusMassEditProps) { { labelText: t('draft', { count: statusCounts - ? statusCounts.counts.concepts.DRAFT + - statusCounts.counts.terms.DRAFT + ? statusCounts.concepts.DRAFT + statusCounts.terms.DRAFT : 0, }), uniqueItemId: 'DRAFT', @@ -164,8 +163,7 @@ export default function StatusMassEdit({ terminologyId }: StatusMassEditProps) { { labelText: t('valid', { count: statusCounts - ? statusCounts.counts.concepts.VALID + - statusCounts.counts.terms.VALID + ? statusCounts.concepts.VALID + statusCounts.terms.VALID : 0, }), uniqueItemId: 'VALID', @@ -173,8 +171,8 @@ export default function StatusMassEdit({ terminologyId }: StatusMassEditProps) { { labelText: t('superseded', { count: statusCounts - ? statusCounts.counts.concepts.SUPERSEDED + - statusCounts.counts.terms.SUPERSEDED + ? statusCounts.concepts.SUPERSEDED + + statusCounts.terms.SUPERSEDED : 0, }), uniqueItemId: 'SUPERSEDED', @@ -182,8 +180,8 @@ export default function StatusMassEdit({ terminologyId }: StatusMassEditProps) { { labelText: t('retired', { count: statusCounts - ? statusCounts.counts.concepts.RETIRED + - statusCounts.counts.terms.RETIRED + ? statusCounts.concepts.RETIRED + + statusCounts.terms.RETIRED : 0, }), uniqueItemId: 'RETIRED', @@ -201,7 +199,7 @@ export default function StatusMassEdit({ terminologyId }: StatusMassEditProps) { > {t('concepts', { count: statusCounts - ? statusCounts.counts.concepts[ + ? statusCounts.concepts[ chosenStartState as keyof StatusCountsObjects ] : 0, @@ -214,7 +212,7 @@ export default function StatusMassEdit({ terminologyId }: StatusMassEditProps) { > {t('terms', { count: statusCounts - ? statusCounts.counts.terms[ + ? statusCounts.terms[ chosenStartState as keyof StatusCountsObjects ] : 0, diff --git a/terminology-ui/src/common/components/vocabulary/vocabulary.slice.tsx b/terminology-ui/src/common/components/vocabulary/vocabulary.slice.tsx index 522f3c28e..d07ea54f6 100644 --- a/terminology-ui/src/common/components/vocabulary/vocabulary.slice.tsx +++ b/terminology-ui/src/common/components/vocabulary/vocabulary.slice.tsx @@ -3,12 +3,15 @@ import { createApi } from '@reduxjs/toolkit/query/react'; import { Collection } from '@app/common/interfaces/collection.interface'; import { VocabulariesDTO, - VocabularyConcepts, VocabularyCopyInfo, - VocabularyInfoDTO, } from '@app/common/interfaces/vocabulary.interface'; import { UrlState } from '@app/common/utils/hooks/use-url-state'; import { getTerminologyApiBaseQuery } from '@app/store/api-base-query'; +import { + ConceptResponseObject, + SearchResponse, + Terminology, +} from '@app/common/interfaces/interfaces-v2'; export const vocabularyInitialState = {}; @@ -18,10 +21,10 @@ export const vocabularySlice = createSlice({ reducers: {}, }); -export const vocabularyApi = createApi({ - reducerPath: 'vocabularyAPI', +export const terminologyApi = createApi({ + reducerPath: 'terminologyApi', baseQuery: getTerminologyApiBaseQuery(), - tagTypes: ['Vocabulary'], + tagTypes: ['Terminology'], endpoints: (builder) => ({ getCollections: builder.query({ query: (terminologyId) => ({ @@ -30,13 +33,13 @@ export const vocabularyApi = createApi({ }), }), getConceptResult: builder.query< - VocabularyConcepts, + SearchResponse, { urlState: UrlState; id: string; language: string } >({ query: (value) => ({ - url: '/searchConcept', - method: 'POST', - data: { + url: '/frontend/search-concepts', + method: 'GET', + params: { highlight: true, pageFrom: Math.max(0, (value.urlState.page - 1) * 50), pageSize: 50, @@ -48,13 +51,13 @@ export const vocabularyApi = createApi({ ? value.language : 'fi', status: value.urlState.status.map((s) => s.toUpperCase()), - terminologyId: [value.id], + namespace: `https://iri.suomi.fi/terminology/${value.id}/`, }, }), }), - getVocabulary: builder.query({ + getTerminology: builder.query({ query: (value) => ({ - url: `/vocabulary?graphId=${value.id}`, + url: `/terminology/${value.id}`, method: 'GET', }), }), @@ -105,16 +108,16 @@ export const vocabularyApi = createApi({ export const { useGetCollectionsQuery, useGetConceptResultQuery, - useGetVocabularyQuery, + useGetTerminologyQuery, usePostNewVocabularyMutation, usePostCreateVersionMutation, useDeleteVocabularyMutation, useGetIfNamespaceInUseMutation, useGetVocabulariesQuery, util: { getRunningQueriesThunk }, -} = vocabularyApi; +} = terminologyApi; export default vocabularySlice.reducer; -export const { getVocabulary, getCollections, getConceptResult } = - vocabularyApi.endpoints; +export const { getTerminology, getCollections, getConceptResult } = + terminologyApi.endpoints; diff --git a/terminology-ui/src/common/interfaces/interfaces-v2.ts b/terminology-ui/src/common/interfaces/interfaces-v2.ts index 9a1a921d4..d21346c6a 100644 --- a/terminology-ui/src/common/interfaces/interfaces-v2.ts +++ b/terminology-ui/src/common/interfaces/interfaces-v2.ts @@ -1,7 +1,25 @@ +import { Group } from 'yti-common-ui/interfaces/group.interface'; +import { Organization } from 'yti-common-ui/interfaces/organization.interface'; import { Status } from 'yti-common-ui/interfaces/status.interface'; +export interface Terminology { + uri: string; + label: LocalizedValue; + description: LocalizedValue; + prefix: string; + type: TerminologyType; + status: Status; + languages: string[]; + contact: string; + groups: Group[]; + organizations: Organization[]; + created: string; + modified: string; + creator: { id: string; name: string }; + modifier: { id: string; name: string }; + origin: string; +} /* build fails for empty interfaces -export interface Terminology {} export interface TerminologyInfo {} diff --git a/terminology-ui/src/common/interfaces/status-counts.interface.ts b/terminology-ui/src/common/interfaces/status-counts.interface.ts index 7fc65cff1..b04fe13fe 100644 --- a/terminology-ui/src/common/interfaces/status-counts.interface.ts +++ b/terminology-ui/src/common/interfaces/status-counts.interface.ts @@ -1,8 +1,6 @@ export interface StatusCounts { - counts: { - concepts: StatusCountsObjects; - terms: StatusCountsObjects; - }; + concepts: StatusCountsObjects; + terms: StatusCountsObjects; } export interface StatusCountsObjects { diff --git a/terminology-ui/src/common/utils/get-store-data.test.tsx b/terminology-ui/src/common/utils/get-store-data.test.tsx index d8d572896..48d820c2d 100644 --- a/terminology-ui/src/common/utils/get-store-data.test.tsx +++ b/terminology-ui/src/common/utils/get-store-data.test.tsx @@ -1,3 +1,4 @@ +import { getTerminology } from '../components/vocabulary/vocabulary.slice'; import { getStoreData } from './get-store-data'; describe('page-head-utils', () => { @@ -6,7 +7,7 @@ describe('page-head-utils', () => { state: { vocabularyAPI: { queries: { - getVocabulary: { + getTerminology: { data: { test: '123', }, @@ -14,8 +15,8 @@ describe('page-head-utils', () => { }, }, }, - reduxKey: 'vocabularyAPI', - functionKey: 'getVocabulary', + reduxKey: 'terminologyAPI', + functionKey: 'getTerminology', }); expect(gotten).toStrictEqual({ test: '123' }); @@ -26,7 +27,7 @@ describe('page-head-utils', () => { state: { vocabularyAPI: { queries: { - getVocabulary: { + getTerminology: { data: { test: '123', }, @@ -35,7 +36,7 @@ describe('page-head-utils', () => { }, }, reduxKey: 'conceptAPI', - functionKey: 'getVocabulary', + functionKey: 'getTerminology', }); expect(gotten).toStrictEqual({}); @@ -46,7 +47,7 @@ describe('page-head-utils', () => { state: { vocabularyAPI: { queries: { - getVocabulary: { + getTerminology: { data: { test: '123', }, @@ -54,7 +55,7 @@ describe('page-head-utils', () => { }, }, }, - reduxKey: 'vocabularyAPI', + reduxKey: 'terminologyAPI', functionKey: 'getConcept', }); diff --git a/terminology-ui/src/common/utils/has-permission.tsx b/terminology-ui/src/common/utils/has-permission.tsx index 6d20eac19..749c8fdb6 100644 --- a/terminology-ui/src/common/utils/has-permission.tsx +++ b/terminology-ui/src/common/utils/has-permission.tsx @@ -7,7 +7,7 @@ import { setLogin, useGetAuthenticatedUserQuery, } from '../components/login/login.slice'; -import { Organization } from '../interfaces/organization.interface'; +import { Organization } from 'yti-common-ui/interfaces/organization.interface'; import { User } from 'yti-common-ui/interfaces/user.interface'; const actions = [ diff --git a/terminology-ui/src/modules/collection/index.tsx b/terminology-ui/src/modules/collection/index.tsx index a1d491fb8..330bdc4a0 100644 --- a/terminology-ui/src/modules/collection/index.tsx +++ b/terminology-ui/src/modules/collection/index.tsx @@ -30,14 +30,14 @@ import { } from './collection.styles'; import { setTitle } from '@app/common/components/title/title.slice'; import { useGetCollectionQuery } from '@app/common/components/collection/collection.slice'; -import { useGetVocabularyQuery } from '@app/common/components/vocabulary/vocabulary.slice'; -import { getProperty } from '@app/common/utils/get-property'; +import { useGetTerminologyQuery } from '@app/common/components/vocabulary/vocabulary.slice'; import { SubTitle, MainTitle, BadgeBar } from 'yti-common-ui/title-block'; import HasPermission from '@app/common/utils/has-permission'; import Link from 'next/link'; import RemovalModal from '@app/common/components/removal-modal'; import { getBlockData } from './utils'; import { useGetOrganizationsQuery } from '@app/common/components/terminology-search/terminology-search.slice'; +import { getLanguageVersion } from 'yti-common-ui/utils/get-language-version'; interface CollectionProps { terminologyId: string; @@ -53,7 +53,7 @@ export default function Collection({ const dispatch = useStoreDispatch(); const router = useRouter(); - const { data: terminology, error: terminologyError } = useGetVocabularyQuery( + const { data: terminology, error: terminologyError } = useGetTerminologyQuery( { id: terminologyId }, { skip: router.isFallback, @@ -104,7 +104,10 @@ export default function Collection({ {!terminologyError && ( - + {getLanguageVersion({ + data: terminology?.label, + lang: i18n.language, + })} )} @@ -145,7 +148,10 @@ export default function Collection({ {!terminologyError && ( - + {getLanguageVersion({ + data: terminology?.label, + lang: i18n.language, + })} )} - + {terminology?.organizations + .map((org) => + getLanguageVersion({ data: org?.label, lang: i18n.language }) + ) + .join(', ')} {t('heading')} - + {getLanguageVersion({ + data: terminology?.label, + lang: i18n.language, + })} {collection?.uri} @@ -193,7 +201,7 @@ export default function Collection({ {HasPermission({ actions: ['EDIT_COLLECTION', 'DELETE_COLLECTION'], - targetOrganization: terminology?.references.contributor, + targetOrganization: terminology?.organizations, }) && ( <> - {terminology?.references.contributor + {terminology?.organizations ?.filter( - (c) => - c && - c.properties.prefLabel && - !childOrganizations?.includes(c.id) + (o) => o && o.label && !childOrganizations?.includes(o.id) ) - .map((contributor) => ( -
  • - + .map((organization) => ( +
  • + {getLanguageVersion({ + data: organization?.label, + lang: i18n.language, + })}
  • ))}
    diff --git a/terminology-ui/src/modules/concept/index.tsx b/terminology-ui/src/modules/concept/index.tsx index e17634146..2a53500b6 100644 --- a/terminology-ui/src/modules/concept/index.tsx +++ b/terminology-ui/src/modules/concept/index.tsx @@ -18,7 +18,6 @@ import { import { Breadcrumb, BreadcrumbLink } from 'yti-common-ui/breadcrumb'; import FormattedDate from 'yti-common-ui/formatted-date'; import { useBreakpoints } from 'yti-common-ui/media-query'; -import PropertyValue from '@app/common/components/property-value'; import { getPropertyValue } from '@app/common/components/property-value/get-property-value'; import Separator from 'yti-common-ui/separator'; import DetailsExpander from './details-expander'; @@ -32,7 +31,7 @@ import { import { useStoreDispatch } from '@app/store'; import { useRouter } from 'next/router'; import { setTitle } from '@app/common/components/title/title.slice'; -import { useGetVocabularyQuery } from '@app/common/components/vocabulary/vocabulary.slice'; +import { useGetTerminologyQuery } from '@app/common/components/vocabulary/vocabulary.slice'; import { useGetConceptQuery } from '@app/common/components/concept/concept.slice'; import { getProperty } from '@app/common/utils/get-property'; import { MainTitle, BadgeBar } from 'yti-common-ui/title-block'; @@ -44,6 +43,7 @@ import RemovalModal from '@app/common/components/removal-modal'; import { getBlockData } from './utils'; import { StatusChip } from 'yti-common-ui/status-chip'; import { useGetOrganizationsQuery } from '@app/common/components/terminology-search/terminology-search.slice'; +import { getLanguageVersion } from 'yti-common-ui/utils/get-language-version'; export interface ConceptProps { terminologyId: string; @@ -55,12 +55,14 @@ export default function Concept({ terminologyId, conceptId }: ConceptProps) { const { breakpoint } = useBreakpoints(); const dispatch = useStoreDispatch(); const router = useRouter(); - const { data: terminology, error: terminologyError } = useGetVocabularyQuery({ - id: terminologyId, - }); + const { data: terminology, error: terminologyError } = useGetTerminologyQuery( + { + id: terminologyId, + } + ); const hasPermission = HasPermission({ actions: ['EDIT_CONCEPT', 'DELETE_CONCEPT'], - targetOrganization: terminology?.references.contributor, + targetOrganization: terminology?.organizations, }); const { data: concept, error: conceptError } = useGetConceptQuery({ terminologyId, @@ -95,9 +97,7 @@ export default function Concept({ terminologyId, conceptId }: ConceptProps) { const status = getPropertyValue({ property: concept?.properties.status }) || 'DRAFT'; - const email = getPropertyValue({ - property: terminology?.properties.contact, - }).trim(); + const email = terminology?.contact?.trim() ?? ''; useEffect(() => { if (concept) { @@ -111,7 +111,10 @@ export default function Concept({ terminologyId, conceptId }: ConceptProps) { {!terminologyError && ( - + {getLanguageVersion({ + data: terminology?.label, + lang: i18n.language, + })} )} @@ -152,7 +155,10 @@ export default function Concept({ terminologyId, conceptId }: ConceptProps) { {!terminologyError && ( - + {getLanguageVersion({ + data: terminology?.label, + lang: i18n.language, + })} )} {prefLabel} {t('heading')} - + {getLanguageVersion({ + data: terminology?.label, + lang: i18n.language, + })} {translateStatus(status, t)} @@ -244,19 +253,12 @@ export default function Concept({ terminologyId, conceptId }: ConceptProps) { id="organization" > - {terminology?.references.contributor + {terminology?.organizations ?.filter( - (c) => - c && - c.properties.prefLabel && - !childOrganizations?.includes(c.id) + (o) => o && o.label && !childOrganizations?.includes(o.id) ) - .map((contributor) => ( -
  • - -
  • + .map((organization) => ( +
  • {organization?.label}
  • ))}
    diff --git a/terminology-ui/src/modules/edit-collection/concept-picker/expander-concept-content.tsx b/terminology-ui/src/modules/edit-collection/concept-picker/expander-concept-content.tsx index 12c40390f..64f616850 100644 --- a/terminology-ui/src/modules/edit-collection/concept-picker/expander-concept-content.tsx +++ b/terminology-ui/src/modules/edit-collection/concept-picker/expander-concept-content.tsx @@ -2,24 +2,24 @@ import { BasicBlock } from 'yti-common-ui/block'; import { MultilingualPropertyBlock } from '@app/common/components/block'; import { useGetConceptQuery } from '@app/common/components/concept/concept.slice'; import FormattedDate from 'yti-common-ui/formatted-date'; -import PropertyValue from '@app/common/components/property-value'; import Separator from 'yti-common-ui/separator'; -import { useGetVocabularyQuery } from '@app/common/components/vocabulary/vocabulary.slice'; +import { useGetTerminologyQuery } from '@app/common/components/vocabulary/vocabulary.slice'; import { useTranslation } from 'next-i18next'; import { ExpanderContent } from 'suomifi-ui-components'; import { ExpanderConceptContent as ExpanderConceptContentType } from './concept-picker.types'; import { compareLocales } from '@app/common/utils/compare-locals'; +import { getLanguageVersion } from 'yti-common-ui/utils/get-language-version'; export function ExpanderConceptContent({ concept, terminologyId, }: ExpanderConceptContentType) { - const { t } = useTranslation('collection'); + const { t, i18n } = useTranslation('collection'); const { data } = useGetConceptQuery({ terminologyId: terminologyId, conceptId: concept.id, }); - const { data: terminologyData } = useGetVocabularyQuery({ + const { data: terminologyData } = useGetTerminologyQuery({ id: terminologyId, }); @@ -52,11 +52,10 @@ export function ExpanderConceptContent({ - + {getLanguageVersion({ + data: terminologyData?.label, + lang: i18n.language, + })} diff --git a/terminology-ui/src/modules/edit-collection/index.tsx b/terminology-ui/src/modules/edit-collection/index.tsx index 371966fe7..43f91b3b2 100644 --- a/terminology-ui/src/modules/edit-collection/index.tsx +++ b/terminology-ui/src/modules/edit-collection/index.tsx @@ -1,10 +1,8 @@ import { Breadcrumb, BreadcrumbLink } from 'yti-common-ui/breadcrumb'; import { useAddCollectionMutation } from '@app/common/components/modify/modify.slice'; -import PropertyValue from '@app/common/components/property-value'; import Separator from 'yti-common-ui/separator'; import { BadgeBar, MainTitle, SubTitle } from 'yti-common-ui/title-block'; -import { useGetVocabularyQuery } from '@app/common/components/vocabulary/vocabulary.slice'; -import { getProperty } from '@app/common/utils/get-property'; +import { useGetTerminologyQuery } from '@app/common/components/vocabulary/vocabulary.slice'; import { useTranslation } from 'next-i18next'; import { useRouter } from 'next/router'; import { useEffect, useState } from 'react'; @@ -37,16 +35,17 @@ import { useGetAuthenticatedUserQuery, } from '@app/common/components/login/login.slice'; import { compareLocales } from '@app/common/utils/compare-locals'; +import { getLanguageVersion } from 'yti-common-ui/utils/get-language-version'; export default function EditCollection({ terminologyId, collectionName, collectionInfo, }: EditCollectionProps) { - const { t } = useTranslation('collection'); + const { t, i18n } = useTranslation('collection'); const { isSmall } = useBreakpoints(); const router = useRouter(); - const { data: terminology } = useGetVocabularyQuery({ + const { data: terminology } = useGetTerminologyQuery({ id: terminologyId, }); const { data: collection } = useGetCollectionQuery( @@ -74,10 +73,7 @@ export default function EditCollection({ const [isCreating, setIsCreating] = useState(false); const languages = - terminology?.properties.language - ?.slice() - .sort((a, b) => compareLocales(a.value, b.value)) - .map(({ value }) => value) ?? []; + terminology?.languages?.slice().sort((a, b) => compareLocales(a, b)) ?? []; const [formData, setFormData] = useState( setInitialData(collection) @@ -188,7 +184,10 @@ export default function EditCollection({ {router.query.terminologyId && ( - + {getLanguageVersion({ + data: terminology?.label, + lang: i18n.language, + })} )} @@ -198,17 +197,19 @@ export default function EditCollection({ - + {terminology?.organizations + .map((org) => + getLanguageVersion({ data: org?.label, lang: i18n.language }) + ) + .join(', ')} {collectionName} {t('heading')} - + {getLanguageVersion({ + data: terminology?.label, + lang: i18n.language, + })} {t('new-collection-page-help')} diff --git a/terminology-ui/src/modules/edit-concept/generate-form-data-test-variables.tsx b/terminology-ui/src/modules/edit-concept/generate-form-data-test-variables.tsx index bf115e1fe..91bd99853 100644 --- a/terminology-ui/src/modules/edit-concept/generate-form-data-test-variables.tsx +++ b/terminology-ui/src/modules/edit-concept/generate-form-data-test-variables.tsx @@ -2417,7 +2417,7 @@ export const extensiveDataReturned = generateFormData( }, uri: 'uri.suomi.fi/terminology/sanasto/concept-1000', }, - [{ lang: 'fi', value: 'keskeneräinen sanasto', regex: '(?s)^.*$)' }] + { fi: 'keskeneräinen sanasto' } ); export const extensiveDataExpected = { diff --git a/terminology-ui/src/modules/edit-concept/generate-form-data.tsx b/terminology-ui/src/modules/edit-concept/generate-form-data.tsx index dd63a66c6..fcef8f39e 100644 --- a/terminology-ui/src/modules/edit-concept/generate-form-data.tsx +++ b/terminology-ui/src/modules/edit-concept/generate-form-data.tsx @@ -5,6 +5,7 @@ import { Property } from '@app/common/interfaces/termed-data-types.interface'; import getDiagramValues from '@app/common/utils/get-diagram-values'; import { v4 } from 'uuid'; import { ConceptTermType, EditConceptType } from './new-concept.types'; +import { LocalizedValue } from '@app/common/interfaces/interfaces-v2'; export default function generateFormData( preferredTerms: { @@ -13,7 +14,7 @@ export default function generateFormData( regex: string; }[], conceptData?: Concept, - terminologyLabel?: Property[] + terminologyLabel?: LocalizedValue ): EditConceptType { if (!conceptData) { return { @@ -145,10 +146,7 @@ export default function generateFormData( }) .reduce((l) => l) ?? {}, terminologyId: broad.type.graph.id, - terminologyLabel: - terminologyLabel - ?.map((l) => ({ [l.lang]: l.value })) - .reduce((l) => l) ?? {}, + terminologyLabel: terminologyLabel ?? {}, }; } }) ?? [], @@ -170,10 +168,7 @@ export default function generateFormData( }) .reduce((l) => l) ?? {}, terminologyId: part.type.graph.id, - terminologyLabel: - terminologyLabel - ?.map((l) => ({ [l.lang]: l.value })) - .reduce((l) => l) ?? {}, + terminologyLabel: terminologyLabel ?? {}, }; } }) ?? [], @@ -195,10 +190,7 @@ export default function generateFormData( }) .reduce((l) => l) ?? {}, terminologyId: part.type.graph.id, - terminologyLabel: - terminologyLabel - ?.map((l) => ({ [l.lang]: l.value })) - .reduce((l) => l) ?? {}, + terminologyLabel: terminologyLabel ?? {}, }; } }) ?? [], @@ -220,10 +212,7 @@ export default function generateFormData( }) .reduce((l) => l) ?? {}, terminologyId: narrow.type.graph.id, - terminologyLabel: - terminologyLabel - ?.map((l) => ({ [l.lang]: l.value })) - .reduce((l) => l) ?? {}, + terminologyLabel: terminologyLabel ?? {}, }; } }) ?? [], @@ -245,10 +234,7 @@ export default function generateFormData( }) .reduce((l) => l) ?? {}, terminologyId: r.type.graph.id, - terminologyLabel: - terminologyLabel - ?.map((l) => ({ [l.lang]: l.value })) - .reduce((l) => l) ?? {}, + terminologyLabel: terminologyLabel ?? {}, }; } }) ?? [], diff --git a/terminology-ui/src/modules/edit-concept/index.tsx b/terminology-ui/src/modules/edit-concept/index.tsx index b9820bd90..aa45865db 100644 --- a/terminology-ui/src/modules/edit-concept/index.tsx +++ b/terminology-ui/src/modules/edit-concept/index.tsx @@ -1,8 +1,7 @@ import { Breadcrumb, BreadcrumbLink } from 'yti-common-ui/breadcrumb'; import PropertyValue from '@app/common/components/property-value'; import { MainTitle, SubTitle, BadgeBar } from 'yti-common-ui/title-block'; -import { useGetVocabularyQuery } from '@app/common/components/vocabulary/vocabulary.slice'; -import { getProperty } from '@app/common/utils/get-property'; +import { useGetTerminologyQuery } from '@app/common/components/vocabulary/vocabulary.slice'; import { useTranslation } from 'next-i18next'; import { useRouter } from 'next/router'; import ConceptBasicInformation from './basic-information/concept-basic-information'; @@ -34,6 +33,7 @@ import { translateStatus } from '@app/common/utils/translation-helpers'; import { v4 } from 'uuid'; import { StatusChip } from 'yti-common-ui/status-chip'; import { compareLocales } from '@app/common/utils/compare-locals'; +import { getLanguageVersion } from 'yti-common-ui/utils/get-language-version'; interface EditConceptProps { terminologyId: string; @@ -44,13 +44,13 @@ export default function EditConcept({ terminologyId, conceptData, }: EditConceptProps) { - const { t } = useTranslation('concept'); + const { t, i18n } = useTranslation('concept'); const { isSmall } = useBreakpoints(); const router = useRouter(); const [addConcept, addConceptStatus] = useAddConceptMutation(); const [isCreating, setIsCreating] = useState(false); const user = useSelector(selectLogin()); - const { data: terminology } = useGetVocabularyQuery({ + const { data: terminology } = useGetTerminologyQuery({ id: terminologyId, }); const { data: authenticatedUser } = useGetAuthenticatedUserQuery(); @@ -58,10 +58,7 @@ export default function EditConcept({ useGetAuthenticatedUserMutMutation(); const [languages] = useState( - terminology?.properties.language - ?.slice() - .sort((a, b) => compareLocales(a.value, b.value)) - .map(({ value }) => value) ?? [] + terminology?.languages?.slice().sort((a, b) => compareLocales(a, b)) ?? [] ); const [preferredTerms] = useState< { @@ -73,11 +70,7 @@ export default function EditConcept({ const [postedData, setPostedData] = useState>(); const [formData, setFormData] = useState( - generateFormData( - preferredTerms, - conceptData, - terminology?.properties.prefLabel - ) + generateFormData(preferredTerms, conceptData, terminology?.label) ); const [errors, setErrors] = useState(validateForm(formData)); @@ -160,7 +153,10 @@ export default function EditConcept({ {router.query.terminologyId && ( - + {getLanguageVersion({ + data: terminology?.label, + lang: i18n.language, + })} )} @@ -200,7 +196,10 @@ export default function EditConcept({ {router.query.terminologyId && ( - + {getLanguageVersion({ + data: terminology?.label, + lang: i18n.language, + })} )} {!!preferredTerms?.length && ( @@ -212,19 +211,21 @@ export default function EditConcept({ - + {terminology?.organizations + .map((org) => + getLanguageVersion({ data: org?.label, lang: i18n.language }) + ) + .join(', ')} {t('heading')} - + {getLanguageVersion({ + data: terminology?.label, + lang: i18n.language, + })} {translateStatus(formData.basicInformation.status, t)} diff --git a/terminology-ui/src/modules/edit-vocabulary/generate-initial-data.ts b/terminology-ui/src/modules/edit-vocabulary/generate-initial-data.ts index a49647d14..3436ca9a1 100644 --- a/terminology-ui/src/modules/edit-vocabulary/generate-initial-data.ts +++ b/terminology-ui/src/modules/edit-vocabulary/generate-initial-data.ts @@ -1,45 +1,45 @@ -import { getPropertyValue } from '@app/common/components/property-value/get-property-value'; +import { + Terminology, + TerminologyType, +} from '@app/common/interfaces/interfaces-v2'; import { NewTerminologyInfo } from '@app/common/interfaces/new-terminology-info'; -import { VocabularyInfoDTO } from '@app/common/interfaces/vocabulary.interface'; import { LanguageBlockType } from 'yti-common-ui/form/language-selector'; +import { getLanguageVersion } from 'yti-common-ui/utils/get-language-version'; export default function generateInitialData( lang: string, - data?: VocabularyInfoDTO + data?: Terminology ): NewTerminologyInfo | undefined { if (!data) { return undefined; } const languages = - data.properties.language?.map((l) => { - const description = - data.properties.description?.find((d) => d.lang === l.value)?.value ?? - ''; - const title = - data.properties.prefLabel?.find((p) => p.lang === l.value)?.value ?? ''; + data.languages?.map((l) => { + const description = ''; + const title = ''; - const labelText = l.value; + const labelText = l; return { title, description, - uniqueItemId: l.value, + uniqueItemId: l, selected: true, labelText, } as LanguageBlockType; }) ?? []; const infoDomains = - data.references.inGroup?.map((group) => { - const label = getPropertyValue({ - property: group.properties.prefLabel, - language: lang, + data.groups?.map((group) => { + const label = getLanguageVersion({ + data: group.label, + lang, }); return { checked: false, - groupId: group.type.graph.id, + groupId: group.identifier, labelText: label, name: label, uniqueItemId: group.id, @@ -47,14 +47,14 @@ export default function generateInitialData( }) ?? []; const contributors = - data.references.contributor?.map((org) => { - const label = getPropertyValue({ - property: org.properties.prefLabel, - language: lang, + data.organizations?.map((org) => { + const label = getLanguageVersion({ + data: org.label, + lang, }); return { - organizationId: org.type.graph.id, + organizationId: org.id, labelText: label, name: label, uniqueItemId: org.id, @@ -71,13 +71,12 @@ export default function generateInitialData( .filter((p) => p)[0]; const obj: NewTerminologyInfo = { - contact: data.properties.contact?.[0].value ?? '', + contact: data.contact ?? '', languages: languages, infoDomains: infoDomains, prefix: [prefix ?? '', true], - status: data.properties.status?.[0].value ?? 'DRAFT', - type: - data.properties.terminologyType?.[0].value ?? 'TERMINOLOGICAL_VOCABULARY', + status: data.status ?? 'DRAFT', + type: data.type ?? TerminologyType.TERMINOLOGICAL_VOCABULARY, contributors: contributors, }; diff --git a/terminology-ui/src/modules/edit-vocabulary/index.tsx b/terminology-ui/src/modules/edit-vocabulary/index.tsx index b989430b4..df88968b4 100644 --- a/terminology-ui/src/modules/edit-vocabulary/index.tsx +++ b/terminology-ui/src/modules/edit-vocabulary/index.tsx @@ -5,10 +5,9 @@ import { useGetAuthenticatedUserQuery, } from '@app/common/components/login/login.slice'; import { useEditTerminologyMutation } from '@app/common/components/modify/modify.slice'; -import PropertyValue from '@app/common/components/property-value'; import SaveSpinner from 'yti-common-ui/save-spinner'; import Title from 'yti-common-ui/title'; -import { useGetVocabularyQuery } from '@app/common/components/vocabulary/vocabulary.slice'; +import { useGetTerminologyQuery } from '@app/common/components/vocabulary/vocabulary.slice'; import useConfirmBeforeLeavingPage from 'yti-common-ui/utils/hooks/use-confirm-before-leaving-page'; import { useTranslation } from 'next-i18next'; import { useRouter } from 'next/router'; @@ -32,7 +31,6 @@ import { ButtonBlock, } from './edit-vocabulary.styles'; import generateInitialData from './generate-initial-data'; -import { getPropertyValue } from '@app/common/components/property-value/get-property-value'; import { TitleType, TitleTypeAndStatusWrapper, @@ -42,6 +40,8 @@ import { translateStatus } from 'yti-common-ui/utils/translation-helpers'; import { useStoreDispatch } from '@app/store'; import { setTitle } from '@app/common/components/title/title.slice'; import { StatusChip } from 'yti-common-ui/status-chip'; +import { getLanguageVersion } from 'yti-common-ui/utils/get-language-version'; +import { TerminologyType } from '@app/common/interfaces/interfaces-v2'; interface EditVocabularyProps { terminologyId: string; @@ -51,7 +51,7 @@ export default function EditVocabulary({ terminologyId }: EditVocabularyProps) { const { t, i18n } = useTranslation('admin'); const router = useRouter(); const dispatch = useStoreDispatch(); - const { data: info, error: infoError } = useGetVocabularyQuery({ + const { data: info, error: infoError } = useGetTerminologyQuery({ id: terminologyId, }); const user = useSelector(selectLogin()); @@ -77,14 +77,14 @@ export default function EditVocabulary({ terminologyId }: EditVocabularyProps) { const newData = generateNewTerminology({ data: data, - code: info?.code, - createdBy: info?.createdBy, - createdDate: info?.createdDate, - id: info?.id, + code: info?.prefix, + createdBy: '', + createdDate: '', + id: info?.prefix, lastModifiedBy: `${user.firstName} ${user.lastName}`, terminologyId: terminologyId, uri: info?.uri, - origin: info?.properties.origin?.[0].value, + origin: '', }); if (!newData) { @@ -108,13 +108,13 @@ export default function EditVocabulary({ terminologyId }: EditVocabularyProps) { useEffect(() => { dispatch( setTitle( - getPropertyValue({ - property: info?.properties.prefLabel, - language: i18n.language, + getLanguageVersion({ + data: info?.label, + lang: i18n.language, }) ) ); - }, [dispatch, info?.properties.prefLabel, i18n.language]); + }, [dispatch, info, i18n.language]); if (infoError || !info) { return ( @@ -132,33 +132,26 @@ export default function EditVocabulary({ terminologyId }: EditVocabularyProps) { <> - + {getLanguageVersion({ data: info?.label, lang: i18n.language })} <TitleType> {translateTerminologyType( - info?.properties.terminologyType?.[0].value ?? - 'TERMINOLOGICAL_VOCABULARY', + info?.type ?? TerminologyType.TERMINOLOGICAL_VOCABULARY, t )} </TitleType>{' '} · - <StatusChip - status={info?.properties.status?.[0].value ?? 'DRAFT'} - id="status-chip" - > - {translateStatus( - info?.properties.status?.[0].value ?? 'DRAFT', - t - )} + <StatusChip status={info?.status ?? 'DRAFT'} id="status-chip"> + {translateStatus(info?.status ?? 'DRAFT', t)} </StatusChip> </TitleTypeAndStatusWrapper> } diff --git a/terminology-ui/src/modules/vocabulary/index.tsx b/terminology-ui/src/modules/vocabulary/index.tsx index dc5ef3c3f..b1b1dc832 100644 --- a/terminology-ui/src/modules/vocabulary/index.tsx +++ b/terminology-ui/src/modules/vocabulary/index.tsx @@ -2,7 +2,7 @@ import { useEffect, useMemo, useState } from 'react'; import { useGetCollectionsQuery, useGetConceptResultQuery, - useGetVocabularyQuery, + useGetTerminologyQuery, } from '@app/common/components/vocabulary/vocabulary.slice'; import { default as SearchResults, @@ -26,7 +26,6 @@ import { Text, } from 'suomifi-ui-components'; import { Breadcrumb, BreadcrumbLink } from 'yti-common-ui/breadcrumb'; -import PropertyValue from '@app/common/components/property-value'; import { useGetVocabularyCountQuery } from '@app/common/components/counts/counts.slice'; import { TerminologyListFilter } from './terminology-list-filter'; import useUrlState from '@app/common/utils/hooks/use-url-state'; @@ -52,6 +51,8 @@ import { useStoreDispatch } from '@app/store'; import { setTitle } from '@app/common/components/title/title.slice'; import { StatusChip } from 'yti-common-ui/status-chip'; import { useGetOrganizationsQuery } from '@app/common/components/terminology-search/terminology-search.slice'; +import { getLanguageVersion } from 'yti-common-ui/utils/get-language-version'; +import { TerminologyType } from '@app/common/interfaces/interfaces-v2'; const NewConceptModal = dynamic( () => import('@app/common/components/new-concept-modal') @@ -84,7 +85,7 @@ export default function Vocabulary({ id }: VocabularyProps) { urlState, language: i18n.language, }); - const { data: info, error: infoError } = useGetVocabularyQuery({ + const { data: info, error: infoError } = useGetTerminologyQuery({ id, }); const { data: counts } = useGetVocabularyCountQuery(id); @@ -105,7 +106,7 @@ export default function Vocabulary({ id }: VocabularyProps) { return []; } - return conceptsData.concepts.map((concept) => ({ + return conceptsData.responseObjects.map((concept) => ({ id: concept.id, description: getPrefLabel({ @@ -117,7 +118,7 @@ export default function Vocabulary({ id }: VocabularyProps) { prefLabels: concept.label, lang: urlState.lang !== '' ? urlState.lang : i18n.language, }), - titleLink: `${id}/concept/${concept.id}`, + titleLink: `${id}/concept/${concept.identifier}`, type: t('vocabulary-info-concept'), })); }, [conceptsData, t, i18n.language, id, urlState.lang]); @@ -169,14 +170,9 @@ export default function Vocabulary({ id }: VocabularyProps) { useEffect(() => { dispatch( - setTitle( - getPropertyValue({ - property: info?.properties.prefLabel, - language: i18n.language, - }) - ) + setTitle(getLanguageVersion({ data: info?.label, lang: i18n.language })) ); - }, [dispatch, info?.properties.prefLabel, i18n.language]); + }, [dispatch, info, i18n.language]); if (infoError) { return ( @@ -209,35 +205,31 @@ export default function Vocabulary({ id }: VocabularyProps) { <> <Breadcrumb> <BreadcrumbLink url={`/terminology/${id}`} current> - <PropertyValue property={info?.properties.prefLabel} /> + {getLanguageVersion({ data: info?.label, lang: i18n.language })} </BreadcrumbLink> </Breadcrumb> <main id="main"> <Title - title={getPropertyValue({ - property: info?.properties.prefLabel, - language: i18n.language, + title={getLanguageVersion({ + data: info?.label, + lang: i18n.language, })} extra={ <> <TitleTypeAndStatusWrapper> <TitleType> {translateTerminologyType( - info?.properties.terminologyType?.[0].value ?? - 'TERMINOLOGICAL_VOCABULARY', + info?.type ?? TerminologyType.TERMINOLOGICAL_VOCABULARY, t )} </TitleType>{' '} · <StatusChip - status={info?.properties.status?.[0].value ?? 'DRAFT'} + status={info?.status.toString() ?? 'DRAFT'} id="status-chip" > - {translateStatus( - info?.properties.status?.[0].value ?? 'DRAFT', - t - )} + {translateStatus(info?.status ?? 'DRAFT', t)} </StatusChip> </TitleTypeAndStatusWrapper> <InfoExpander @@ -251,7 +243,7 @@ export default function Vocabulary({ id }: VocabularyProps) { {!isSmall ? ( <TerminologyListFilter counts={counts} - languages={info?.properties.language} + languages={info?.languages} /> ) : ( <Modal @@ -267,7 +259,7 @@ export default function Vocabulary({ id }: VocabularyProps) { onModalClose={() => setShowModal(false)} resultCount={conceptsData?.totalHitCount} counts={counts} - languages={info?.properties.language} + languages={info?.languages} /> </ModalContent> </Modal> @@ -281,14 +273,12 @@ export default function Vocabulary({ id }: VocabularyProps) { </Heading> {HasPermission({ actions: 'CREATE_CONCEPT', - targetOrganization: info?.references.contributor, + targetOrganization: info?.organizations, }) && ( <> <NewConceptModal terminologyId={id} - languages={ - info?.properties.language?.map(({ value }) => value) ?? [] - } + languages={info?.languages ?? []} /> <ConceptImportModal refetch={refetchConcepts} diff --git a/terminology-ui/src/modules/vocabulary/terminology-list-filter.tsx b/terminology-ui/src/modules/vocabulary/terminology-list-filter.tsx index 5979d2d1b..cc18aa4b4 100644 --- a/terminology-ui/src/modules/vocabulary/terminology-list-filter.tsx +++ b/terminology-ui/src/modules/vocabulary/terminology-list-filter.tsx @@ -17,7 +17,7 @@ export interface TerminologyListFilterProps { onModalClose?: () => void; resultCount?: number; counts?: Counts; - languages?: Property[]; + languages?: string[]; } export function TerminologyListFilter({ @@ -47,10 +47,10 @@ export function TerminologyListFilter({ languages={ languages ?.slice() - ?.sort((a, b) => compareLocales(a.value, b.value)) + ?.sort((a, b) => compareLocales(a, b)) ?.map((lang) => ({ - labelText: lang.value, - uniqueItemId: lang.value, + labelText: lang, + uniqueItemId: lang, })) ?? [] } /> @@ -60,8 +60,8 @@ export function TerminologyListFilter({ title={t('vocabulary-filter-show-only')} isModal={isModal} counts={{ - concepts: counts?.counts.categories.Concept, - collections: counts?.counts.categories.Collection, + concepts: counts?.counts.categories?.Concept, + collections: counts?.counts.categories?.Collection, }} /> {shouldRenderStatusFilter && ( diff --git a/terminology-ui/src/pages/terminology/[terminologyId].tsx b/terminology-ui/src/pages/terminology/[terminologyId].tsx index 80089ee6a..77c9aee13 100644 --- a/terminology-ui/src/pages/terminology/[terminologyId].tsx +++ b/terminology-ui/src/pages/terminology/[terminologyId].tsx @@ -10,7 +10,7 @@ import Vocabulary from '@app/modules/vocabulary'; import { getConceptResult, getRunningQueriesThunk, - getVocabulary, + getTerminology, } from '@app/common/components/vocabulary/vocabulary.slice'; import { initialUrlState } from '@app/common/utils/hooks/use-url-state'; import { @@ -21,9 +21,9 @@ import PageHead from 'yti-common-ui/page-head'; import { getPropertyValue } from '@app/common/components/property-value/get-property-value'; import { getStoreData } from '@app/common/utils/get-store-data'; import { - getVocabularyCount, - getStatusCounts, getRunningQueriesThunk as countsGetRunningQueriesThunk, + getStatusCounts, + getVocabularyCount, } from '@app/common/components/counts/counts.slice'; import { wrapper } from '@app/store'; @@ -97,7 +97,7 @@ export const getServerSideProps = createCommonGetServerSideProps( urlState.type = Array.isArray(query.type) ? query.type[0] : query.type; } - store.dispatch(getVocabulary.initiate({ id })); + store.dispatch(getTerminology.initiate({ id })); store.dispatch( getConceptResult.initiate({ urlState: urlState, @@ -113,8 +113,8 @@ export const getServerSideProps = createCommonGetServerSideProps( const vocabularyData = getStoreData({ state: store.getState(), - reduxKey: 'vocabularyAPI', - functionKey: 'getVocabulary', + reduxKey: 'terminologyAPI', + functionKey: 'getTerminology', }); const title = getPropertyValue({ diff --git a/terminology-ui/src/pages/terminology/[terminologyId]/collection/[collectionId].tsx b/terminology-ui/src/pages/terminology/[terminologyId]/collection/[collectionId].tsx index 790926544..e448afd4c 100644 --- a/terminology-ui/src/pages/terminology/[terminologyId]/collection/[collectionId].tsx +++ b/terminology-ui/src/pages/terminology/[terminologyId]/collection/[collectionId].tsx @@ -13,7 +13,7 @@ import { getRunningQueriesThunk as getCollectionRunningQueriesThunk, } from '@app/common/components/collection/collection.slice'; import { - getVocabulary, + getTerminology, getRunningQueriesThunk as getVocabularyRunningQueriesThunk, } from '@app/common/components/vocabulary/vocabulary.slice'; import { @@ -76,7 +76,7 @@ export const getServerSideProps = createCommonGetServerSideProps( throw new Error('Invalid parameters for page'); } - store.dispatch(getVocabulary.initiate({ id: terminologyId })); + store.dispatch(getTerminology.initiate({ id: terminologyId })); store.dispatch(getCollection.initiate({ terminologyId, collectionId })); store.dispatch(getCollections.initiate(terminologyId)); @@ -85,8 +85,8 @@ export const getServerSideProps = createCommonGetServerSideProps( const vocabularyData = getStoreData({ state: store.getState(), - reduxKey: 'vocabularyAPI', - functionKey: 'getVocabulary', + reduxKey: 'terminologyAPI', + functionKey: 'getTerminology', }); const collectionData = getStoreData({ diff --git a/terminology-ui/src/pages/terminology/[terminologyId]/collection/[collectionId]/edit.tsx b/terminology-ui/src/pages/terminology/[terminologyId]/collection/[collectionId]/edit.tsx index 5267bd4a7..c9fe423c9 100644 --- a/terminology-ui/src/pages/terminology/[terminologyId]/collection/[collectionId]/edit.tsx +++ b/terminology-ui/src/pages/terminology/[terminologyId]/collection/[collectionId]/edit.tsx @@ -10,7 +10,7 @@ import PageHead from 'yti-common-ui/page-head'; import { getStoreData } from '@app/common/utils/get-store-data'; import { getPropertyValue } from '@app/common/components/property-value/get-property-value'; import { - getVocabulary, + getTerminology, getRunningQueriesThunk, } from '@app/common/components/vocabulary/vocabulary.slice'; import { @@ -70,7 +70,7 @@ export const getServerSideProps = createCommonGetServerSideProps( throw new Error('Invalid parameter for page'); } - store.dispatch(getVocabulary.initiate({ id: terminologyId })); + store.dispatch(getTerminology.initiate({ id: terminologyId })); store.dispatch( getCollection.initiate({ collectionId: collectionId, diff --git a/terminology-ui/src/pages/terminology/[terminologyId]/concept/[conceptId].tsx b/terminology-ui/src/pages/terminology/[terminologyId]/concept/[conceptId].tsx index eb227dbf0..29aa66fea 100644 --- a/terminology-ui/src/pages/terminology/[terminologyId]/concept/[conceptId].tsx +++ b/terminology-ui/src/pages/terminology/[terminologyId]/concept/[conceptId].tsx @@ -12,7 +12,7 @@ import { getRunningQueriesThunk as getConceptRunningQueriesThunk, } from '@app/common/components/concept/concept.slice'; import { - getVocabulary, + getTerminology, getRunningQueriesThunk as getVocabularyRunningQueriesThunk, } from '@app/common/components/vocabulary/vocabulary.slice'; import { @@ -76,7 +76,7 @@ export const getServerSideProps = createCommonGetServerSideProps( throw new Error('Invalid parameters for page'); } - store.dispatch(getVocabulary.initiate({ id: terminologyId })); + store.dispatch(getTerminology.initiate({ id: terminologyId })); store.dispatch(getConcept.initiate({ terminologyId, conceptId })); await Promise.all(store.dispatch(getVocabularyRunningQueriesThunk())); @@ -84,8 +84,8 @@ export const getServerSideProps = createCommonGetServerSideProps( const vocabularyData = getStoreData({ state: store.getState(), - reduxKey: 'vocabularyAPI', - functionKey: 'getVocabulary', + reduxKey: 'terminologyAPI', + functionKey: 'getTerminology', }); const conceptData = getStoreData({ diff --git a/terminology-ui/src/pages/terminology/[terminologyId]/concept/[conceptId]/edit.tsx b/terminology-ui/src/pages/terminology/[terminologyId]/concept/[conceptId]/edit.tsx index d843e732e..fe8d04cf4 100644 --- a/terminology-ui/src/pages/terminology/[terminologyId]/concept/[conceptId]/edit.tsx +++ b/terminology-ui/src/pages/terminology/[terminologyId]/concept/[conceptId]/edit.tsx @@ -12,7 +12,7 @@ import PageHead from 'yti-common-ui/page-head'; import { default as EditConceptModule } from '@app/modules/edit-concept'; import { useRouter } from 'next/router'; import { - getVocabulary, + getTerminology, getRunningQueriesThunk as getVocabularyRunningQueriesThunk, } from '@app/common/components/vocabulary/vocabulary.slice'; import { @@ -71,7 +71,7 @@ export const getServerSideProps = createCommonGetServerSideProps( throw new Error('Invalid parameters for page'); } - store.dispatch(getVocabulary.initiate({ id: terminologyId })); + store.dispatch(getTerminology.initiate({ id: terminologyId })); store.dispatch(getConcept.initiate({ terminologyId, conceptId })); await Promise.all(store.dispatch(getVocabularyRunningQueriesThunk())); diff --git a/terminology-ui/src/pages/terminology/[terminologyId]/edit.tsx b/terminology-ui/src/pages/terminology/[terminologyId]/edit.tsx index 408d1a1a9..7839cb3d2 100644 --- a/terminology-ui/src/pages/terminology/[terminologyId]/edit.tsx +++ b/terminology-ui/src/pages/terminology/[terminologyId]/edit.tsx @@ -9,7 +9,7 @@ import { getRunningQueriesThunk as getTermSearchRunningQueriesThunk, } from '@app/common/components/terminology-search/terminology-search.slice'; import { - getVocabulary, + getTerminology, getRunningQueriesThunk, } from '@app/common/components/vocabulary/vocabulary.slice'; import { @@ -61,7 +61,7 @@ export const getServerSideProps = createCommonGetServerSideProps( throw new Error('Invalid parameter for page'); } - store.dispatch(getVocabulary.initiate({ id: terminologyId })); + store.dispatch(getTerminology.initiate({ id: terminologyId })); store.dispatch( getOrganizations.initiate({ language: locale ?? 'fi', diff --git a/terminology-ui/src/pages/terminology/[terminologyId]/new-collection.tsx b/terminology-ui/src/pages/terminology/[terminologyId]/new-collection.tsx index 21905f9fe..9b4de4416 100644 --- a/terminology-ui/src/pages/terminology/[terminologyId]/new-collection.tsx +++ b/terminology-ui/src/pages/terminology/[terminologyId]/new-collection.tsx @@ -12,7 +12,7 @@ import { } from 'yti-common-ui/common-context-provider'; import PageHead from 'yti-common-ui/page-head'; import { - getVocabulary, + getTerminology, getRunningQueriesThunk, } from '@app/common/components/vocabulary/vocabulary.slice'; import { wrapper } from '@app/store'; @@ -55,7 +55,7 @@ export const getServerSideProps = createCommonGetServerSideProps( throw new Error('Invalid parameter for page'); } - store.dispatch(getVocabulary.initiate({ id: terminologyId })); + store.dispatch(getTerminology.initiate({ id: terminologyId })); await Promise.all(store.dispatch(getRunningQueriesThunk())); diff --git a/terminology-ui/src/pages/terminology/[terminologyId]/new-concept.tsx b/terminology-ui/src/pages/terminology/[terminologyId]/new-concept.tsx index ceee32854..ff0ac0df8 100644 --- a/terminology-ui/src/pages/terminology/[terminologyId]/new-concept.tsx +++ b/terminology-ui/src/pages/terminology/[terminologyId]/new-concept.tsx @@ -12,7 +12,7 @@ import { } from 'yti-common-ui/common-context-provider'; import PageHead from 'yti-common-ui/page-head'; import { - getVocabulary, + getTerminology, getRunningQueriesThunk, } from '@app/common/components/vocabulary/vocabulary.slice'; import { wrapper } from '@app/store'; @@ -53,7 +53,7 @@ export const getServerSideProps = createCommonGetServerSideProps( throw new Error('Invalid parameter for page'); } - store.dispatch(getVocabulary.initiate({ id: terminologyId })); + store.dispatch(getTerminology.initiate({ id: terminologyId })); await Promise.all(store.dispatch(getRunningQueriesThunk())); diff --git a/terminology-ui/src/store/api-base-query.test.ts b/terminology-ui/src/store/api-base-query.test.ts index 7faa9bb0a..455504fd1 100644 --- a/terminology-ui/src/store/api-base-query.test.ts +++ b/terminology-ui/src/store/api-base-query.test.ts @@ -6,7 +6,7 @@ import { } from '@app/common/utils/create-getserversideprops'; import { getRunningQueriesThunk, - getVocabulary, + getTerminology, } from '@app/common/components/vocabulary/vocabulary.slice'; import MockAdapter from 'axios-mock-adapter'; import axios from 'axios'; @@ -54,12 +54,12 @@ describe('axios base query', () => { req.session.save(); // initiate API call - store.dispatch(getVocabulary.initiate({ id: vocabularyId })); + store.dispatch(getTerminology.initiate({ id: vocabularyId })); await Promise.all(store.dispatch(getRunningQueriesThunk())); // get the result from the API call const data = store.getState().vocabularyAPI.queries[ - 'getVocabulary({"id":"42"})' + 'getTerminology({"id":"42"})' ]?.data as string; return { diff --git a/terminology-ui/src/store/index.tsx b/terminology-ui/src/store/index.tsx index 2d2278646..607bc5171 100644 --- a/terminology-ui/src/store/index.tsx +++ b/terminology-ui/src/store/index.tsx @@ -10,7 +10,7 @@ import { terminologySearchApi, } from '@app/common/components/terminology-search/terminology-search.slice'; import { - vocabularyApi, + terminologyApi, vocabularySlice, } from '@app/common/components/vocabulary/vocabulary.slice'; import { conceptApi } from '@app/common/components/concept/concept.slice'; @@ -38,7 +38,7 @@ const reducers = { [terminologySearchSlice.name]: terminologySearchSlice.reducer, [terminologySearchApi.reducerPath]: terminologySearchApi.reducer, [vocabularySlice.name]: vocabularySlice.reducer, - [vocabularyApi.reducerPath]: vocabularyApi.reducer, + [terminologyApi.reducerPath]: terminologyApi.reducer, [conceptApi.reducerPath]: conceptApi.reducer, [collectionApi.reducerPath]: collectionApi.reducer, [countsApi.reducerPath]: countsApi.reducer, @@ -69,7 +69,7 @@ export const makeStore: MakeStore<any> = ({ middleware: (getDefaultMiddleware) => [ ...getDefaultMiddleware({ thunk: { extraArgument: context } }), terminologySearchApi.middleware, - vocabularyApi.middleware, + terminologyApi.middleware, conceptApi.middleware, collectionApi.middleware, countsApi.middleware, From c1270ff699869c8d4a3f1d49c8135b0d2eef75c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kalle=20Kytt=C3=A4l=C3=A4?= <kalle.kyttala@rebase.fi> Date: Mon, 26 Aug 2024 14:27:16 +0300 Subject: [PATCH 03/35] concept page api v2 --- common-ui/components/block/index.ts | 2 + .../block/multilingual-block-list.tsx | 41 ++++++ .../components/block/multilingual-block.tsx | 21 +-- .../src/modules/model/model-info-view.tsx | 22 +-- .../common/components/block/term-block.tsx | 9 +- .../components/concept/concept.slice.tsx | 6 +- .../info-dropdown/info-expander.tsx | 12 +- .../render-expander-content.tsx | 3 + .../common/components/removal-modal/index.tsx | 11 +- .../common/components/term-modal/index.tsx | 93 +++--------- .../components/term-modal/term-expander.tsx | 5 +- .../src/common/interfaces/interfaces-v2.ts | 92 +++++++++++- .../src/common/utils/compare-locals.ts | 13 +- .../administrative-details-expander.tsx | 81 +++++------ .../src/modules/concept/concept-sidebar.tsx | 136 ++++++++---------- .../src/modules/concept/details-expander.tsx | 22 ++- .../concept/diagrams-and-sources-expander.tsx | 66 +++------ .../modules/concept/get-reference-values.tsx | 76 ---------- terminology-ui/src/modules/concept/index.tsx | 77 +++++----- .../concept/other-details-expander.tsx | 44 ++---- terminology-ui/src/modules/concept/utils.tsx | 46 +++--- .../expander-concept-content.tsx | 2 +- .../[terminologyId]/concept/[conceptId].tsx | 15 +- 23 files changed, 402 insertions(+), 493 deletions(-) create mode 100644 common-ui/components/block/multilingual-block-list.tsx delete mode 100644 terminology-ui/src/modules/concept/get-reference-values.tsx diff --git a/common-ui/components/block/index.ts b/common-ui/components/block/index.ts index 5d22d08ed..ae42c450e 100644 --- a/common-ui/components/block/index.ts +++ b/common-ui/components/block/index.ts @@ -1,5 +1,6 @@ import BasicBlock from './basic-block'; import MultilingualBlock from './multilingual-block'; +import MultilingualBlockList from './multilingual-block-list'; import { BasicBlockWrapper, BasicBlockExtraWrapper, @@ -9,6 +10,7 @@ import { export { BasicBlock, MultilingualBlock, + MultilingualBlockList, BasicBlockWrapper, BasicBlockExtraWrapper, List, diff --git a/common-ui/components/block/multilingual-block-list.tsx b/common-ui/components/block/multilingual-block-list.tsx new file mode 100644 index 000000000..e991d64b5 --- /dev/null +++ b/common-ui/components/block/multilingual-block-list.tsx @@ -0,0 +1,41 @@ +import { + MultilingualBlockItem, + MultilingualBlockWrapper, +} from './multilingual-block.styles'; +import { v4 } from 'uuid'; +import { maxBy } from 'lodash'; +import SanitizedTextContent from '../../components/sanitized-text-content'; + +interface MultilingualBlockProps { + data?: { + language: string; + value: string; + }[]; + renderHtml?: boolean; +} + +export default function MultilingualBlockList({ + data, + renderHtml, +}: MultilingualBlockProps) { + if (!data) { + return <></>; + } + + const id = v4().slice(0, 8); + const maxSize = + maxBy(data, (item) => item.language.length)?.language.length ?? 0; + + return ( + <MultilingualBlockWrapper $maxSize={maxSize}> + {data.map((d, idx) => ( + <MultilingualBlockItem + key={`multilingual-block-${id}-item-${idx}`} + lang={d.language} + > + {renderHtml ? <SanitizedTextContent text={d.value} /> : d.value} + </MultilingualBlockItem> + ))} + </MultilingualBlockWrapper> + ); +} diff --git a/common-ui/components/block/multilingual-block.tsx b/common-ui/components/block/multilingual-block.tsx index 56ce9d142..cf878d5a4 100644 --- a/common-ui/components/block/multilingual-block.tsx +++ b/common-ui/components/block/multilingual-block.tsx @@ -4,30 +4,31 @@ import { } from './multilingual-block.styles'; import { v4 } from 'uuid'; import { maxBy } from 'lodash'; +import SanitizedTextContent from '../../components/sanitized-text-content'; interface MultilingualBlockProps { - data?: { - lang: string; - value: string | JSX.Element; - }[]; + data: { [key: string]: string }; + renderHtml?: boolean; } -export default function MultilingualBlock({ data }: MultilingualBlockProps) { +export default function MultilingualBlock({ + data, + renderHtml, +}: MultilingualBlockProps) { if (!data) { return <></>; } - const id = v4().slice(0, 8); - const maxSize = maxBy(data, (item) => item.lang.length)?.lang.length ?? 0; + const maxSize = maxBy(Object.keys(data), (key) => key.length)?.length ?? 0; return ( <MultilingualBlockWrapper $maxSize={maxSize}> - {data.map((d, idx) => ( + {Object.keys(data).map((key, idx) => ( <MultilingualBlockItem key={`multilingual-block-${id}-item-${idx}`} - lang={d.lang} + lang={key} > - {d.value} + {renderHtml ? <SanitizedTextContent text={data[key]} /> : data[key]} </MultilingualBlockItem> ))} </MultilingualBlockWrapper> diff --git a/datamodel-ui/src/modules/model/model-info-view.tsx b/datamodel-ui/src/modules/model/model-info-view.tsx index 3e1e21f90..af6a68bab 100644 --- a/datamodel-ui/src/modules/model/model-info-view.tsx +++ b/datamodel-ui/src/modules/model/model-info-view.tsx @@ -296,20 +296,24 @@ export default function ModelInfoView({ <DrawerContent height={headerHeight}> <BasicBlock title={t('name')}> <MultilingualBlock - data={Object.entries(modelInfo.label) - .sort((a, b) => compareLocales(a[0], b[0])) - .map((l) => ({ lang: l[0], value: l[1] }))} + data={Object.keys(modelInfo.label) + .sort((a, b) => compareLocales(a, b)) + .reduce((result, key) => { + result[key] = modelInfo.label[key]; + return result; + }, {} as { [key: string]: string })} /> </BasicBlock> <BasicBlock title={t('description')}> {Object.keys(modelInfo.description).length > 0 ? ( <MultilingualBlock - data={Object.entries(modelInfo.description) - .sort((a, b) => compareLocales(a[0], b[0])) - .map((d) => ({ - lang: d[0], - value: <SanitizedTextContent text={d[1]} />, - }))} + renderHtml={true} + data={Object.keys(modelInfo.description) + .sort((a, b) => compareLocales(a, b)) + .reduce((result, key) => { + result[key] = modelInfo.description[key]; + return result; + }, {} as { [key: string]: string })} /> ) : ( t('not-added') diff --git a/terminology-ui/src/common/components/block/term-block.tsx b/terminology-ui/src/common/components/block/term-block.tsx index 205b686e1..d8bb9fbad 100644 --- a/terminology-ui/src/common/components/block/term-block.tsx +++ b/terminology-ui/src/common/components/block/term-block.tsx @@ -1,12 +1,12 @@ import { useTranslation } from 'next-i18next'; import React from 'react'; -import { Term } from '@app/common/interfaces/term.interface'; import TermModal from '@app/common/components/term-modal'; import MultilingualBlock, { MultilingualBlockItemMapper, } from './multilingual-block'; import { TermWrapper } from './term-block.styles'; import { translateStatus } from '@app/common/utils/translation-helpers'; +import { Term } from '@app/common/interfaces/interfaces-v2'; export interface TermBlockProps { title: React.ReactNode; @@ -27,15 +27,14 @@ export default function TermBlock({ term: Term; type: string; }> = ({ term, type }) => ({ - language: term.properties.prefLabel?.[0].lang ?? '', + language: term.language, content: ( <TermWrapper> - <span lang={term.properties.prefLabel?.[0].lang}> + <span lang={term.language}> <TermModal data={{ term: term, type: type }} /> </span> <span> - {type},{' '} - {translateStatus(term.properties.status?.[0].value ?? 'DRAFT', t)} + {type}, {translateStatus(term.status ?? 'DRAFT', t)} </span> </TermWrapper> ), diff --git a/terminology-ui/src/common/components/concept/concept.slice.tsx b/terminology-ui/src/common/components/concept/concept.slice.tsx index 9d6ca8914..c0e1a6dcb 100644 --- a/terminology-ui/src/common/components/concept/concept.slice.tsx +++ b/terminology-ui/src/common/components/concept/concept.slice.tsx @@ -1,7 +1,7 @@ import { createApi } from '@reduxjs/toolkit/query/react'; -import { Concept } from '@app/common/interfaces/concept.interface'; import { Concepts } from '@app/common/interfaces/concepts.interface'; import { getTerminologyApiBaseQuery } from '@app/store/api-base-query'; +import { ConceptInfo } from '@app/common/interfaces/interfaces-v2'; export const conceptApi = createApi({ reducerPath: 'conceptAPI', @@ -9,11 +9,11 @@ export const conceptApi = createApi({ tagTypes: ['Concept'], endpoints: (builder) => ({ getConcept: builder.query< - Concept, + ConceptInfo, { terminologyId: string; conceptId: string } >({ query: ({ terminologyId, conceptId }) => ({ - url: `/concept?graphId=${terminologyId}&conceptId=${conceptId}`, + url: `/concept/${terminologyId}/${conceptId}`, method: 'GET', }), }), diff --git a/terminology-ui/src/common/components/info-dropdown/info-expander.tsx b/terminology-ui/src/common/components/info-dropdown/info-expander.tsx index 561b82233..2974c6084 100644 --- a/terminology-ui/src/common/components/info-dropdown/info-expander.tsx +++ b/terminology-ui/src/common/components/info-dropdown/info-expander.tsx @@ -83,20 +83,12 @@ export default function InfoExpander({ </ExpanderTitleButton> <ExpanderContent> <BasicBlock title={t('vocabulary-info-name')}> - <MultilingualBlock - data={Object.entries(data?.label) - .sort((a, b) => compareLocales(a[0], b[0])) - .map((l) => ({ lang: l[0], value: l[1] }))} - /> + <MultilingualBlock data={data.label} /> </BasicBlock> <BasicBlock title={t('vocabulary-info-description')}> {Object.keys(data.description).length > 0 ? ( - <MultilingualBlock - data={Object.entries(data?.description) - .sort((a, b) => compareLocales(a[0], b[0])) - .map((l) => ({ lang: l[0], value: l[1] }))} - /> + <MultilingualBlock data={data.description} /> ) : ( <></> )} diff --git a/terminology-ui/src/common/components/relational-information-block/render-expander-content.tsx b/terminology-ui/src/common/components/relational-information-block/render-expander-content.tsx index 0c4cfdf4d..1694873c9 100644 --- a/terminology-ui/src/common/components/relational-information-block/render-expander-content.tsx +++ b/terminology-ui/src/common/components/relational-information-block/render-expander-content.tsx @@ -49,6 +49,8 @@ function RenderExpanderContent({ return ( <ExpanderContent> + <></> + {/*TODO} <MultilingualPropertyBlock title={<h2>{t('preferred-terms')}</h2>} data={concept?.references.prefLabelXl?.[0].properties?.prefLabel} @@ -66,6 +68,7 @@ function RenderExpanderContent({ <FormattedDate date={concept?.lastModifiedDate} />,{' '} {concept?.lastModifiedBy} </BasicBlock> + {*/} </ExpanderContent> ); } diff --git a/terminology-ui/src/common/components/removal-modal/index.tsx b/terminology-ui/src/common/components/removal-modal/index.tsx index 2a7e19523..73cdabed4 100644 --- a/terminology-ui/src/common/components/removal-modal/index.tsx +++ b/terminology-ui/src/common/components/removal-modal/index.tsx @@ -1,5 +1,4 @@ import { Collection } from '@app/common/interfaces/collection.interface'; -import { Concept } from '@app/common/interfaces/concept.interface'; import { translateRemovalModalConfirmation, translateRemovalModalDescription, @@ -38,7 +37,7 @@ import { RemoveModalContent, } from './removal-modal.styles'; import { useGetAuthenticatedUserMutMutation } from '../login/login.slice'; -import { Terminology } from '@app/common/interfaces/interfaces-v2'; +import { Terminology, ConceptInfo } from '@app/common/interfaces/interfaces-v2'; interface RemovalModalProps { nonDescriptive?: boolean; @@ -49,7 +48,7 @@ interface RemovalModalProps { } | { type: 'concept'; - data?: Concept; + data?: ConceptInfo; } | { type: 'collection'; @@ -82,13 +81,15 @@ export default function RemovalModal({ } if (removalData.type === 'concept' && removalData.data) { - const data = generateConceptData(removalData.data); + /* TODO + const data = generateConceptData(removalData); if (data.length < 2) { return null; } deleteTarget(data); + */ } if (removalData.type === 'collection' && removalData.data) { @@ -110,7 +111,7 @@ export default function RemovalModal({ } if (removalData.data) { - router.push(`/terminology/${removalData.data.type.graph.id}`); + router.push('/terminology/id'); // TODO ${removalData.data.type.graph.id}`); return; } }; diff --git a/terminology-ui/src/common/components/term-modal/index.tsx b/terminology-ui/src/common/components/term-modal/index.tsx index 6c90d7756..5258cf86a 100644 --- a/terminology-ui/src/common/components/term-modal/index.tsx +++ b/terminology-ui/src/common/components/term-modal/index.tsx @@ -8,11 +8,7 @@ import { ModalFooter, ModalTitle, } from 'suomifi-ui-components'; -import { Term } from '@app/common/interfaces/term.interface'; -import { Property } from '@app/common/interfaces/termed-data-types.interface'; import { selectLogin } from '@app/common/components/login/login.slice'; -import PropertyValue from '@app/common/components/property-value'; -import { getPropertyValue } from '@app/common/components/property-value/get-property-value'; import TermExpander from './term-expander'; import { TermHeading, @@ -26,9 +22,9 @@ import { translateStatus, translateTermConjugation, translateTermFamily, - translateTermStyle, translateWordClass, } from '@app/common/utils/translation-helpers'; +import { Term } from '@app/common/interfaces/interfaces-v2'; interface TermModalProps { data?: { term: Term; type: string }; @@ -50,15 +46,7 @@ export default function TermModal({ data }: TermModalProps) { variant="secondaryNoBorder" onClick={() => setVisible(true)} > - {/* - Note: Preferencing upper solution instead of <PropertyValue /> - because term should only have one prefLabel. If prefLabel is - in English and the language used is Finnish the button won't - be rendered. Same solution in <ModalTitle /> below. - */} - {data.term.properties.prefLabel?.[0].value ?? ( - <PropertyValue property={data.term.properties.prefLabel} /> - )} + {data.term.label} </TermModalButton> <Modal appElementId="__next" @@ -67,63 +55,38 @@ export default function TermModal({ data }: TermModalProps) { variant={isSmall ? 'smallScreen' : 'default'} > <ModalContent> - <ModalTitle> - {data.term.properties.prefLabel?.[0].value ?? ( - <PropertyValue property={data.term.properties.prefLabel} /> - )} - </ModalTitle> - + <ModalTitle>{data.term.label}</ModalTitle> {renderInfo(t('term-modal-type'), data.type)} {renderInfoChip( t('term-modal-status'), - data.term.properties.status, + data.term.status ?? 'DRAFT', 'DRAFT' )} {renderInfo( t('term-modal-homograph-number'), - data.term.properties.termHomographNumber?.[0].value - )} - {renderInfo( - t('term-modal-info'), - data.term.properties.termInfo?.[0].value - )} - {renderInfo( - t('term-modal-scope'), - data.term.properties.scope?.[0].value - )} - {renderInfo( - t('term-modal-equivalency'), - data.term.properties.termEquivalency?.[0].value - )} - {renderInfo( - t('term-modal-source'), - data.term.properties.source?.map((source) => source.value) + data.term.homographNumber )} + {renderInfo(t('term-modal-info'), data.term.termInfo)} + {renderInfo(t('term-modal-scope'), data.term.scope)} + {renderInfo(t('term-modal-equivalency'), data.term.termEquivalency)} + {renderInfo(t('term-modal-source'), data.term.sources)} <TermExpander title={t('term-modal-organizational-information')} data={[ { subtitle: t('term-modal-change-note'), - value: data.term.properties.changeNote?.[0].value, + value: data.term.changeNote, }, { subtitle: t('term-modal-history-note'), - value: data.term.properties.historyNote?.[0].value, + value: data.term.historyNote, }, { subtitle: t('term-modal-editorial-note'), - value: data.term.properties.editorialNote?.map( - (note) => note.value - ), + value: data.term.editorialNotes, checkCondition: !user.anonymous, }, - { - subtitle: t('term-modal-draft-note'), - value: data.term.properties.draftComment?.[0].value, - checkCondition: - data.term.properties.status?.[0].value === 'DRAFT', - }, ]} /> <TermExpander @@ -131,31 +94,22 @@ export default function TermModal({ data }: TermModalProps) { data={[ { subtitle: t('term-modal-style'), - value: translateTermStyle( - data.term.properties.termStyle?.[0].value ?? '', - t - ), + value: data.term.termStyle, }, { subtitle: t('term-modal-family'), - value: translateTermFamily( - data.term.properties.termFamily?.[0].value ?? '', - t - ), + value: translateTermFamily(data.term.termFamily ?? '', t), }, { subtitle: t('term-modal-conjugation'), value: translateTermConjugation( - data.term.properties.termConjugation?.[0].value ?? '', + data.term.termConjugation ?? '', t ), }, { subtitle: t('term-modal-word-class'), - value: translateWordClass( - data.term.properties.wordClass?.[0].value ?? '', - t - ), + value: translateWordClass(data.term.wordClass ?? '', t), }, ]} /> @@ -173,7 +127,7 @@ export default function TermModal({ data }: TermModalProps) { </> ); - function renderInfo(subtitle: string, value?: string | string[]) { + function renderInfo(subtitle: string, value?: string | number | string[]) { if (!value) { return null; } @@ -194,11 +148,7 @@ export default function TermModal({ data }: TermModalProps) { ); } - function renderInfoChip( - subtitle: string, - value?: Property[], - defaultValue = '' - ) { + function renderInfoChip(subtitle: string, value?: string, defaultValue = '') { if (!value) { return null; } @@ -211,12 +161,9 @@ export default function TermModal({ data }: TermModalProps) { */} <TermModalChip aria-disabled={true} - $isValid={value[0].value === 'VALID' ? 'true' : undefined} + $isValid={value === 'VALID' ? 'true' : undefined} > - {translateStatus( - getPropertyValue({ property: value }) ?? defaultValue, - t - )} + {translateStatus(value ?? defaultValue, t)} </TermModalChip> </> ); diff --git a/terminology-ui/src/common/components/term-modal/term-expander.tsx b/terminology-ui/src/common/components/term-modal/term-expander.tsx index 1d112c425..a619d8478 100644 --- a/terminology-ui/src/common/components/term-modal/term-expander.tsx +++ b/terminology-ui/src/common/components/term-modal/term-expander.tsx @@ -4,6 +4,7 @@ import { ExpanderTitleButton, } from 'suomifi-ui-components'; import { PropertyList, TermHeading, TermText } from './term-modal.styles'; +import { isArray } from 'lodash'; interface TermExpanderProps { title: string; @@ -15,7 +16,9 @@ interface TermExpanderProps { } export default function TermExpander({ title, data }: TermExpanderProps) { - if (data.filter((d) => d.value).length < 1) { + if ( + data.filter((d) => (isArray(d.value) ? d.value.length : d.value)).length < 1 + ) { return null; } diff --git a/terminology-ui/src/common/interfaces/interfaces-v2.ts b/terminology-ui/src/common/interfaces/interfaces-v2.ts index d21346c6a..dfd4a7c7f 100644 --- a/terminology-ui/src/common/interfaces/interfaces-v2.ts +++ b/terminology-ui/src/common/interfaces/interfaces-v2.ts @@ -15,25 +15,93 @@ export interface Terminology { organizations: Organization[]; created: string; modified: string; - creator: { id: string; name: string }; - modifier: { id: string; name: string }; + creator: UserMeta; + modifier: UserMeta; origin: string; } +export interface ConceptInfo { + identifier: string; + uri: string; + created: string; + modified: string; + creator: UserMeta; + modifier: UserMeta; + definition: LocalizedValue; + notes: LocalizedListItem[]; + examples: LocalizedListItem[]; + subjectArea: string; + status: Status; + sources: string[]; + links: { + name: LocalizedValue; + uri: string; + description: LocalizedValue; + }[]; + changeNote: string; + historyNote: string; + conceptClass: string; + editorialNotes: string[]; + recommendedTerms: Term[]; + synonyms: Term[]; + notRecommendedTerms: Term[]; + searchTerms: Term[]; + broader: ConceptReferenceInfo[]; + narrower: ConceptReferenceInfo[]; + isPartOf: ConceptReferenceInfo[]; + hasPart: ConceptReferenceInfo[]; + related: ConceptReferenceInfo[]; + broadMatch: ConceptReferenceInfo[]; + narrowMatch: ConceptReferenceInfo[]; + exactMatch: ConceptReferenceInfo[]; + closeMatch: ConceptReferenceInfo[]; + relatedMatch: ConceptReferenceInfo[]; + memberOf: ConceptReferenceInfo[]; +} + +export interface Term { + language: string; + label?: string; + homographNumber: number; + status?: Status; + termInfo?: string; + scope?: string; + historyNote?: string; + changeNote?: string; + termStyle?: string; + termFamily?: string; + termConjugation?: string; + termEquivalency?: string; + wordClass?: string; + sources?: string[]; + editorialNotes?: string[]; +} + +export interface ConceptReferenceInfo { + conceptURI: string; + prefix: string; + identifier: string; + label: LocalizedValue; +} + +export interface ConceptCollectionInfo { + identifier: string; + prefix: string; + members: { + identifier: string; + label: LocalizedValue; + }; +} + /* build fails for empty interfaces export interface TerminologyInfo {} -export interface Concept {} export interface ConceptInfo {} -export interface Term {} export interface ConceptCollection {} -export interface ConceptCollectionInfo {} - -export interface ConceptReferenceInfo {} */ export interface SearchRequest { @@ -87,6 +155,16 @@ export interface LocalizedValue { [key: string]: string; } +export interface LocalizedListItem { + language: string; + value: string; +} + +export interface UserMeta { + id: string; + name: string; +} + export enum TerminologyType { TERMINOLOGICAL_VOCABULARY = 'TERMINOLOGICAL_VOCABULARY', OTHER_VOCABULARY = 'OTHER_VOCABULARY', diff --git a/terminology-ui/src/common/utils/compare-locals.ts b/terminology-ui/src/common/utils/compare-locals.ts index 4d0e75881..634705142 100644 --- a/terminology-ui/src/common/utils/compare-locals.ts +++ b/terminology-ui/src/common/utils/compare-locals.ts @@ -1,3 +1,4 @@ +import { LocalizedListItem } from '../interfaces/interfaces-v2'; import { Term } from '../interfaces/term.interface'; import { Property } from '../interfaces/termed-data-types.interface'; @@ -40,17 +41,17 @@ export function compareLocales( * @returns */ export function sortPropertyListByLanguage( - properties?: Property[] -): Property[] { + properties?: LocalizedListItem[] +): LocalizedListItem[] { // map properties by language const propertiesByLanguage = properties?.slice().reduce((result, property) => { - if (!result[property.lang]) { - result[property.lang] = []; + if (!result[property.language]) { + result[property.language] = []; } - result[property.lang].unshift(property); + result[property.language].unshift(property); return result; - }, {} as { [key: string]: Property[] }) ?? {}; + }, {} as { [key: string]: LocalizedListItem[] }) ?? {}; // sort by key (=language) and flat map return Object.keys(propertiesByLanguage) diff --git a/terminology-ui/src/modules/concept/administrative-details-expander.tsx b/terminology-ui/src/modules/concept/administrative-details-expander.tsx index 62213353a..a683964aa 100644 --- a/terminology-ui/src/modules/concept/administrative-details-expander.tsx +++ b/terminology-ui/src/modules/concept/administrative-details-expander.tsx @@ -5,47 +5,28 @@ import { ExpanderTitleButton, } from 'suomifi-ui-components'; import { BasicBlock } from 'yti-common-ui/block'; -import { PropertyBlock } from '@app/common/components/block'; -import { getPropertyValue } from '@app/common/components/property-value/get-property-value'; -import { Concept } from '@app/common/interfaces/concept.interface'; -import { PropertyList } from './concept.styles'; - -export function hasAdministrativeDetails(concept?: Concept, language?: string) { - const rest = { language, fallbackLanguage: 'fi' }; - - if (getPropertyValue({ property: concept?.properties.changeNote, ...rest })) { - return true; - } - - if ( - getPropertyValue({ property: concept?.properties.historyNote, ...rest }) - ) { - return true; - } - if ( - getPropertyValue({ property: concept?.properties.editorialNote, ...rest }) - ) { - return true; - } - - if (getPropertyValue({ property: concept?.properties.notation, ...rest })) { - return true; - } +import { PropertyList } from './concept.styles'; +import { ConceptInfo } from '@app/common/interfaces/interfaces-v2'; - return false; +export function hasAdministrativeDetails(concept?: ConceptInfo) { + return ( + concept?.changeNote || + concept?.historyNote || + concept?.editorialNotes.length + ); } export interface AdministrativeDetailsExpanderProps { - concept?: Concept; + concept?: ConceptInfo; } export default function AdministrativeDetailsExpander({ concept, }: AdministrativeDetailsExpanderProps) { - const { t, i18n } = useTranslation('concept'); + const { t } = useTranslation('concept'); - if (!hasAdministrativeDetails(concept, i18n.language)) { + if (!hasAdministrativeDetails(concept)) { return null; } @@ -55,25 +36,27 @@ export default function AdministrativeDetailsExpander({ {t('section-administrative-details')} </ExpanderTitleButton> <ExpanderContent> - <PropertyBlock - title={t('field-change-note')} - property={concept?.properties.changeNote} - /> - <PropertyBlock - title={t('field-history-note')} - property={concept?.properties.historyNote} - /> - <BasicBlock title={t('field-editorial-note')}> - <PropertyList> - {concept?.properties.editorialNote?.map((note) => ( - <li key={note.value}>{note.value}</li> - ))} - </PropertyList> - </BasicBlock> - <PropertyBlock - title={t('field-notation')} - property={concept?.properties.notation} - /> + {concept?.changeNote && ( + <BasicBlock title={t('field-change-note')}> + {concept?.changeNote} + </BasicBlock> + )} + + {concept?.historyNote && ( + <BasicBlock title={t('field-history-note')}> + {concept?.historyNote} + </BasicBlock> + )} + + {concept?.editorialNotes && concept?.editorialNotes?.length > 0 && ( + <BasicBlock title={t('field-editorial-note')}> + <PropertyList> + {concept?.editorialNotes?.map((note, idx) => ( + <li key={`note-${idx}`}>{note}</li> + ))} + </PropertyList> + </BasicBlock> + )} </ExpanderContent> </Expander> ); diff --git a/terminology-ui/src/modules/concept/concept-sidebar.tsx b/terminology-ui/src/modules/concept/concept-sidebar.tsx index db6a065be..580bfe1ff 100644 --- a/terminology-ui/src/modules/concept/concept-sidebar.tsx +++ b/terminology-ui/src/modules/concept/concept-sidebar.tsx @@ -1,40 +1,65 @@ -import { useTranslation } from 'next-i18next'; +import { i18n, useTranslation } from 'next-i18next'; import React from 'react'; import Separator from 'yti-common-ui/separator'; import { Sidebar, SidebarHeader, SidebarSection } from 'yti-common-ui/sidebar'; -import { Concept } from '@app/common/interfaces/concept.interface'; -import getReferenceValues from './get-reference-values'; +import { + ConceptInfo, + ConceptReferenceInfo, +} from '@app/common/interfaces/interfaces-v2'; +import { getLanguageVersion } from 'yti-common-ui/utils/get-language-version'; export interface ConceptSidebarProps { - concept?: Concept; + concept?: ConceptInfo; +} + +function getConceptReferenceValues(references?: ConceptReferenceInfo[]) { + return getReferenceValues('concept', references); +} + +function getCollectiontReferenceValues(references?: ConceptReferenceInfo[]) { + return getReferenceValues('collection', references); +} + +function getReferenceValues( + type: 'concept' | 'collection', + references?: ConceptReferenceInfo[] +) { + if (!references) { + return []; + } + + return references.map((ref) => { + return { + id: ref.conceptURI, + href: `/terminology/${ref.prefix}/${type}/${ref.identifier}`, + value: getLanguageVersion({ + data: ref.label, + lang: i18n?.language ?? 'fi', + }), + }; + }); } export default function ConceptSidebar({ concept }: ConceptSidebarProps) { const { t, i18n } = useTranslation('concept'); - const terminologyId = concept?.type.graph.id; - const shouldRenderDivider1 = [ - concept?.references.broader, - concept?.references.narrower, - concept?.references.related, - concept?.references.isPartOf, - concept?.references.hasPart, - concept?.references.relatedMatch, - concept?.references.exactMatch, - concept?.references.closeMatch, + concept?.broader, + concept?.narrower, + concept?.related, + concept?.isPartOf, + concept?.hasPart, + concept?.relatedMatch, + concept?.exactMatch, + concept?.closeMatch, ] .flat() .filter(Boolean).length > 0; - const shouldRenderDivider2 = false; - - const shouldRenderDivider3 = - [concept?.referrers.member].flat().filter(Boolean).length > 0; + const shouldRenderDivider2 = (concept?.memberOf ?? []).length > 0; - const isEmpty = - !shouldRenderDivider1 && !shouldRenderDivider2 && !shouldRenderDivider3; + const isEmpty = !shouldRenderDivider1 && !shouldRenderDivider2; return ( <Sidebar isEmpty={isEmpty}> @@ -44,106 +69,59 @@ export default function ConceptSidebar({ concept }: ConceptSidebarProps) { <SidebarSection heading={t('sidebar-section-heading-broader')} - items={getReferenceValues( - concept?.references.broader, - i18n.language, - terminologyId - )} + items={getConceptReferenceValues(concept?.broader)} /> <SidebarSection heading={t('sidebar-section-heading-narrower')} - items={getReferenceValues( - concept?.references.narrower, - i18n.language, - terminologyId - )} + items={getConceptReferenceValues(concept?.narrower)} /> <SidebarSection heading={t('sidebar-section-heading-related')} - items={getReferenceValues( - concept?.references.related, - i18n.language, - terminologyId - )} + items={getConceptReferenceValues(concept?.related)} /> <SidebarSection heading={t('sidebar-section-heading-is-part-of')} - items={getReferenceValues( - concept?.references.isPartOf, - i18n.language, - terminologyId - )} + items={getConceptReferenceValues(concept?.isPartOf)} /> <SidebarSection heading={t('sidebar-section-heading-has-part')} - items={getReferenceValues( - concept?.references.hasPart, - i18n.language, - terminologyId - )} + items={getConceptReferenceValues(concept?.hasPart)} /> <SidebarSection heading={t('sidebar-section-heading-related-match')} - items={getReferenceValues( - concept?.references.relatedMatch, - i18n.language - )} + items={getConceptReferenceValues(concept?.relatedMatch)} /> <SidebarSection heading={t('sidebar-section-heading-exact-match')} - items={getReferenceValues( - concept?.references.exactMatch, - i18n.language - )} + items={getConceptReferenceValues(concept?.exactMatch)} /> <SidebarSection heading={t('sidebar-section-heading-close-match')} - items={getReferenceValues( - concept?.references.closeMatch, - i18n.language - )} + items={getConceptReferenceValues(concept?.closeMatch)} /> <SidebarSection heading={t('sidebar-section-heading-broad-match')} - items={getReferenceValues( - concept?.references.broadMatch, - i18n.language - )} + items={getConceptReferenceValues(concept?.broadMatch)} /> <SidebarSection heading={t('sidebar-section-heading-narrow-match')} - items={getReferenceValues( - concept?.references.narrowMatch, - i18n.language - )} + items={getConceptReferenceValues(concept?.narrowMatch)} /> - {shouldRenderDivider2 && <Separator />} - {/* <SidebarSection<ConceptLink> - header={t('sidebar-section-heading-in-other-terminologies')} - items={concept?.references.closeMatch} - linkPrefix={`/terminology/${terminologyId}/concept/`} - propertyAccessor={conceptLink => conceptLink?.properties?.prefLabel} - /> */} - - {shouldRenderDivider3 && <Separator />} + {shouldRenderDivider2 && <Separator />} <SidebarSection heading={t('sidebar-section-heading-member')} - items={getReferenceValues( - concept?.referrers.member, - i18n.language, - terminologyId - )} + items={getCollectiontReferenceValues(concept?.memberOf)} /> </Sidebar> ); diff --git a/terminology-ui/src/modules/concept/details-expander.tsx b/terminology-ui/src/modules/concept/details-expander.tsx index d265d6fe5..a4a809977 100644 --- a/terminology-ui/src/modules/concept/details-expander.tsx +++ b/terminology-ui/src/modules/concept/details-expander.tsx @@ -1,30 +1,24 @@ import { useTranslation } from 'next-i18next'; import { BasicBlock } from 'yti-common-ui/block'; -import { Concept } from '@app/common/interfaces/concept.interface'; import AdministrativeDetailsExpander, { hasAdministrativeDetails, } from './administrative-details-expander'; -import DiagramsAndSourcesExpander, { - hasDiagramsAndSources, -} from './diagrams-and-sources-expander'; -import OtherDetailsExpander, { - hasOtherDetails, -} from './other-details-expander'; +import DiagramsAndSourcesExpander from './diagrams-and-sources-expander'; import { DetailsExpanderGroup } from './concept.styles'; +import { ConceptInfo } from '@app/common/interfaces/interfaces-v2'; +import OtherDetailsExpander from './other-details-expander'; export interface DetailsExpanderProps { - concept?: Concept; + concept?: ConceptInfo; } export default function DetailsExpander({ concept }: DetailsExpanderProps) { const { i18n } = useTranslation('concept'); - const noDiagramsAndSources = !hasDiagramsAndSources(concept, i18n.language); - const noAdministrativeDetails = !hasAdministrativeDetails( - concept, - i18n.language - ); - const noOtherDetails = !hasOtherDetails(concept, i18n.language); + const noDiagramsAndSources = + !concept?.links.length && !concept?.sources.length; + const noAdministrativeDetails = !hasAdministrativeDetails(concept); + const noOtherDetails = !concept?.conceptClass; if (noDiagramsAndSources && noAdministrativeDetails && noOtherDetails) { return null; } diff --git a/terminology-ui/src/modules/concept/diagrams-and-sources-expander.tsx b/terminology-ui/src/modules/concept/diagrams-and-sources-expander.tsx index d47205e95..08fd7ffd0 100644 --- a/terminology-ui/src/modules/concept/diagrams-and-sources-expander.tsx +++ b/terminology-ui/src/modules/concept/diagrams-and-sources-expander.tsx @@ -6,46 +6,13 @@ import { ExternalLink, } from 'suomifi-ui-components'; import { BasicBlock } from 'yti-common-ui/block'; -import { getPropertyValue } from '@app/common/components/property-value/get-property-value'; -import { Concept } from '@app/common/interfaces/concept.interface'; import { PropertyList } from './concept.styles'; import Link from 'next/link'; -import getDiagramValues from '@app/common/utils/get-diagram-values'; - -export function hasDiagramsAndSources(concept?: Concept, language?: string) { - const rest = { language, fallbackLanguage: 'fi' }; - - if (getPropertyValue({ property: concept?.properties.source, ...rest })) { - return true; - } - - if ( - getPropertyValue({ property: concept?.properties.externalLink, ...rest }) - ) { - return true; - } - - return false; -} - -function hasDiagrams(concept?: Concept, language?: string) { - if ( - getPropertyValue({ property: concept?.properties.externalLink, language }) - ) { - return true; - } - return false; -} - -function hasSources(concept?: Concept, language?: string) { - if (getPropertyValue({ property: concept?.properties.source, language })) { - return true; - } - return false; -} +import { ConceptInfo } from '@app/common/interfaces/interfaces-v2'; +import { getLanguageVersion } from 'yti-common-ui/utils/get-language-version'; export interface DiagramsAndSourcesExpanderProps { - concept?: Concept; + concept?: ConceptInfo; } export default function DiagramsAndSourcesExpander({ @@ -53,7 +20,7 @@ export default function DiagramsAndSourcesExpander({ }: DiagramsAndSourcesExpanderProps) { const { t, i18n } = useTranslation('concept'); - if (!hasDiagramsAndSources(concept, i18n.language)) { + if (!concept?.links?.length && !concept?.sources?.length) { return null; } @@ -63,26 +30,31 @@ export default function DiagramsAndSourcesExpander({ {t('section-concept-diagrams-and-sources')} </ExpanderTitleButton> <ExpanderContent> - {hasDiagrams(concept, i18n.language) && ( + {concept?.links?.length && ( <BasicBlock title={t('field-concept-diagrams')}> <PropertyList $smBot> - {concept?.properties.externalLink?.map((l, idx) => { - const link = getDiagramValues(l.value); + {concept?.links?.map((link, idx) => { return ( <li key={`diagrams-${idx}`}> - <Link href={link.url} passHref legacyBehavior> + <Link href={link.uri} passHref legacyBehavior> <ExternalLink href="" labelNewWindow="" style={{ fontSize: '16px' }} > - {link.name} + {getLanguageVersion({ + data: link.name, + lang: i18n.language, + })} </ExternalLink> </Link> - {link.description !== '' && ( + {Object.entries(link.description).length > 0 && ( <> <br /> - {link.description} + {getLanguageVersion({ + data: link.description, + lang: i18n.language, + })} </> )} </li> @@ -92,11 +64,11 @@ export default function DiagramsAndSourcesExpander({ </BasicBlock> )} - {hasSources(concept, i18n.language) && ( + {concept?.sources.length && ( <BasicBlock title={t('field-sources')}> <PropertyList> - {concept?.properties.source?.map((source) => ( - <li key={source.value}>{source.value}</li> + {concept?.sources?.map((source, idx) => ( + <li key={`source-${idx}`}>{source}</li> ))} </PropertyList> </BasicBlock> diff --git a/terminology-ui/src/modules/concept/get-reference-values.tsx b/terminology-ui/src/modules/concept/get-reference-values.tsx deleted file mode 100644 index 95c7780e7..000000000 --- a/terminology-ui/src/modules/concept/get-reference-values.tsx +++ /dev/null @@ -1,76 +0,0 @@ -import { getPropertyValue } from '@app/common/components/property-value/get-property-value'; -import { Collection } from '@app/common/interfaces/collection.interface'; -import { ConceptLink } from '@app/common/interfaces/concept-link.interface'; -import { Concept } from '@app/common/interfaces/concept.interface'; -import { Property } from '@app/common/interfaces/termed-data-types.interface'; - -export default function getReferenceValues( - reference?: Concept[] | ConceptLink[] | Collection[], - language?: string, - terminologyId?: string -): { - id: string; - href: string; - value: string; -}[] { - if (!reference || !language) { - return []; - } - - if (isConcept(reference[0])) { - return (reference as Concept[]).map((r) => { - const prefLabels: Property[] = - r.references.prefLabelXl - ?.flatMap((xl) => xl.properties.prefLabel) - .filter((label): label is Property => typeof label !== 'undefined') ?? - []; - - const value = getPropertyValue({ - property: prefLabels, - language: language, - }); - - return { - id: r.id, - href: `/terminology/${terminologyId ?? r.type.graph.id}/concept/${ - r.id - }`, - value: value, - }; - }); - } - - if ('targetGraph' in reference[0].properties) { - return (reference as ConceptLink[]).map((r) => { - const terminologyId = getPropertyValue({ - property: r.properties.targetGraph, - }); - const conceptId = getPropertyValue({ property: r.properties.targetId }); - return { - id: conceptId, - href: `/terminology/${terminologyId}/concept/${conceptId}`, - value: getPropertyValue({ - property: r.properties.prefLabel, - language: language, - }), - }; - }); - } - - return (reference as Collection[]).map((r) => ({ - id: r.id, - href: `/terminology/${terminologyId}/collection/${r.id}`, - value: getPropertyValue({ - property: r.properties.prefLabel, - language: language, - }), - })); -} - -function isConcept(reference: Concept | ConceptLink | Collection): boolean { - if ('prefLabelXl' in reference.references) { - return true; - } - - return false; -} diff --git a/terminology-ui/src/modules/concept/index.tsx b/terminology-ui/src/modules/concept/index.tsx index 2a53500b6..f759fca51 100644 --- a/terminology-ui/src/modules/concept/index.tsx +++ b/terminology-ui/src/modules/concept/index.tsx @@ -9,7 +9,12 @@ import { Text, VisuallyHidden, } from 'suomifi-ui-components'; -import { BasicBlock, BasicBlockExtraWrapper } from 'yti-common-ui/block'; +import { + BasicBlock, + BasicBlockExtraWrapper, + MultilingualBlock, + MultilingualBlockList, +} from 'yti-common-ui/block'; import { MultilingualPropertyBlock, PropertyBlock, @@ -90,18 +95,17 @@ export default function Concept({ terminologyId, conceptId }: ConceptProps) { .map((org) => org.id); }, [organizations, isLoading, isError]); - const prefLabel = getPropertyValue({ - property: getProperty('prefLabel', concept?.references.prefLabelXl), - language: i18n.language, - }); + const recommendedTerm = + concept?.recommendedTerms.find((term) => term.language === i18n.language) ?? + concept?.recommendedTerms[0]; - const status = - getPropertyValue({ property: concept?.properties.status }) || 'DRAFT'; + const prefLabel = recommendedTerm?.label ?? ''; + const status = concept?.status || 'DRAFT'; const email = terminology?.contact?.trim() ?? ''; useEffect(() => { if (concept) { - dispatch(setTitle(prefLabel ?? '')); + dispatch(setTitle(prefLabel)); } }, [concept, dispatch, prefLabel]); @@ -182,35 +186,37 @@ export default function Concept({ terminologyId, conceptId }: ConceptProps) { {translateStatus(status, t)} </StatusChip> </BadgeBar> - <BasicBlock title="URI">{concept?.uri}</BasicBlock> <TermBlock title={<h2>{t('field-terms-label')}</h2>} data={terms} /> - <MultilingualPropertyBlock - title={<h2>{t('field-definition')}</h2>} - data={definitions} - /> + {Object.keys(definitions).length > 0 && ( + <BasicBlock title={t('field-definition')}> + <MultilingualBlock data={definitions} /> + </BasicBlock> + )} - <PropertyBlock - title={t('field-subject-area')} - property={concept?.properties.subjectArea} - /> + {concept?.subjectArea && ( + <BasicBlock title={t('field-subject-area')}> + {concept?.subjectArea} + </BasicBlock> + )} - <MultilingualPropertyBlock - title={<h2>{t('field-note')}</h2>} - data={notes} - /> + {notes && notes.length > 0 && ( + <BasicBlock title={t('field-note')}> + <MultilingualBlockList data={concept?.notes} renderHtml /> + </BasicBlock> + )} - <MultilingualPropertyBlock - title={<h2>{t('field-example')}</h2>} - data={examples} - /> + {examples && examples.length > 0 && ( + <BasicBlock title={t('field-example')}> + <MultilingualBlockList data={concept?.examples} renderHtml /> + </BasicBlock> + )} <DetailsExpander concept={concept} /> <Separator isLarge /> - {hasPermission && ( <> <BasicBlock @@ -233,7 +239,7 @@ export default function Concept({ terminologyId, conceptId }: ConceptProps) { <RemovalModal nonDescriptive={true} removalData={{ type: 'concept', data: concept }} - targetId={concept?.id ?? ''} + targetId={concept?.identifier ?? ''} targetName={prefLabel} /> </EditToolsBlock> @@ -243,11 +249,9 @@ export default function Concept({ terminologyId, conceptId }: ConceptProps) { <Separator isLarge /> </> )} - <VisuallyHidden as="h2"> {t('additional-technical-information', { ns: 'common' })} </VisuallyHidden> - <BasicBlock title={t('vocabulary-info-organization', { ns: 'common' })} id="organization" @@ -258,24 +262,23 @@ export default function Concept({ terminologyId, conceptId }: ConceptProps) { (o) => o && o.label && !childOrganizations?.includes(o.id) ) .map((organization) => ( - <li key={organization.id}>{organization?.label}</li> + <li key={organization.id}> + {organization?.label[i18n.language]} + </li> ))} </PropertyList> </BasicBlock> - <BasicBlock title={t('vocabulary-info-created-at', { ns: 'common' })}> - <FormattedDate date={concept?.createdDate} /> - {concept?.createdBy && `, ${concept.createdBy}`} + <FormattedDate date={concept?.created} /> + {concept?.creator.name && `, ${concept.creator.name}`} </BasicBlock> <BasicBlock title={t('vocabulary-info-modified-at', { ns: 'common' })} > - <FormattedDate date={concept?.lastModifiedDate} /> - {concept?.lastModifiedBy && `, ${concept.lastModifiedBy}`} + <FormattedDate date={concept?.modified} /> + {concept?.modifier.name && `, ${concept.modifier.name}`} </BasicBlock> - <Separator isLarge /> - <BasicBlock extra={ <ExternalLink diff --git a/terminology-ui/src/modules/concept/other-details-expander.tsx b/terminology-ui/src/modules/concept/other-details-expander.tsx index f51b51141..68ccbb6c3 100644 --- a/terminology-ui/src/modules/concept/other-details-expander.tsx +++ b/terminology-ui/src/modules/concept/other-details-expander.tsx @@ -4,36 +4,23 @@ import { ExpanderContent, ExpanderTitleButton, } from 'suomifi-ui-components'; -import { PropertyBlock } from '@app/common/components/block'; -import { getPropertyValue } from '@app/common/components/property-value/get-property-value'; -import { Concept } from '@app/common/interfaces/concept.interface'; +import { ConceptInfo } from '@app/common/interfaces/interfaces-v2'; +import { BasicBlock } from 'yti-common-ui/block'; -export function hasOtherDetails(concept?: Concept, language?: string) { - const rest = { language, fallbackLanguage: 'fi' }; - - if ( - getPropertyValue({ property: concept?.properties.conceptClass, ...rest }) - ) { - return true; - } - - if (getPropertyValue({ property: concept?.properties.wordClass, ...rest })) { - return true; - } - - return false; +export function hasOtherDetails(concept?: ConceptInfo) { + concept?.conceptClass; } export interface OtherDetailsExpanderProps { - concept?: Concept; + concept?: ConceptInfo; } export default function OtherDetailsExpander({ concept, }: OtherDetailsExpanderProps) { - const { t, i18n } = useTranslation('concept'); + const { t } = useTranslation('concept'); - if (!hasOtherDetails(concept, i18n.language)) { + if (!concept?.conceptClass) { return null; } @@ -41,18 +28,11 @@ export default function OtherDetailsExpander({ <Expander id="other-details-expander"> <ExpanderTitleButton>{t('section-other-details')}</ExpanderTitleButton> <ExpanderContent> - {/* <MultilingualPropertyBlock - title="Aihealue" - data={concept?.properties.something} - /> */} - <PropertyBlock - title={t('field-concept-class')} - property={concept?.properties.conceptClass} - /> - <PropertyBlock - title={t('field-word-class')} - property={concept?.properties.wordClass} - /> + {concept?.conceptClass && ( + <BasicBlock title={t('field-concept-class')}> + {concept?.conceptClass} + </BasicBlock> + )} </ExpanderContent> </Expander> ); diff --git a/terminology-ui/src/modules/concept/utils.tsx b/terminology-ui/src/modules/concept/utils.tsx index 60ac2d873..5f2a0e2f1 100644 --- a/terminology-ui/src/modules/concept/utils.tsx +++ b/terminology-ui/src/modules/concept/utils.tsx @@ -1,5 +1,8 @@ -import { Concept } from '@app/common/interfaces/concept.interface'; -import { Term } from '@app/common/interfaces/term.interface'; +import { + ConceptInfo, + LocalizedValue, + Term, +} from '@app/common/interfaces/interfaces-v2'; import { compareLocales, sortPropertyListByLanguage, @@ -13,8 +16,8 @@ const langOrder: { [key: string]: string } = { }; const termTypeOrder: { [key: string]: string } = { - prefLabelXl: 'a', - altLabelXl: 'b', + recommendedTerm: 'a', + synonym: 'b', }; /** @@ -31,48 +34,49 @@ const termTypeOrder: { [key: string]: string } = { * @returns */ function getCompareKey(term: Term, type: string, index: number) { - const prefLabel = term.properties.prefLabel?.[0]; - const langKey = `${ - langOrder[prefLabel?.lang ?? ''] ?? `x_${prefLabel?.lang}` - }`; + const langKey = `${langOrder[term.language ?? ''] ?? `x_${term.language}`}`; return `${langKey}_${termTypeOrder[type] ?? 'x'}_${index}`; } -export function getBlockData(t: TFunction, concept?: Concept) { +export function getBlockData(t: TFunction, concept?: ConceptInfo) { if (!concept) { - return { terms: [], definitions: [], notes: [], examples: [] }; + return { terms: [], definitions: {}, notes: [], examples: [] }; } const terms = [ - ...(concept.references.prefLabelXl ?? []).map((term, idx) => ({ + ...(concept.recommendedTerms ?? []).map((term, idx) => ({ term, type: t('field-terms-preferred', { ns: 'concept' }), - compareKey: getCompareKey(term, 'prefLabelXl', idx), + compareKey: getCompareKey(term, 'recommendedTerm', idx), })), - ...(concept.references.altLabelXl ?? []).map((term, idx) => ({ + ...(concept.synonyms ?? []).map((term, idx) => ({ term, type: t('field-terms-alternative', { ns: 'concept' }), - compareKey: getCompareKey(term, 'altLabelXl', idx), + compareKey: getCompareKey(term, 'synonym', idx), })), - ...(concept.references.notRecommendedSynonym ?? []).map((term, idx) => ({ + ...(concept.notRecommendedTerms ?? []).map((term, idx) => ({ term, type: t('field-terms-non-recommended', { ns: 'concept' }), compareKey: getCompareKey(term, 'notRecommendedSynonym', idx), })), - ...(concept.references.hiddenTerm ?? []).map((term) => ({ + ...(concept.searchTerms ?? []).map((term) => ({ term, type: t('field-terms-hidden', { ns: 'concept' }), - compareKey: getCompareKey(term, 'hiddenTerm', 0), + compareKey: getCompareKey(term, 'searchTerm', 0), })), ].sort((t1, t2) => t1.compareKey.localeCompare(t2.compareKey)); const definitions = - concept.properties.definition + Object.keys(concept.definition) ?.slice() - .sort((t1, t2) => compareLocales(t1, t2)) ?? []; + .sort((t1, t2) => compareLocales(t1, t2)) + .reduce((result, lang) => { + result[lang] = concept.definition[lang]; + return result; + }, {} as LocalizedValue) ?? {}; - const notes = sortPropertyListByLanguage(concept.properties.note); - const examples = sortPropertyListByLanguage(concept.properties.example); + const notes = sortPropertyListByLanguage(concept.notes); + const examples = sortPropertyListByLanguage(concept.examples); return { terms, definitions, notes, examples }; } diff --git a/terminology-ui/src/modules/edit-collection/concept-picker/expander-concept-content.tsx b/terminology-ui/src/modules/edit-collection/concept-picker/expander-concept-content.tsx index 64f616850..fdd09b054 100644 --- a/terminology-ui/src/modules/edit-collection/concept-picker/expander-concept-content.tsx +++ b/terminology-ui/src/modules/edit-collection/concept-picker/expander-concept-content.tsx @@ -60,7 +60,7 @@ export function ExpanderConceptContent({ <BasicBlock title={t('last-modified')}> <FormattedDate date={concept.modified} /> - {data?.lastModifiedBy && `, ${data?.lastModifiedBy}`} + {/*data?.lastModifiedBy && `, ${data?.lastModifiedBy}`*/} </BasicBlock> </ExpanderContent> ); diff --git a/terminology-ui/src/pages/terminology/[terminologyId]/concept/[conceptId].tsx b/terminology-ui/src/pages/terminology/[terminologyId]/concept/[conceptId].tsx index 29aa66fea..91faabf01 100644 --- a/terminology-ui/src/pages/terminology/[terminologyId]/concept/[conceptId].tsx +++ b/terminology-ui/src/pages/terminology/[terminologyId]/concept/[conceptId].tsx @@ -21,9 +21,9 @@ import { } from 'yti-common-ui/common-context-provider'; import PageHead from 'yti-common-ui/page-head'; import { getPropertyValue } from '@app/common/components/property-value/get-property-value'; -import { getProperty } from '@app/common/utils/get-property'; import { getStoreData } from '@app/common/utils/get-store-data'; import { wrapper } from '@app/store'; +import { getLanguageVersion } from 'yti-common-ui/utils/get-language-version'; interface ConceptPageProps extends CommonContextState { _netI18Next: SSRConfig; @@ -108,16 +108,15 @@ export const getServerSideProps = createCommonGetServerSideProps( language: locale, }); - const conceptTitle = getPropertyValue({ - property: getProperty('prefLabel', conceptData?.references.prefLabelXl), - language: locale, + const conceptTitle = getLanguageVersion({ + data: conceptData.label, + lang: locale ?? 'fi', }); - const conceptDescription = getPropertyValue({ - property: conceptData?.properties.definition, - language: locale, + const conceptDescription = getLanguageVersion({ + data: conceptData.definition, + lang: locale ?? 'fi', }); - return { props: { conceptDescription: conceptDescription, From d46c301e1179d10d865f168b4c584be6aa3b6506 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kalle=20Kytt=C3=A4l=C3=A4?= <kalle.kyttala@rebase.fi> Date: Tue, 27 Aug 2024 13:03:40 +0300 Subject: [PATCH 04/35] concept collections view and remove concepts, collections and terminologies --- .../components/block/concept-list-block.tsx | 48 ------- .../src/common/components/block/index.ts | 2 - .../collection/collection.slice.tsx | 20 ++- .../components/concept/concept.slice.tsx | 10 ++ .../info-dropdown/info-expander.tsx | 3 +- .../common/components/removal-modal/index.tsx | 120 +++++++----------- .../common/components/remove/remove.slice.tsx | 20 --- .../common/components/term-modal/index.tsx | 2 +- .../vocabulary/vocabulary.slice.tsx | 13 +- .../src/common/interfaces/interfaces-v2.ts | 13 +- .../modules/collection/collection-sidebar.tsx | 23 ++-- .../src/modules/collection/index.tsx | 95 ++++++++------ .../src/modules/collection/utils.tsx | 20 --- .../concept/diagrams-and-sources-expander.tsx | 2 +- terminology-ui/src/modules/concept/index.tsx | 9 +- .../src/modules/terminology-search/index.tsx | 17 +-- .../src/modules/vocabulary/index.tsx | 103 +++++++-------- .../collection/[collectionId].tsx | 24 ++-- terminology-ui/src/store/index.tsx | 3 - 19 files changed, 226 insertions(+), 321 deletions(-) delete mode 100644 terminology-ui/src/common/components/block/concept-list-block.tsx delete mode 100644 terminology-ui/src/common/components/remove/remove.slice.tsx delete mode 100644 terminology-ui/src/modules/collection/utils.tsx diff --git a/terminology-ui/src/common/components/block/concept-list-block.tsx b/terminology-ui/src/common/components/block/concept-list-block.tsx deleted file mode 100644 index a1e58788e..000000000 --- a/terminology-ui/src/common/components/block/concept-list-block.tsx +++ /dev/null @@ -1,48 +0,0 @@ -import React from 'react'; -import Link from 'next/link'; -import { Link as SuomiLink } from 'suomifi-ui-components'; -import { BasicBlock, List } from 'yti-common-ui/block'; -import { Concept } from '@app/common/interfaces/concept.interface'; -import { getProperty } from '@app/common/utils/get-property'; -import PropertyValue from '@app/common/components/property-value'; - -export interface ConceptListBlockProps { - title: React.ReactNode; - data?: Concept[]; - extra?: React.ReactNode; -} - -export default function ConceptListBlock({ - title, - data, - extra, -}: ConceptListBlockProps) { - if (!data?.length) { - return null; - } - - return ( - <BasicBlock title={title} extra={extra}> - <List> - {data?.map((concept) => ( - <li key={concept.id}> - <Link - href={`/terminology/${concept.identifier.type.graph.id}/concept/${concept.id}`} - passHref - legacyBehavior - > - <SuomiLink href=""> - <PropertyValue - property={getProperty( - 'prefLabel', - concept.references.prefLabelXl - )} - /> - </SuomiLink> - </Link> - </li> - ))} - </List> - </BasicBlock> - ); -} diff --git a/terminology-ui/src/common/components/block/index.ts b/terminology-ui/src/common/components/block/index.ts index 47db52fe3..a2065d45c 100644 --- a/terminology-ui/src/common/components/block/index.ts +++ b/terminology-ui/src/common/components/block/index.ts @@ -1,11 +1,9 @@ -import ConceptListBlock from './concept-list-block'; import MultilingualBlock from './multilingual-block'; import MultilingualPropertyBlock from './multilingual-property-block'; import PropertyBlock from './property-block'; import TermBlock from './term-block'; export { - ConceptListBlock, MultilingualBlock, MultilingualPropertyBlock, PropertyBlock, diff --git a/terminology-ui/src/common/components/collection/collection.slice.tsx b/terminology-ui/src/common/components/collection/collection.slice.tsx index 4ef9d71c4..84ea8b96f 100644 --- a/terminology-ui/src/common/components/collection/collection.slice.tsx +++ b/terminology-ui/src/common/components/collection/collection.slice.tsx @@ -1,6 +1,6 @@ import { createApi } from '@reduxjs/toolkit/query/react'; -import { Collection } from '@app/common/interfaces/collection.interface'; import { getTerminologyApiBaseQuery } from '@app/store/api-base-query'; +import { ConceptCollectionInfo } from '@app/common/interfaces/interfaces-v2'; export const collectionApi = createApi({ reducerPath: 'collectionAPI', @@ -8,26 +8,36 @@ export const collectionApi = createApi({ tagTypes: ['Collection'], endpoints: (builder) => ({ getCollection: builder.query< - Collection, + ConceptCollectionInfo, { terminologyId: string; collectionId: string } >({ query: ({ terminologyId, collectionId }) => ({ - url: `/collection?graphId=${terminologyId}&collectionId=${collectionId}`, + url: `/collection/${terminologyId}/${collectionId}`, method: 'GET', }), }), - getCollections: builder.query<Collection[], string>({ + getCollections: builder.query<ConceptCollectionInfo[], string>({ query: (terminologyId) => ({ - url: `/collections?graphId=${terminologyId}`, + url: `/collection/${terminologyId}`, method: 'GET', }), }), + deleteCollection: builder.mutation< + null, + { prefix: string; collectionId: string } + >({ + query: (value) => ({ + url: `/collection/${value.prefix}/${value.collectionId}`, + method: 'DELETE', + }), + }), }), }); export const { useGetCollectionQuery, useGetCollectionsQuery, + useDeleteCollectionMutation, util: { getRunningQueriesThunk }, } = collectionApi; diff --git a/terminology-ui/src/common/components/concept/concept.slice.tsx b/terminology-ui/src/common/components/concept/concept.slice.tsx index c0e1a6dcb..6c204a127 100644 --- a/terminology-ui/src/common/components/concept/concept.slice.tsx +++ b/terminology-ui/src/common/components/concept/concept.slice.tsx @@ -17,6 +17,15 @@ export const conceptApi = createApi({ method: 'GET', }), }), + deleteConcept: builder.mutation< + null, + { prefix: string; conceptId: string } + >({ + query: (value) => ({ + url: `/concept/${value.prefix}/${value.conceptId}`, + method: 'DELETE', + }), + }), searchConcept: builder.mutation< { concepts: Concepts[]; @@ -60,6 +69,7 @@ export const conceptApi = createApi({ export const { useGetConceptQuery, useSearchConceptMutation, + useDeleteConceptMutation, util: { getRunningQueriesThunk }, } = conceptApi; diff --git a/terminology-ui/src/common/components/info-dropdown/info-expander.tsx b/terminology-ui/src/common/components/info-dropdown/info-expander.tsx index 2974c6084..3387eda4b 100644 --- a/terminology-ui/src/common/components/info-dropdown/info-expander.tsx +++ b/terminology-ui/src/common/components/info-dropdown/info-expander.tsx @@ -176,7 +176,8 @@ export default function InfoExpander({ /> <RemovalModal - removalData={{ type: 'terminology', data }} + dataType="terminology" + status={data.status} targetId={terminologyId} targetName={getLanguageVersion({ data: data.label, diff --git a/terminology-ui/src/common/components/removal-modal/index.tsx b/terminology-ui/src/common/components/removal-modal/index.tsx index 73cdabed4..314b0c8bc 100644 --- a/terminology-ui/src/common/components/removal-modal/index.tsx +++ b/terminology-ui/src/common/components/removal-modal/index.tsx @@ -1,4 +1,3 @@ -import { Collection } from '@app/common/interfaces/collection.interface'; import { translateRemovalModalConfirmation, translateRemovalModalDescription, @@ -23,46 +22,34 @@ import { } from 'suomifi-ui-components'; import { BasicBlock, BasicBlockExtraWrapper } from 'yti-common-ui/block'; import { useBreakpoints } from 'yti-common-ui/media-query'; -import { useDeleteTargetMutation } from '../remove/remove.slice'; import SaveSpinner from 'yti-common-ui/save-spinner'; import { terminologySearchApi } from '../terminology-search/terminology-search.slice'; -import { useDeleteVocabularyMutation } from '../vocabulary/vocabulary.slice'; -import { - generateCollectionData, - generateConceptData, -} from './generate-removal-data'; import { FooterBlock, RemoveModal, RemoveModalContent, } from './removal-modal.styles'; import { useGetAuthenticatedUserMutMutation } from '../login/login.slice'; -import { Terminology, ConceptInfo } from '@app/common/interfaces/interfaces-v2'; +import { useDeleteConceptMutation } from '../concept/concept.slice'; +import { useDeleteCollectionMutation } from '../collection/collection.slice'; +import { useDeleteTerminologyMutation } from '../vocabulary/vocabulary.slice'; interface RemovalModalProps { nonDescriptive?: boolean; - removalData: - | { - type: 'terminology'; - data?: Terminology; - } - | { - type: 'concept'; - data?: ConceptInfo; - } - | { - type: 'collection'; - data?: Collection; - }; + dataType: 'terminology' | 'concept' | 'collection'; + status?: string; + targetPrefix?: string; targetId: string; targetName: string; } export default function RemovalModal({ nonDescriptive, - removalData, + dataType, + status, targetId, targetName, + targetPrefix, }: RemovalModalProps) { const { t } = useTranslation('admin'); const dispatch = useStoreDispatch(); @@ -70,57 +57,41 @@ export default function RemovalModal({ const router = useRouter(); const [visible, setVisible] = useState(false); const [showError, setShowError] = useState(false); - const [deleteVocabulary, terminology] = useDeleteVocabularyMutation(); - const [deleteTarget, target] = useDeleteTargetMutation(); + const [deleteTerminology, terminology] = useDeleteTerminologyMutation(); + const [deleteConcept, concept] = useDeleteConceptMutation(); + const [deleteCollection, collection] = useDeleteCollectionMutation(); const [getAuthenticatedUser, authenticatedUser] = useGetAuthenticatedUserMutMutation(); const handleClick = () => { - if (removalData.type === 'terminology') { - deleteVocabulary(targetId); + if (dataType === 'terminology') { + deleteTerminology(targetId); } - if (removalData.type === 'concept' && removalData.data) { - /* TODO - const data = generateConceptData(removalData); - - if (data.length < 2) { - return null; - } - - deleteTarget(data); - */ + if (dataType === 'concept' && targetPrefix) { + deleteConcept({ prefix: targetPrefix, conceptId: targetId }); } - if (removalData.type === 'collection' && removalData.data) { - const data = generateCollectionData(removalData.data); - - if (data.length < 1) { - return null; - } - - deleteTarget(data); + if (dataType === 'collection' && targetPrefix) { + deleteCollection({ prefix: targetPrefix, collectionId: targetId }); } }; const handleReturn = () => { - if (removalData.type === 'terminology') { + if (dataType === 'terminology') { router.push('/'); dispatch(terminologySearchApi.util.invalidateTags(['TerminologySearch'])); return; } - if (removalData.data) { - router.push('/terminology/id'); // TODO ${removalData.data.type.graph.id}`); + if (targetPrefix) { + router.push(`/terminology/${targetPrefix}`); return; } }; const handleVisibility = () => { - if ( - removalData.type === 'terminology' && - removalData.data?.status === 'VALID' - ) { + if (dataType === 'terminology' && status === 'VALID') { setShowError(true); return; } @@ -130,50 +101,54 @@ export default function RemovalModal({ }; const isUninitialized = () => { - return terminology.isUninitialized && target.isUninitialized; + return ( + terminology.isUninitialized && + concept.isUninitialized && + collection.isUninitialized + ); }; const isLoading = () => { - return terminology.isLoading || target.isLoading; + return terminology.isLoading || concept.isLoading || collection.isLoading; }; const isSuccess = () => { - return terminology.isSuccess || target.isSuccess; + return terminology.isSuccess || concept.isSuccess || collection.isSuccess; }; const isError = () => { - return terminology.isError || target.isError; + return terminology.isError || concept.isError || collection.isError; }; return ( <> {!nonDescriptive ? ( <BasicBlock - title={translateRemovalModalTitle(removalData.type, t)} + title={translateRemovalModalTitle(dataType, t)} extra={ <BasicBlockExtraWrapper> <Button variant="secondary" icon={<IconRemove />} - id={`open-remove-${removalData.type}-modal`} + id={`open-remove-${dataType}-modal`} onClick={() => handleVisibility()} > - {translateRemovalModalTitle(removalData.type, t)} + {translateRemovalModalTitle(dataType, t)} </Button> </BasicBlockExtraWrapper> } > - {translateRemovalModalDescription(removalData.type, t)} + {translateRemovalModalDescription(dataType, t)} </BasicBlock> ) : ( <> <Button variant="secondary" icon={<IconRemove />} - id={`open-remove-${removalData.type}-modal`} + id={`open-remove-${dataType}-modal`} onClick={() => handleVisibility()} > - {translateRemovalModalTitle(removalData.type, t)} + {translateRemovalModalTitle(dataType, t)} </Button> </> )} @@ -201,9 +176,7 @@ export default function RemovalModal({ variant={isSmall ? 'smallScreen' : 'default'} > <RemoveModalContent> - <ModalTitle> - {translateRemovalModalTitle(removalData.type, t)} - </ModalTitle> + <ModalTitle>{translateRemovalModalTitle(dataType, t)}</ModalTitle> {renderConfirmation()} {renderProcessing()} {renderFinished()} @@ -227,11 +200,11 @@ export default function RemovalModal({ <> <Paragraph> <Text> - {translateRemovalModalConfirmation(removalData.type, targetName, t)} + {translateRemovalModalConfirmation(dataType, targetName, t)} </Text> </Paragraph> <Paragraph> - <Text>{translateRemovalModalWarning(removalData.type, t)}</Text> + <Text>{translateRemovalModalWarning(dataType, t)}</Text> </Paragraph> </> ); @@ -242,11 +215,7 @@ export default function RemovalModal({ return null; } - return ( - <SaveSpinner - text={translateRemovalModalProcessing(removalData.type, t)} - /> - ); + return <SaveSpinner text={translateRemovalModalProcessing(dataType, t)} />; } function renderFinished() { @@ -254,9 +223,7 @@ export default function RemovalModal({ return ( <> <Paragraph> - <Text> - {translateRemovalModalRemoved(removalData.type, targetName, t)} - </Text> + <Text>{translateRemovalModalRemoved(dataType, targetName, t)}</Text> </Paragraph> </> ); @@ -265,7 +232,7 @@ export default function RemovalModal({ if (isError()) { return ( <InlineAlert status="error"> - {translateRemovalModalError(removalData.type, t)} + {translateRemovalModalError(dataType, t)} </InlineAlert> ); } @@ -291,10 +258,11 @@ export default function RemovalModal({ } if (isSuccess()) { + console.info('success'); return ( <> <Button onClick={() => handleReturn()}> - {removalData.type === 'terminology' + {dataType === 'terminology' ? t('return-to-front-page') : t('return-to-terminology-page')} </Button> diff --git a/terminology-ui/src/common/components/remove/remove.slice.tsx b/terminology-ui/src/common/components/remove/remove.slice.tsx deleted file mode 100644 index ba5477ce2..000000000 --- a/terminology-ui/src/common/components/remove/remove.slice.tsx +++ /dev/null @@ -1,20 +0,0 @@ -import { createApi } from '@reduxjs/toolkit/query/react'; -import { getTerminologyApiBaseQuery } from '@app/store/api-base-query'; -import { Identifier } from '@app/common/interfaces/termed-data-types.interface'; - -export const removeApi = createApi({ - reducerPath: 'removeApi', - baseQuery: getTerminologyApiBaseQuery(), - tagTypes: ['Remove'], - endpoints: (builder) => ({ - deleteTarget: builder.mutation<null, Identifier<string>[]>({ - query: (data) => ({ - url: '/remove?sync=true&disconnect=true', - method: 'DELETE', - data: data, - }), - }), - }), -}); - -export const { useDeleteTargetMutation } = removeApi; diff --git a/terminology-ui/src/common/components/term-modal/index.tsx b/terminology-ui/src/common/components/term-modal/index.tsx index 5258cf86a..35faddcfe 100644 --- a/terminology-ui/src/common/components/term-modal/index.tsx +++ b/terminology-ui/src/common/components/term-modal/index.tsx @@ -128,7 +128,7 @@ export default function TermModal({ data }: TermModalProps) { ); function renderInfo(subtitle: string, value?: string | number | string[]) { - if (!value) { + if (!value || (Array.isArray(value) && value.length === 0)) { return null; } diff --git a/terminology-ui/src/common/components/vocabulary/vocabulary.slice.tsx b/terminology-ui/src/common/components/vocabulary/vocabulary.slice.tsx index d07ea54f6..7b1c00ffe 100644 --- a/terminology-ui/src/common/components/vocabulary/vocabulary.slice.tsx +++ b/terminology-ui/src/common/components/vocabulary/vocabulary.slice.tsx @@ -8,6 +8,7 @@ import { import { UrlState } from '@app/common/utils/hooks/use-url-state'; import { getTerminologyApiBaseQuery } from '@app/store/api-base-query'; import { + ConceptCollectionInfo, ConceptResponseObject, SearchResponse, Terminology, @@ -26,9 +27,9 @@ export const terminologyApi = createApi({ baseQuery: getTerminologyApiBaseQuery(), tagTypes: ['Terminology'], endpoints: (builder) => ({ - getCollections: builder.query<Collection[], string>({ + getCollections: builder.query<ConceptCollectionInfo[], string>({ query: (terminologyId) => ({ - url: `/collections?graphId=${terminologyId}`, + url: `/collection/${terminologyId}`, method: 'GET', }), }), @@ -84,9 +85,9 @@ export const terminologyApi = createApi({ }, }), }), - deleteVocabulary: builder.mutation<null, string>({ - query: (uuid) => ({ - url: `/vocabulary?graphId=${uuid}`, + deleteTerminology: builder.mutation<null, string>({ + query: (prefix) => ({ + url: `/terminology/${prefix}`, method: 'DELETE', }), }), @@ -111,7 +112,7 @@ export const { useGetTerminologyQuery, usePostNewVocabularyMutation, usePostCreateVersionMutation, - useDeleteVocabularyMutation, + useDeleteTerminologyMutation, useGetIfNamespaceInUseMutation, useGetVocabulariesQuery, util: { getRunningQueriesThunk }, diff --git a/terminology-ui/src/common/interfaces/interfaces-v2.ts b/terminology-ui/src/common/interfaces/interfaces-v2.ts index dfd4a7c7f..cc057ec9a 100644 --- a/terminology-ui/src/common/interfaces/interfaces-v2.ts +++ b/terminology-ui/src/common/interfaces/interfaces-v2.ts @@ -84,12 +84,15 @@ export interface ConceptReferenceInfo { } export interface ConceptCollectionInfo { + uri: string; identifier: string; - prefix: string; - members: { - identifier: string; - label: LocalizedValue; - }; + label: LocalizedValue; + description: LocalizedValue; + members: ConceptReferenceInfo[]; + created: string; + modified: string; + creator: UserMeta; + modifier: UserMeta; } /* build fails for empty interfaces diff --git a/terminology-ui/src/modules/collection/collection-sidebar.tsx b/terminology-ui/src/modules/collection/collection-sidebar.tsx index f00cd92ef..c6d879ca3 100644 --- a/terminology-ui/src/modules/collection/collection-sidebar.tsx +++ b/terminology-ui/src/modules/collection/collection-sidebar.tsx @@ -3,27 +3,28 @@ import React from 'react'; import { useGetCollectionsQuery } from '@app/common/components/collection/collection.slice'; import Separator from 'yti-common-ui/separator'; import { Sidebar, SidebarHeader, SidebarSection } from 'yti-common-ui/sidebar'; -import { Collection } from '@app/common/interfaces/collection.interface'; -import { getPropertyValue } from '@app/common/components/property-value/get-property-value'; +import { ConceptCollectionInfo } from '@app/common/interfaces/interfaces-v2'; +import { getLanguageVersion } from 'yti-common-ui/utils/get-language-version'; export interface CollectionSidebarProps { - collection: Collection; + collection: ConceptCollectionInfo; + prefix: string; } export default function CollectionSidebar({ collection, + prefix, }: CollectionSidebarProps) { const { t, i18n } = useTranslation('collection'); - const terminologyId = collection.type.graph.id; - const { data: collections } = useGetCollectionsQuery(terminologyId); + const { data: collections } = useGetCollectionsQuery(prefix); const otherCollections = collections - ?.filter((other) => other.id !== collection.id) + ?.filter((other) => other.identifier !== collection.identifier) .map((c) => ({ - id: c.id, - href: `/terminology/${terminologyId}/collection/${c.id}`, - value: getPropertyValue({ - property: c.properties.prefLabel, - language: i18n.language, + id: c.identifier, + href: `/terminology/${prefix}/collection/${c.identifier}`, + value: getLanguageVersion({ + data: c.label, + lang: i18n.language, }), })); diff --git a/terminology-ui/src/modules/collection/index.tsx b/terminology-ui/src/modules/collection/index.tsx index 330bdc4a0..4b22056cc 100644 --- a/terminology-ui/src/modules/collection/index.tsx +++ b/terminology-ui/src/modules/collection/index.tsx @@ -8,17 +8,17 @@ import { Paragraph, Text, VisuallyHidden, + Link as SuomiLink, } from 'suomifi-ui-components'; -import { BasicBlock, BasicBlockExtraWrapper } from 'yti-common-ui/block'; import { - MultilingualPropertyBlock, - ConceptListBlock, -} from '@app/common/components/block'; + BasicBlock, + BasicBlockExtraWrapper, + List, + MultilingualBlock, +} from 'yti-common-ui/block'; import { Breadcrumb, BreadcrumbLink } from 'yti-common-ui/breadcrumb'; import FormattedDate from 'yti-common-ui/formatted-date'; import { useBreakpoints } from 'yti-common-ui/media-query'; -import PropertyValue from '@app/common/components/property-value'; -import { getPropertyValue } from '@app/common/components/property-value/get-property-value'; import Separator from 'yti-common-ui/separator'; import { useStoreDispatch } from '@app/store'; import CollectionSidebar from './collection-sidebar'; @@ -35,7 +35,6 @@ import { SubTitle, MainTitle, BadgeBar } from 'yti-common-ui/title-block'; import HasPermission from '@app/common/utils/has-permission'; import Link from 'next/link'; import RemovalModal from '@app/common/components/removal-modal'; -import { getBlockData } from './utils'; import { useGetOrganizationsQuery } from '@app/common/components/terminology-search/terminology-search.slice'; import { getLanguageVersion } from 'yti-common-ui/utils/get-language-version'; @@ -83,15 +82,11 @@ export default function Collection({ .map((org) => org.id); }, [organizations, isLoading, isError]); - const prefLabel = getPropertyValue({ - property: collection?.properties.prefLabel, - language: i18n.language, + const prefLabel = getLanguageVersion({ + data: collection?.label, + lang: i18n.language, }); - const { prefLabels, definitions } = useMemo(() => { - return getBlockData(collection); - }, [collection]); - useEffect(() => { if (collection) { dispatch(setTitle(prefLabel ?? '')); @@ -158,7 +153,7 @@ export default function Collection({ url={`/terminology/${terminologyId}/collections/${collectionId}`} current > - <PropertyValue property={collection?.properties.prefLabel} /> + {getLanguageVersion({ data: collection?.label, lang: i18n.language })} </BreadcrumbLink> </Breadcrumb> @@ -172,7 +167,10 @@ export default function Collection({ .join(', ')} </SubTitle> <MainTitle> - <PropertyValue property={collection?.properties.prefLabel} /> + {getLanguageVersion({ + data: collection?.label, + lang: i18n.language, + })} </MainTitle> <BadgeBar> {t('heading')} @@ -184,18 +182,36 @@ export default function Collection({ <BasicBlock title="URI">{collection?.uri}</BasicBlock> - <MultilingualPropertyBlock - title={t('field-name')} - data={prefLabels} - /> - <MultilingualPropertyBlock - title={t('field-definition')} - data={definitions} - /> - <ConceptListBlock - title={<h2>{t('field-member')}</h2>} - data={collection?.references.member} - /> + <BasicBlock title={t('field-name')}> + <MultilingualBlock data={collection?.label ?? {}} /> + </BasicBlock> + + {collection?.description && ( + <BasicBlock title={t('field-definition')}> + <MultilingualBlock data={collection?.description ?? {}} /> + </BasicBlock> + )} + + <BasicBlock title={<h2>{t('field-member')}</h2>}> + <List> + {collection?.members?.map((concept) => ( + <li key={concept.identifier}> + <Link + href={`/terminology/${terminology?.prefix}/concept/${concept.identifier}`} + passHref + legacyBehavior + > + <SuomiLink href=""> + {getLanguageVersion({ + data: concept.label, + lang: i18n.language, + })} + </SuomiLink> + </Link> + </li> + ))} + </List> + </BasicBlock> <Separator /> @@ -221,11 +237,9 @@ export default function Collection({ <RemovalModal nonDescriptive={true} - removalData={{ - type: 'collection', - data: collection, - }} - targetId={collection?.id ?? ''} + dataType="collection" + targetPrefix={terminology?.prefix} + targetId={collection?.identifier ?? ''} targetName={prefLabel} /> </EditToolsBlock> @@ -262,17 +276,22 @@ export default function Collection({ </BasicBlock> <BasicBlock title={t('vocabulary-info-created-at', { ns: 'common' })}> - <FormattedDate date={collection?.createdDate} /> - {collection?.createdBy && `, ${collection.createdBy}`} + <FormattedDate date={collection?.created} /> + {collection?.creator.name && `, ${collection.creator.name}`} </BasicBlock> <BasicBlock title={t('vocabulary-info-modified-at', { ns: 'common' })} > - <FormattedDate date={collection?.lastModifiedDate} /> - {collection?.lastModifiedBy && `, ${collection.lastModifiedBy}`} + <FormattedDate date={collection?.modified} /> + {collection?.modifier.name && `, ${collection.modifier.name}`} </BasicBlock> </MainContent> - {collection && <CollectionSidebar collection={collection} />} + {collection && ( + <CollectionSidebar + collection={collection} + prefix={terminology?.prefix ?? ''} + /> + )} </PageContent> </> ); diff --git a/terminology-ui/src/modules/collection/utils.tsx b/terminology-ui/src/modules/collection/utils.tsx deleted file mode 100644 index ae1d65f42..000000000 --- a/terminology-ui/src/modules/collection/utils.tsx +++ /dev/null @@ -1,20 +0,0 @@ -import { Collection } from '@app/common/interfaces/collection.interface'; -import { compareLocales } from '@app/common/utils/compare-locals'; - -export function getBlockData(collection?: Collection) { - if (!collection) { - return { prefLabels: [], definitions: [] }; - } - - const prefLabels = - collection.properties.prefLabel - ?.slice() - .sort((l1, l2) => compareLocales(l1, l2)) ?? []; - - const definitions = - collection.properties.definition - ?.slice() - .sort((d1, d2) => compareLocales(d1, d2)) ?? []; - - return { prefLabels, definitions }; -} diff --git a/terminology-ui/src/modules/concept/diagrams-and-sources-expander.tsx b/terminology-ui/src/modules/concept/diagrams-and-sources-expander.tsx index 08fd7ffd0..8488c6b56 100644 --- a/terminology-ui/src/modules/concept/diagrams-and-sources-expander.tsx +++ b/terminology-ui/src/modules/concept/diagrams-and-sources-expander.tsx @@ -30,7 +30,7 @@ export default function DiagramsAndSourcesExpander({ {t('section-concept-diagrams-and-sources')} </ExpanderTitleButton> <ExpanderContent> - {concept?.links?.length && ( + {concept?.links?.length > 0 && ( <BasicBlock title={t('field-concept-diagrams')}> <PropertyList $smBot> {concept?.links?.map((link, idx) => { diff --git a/terminology-ui/src/modules/concept/index.tsx b/terminology-ui/src/modules/concept/index.tsx index f759fca51..6692c1548 100644 --- a/terminology-ui/src/modules/concept/index.tsx +++ b/terminology-ui/src/modules/concept/index.tsx @@ -192,7 +192,7 @@ export default function Concept({ terminologyId, conceptId }: ConceptProps) { {Object.keys(definitions).length > 0 && ( <BasicBlock title={t('field-definition')}> - <MultilingualBlock data={definitions} /> + <MultilingualBlock data={definitions} renderHtml={true} /> </BasicBlock> )} @@ -204,13 +204,13 @@ export default function Concept({ terminologyId, conceptId }: ConceptProps) { {notes && notes.length > 0 && ( <BasicBlock title={t('field-note')}> - <MultilingualBlockList data={concept?.notes} renderHtml /> + <MultilingualBlockList data={notes} renderHtml /> </BasicBlock> )} {examples && examples.length > 0 && ( <BasicBlock title={t('field-example')}> - <MultilingualBlockList data={concept?.examples} renderHtml /> + <MultilingualBlockList data={examples} renderHtml /> </BasicBlock> )} @@ -238,9 +238,10 @@ export default function Concept({ terminologyId, conceptId }: ConceptProps) { <RemovalModal nonDescriptive={true} - removalData={{ type: 'concept', data: concept }} + dataType="concept" targetId={concept?.identifier ?? ''} targetName={prefLabel} + targetPrefix={terminology?.prefix} /> </EditToolsBlock> </BasicBlockExtraWrapper> diff --git a/terminology-ui/src/modules/terminology-search/index.tsx b/terminology-ui/src/modules/terminology-search/index.tsx index ecb43735b..a08c95245 100644 --- a/terminology-ui/src/modules/terminology-search/index.tsx +++ b/terminology-ui/src/modules/terminology-search/index.tsx @@ -33,7 +33,6 @@ import { import LoadIndicator from 'yti-common-ui/load-indicator'; import { useStoreDispatch } from '@app/store'; import { useSelector } from 'react-redux'; -import getPrefLabel from '@app/common/utils/get-preflabel'; import { translateTerminologyType } from '@app/common/utils/translation-helpers'; import { Description, @@ -98,19 +97,17 @@ export default function TerminologySearch() { contributors: terminology.organizations.map( (c) => organizations.find((o) => o.id === c)?.label ?? '' ), - description: terminology.description - ? getPrefLabel({ - prefLabels: terminology.description, - lang: i18n.language, - }) - : '', + description: getLanguageVersion({ + data: terminology.description, + lang: i18n.language, + }), icon: <IconRegisters />, status: terminology.status, partOf: terminology.groups.map( (d) => groups.find((g) => g.id === d)?.label ?? '' ), - title: getPrefLabel({ - prefLabels: terminology.label, + title: getLanguageVersion({ + data: terminology.label, lang: i18n.language, }), titleLink: `terminology/${terminology.prefix}`, @@ -128,7 +125,7 @@ export default function TerminologySearch() { lang: i18n.language, }), id: concept.identifier, - uri: concept.uri, + uri: `/terminology/${object.prefix}/concept/${concept.identifier}`, }; }); if (concepts.length > 0) { diff --git a/terminology-ui/src/modules/vocabulary/index.tsx b/terminology-ui/src/modules/vocabulary/index.tsx index b1b1dc832..63e4a35fb 100644 --- a/terminology-ui/src/modules/vocabulary/index.tsx +++ b/terminology-ui/src/modules/vocabulary/index.tsx @@ -36,8 +36,6 @@ import HasPermission from '@app/common/utils/has-permission'; import dynamic from 'next/dynamic'; import ConceptImportModal from '@app/common/components/concept-import'; import getPrefLabel from '@app/common/utils/get-preflabel'; -import { getPropertyValue } from '@app/common/components/property-value/get-property-value'; -import { Property } from '@app/common/interfaces/termed-data-types.interface'; import { TitleType, TitleTypeAndStatusWrapper, @@ -129,16 +127,16 @@ export default function Vocabulary({ id }: VocabularyProps) { } return collectionsData.map((collection) => ({ - id: collection.id, - description: getPropertyValue({ - property: collection.properties.definition, - language: urlState.lang !== '' ? urlState.lang : i18n.language, + id: collection.identifier, + description: getLanguageVersion({ + data: collection.description, + lang: urlState.lang !== '' ? urlState.lang : i18n.language, }), - title: getPropertyValue({ - property: collection.properties.prefLabel, - language: urlState.lang !== '' ? urlState.lang : i18n.language, + title: getLanguageVersion({ + data: collection.label, + lang: urlState.lang !== '' ? urlState.lang : i18n.language, }), - titleLink: `${id}/collection/${collection.id}`, + titleLink: `${id}/collection/${collection.identifier}`, type: t('vocabulary-info-collection'), })); }, [collectionsData, t, id, urlState.lang, i18n.language]); @@ -345,46 +343,38 @@ export default function Vocabulary({ id }: VocabularyProps) { } if (collectionsData) { + const regexp = urlState.q ? new RegExp(urlState.q, 'gi') : undefined; + const collectionMembers: { [key: string]: string }[] = collectionsData.map((collection) => { const memberLabels = - collection.references.member?.map((m) => { - const labels: Property[] = - m.references.prefLabelXl - ?.flatMap((label) => label.properties.prefLabel ?? []) - .filter((val) => val) ?? []; - - if (labels) { - return Object.assign( - {}, - labels.reduce( - (obj, item) => ({ - ...obj, - [item.lang]: urlState.q - ? item.value.replaceAll( - urlState.q, - `<b>${urlState.q}</b>` - ) - : item.value, - }), - {} - ) - ); - } - - return []; + collection.members?.map((m) => { + return Object.assign( + {}, + Object.entries(m.label).reduce( + (obj, item) => ({ + ...obj, + [item[0]]: regexp + ? item[1].replaceAll(regexp, `<b>${urlState.q}</b>`) + : item[1], + }), + {} + ) + ); }) ?? []; const membersWithCorrectLabels = memberLabels.map((label) => - getPrefLabel({ - prefLabels: label, + getLanguageVersion({ + data: label, lang: urlState.lang !== '' ? urlState.lang : i18n.language, }) ); if (urlState.q !== '') { const matchingMembers = membersWithCorrectLabels - .filter((value) => value.includes(urlState.q)) + .filter((value) => + value.toLowerCase().includes(urlState.q.toLowerCase()) + ) .slice(0, 5); if ( @@ -394,35 +384,36 @@ export default function Vocabulary({ id }: VocabularyProps) { const diff = membersWithCorrectLabels.length - matchingMembers.length; return { - [collection.id]: `${matchingMembers.join(', ')} + ${diff} ${t( - 'vocabulary-results-more' - )}`, + [collection.identifier]: `${matchingMembers.join( + ', ' + )} + ${diff} ${t('vocabulary-results-more')}`, }; } - return { [collection.id]: matchingMembers.join(', ') }; + return { [collection.identifier]: matchingMembers.join(', ') }; } return { - [collection.id]: membersWithCorrectLabels.slice(0, 5).join(', '), + [collection.identifier]: membersWithCorrectLabels + .slice(0, 5) + .join(', '), }; }); const collectionsExtra = Object.assign({}, ...collectionMembers); - const filteredCollections = - urlState.q !== '' - ? collections.filter((collection) => { - if ( - collection.title.includes(urlState.q) || - collectionsExtra[collection.id] || - collectionsExtra[collection.id] !== '' - ) { - return true; - } - return false; - }) - : collections; + const filteredCollections = regexp + ? collections.filter((collection) => { + if ( + collection.title.match(regexp) || + collectionsExtra[collection.id] || + collectionsExtra[collection.id] !== '' + ) { + return true; + } + return false; + }) + : collections; return ( <> diff --git a/terminology-ui/src/pages/terminology/[terminologyId]/collection/[collectionId].tsx b/terminology-ui/src/pages/terminology/[terminologyId]/collection/[collectionId].tsx index e448afd4c..b6517a030 100644 --- a/terminology-ui/src/pages/terminology/[terminologyId]/collection/[collectionId].tsx +++ b/terminology-ui/src/pages/terminology/[terminologyId]/collection/[collectionId].tsx @@ -21,9 +21,9 @@ import { CommonContextProvider, } from 'yti-common-ui/common-context-provider'; import PageHead from 'yti-common-ui/page-head'; -import { getPropertyValue } from '@app/common/components/property-value/get-property-value'; import { getStoreData } from '@app/common/utils/get-store-data'; import { wrapper } from '@app/store'; +import { getLanguageVersion } from 'yti-common-ui/utils/get-language-version'; interface CollectionPageProps extends CommonContextState { _netI18Next: SSRConfig; @@ -83,7 +83,7 @@ export const getServerSideProps = createCommonGetServerSideProps( await Promise.all(store.dispatch(getVocabularyRunningQueriesThunk())); await Promise.all(store.dispatch(getCollectionRunningQueriesThunk())); - const vocabularyData = getStoreData({ + const terminologyData = getStoreData({ state: store.getState(), reduxKey: 'terminologyAPI', functionKey: 'getTerminology', @@ -95,11 +95,6 @@ export const getServerSideProps = createCommonGetServerSideProps( functionKey: 'getCollection', }); - const vocabularyTitle = getPropertyValue({ - property: vocabularyData?.properties?.prefLabel, - language: locale, - }); - if (!collectionData) { return { redirect: { @@ -109,15 +104,16 @@ export const getServerSideProps = createCommonGetServerSideProps( }; } - const collectionTitle = getPropertyValue({ - property: collectionData?.properties.prefLabel, - language: locale, - }); - return { props: { - collectionTitle: collectionTitle, - vocabularyTitle: vocabularyTitle, + collectionTitle: getLanguageVersion({ + data: collectionData.label, + lang: locale ?? 'fi', + }), + vocabularyTitle: getLanguageVersion({ + data: terminologyData.label, + lang: locale ?? 'fi', + }), }, }; } diff --git a/terminology-ui/src/store/index.tsx b/terminology-ui/src/store/index.tsx index 607bc5171..0834c5fdb 100644 --- a/terminology-ui/src/store/index.tsx +++ b/terminology-ui/src/store/index.tsx @@ -25,7 +25,6 @@ import { accessRequestApi } from '@app/common/components/access-request/access-r import { adminControlsSlice } from '@app/common/components/admin-controls/admin-controls.slice'; import { importApi } from '@app/common/components/import/import.slice'; import { modifyApi } from '@app/common/components/modify/modify.slice'; -import { removeApi } from '@app/common/components/remove/remove.slice'; import { NextApiRequest } from 'next'; import { modifyStatusesApi } from '@app/common/components/modify-statuses/modify-statuses.slice'; import { fakeableUsersApi } from '@app/common/components/fakeable-user/fakeable-user.slice'; @@ -51,7 +50,6 @@ const reducers = { [adminControlsSlice.name]: adminControlsSlice.reducer, [importApi.reducerPath]: importApi.reducer, [modifyApi.reducerPath]: modifyApi.reducer, - [removeApi.reducerPath]: removeApi.reducer, [modifyStatusesApi.reducerPath]: modifyStatusesApi.reducer, [fakeableUsersApi.reducerPath]: fakeableUsersApi.reducer, [codeListApi.reducerPath]: codeListApi.reducer, @@ -77,7 +75,6 @@ export const makeStore: MakeStore<any> = ({ accessRequestApi.middleware, importApi.middleware, modifyApi.middleware, - removeApi.middleware, loginApi.middleware, modifyStatusesApi.middleware, fakeableUsersApi.middleware, From aaeb24428b669cbc4104d4af568ede7d39928b2e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kalle=20Kytt=C3=A4l=C3=A4?= <kalle.kyttala@rebase.fi> Date: Tue, 27 Aug 2024 14:13:20 +0300 Subject: [PATCH 05/35] import concepts NTRF --- terminology-ui/public/locales/en/admin.json | 2 + terminology-ui/public/locales/fi/admin.json | 2 + terminology-ui/public/locales/sv/admin.json | 2 + .../common/components/removal-modal/index.tsx | 1 - .../modules/new-terminology/file-upload.tsx | 76 ++++--------------- 5 files changed, 19 insertions(+), 64 deletions(-) diff --git a/terminology-ui/public/locales/en/admin.json b/terminology-ui/public/locales/en/admin.json index 51c6a9363..0efa5272b 100644 --- a/terminology-ui/public/locales/en/admin.json +++ b/terminology-ui/public/locales/en/admin.json @@ -158,6 +158,7 @@ "incorrect-file-type_other": "Incorrect file type. Allowed file typea are {{fileTypes}}.", "upload-error": "File upload failed. Try again." }, + "finished": "Done", "from-terminology-manual": "Terminology manual.", "give-definition": "Give definition", "has-part-concept": "Has part concept", @@ -168,6 +169,7 @@ "import-concepts": "Import concepts", "import-concepts-description-1": "New concepts can imported with a file. Requirements for the contents of the file can be read from", "import-concepts-description-2": "If you want to update existing concept or other information of the terminology, choose 'Update terminology with a file' in 'Terminology information and action'.", + "import-concepts-in-progress": "Importing concepts", "import-concepts-to-terminology": "Import concepts to terminology", "import-incorrect-excel-file-1": { "concept-import": "File does not match the specifications of the concept import file. Check the specifications of the file in", diff --git a/terminology-ui/public/locales/fi/admin.json b/terminology-ui/public/locales/fi/admin.json index 7bd45b74b..9ca27bb6a 100644 --- a/terminology-ui/public/locales/fi/admin.json +++ b/terminology-ui/public/locales/fi/admin.json @@ -158,6 +158,7 @@ "incorrect-file-type_other": "Väärä tiedostotyyppi. Sallitut tiedostotyypit on {{fileTypes}}.", "upload-error": "Tiedoston lataus ei onnistunut. Kokeile uudelleen." }, + "finished": "Valmis", "from-terminology-manual": "Sanastot-työkalun käyttöohjeista.", "give-definition": "Kirjoita määritelmä", "has-part-concept": "Koostumussuhteinen alakäsite", @@ -168,6 +169,7 @@ "import-concepts": "Tuo käsitteitä", "import-concepts-description-1": "Voit tuoda sanastoon uusia käsitteitä tiedostolla. Lisätietoja vaaditusta tiedoston sisällöstä voit lukea", "import-concepts-description-2": "Mikäli haluat päivittää sanastossa olevia käsitteita tai muita sanaston tietoja, valitse 'Päivitä sanasto tiedostolla' -toiminto 'Sanaston tiedot ja toiminnot' -näkymästä.", + "import-concepts-in-progress": "Tuodaan käsitteitä", "import-concepts-to-terminology": "Tuo käsitteitä sanastoon", "import-incorrect-excel-file-1": { "concept-import": "Tiedosto ei vastaa käsitteiden tuontiin tarkoitetun tiedoston määrityksiä. Tarkista tiedoston määritykset", diff --git a/terminology-ui/public/locales/sv/admin.json b/terminology-ui/public/locales/sv/admin.json index 7dd361645..ed3a22377 100644 --- a/terminology-ui/public/locales/sv/admin.json +++ b/terminology-ui/public/locales/sv/admin.json @@ -158,6 +158,7 @@ "incorrect-file-type_other": "Fel filtyp. Tillåtna filtyper är {{fileTypes}}.", "upload-error": "Laddning av filen gick fel. Försök igen." }, + "finished": "Färdig", "from-terminology-manual": "från Ordlistor-verktygets bruksanvisningar.", "give-definition": "Ange definition", "has-part-concept": "Delbegrepp", @@ -168,6 +169,7 @@ "import-concepts": "Importera begrepp", "import-concepts-description-1": "Du kan lägga nya begrepp till en ordlista med en fil. Mer information om önskat filinnehåll kan du läsa ", "import-concepts-description-2": "Om du vill uppdatera ordlistas begrepp eller andra uppgifter med en fil, välj \"Uppdatera ordlistan med en fil\" på Ordlistans uppgifter och funktioner -vyn", + "import-concepts-in-progress": "Importering", "import-concepts-to-terminology": "Importera begreppen till ordlistan", "import-incorrect-excel-file-1": { "concept-import": "Filen uppfyller inte formateringskrav för en importerad fil. Var god och kolla filens specifikationer i bruksanvisningarna.", diff --git a/terminology-ui/src/common/components/removal-modal/index.tsx b/terminology-ui/src/common/components/removal-modal/index.tsx index 314b0c8bc..762f709dc 100644 --- a/terminology-ui/src/common/components/removal-modal/index.tsx +++ b/terminology-ui/src/common/components/removal-modal/index.tsx @@ -258,7 +258,6 @@ export default function RemovalModal({ } if (isSuccess()) { - console.info('success'); return ( <> <Button onClick={() => handleReturn()}> diff --git a/terminology-ui/src/modules/new-terminology/file-upload.tsx b/terminology-ui/src/modules/new-terminology/file-upload.tsx index d6f40e3c4..f1a6cb9e1 100644 --- a/terminology-ui/src/modules/new-terminology/file-upload.tsx +++ b/terminology-ui/src/modules/new-terminology/file-upload.tsx @@ -2,11 +2,9 @@ import { ExcelError, ExcelErrorDetailBlock, } from '@app/common/components/import/excel.error'; -import { useGetImportStatusMutation } from '@app/common/components/import/import.slice'; import { ImportResponse } from '@app/common/interfaces/import.interface'; import { translateExcelParseError } from '@app/common/utils/translation-helpers'; import { useTranslation } from 'next-i18next'; -import { useEffect, useState } from 'react'; import { Button, ExternalLink, @@ -16,10 +14,10 @@ import { } from 'suomifi-ui-components'; import { ButtonBlock, - DownloadIndicator, FileUploadWrapper, SuccessIndicator, } from './new-terminology.styles'; +import SaveSpinner from 'yti-common-ui/save-spinner'; interface FileUploadProps { importResponseData?: ImportResponse; @@ -30,50 +28,20 @@ interface FileUploadProps { } export default function FileUpload({ - importResponseData, importResponseStatus, handlePost, handleClose, errorInfo, }: FileUploadProps) { const { t } = useTranslation('admin'); - const [fetchImportStatus, importStatus] = useGetImportStatusMutation(); - const [getStatusRetries, setGetStatusRetries] = useState(0); const handleTryAgain = () => { - setGetStatusRetries(0); handlePost(); }; - useEffect(() => { - if (getStatusRetries > 9) { - return; - } - - if (importStatus.isError) { - setGetStatusRetries(getStatusRetries + 1); - } - - if ( - (importResponseData?.jobtoken || importResponseData?.jobToken) && - !importStatus.isLoading && - importStatus.data?.status !== 'SUCCESS' - ) { - const timerId = setTimeout(() => { - if (importResponseData.jobtoken) { - fetchImportStatus(importResponseData.jobtoken); - } - if (importResponseData.jobToken) { - fetchImportStatus(importResponseData.jobToken); - } - }, 1000); - return () => clearTimeout(timerId); - } - }, [importResponseData, fetchImportStatus, importStatus, getStatusRetries]); - return ( <> - {importResponseStatus === 'rejected' || getStatusRetries > 3 ? ( + {importResponseStatus === 'rejected' ? ( <> <InlineAlert status="error" style={{ marginBottom: '25px' }}> {errorInfo ? ( @@ -115,37 +83,19 @@ export default function FileUpload({ ) : ( <> <FileUploadWrapper> - {importStatus.data?.status.toLowerCase() === 'success' ? ( - <> - <SuccessIndicator color="white" /> - <Text variant="bold">{t('percent-done', { count: 100 })}</Text> - </> - ) : ( - <> - <DownloadIndicator /> - <Text variant="bold"> - {t('percent-done', { - count: - importStatus.data?.processingProgress !== undefined && - importStatus.data?.processingTotal !== undefined && - importStatus.data?.processingTotal !== 0 - ? Math.floor( - (importStatus.data?.processingProgress / - importStatus.data?.processingTotal) * - 100 - ) - : 0, - })} - </Text> - </> - )} + <> + {importResponseStatus !== 'fulfilled' ? ( + <SaveSpinner text={t('import-concepts-in-progress')} /> + ) : ( + <> + <SuccessIndicator color="white" /> + <Text variant="bold">{t('finished')}</Text> + </> + )} + </> </FileUploadWrapper> <Button - disabled={ - importStatus.data?.status.toLowerCase() === 'success' - ? false - : true - } + disabled={importResponseStatus !== 'fulfilled'} onClick={() => handleClose()} > {t('close')} From 9d1c04427d48e1542bb62f52df4a2f0b5abb8019 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kalle=20Kytt=C3=A4l=C3=A4?= <kalle.kyttala@rebase.fi> Date: Wed, 28 Aug 2024 15:41:26 +0300 Subject: [PATCH 06/35] collection add and edit --- .../public/locales/en/collection.json | 2 + .../public/locales/fi/collection.json | 2 + .../public/locales/sv/collection.json | 2 + .../collection/collection.slice.tsx | 45 +++- .../components/concept/concept.slice.tsx | 46 ++-- .../src/common/interfaces/interfaces-v2.ts | 28 ++- terminology-ui/src/common/utils/namespace.ts | 5 + .../expander-concept-content.tsx | 55 ++--- .../edit-collection/concept-picker/index.tsx | 13 +- .../concept-picker/picker-modal.tsx | 80 +++---- .../edit-collection.styles.tsx | 2 +- .../edit-collection/edit-collection.types.tsx | 22 +- .../src/modules/edit-collection/index.tsx | 201 +++++++++--------- .../collection/[collectionId]/edit.tsx | 11 +- 14 files changed, 278 insertions(+), 236 deletions(-) create mode 100644 terminology-ui/src/common/utils/namespace.ts diff --git a/terminology-ui/public/locales/en/collection.json b/terminology-ui/public/locales/en/collection.json index 98a6260ed..9b89348bd 100644 --- a/terminology-ui/public/locales/en/collection.json +++ b/terminology-ui/public/locales/en/collection.json @@ -18,9 +18,11 @@ "prefLabel": "Collection name must be defined in at least one of the available languages." }, "enter-collection-description": "Enter a definition", + "enter-collection-identifier": "Enter a identifier", "enter-collection-name": "Enter a name", "enter-search-term": "Enter search term", "field-definition": "Definition", + "field-identifier": "Identifier", "field-member": "Concepts in the collection", "field-name": "Name", "heading": "Collection", diff --git a/terminology-ui/public/locales/fi/collection.json b/terminology-ui/public/locales/fi/collection.json index cdb17cf04..e488cf207 100644 --- a/terminology-ui/public/locales/fi/collection.json +++ b/terminology-ui/public/locales/fi/collection.json @@ -18,9 +18,11 @@ "prefLabel": "Käsitekokoelman nimi tulee olla määritettynä vähintään yhdellä kielellä." }, "enter-collection-description": "Kirjoita kuvaus", + "enter-collection-identifier": "Kirjoita tunnus", "enter-collection-name": "Kirjoita nimi", "enter-search-term": "Kirjoita hakusana", "field-definition": "Kuvaus", + "field-identifier": "Tunnus", "field-member": "Valikoimaan kuuluvat käsitteet", "field-name": "Nimi", "heading": "Käsitekokoelma", diff --git a/terminology-ui/public/locales/sv/collection.json b/terminology-ui/public/locales/sv/collection.json index 32f25b1cd..d30dc3c84 100644 --- a/terminology-ui/public/locales/sv/collection.json +++ b/terminology-ui/public/locales/sv/collection.json @@ -18,9 +18,11 @@ "prefLabel": "Namnet för begreppsurval måste anges åtminstone på ett språk." }, "enter-collection-description": "Ange en beskrivning", + "enter-collection-identifier": "Ange en Identifierare", "enter-collection-name": "Ange ett namn", "enter-search-term": "Ange ett sökord", "field-definition": "Definition", + "field-identifier": "Identifierare", "field-member": "Begrepp, som hör till urvalet", "field-name": "Namn", "heading": "Begreppsurval", diff --git a/terminology-ui/src/common/components/collection/collection.slice.tsx b/terminology-ui/src/common/components/collection/collection.slice.tsx index 84ea8b96f..52bc8eb6f 100644 --- a/terminology-ui/src/common/components/collection/collection.slice.tsx +++ b/terminology-ui/src/common/components/collection/collection.slice.tsx @@ -1,6 +1,10 @@ import { createApi } from '@reduxjs/toolkit/query/react'; import { getTerminologyApiBaseQuery } from '@app/store/api-base-query'; -import { ConceptCollectionInfo } from '@app/common/interfaces/interfaces-v2'; +import { + ConceptCollection, + ConceptCollectionInfo, +} from '@app/common/interfaces/interfaces-v2'; +import { terminologyApi } from '../vocabulary/vocabulary.slice'; export const collectionApi = createApi({ reducerPath: 'collectionAPI', @@ -22,6 +26,33 @@ export const collectionApi = createApi({ method: 'GET', }), }), + addCollection: builder.mutation< + null, + { + terminologyId: string; + payload: ConceptCollection; + } + >({ + query: (data) => ({ + url: `/collection/${data.terminologyId}`, + method: 'POST', + data: data.payload, + }), + }), + updateCollection: builder.mutation< + null, + { + terminologyId: string; + collectionId: string; + payload: ConceptCollection; + } + >({ + query: (data) => ({ + url: `/collection/${data.terminologyId}/${data.collectionId}`, + method: 'PUT', + data: data.payload, + }), + }), deleteCollection: builder.mutation< null, { prefix: string; collectionId: string } @@ -31,12 +62,24 @@ export const collectionApi = createApi({ method: 'DELETE', }), }), + collectionExists: builder.mutation< + boolean, + { terminologyId: string; collectionId: string } + >({ + query: (params) => ({ + url: `/collection/${params.terminologyId}/${params.collectionId}/exists`, + method: 'GET', + }), + }), }), }); export const { useGetCollectionQuery, useGetCollectionsQuery, + useAddCollectionMutation, + useUpdateCollectionMutation, + useCollectionExistsMutation, useDeleteCollectionMutation, util: { getRunningQueriesThunk }, } = collectionApi; diff --git a/terminology-ui/src/common/components/concept/concept.slice.tsx b/terminology-ui/src/common/components/concept/concept.slice.tsx index 6c204a127..5344ed9e4 100644 --- a/terminology-ui/src/common/components/concept/concept.slice.tsx +++ b/terminology-ui/src/common/components/concept/concept.slice.tsx @@ -1,7 +1,12 @@ import { createApi } from '@reduxjs/toolkit/query/react'; import { Concepts } from '@app/common/interfaces/concepts.interface'; import { getTerminologyApiBaseQuery } from '@app/store/api-base-query'; -import { ConceptInfo } from '@app/common/interfaces/interfaces-v2'; +import { + ConceptInfo, + ConceptResponseObject, + ConceptSearchRequest, + SearchResponse, +} from '@app/common/interfaces/interfaces-v2'; export const conceptApi = createApi({ reducerPath: 'conceptAPI', @@ -27,40 +32,13 @@ export const conceptApi = createApi({ }), }), searchConcept: builder.mutation< - { - concepts: Concepts[]; - resultStart: number; - totalHitCount: number; - }, - { - terminologyId?: string; - query?: string; - notInTerminologyId?: string; - status?: string; - pageFrom?: number; - pageSize?: number; - } + SearchResponse<ConceptResponseObject>, + ConceptSearchRequest >({ - query: (props) => ({ - url: '/searchConcept', - method: 'POST', - data: { - highlight: true, - ...(props.notInTerminologyId && { - notInTerminologyId: [props.notInTerminologyId], - }), - pageFrom: props.pageFrom ?? 0, - pageSize: props.pageSize ?? 100, - ...(props.query && { query: props.query }), - sortDirection: 'ASC', - sortLanguage: 'fi', - ...(props.status && { - status: [props.status], - }), - ...(props.terminologyId && { - terminologyId: [props.terminologyId], - }), - }, + query: (request) => ({ + url: '/frontend/search-concepts', + method: 'GET', + params: request, }), }), }), diff --git a/terminology-ui/src/common/interfaces/interfaces-v2.ts b/terminology-ui/src/common/interfaces/interfaces-v2.ts index cc057ec9a..23b8772a5 100644 --- a/terminology-ui/src/common/interfaces/interfaces-v2.ts +++ b/terminology-ui/src/common/interfaces/interfaces-v2.ts @@ -77,9 +77,9 @@ export interface Term { } export interface ConceptReferenceInfo { - conceptURI: string; - prefix: string; identifier: string; + referenceURI: string; + prefix: string; label: LocalizedValue; } @@ -95,6 +95,13 @@ export interface ConceptCollectionInfo { modifier: UserMeta; } +export interface ConceptCollection { + identifier: string; + label: LocalizedValue; + description: LocalizedValue; + members: string[]; +} + /* build fails for empty interfaces export interface TerminologyInfo {} @@ -103,27 +110,26 @@ export interface TerminologyInfo {} export interface ConceptInfo {} -export interface ConceptCollection {} */ export interface SearchRequest { - query: string; - sortLang: string; - status: Status[]; - pageSize: number; - pageFrom: number; + query?: string; + sortLang?: string; + status?: Status[] | string[]; + pageSize?: number; + pageFrom?: number; } export interface TerminologySearchRequest extends SearchRequest { - searchconcepts: boolean; + searchConcepts: boolean; groups: string[]; organizations: string[]; languages: string[]; } export interface ConceptSearchRequest extends SearchRequest { - namespace: string; + namespace?: string; } export interface ResponseObject { @@ -131,6 +137,8 @@ export interface ResponseObject { id: string; label: LocalizedValue; status: Status; + created: string; + modified: string; } export interface TerminogyResponseObject extends ResponseObject { diff --git a/terminology-ui/src/common/utils/namespace.ts b/terminology-ui/src/common/utils/namespace.ts new file mode 100644 index 000000000..297a40174 --- /dev/null +++ b/terminology-ui/src/common/utils/namespace.ts @@ -0,0 +1,5 @@ +export const NAMESPACE_ROOT = 'https://iri.suomi.fi/terminology/'; + +export function getNamespace(terminologyId: string) { + return `${NAMESPACE_ROOT}${terminologyId}/`; +} diff --git a/terminology-ui/src/modules/edit-collection/concept-picker/expander-concept-content.tsx b/terminology-ui/src/modules/edit-collection/concept-picker/expander-concept-content.tsx index fdd09b054..58b3bd86c 100644 --- a/terminology-ui/src/modules/edit-collection/concept-picker/expander-concept-content.tsx +++ b/terminology-ui/src/modules/edit-collection/concept-picker/expander-concept-content.tsx @@ -1,23 +1,24 @@ -import { BasicBlock } from 'yti-common-ui/block'; -import { MultilingualPropertyBlock } from '@app/common/components/block'; +import { BasicBlock, MultilingualBlock } from 'yti-common-ui/block'; import { useGetConceptQuery } from '@app/common/components/concept/concept.slice'; import FormattedDate from 'yti-common-ui/formatted-date'; import Separator from 'yti-common-ui/separator'; import { useGetTerminologyQuery } from '@app/common/components/vocabulary/vocabulary.slice'; import { useTranslation } from 'next-i18next'; import { ExpanderContent } from 'suomifi-ui-components'; -import { ExpanderConceptContent as ExpanderConceptContentType } from './concept-picker.types'; -import { compareLocales } from '@app/common/utils/compare-locals'; import { getLanguageVersion } from 'yti-common-ui/utils/get-language-version'; +import { ConceptResponseObject } from '@app/common/interfaces/interfaces-v2'; export function ExpanderConceptContent({ concept, terminologyId, -}: ExpanderConceptContentType) { +}: { + concept: ConceptResponseObject; + terminologyId: string; +}) { const { t, i18n } = useTranslation('collection'); const { data } = useGetConceptQuery({ terminologyId: terminologyId, - conceptId: concept.id, + conceptId: concept.identifier, }); const { data: terminologyData } = useGetTerminologyQuery({ id: terminologyId, @@ -25,42 +26,32 @@ export function ExpanderConceptContent({ return ( <ExpanderContent> - <MultilingualPropertyBlock - title={t('recommended-terms')} - data={Object.keys(concept.label) - .map((key) => ({ - lang: key, - regex: '(?s)^.*$', - value: concept.label[key], - })) - .sort(compareLocales)} - /> + <BasicBlock title={t('recommended-terms')}> + <MultilingualBlock data={concept.label} /> + </BasicBlock> {concept.definition && ( - <MultilingualPropertyBlock - title={t('definition')} - data={Object.keys(concept.definition) - .map((key) => ({ - lang: key, - regex: '(?s)^.*$', - value: concept.definition[key], - })) - .sort(compareLocales)} - /> + <BasicBlock title={t('definition')}> + <MultilingualBlock data={concept.definition} /> + </BasicBlock> )} <Separator isLarge /> <BasicBlock title={t('admin-organization')}> - {getLanguageVersion({ - data: terminologyData?.label, - lang: i18n.language, - })} + {terminologyData?.organizations + .map((organization) => + getLanguageVersion({ + data: organization.label, + lang: i18n.language, + }) + ) + .join(', ')} </BasicBlock> <BasicBlock title={t('last-modified')}> - <FormattedDate date={concept.modified} /> - {/*data?.lastModifiedBy && `, ${data?.lastModifiedBy}`*/} + <FormattedDate date={data?.modified} /> + {data?.modifier.name && `, ${data?.modifier.name}`} </BasicBlock> </ExpanderContent> ); diff --git a/terminology-ui/src/modules/edit-collection/concept-picker/index.tsx b/terminology-ui/src/modules/edit-collection/concept-picker/index.tsx index c083b2aa5..ece4c8c11 100644 --- a/terminology-ui/src/modules/edit-collection/concept-picker/index.tsx +++ b/terminology-ui/src/modules/edit-collection/concept-picker/index.tsx @@ -5,6 +5,7 @@ import { Button, Chip } from 'suomifi-ui-components'; import { EditCollectionFormDataType } from '../edit-collection.types'; import { SelectedConceptBlock } from './concept-picker.styles'; import PickerModal from './picker-modal'; +import { getLanguageVersion } from 'yti-common-ui/utils/get-language-version'; interface ConceptPickerProps { concepts: EditCollectionFormDataType['concepts']; @@ -59,16 +60,16 @@ export default function ConceptPicker({ <SelectedConceptBlock id="selected-concept-chips-block"> {concepts.map((concept) => ( <Chip - key={`concept-${concept.id}`} + key={`concept-${concept.identifier}`} onClick={() => - onChange(concepts.filter((c) => c.id !== concept.id)) + onChange(concepts.filter((c) => c.uri !== concept.uri)) } removable > - {concept.prefLabels[i18n.language] ?? - concept.prefLabels['fi'] ?? - concept.prefLabels[Object.keys(concept.prefLabels)[0]] ?? - ''} + {getLanguageVersion({ + data: concept.label, + lang: i18n.language, + })} </Chip> ))} </SelectedConceptBlock> diff --git a/terminology-ui/src/modules/edit-collection/concept-picker/picker-modal.tsx b/terminology-ui/src/modules/edit-collection/concept-picker/picker-modal.tsx index 651f8754b..ca2efeee2 100644 --- a/terminology-ui/src/modules/edit-collection/concept-picker/picker-modal.tsx +++ b/terminology-ui/src/modules/edit-collection/concept-picker/picker-modal.tsx @@ -23,7 +23,10 @@ import { SearchInput, Text, } from 'suomifi-ui-components'; -import { EditCollectionFormDataType } from '../edit-collection.types'; +import { + CollectionMember, + EditCollectionFormDataType, +} from '../edit-collection.types'; import { FooterButton, ResultBlock, @@ -35,6 +38,12 @@ import { import { PickerModalProps, SelectedConceptProps } from './concept-picker.types'; import { ExpanderConceptContent } from './expander-concept-content'; import { DetachedPagination } from 'yti-common-ui/pagination'; +import { + ConceptResponseObject, + SearchResponse, +} from '@app/common/interfaces/interfaces-v2'; +import { getNamespace } from '@app/common/utils/namespace'; +import { getLanguageVersion } from 'yti-common-ui/utils/get-language-version'; export default function PickerModal({ setVisible, @@ -46,12 +55,13 @@ export default function PickerModal({ const { isSmall } = useBreakpoints(); const [searchConcept, result] = useSearchConceptMutation(); const [selectedConcepts, setSelectedConcepts] = - useState<EditCollectionFormDataType['concepts']>(orgConcepts); + useState<CollectionMember[]>(orgConcepts); const [showSelected, setShowSelected] = useState(false); const [searchTerm, setSearchTerm] = useState(''); const [status, setStatus] = useState<string>('ALL-STATUSES'); const [totalResults, setTotalResults] = useState(0); - const [searchResults, setSearchResults] = useState<Concepts[]>([]); + const [searchResults, setSearchResults] = + useState<SearchResponse<ConceptResponseObject>>(); const [currPage, setCurrPage] = useState(1); const modalRef = createRef<HTMLDivElement>(); @@ -98,7 +108,10 @@ export default function PickerModal({ setVisible(false); }; - const handleCheckbox = (checkboxState: boolean, concept: Concepts) => { + const handleCheckbox = ( + checkboxState: boolean, + concept: ConceptResponseObject + ) => { if (checkboxState) { let label = concept.label; @@ -113,18 +126,21 @@ export default function PickerModal({ setSelectedConcepts([ ...selectedConcepts, { - id: concept.id, - prefLabels: label, + uri: concept.uri, + identifier: concept.identifier, + label, }, ]); } else { - setSelectedConcepts(selectedConcepts.filter((c) => c.id !== concept.id)); + setSelectedConcepts( + selectedConcepts.filter((c) => c.uri !== concept.uri) + ); } }; - const handleDeselect = (id: string) => { + const handleDeselect = (uri: string) => { const updatedConcepts = selectedConcepts.filter( - (concept) => concept.id !== id + (concept) => concept.uri !== uri ); setSelectedConcepts(updatedConcepts); @@ -139,25 +155,25 @@ export default function PickerModal({ const handleSearch = () => { searchConcept({ - terminologyId: terminologyId, + namespace: getNamespace(terminologyId), query: searchTerm, - status: status !== 'ALL-STATUSES' ? status : undefined, + status: status !== 'ALL-STATUSES' ? [status] : undefined, pageFrom: (currPage - 1) * 20, pageSize: 20, }); }; const handleClear = () => { - searchConcept({ terminologyId: terminologyId }); + searchConcept({ namespace: getNamespace(terminologyId) }); setSearchTerm(''); }; const handlePageChange = (num: number) => { setCurrPage(num); searchConcept({ - terminologyId: terminologyId, + namespace: getNamespace(terminologyId), query: searchTerm, - status: status !== 'ALL-STATUSES' ? status : undefined, + status: status !== 'ALL-STATUSES' ? [status] : undefined, pageFrom: (num - 1) * 20, pageSize: 20, }); @@ -172,12 +188,12 @@ export default function PickerModal({ useEffect(() => { if (result.isUninitialized) { - setSearchResults([]); + setSearchResults(undefined); setTotalResults(0); } if (result.isSuccess) { - setSearchResults(result.data.concepts); + setSearchResults(result.data); setTotalResults(result.data.totalHitCount); } }, [result, setTotalResults]); @@ -259,7 +275,7 @@ export default function PickerModal({ openAllText="" showToggleAllButton={false} > - {searchResults.map((concept) => { + {searchResults?.responseObjects.map((concept) => { return ( <Expander key={`concept-${concept.id}`}> <ExpanderTitle @@ -274,31 +290,22 @@ export default function PickerModal({ hintText={`${translateStatus( concept.status, t - )} \u00B7 ${ - concept.terminology.label[i18n.language] ?? - concept.terminology.label.fi ?? - concept.terminology.label[ - Object.keys(concept.terminology.label)[0] - ] ?? - '' - }`} + )} \u00B7 TODO: terminology label`} id={`checkbox-id-${concept.id}`} onClick={(e) => handleCheckbox(e.checkboxState, concept) } defaultChecked={selectedConcepts - .map((c) => c.id) - .includes(concept.id)} + .map((c) => c.uri) + .includes(concept.uri)} className="search-result-checkbox" variant={isSmall ? 'large' : 'small'} > <SanitizedTextContent - text={ - concept.label[i18n.language] ?? - concept.label.fi ?? - concept.label[Object.keys(concept.label)[0]] ?? - '' - } + text={getLanguageVersion({ + data: concept.label, + lang: i18n.language, + })} /> </Checkbox> </ExpanderTitle> @@ -365,13 +372,10 @@ function SelectedConcepts({ return ( <Chip key={`selected-concept-${idx}`} - onClick={() => deselect(concept.id)} + onClick={() => deselect(concept.uri)} removable > - {concept.prefLabels[i18n.language] ?? - concept.prefLabels.fi ?? - concept.prefLabels[Object.keys(concept.prefLabels)[0]] ?? - ''} + {getLanguageVersion({ data: concept.label, lang: i18n.language })} </Chip> ); })} diff --git a/terminology-ui/src/modules/edit-collection/edit-collection.styles.tsx b/terminology-ui/src/modules/edit-collection/edit-collection.styles.tsx index 2570fd31e..2d4d6fbe4 100644 --- a/terminology-ui/src/modules/edit-collection/edit-collection.styles.tsx +++ b/terminology-ui/src/modules/edit-collection/edit-collection.styles.tsx @@ -22,7 +22,7 @@ export const TextBlockWrapper = styled(Block)` } `; -export const NameTextInput = styled(TextInput)` +export const CollectionTextInput = styled(TextInput)` max-width: 500px; width: 100%; `; diff --git a/terminology-ui/src/modules/edit-collection/edit-collection.types.tsx b/terminology-ui/src/modules/edit-collection/edit-collection.types.tsx index d8891e553..ac90d0623 100644 --- a/terminology-ui/src/modules/edit-collection/edit-collection.types.tsx +++ b/terminology-ui/src/modules/edit-collection/edit-collection.types.tsx @@ -1,3 +1,5 @@ +import { LocalizedValue } from '@app/common/interfaces/interfaces-v2'; + export interface EditCollectionProps { terminologyId: string; collectionName: string; @@ -9,6 +11,19 @@ export interface EditCollectionProps { }; } +export interface CollectionMember { + uri: string; + identifier: string; + label: LocalizedValue; +} + +export interface CollectionFormData { + identifier: string; + label: LocalizedValue; + description: LocalizedValue; + members: CollectionMember[]; +} + export interface EditCollectionFormDataType { name: { lang: string; @@ -19,10 +34,9 @@ export interface EditCollectionFormDataType { value: string; }[]; concepts: { - id: string; - prefLabels: { - [key: string]: string; - }; + uri: string; + identifier: string; + label: LocalizedValue; }[]; } diff --git a/terminology-ui/src/modules/edit-collection/index.tsx b/terminology-ui/src/modules/edit-collection/index.tsx index 43f91b3b2..0fe1c40aa 100644 --- a/terminology-ui/src/modules/edit-collection/index.tsx +++ b/terminology-ui/src/modules/edit-collection/index.tsx @@ -1,29 +1,32 @@ import { Breadcrumb, BreadcrumbLink } from 'yti-common-ui/breadcrumb'; -import { useAddCollectionMutation } from '@app/common/components/modify/modify.slice'; import Separator from 'yti-common-ui/separator'; import { BadgeBar, MainTitle, SubTitle } from 'yti-common-ui/title-block'; import { useGetTerminologyQuery } from '@app/common/components/vocabulary/vocabulary.slice'; import { useTranslation } from 'next-i18next'; import { useRouter } from 'next/router'; import { useEffect, useState } from 'react'; -import { Button, Heading, InlineAlert } from 'suomifi-ui-components'; +import { Button, Heading, InlineAlert, TextInput } from 'suomifi-ui-components'; import ConceptPicker from './concept-picker'; -import generateCollection from './generate-collection'; import { ButtonBlock, DescriptionTextarea, FooterBlock, - NameTextInput, + CollectionTextInput, NewCollectionBlock, PageHelpText, TextBlockWrapper, } from './edit-collection.styles'; import { - EditCollectionFormDataType, + CollectionFormData, + CollectionMember, EditCollectionProps, } from './edit-collection.types'; -import { useGetCollectionQuery } from '@app/common/components/collection/collection.slice'; -import { Collection } from '@app/common/interfaces/collection.interface'; +import { + useAddCollectionMutation, + useCollectionExistsMutation, + useGetCollectionQuery, + useUpdateCollectionMutation, +} from '@app/common/components/collection/collection.slice'; import { translateLanguage } from '@app/common/utils/translation-helpers'; import { TEXT_INPUT_MAX, TEXT_AREA_MAX } from 'yti-common-ui/utils/constants'; import useConfirmBeforeLeavingPage from 'yti-common-ui/utils/hooks/use-confirm-before-leaving-page'; @@ -36,6 +39,7 @@ import { } from '@app/common/components/login/login.slice'; import { compareLocales } from '@app/common/utils/compare-locals'; import { getLanguageVersion } from 'yti-common-ui/utils/get-language-version'; +import { ConceptCollectionInfo } from '@app/common/interfaces/interfaces-v2'; export default function EditCollection({ terminologyId, @@ -68,45 +72,46 @@ export default function EditCollection({ useGetAuthenticatedUserMutMutation(); const [addCollection, result] = useAddCollectionMutation(); - const [newCollectionId, setNewCollectionId] = useState(''); + const [updateCollection, updateResult] = useUpdateCollectionMutation(); + const [emptyError, setEmptyError] = useState(false); const [isCreating, setIsCreating] = useState(false); const languages = terminology?.languages?.slice().sort((a, b) => compareLocales(a, b)) ?? []; - const [formData, setFormData] = useState<EditCollectionFormDataType>( + const [formData, setFormData] = useState<CollectionFormData>( setInitialData(collection) ); const { enableConfirmation, disableConfirmation } = useConfirmBeforeLeavingPage('disabled'); + const [checkCollectionExists, exists] = useCollectionExistsMutation(); + useEffect(() => { if (result.isSuccess) { router.replace( - `/terminology/${terminologyId}/collection/${newCollectionId}` + `/terminology/${terminologyId}/collection/${formData.identifier}` + ); + } else if (updateResult.isSuccess) { + router.replace( + `/terminology/${terminologyId}/collection/${collectionInfo?.collectionId}` ); } - }, [result, router, terminologyId, newCollectionId]); + }, [result, updateResult, router, terminologyId]); + + const setIdentifier = (value: string) => { + const data = formData; + Object.assign(data, { identifier: value }); + + setFormData(data); + enableConfirmation(); + }; const setName = (language: string, value: string) => { const data = formData; - if (data.name.some((n) => n.lang === language)) { - data.name = data.name.map((n) => { - if (n.lang === language) { - return { - lang: n.lang, - value: value, - }; - } - return n; - }); - } else { - data.name.push({ - lang: language, - value: value, - }); - } + Object.assign(data, { label: { ...data.label, [language]: value } }); + setFormData(data); enableConfirmation(); @@ -117,33 +122,17 @@ export default function EditCollection({ const setDescription = (language: string, value: string) => { const data = formData; - - if (data.definition.some((d) => d.lang === language)) { - data.definition = data.definition.map((d) => { - if (d.lang === language) { - return { - lang: d.lang, - value: value, - }; - } - return d; - }); - } else { - data.definition.push({ - lang: language, - value: value, - }); - } + Object.assign(data, { + description: { ...data.description, [language]: value }, + }); setFormData(data); enableConfirmation(); }; - const setFormConcepts = ( - concepts: EditCollectionFormDataType['concepts'] - ) => { + const setFormConcepts = (concepts: CollectionMember[]) => { const data = { ...formData }; - data.concepts = concepts; + data.members = concepts; setFormData(data); enableConfirmation(); }; @@ -151,21 +140,33 @@ export default function EditCollection({ const handleClick = () => { getAuthenticatedMutUser(); - if (formData.name.every((n) => !n.value)) { + const payload = Object.assign( + {}, + { + ...formData, + members: formData.members.map((member) => member.identifier), + } + ); + + if (Object.keys(formData.label).every((n) => !formData.label[n])) { setEmptyError(true); return; } - disableConfirmation(); setIsCreating(true); - const data = generateCollection( - formData, - terminologyId, - `${authenticatedUser?.firstName} ${authenticatedUser?.lastName}`, - collectionInfo - ); - setNewCollectionId(collectionInfo?.collectionId ?? data[0].id); - addCollection(data); + + if (collectionInfo?.collectionId) { + updateCollection({ + collectionId: collectionInfo.collectionId, + terminologyId, + payload: Object.assign(payload, { identifier: null }), + }); + } else { + addCollection({ + terminologyId, + payload, + }); + } }; const handleCancel = () => { @@ -218,8 +219,26 @@ export default function EditCollection({ <Heading variant="h3">{t('collection-basic-information')}</Heading> <TextBlockWrapper id="collection-text-info-block"> + <CollectionTextInput + labelText={t('field-identifier')} + visualPlaceholder={t('enter-collection-identifier')} + defaultValue={collection?.identifier ?? ''} + disabled={!!collectionInfo} + onChange={(e) => setIdentifier(e?.toString() ?? '')} + status={exists.data ? 'error' : 'default'} + onBlur={() => + checkCollectionExists({ + terminologyId, + collectionId: formData.identifier, + }) + } + statusText={exists.data ? t('prefix-taken', { ns: 'admin' }) : ''} + id="prefix-input" + maxLength={100} + /> + {languages.map((language) => ( - <NameTextInput + <CollectionTextInput key={`name-input-${language}`} labelText={`${t('field-name')}, ${translateLanguage( language, @@ -228,9 +247,7 @@ export default function EditCollection({ visualPlaceholder={t('enter-collection-name')} onBlur={(e) => setName(language, e.target.value.trim())} status={emptyError ? 'error' : 'default'} - defaultValue={ - formData.name.find((n) => n.lang === language)?.value - } + defaultValue={formData.label[language]} maxLength={TEXT_INPUT_MAX} className="collection-name-input" /> @@ -246,9 +263,7 @@ export default function EditCollection({ optionalText={t('optional', { ns: 'admin' })} visualPlaceholder={t('enter-collection-description')} onBlur={(e) => setDescription(language, e.target.value.trim())} - defaultValue={ - formData.definition.find((n) => n.lang === language)?.value - } + defaultValue={formData.description[language]} maxLength={TEXT_AREA_MAX} className="collection-description-input" /> @@ -258,7 +273,7 @@ export default function EditCollection({ <Separator isLarge /> <ConceptPicker - concepts={formData.concepts} + concepts={formData.members} terminologyId={terminologyId} onChange={setFormConcepts} /> @@ -304,51 +319,27 @@ export default function EditCollection({ </> ); - function setInitialData(collection?: Collection) { + function setInitialData(collection?: ConceptCollectionInfo) { if (collection) { return { - name: collection.properties.prefLabel - ? collection.properties.prefLabel.map((l) => ({ - lang: l.lang, - value: l.value.trim(), - })) - : [], - definition: collection.properties.definition - ? collection.properties.definition.map((d) => ({ - lang: d.lang, - value: d.value.trim(), - })) - : [], - concepts: collection.references.member - ? collection.references.member.map((m) => { - const prefLabels = new Map(); - - m.references.prefLabelXl?.map((label) => { - prefLabels.set( - label.properties.prefLabel?.[0].lang, - label.properties.prefLabel?.[0].value - ); - }); - - return { - id: m.id, - prefLabels: Object.fromEntries(prefLabels), - }; - }) - : [], + identifier: collection.identifier, + label: collection.label, + description: collection.description, + members: collection.members.map((member) => { + return { + uri: member.referenceURI, + identifier: member.identifier, + label: member.label, + }; + }), }; } return { - name: languages.map((language) => ({ - lang: language, - value: '', - })), - definition: languages.map((language) => ({ - lang: language, - value: '', - })), - concepts: [], + identifier: '', + label: {}, + description: {}, + members: [], }; } } diff --git a/terminology-ui/src/pages/terminology/[terminologyId]/collection/[collectionId]/edit.tsx b/terminology-ui/src/pages/terminology/[terminologyId]/collection/[collectionId]/edit.tsx index c9fe423c9..e8b9fe906 100644 --- a/terminology-ui/src/pages/terminology/[terminologyId]/collection/[collectionId]/edit.tsx +++ b/terminology-ui/src/pages/terminology/[terminologyId]/collection/[collectionId]/edit.tsx @@ -21,6 +21,7 @@ import Layout from '@app/common/components/layout'; import EditCollection from '@app/modules/edit-collection'; import { SSRConfig } from 'next-i18next'; import { wrapper } from '@app/store'; +import { getLanguageVersion } from 'yti-common-ui/utils/get-language-version'; interface CollectionEditPageProps extends CommonContextState { _netI18Next: SSRConfig; @@ -86,9 +87,9 @@ export const getServerSideProps = createCommonGetServerSideProps( functionKey: 'getCollection', }); - const collectionLabel = getPropertyValue({ - property: collectionData?.properties?.prefLabel, - language: locale, + const collectionLabel = getLanguageVersion({ + data: collectionData.label, + lang: locale ?? 'fi', }); return { @@ -97,8 +98,8 @@ export const getServerSideProps = createCommonGetServerSideProps( terminologyId: terminologyId, collectionInfo: { collectionId: collectionId, - createdBy: collectionData.createdBy ?? null, - collectionCode: collectionData.code, + createdBy: collectionData.creator ?? null, + collectionCode: '', collectionUri: collectionData.uri, }, requireAuthenticated: true, From 5095d99fa1c40fc566d4d9fe74b9af4cd98f72a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kalle=20Kytt=C3=A4l=C3=A4?= <kalle.kyttala@rebase.fi> Date: Wed, 28 Aug 2024 15:42:52 +0300 Subject: [PATCH 07/35] revert next config change --- datamodel-ui/next.config.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/datamodel-ui/next.config.js b/datamodel-ui/next.config.js index 39a97f393..6cfe80384 100644 --- a/datamodel-ui/next.config.js +++ b/datamodel-ui/next.config.js @@ -103,7 +103,7 @@ module.exports = () => { }, { source: '/datamodel-api/:path*', - destination: 'https://localhost:9004/datamodel-api/:path*', + destination: 'http://localhost:9004/datamodel-api/:path*', }, { source: '/terminology-api/:path*', From e9921e8e20e8c33b4e7d1d69131044d49d3cb3b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kalle=20Kytt=C3=A4l=C3=A4?= <kalle.kyttala@rebase.fi> Date: Thu, 29 Aug 2024 08:56:21 +0300 Subject: [PATCH 08/35] collection form validation --- .../src/modules/edit-collection/index.tsx | 58 ++++++++++++++++--- 1 file changed, 50 insertions(+), 8 deletions(-) diff --git a/terminology-ui/src/modules/edit-collection/index.tsx b/terminology-ui/src/modules/edit-collection/index.tsx index 0fe1c40aa..1fe78a3dd 100644 --- a/terminology-ui/src/modules/edit-collection/index.tsx +++ b/terminology-ui/src/modules/edit-collection/index.tsx @@ -75,6 +75,8 @@ export default function EditCollection({ const [updateCollection, updateResult] = useUpdateCollectionMutation(); const [emptyError, setEmptyError] = useState(false); + const [invalidIdentifierError, setInvalidIdentifierError] = useState(false); + const [errorMessages, setErrorMessages] = useState<string[]>([]); const [isCreating, setIsCreating] = useState(false); const languages = @@ -100,6 +102,43 @@ export default function EditCollection({ } }, [result, updateResult, router, terminologyId]); + useEffect(() => { + if (exists.data) { + setInvalidIdentifierError(true); + } else { + setInvalidIdentifierError(false); + } + }, [exists]); + + const isFormValid = (formData: CollectionFormData) => { + const isValidIdentifier = formData.identifier.match( + /^[a-zA-Z]([\w-]){1,98}$/ + ); + const isValidLabel = Object.keys(formData.label).every( + (n) => formData.label[n] + ); + + const messages: string[] = []; + if (!isValidLabel) { + setEmptyError(true); + messages.push(t('edit-collection-error.prefLabel')); + } + + if (!isValidIdentifier) { + setInvalidIdentifierError(true); + messages.push(t('prefix-invalid', { ns: 'admin' })); + } + + if (exists.data) { + setInvalidIdentifierError(true); + messages.push(t('prefix-taken', { ns: 'admin' })); + } + + setErrorMessages(messages); + + return isValidIdentifier && !exists.data && isValidLabel; + }; + const setIdentifier = (value: string) => { const data = formData; Object.assign(data, { identifier: value }); @@ -139,6 +178,7 @@ export default function EditCollection({ const handleClick = () => { getAuthenticatedMutUser(); + setErrorMessages([]); const payload = Object.assign( {}, @@ -148,10 +188,10 @@ export default function EditCollection({ } ); - if (Object.keys(formData.label).every((n) => !formData.label[n])) { - setEmptyError(true); + if (!isFormValid(formData)) { return; } + disableConfirmation(); setIsCreating(true); @@ -225,14 +265,16 @@ export default function EditCollection({ defaultValue={collection?.identifier ?? ''} disabled={!!collectionInfo} onChange={(e) => setIdentifier(e?.toString() ?? '')} - status={exists.data ? 'error' : 'default'} - onBlur={() => + status={invalidIdentifierError ? 'error' : 'default'} + onBlur={() => { checkCollectionExists({ terminologyId, collectionId: formData.identifier, - }) + }); + }} + statusText={ + invalidIdentifierError ? t('prefix-taken', { ns: 'admin' }) : '' } - statusText={exists.data ? t('prefix-taken', { ns: 'admin' }) : ''} id="prefix-input" maxLength={100} /> @@ -287,9 +329,9 @@ export default function EditCollection({ {t('error-occurred_unauthenticated', { ns: 'alert' })} </InlineAlert> )} - {emptyError && ( + {errorMessages.length > 0 && ( <FormFooterAlert - alerts={[t('edit-collection-error.prefLabel')]} + alerts={errorMessages} labelText={t('missing-information', { ns: 'admin' })} /> )} From b5829c2363e348f6505a3a3848ba4bd4418b7b79 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kalle=20Kytt=C3=A4l=C3=A4?= <kalle.kyttala@rebase.fi> Date: Thu, 29 Aug 2024 15:04:53 +0300 Subject: [PATCH 09/35] terminology add and edit --- common-ui/components/form/prefix.tsx | 7 +- .../info-dropdown/info-expander.tsx | 8 +- .../information-domains-selector.tsx | 17 +- .../organization-selector.tsx | 16 +- .../terminology-components/prefix.tsx | 2 +- .../vocabulary/vocabulary.slice.tsx | 39 ++-- .../src/common/interfaces/interfaces-v2.ts | 17 +- .../edit-vocabulary/generate-initial-data.ts | 24 +- .../src/modules/edit-vocabulary/index.tsx | 28 +-- .../generate-new-terminology.tsx | 212 +++--------------- .../modules/new-terminology/info-manual.tsx | 34 ++- .../new-terminology/missing-info-alert.tsx | 12 +- .../new-terminology/new-terminology-modal.tsx | 46 ++-- .../terminology-initial-state.tsx | 11 - .../src/modules/vocabulary/index.tsx | 3 +- 15 files changed, 166 insertions(+), 310 deletions(-) delete mode 100644 terminology-ui/src/modules/new-terminology/terminology-initial-state.tsx diff --git a/common-ui/components/form/prefix.tsx b/common-ui/components/form/prefix.tsx index 2a295cc9c..ab11278f4 100644 --- a/common-ui/components/form/prefix.tsx +++ b/common-ui/components/form/prefix.tsx @@ -39,8 +39,7 @@ export default function Prefix({ maxLength, minLength, }: PrefixProps) { - const URI = - typeInUri === 'model' ? 'https://iri.suomi.fi' : 'http://uri.suomi.fi'; + const namespace = 'https://iri.suomi.fi'; const [prefixValid, setPrefixValid] = useState(true); const [prefixInternal, setPrefixInternal] = useState(prefix); @@ -98,8 +97,8 @@ export default function Prefix({ <Label>{translations.uriPreview}</Label> <Paragraph> <Text smallScreen> - {URI}/{typeInUri}/{prefixInternal} - {prefixInternal && '/'} + {namespace}/{typeInUri}/{prefix} + {prefix && '/'} </Text> </Paragraph> </div> diff --git a/terminology-ui/src/common/components/info-dropdown/info-expander.tsx b/terminology-ui/src/common/components/info-dropdown/info-expander.tsx index 3387eda4b..de718d282 100644 --- a/terminology-ui/src/common/components/info-dropdown/info-expander.tsx +++ b/terminology-ui/src/common/components/info-dropdown/info-expander.tsx @@ -40,7 +40,7 @@ import isEmail from 'validator/lib/isEmail'; import { useRouter } from 'next/router'; import { compareLocales } from '@app/common/utils/compare-locals'; import { - Terminology, + TerminologyInfo, TerminologyType, } from '@app/common/interfaces/interfaces-v2'; import { getLanguageVersion } from 'yti-common-ui/utils/get-language-version'; @@ -51,7 +51,7 @@ const Subscription = dynamic( const CopyTerminologyModal = dynamic(() => import('../copy-terminology-modal')); interface InfoExpanderProps { - data?: Terminology; + data?: TerminologyInfo; childOrganizations?: string[]; } @@ -124,7 +124,7 @@ export default function InfoExpander({ <BasicBlock title={t('vocabulary-info-vocabulary-type')} id="type"> {translateTerminologyType( - data.type ?? TerminologyType.TERMINOLOGICAL_VOCABULARY, + data.graphType ?? TerminologyType.TERMINOLOGICAL_VOCABULARY, t )} </BasicBlock> @@ -168,8 +168,6 @@ export default function InfoExpander({ {t('edit-terminology', { ns: 'admin' })} </Button> - <UpdateWithFileModal /> - <CopyTerminologyModal terminologyId={terminologyId ?? ''} noWrap diff --git a/terminology-ui/src/common/components/terminology-components/information-domains-selector.tsx b/terminology-ui/src/common/components/terminology-components/information-domains-selector.tsx index 74fa4a7aa..8655f0f1f 100644 --- a/terminology-ui/src/common/components/terminology-components/information-domains-selector.tsx +++ b/terminology-ui/src/common/components/terminology-components/information-domains-selector.tsx @@ -8,13 +8,13 @@ import { MultiselectSmBot, } from './terminology-components.styles'; import { UpdateTerminology } from '@app/modules/new-terminology/update-terminology.interface'; -import { NewTerminologyInfo } from '@app/common/interfaces/new-terminology-info'; import { getLanguageVersion } from 'yti-common-ui/utils/get-language-version'; +import { TerminologyForm } from '@app/modules/new-terminology/info-manual'; interface InformationDomainsSelectorProps { update: ({ key, data }: UpdateTerminology) => void; userPosted: boolean; - initialData?: NewTerminologyInfo; + initialData?: TerminologyForm; disabled?: boolean; } @@ -31,10 +31,10 @@ export default function InformationDomainsSelector({ MultiSelectData[] >( initialData - ? initialData.infoDomains.map((domain) => ({ - labelText: domain.labelText, - uniqueItemId: domain.uniqueItemId, - chipText: domain.labelText, + ? initialData.groups.map((group) => ({ + labelText: group.labelText, + uniqueItemId: group.uniqueItemId, + chipText: group.labelText, disabled: false, })) : [] @@ -42,7 +42,7 @@ export default function InformationDomainsSelector({ const handleChange = (e: MultiSelectData[]) => { setSelectedInfoDomains(e); - update({ key: 'infoDomains', data: e }); + update({ key: 'groups', data: e }); }; const infoDomains: MultiSelectData[] = informationDomains?.map( @@ -56,8 +56,7 @@ export default function InformationDomainsSelector({ return { name: domainName, labelText: domainName, - uniqueItemId: infoDomain.id, - // groupId: infoDomain.type.graph.id, + uniqueItemId: infoDomain.identifier, } as MultiSelectData; } } diff --git a/terminology-ui/src/common/components/terminology-components/organization-selector.tsx b/terminology-ui/src/common/components/terminology-components/organization-selector.tsx index 7725474a1..a4796beac 100644 --- a/terminology-ui/src/common/components/terminology-components/organization-selector.tsx +++ b/terminology-ui/src/common/components/terminology-components/organization-selector.tsx @@ -7,17 +7,17 @@ import { MultiselectSmBot, } from './terminology-components.styles'; import { UpdateTerminology } from '@app/modules/new-terminology/update-terminology.interface'; -import { NewTerminologyInfo } from '@app/common/interfaces/new-terminology-info'; import { useEffect, useMemo, useState } from 'react'; import { checkPermission } from '@app/common/utils/has-permission'; import { useSelector } from 'react-redux'; import { selectLogin } from '../login/login.slice'; import { getLanguageVersion } from 'yti-common-ui/utils/get-language-version'; +import { TerminologyForm } from '@app/modules/new-terminology/info-manual'; export interface OrganizationSelectorProps { update: ({ key, data }: UpdateTerminology) => void; userPosted: boolean; - initialData?: NewTerminologyInfo; + initialData?: TerminologyForm; disabled?: boolean; } @@ -42,9 +42,9 @@ export default function OrganizationSelector({ MultiSelectData[] >( initialData - ? initialData.contributors.map((contributor) => ({ - labelText: contributor.labelText, - uniqueItemId: contributor.uniqueItemId, + ? initialData.organizations.map((organization) => ({ + labelText: organization.labelText, + uniqueItemId: organization.uniqueItemId, })) : [] ); @@ -73,8 +73,6 @@ export default function OrganizationSelector({ name: orgName, labelText: orgName, uniqueItemId: org.id, - // TODO - // organizationId: org.type.graph.id, } as MultiSelectData; } } @@ -85,13 +83,13 @@ export default function OrganizationSelector({ useEffect(() => { if (adminOrgs?.length === 1 && selectedOrganizations.length === 0) { setSelectedOrganizations(adminOrgs); - update({ key: 'contributors', data: adminOrgs }); + update({ key: 'organizations', data: adminOrgs }); } }, [adminOrgs, update, selectedOrganizations]); const handleSelectOrganizations = (value: MultiSelectData[]) => { setSelectedOrganizations(value); - update({ key: 'contributors', data: value }); + update({ key: 'organizations', data: value }); }; return ( diff --git a/terminology-ui/src/common/components/terminology-components/prefix.tsx b/terminology-ui/src/common/components/terminology-components/prefix.tsx index 27e197402..96bac5992 100644 --- a/terminology-ui/src/common/components/terminology-components/prefix.tsx +++ b/terminology-ui/src/common/components/terminology-components/prefix.tsx @@ -19,7 +19,7 @@ export interface PrefixProps { } export default function Prefix({ update, userPosted, disabled }: PrefixProps) { - const URI = 'http://uri.suomi.fi/'; + const URI = 'https://iri.suomi.fi/'; const { t } = useTranslation('admin'); const { isSmall } = useBreakpoints(); const [randomURL] = useState(v4().substring(0, 8)); diff --git a/terminology-ui/src/common/components/vocabulary/vocabulary.slice.tsx b/terminology-ui/src/common/components/vocabulary/vocabulary.slice.tsx index 7b1c00ffe..926cd6aa2 100644 --- a/terminology-ui/src/common/components/vocabulary/vocabulary.slice.tsx +++ b/terminology-ui/src/common/components/vocabulary/vocabulary.slice.tsx @@ -12,6 +12,7 @@ import { ConceptResponseObject, SearchResponse, Terminology, + TerminologyInfo, } from '@app/common/interfaces/interfaces-v2'; export const vocabularyInitialState = {}; @@ -56,20 +57,30 @@ export const terminologyApi = createApi({ }, }), }), - getTerminology: builder.query<Terminology, { id: string }>({ + getTerminology: builder.query<TerminologyInfo, { id: string }>({ query: (value) => ({ url: `/terminology/${value.id}`, method: 'GET', }), }), - postNewVocabulary: builder.mutation< - string, - { templateGraphID: string; prefix: string; newTerminology: object } - >({ - query: ({ templateGraphID, prefix, newTerminology }) => ({ - url: `/validatedVocabulary?templateGraphId=${templateGraphID}&prefix=${prefix}`, + createTerminology: builder.mutation<string, Terminology>({ + query: (data) => ({ + url: '/terminology', method: 'POST', - data: newTerminology, + data, + }), + }), + updateTerminology: builder.mutation< + null, + { + prefix: string; + payload: Terminology; + } + >({ + query: (data) => ({ + url: `/terminology/${data.prefix}`, + method: 'PUT', + data: data.payload, }), }), postCreateVersion: builder.mutation< @@ -93,13 +104,7 @@ export const terminologyApi = createApi({ }), getIfNamespaceInUse: builder.mutation<boolean, string>({ query: (prefix) => ({ - url: `/namespaceInUse?prefix=${prefix}`, - method: 'GET', - }), - }), - getVocabularies: builder.query<VocabulariesDTO[], null>({ - query: () => ({ - url: '/vocabularies', + url: `/terminology/${prefix}/exists`, method: 'GET', }), }), @@ -110,11 +115,11 @@ export const { useGetCollectionsQuery, useGetConceptResultQuery, useGetTerminologyQuery, - usePostNewVocabularyMutation, + useCreateTerminologyMutation, + useUpdateTerminologyMutation, usePostCreateVersionMutation, useDeleteTerminologyMutation, useGetIfNamespaceInUseMutation, - useGetVocabulariesQuery, util: { getRunningQueriesThunk }, } = terminologyApi; diff --git a/terminology-ui/src/common/interfaces/interfaces-v2.ts b/terminology-ui/src/common/interfaces/interfaces-v2.ts index 23b8772a5..ac8171b8a 100644 --- a/terminology-ui/src/common/interfaces/interfaces-v2.ts +++ b/terminology-ui/src/common/interfaces/interfaces-v2.ts @@ -3,11 +3,23 @@ import { Organization } from 'yti-common-ui/interfaces/organization.interface'; import { Status } from 'yti-common-ui/interfaces/status.interface'; export interface Terminology { + label: LocalizedValue; + description: LocalizedValue; + status: Status; + organizations: string[]; + groups: string[]; + contact: string; + prefix: string; + graphType: TerminologyType; + languages: string[]; +} + +export interface TerminologyInfo { uri: string; label: LocalizedValue; description: LocalizedValue; prefix: string; - type: TerminologyType; + graphType: TerminologyType; status: Status; languages: string[]; contact: string; @@ -19,6 +31,7 @@ export interface Terminology { modifier: UserMeta; origin: string; } + export interface ConceptInfo { identifier: string; uri: string; @@ -104,7 +117,7 @@ export interface ConceptCollection { /* build fails for empty interfaces -export interface TerminologyInfo {} + export interface ConceptInfo {} diff --git a/terminology-ui/src/modules/edit-vocabulary/generate-initial-data.ts b/terminology-ui/src/modules/edit-vocabulary/generate-initial-data.ts index 3436ca9a1..102f77c33 100644 --- a/terminology-ui/src/modules/edit-vocabulary/generate-initial-data.ts +++ b/terminology-ui/src/modules/edit-vocabulary/generate-initial-data.ts @@ -1,23 +1,23 @@ import { - Terminology, + TerminologyInfo, TerminologyType, } from '@app/common/interfaces/interfaces-v2'; -import { NewTerminologyInfo } from '@app/common/interfaces/new-terminology-info'; import { LanguageBlockType } from 'yti-common-ui/form/language-selector'; import { getLanguageVersion } from 'yti-common-ui/utils/get-language-version'; +import { TerminologyForm } from '../new-terminology/info-manual'; export default function generateInitialData( lang: string, - data?: Terminology -): NewTerminologyInfo | undefined { + data?: TerminologyInfo +): TerminologyForm | undefined { if (!data) { return undefined; } const languages = data.languages?.map((l) => { - const description = ''; - const title = ''; + const description = data.description[l]; + const title = data.label[l]; const labelText = l; @@ -30,7 +30,7 @@ export default function generateInitialData( } as LanguageBlockType; }) ?? []; - const infoDomains = + const groups = data.groups?.map((group) => { const label = getLanguageVersion({ data: group.label, @@ -42,7 +42,7 @@ export default function generateInitialData( groupId: group.identifier, labelText: label, name: label, - uniqueItemId: group.id, + uniqueItemId: group.identifier, }; }) ?? []; @@ -70,14 +70,14 @@ export default function generateInitialData( }) .filter((p) => p)[0]; - const obj: NewTerminologyInfo = { + const obj: TerminologyForm = { contact: data.contact ?? '', languages: languages, - infoDomains: infoDomains, + groups: groups, prefix: [prefix ?? '', true], status: data.status ?? 'DRAFT', - type: data.type ?? TerminologyType.TERMINOLOGICAL_VOCABULARY, - contributors: contributors, + type: data.graphType ?? TerminologyType.TERMINOLOGICAL_VOCABULARY, + organizations: contributors, }; return obj; diff --git a/terminology-ui/src/modules/edit-vocabulary/index.tsx b/terminology-ui/src/modules/edit-vocabulary/index.tsx index df88968b4..c21c3ee03 100644 --- a/terminology-ui/src/modules/edit-vocabulary/index.tsx +++ b/terminology-ui/src/modules/edit-vocabulary/index.tsx @@ -7,7 +7,10 @@ import { import { useEditTerminologyMutation } from '@app/common/components/modify/modify.slice'; import SaveSpinner from 'yti-common-ui/save-spinner'; import Title from 'yti-common-ui/title'; -import { useGetTerminologyQuery } from '@app/common/components/vocabulary/vocabulary.slice'; +import { + useGetTerminologyQuery, + useUpdateTerminologyMutation, +} from '@app/common/components/vocabulary/vocabulary.slice'; import useConfirmBeforeLeavingPage from 'yti-common-ui/utils/hooks/use-confirm-before-leaving-page'; import { useTranslation } from 'next-i18next'; import { useRouter } from 'next/router'; @@ -20,7 +23,7 @@ import { Paragraph, Text, } from 'suomifi-ui-components'; -import generateNewTerminology from '../new-terminology/generate-new-terminology'; +import generateTerminologyPayload from '../new-terminology/generate-new-terminology'; import InfoManual from '../new-terminology/info-manual'; import MissingInfoAlert from '../new-terminology/missing-info-alert'; import { TallerSeparator } from '../new-terminology/new-terminology.styles'; @@ -59,7 +62,7 @@ export default function EditVocabulary({ terminologyId }: EditVocabularyProps) { const [isValid, setIsValid] = useState(true); const [userPosted, setUserPosted] = useState(false); const [isCreating, setIsCreating] = useState(false); - const [editTerminology, result] = useEditTerminologyMutation(); + const [updateTerminology, result] = useUpdateTerminologyMutation(); const { enableConfirmation, disableConfirmation } = useConfirmBeforeLeavingPage('disabled'); const { data: authenticatedUser } = useGetAuthenticatedUserQuery(); @@ -75,23 +78,16 @@ export default function EditVocabulary({ terminologyId }: EditVocabularyProps) { return; } - const newData = generateNewTerminology({ - data: data, - code: info?.prefix, - createdBy: '', - createdDate: '', - id: info?.prefix, - lastModifiedBy: `${user.firstName} ${user.lastName}`, - terminologyId: terminologyId, - uri: info?.uri, - origin: '', - }); + const newData = generateTerminologyPayload({ data, update: true }); if (!newData) { return; } setIsCreating(true); - editTerminology(newData); + updateTerminology({ + prefix: terminologyId, + payload: newData, + }); }; const handleReturn = useCallback(() => { @@ -145,7 +141,7 @@ export default function EditVocabulary({ terminologyId }: EditVocabularyProps) { <TitleTypeAndStatusWrapper> <TitleType> {translateTerminologyType( - info?.type ?? TerminologyType.TERMINOLOGICAL_VOCABULARY, + info?.graphType ?? TerminologyType.TERMINOLOGICAL_VOCABULARY, t )} </TitleType>{' '} diff --git a/terminology-ui/src/modules/new-terminology/generate-new-terminology.tsx b/terminology-ui/src/modules/new-terminology/generate-new-terminology.tsx index 1cad9d4d8..8e6377bdf 100644 --- a/terminology-ui/src/modules/new-terminology/generate-new-terminology.tsx +++ b/terminology-ui/src/modules/new-terminology/generate-new-terminology.tsx @@ -1,187 +1,39 @@ -import { NewTerminology } from '@app/common/interfaces/new-terminology'; -import { NewTerminologyInfo } from '@app/common/interfaces/new-terminology-info'; -import { v4 } from 'uuid'; +import { TerminologyForm } from './info-manual'; +import { Terminology } from '@app/common/interfaces/interfaces-v2'; -interface GenerateNewTerminologyProps { - data: NewTerminologyInfo; - code?: string; - createdBy?: string; - createdDate?: string; - id?: string; - lastModifiedBy?: string; - terminologyId?: string; - uri?: string; - origin?: string; -} - -export default function generateNewTerminology({ +export default function generateTerminologyPayload({ data, - code, - createdBy, - createdDate, - id, - lastModifiedBy, - terminologyId, - uri, - origin, -}: GenerateNewTerminologyProps) { - if (!data.contributors) { - return; - } - - const postData = Object.assign({}, template); - const regex = '(?s)^.*$'; - - const now = new Date(); - const UUID = v4(); - postData.id = id ? id : UUID; - postData.createdDate = createdDate ? createdDate : now.toISOString(); - postData.lastModifiedDate = now.toISOString(); - - postData.properties.contact = [ - { - lang: '', - regex: regex, - value: data.contact ? data.contact : 'yhteentoimivuus@dvv.fi', - }, - ]; - - postData.properties.prefLabel = data.languages.map((lang) => ({ - lang: lang.uniqueItemId, - regex, - value: lang.title, - })); - postData.properties.description = data.languages.map((lang) => ({ - lang: lang.uniqueItemId, - regex, - value: lang.description, - })); - postData.properties.language = data.languages.map((lang) => ({ - lang: '', - regex, - value: lang.uniqueItemId, - })); - - postData.properties.terminologyType = [ - { - lang: '', - regex: '^(OTHER_VOCABULARY|TERMINOLOGICAL_VOCABULARY)$', - value: data.type, - }, - ]; - - if (data.status) { - postData.properties.status = [ - { - lang: '', - regex: '(?s)^.*$', - value: data.status, - }, - ]; - } - - postData.references.contributor = data.contributors.map((contributor) => ({ - id: contributor.uniqueItemId, - type: { - graph: { - id: contributor.organizationId, - }, - id: 'Organization', - }, - })); - - if (terminologyId) { - postData.references.contributor[0].type.uri = ''; - } - - postData.references.inGroup = data.infoDomains.map((infoDomain) => { - const obj: NewTerminology['references']['inGroup'][0] = { - id: infoDomain.uniqueItemId, - type: { - graph: { - id: infoDomain.groupId, - }, - id: 'Group', - }, - }; - - if (terminologyId) { - obj.type.uri = ''; + update, +}: { + data: TerminologyForm; + update?: boolean; +}) { + const initial = { + label: {}, + description: {}, + languages: [] as string[], + } as Terminology; + + const postData = data.languages.reduce((terminology, lang) => { + const langCode = lang.uniqueItemId; + terminology.languages.push(langCode); + terminology.label[langCode] = lang.title; + if (lang.description) { + terminology.description[langCode] = lang.description; } - - return obj; + return terminology; + }, initial); + + Object.assign(postData, { + contact: data.contact, + ...(!update && { + prefix: data.prefix[0], + }), + status: data.status ?? 'DRAFT', + groups: data.groups.map((g) => g.uniqueItemId), + organizations: data.organizations.map((o) => o.uniqueItemId), + graphType: data.type, }); - postData.createdBy = createdBy ? createdBy : ''; - postData.lastModifiedBy = lastModifiedBy ? lastModifiedBy : ''; - - if (code) { - postData.code = code; - } - - if (terminologyId) { - postData.type.uri = ''; - postData.type.graph.id = terminologyId; - postData.properties.origin = [ - { - lang: '', - regex: regex, - value: origin ? origin : '', - }, - ]; - } - - if (uri) { - postData.uri = uri; - } - return postData; } - -const template: NewTerminology = { - createdBy: '', - createdDate: '', - id: '', - lastModifiedBy: '', - lastModifiedDate: '', - properties: { - contact: [], - description: [], - language: [], - prefLabel: [], - priority: [ - { - lang: '', - regex: '(?s)^.*$', - value: '', - }, - ], - status: [ - { - lang: '', - regex: '(?s)^.*$', - value: 'DRAFT', - }, - ], - terminologyType: [ - { - lang: '', - regex: '^(OTHER_VOCABULARY|TERMINOLOGICAL_VOCABULARY)$', - value: 'TERMINOLOGICAL_VOCABULARY', - }, - ], - }, - references: { - contributor: [], - inGroup: [], - }, - referrers: {}, - type: { - graph: { - // Default value for TerminologicalVocabulary nodes - id: '61cf6bde-46e6-40bb-b465-9b2c66bf4ad8', - }, - id: 'TerminologicalVocabulary', - uri: 'http://www.w3.org/2004/02/skos/core#ConceptScheme', - }, -}; diff --git a/terminology-ui/src/modules/new-terminology/info-manual.tsx b/terminology-ui/src/modules/new-terminology/info-manual.tsx index a7130c3a2..c8364b492 100644 --- a/terminology-ui/src/modules/new-terminology/info-manual.tsx +++ b/terminology-ui/src/modules/new-terminology/info-manual.tsx @@ -1,14 +1,12 @@ import { useEffect, useState } from 'react'; -import { Paragraph, Text } from 'suomifi-ui-components'; +import { MultiSelectData, Paragraph, Text } from 'suomifi-ui-components'; import ContactInfo from '@app/common/components/terminology-components/contact-info'; import InformationDomainsSelector from '@app/common/components/terminology-components/information-domains-selector'; import { TallerSeparator } from './new-terminology.styles'; import OrganizationSelector from '@app/common/components/terminology-components/organization-selector'; import Prefix from 'yti-common-ui/form/prefix'; import TypeSelector from '@app/common/components/terminology-components/type-selector'; -import { NewTerminologyInfo } from '@app/common/interfaces/new-terminology-info'; import { useTranslation } from 'next-i18next'; -import { TerminologyDataInitialState } from './terminology-initial-state'; import { UpdateTerminology } from './update-terminology.interface'; import StatusSelector from './status-selector'; import isEmail from 'validator/lib/isEmail'; @@ -18,16 +16,29 @@ import LanguageSelector, { } from 'yti-common-ui/form/language-selector'; import { useGetCodesQuery } from '@app/common/components/codelist/codelist.slice'; import { MODEL_PREFIX_MAX } from 'yti-common-ui/utils/constants'; +import { TerminologyType } from '@app/common/interfaces/interfaces-v2'; +import { Status } from 'yti-common-ui/interfaces/status.interface'; +import { v4 } from 'uuid'; interface InfoManualProps { setIsValid: (valid: boolean) => void; - setManualData: (object: NewTerminologyInfo) => void; + setManualData: (object: TerminologyForm) => void; userPosted: boolean; - initialData?: NewTerminologyInfo; + initialData?: TerminologyForm; onChange: () => void; disabled?: boolean; } +export interface TerminologyForm { + contact: string; + languages: (LanguageBlockType & { selected: boolean })[]; + organizations: MultiSelectData[]; + prefix: [string, boolean]; + groups: MultiSelectData[]; + status?: Status; + type: TerminologyType; +} + export default function InfoManual({ setIsValid, setManualData, @@ -37,8 +48,17 @@ export default function InfoManual({ disabled, }: InfoManualProps) { const { t, i18n } = useTranslation('admin'); - const [terminologyData, setTerminologyData] = useState<NewTerminologyInfo>( - initialData ? initialData : TerminologyDataInitialState + const [terminologyData, setTerminologyData] = useState<TerminologyForm>( + initialData + ? initialData + : { + groups: [], + organizations: [], + languages: [], + contact: '', + prefix: [v4().slice(0, 8), true], + type: TerminologyType.TERMINOLOGICAL_VOCABULARY, + } ); const { data: languages } = useGetCodesQuery({ registry: 'interoperabilityplatform', diff --git a/terminology-ui/src/modules/new-terminology/missing-info-alert.tsx b/terminology-ui/src/modules/new-terminology/missing-info-alert.tsx index 9a99255b2..18c1faddd 100644 --- a/terminology-ui/src/modules/new-terminology/missing-info-alert.tsx +++ b/terminology-ui/src/modules/new-terminology/missing-info-alert.tsx @@ -1,11 +1,11 @@ import FormFooterAlert from 'yti-common-ui/form-footer-alert'; -import { NewTerminologyInfo } from '@app/common/interfaces/new-terminology-info'; import { translateLanguage } from '@app/common/utils/translation-helpers'; import { useTranslation } from 'next-i18next'; import isEmail from 'validator/lib/isEmail'; +import { TerminologyForm } from './info-manual'; interface MissingInfoAlertProps { - data: NewTerminologyInfo; + data: TerminologyForm; } export default function MissingInfoAlert({ data }: MissingInfoAlertProps) { @@ -38,8 +38,8 @@ export default function MissingInfoAlert({ data }: MissingInfoAlertProps) { if ( data.languages.length === 0 || data.languages.some((l) => !l.title) || - data.contributors.length === 0 || - data.infoDomains.length === 0 || + data.organizations.length === 0 || + data.groups.length === 0 || !data.prefix[0] || data.prefix[1] === false || (Object.keys(data).includes('status') && !data.status) || @@ -71,14 +71,14 @@ export default function MissingInfoAlert({ data }: MissingInfoAlertProps) { } function renderOrganizationAlerts(): string { - if (data.contributors.length === 0) { + if (data.organizations.length === 0) { return t('alert-org-undefined'); } return ''; } function renderInformationDomainAlerts(): string { - if (data.infoDomains.length === 0) { + if (data.groups.length === 0) { return t('alert-information-domain-undefined'); } diff --git a/terminology-ui/src/modules/new-terminology/new-terminology-modal.tsx b/terminology-ui/src/modules/new-terminology/new-terminology-modal.tsx index a4b5ddbb9..19da08f36 100644 --- a/terminology-ui/src/modules/new-terminology/new-terminology-modal.tsx +++ b/terminology-ui/src/modules/new-terminology/new-terminology-modal.tsx @@ -3,8 +3,7 @@ import { usePostImportExcelMutation } from '@app/common/components/import/import import { useBreakpoints } from 'yti-common-ui/media-query'; import SaveSpinner from 'yti-common-ui/save-spinner'; import { terminologySearchApi } from '@app/common/components/terminology-search/terminology-search.slice'; -import { usePostNewVocabularyMutation } from '@app/common/components/vocabulary/vocabulary.slice'; -import { NewTerminologyInfo } from '@app/common/interfaces/new-terminology-info'; +import { useCreateTerminologyMutation } from '@app/common/components/vocabulary/vocabulary.slice'; import useConfirmBeforeLeavingPage from 'yti-common-ui/utils/hooks/use-confirm-before-leaving-page'; import { translateFileUploadError, @@ -24,8 +23,8 @@ import { Text, } from 'suomifi-ui-components'; import FileUpload from './file-upload'; -import generateNewTerminology from './generate-new-terminology'; -import InfoManual from './info-manual'; +import generateTerminologyPayload from './generate-new-terminology'; +import InfoManual, { TerminologyForm } from './info-manual'; import MissingInfoAlert from './missing-info-alert'; import { FooterBlock, ModalTitleAsH1 } from './new-terminology.styles'; @@ -53,10 +52,10 @@ export default function NewTerminologyModal({ const [startFileUpload, setStartFileUpload] = useState(false); const [fileData, setFileData] = useState<File | null>(); const [userPosted, setUserPosted] = useState(false); - const [manualData, setManualData] = useState<NewTerminologyInfo>(); + const [manualData, setManualData] = useState<TerminologyForm>(); const [isCreating, setIsCreating] = useState<boolean>(false); const [error, setError] = useState(false); - const [postNewVocabulary, newVocabulary] = usePostNewVocabularyMutation(); + const [createTerminology, newTerminology] = useCreateTerminologyMutation(); const [postImportExcel, importExcel] = usePostImportExcelMutation(); const handleClose = useCallback(() => { @@ -71,22 +70,16 @@ export default function NewTerminologyModal({ }, [setShowModal, disableConfirmation]); useEffect(() => { - if (newVocabulary.isSuccess) { + if (newTerminology.isSuccess) { dispatch(terminologySearchApi.util.invalidateTags(['TerminologySearch'])); - - // API should return ID of new terminology but just in case if - // create is succesful and ID is missing then just close the modal - if (newVocabulary.data.length > 0) { - disableConfirmation(); - router.push(`/terminology/${newVocabulary.data}`); - } else { - handleClose(); - } - } else if (newVocabulary.isError) { + disableConfirmation(); + handleClose(); + router.push(`/terminology/${manualData?.prefix[0]}`); + } else if (newTerminology.isError) { setIsCreating(false); setError(true); } - }, [t, newVocabulary, dispatch, handleClose, router, disableConfirmation]); + }, [t, newTerminology, dispatch, handleClose, router, disableConfirmation]); const handleCloseRequest = () => { handleClose(); @@ -102,17 +95,10 @@ export default function NewTerminologyModal({ return; } - const newTerminology = generateNewTerminology({ data: manualData }); - - if (!newTerminology) { - console.error('Main organization missing'); - return; - } + const postData = generateTerminologyPayload({ data: manualData }); setIsCreating(true); - const templateGraphID = newTerminology.type.graph.id; - const prefix = manualData.prefix[0]; - postNewVocabulary({ templateGraphID, prefix, newTerminology }); + createTerminology(postData); } if (inputType === 'file' && fileData) { @@ -157,11 +143,11 @@ export default function NewTerminologyModal({ {!(inputType === 'file' && userPosted) && ( <ModalFooter id="new-terminology-modal-footer"> {userPosted && manualData && <MissingInfoAlert data={manualData} />} - {newVocabulary.isError && ( + {newTerminology.isError && ( <InlineAlert status="error" role="alert" id="api-error-alert"> {translateHttpError( - 'status' in newVocabulary.error - ? newVocabulary.error.status + 'status' in newTerminology.error + ? newTerminology.error.status : 'GENERIC_ERROR', t )} diff --git a/terminology-ui/src/modules/new-terminology/terminology-initial-state.tsx b/terminology-ui/src/modules/new-terminology/terminology-initial-state.tsx deleted file mode 100644 index 5aab249af..000000000 --- a/terminology-ui/src/modules/new-terminology/terminology-initial-state.tsx +++ /dev/null @@ -1,11 +0,0 @@ -import { NewTerminologyInfo } from '@app/common/interfaces/new-terminology-info'; -import { v4 } from 'uuid'; - -export const TerminologyDataInitialState: NewTerminologyInfo = { - contact: '', - languages: [], - infoDomains: [], - contributors: [], - prefix: [v4().slice(0, 8), true], - type: 'TERMINOLOGICAL_VOCABULARY', -}; diff --git a/terminology-ui/src/modules/vocabulary/index.tsx b/terminology-ui/src/modules/vocabulary/index.tsx index 63e4a35fb..da993b8be 100644 --- a/terminology-ui/src/modules/vocabulary/index.tsx +++ b/terminology-ui/src/modules/vocabulary/index.tsx @@ -218,7 +218,8 @@ export default function Vocabulary({ id }: VocabularyProps) { <TitleTypeAndStatusWrapper> <TitleType> {translateTerminologyType( - info?.type ?? TerminologyType.TERMINOLOGICAL_VOCABULARY, + info?.graphType ?? + TerminologyType.TERMINOLOGICAL_VOCABULARY, t )} </TitleType>{' '} From dc7efe5d841e388342d91dc3174e0adf9a5b6256 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kalle=20Kytt=C3=A4l=C3=A4?= <kalle.kyttala@rebase.fi> Date: Tue, 3 Sep 2024 07:08:36 +0300 Subject: [PATCH 10/35] create and edit concept --- terminology-ui/next.config.js | 3 +- .../components/concept/concept.slice.tsx | 32 + .../info-dropdown/info-expander.tsx | 1 - .../relational-information-block/index.tsx | 73 +- .../relational-modal-content.tsx | 39 +- .../render-chosen.tsx | 12 +- .../render-concepts.tsx | 45 +- .../render-expander-content.tsx | 60 +- .../src/common/interfaces/interfaces-v2.ts | 50 +- .../src/modules/concept/concept-sidebar.tsx | 2 +- .../concept-basic-information.tsx | 28 + .../concept-diagrams-and-sources.tsx | 3 + .../basic-information/diagrams.tsx | 82 +- .../basic-information/other-information.tsx | 1 - .../concept-term-block-types.tsx | 2 - .../concept-terms-block/term-form.tsx | 14 +- .../modules/edit-concept/generate-concept.tsx | 863 ++---------------- .../edit-concept/generate-form-data.tsx | 368 ++------ .../src/modules/edit-concept/index.tsx | 102 +-- .../edit-concept/new-concept.types.tsx | 14 +- .../modules/edit-concept/validate-form.tsx | 2 +- 21 files changed, 497 insertions(+), 1299 deletions(-) diff --git a/terminology-ui/next.config.js b/terminology-ui/next.config.js index 198a3f739..0b4430a4c 100644 --- a/terminology-ui/next.config.js +++ b/terminology-ui/next.config.js @@ -117,7 +117,8 @@ module.exports = (phase, { defaultConfig }) => { }, { source: '/codelist-api/:path*', - destination: 'http://localhost:9601/codelist-api/:path*', + destination: + 'https://koodistot.test.yti.cloud.dvv.fi/codelist-api/:path*', }, ]; }, diff --git a/terminology-ui/src/common/components/concept/concept.slice.tsx b/terminology-ui/src/common/components/concept/concept.slice.tsx index 5344ed9e4..d2ac4e41f 100644 --- a/terminology-ui/src/common/components/concept/concept.slice.tsx +++ b/terminology-ui/src/common/components/concept/concept.slice.tsx @@ -2,6 +2,7 @@ import { createApi } from '@reduxjs/toolkit/query/react'; import { Concepts } from '@app/common/interfaces/concepts.interface'; import { getTerminologyApiBaseQuery } from '@app/store/api-base-query'; import { + Concept, ConceptInfo, ConceptResponseObject, ConceptSearchRequest, @@ -22,6 +23,25 @@ export const conceptApi = createApi({ method: 'GET', }), }), + createConcept: builder.mutation<null, { prefix: string; concept: Concept }>( + { + query: (value) => ({ + url: `/concept/${value.prefix}`, + method: 'POST', + data: value.concept, + }), + } + ), + updateConcept: builder.mutation< + null, + { prefix: string; conceptId: string; concept: Concept } + >({ + query: (value) => ({ + url: `/concept/${value.prefix}/${value.conceptId}`, + method: 'PUT', + data: value.concept, + }), + }), deleteConcept: builder.mutation< null, { prefix: string; conceptId: string } @@ -41,11 +61,23 @@ export const conceptApi = createApi({ params: request, }), }), + conceptExists: builder.mutation< + boolean, + { terminologyId: string; conceptId: string } + >({ + query: (params) => ({ + url: `/concept/${params.terminologyId}/${params.conceptId}/exists`, + method: 'GET', + }), + }), }), }); export const { useGetConceptQuery, + useCreateConceptMutation, + useUpdateConceptMutation, + useConceptExistsMutation, useSearchConceptMutation, useDeleteConceptMutation, util: { getRunningQueriesThunk }, diff --git a/terminology-ui/src/common/components/info-dropdown/info-expander.tsx b/terminology-ui/src/common/components/info-dropdown/info-expander.tsx index de718d282..054c4366a 100644 --- a/terminology-ui/src/common/components/info-dropdown/info-expander.tsx +++ b/terminology-ui/src/common/components/info-dropdown/info-expander.tsx @@ -272,7 +272,6 @@ export default function InfoExpander({ <FormattedDate date={data.created} /> {data.creator.name && `, ${data.creator.name}`} </BasicBlock> - <div>TODO: origin</div> <BasicBlock title={t('vocabulary-info-modified-at')} id="modified-at"> <FormattedDate date={data.modified} /> {data.modifier.name && `, ${data.modifier.name}`} diff --git a/terminology-ui/src/common/components/relational-information-block/index.tsx b/terminology-ui/src/common/components/relational-information-block/index.tsx index bc6f06e4b..54ba7415a 100644 --- a/terminology-ui/src/common/components/relational-information-block/index.tsx +++ b/terminology-ui/src/common/components/relational-information-block/index.tsx @@ -1,6 +1,4 @@ import { BasicBlock, BasicBlockExtraWrapper } from 'yti-common-ui/block'; -import PropertyValue from '@app/common/components/property-value'; -import { Concepts } from '@app/common/interfaces/concepts.interface'; import { RelationInfoType } from '@app/modules/edit-concept/new-concept.types'; import { useTranslation } from 'next-i18next'; import { useRouter } from 'next/router'; @@ -21,6 +19,8 @@ import { useBreakpoints } from 'yti-common-ui/media-query'; import { ChipBlock } from './relation-information-block.styles'; import RelationModalContent from './relational-modal-content'; import RenderChosen from './render-chosen'; +import { ConceptResponseObject } from '@app/common/interfaces/interfaces-v2'; +import { getLanguageVersion } from 'yti-common-ui/utils/get-language-version'; interface RelationalInformationBlockProps { infoKey: string; @@ -43,10 +43,10 @@ export default function RelationalInformationBlock({ updateData, fromOther, }: RelationalInformationBlockProps) { - const [chosen, setChosen] = useState<Concepts[] | RelationInfoType[]>( - data[infoKey] ?? [] - ); - const { t } = useTranslation('admin'); + const [chosen, setChosen] = useState< + ConceptResponseObject[] | RelationInfoType[] + >(data[infoKey] ?? []); + const { t, i18n } = useTranslation('admin'); const router = useRouter(); const terminologyId = Array.isArray(router.query.terminologyId) ? router.query.terminologyId[0] @@ -55,14 +55,16 @@ export default function RelationalInformationBlock({ data[infoKey] ?? [] ); - const handleUpdate = (concepts: Concepts[] | RelationInfoType[]) => { + const handleUpdate = ( + concepts: ConceptResponseObject[] | RelationInfoType[] + ) => { const newValue = concepts.map((concept) => { if ('terminology' in concept) { return { id: concept.id, label: concept.label, - terminologyId: concept.terminology.id, + terminologyId: concept.terminology.prefix, terminologyLabel: concept.terminology.label, }; } else { @@ -71,10 +73,6 @@ export default function RelationalInformationBlock({ label: concept.label, terminologyId: concept.terminologyId, terminologyLabel: concept.terminologyLabel, - targetId: - 'targetId' in concept - ? (concept as RelationInfoType).targetId - : concept.id, }; } }) ?? []; @@ -93,8 +91,8 @@ export default function RelationalInformationBlock({ const newIds: string[] = concepts.map((concept) => concept.id); setChosen( 'terminology' in concepts[0] - ? (chosen as Concepts[]).filter((c: Concepts) => - newIds.includes(c.id) + ? (chosen as ConceptResponseObject[]).filter( + (c: ConceptResponseObject) => newIds.includes(c.id) ) : (chosen as RelationInfoType[]).filter((c: RelationInfoType) => newIds.includes(c.id) @@ -143,29 +141,18 @@ export default function RelationalInformationBlock({ ) } > - {concept.label ? ( - <PropertyValue - property={Object.keys(concept.label).map((lang) => ({ - lang, - value: concept.label[lang], - regex: '', - }))} - /> - ) : ( - t('concept-label-undefined', { ns: 'common' }) - )} + {getLanguageVersion({ + data: concept.label, + lang: i18n?.language, + })} {fromOther && ' - '} - {fromOther && ( - <PropertyValue - property={Object.keys(concept.terminologyLabel).map( - (lang) => ({ - lang, - value: concept.terminologyLabel[lang], - regex: '', - }) - )} - /> - )} + {fromOther && + getLanguageVersion({ + data: concept.terminologyLabel ?? { + fi: 'TODO: terminology label', + }, + lang: i18n.language, + })} </Chip> ); })} @@ -183,11 +170,13 @@ export default function RelationalInformationBlock({ interface ManageRelationalInfoModalProps { buttonTitle: string; selectedConceptIds: string[]; - setSelectedConcepts: (value: Concepts[] | RelationInfoType[]) => void; + setSelectedConcepts: ( + value: ConceptResponseObject[] | RelationInfoType[] + ) => void; terminologyId: string; chipLabel: string; - chosen: Concepts[] | RelationInfoType[]; - setChosen: (value: Concepts[] | RelationInfoType[]) => void; + chosen: ConceptResponseObject[] | RelationInfoType[]; + setChosen: (value: ConceptResponseObject[] | RelationInfoType[]) => void; fromOther?: boolean; } @@ -205,7 +194,9 @@ function ManageRelationalInfoModal({ const { isSmall } = useBreakpoints(); const [visible, setVisible] = useState(false); const [showChosen, setShowChosen] = useState(false); - const [searchResults, setSearchResults] = useState<Concepts[]>([]); + const [searchResults, setSearchResults] = useState<ConceptResponseObject[]>( + [] + ); const handleClose = () => { setVisible(false); @@ -224,7 +215,7 @@ function ManageRelationalInfoModal({ ), terminologyId: 'terminology' in concept - ? concept.terminology.id + ? concept.terminology.prefix : concept.terminologyId, terminologyLabel: 'terminology' in concept diff --git a/terminology-ui/src/common/components/relational-information-block/relational-modal-content.tsx b/terminology-ui/src/common/components/relational-information-block/relational-modal-content.tsx index cbd747cd0..969e91baa 100644 --- a/terminology-ui/src/common/components/relational-information-block/relational-modal-content.tsx +++ b/terminology-ui/src/common/components/relational-information-block/relational-modal-content.tsx @@ -1,4 +1,3 @@ -import { Concepts } from '@app/common/interfaces/concepts.interface'; import useMountEffect from '@app/common/utils/hooks/use-mount-effect'; import { RelationInfoType } from '@app/modules/edit-concept/new-concept.types'; import { useTranslation } from 'next-i18next'; @@ -10,13 +9,18 @@ import { useSearchConceptMutation } from '../concept/concept.slice'; import { RelationalModalBlock } from './relation-information-block.styles'; import RenderConcepts from './render-concepts'; import Search from './search'; +import { getNamespace } from '@app/common/utils/namespace'; +import { + ConceptResponseObject, + SearchResponse, +} from '@app/common/interfaces/interfaces-v2'; interface RelationModalContentProps { fromOther?: boolean; - chosen: Concepts[] | RelationInfoType[]; - setChosen: (value: Concepts[] | RelationInfoType[]) => void; - searchResults: Concepts[]; - setSearchResults: (value: Concepts[]) => void; + chosen: ConceptResponseObject[] | RelationInfoType[]; + setChosen: (value: ConceptResponseObject[] | RelationInfoType[]) => void; + searchResults: ConceptResponseObject[]; + setSearchResults: (value: ConceptResponseObject[]) => void; terminologyId: string; initialChosenConcepts: string[]; } @@ -46,6 +50,7 @@ export default function RelationModalContent({ const [totalResults, setTotalResults] = useState(0); const [currPage, setCurrPage] = useState(1); const modalRef = createRef<HTMLDivElement>(); + const pageSize = 20; const statuses: StatusesType[] = [ { @@ -81,12 +86,11 @@ export default function RelationModalContent({ ]; const handleSearch = () => { + const namespace = getNamespace(terminologyId); searchConcept({ - ...(fromOther - ? { notInTerminologyId: terminologyId } - : { terminologyId: terminologyId }), + ...(fromOther ? { notInTerminologyId: namespace } : { namespace }), query: searchTerm, - status: status?.uniqueItemId, + ...(status?.uniqueItemId && { status: [status.uniqueItemId] }), pageFrom: (currPage - 1) * 20, pageSize: 20, }); @@ -101,10 +105,11 @@ export default function RelationModalContent({ return; } + const namespace = getNamespace(terminologyId); searchConcept({ - ...(fromOther - ? { notInTerminologyId: terminologyId } - : { terminologyId: terminologyId }), + ...(fromOther ? { notInTerminologyId: namespace } : { namespace }), + pageFrom: (currPage - 1) * pageSize, + pageSize, }); }; @@ -115,9 +120,9 @@ export default function RelationModalContent({ ? { notInTerminologyId: terminologyId } : { terminologyId: terminologyId }), query: searchTerm, - status: status?.uniqueItemId, - pageFrom: (num - 1) * 20, - pageSize: 20, + ...(status?.uniqueItemId && { status: [status.uniqueItemId] }), + pageFrom: (num - 1) * pageSize, + pageSize, }); focusToTop(); }; @@ -135,11 +140,11 @@ export default function RelationModalContent({ } if (result.isSuccess) { - setSearchResults(result.data.concepts); + setSearchResults(result.data.responseObjects); const totalHits = !fromOther && typeof query.conceptId !== 'undefined' - ? result.data.concepts + ? result.data.responseObjects .map((c) => c.id) .includes( Array.isArray(query.conceptId) diff --git a/terminology-ui/src/common/components/relational-information-block/render-chosen.tsx b/terminology-ui/src/common/components/relational-information-block/render-chosen.tsx index 921c099d7..747d85847 100644 --- a/terminology-ui/src/common/components/relational-information-block/render-chosen.tsx +++ b/terminology-ui/src/common/components/relational-information-block/render-chosen.tsx @@ -1,13 +1,13 @@ -import { Concepts } from '@app/common/interfaces/concepts.interface'; import { RelationInfoType } from '@app/modules/edit-concept/new-concept.types'; import { useTranslation } from 'next-i18next'; import { Chip, Label } from 'suomifi-ui-components'; import PropertyValue from '../property-value'; import { ChipBlock } from './relation-information-block.styles'; +import { ConceptResponseObject } from '@app/common/interfaces/interfaces-v2'; interface RenderChosenProps { - chosen: Concepts[] | RelationInfoType[]; - setChosen: (value: Concepts[] | RelationInfoType[]) => void; + chosen: ConceptResponseObject[] | RelationInfoType[]; + setChosen: (value: ConceptResponseObject[] | RelationInfoType[]) => void; setShowChosen: (value: boolean) => void; chipLabel: string; } @@ -19,10 +19,12 @@ export default function RenderChosen({ chipLabel, }: RenderChosenProps) { const { t } = useTranslation('admin'); - const handleChipRemove = (chose: Concepts | RelationInfoType) => { + const handleChipRemove = ( + chose: ConceptResponseObject | RelationInfoType + ) => { const updatedChosen = 'terminology' in chosen - ? (chosen as Concepts[]).filter((c) => c.id !== chose.id) + ? (chosen as ConceptResponseObject[]).filter((c) => c.id !== chose.id) : (chosen as RelationInfoType[]).filter((c) => c.id !== chose.id); setChosen(updatedChosen); diff --git a/terminology-ui/src/common/components/relational-information-block/render-concepts.tsx b/terminology-ui/src/common/components/relational-information-block/render-concepts.tsx index 70443e8e4..e170c0abd 100644 --- a/terminology-ui/src/common/components/relational-information-block/render-concepts.tsx +++ b/terminology-ui/src/common/components/relational-information-block/render-concepts.tsx @@ -7,19 +7,18 @@ import { import SanitizedTextContent from 'yti-common-ui/sanitized-text-content'; import { useTranslation } from 'next-i18next'; import { getPropertyValue } from '@app/common/components/property-value/get-property-value'; -import getPrefLabel from '@app/common/utils/get-preflabel'; -import { Concepts } from '@app/common/interfaces/concepts.interface'; import { translateStatus } from '@app/common/utils/translation-helpers'; import { useBreakpoints } from 'yti-common-ui/media-query'; import { useEffect, useState } from 'react'; import RenderExpanderContent from './render-expander-content'; import { useRouter } from 'next/router'; import { RelationInfoType } from '@app/modules/edit-concept/new-concept.types'; +import { ConceptResponseObject } from '@app/common/interfaces/interfaces-v2'; interface RenderConceptsProps { - concepts?: Concepts[]; - chosen: Concepts[] | RelationInfoType[]; - setChosen: (value: Concepts[] | RelationInfoType[]) => void; + concepts?: ConceptResponseObject[]; + chosen: ConceptResponseObject[] | RelationInfoType[]; + setChosen: (value: ConceptResponseObject[] | RelationInfoType[]) => void; } export default function RenderConcepts({ @@ -40,23 +39,15 @@ export default function RenderConcepts({ const handleCheckbox = ( e: { checkboxState: boolean }, - concept: Concepts | RelationInfoType + concept: ConceptResponseObject ) => { if (e.checkboxState) { - setChosen( - 'terminology' in concept - ? [...(chosen as Concepts[]), concept] - : [...(chosen as RelationInfoType[]), concept] - ); + setChosen([...(chosen as ConceptResponseObject[]), concept]); } else { setChosen( - 'terminology' in chosen - ? (chosen as Concepts[]).filter((chose) => chose.id !== concept.id) - : (chosen as RelationInfoType[]).filter((chose) => - chose.targetId - ? chose.targetId !== concept.id - : chose.id !== concept.id - ) + (chosen as ConceptResponseObject[]).filter( + (chose) => chose.id !== concept.id + ) ); } }; @@ -91,16 +82,12 @@ export default function RenderConcepts({ toggleButtonAriaLabel={t('additional-information')} > <Checkbox - hintText={`${getPrefLabel({ - prefLabels: concept.terminology.label, - lang: i18n.language, - })} - ${translateStatus(concept.status ?? 'DRAFT', t)}`} + hintText={`TODO: terminology label - ${translateStatus( + concept.status ?? 'DRAFT', + t + )}`} onClick={(e) => handleCheckbox(e, concept)} - checked={chosen.some((chose) => - 'targetId' in chose - ? (chose as RelationInfoType).targetId === concept.id - : chose.id === concept.id - )} + checked={chosen.some((chose) => chose.id === concept.id)} className="concept-checkbox" variant={isSmall ? 'large' : 'small'} id={`concept-result-checkbox-${concept.id}`} @@ -130,8 +117,8 @@ export default function RenderConcepts({ </ExpanderTitle> <RenderExpanderContent - terminologyId={concept.terminology.id} - conceptId={concept.id} + terminologyId={concept.terminology?.prefix} + conceptId={concept.identifier} isOpen={ (expandersOpen?.filter( (c) => c[0] === concept.id diff --git a/terminology-ui/src/common/components/relational-information-block/render-expander-content.tsx b/terminology-ui/src/common/components/relational-information-block/render-expander-content.tsx index 1694873c9..aeb8aa3e2 100644 --- a/terminology-ui/src/common/components/relational-information-block/render-expander-content.tsx +++ b/terminology-ui/src/common/components/relational-information-block/render-expander-content.tsx @@ -4,10 +4,11 @@ import { useGetConceptQuery } from '../concept/concept.slice'; import { useGetTerminologyQuery } from '../vocabulary/vocabulary.slice'; import { ExpanderContent } from 'suomifi-ui-components'; import SaveSpinner from 'yti-common-ui/save-spinner'; -import { BasicBlock } from 'yti-common-ui/block'; -import { MultilingualPropertyBlock, PropertyBlock } from '../block'; +import { BasicBlock, MultilingualBlock } from 'yti-common-ui/block'; import Separator from 'yti-common-ui/separator'; import FormattedDate from 'yti-common-ui/formatted-date'; +import { getLanguageVersion } from 'yti-common-ui/utils/get-language-version'; +import { LocalizedValue } from '@app/common/interfaces/interfaces-v2'; interface RenderExpanderContentProps { terminologyId: string; @@ -20,7 +21,7 @@ function RenderExpanderContent({ conceptId, isOpen, }: RenderExpanderContentProps) { - const { t } = useTranslation('admin'); + const { t, i18n } = useTranslation('admin'); const { data: concept, isLoading: conceptIsLoading } = useGetConceptQuery( { terminologyId: terminologyId, @@ -49,26 +50,45 @@ function RenderExpanderContent({ return ( <ExpanderContent> - <></> - {/*TODO} - <MultilingualPropertyBlock - title={<h2>{t('preferred-terms')}</h2>} - data={concept?.references.prefLabelXl?.[0].properties?.prefLabel} - /> - <MultilingualPropertyBlock - title={<h2>{t('definition')}</h2>} - data={concept?.properties.definition} - /> + {concept && ( + <> + <BasicBlock title={<h2>{t('preferred-terms')}</h2>}> + <MultilingualBlock + data={concept.recommendedTerms.reduce( + (label, term) => ({ + ...label, + [label[term.language]]: term.label ?? '', + }), + {} as LocalizedValue + )} + /> + </BasicBlock> - <Separator isLarge /> + {Object.keys(concept.definition).length > 0 && ( + <BasicBlock title={<h2>{t('definition')}</h2>}> + <MultilingualBlock data={concept.definition} /> + </BasicBlock> + )} - {terminology && <div>TODO contributor</div>} + <Separator isLarge /> - <BasicBlock title={t('modified-at')}> - <FormattedDate date={concept?.lastModifiedDate} />,{' '} - {concept?.lastModifiedBy} - </BasicBlock> - {*/} + <BasicBlock title={t('contributor')}> + {terminology?.organizations + .map((o) => + getLanguageVersion({ + data: o.label, + lang: i18n.language ?? 'fi', + }) + ) + .join(', ')} + </BasicBlock> + + <BasicBlock title={t('modified-at')}> + <FormattedDate date={concept?.modified} /> + {concept?.modifier?.name ? `, ${concept.modifier.name}` : ''} + </BasicBlock> + </> + )} </ExpanderContent> ); } diff --git a/terminology-ui/src/common/interfaces/interfaces-v2.ts b/terminology-ui/src/common/interfaces/interfaces-v2.ts index ac8171b8a..0d1500340 100644 --- a/terminology-ui/src/common/interfaces/interfaces-v2.ts +++ b/terminology-ui/src/common/interfaces/interfaces-v2.ts @@ -32,9 +32,43 @@ export interface TerminologyInfo { origin: string; } +export interface Concept { + identifier: string; + definition: LocalizedValue; + notes: LocalizedListItem[]; + examples: LocalizedListItem[]; + subjectArea: string; + status: Status; + sources: string[]; + links: { + name: LocalizedValue; + uri: string; + description: LocalizedValue; + }[]; + changeNote: string; + historyNote: string; + conceptClass: string; + editorialNotes: string[]; + recommendedTerms: Term[]; + synonyms: Term[]; + notRecommendedTerms: Term[]; + searchTerms: Term[]; + broader: string[]; + narrower: string[]; + isPartOf: string[]; + hasPart: string[]; + related: string[]; + broadMatch: string[]; + narrowMatch: string[]; + exactMatch: string[]; + closeMatch: string[]; + relatedMatch: string[]; +} + export interface ConceptInfo { identifier: string; uri: string; + label: LocalizedValue; created: string; modified: string; creator: UserMeta; @@ -94,6 +128,7 @@ export interface ConceptReferenceInfo { referenceURI: string; prefix: string; label: LocalizedValue; + terminologyLabel: LocalizedValue; } export interface ConceptCollectionInfo { @@ -115,17 +150,6 @@ export interface ConceptCollection { members: string[]; } -/* build fails for empty interfaces - - - - -export interface ConceptInfo {} - - - -*/ - export interface SearchRequest { query?: string; sortLang?: string; @@ -164,6 +188,10 @@ export interface TerminogyResponseObject extends ResponseObject { } export interface ConceptResponseObject extends ResponseObject { + terminology: { + prefix: string; + label: LocalizedValue; + }; definition: LocalizedValue; identifier: string; } diff --git a/terminology-ui/src/modules/concept/concept-sidebar.tsx b/terminology-ui/src/modules/concept/concept-sidebar.tsx index 580bfe1ff..d212bce76 100644 --- a/terminology-ui/src/modules/concept/concept-sidebar.tsx +++ b/terminology-ui/src/modules/concept/concept-sidebar.tsx @@ -30,7 +30,7 @@ function getReferenceValues( return references.map((ref) => { return { - id: ref.conceptURI, + id: ref.referenceURI, href: `/terminology/${ref.prefix}/${type}/${ref.identifier}`, value: getLanguageVersion({ data: ref.label, diff --git a/terminology-ui/src/modules/edit-concept/basic-information/concept-basic-information.tsx b/terminology-ui/src/modules/edit-concept/basic-information/concept-basic-information.tsx index e57b1c2b8..2b8b11c91 100644 --- a/terminology-ui/src/modules/edit-concept/basic-information/concept-basic-information.tsx +++ b/terminology-ui/src/modules/edit-concept/basic-information/concept-basic-information.tsx @@ -17,12 +17,16 @@ import { translateLanguage } from '@app/common/utils/translation-helpers'; import { TEXT_AREA_MAX, TEXT_INPUT_MAX } from 'yti-common-ui/utils/constants'; import { FormError } from '../validate-form'; import StatusPicker from './status-picker'; +import { TextInput } from 'suomifi-ui-components'; +import { useConceptExistsMutation } from '@app/common/components/concept/concept.slice'; interface ConceptBasicInformationProps { updateBasicInformation: (value: BasicInfo) => void; initialValues: BasicInfo; languages: string[]; errors: FormError; + terminologyId: string; + isEdit: boolean; } export interface BasicInfoUpdate { @@ -36,9 +40,12 @@ export default function ConceptBasicInformation({ initialValues, languages, errors, + terminologyId, + isEdit, }: ConceptBasicInformationProps) { const { t } = useTranslation('admin'); const [basicInfo, setBasicInfo] = useState<BasicInfo>(initialValues); + const [conceptExists, exitst] = useConceptExistsMutation(); const handleBasicInfoUpdate = ({ key, lang, value }: BasicInfoUpdate) => { if (key === 'definition' && lang && typeof value === 'string') { @@ -63,6 +70,26 @@ export default function ConceptBasicInformation({ <Separator isLarge /> <H2Sm variant="h2">{t('concept-basic-information')}</H2Sm> + <TextInput + labelText={t('concept-identifier')} + disabled={isEdit} + defaultValue={basicInfo.identifier} + onBlur={() => + conceptExists({ + terminologyId, + conceptId: basicInfo.identifier, + }) + } + status={exitst.data ? 'error' : 'default'} + statusText={exitst.data ? t('prefix-taken') : ''} + onChange={(e) => + handleBasicInfoUpdate({ + key: 'identifier', + value: e?.toString() ?? '', + }) + } + /> + {renderDefinitions()} {renderSubject()} @@ -107,6 +134,7 @@ export default function ConceptBasicInformation({ update={handleBasicInfoUpdate} initialValues={basicInfo.diagramAndSource} errors={errors} + languages={languages} /> <OrganizationInformation infoKey="orgInfo" diff --git a/terminology-ui/src/modules/edit-concept/basic-information/concept-diagrams-and-sources.tsx b/terminology-ui/src/modules/edit-concept/basic-information/concept-diagrams-and-sources.tsx index b728e30dc..2101ad936 100644 --- a/terminology-ui/src/modules/edit-concept/basic-information/concept-diagrams-and-sources.tsx +++ b/terminology-ui/src/modules/edit-concept/basic-information/concept-diagrams-and-sources.tsx @@ -20,6 +20,7 @@ interface ConceptDiagramsAndSourcesProps { sources: ListType[]; }; errors: FormError; + languages: string[]; } export default function ConceptDiagramsAndSources({ @@ -27,6 +28,7 @@ export default function ConceptDiagramsAndSources({ update, initialValues, errors, + languages, }: ConceptDiagramsAndSourcesProps) { const { t } = useTranslation('admin'); const [sources, setSources] = useState<ListType[]>( @@ -79,6 +81,7 @@ export default function ConceptDiagramsAndSources({ setDiagrams={setDiagrams} handleRemove={handleRemove} isError={errors.diagrams || errors.diagramsUri} + languages={languages} /> </BasicBlockExtraWrapper> } diff --git a/terminology-ui/src/modules/edit-concept/basic-information/diagrams.tsx b/terminology-ui/src/modules/edit-concept/basic-information/diagrams.tsx index 468c9cadd..bbfa554d5 100644 --- a/terminology-ui/src/modules/edit-concept/basic-information/diagrams.tsx +++ b/terminology-ui/src/modules/edit-concept/basic-information/diagrams.tsx @@ -7,12 +7,14 @@ import { FullwidthTextarea, ItemsList, } from './concept-diagrams-and-sources.styles'; +import { LocalizedValue } from '@app/common/interfaces/interfaces-v2'; interface DiagramsProps { diagrams: DiagramType[]; setDiagrams: (value: DiagramType[]) => void; handleRemove: (s?: ListType[], d?: DiagramType[]) => void; isError: boolean; + languages: string[]; } export default function Diagrams({ @@ -20,6 +22,7 @@ export default function Diagrams({ setDiagrams, handleRemove, isError, + languages, }: DiagramsProps) { const { t } = useTranslation('admin'); @@ -27,9 +30,15 @@ export default function Diagrams({ setDiagrams([ ...diagrams, { - name: '', + name: languages.reduce((names, l) => { + names[l] = ''; + return names; + }, {} as LocalizedValue), url: '', - description: '', + description: languages.reduce((desc, l) => { + desc[l] = ''; + return desc; + }, {} as LocalizedValue), id: v4(), }, ]); @@ -42,6 +51,23 @@ export default function Diagrams({ ); }; + const handleLocalizedUpdate = ( + id: string, + key: 'name' | 'description', + lang: string, + value: string + ) => { + setDiagrams( + diagrams.map((d) => { + if (d.id !== id) { + return d; + } else { + return { ...d, [key]: { ...d[key], [lang]: value } }; + } + }) + ); + }; + const handleUpdate = (id: string, key: string, value: string) => { setDiagrams( diagrams.map((d) => { @@ -71,15 +97,6 @@ export default function Diagrams({ </Button> </div> - <TextInput - labelText={t('diagram-name')} - defaultValue={diagram.name} - onChange={(e) => - handleUpdate(diagram.id, 'name', e?.toString() ?? '') - } - status={isError && diagram.name === '' ? 'error' : 'default'} - /> - <TextInput labelText={t('diagram-url')} defaultValue={diagram.url} @@ -93,14 +110,41 @@ export default function Diagrams({ } /> - <FullwidthTextarea - labelText={t('description')} - optionalText={t('optional')} - defaultValue={diagram.description} - onChange={(e) => - handleUpdate(diagram.id, 'description', e.target.value) - } - /> + {languages.map((lang) => ( + <TextInput + key={`link-name-${lang}`} + labelText={`${t('diagram-name')}, ${lang}`} + defaultValue={diagram.name[lang]} + onChange={(e) => + handleLocalizedUpdate( + diagram.id, + 'name', + lang, + e?.toString() ?? '' + ) + } + status={ + isError && diagram.name[lang] === '' ? 'error' : 'default' + } + /> + ))} + + {languages.map((lang) => ( + <FullwidthTextarea + key={`link-description-${lang}`} + labelText={`${t('description')}, ${lang}`} + optionalText={t('optional')} + defaultValue={diagram.description[lang]} + onChange={(e) => + handleLocalizedUpdate( + diagram.id, + 'description', + lang, + e.target.value ?? '' + ) + } + /> + ))} </ColoredBlock> </li> ))} diff --git a/terminology-ui/src/modules/edit-concept/basic-information/other-information.tsx b/terminology-ui/src/modules/edit-concept/basic-information/other-information.tsx index 3657d38e1..92ab85179 100644 --- a/terminology-ui/src/modules/edit-concept/basic-information/other-information.tsx +++ b/terminology-ui/src/modules/edit-concept/basic-information/other-information.tsx @@ -13,7 +13,6 @@ interface OtherInformationProps { update: (object: BasicInfoUpdate) => void; initialValues?: { conceptClass: string; - wordClass: string; }; } diff --git a/terminology-ui/src/modules/edit-concept/concept-terms-block/concept-term-block-types.tsx b/terminology-ui/src/modules/edit-concept/concept-terms-block/concept-term-block-types.tsx index 6ec631447..123922e8f 100644 --- a/terminology-ui/src/modules/edit-concept/concept-terms-block/concept-term-block-types.tsx +++ b/terminology-ui/src/modules/edit-concept/concept-terms-block/concept-term-block-types.tsx @@ -1,6 +1,5 @@ export interface ConceptTermType { changeNote: string; // Muutoshistoria - draftComment: string; // Ei käytössä editorialNote: ItemType[]; historyNote: string; id: string; @@ -11,7 +10,6 @@ export interface ConceptTermType { status: string; termConjugation: string; // Termin luku termEquivalency: string; // Termin vastaavuus - termEquivalencyRelation: string; // Termi, johon vastaavuus liittyy | Ei käytössä termFamily: string; termHomographNumber: string; termInfo: string; // Termin lisätieto diff --git a/terminology-ui/src/modules/edit-concept/concept-terms-block/term-form.tsx b/terminology-ui/src/modules/edit-concept/concept-terms-block/term-form.tsx index 8e89be767..7a4873947 100644 --- a/terminology-ui/src/modules/edit-concept/concept-terms-block/term-form.tsx +++ b/terminology-ui/src/modules/edit-concept/concept-terms-block/term-form.tsx @@ -72,37 +72,37 @@ export default function TermForm({ const termFamily = [ { labelText: t('term-family.masculine', { ns: 'common' }), - uniqueItemId: 'masculine', + uniqueItemId: 'MASCULINE', }, { labelText: t('term-family.neutral', { ns: 'common' }), - uniqueItemId: 'neutral', + uniqueItemId: 'NEUTRAL', }, { labelText: t('term-family.feminine', { ns: 'common' }), - uniqueItemId: 'feminine', + uniqueItemId: 'FEMININE', }, ]; const termConjugation = [ { labelText: t('term-conjugation.singular', { ns: 'common' }), - uniqueItemId: 'singular', + uniqueItemId: 'SINGULAR', }, { labelText: t('term-conjugation.plural', { ns: 'common' }), - uniqueItemId: 'plural', + uniqueItemId: 'PLURAL', }, ]; const wordClasses = [ { labelText: translateWordClass('adjective', t), - uniqueItemId: 'adjective', + uniqueItemId: 'ADJECTIVE', }, { labelText: translateWordClass('verb', t), - uniqueItemId: 'verb', + uniqueItemId: 'VERB', }, ]; diff --git a/terminology-ui/src/modules/edit-concept/generate-concept.tsx b/terminology-ui/src/modules/edit-concept/generate-concept.tsx index 901622cec..8fdfe38bb 100644 --- a/terminology-ui/src/modules/edit-concept/generate-concept.tsx +++ b/terminology-ui/src/modules/edit-concept/generate-concept.tsx @@ -1,805 +1,80 @@ -import { Concept } from '@app/common/interfaces/concept.interface'; -import { Term } from '@app/common/interfaces/term.interface'; -import toUri from '@app/common/utils/to-uri'; -import { v4 } from 'uuid'; -import { EditConceptType, RelationInfoType } from './new-concept.types'; +import { ConceptTermType, EditConceptType } from './new-concept.types'; +import { Concept, Term } from '@app/common/interfaces/interfaces-v2'; interface generateConceptProps { data: EditConceptType; - terminologyId: string; - initialValue?: Concept; - lastModifiedBy?: string; + isEdit: boolean; } -const regex = '(?s)^.*$'; - -export default function generateConcept({ +export default function generateConceptPayload({ data, - terminologyId, - initialValue, - lastModifiedBy, + isEdit, }: generateConceptProps) { - const now = new Date(); - let matchingIds: string[] = []; - let relatedMatchIds: string[] = []; - let closeMatchIds: string[] = []; - let broadMatchIds: string[] = []; - let narrowMatchIds: string[] = []; - - let referrers: object; - if (initialValue && initialValue.referrers) { - const temp = new Map(); - - Object.keys(initialValue.referrers).forEach((key) => { - if ( - typeof initialValue.referrers[key as keyof Concept['referrers']] !== - 'string' - ) { - const obj = initialValue.referrers[ - key as keyof Concept['referrers'] - ]?.map((referrer) => ({ - id: referrer.id, - type: { - graph: { - id: terminologyId, - }, - id: 'Collection', - uri: '', - }, - })); - - temp.set(key, obj); - } - }); - - referrers = Object.fromEntries(temp); - } else { - referrers = {}; - } - - const terms = data.terms.map((term) => { - const initialTerm = initialValue - ? getInitialTerm(term.id, initialValue.references) - : null; - - const obj: any = { - createdBy: initialValue ? initialTerm?.createdBy ?? '' : '', - createdDate: initialValue - ? initialTerm?.createdDate ?? '' - : now.toISOString(), - id: term.id, - lastModifiedBy: initialValue ? lastModifiedBy ?? '' : '', - lastModifiedDate: now.toISOString(), - properties: { - changeNote: [ - { - lang: '', - regex: regex, - value: term.changeNote, - }, - ], - draftComment: [ - { - lang: '', - regex: regex, - value: '', - }, - ], - editorialNote: term.editorialNote - ? term.editorialNote.map((note) => ({ - lang: '', - regex: regex, - value: note.value, - })) - : [], - historyNote: [ - { - lang: '', - regex: regex, - value: term.historyNote, - }, - ], - prefLabel: [ - { - lang: term.language, - regex: regex, - value: term.prefLabel, - }, - ], - scope: [ - { - lang: '', - regex: regex, - value: term.scope, - }, - ], - source: term.source - ? term.source.map((note) => ({ - lang: '', - regex: regex, - value: note.value, - })) - : [], - status: [ - { - lang: '', - regex: regex, - value: term.status.toUpperCase(), - }, - ], - termConjugation: [ - { - lang: '', - regex: regex, - value: term.termConjugation, - }, - ], - termEquivalency: [ - { - lang: '', - regex: regex, - value: term.termEquivalency, - }, - ], - termEquivalencyRelation: [ - { - lang: '', - regex: regex, - value: term.termEquivalencyRelation, - }, - ], - termFamily: [ - { - lang: '', - regex: regex, - value: term.termFamily, - }, - ], - termHomographNumber: [ - { - lang: '', - regex: regex, - value: term.termHomographNumber, - }, - ], - termInfo: [ - { - lang: '', - regex: regex, - value: term.termInfo, - }, - ], - termStyle: [ - { - lang: '', - regex: regex, - value: term.termStyle, - }, - ], - wordClass: [ - { - lang: '', - regex: regex, - value: term.wordClass, - }, - ], - }, - references: {}, - referrers: - initialValue && term.termType === 'recommended-term' - ? { - prefLabelXl: [ - { - id: initialValue.id, - type: { - graph: { - id: terminologyId, - }, - id: 'Concept', - uri: '', - }, - }, - ], - } - : {}, - type: { - graph: { - id: terminologyId, - }, - id: 'Term', - uri: initialValue ? '' : 'http://www.w3.org/2008/05/skos-xl#Label', - }, - }; - - if (initialTerm) { - obj.code = initialTerm.code; - obj.uri = initialTerm.uri; - } - - return obj; - }); - - const exactMatch = getExternalReference( - data.basicInformation.relationalInfo.matchInOther, - 'exactMatch', - initialValue, - terminologyId - ); - - let externalTerms = exactMatch; - - matchingIds = [...matchingIds, ...exactMatch.map((match) => match.id)]; - - if (data.basicInformation.relationalInfo.relatedConceptInOther) { - const relatedMatch = getExternalReference( - data.basicInformation.relationalInfo.relatedConceptInOther, - 'relatedMatch', - initialValue, - terminologyId - ); - relatedMatchIds = [ - ...relatedMatchIds, - ...relatedMatch.map((match) => match.id), - ]; - externalTerms = [...externalTerms, ...relatedMatch]; - } - - if (data.basicInformation.relationalInfo.closeMatch) { - const closeMatch = getExternalReference( - data.basicInformation.relationalInfo.closeMatch, - 'closeMatch', - initialValue, - terminologyId - ); - closeMatchIds = [...closeMatchIds, ...closeMatch.map((match) => match.id)]; - externalTerms = [...externalTerms, ...closeMatch]; - } - - if (data.basicInformation.relationalInfo.broadInOther) { - const broadMatch = getExternalReference( - data.basicInformation.relationalInfo.broadInOther, - 'broadMatch', - initialValue, - terminologyId - ); - broadMatchIds = [...narrowMatchIds, ...broadMatch.map((match) => match.id)]; - externalTerms = [...externalTerms, ...broadMatch]; - } - - if (data.basicInformation.relationalInfo.narrowInOther) { - const narrowMatch = getExternalReference( - data.basicInformation.relationalInfo.narrowInOther, - 'narrowMatch', - initialValue, - terminologyId - ); - narrowMatchIds = [ - ...narrowMatchIds, - ...narrowMatch.map((match) => match.id), - ]; - externalTerms = [...externalTerms, ...narrowMatch]; - } - - const retVal = [ - ...terms, - ...externalTerms, - { - createdBy: initialValue ? initialValue.createdBy : '', - createdDate: now.toISOString(), - id: initialValue ? initialValue.id : v4(), - lastModifiedBy: '', - lastModifiedDate: now.toISOString(), - properties: { - changeNote: [ - { - lang: '', - regex: regex, - value: data.basicInformation.orgInfo.changeHistory ?? '', - }, - ], - conceptClass: [ - { - lang: '', - regex: regex, - value: data.basicInformation.otherInfo.conceptClass ?? '', - }, - ], - conceptScope: [ - { - lang: '', - regex: regex, - value: '', - }, - ], - definition: data.basicInformation.definition - ? Object.keys(data.basicInformation.definition).map((lang) => ({ - lang: lang, - regex: regex, - value: data.basicInformation.definition[lang] ?? '', - })) - : [ - { - lang: '', - regex: regex, - value: '', - }, - ], - editorialNote: data.basicInformation.orgInfo.editorialNote.map( - (note) => ({ - lang: '', - regex: regex, - value: note.value, - }) - ), - example: data.basicInformation.example.map((ex) => ({ - lang: ex.lang, - regex: regex, - value: ex.value ?? '', - })), - externalLink: - data.basicInformation.diagramAndSource.diagrams?.length > 0 - ? data.basicInformation.diagramAndSource.diagrams?.map( - (diagram) => ({ - lang: '', - regex: regex, - value: `"{"name":"${diagram.name ?? ''}","url":"${ - diagram.url ? toUri(diagram.url) : '' - }","description":"${diagram.description ?? ''}"}"`, - }) - ) - : [{ lang: '', regex: regex, value: '' }], - historyNote: [ - { - lang: '', - regex: regex, - value: data.basicInformation.orgInfo?.etymology ?? '', - }, - ], - notation: [ - { - lang: '', - regex: regex, - value: '', - }, - ], - note: data.basicInformation.note?.map((n) => ({ - lang: n.lang, - regex: regex, - value: n.value ?? '', - })), - source: - data.basicInformation.diagramAndSource.sources?.length > 0 - ? data.basicInformation.diagramAndSource.sources.map((source) => ({ - lang: '', - regex: regex, - value: source.value ?? '', - })) - : [{ lang: '', regex: regex, value: '' }], - status: [ - { - lang: '', - regex: regex, - value: data.basicInformation.status, - }, - ], - subjectArea: [ - { - lang: '', - regex: regex, - value: data.basicInformation.subject ?? '', - }, - ], - wordClass: [ - { - lang: '', - regex: regex, - value: '', - }, - ], - }, - references: { - altLabelXl: data.terms - .filter((term) => term.termType === 'synonym') - .map((term) => ({ - id: term.id, - type: { - graph: { - id: terminologyId, - }, - id: 'Term', - uri: initialValue - ? '' - : 'http://www.w3.org/2008/05/skos-xl#Label', - }, - })), - broader: data.basicInformation.relationalInfo.broaderConcept.map( - (basic) => ({ - id: basic.id, - type: { - graph: { - id: terminologyId, - }, - id: 'Concept', - uri: '', - }, - }) - ), - closeMatch: closeMatchIds.map((id) => ({ - id: id, - type: { - graph: { - id: terminologyId, - }, - id: 'ConceptLink', - uri: initialValue - ? '' - : 'http://www.w3.org/1999/02/22-rdf-syntax-ns#Resource', - }, - })), - exactMatch: matchingIds.map((id) => ({ - id: id, - type: { - graph: { - id: terminologyId, - }, - id: 'ConceptLink', - uri: initialValue - ? '' - : 'http://www.w3.org/1999/02/22-rdf-syntax-ns#Resource', - }, - })), - hasPart: data.basicInformation.relationalInfo.hasPartConcept.map( - (part) => ({ - id: part.id, - type: { - graph: { - id: terminologyId, - }, - id: 'Concept', - uri: '', - }, - }) - ), - hiddenTerm: [], - isPartOf: data.basicInformation.relationalInfo.isPartOfConcept.map( - (part) => ({ - id: part.id, - type: { - graph: { - id: terminologyId, - }, - id: 'Concept', - uri: '', - }, - }) - ), - narrower: data.basicInformation.relationalInfo.narrowerConcept - ? data.basicInformation.relationalInfo.narrowerConcept.map( - (narrow) => ({ - id: narrow.id, - type: { - graph: { - id: terminologyId, - }, - id: 'Concept', - uri: '', - }, - }) - ) - : [], - notRecommendedSynonym: data.terms - .filter((term) => term.termType === 'not-recommended-synonym') - .map((term) => ({ - id: term.id, - type: { - graph: { - id: terminologyId, - }, - id: 'Term', - uri: initialValue - ? '' - : 'http://www.w3.org/2008/05/skos-xl#Label', - }, - })), - prefLabelXl: data.terms - .filter((term) => term.termType === 'recommended-term') - .map((term) => ({ - id: term.id, - type: { - graph: { - id: terminologyId, - }, - id: 'Term', - uri: initialValue - ? '' - : 'http://www.w3.org/2008/05/skos-xl#Label', - }, - })), - related: data.basicInformation.relationalInfo.relatedConcept.map( - (related) => ({ - id: related.id, - type: { - graph: { - id: terminologyId, - }, - id: 'Concept', - uri: '', - }, - }) - ), - relatedMatch: relatedMatchIds.map((id) => ({ - id: id, - type: { - graph: { - id: terminologyId, - }, - id: 'ConceptLink', - uri: initialValue - ? '' - : 'http://www.w3.org/1999/02/22-rdf-syntax-ns#Resource', - }, - })), - broadMatch: broadMatchIds.map((id) => ({ - id: id, - type: { - graph: { - id: terminologyId, - }, - id: 'ConceptLink', - uri: initialValue - ? '' - : 'http://www.w3.org/1999/02/22-rdf-syntax-ns#Resource', - }, - })), - narrowMatch: narrowMatchIds.map((id) => ({ - id: id, - type: { - graph: { - id: terminologyId, - }, - id: 'ConceptLink', - uri: initialValue - ? '' - : 'http://www.w3.org/1999/02/22-rdf-syntax-ns#Resource', - }, - })), - searchTerm: data.terms - .filter((term) => term.termType === 'search-term') - .map((term) => ({ - id: term.id, - type: { - graph: { - id: terminologyId, - }, - id: 'Term', - uri: initialValue - ? '' - : 'http://www.w3.org/2008/05/skos-xl#Label', - }, - })), - }, - referrers: referrers, - type: { - graph: { - id: terminologyId, - }, - id: 'Concept', - uri: initialValue ? '' : 'http://www.w3.org/2004/02/skos/core#Concept', - }, - }, - ]; - - if (initialValue) { - retVal[retVal.length - 1].code = initialValue.code ?? ''; - retVal[retVal.length - 1].uri = initialValue.uri ?? ''; - } - - const initialTermIds: string[] = initialValue - ? Object.keys(initialValue?.references).flatMap((key) => { - if ( - [ - 'broader', - 'closeMatch', - 'exactMatch', - 'hasPart', - 'isPartOf', - 'narrower', - 'related', - 'relatedMatch', - 'broadMatch', - 'narrowMatch', - ].includes(key) - ) { - return []; - } - return ( - initialValue?.references[key as keyof Concept['references']]?.map( - (val) => val.id - ) ?? [] - ); - }) - : []; - - let initialInOtherIds: string[] = initialValue - ? initialValue.references.exactMatch - ?.map((match) => match.id ?? '') - .filter((val) => val) ?? [] - : []; - - initialInOtherIds = initialValue - ? [ - ...initialInOtherIds, - ...(initialValue.references.relatedMatch - ?.map((match) => match.id ?? '') - .filter((val) => val) ?? []), - ] - : initialInOtherIds; - - initialInOtherIds = initialValue - ? [ - ...initialInOtherIds, - ...(initialValue.references.closeMatch - ?.map((match) => match.id ?? '') - .filter((val) => val) ?? []), - ] - : initialInOtherIds; - - initialInOtherIds = initialValue - ? [ - ...initialInOtherIds, - ...(initialValue.references.broadMatch - ?.map((match) => match.id ?? '') - .filter((val) => val) ?? []), - ] - : initialInOtherIds; - - initialInOtherIds = initialValue - ? [ - ...initialInOtherIds, - ...(initialValue.references.narrowMatch - ?.map((match) => match.id ?? '') - .filter((val) => val) ?? []), - ] - : initialInOtherIds; - - const newTermIds = data.terms.map((term) => term.id); - const inOtherIds = [ - ...data.basicInformation.relationalInfo.matchInOther.map( - (match) => match.id - ), - ...data.basicInformation.relationalInfo.relatedConceptInOther.map( - (related) => related.id - ), - ...data.basicInformation.relationalInfo.closeMatch.map((close) => close.id), - ...data.basicInformation.relationalInfo.broadInOther.map( - (broad) => broad.id - ), - ...data.basicInformation.relationalInfo.narrowInOther.map( - (narrow) => narrow.id - ), - ]; - - let deleteVal = - initialTermIds.length > 0 - ? initialTermIds - .filter((initId) => !newTermIds.includes(initId)) - .map((id) => ({ - id: id, - type: { - graph: { - id: terminologyId, - }, - id: 'Term', - uri: '', - }, - })) - : []; - - deleteVal = - initialInOtherIds.length > 0 - ? [ - ...deleteVal, - ...initialInOtherIds - .filter((id) => !inOtherIds.includes(id)) - .map((id) => ({ - id: id, - type: { - graph: { - id: terminologyId, - }, - id: 'ConceptLink', - uri: '', - }, - })), - ] - : deleteVal; - + const basicInfo = data.basicInformation; + const relations = basicInfo.relationalInfo; return { - delete: deleteVal, - save: retVal, - }; + ...(!isEdit && { identifier: basicInfo.identifier }), + definition: basicInfo.definition, + notes: basicInfo.note.map((note) => ({ + language: note.lang, + value: note.value, + })), + examples: basicInfo.example.map((ex) => ({ + language: ex.lang, + value: ex.value, + })), + subjectArea: basicInfo.subject, + status: basicInfo.status, + sources: basicInfo.diagramAndSource.sources.map((source) => source.value), + links: basicInfo.diagramAndSource.diagrams.map((link) => ({ + name: link.name, + uri: link.url, + description: link.description, + })), + changeNote: basicInfo.orgInfo.changeHistory, + historyNote: basicInfo.orgInfo.etymology, + editorialNotes: basicInfo.orgInfo.editorialNote.map((e) => e.value), + conceptClass: basicInfo.otherInfo.conceptClass, + recommendedTerms: mapTerms(data.terms, 'recommended-term'), + synonyms: mapTerms(data.terms, 'synonym'), + notRecommendedTerms: mapTerms(data.terms, 'not-recommended-synonym'), + searchTerms: mapTerms(data.terms, 'search-term'), + broader: relations.broaderConcept.map((r) => r.id), + narrower: relations.narrowerConcept.map((r) => r.id), + related: relations.relatedConcept.map((r) => r.id), + isPartOf: relations.isPartOfConcept.map((r) => r.id), + hasPart: relations.hasPartConcept.map((r) => r.id), + broadMatch: relations.broadInOther.map((r) => r.id), + narrowMatch: relations.narrowInOther.map((r) => r.id), + exactMatch: relations.matchInOther.map((r) => r.id), + closeMatch: relations.closeMatch.map((r) => r.id), + relatedMatch: relations.relatedConceptInOther.map((r) => r.id), + } as Concept; } -function getInitialTerm(id: string, terms: Concept['references']): Term | null { - let retVal = null; - - for (const [, values] of Object.entries(terms)) { - values.forEach((value) => { - if (value.id === id) { - retVal = value; - } - }); - - if (retVal !== null) { - break; - } - } - - return retVal; -} - -function getExternalReference( - relations: RelationInfoType[], - key: - | 'exactMatch' - | 'closeMatch' - | 'relatedMatch' - | 'narrowMatch' - | 'broadMatch', - initialValue: Concept | undefined, - terminologyId: string -) { - return ( - relations?.map((match) => { - const initialTerm = initialValue?.references[key]?.find( - (m) => m.id === match.id - ); - const id = initialTerm?.id ?? v4(); - - return { - code: initialTerm?.code, - createdBy: initialTerm?.createdBy ?? '', - createdDate: initialTerm?.createdDate ?? '', - id: id, - lastModifiedBy: initialTerm?.lastModifiedBy ?? '', - lastModifiedDate: initialTerm?.lastModifiedDate ?? '', - properties: { - prefLabel: Object.keys(match.label).map((key) => ({ - lang: key, - regex: regex, - value: match.label[key], - })), - targetGraph: [ - { - lang: '', - regex: regex, - value: match.terminologyId, - }, - ], - targetId: [ - { - lang: '', - regex: regex, - value: match.targetId ?? '', - }, - ], - vocabularyLabel: Object.keys(match.terminologyLabel).map((key) => ({ - lang: key, - regex: regex, - value: match.terminologyLabel[key], - })), - }, - references: {}, - referrers: {}, - type: { - graph: { - id: terminologyId, - }, - id: 'ConceptLink', - uri: 'http://www.w3.org/1999/02/22-rdf-syntax-ns#Resource', - }, - uri: initialTerm?.uri, - }; - }) ?? [] - ); +function mapTerms(terms: ConceptTermType[], type: string) { + return terms + .filter((term) => term.termType === type) + .map( + (term) => + ({ + language: term.language, + label: term.prefLabel, + homographNumber: +term.termHomographNumber, + status: term.status, + termInfo: term.termInfo, + scope: term.scope, + historyNote: term.historyNote, + changeNote: term.changeNote, + termStyle: term.termStyle, + termFamily: term.termFamily !== '' ? term.termFamily : undefined, + termConjugation: + term.termConjugation !== '' ? term.termConjugation : undefined, + termEquivalency: + term.termEquivalency !== '' ? term.termEquivalency : undefined, + wordClass: term.wordClass !== '' ? term.wordClass : undefined, + sources: term.source.map((s) => s.value), + editorialNotes: term.editorialNote.map((e) => e.value), + } as Term) + ); } diff --git a/terminology-ui/src/modules/edit-concept/generate-form-data.tsx b/terminology-ui/src/modules/edit-concept/generate-form-data.tsx index fcef8f39e..de75427c4 100644 --- a/terminology-ui/src/modules/edit-concept/generate-form-data.tsx +++ b/terminology-ui/src/modules/edit-concept/generate-form-data.tsx @@ -1,31 +1,26 @@ -import { ConceptLink } from '@app/common/interfaces/concept-link.interface'; -import { Concept } from '@app/common/interfaces/concept.interface'; -import { Term } from '@app/common/interfaces/term.interface'; -import { Property } from '@app/common/interfaces/termed-data-types.interface'; -import getDiagramValues from '@app/common/utils/get-diagram-values'; import { v4 } from 'uuid'; import { ConceptTermType, EditConceptType } from './new-concept.types'; -import { LocalizedValue } from '@app/common/interfaces/interfaces-v2'; +import { + ConceptInfo, + ConceptReferenceInfo, + LocalizedListItem, + LocalizedValue, + Term, +} from '@app/common/interfaces/interfaces-v2'; export default function generateFormData( - preferredTerms: { - lang: string; - value: string; - regex: string; - }[], - conceptData?: Concept, - terminologyLabel?: LocalizedValue + preferredTerms: LocalizedValue, + conceptData?: ConceptInfo ): EditConceptType { if (!conceptData) { return { - terms: preferredTerms.map((term) => ({ + terms: Object.keys(preferredTerms).map((lang) => ({ changeNote: '', - draftComment: '', editorialNote: [], historyNote: '', - id: '', - language: term.lang, - prefLabel: term.value, + id: v4(), + language: lang, + prefLabel: preferredTerms[lang], scope: '', source: [], status: 'DRAFT', @@ -40,6 +35,7 @@ export default function generateFormData( wordClass: '', })), basicInformation: { + identifier: '', definition: {}, example: [], status: 'DRAFT', @@ -56,7 +52,6 @@ export default function generateFormData( }, otherInfo: { conceptClass: '', - wordClass: '', }, relationalInfo: { broaderConcept: [], @@ -74,288 +69,89 @@ export default function generateFormData( }; } - const definition = new Map(); - conceptData.properties.definition?.map((d) => { - definition.set(d.lang, d.value); - }) ?? {}; - const retVal: EditConceptType = { - terms: [], basicInformation: { - definition: Object.fromEntries(definition), + identifier: conceptData.identifier, + definition: conceptData.definition, + example: conceptData.examples.map(mapListType), + status: conceptData.status, + subject: conceptData.subjectArea, + note: conceptData.notes.map(mapListType), diagramAndSource: { - diagrams: - conceptData.properties.externalLink?.map((l) => { - const dValues = getDiagramValues(l.value); - return { - description: dValues.description, - id: v4(), - name: dValues.name, - url: dValues.url, - }; - }) ?? [], - sources: - conceptData.properties.source?.map((e) => ({ - id: v4(), - lang: e.lang, - value: e.value, - })) ?? [], + diagrams: [], + sources: conceptData.sources.map(mapListType), }, - example: - conceptData.properties.example?.map((e) => ({ - id: v4(), - lang: e.lang, - value: e.value, - })) ?? [], - note: - conceptData.properties.note?.map((n) => ({ - id: v4(), - lang: n.lang, - value: n.value, - })) ?? [], orgInfo: { - changeHistory: conceptData.properties.changeNote?.[0].value ?? '', - editorialNote: - conceptData.properties.editorialNote?.map((en) => ({ - id: v4(), - lang: '', - value: en.value, - })) ?? [], - etymology: conceptData.properties.historyNote?.[0].value ?? '', + changeHistory: conceptData.changeNote, + editorialNote: conceptData.editorialNotes.map(mapListType), + etymology: conceptData.historyNote, }, otherInfo: { - conceptClass: conceptData.properties.conceptClass?.[0]?.value ?? '', - wordClass: conceptData.properties.wordClass?.[0]?.value ?? '', + conceptClass: conceptData.conceptClass, }, relationalInfo: { - broaderConcept: - conceptData.references.broader?.map((broad) => { - { - return { - id: broad.id, - label: - broad.references.prefLabelXl - ?.map((label) => { - return ( - label.properties.prefLabel - ?.map((l) => ({ - [l.lang]: l.value, - })) - .reduce((l) => l) ?? {} - ); - }) - .reduce((l) => l) ?? {}, - terminologyId: broad.type.graph.id, - terminologyLabel: terminologyLabel ?? {}, - }; - } - }) ?? [], - hasPartConcept: - conceptData.references.hasPart?.map((part) => { - { - return { - id: part.id, - label: - part.references?.prefLabelXl - ?.map((label) => { - return ( - label.properties.prefLabel - ?.map((l) => ({ - [l.lang]: l.value, - })) - .reduce((l) => l) ?? {} - ); - }) - .reduce((l) => l) ?? {}, - terminologyId: part.type.graph.id, - terminologyLabel: terminologyLabel ?? {}, - }; - } - }) ?? [], - isPartOfConcept: - conceptData.references?.isPartOf?.map((part) => { - { - return { - id: part.id, - label: - part.references.prefLabelXl - ?.map((label) => { - return ( - label.properties.prefLabel - ?.map((l) => ({ - [l.lang]: l.value, - })) - .reduce((l) => l) ?? {} - ); - }) - .reduce((l) => l) ?? {}, - terminologyId: part.type.graph.id, - terminologyLabel: terminologyLabel ?? {}, - }; - } - }) ?? [], - narrowerConcept: - conceptData.references?.narrower?.map((narrow) => { - { - return { - id: narrow.id, - label: - narrow.references?.prefLabelXl - ?.map((label) => { - return ( - label.properties.prefLabel - ?.map((l) => ({ - [l.lang]: l.value, - })) - .reduce((l) => l) ?? {} - ); - }) - .reduce((l) => l) ?? {}, - terminologyId: narrow.type.graph.id, - terminologyLabel: terminologyLabel ?? {}, - }; - } - }) ?? [], - relatedConcept: - conceptData.references.related?.map((r) => { - { - return { - id: r.id, - label: - r.references?.prefLabelXl - ?.map((label) => { - return ( - label.properties.prefLabel - ?.map((l) => ({ - [l.lang]: l.value, - })) - .reduce((l) => l) ?? {} - ); - }) - .reduce((l) => l) ?? {}, - terminologyId: r.type.graph.id, - terminologyLabel: terminologyLabel ?? {}, - }; - } - }) ?? [], - matchInOther: mapExternalConcept(conceptData.references.exactMatch), - relatedConceptInOther: mapExternalConcept( - conceptData.references.relatedMatch - ), - closeMatch: mapExternalConcept(conceptData.references.closeMatch), - broadInOther: mapExternalConcept(conceptData.references.broadMatch), - narrowInOther: mapExternalConcept(conceptData.references.narrowMatch), + broaderConcept: conceptData.broader.map(mapRelationInfo), + narrowerConcept: conceptData.narrower.map(mapRelationInfo), + relatedConcept: conceptData.related.map(mapRelationInfo), + isPartOfConcept: conceptData.isPartOf.map(mapRelationInfo), + hasPartConcept: conceptData.hasPart.map(mapRelationInfo), + relatedConceptInOther: conceptData.relatedMatch.map(mapRelationInfo), + matchInOther: conceptData.exactMatch.map(mapRelationInfo), + closeMatch: conceptData.closeMatch.map(mapRelationInfo), + broadInOther: conceptData.broadMatch.map(mapRelationInfo), + narrowInOther: conceptData.narrowMatch.map(mapRelationInfo), }, - status: conceptData.properties.status?.[0].value ?? 'DRAFT', - subject: conceptData.properties.subjectArea?.[0].value ?? '', }, + terms: [ + ...conceptData.recommendedTerms.map((t) => + mapTerm(t, 'recommended-term') + ), + ...conceptData.synonyms.map((t) => mapTerm(t, 'synonym')), + ...conceptData.notRecommendedTerms.map((t) => + mapTerm(t, 'not-recommended-synonym') + ), + ...conceptData.searchTerms.map((t) => mapTerm(t, 'search-term')), + ], }; - const termKeys = [ - 'altLabelXl', - 'notRecommendedSynonym', - 'prefLabelXl', - 'searchTerm', - ]; - - const terms = Object.keys(conceptData.references) - .filter((key) => termKeys.includes(key)) - ?.map((key) => - conceptData.references[key as keyof Concept['references']]?.map( - (reference) => { - const r = reference as Term; - let termType = ''; + return retVal; +} - switch (key) { - case 'altLabelXl': { - termType = 'synonym'; - break; - } - case 'notRecommendedSynonym': { - termType = 'not-recommended-synonym'; - break; - } - case 'prefLabelXl': { - termType = 'recommended-term'; - break; - } - case 'searchTerm': { - termType = 'search-term'; - break; - } - } +const mapListType = (e: LocalizedListItem | string) => ({ + id: v4(), + lang: typeof e === 'string' ? '' : e.language, + value: typeof e === 'string' ? e : e.value, +}); - return { - changeNote: r.properties.changeNote?.[0].value ?? '', - draftComment: '', - editorialNote: - r.properties.editorialNote?.map((note) => ({ - id: v4(), - lang: note.lang, - value: note.value, - })) ?? [], - historyNote: r.properties.historyNote?.[0].value ?? '', - id: r.id, - language: r.properties.prefLabel?.[0].lang ?? '', - prefLabel: r.properties.prefLabel?.[0].value ?? '', - scope: r.properties.scope?.[0].value ?? '', - source: - r.properties.source?.map((note) => ({ - id: v4(), - lang: note.lang, - value: note.value, - })) ?? [], - status: r.properties.status?.[0].value ?? '', - termConjugation: r.properties.termConjugation?.[0].value ?? '', - termEquivalency: r.properties.termEquivalency?.[0].value ?? '', - termEquivalencyRelation: - r.properties.termEquivalencyRelation?.[0].value ?? '', - termFamily: r.properties.termFamily?.[0].value ?? '', - termHomographNumber: - r.properties.termHomographNumber?.[0].value ?? '', - termInfo: r.properties.termInfo?.[0].value ?? '', - termStyle: r.properties.termStyle?.[0].value ?? '', - termType: termType, - wordClass: r.properties.wordClass?.[0].value ?? '', - }; - } - ) - ) - .flat() - .filter((term) => term) as ConceptTermType[]; +const mapTerm = (term: Term, type: string) => { + return { + changeNote: term.changeNote, + editorialNote: term.editorialNotes?.map(mapListType), + historyNote: term.historyNote, + id: v4(), + language: term.language, + prefLabel: term.label, + scope: term.scope, + source: term.sources?.map(mapListType), + status: term.status, + termConjugation: term.termConjugation, + termEquivalency: term.termEquivalency, + termFamily: term.termFamily, + termHomographNumber: term.homographNumber + ? term.homographNumber + '' + : null, + termInfo: term.termInfo, + termStyle: term.termStyle, + termType: type, + wordClass: term.wordClass, + } as ConceptTermType; +}; +const mapRelationInfo = (r: ConceptReferenceInfo) => { return { - ...retVal, - terms: terms, + id: r.referenceURI, + label: r.label, + terminologyId: r.prefix, + terminologyLabel: r.terminologyLabel, }; -} - -function mapExternalConcept(conceptLinks: ConceptLink[] | undefined) { - if (!conceptLinks) { - return []; - } - return conceptLinks.map((link) => { - const terminologyLabels = link.properties.vocabularyLabel?.reduce( - (labels, label) => { - labels.set(label.lang, label.value); - return labels; - }, - new Map() - ); - - const conceptLabels = link.properties.prefLabel?.reduce((labels, label) => { - labels.set(label.lang, label.value); - return labels; - }, new Map()); - - return { - id: link.id ?? '', - label: conceptLabels ? Object.fromEntries(conceptLabels) : {}, - terminologyId: link.properties.targetGraph?.[0].value ?? '', - terminologyLabel: terminologyLabels - ? Object.fromEntries(terminologyLabels) - : {}, - targetId: link.properties.targetId?.[0].value ?? '', - }; - }); -} +}; diff --git a/terminology-ui/src/modules/edit-concept/index.tsx b/terminology-ui/src/modules/edit-concept/index.tsx index aa45865db..27f8f14bf 100644 --- a/terminology-ui/src/modules/edit-concept/index.tsx +++ b/terminology-ui/src/modules/edit-concept/index.tsx @@ -1,5 +1,4 @@ import { Breadcrumb, BreadcrumbLink } from 'yti-common-ui/breadcrumb'; -import PropertyValue from '@app/common/components/property-value'; import { MainTitle, SubTitle, BadgeBar } from 'yti-common-ui/title-block'; import { useGetTerminologyQuery } from '@app/common/components/vocabulary/vocabulary.slice'; import { useTranslation } from 'next-i18next'; @@ -10,14 +9,11 @@ import { NewConceptBlock, PageHelpText } from './new-concept.styles'; import ConceptTermsBlock from './concept-terms-block'; import { asString } from '@app/common/utils/hooks/use-url-state'; import { useEffect, useState } from 'react'; -import generateConcept from './generate-concept'; -import { useAddConceptMutation } from '@app/common/components/modify/modify.slice'; import { BasicInfo, EditConceptType, ConceptTermType, } from './new-concept.types'; -import { Concept } from '@app/common/interfaces/concept.interface'; import generateFormData from './generate-form-data'; import { useSelector } from 'react-redux'; import { @@ -34,10 +30,19 @@ import { v4 } from 'uuid'; import { StatusChip } from 'yti-common-ui/status-chip'; import { compareLocales } from '@app/common/utils/compare-locals'; import { getLanguageVersion } from 'yti-common-ui/utils/get-language-version'; +import { + ConceptInfo, + LocalizedValue, +} from '@app/common/interfaces/interfaces-v2'; +import generateConceptPayload from './generate-concept'; +import { + useCreateConceptMutation, + useUpdateConceptMutation, +} from '@app/common/components/concept/concept.slice'; interface EditConceptProps { terminologyId: string; - conceptData?: Concept; + conceptData?: ConceptInfo; } export default function EditConcept({ @@ -47,7 +52,8 @@ export default function EditConcept({ const { t, i18n } = useTranslation('concept'); const { isSmall } = useBreakpoints(); const router = useRouter(); - const [addConcept, addConceptStatus] = useAddConceptMutation(); + const [createConcept, createConceptStatus] = useCreateConceptMutation(); + const [updateConcept, updateConceptStatus] = useUpdateConceptMutation(); const [isCreating, setIsCreating] = useState(false); const user = useSelector(selectLogin()); const { data: terminology } = useGetTerminologyQuery({ @@ -60,23 +66,19 @@ export default function EditConcept({ const [languages] = useState( terminology?.languages?.slice().sort((a, b) => compareLocales(a, b)) ?? [] ); - const [preferredTerms] = useState< - { - lang: string; - regex: string; - value: string; - }[] - >(getPreferredTerms()); - const [postedData, setPostedData] = - useState<ReturnType<typeof generateConcept>>(); + const [preferredTerms] = useState<LocalizedValue>(getPreferredTerms()); const [formData, setFormData] = useState<EditConceptType>( - generateFormData(preferredTerms, conceptData, terminology?.label) + generateFormData(preferredTerms, conceptData) ); const [errors, setErrors] = useState<FormError>(validateForm(formData)); + const isEdit = 'conceptId' in router.query; + const { disableConfirmation, enableConfirmation } = useConfirmBeforeLeavingPage( - preferredTerms.length > 0 && !conceptData ? 'enabled' : 'disabled' + Object.keys(preferredTerms).length > 0 && !conceptData + ? 'enabled' + : 'disabled' ); const handlePost = () => { @@ -94,15 +96,21 @@ export default function EditConcept({ } setIsCreating(true); - const concept = generateConcept({ + const concept = generateConceptPayload({ data: formData, - terminologyId: terminologyId, - initialValue: conceptData, - lastModifiedBy: `${user.firstName} ${user.lastName}`, + isEdit, }); - setPostedData(concept); - addConcept(concept); + if (isEdit) { + updateConcept({ + prefix: terminologyId, + conceptId: formData.basicInformation.identifier, + concept, + }); + } else { + createConcept({ prefix: terminologyId, concept }); + } + disableConfirmation(); }; @@ -125,14 +133,12 @@ export default function EditConcept({ }; useEffect(() => { - if (addConceptStatus.isSuccess && postedData) { + if (createConceptStatus.isSuccess || updateConceptStatus.isSuccess) { router.replace( - `/terminology/${terminologyId}/concept/${ - postedData.save[postedData.save.length - 1].id - }` + `/terminology/${terminologyId}/concept/${formData.basicInformation.identifier}` ); } - }, [addConceptStatus, postedData, terminologyId, router]); + }, [createConceptStatus, updateConceptStatus, terminologyId, router]); useEffect(() => { if (formData.terms.some((term) => term.id === '')) { @@ -147,7 +153,7 @@ export default function EditConcept({ // eslint-disable-next-line react-hooks/exhaustive-deps }, []); - if (preferredTerms.length < 1) { + if (Object.keys(preferredTerms).length < 1) { return ( <> <Breadcrumb> @@ -202,11 +208,10 @@ export default function EditConcept({ })} </BreadcrumbLink> )} - {!!preferredTerms?.length && ( - <BreadcrumbLink url="" current> - <PropertyValue property={preferredTerms} /> - </BreadcrumbLink> - )} + + <BreadcrumbLink url="" current> + {getLanguageVersion({ data: preferredTerms, lang: i18n.language })} + </BreadcrumbLink> </Breadcrumb> <NewConceptBlock variant="main" id="main" $isSmall={isSmall}> @@ -218,7 +223,7 @@ export default function EditConcept({ .join(', ')} </SubTitle> <MainTitle> - <PropertyValue property={preferredTerms} /> + {getLanguageVersion({ data: preferredTerms, lang: i18n.language })} </MainTitle> <BadgeBar> {t('heading')} @@ -244,6 +249,8 @@ export default function EditConcept({ initialValues={formData.basicInformation} languages={languages} errors={errors} + terminologyId={terminologyId} + isEdit={isEdit} /> <FormFooter @@ -260,25 +267,10 @@ export default function EditConcept({ </> ); - function getPreferredTerms(): { - lang: string; - regex: string; - value: string; - }[] { - const temp = conceptData?.references?.prefLabelXl?.flatMap((label) => - label.properties.prefLabel?.flatMap((l) => ({ - lang: l.lang, - regex: '', - value: l.value, - })) - ); - - if (temp && !temp.some((t) => t === undefined)) { - return temp as ReturnType<typeof getPreferredTerms>; - } - - return languages - .map((lang) => ({ lang, value: asString(router.query[lang]), regex: '' })) - .filter(({ value }) => !!value.trim()); + function getPreferredTerms(): LocalizedValue { + return languages.reduce((terms, lang) => { + terms[lang] = asString(router.query[lang]); + return terms; + }, {} as LocalizedValue); } } diff --git a/terminology-ui/src/modules/edit-concept/new-concept.types.tsx b/terminology-ui/src/modules/edit-concept/new-concept.types.tsx index dfe949832..6598fb0d9 100644 --- a/terminology-ui/src/modules/edit-concept/new-concept.types.tsx +++ b/terminology-ui/src/modules/edit-concept/new-concept.types.tsx @@ -1,6 +1,7 @@ +import { LocalizedValue } from '@app/common/interfaces/interfaces-v2'; + export interface ConceptTermType { changeNote: string; - draftComment: string; editorialNote: ListType[]; historyNote: string; id: string; @@ -21,9 +22,8 @@ export interface ConceptTermType { } export interface BasicInfo { - definition: { - [key: string]: string; - }; + identifier: string; + definition: LocalizedValue; example: ListType[]; status: string; subject: string; @@ -39,7 +39,6 @@ export interface BasicInfo { }; otherInfo: { conceptClass: string; - wordClass: string; }; relationalInfo: { broaderConcept: RelationInfoType[]; @@ -60,7 +59,6 @@ export interface RelationInfoType { label: { [key: string]: string }; terminologyId: string; terminologyLabel: { [key: string]: string }; - targetId?: string; } export interface ListType { @@ -70,8 +68,8 @@ export interface ListType { } export interface DiagramType { - description: string; - name: string; + description: { [key: string]: string }; + name: { [key: string]: string }; url: string; id: string; } diff --git a/terminology-ui/src/modules/edit-concept/validate-form.tsx b/terminology-ui/src/modules/edit-concept/validate-form.tsx index 3c1f31284..48022ab36 100644 --- a/terminology-ui/src/modules/edit-concept/validate-form.tsx +++ b/terminology-ui/src/modules/edit-concept/validate-form.tsx @@ -147,7 +147,7 @@ export default function validateForm(data: EditConceptType): FormError { if ( data.terms .map((t) => t.termConjugation) - .filter((c) => c && !['singular', 'plural'].includes(c)).length > 0 + .filter((c) => c && !['SINGULAR', 'PLURAL'].includes(c)).length > 0 ) { errors.termConjugation = true; } From 57f1e9f71f486ebf076095ba88c892ffac50d063 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kalle=20Kytt=C3=A4l=C3=A4?= <kalle.kyttala@rebase.fi> Date: Tue, 3 Sep 2024 12:24:31 +0300 Subject: [PATCH 11/35] refactor comparing locales, add translations --- .../components/block/multilingual-block.tsx | 12 +++++++++- .../components/form/language-selector.tsx | 11 +++++++--- common-ui/utils/compare-locales.ts | 22 +++++++++++++++++++ terminology-ui/public/locales/en/admin.json | 2 ++ terminology-ui/public/locales/fi/admin.json | 2 ++ terminology-ui/public/locales/sv/admin.json | 2 ++ .../info-dropdown/info-expander.tsx | 4 ++-- .../components/new-concept-modal/index.tsx | 2 +- .../subscription/remove-subscription.tsx | 15 +++++++------ .../src/common/utils/compare-locals.ts | 3 ++- .../src/common/utils/translation-helpers.ts | 2 ++ .../concept/diagrams-and-sources-expander.tsx | 2 +- terminology-ui/src/modules/concept/utils.tsx | 6 ++--- .../src/modules/edit-collection/index.tsx | 2 +- .../edit-concept/generate-form-data.tsx | 7 +++++- .../src/modules/edit-concept/index.tsx | 2 +- .../modules/edit-concept/validate-form.tsx | 8 ++++++- .../own-information/subscription-block.tsx | 6 ++--- .../src/modules/vocabulary/index.tsx | 9 ++++---- .../vocabulary/terminology-list-filter.tsx | 3 +-- 20 files changed, 88 insertions(+), 34 deletions(-) create mode 100644 common-ui/utils/compare-locales.ts diff --git a/common-ui/components/block/multilingual-block.tsx b/common-ui/components/block/multilingual-block.tsx index cf878d5a4..4b8eb3c0c 100644 --- a/common-ui/components/block/multilingual-block.tsx +++ b/common-ui/components/block/multilingual-block.tsx @@ -5,6 +5,7 @@ import { import { v4 } from 'uuid'; import { maxBy } from 'lodash'; import SanitizedTextContent from '../../components/sanitized-text-content'; +import { compareLocales } from '../../utils/compare-locales'; interface MultilingualBlockProps { data: { [key: string]: string }; @@ -21,9 +22,18 @@ export default function MultilingualBlock({ const id = v4().slice(0, 8); const maxSize = maxBy(Object.keys(data), (key) => key.length)?.length ?? 0; + const sortedData = + Object.keys(data) + ?.slice() + .sort((t1, t2) => compareLocales(t1, t2)) + .reduce((result, lang) => { + result[lang] = data[lang]; + return result; + }, {} as { [key: string]: string }) ?? {}; + return ( <MultilingualBlockWrapper $maxSize={maxSize}> - {Object.keys(data).map((key, idx) => ( + {Object.keys(sortedData).map((key, idx) => ( <MultilingualBlockItem key={`multilingual-block-${id}-item-${idx}`} lang={key} diff --git a/common-ui/components/form/language-selector.tsx b/common-ui/components/form/language-selector.tsx index 1167f51ad..9c266d959 100644 --- a/common-ui/components/form/language-selector.tsx +++ b/common-ui/components/form/language-selector.tsx @@ -12,6 +12,7 @@ import { DescriptionInput, } from './language-selector.styles'; import { TEXT_AREA_MAX, TEXT_INPUT_MAX } from '../../utils/constants'; +import { compareLocales } from '../../utils/compare-locales'; export interface LanguageBlockType { labelText: string; @@ -92,6 +93,10 @@ export default function LanguageSelector( ); }; + const sortedItems = selectedItems + .slice() + .sort((a, b) => compareLocales(a.uniqueItemId, b.uniqueItemId)); + return ( <> <MultiSelect @@ -102,12 +107,12 @@ export default function LanguageSelector( visualPlaceholder={props.visualPlaceholder} $isWide={props.isWide} allowItemAddition={false} - selectedItems={selectedItems} + selectedItems={sortedItems} onItemSelectionsChange={(e) => handleSelectedChange(e as (MultiSelectData & LanguageBlockType)[]) } onRemoveAll={() => setSelectedItems([])} - defaultSelectedItems={selectedItems} + defaultSelectedItems={sortedItems} ariaChipActionLabel={props.ariaChipActionLabel ?? ''} ariaSelectedAmountText={props.ariaSelectedAmountText ?? ''} ariaOptionsAvailableText={props.ariaOptionsAvailableText ?? ''} @@ -118,7 +123,7 @@ export default function LanguageSelector( id="language-selector" /> - {selectedItems.map((item, idx) => ( + {sortedItems.map((item, idx) => ( <LanguageBlock padding="m" className="language-block" diff --git a/common-ui/utils/compare-locales.ts b/common-ui/utils/compare-locales.ts new file mode 100644 index 000000000..6fade3b11 --- /dev/null +++ b/common-ui/utils/compare-locales.ts @@ -0,0 +1,22 @@ +export function compareLocales(locale1: string, locale2: string): number { + // const t1Lang = typeof t1 === 'object' ? t1.lang.toLowerCase() : t1; + //const t2Lang = typeof t2 === 'object' ? t2.lang.toLowerCase() : t2; + + if (locale1 === 'fi' || locale2 === 'fi') { + return locale1 === 'fi' ? -1 : 1; + } + + if (locale1 === 'sv' || locale2 === 'sv') { + return locale1 === 'sv' ? -1 : 1; + } + + if (locale1 === 'en' || locale2 === 'en') { + return locale1 === 'en' ? -1 : 1; + } + + if (locale1 !== locale2) { + return locale1 > locale2 ? 1 : -1; + } + + return 0; +} diff --git a/terminology-ui/public/locales/en/admin.json b/terminology-ui/public/locales/en/admin.json index 0efa5272b..515e1313a 100644 --- a/terminology-ui/public/locales/en/admin.json +++ b/terminology-ui/public/locales/en/admin.json @@ -78,6 +78,7 @@ "concept-class": "Concept class", "concept-diagram-or-link": "Concept diagram or other link", "concept-diagrams-and-sources": "Concept diagrams and sources", + "concept-identifier": "Identifier", "concept-import": { "prefLabel-column-missing": "prefLabel column is missing", "prefLabel-row-missing": "preflabel needs to be defined in at least 1 language", @@ -125,6 +126,7 @@ "diagramsUri": "One or more diagram's url is not in valid form", "editorialNote": "At least one editorial note is missing a descriptive text", "example": "At least one example is missing a descriptive text", + "identifier": "Concept's identifier is undefined", "language": "Term's language is undefined", "note": "At least one note is missing a descriptive text", "prefLabel": "Term's name is undefined", diff --git a/terminology-ui/public/locales/fi/admin.json b/terminology-ui/public/locales/fi/admin.json index 9ca27bb6a..b9574f567 100644 --- a/terminology-ui/public/locales/fi/admin.json +++ b/terminology-ui/public/locales/fi/admin.json @@ -78,6 +78,7 @@ "concept-class": "Käsitteen luokka", "concept-diagram-or-link": "Käsitekaavio tai muu linkki", "concept-diagrams-and-sources": "Käsitekaaviot ja lähteet", + "concept-identifier": "Tunnus", "concept-import": { "prefLabel-column-missing": "prefLabel kenttä puuttuu", "prefLabel-row-missing": "prefLabel pitää määrittää vähintään yhdellä kielellä", @@ -125,6 +126,7 @@ "diagramsUri": "Yhdellä tai useammalla käsitekaaviolla on virheellinen verkko-osoite", "editorialNote": "Vähintään yhdeltä ylläpitäjän muistiipanolta puuttuu selite", "example": "Vähintään yhdeltä käyttöesimerkiltä puuttuu selite", + "identifier": "Käsitteen tunnusta ei ole määritetty", "language": "Termin kieltä ei ole määritetty", "note": "Vähintään yhdeltä huomautukselta puuttuu selite", "prefLabel": "Termin nimeä ei ole määritetty", diff --git a/terminology-ui/public/locales/sv/admin.json b/terminology-ui/public/locales/sv/admin.json index ed3a22377..4c9a6a676 100644 --- a/terminology-ui/public/locales/sv/admin.json +++ b/terminology-ui/public/locales/sv/admin.json @@ -78,6 +78,7 @@ "concept-class": "Begreppets klass", "concept-diagram-or-link": "Begreppsdiagram eller en annan länke", "concept-diagrams-and-sources": "Begreppsdiagram och källor", + "concept-identifier": "Identifierare", "concept-import": { "prefLabel-column-missing": "prefLabel fältet har inte definierats", "prefLabel-row-missing": "prefLabel måste anges åtminstone på ett språk", @@ -125,6 +126,7 @@ "diagramsUri": "Ett eller flera begreppsdiagram har en felaktig webbadress", "editorialNote": "Beskrivning fattas åtminstone från en administratörens anmärkning", "example": "Beskrivning fattas åtminstone från ett användningsexempel", + "identifier": "Begreppets identifierare har inte definierats", "language": "Termens språk har inte definierats", "note": "Beskrivning fattas åtminstone från en anmärkning", "prefLabel": "Termens namn har inte definierats", diff --git a/terminology-ui/src/common/components/info-dropdown/info-expander.tsx b/terminology-ui/src/common/components/info-dropdown/info-expander.tsx index 054c4366a..62017c77d 100644 --- a/terminology-ui/src/common/components/info-dropdown/info-expander.tsx +++ b/terminology-ui/src/common/components/info-dropdown/info-expander.tsx @@ -34,16 +34,16 @@ import ConceptImportModal from '../concept-import'; import { useGetConceptResultQuery } from '../vocabulary/vocabulary.slice'; import useUrlState from '@app/common/utils/hooks/use-url-state'; import { useStoreDispatch } from '@app/store'; -import UpdateWithFileModal from '../update-with-file-modal'; import StatusMassEdit from '../status-mass-edit'; import isEmail from 'validator/lib/isEmail'; import { useRouter } from 'next/router'; -import { compareLocales } from '@app/common/utils/compare-locals'; + import { TerminologyInfo, TerminologyType, } from '@app/common/interfaces/interfaces-v2'; import { getLanguageVersion } from 'yti-common-ui/utils/get-language-version'; +import { compareLocales } from 'yti-common-ui/utils/compare-locales'; const Subscription = dynamic( () => import('@app/common/components/subscription/subscription') diff --git a/terminology-ui/src/common/components/new-concept-modal/index.tsx b/terminology-ui/src/common/components/new-concept-modal/index.tsx index 221a349db..42d3b87f1 100644 --- a/terminology-ui/src/common/components/new-concept-modal/index.tsx +++ b/terminology-ui/src/common/components/new-concept-modal/index.tsx @@ -3,7 +3,7 @@ import dynamic from 'next/dynamic'; import { useState } from 'react'; import { Button, IconPlus } from 'suomifi-ui-components'; import { useGetAuthenticatedUserMutMutation } from '../login/login.slice'; -import { compareLocales } from '@app/common/utils/compare-locals'; +import { compareLocales } from 'yti-common-ui/utils/compare-locales'; const NewConceptModalDynamic = dynamic(() => import('./new-concept-modal')); diff --git a/terminology-ui/src/common/components/subscription/remove-subscription.tsx b/terminology-ui/src/common/components/subscription/remove-subscription.tsx index c07599149..e69396f1e 100644 --- a/terminology-ui/src/common/components/subscription/remove-subscription.tsx +++ b/terminology-ui/src/common/components/subscription/remove-subscription.tsx @@ -17,6 +17,7 @@ import { RemoveModalContent, RemoveModalUl, } from './subscription.styles'; +import { getLanguageVersion } from 'yti-common-ui/utils/get-language-version'; interface RemoveSubscriptionProps { resources?: Resource[]; @@ -45,7 +46,7 @@ export default function RemoveSubscription({ setUnsubscribedItem( resource.prefLabel - ? getPrefLabel({ prefLabels: resource.prefLabel, lang: i18n.language }) + ? getLanguageVersion({ data: resource.prefLabel, lang: i18n.language }) : resource.uri ); setVisible(false); @@ -65,8 +66,8 @@ export default function RemoveSubscription({ .join(','); setUnsubscribedItem( resources[0].prefLabel - ? getPrefLabel({ - prefLabels: resources[0].prefLabel, + ? getLanguageVersion({ + data: resources[0].prefLabel, lang: i18n.language, }) : resources[0].uri @@ -125,8 +126,8 @@ export default function RemoveSubscription({ <li key={`resource-${idx}`}> <Text smallScreen className="to-be-removed-subscription"> {resource.prefLabel - ? getPrefLabel({ - prefLabels: resource.prefLabel, + ? getLanguageVersion({ + data: resource.prefLabel, lang: i18n.language, }) : resource.uri} @@ -139,8 +140,8 @@ export default function RemoveSubscription({ <Paragraph> <Text smallScreen> {resource.prefLabel - ? getPrefLabel({ - prefLabels: resource.prefLabel, + ? getLanguageVersion({ + data: resource.prefLabel, lang: i18n.language, }) : resource.uri} diff --git a/terminology-ui/src/common/utils/compare-locals.ts b/terminology-ui/src/common/utils/compare-locals.ts index 634705142..a68764409 100644 --- a/terminology-ui/src/common/utils/compare-locals.ts +++ b/terminology-ui/src/common/utils/compare-locals.ts @@ -1,3 +1,4 @@ +import { compareLocales } from 'yti-common-ui/utils/compare-locales'; import { LocalizedListItem } from '../interfaces/interfaces-v2'; import { Term } from '../interfaces/term.interface'; import { Property } from '../interfaces/termed-data-types.interface'; @@ -8,7 +9,7 @@ export interface TermBlockType { } // Prioritizes Finnish and Swedish over other languages -export function compareLocales( +export function compareLocales_OLD( t1: Property | string, t2: Property | string ): number { diff --git a/terminology-ui/src/common/utils/translation-helpers.ts b/terminology-ui/src/common/utils/translation-helpers.ts index 47f0de8a5..f6343dcf7 100644 --- a/terminology-ui/src/common/utils/translation-helpers.ts +++ b/terminology-ui/src/common/utils/translation-helpers.ts @@ -158,6 +158,8 @@ export function translateEditConceptError(error: string, t: TFunction) { return t('edit-concept-error.diagramsUri', { ns: 'admin' }); case 'termConjugation': return t('edit-concept-error.termConjugation', { ns: 'admin' }); + case 'identifier': + return t('edit-concept-error.identifier', { ns: 'admin' }); default: return t('edit-concept-error.default', { ns: 'admin' }); } diff --git a/terminology-ui/src/modules/concept/diagrams-and-sources-expander.tsx b/terminology-ui/src/modules/concept/diagrams-and-sources-expander.tsx index 8488c6b56..b326de2dc 100644 --- a/terminology-ui/src/modules/concept/diagrams-and-sources-expander.tsx +++ b/terminology-ui/src/modules/concept/diagrams-and-sources-expander.tsx @@ -64,7 +64,7 @@ export default function DiagramsAndSourcesExpander({ </BasicBlock> )} - {concept?.sources.length && ( + {concept?.sources.length > 0 && ( <BasicBlock title={t('field-sources')}> <PropertyList> {concept?.sources?.map((source, idx) => ( diff --git a/terminology-ui/src/modules/concept/utils.tsx b/terminology-ui/src/modules/concept/utils.tsx index 5f2a0e2f1..ea3ad921b 100644 --- a/terminology-ui/src/modules/concept/utils.tsx +++ b/terminology-ui/src/modules/concept/utils.tsx @@ -3,11 +3,9 @@ import { LocalizedValue, Term, } from '@app/common/interfaces/interfaces-v2'; -import { - compareLocales, - sortPropertyListByLanguage, -} from '@app/common/utils/compare-locals'; +import { sortPropertyListByLanguage } from '@app/common/utils/compare-locals'; import { TFunction } from 'next-i18next'; +import { compareLocales } from 'yti-common-ui/utils/compare-locales'; const langOrder: { [key: string]: string } = { fi: 'a', diff --git a/terminology-ui/src/modules/edit-collection/index.tsx b/terminology-ui/src/modules/edit-collection/index.tsx index 1fe78a3dd..35ec4a6f7 100644 --- a/terminology-ui/src/modules/edit-collection/index.tsx +++ b/terminology-ui/src/modules/edit-collection/index.tsx @@ -37,7 +37,7 @@ import { useGetAuthenticatedUserMutMutation, useGetAuthenticatedUserQuery, } from '@app/common/components/login/login.slice'; -import { compareLocales } from '@app/common/utils/compare-locals'; +import { compareLocales } from 'yti-common-ui/utils/compare-locales'; import { getLanguageVersion } from 'yti-common-ui/utils/get-language-version'; import { ConceptCollectionInfo } from '@app/common/interfaces/interfaces-v2'; diff --git a/terminology-ui/src/modules/edit-concept/generate-form-data.tsx b/terminology-ui/src/modules/edit-concept/generate-form-data.tsx index de75427c4..9108e54d8 100644 --- a/terminology-ui/src/modules/edit-concept/generate-form-data.tsx +++ b/terminology-ui/src/modules/edit-concept/generate-form-data.tsx @@ -78,7 +78,12 @@ export default function generateFormData( subject: conceptData.subjectArea, note: conceptData.notes.map(mapListType), diagramAndSource: { - diagrams: [], + diagrams: conceptData.links.map((link) => ({ + id: v4(), + name: link.name, + description: link.description, + url: link.uri, + })), sources: conceptData.sources.map(mapListType), }, orgInfo: { diff --git a/terminology-ui/src/modules/edit-concept/index.tsx b/terminology-ui/src/modules/edit-concept/index.tsx index 27f8f14bf..7fa39790d 100644 --- a/terminology-ui/src/modules/edit-concept/index.tsx +++ b/terminology-ui/src/modules/edit-concept/index.tsx @@ -28,7 +28,7 @@ import { useBreakpoints } from 'yti-common-ui/media-query'; import { translateStatus } from '@app/common/utils/translation-helpers'; import { v4 } from 'uuid'; import { StatusChip } from 'yti-common-ui/status-chip'; -import { compareLocales } from '@app/common/utils/compare-locals'; +import { compareLocales } from 'yti-common-ui/utils/compare-locales'; import { getLanguageVersion } from 'yti-common-ui/utils/get-language-version'; import { ConceptInfo, diff --git a/terminology-ui/src/modules/edit-concept/validate-form.tsx b/terminology-ui/src/modules/edit-concept/validate-form.tsx index 48022ab36..feff73b6e 100644 --- a/terminology-ui/src/modules/edit-concept/validate-form.tsx +++ b/terminology-ui/src/modules/edit-concept/validate-form.tsx @@ -27,6 +27,7 @@ export const EmptyFormError = { diagramsUri: false, termConjugation: false, total: false, + identifier: false, }; export default function validateForm(data: EditConceptType): FormError { @@ -42,8 +43,13 @@ export default function validateForm(data: EditConceptType): FormError { diagramsUri: false, termConjugation: false, total: false, + identifier: false, }; + if (!data.basicInformation.identifier) { + errors.identifier = true; + } + // If any term has a name that is empty or undefined if ( data.terms.filter((term) => !term.prefLabel || term.prefLabel === '') @@ -115,7 +121,7 @@ export default function validateForm(data: EditConceptType): FormError { data.basicInformation.diagramAndSource.diagrams.filter( (diagram) => !diagram.name || - diagram.name === '' || + Object.entries(diagram.name).some((e) => e[1] === '') || !diagram.url || diagram.url === '' ).length > 0 diff --git a/terminology-ui/src/modules/own-information/subscription-block.tsx b/terminology-ui/src/modules/own-information/subscription-block.tsx index 6f9b2a2b2..6dee9064f 100644 --- a/terminology-ui/src/modules/own-information/subscription-block.tsx +++ b/terminology-ui/src/modules/own-information/subscription-block.tsx @@ -12,7 +12,7 @@ import { useToggleSubscriptionMutation } from '../../common/components/subscript import { setAlert } from '../../common/components/alert/alert.slice'; import { useEffect, useState } from 'react'; import RemoveSubscription from '../../common/components/subscription/remove-subscription'; -import getPrefLabel from '../../common/utils/get-preflabel'; +import { getLanguageVersion } from 'yti-common-ui/utils/get-language-version'; interface SubscriptionBlockProps { subscriptions: Subscriptions; @@ -94,8 +94,8 @@ export default function SubscriptionBlock({ > <SuomiLink href=""> {resource.prefLabel - ? getPrefLabel({ - prefLabels: resource.prefLabel, + ? getLanguageVersion({ + data: resource.prefLabel, lang: i18n.language, }) : resource.uri} diff --git a/terminology-ui/src/modules/vocabulary/index.tsx b/terminology-ui/src/modules/vocabulary/index.tsx index da993b8be..ea2946666 100644 --- a/terminology-ui/src/modules/vocabulary/index.tsx +++ b/terminology-ui/src/modules/vocabulary/index.tsx @@ -35,7 +35,6 @@ import { useRouter } from 'next/router'; import HasPermission from '@app/common/utils/has-permission'; import dynamic from 'next/dynamic'; import ConceptImportModal from '@app/common/components/concept-import'; -import getPrefLabel from '@app/common/utils/get-preflabel'; import { TitleType, TitleTypeAndStatusWrapper, @@ -107,13 +106,13 @@ export default function Vocabulary({ id }: VocabularyProps) { return conceptsData.responseObjects.map((concept) => ({ id: concept.id, description: - getPrefLabel({ - prefLabels: concept.definition, + getLanguageVersion({ + data: concept.definition, lang: urlState.lang !== '' ? urlState.lang : i18n.language, }) ?? '', status: concept.status, - title: getPrefLabel({ - prefLabels: concept.label, + title: getLanguageVersion({ + data: concept.label, lang: urlState.lang !== '' ? urlState.lang : i18n.language, }), titleLink: `${id}/concept/${concept.identifier}`, diff --git a/terminology-ui/src/modules/vocabulary/terminology-list-filter.tsx b/terminology-ui/src/modules/vocabulary/terminology-list-filter.tsx index cc18aa4b4..4a4d2ff1a 100644 --- a/terminology-ui/src/modules/vocabulary/terminology-list-filter.tsx +++ b/terminology-ui/src/modules/vocabulary/terminology-list-filter.tsx @@ -9,8 +9,7 @@ import Filter, { } from 'yti-common-ui/filter'; import useUrlState from '@app/common/utils/hooks/use-url-state'; import { FilterTopPartBlock } from './vocabulary.styles'; -import { Property } from '@app/common/interfaces/termed-data-types.interface'; -import { compareLocales } from '@app/common/utils/compare-locals'; +import { compareLocales } from 'yti-common-ui/utils/compare-locales'; export interface TerminologyListFilterProps { isModal?: boolean; From a0ccbc34369d2e4cee66edc0299ab99f88d489a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kalle=20Kytt=C3=A4l=C3=A4?= <kalle.kyttala@rebase.fi> Date: Thu, 5 Sep 2024 08:38:02 +0300 Subject: [PATCH 12/35] add missing search functionalities, fix enum values, handle resource in use error --- common-ui/interfaces/error.interface.ts | 5 ++ terminology-ui/public/locales/en/admin.json | 1 + terminology-ui/public/locales/en/alert.json | 3 +- terminology-ui/public/locales/fi/admin.json | 1 + terminology-ui/public/locales/fi/alert.json | 3 +- terminology-ui/public/locales/sv/admin.json | 1 + .../relational-information-block/index.tsx | 8 +-- .../relational-modal-content.tsx | 14 ++--- .../render-concepts.tsx | 11 ++-- .../render-expander-content.tsx | 36 ++---------- .../common/components/removal-modal/index.tsx | 23 ++++++++ .../src/common/interfaces/interfaces-v2.ts | 2 + .../src/common/utils/translation-helpers.ts | 32 +++++------ .../src/modules/collection/index.tsx | 4 +- .../concept-picker/picker-modal.tsx | 18 +++--- .../concept-terms-block/term-form.tsx | 55 +++++++++++++------ .../modules/edit-concept/generate-concept.tsx | 19 ++++++- 17 files changed, 139 insertions(+), 97 deletions(-) create mode 100644 common-ui/interfaces/error.interface.ts diff --git a/common-ui/interfaces/error.interface.ts b/common-ui/interfaces/error.interface.ts new file mode 100644 index 000000000..6afdd9aee --- /dev/null +++ b/common-ui/interfaces/error.interface.ts @@ -0,0 +1,5 @@ +export interface ApiError { + status: string; + message: string; + details?: string; +} diff --git a/terminology-ui/public/locales/en/admin.json b/terminology-ui/public/locales/en/admin.json index 515e1313a..b71ce7448 100644 --- a/terminology-ui/public/locales/en/admin.json +++ b/terminology-ui/public/locales/en/admin.json @@ -58,6 +58,7 @@ "changing-statuses": "Changing statuses", "choose-orgs": "Select contributors", "choose-term-conjugation": "Choose term conjugation", + "choose-term-equivalency": "Choose term equivalency", "choose-term-family": "Choose term family", "choose-term-word-class": "Choose term word class", "choose-type": "Choose type", diff --git a/terminology-ui/public/locales/en/alert.json b/terminology-ui/public/locales/en/alert.json index 9121d8a27..ea53816b8 100644 --- a/terminology-ui/public/locales/en/alert.json +++ b/terminology-ui/public/locales/en/alert.json @@ -14,5 +14,6 @@ "error-occurred_remove-concept": "An error occured while removing concept", "error-occurred_remove-terminology": "An error occured while removing terminology", "error-occurred_session": "An error occured with the session, try logging in again", - "error-occurred_unauthenticated": "An error occured with login information verification. Please login to the service again." + "error-occurred_unauthenticated": "An error occured with login information verification. Please login to the service again.", + "error-occured_resource-in-use": "Cannot remove because resource is in use" } diff --git a/terminology-ui/public/locales/fi/admin.json b/terminology-ui/public/locales/fi/admin.json index b9574f567..3fda071f8 100644 --- a/terminology-ui/public/locales/fi/admin.json +++ b/terminology-ui/public/locales/fi/admin.json @@ -58,6 +58,7 @@ "changing-statuses": "Muutetaan tilatietoja", "choose-orgs": "Valitse sisällöntuottajat", "choose-term-conjugation": "Valitse termin luku", + "choose-term-equivalency": "Valitse termin vastaavuus", "choose-term-family": "Valitse termin suku", "choose-term-word-class": "Valitse termin sanaluokka", "choose-type": "Valitse tyyppi", diff --git a/terminology-ui/public/locales/fi/alert.json b/terminology-ui/public/locales/fi/alert.json index 2f1dac809..c9773dc25 100644 --- a/terminology-ui/public/locales/fi/alert.json +++ b/terminology-ui/public/locales/fi/alert.json @@ -14,5 +14,6 @@ "error-occurred_remove-concept": "Käsitteen poistamisessa ilmeni ongelma", "error-occurred_remove-terminology": "Sanaston poistamisessa ilmeni ongelma", "error-occurred_session": "Istunnossa ilmeni ongelma, kokeile kirjautua uudelleen", - "error-occurred_unauthenticated": "Kirjautumistietojen varmistuksessa ilmeni ongelma. Kirjaudu palveluun uudestaan." + "error-occurred_unauthenticated": "Kirjautumistietojen varmistuksessa ilmeni ongelma. Kirjaudu palveluun uudestaan.", + "error-occured_resource-in-use": "Resurssia ei voi poistaa koska siihen on viittauksia." } diff --git a/terminology-ui/public/locales/sv/admin.json b/terminology-ui/public/locales/sv/admin.json index 4c9a6a676..8c800350d 100644 --- a/terminology-ui/public/locales/sv/admin.json +++ b/terminology-ui/public/locales/sv/admin.json @@ -58,6 +58,7 @@ "changing-statuses": "Statusuppgifter uppdateras", "choose-orgs": "Välj innehållsproducenter", "choose-term-conjugation": "Välj termens numerus", + "choose-term-equivalency": "Välj termens ekvivalens", "choose-term-family": "Välj termens genus", "choose-term-word-class": "Välj termens ordklass", "choose-type": "Välj typen", diff --git a/terminology-ui/src/common/components/relational-information-block/index.tsx b/terminology-ui/src/common/components/relational-information-block/index.tsx index 54ba7415a..709f93c4e 100644 --- a/terminology-ui/src/common/components/relational-information-block/index.tsx +++ b/terminology-ui/src/common/components/relational-information-block/index.tsx @@ -148,9 +148,7 @@ export default function RelationalInformationBlock({ {fromOther && ' - '} {fromOther && getLanguageVersion({ - data: concept.terminologyLabel ?? { - fi: 'TODO: terminology label', - }, + data: concept.terminologyLabel, lang: i18n.language, })} </Chip> @@ -221,10 +219,6 @@ function ManageRelationalInfoModal({ 'terminology' in concept ? concept.terminology.label : concept.terminologyLabel, - targetId: - 'targetId' in concept - ? (concept as RelationInfoType).targetId - : concept.id, })); setSelectedConcepts(choseWithoutHtml); handleClose(); diff --git a/terminology-ui/src/common/components/relational-information-block/relational-modal-content.tsx b/terminology-ui/src/common/components/relational-information-block/relational-modal-content.tsx index 969e91baa..b4a069bda 100644 --- a/terminology-ui/src/common/components/relational-information-block/relational-modal-content.tsx +++ b/terminology-ui/src/common/components/relational-information-block/relational-modal-content.tsx @@ -51,6 +51,7 @@ export default function RelationModalContent({ const [currPage, setCurrPage] = useState(1); const modalRef = createRef<HTMLDivElement>(); const pageSize = 20; + const namespace = getNamespace(terminologyId); const statuses: StatusesType[] = [ { @@ -86,13 +87,13 @@ export default function RelationModalContent({ ]; const handleSearch = () => { - const namespace = getNamespace(terminologyId); searchConcept({ - ...(fromOther ? { notInTerminologyId: namespace } : { namespace }), + ...(fromOther ? { excludeNamespace: namespace } : { namespace }), query: searchTerm, ...(status?.uniqueItemId && { status: [status.uniqueItemId] }), pageFrom: (currPage - 1) * 20, pageSize: 20, + extendTerminologies: true, }); }; @@ -105,24 +106,23 @@ export default function RelationModalContent({ return; } - const namespace = getNamespace(terminologyId); searchConcept({ - ...(fromOther ? { notInTerminologyId: namespace } : { namespace }), + ...(fromOther ? { excludeNamespace: namespace } : { namespace }), pageFrom: (currPage - 1) * pageSize, pageSize, + extendTerminologies: true, }); }; const handlePageChange = (num: number) => { setCurrPage(num); searchConcept({ - ...(fromOther - ? { notInTerminologyId: terminologyId } - : { terminologyId: terminologyId }), + ...(fromOther ? { excludeNamespace: terminologyId } : { namespace }), query: searchTerm, ...(status?.uniqueItemId && { status: [status.uniqueItemId] }), pageFrom: (num - 1) * pageSize, pageSize, + extendTerminologies: true, }); focusToTop(); }; diff --git a/terminology-ui/src/common/components/relational-information-block/render-concepts.tsx b/terminology-ui/src/common/components/relational-information-block/render-concepts.tsx index e170c0abd..1ab961a2c 100644 --- a/terminology-ui/src/common/components/relational-information-block/render-concepts.tsx +++ b/terminology-ui/src/common/components/relational-information-block/render-concepts.tsx @@ -14,6 +14,7 @@ import RenderExpanderContent from './render-expander-content'; import { useRouter } from 'next/router'; import { RelationInfoType } from '@app/modules/edit-concept/new-concept.types'; import { ConceptResponseObject } from '@app/common/interfaces/interfaces-v2'; +import { getLanguageVersion } from 'yti-common-ui/utils/get-language-version'; interface RenderConceptsProps { concepts?: ConceptResponseObject[]; @@ -82,10 +83,10 @@ export default function RenderConcepts({ toggleButtonAriaLabel={t('additional-information')} > <Checkbox - hintText={`TODO: terminology label - ${translateStatus( - concept.status ?? 'DRAFT', - t - )}`} + hintText={`${getLanguageVersion({ + data: concept.terminology.label, + lang: i18n.language, + })} - ${translateStatus(concept.status ?? 'DRAFT', t)}`} onClick={(e) => handleCheckbox(e, concept)} checked={chosen.some((chose) => chose.id === concept.id)} className="concept-checkbox" @@ -118,7 +119,7 @@ export default function RenderConcepts({ <RenderExpanderContent terminologyId={concept.terminology?.prefix} - conceptId={concept.identifier} + concept={concept} isOpen={ (expandersOpen?.filter( (c) => c[0] === concept.id diff --git a/terminology-ui/src/common/components/relational-information-block/render-expander-content.tsx b/terminology-ui/src/common/components/relational-information-block/render-expander-content.tsx index aeb8aa3e2..8c6b28c24 100644 --- a/terminology-ui/src/common/components/relational-information-block/render-expander-content.tsx +++ b/terminology-ui/src/common/components/relational-information-block/render-expander-content.tsx @@ -1,34 +1,25 @@ import { memo } from 'react'; import { useTranslation } from 'next-i18next'; -import { useGetConceptQuery } from '../concept/concept.slice'; import { useGetTerminologyQuery } from '../vocabulary/vocabulary.slice'; import { ExpanderContent } from 'suomifi-ui-components'; -import SaveSpinner from 'yti-common-ui/save-spinner'; import { BasicBlock, MultilingualBlock } from 'yti-common-ui/block'; import Separator from 'yti-common-ui/separator'; import FormattedDate from 'yti-common-ui/formatted-date'; import { getLanguageVersion } from 'yti-common-ui/utils/get-language-version'; -import { LocalizedValue } from '@app/common/interfaces/interfaces-v2'; +import { ConceptResponseObject } from '@app/common/interfaces/interfaces-v2'; interface RenderExpanderContentProps { terminologyId: string; - conceptId: string; + concept: ConceptResponseObject; isOpen: boolean; } function RenderExpanderContent({ terminologyId, - conceptId, isOpen, + concept, }: RenderExpanderContentProps) { const { t, i18n } = useTranslation('admin'); - const { data: concept, isLoading: conceptIsLoading } = useGetConceptQuery( - { - terminologyId: terminologyId, - conceptId: conceptId, - }, - { skip: !isOpen } - ); const { data: terminology } = useGetTerminologyQuery( { id: terminologyId, @@ -40,33 +31,17 @@ function RenderExpanderContent({ return null; } - if (conceptIsLoading) { - return ( - <ExpanderContent> - <SaveSpinner text={t('loading', { ns: 'common' })} /> - </ExpanderContent> - ); - } - return ( <ExpanderContent> {concept && ( <> <BasicBlock title={<h2>{t('preferred-terms')}</h2>}> - <MultilingualBlock - data={concept.recommendedTerms.reduce( - (label, term) => ({ - ...label, - [label[term.language]]: term.label ?? '', - }), - {} as LocalizedValue - )} - /> + <MultilingualBlock data={concept.label} /> </BasicBlock> {Object.keys(concept.definition).length > 0 && ( <BasicBlock title={<h2>{t('definition')}</h2>}> - <MultilingualBlock data={concept.definition} /> + <MultilingualBlock data={concept.definition} renderHtml={true} /> </BasicBlock> )} @@ -85,7 +60,6 @@ function RenderExpanderContent({ <BasicBlock title={t('modified-at')}> <FormattedDate date={concept?.modified} /> - {concept?.modifier?.name ? `, ${concept.modifier.name}` : ''} </BasicBlock> </> )} diff --git a/terminology-ui/src/common/components/removal-modal/index.tsx b/terminology-ui/src/common/components/removal-modal/index.tsx index 762f709dc..7fce61e9f 100644 --- a/terminology-ui/src/common/components/removal-modal/index.tsx +++ b/terminology-ui/src/common/components/removal-modal/index.tsx @@ -1,4 +1,5 @@ import { + translateErrroMessage, translateRemovalModalConfirmation, translateRemovalModalDescription, translateRemovalModalError, @@ -21,6 +22,7 @@ import { Text, } from 'suomifi-ui-components'; import { BasicBlock, BasicBlockExtraWrapper } from 'yti-common-ui/block'; +import { ApiError } from 'yti-common-ui/interfaces/error.interface'; import { useBreakpoints } from 'yti-common-ui/media-query'; import SaveSpinner from 'yti-common-ui/save-spinner'; import { terminologySearchApi } from '../terminology-search/terminology-search.slice'; @@ -230,6 +232,27 @@ export default function RemovalModal({ } if (isError()) { + //es-lint no-unsafe-optional-chaining + if (concept?.error) { + /*const data = concept.error.data as ApiError; + return ( + <InlineAlert status="error"> + {translateErrroMessage(data.message, t)} + {data.details && ( + <div> + {data.details?.split(',').map((d) => { + return ( + <> + {d} <br /> + </> + ); + })} + </div> + )} + </InlineAlert> + );*/ + } + return ( <InlineAlert status="error"> {translateRemovalModalError(dataType, t)} diff --git a/terminology-ui/src/common/interfaces/interfaces-v2.ts b/terminology-ui/src/common/interfaces/interfaces-v2.ts index 0d1500340..c40a0c610 100644 --- a/terminology-ui/src/common/interfaces/interfaces-v2.ts +++ b/terminology-ui/src/common/interfaces/interfaces-v2.ts @@ -167,6 +167,8 @@ export interface TerminologySearchRequest extends SearchRequest { export interface ConceptSearchRequest extends SearchRequest { namespace?: string; + excludeNamespace?: string; + extendTerminologies?: boolean; } export interface ResponseObject { diff --git a/terminology-ui/src/common/utils/translation-helpers.ts b/terminology-ui/src/common/utils/translation-helpers.ts index f6343dcf7..96f441453 100644 --- a/terminology-ui/src/common/utils/translation-helpers.ts +++ b/terminology-ui/src/common/utils/translation-helpers.ts @@ -81,22 +81,13 @@ export function translateTerminologyType( } } -export function translateTermStyle(termStyle: string, t: TFunction) { - switch (termStyle) { - case 'spoken-form': - return t('term-style.spoken-form', { ns: 'common' }); - default: - return termStyle; - } -} - export function translateTermFamily(termFamily: string, t: TFunction) { switch (termFamily) { - case 'masculine': + case 'MASCULINE': return t('term-family.masculine', { ns: 'common' }); - case 'neutral': + case 'NEUTRAL': return t('term-family.neutral', { ns: 'common' }); - case 'feminine': + case 'FEMININE': return t('term-family.feminine', { ns: 'common' }); default: return termFamily; @@ -108,9 +99,9 @@ export function translateTermConjugation( t: TFunction ) { switch (termConjugation) { - case 'singular': + case 'SINGULAR': return t('term-conjugation.singular', { ns: 'common' }); - case 'plural': + case 'PLURAL': return t('term-conjugation.plural', { ns: 'common' }); default: return termConjugation; @@ -119,9 +110,9 @@ export function translateTermConjugation( export function translateWordClass(wordClass: string, t: TFunction) { switch (wordClass) { - case 'adjective': + case 'ADJECTIVE': return t('word-class.adjective', { ns: 'common' }); - case 'verb': + case 'VERB': return t('word-class.verb', { ns: 'common' }); default: return wordClass; @@ -237,6 +228,15 @@ export function translateRemovalModalError( } } +export function translateErrroMessage(message: string, t: TFunction) { + switch (message) { + case 'resource-in-use': + return t('error-occured_resource-in-use', { ns: 'alert' }); + default: + return t('error-occured', { ns: 'alert' }); + } +} + export function translateRemovalModalTitle( type: 'terminology' | 'concept' | 'collection', t: TFunction diff --git a/terminology-ui/src/modules/collection/index.tsx b/terminology-ui/src/modules/collection/index.tsx index 4b22056cc..5ef3ad8b4 100644 --- a/terminology-ui/src/modules/collection/index.tsx +++ b/terminology-ui/src/modules/collection/index.tsx @@ -277,13 +277,13 @@ export default function Collection({ <BasicBlock title={t('vocabulary-info-created-at', { ns: 'common' })}> <FormattedDate date={collection?.created} /> - {collection?.creator.name && `, ${collection.creator.name}`} + {collection?.creator?.name && `, ${collection.creator.name}`} </BasicBlock> <BasicBlock title={t('vocabulary-info-modified-at', { ns: 'common' })} > <FormattedDate date={collection?.modified} /> - {collection?.modifier.name && `, ${collection.modifier.name}`} + {collection?.modifier?.name && `, ${collection.modifier.name}`} </BasicBlock> </MainContent> {collection && ( diff --git a/terminology-ui/src/modules/edit-collection/concept-picker/picker-modal.tsx b/terminology-ui/src/modules/edit-collection/concept-picker/picker-modal.tsx index ca2efeee2..be23c9cd5 100644 --- a/terminology-ui/src/modules/edit-collection/concept-picker/picker-modal.tsx +++ b/terminology-ui/src/modules/edit-collection/concept-picker/picker-modal.tsx @@ -1,7 +1,6 @@ import { useSearchConceptMutation } from '@app/common/components/concept/concept.slice'; import { useBreakpoints } from 'yti-common-ui/media-query'; import SanitizedTextContent from 'yti-common-ui/sanitized-text-content'; -import { Concepts } from '@app/common/interfaces/concepts.interface'; import { TEXT_INPUT_MAX } from 'yti-common-ui/utils/constants'; import { translateStatus } from '@app/common/utils/translation-helpers'; import { useTranslation } from 'next-i18next'; @@ -23,10 +22,7 @@ import { SearchInput, Text, } from 'suomifi-ui-components'; -import { - CollectionMember, - EditCollectionFormDataType, -} from '../edit-collection.types'; +import { CollectionMember } from '../edit-collection.types'; import { FooterButton, ResultBlock, @@ -160,11 +156,15 @@ export default function PickerModal({ status: status !== 'ALL-STATUSES' ? [status] : undefined, pageFrom: (currPage - 1) * 20, pageSize: 20, + extendTerminologies: true, }); }; const handleClear = () => { - searchConcept({ namespace: getNamespace(terminologyId) }); + searchConcept({ + namespace: getNamespace(terminologyId), + extendTerminologies: true, + }); setSearchTerm(''); }; @@ -176,6 +176,7 @@ export default function PickerModal({ status: status !== 'ALL-STATUSES' ? [status] : undefined, pageFrom: (num - 1) * 20, pageSize: 20, + extendTerminologies: true, }); focusToTop(); }; @@ -290,7 +291,10 @@ export default function PickerModal({ hintText={`${translateStatus( concept.status, t - )} \u00B7 TODO: terminology label`} + )} \u00B7 ${getLanguageVersion({ + data: concept?.terminology?.label, + lang: i18n.language, + })}`} id={`checkbox-id-${concept.id}`} onClick={(e) => handleCheckbox(e.checkboxState, concept) diff --git a/terminology-ui/src/modules/edit-concept/concept-terms-block/term-form.tsx b/terminology-ui/src/modules/edit-concept/concept-terms-block/term-form.tsx index 7a4873947..93bfb6bcf 100644 --- a/terminology-ui/src/modules/edit-concept/concept-terms-block/term-form.tsx +++ b/terminology-ui/src/modules/edit-concept/concept-terms-block/term-form.tsx @@ -84,6 +84,21 @@ export default function TermForm({ }, ]; + const termEquivalency = [ + { + labelText: '<', + uniqueItemId: 'NARROWER', + }, + { + labelText: '>', + uniqueItemId: 'BROADER', + }, + { + labelText: `${t('almost-the-same-as', { ns: 'admin' })} (~)`, + uniqueItemId: 'CLOSE', + }, + ]; + const termConjugation = [ { labelText: t('term-conjugation.singular', { ns: 'common' }), @@ -254,24 +269,28 @@ export default function TermForm({ maxLength={TEXT_AREA_MAX} /> - <TermEquivalencyBlock> - <label> - {t('term-equivalency')} - <span> ({t('optional')})</span> - </label> - <span>{t('term-equivalency-description')}</span> - <Dropdown - labelText="" - labelMode="hidden" - defaultValue={term.termEquivalency} - onChange={(e) => handleUpdate({ key: 'termEquivalency', value: e })} - > - <DropdownItem value="undefined">{t('no-selection')}</DropdownItem> - <DropdownItem value="<">{'<'}</DropdownItem> - <DropdownItem value=">">{'>'}</DropdownItem> - <DropdownItem value="~">{t('almost-the-same-as')} (~)</DropdownItem> - </Dropdown> - </TermEquivalencyBlock> + <SingleSelect + ariaOptionsAvailableText={t('available-term-equivalencies') as string} + clearButtonLabel={t('clear-button-label')} + labelText={t('term-equivalency')} + optionalText={t('optional')} + hintText={t('term-equivalency-description')} + visualPlaceholder={t('choose-term-equivalency')} + itemAdditionHelpText={''} + items={termEquivalency} + defaultSelectedItem={ + term.wordClass + ? termEquivalency.filter( + (ts) => + ts.uniqueItemId === term.termEquivalency || + ts.labelText === term.termEquivalency + )[0] + : undefined + } + onItemSelect={(e) => handleUpdate({ key: 'termEquivalency', value: e })} + id={`equivalency-picker_${term.id}`} + style={{ marginTop: '20px' }} + /> <ListBlock update={handleUpdate} diff --git a/terminology-ui/src/modules/edit-concept/generate-concept.tsx b/terminology-ui/src/modules/edit-concept/generate-concept.tsx index 8fdfe38bb..dc5e20a6b 100644 --- a/terminology-ui/src/modules/edit-concept/generate-concept.tsx +++ b/terminology-ui/src/modules/edit-concept/generate-concept.tsx @@ -1,5 +1,9 @@ import { ConceptTermType, EditConceptType } from './new-concept.types'; -import { Concept, Term } from '@app/common/interfaces/interfaces-v2'; +import { + Concept, + LocalizedValue, + Term, +} from '@app/common/interfaces/interfaces-v2'; interface generateConceptProps { data: EditConceptType; @@ -12,9 +16,20 @@ export default function generateConceptPayload({ }: generateConceptProps) { const basicInfo = data.basicInformation; const relations = basicInfo.relationalInfo; + + const definition = Object.keys(basicInfo.definition).reduce( + (definition, lang) => { + if (basicInfo.definition[lang]) { + definition[lang] = basicInfo.definition[lang]; + } + return definition; + }, + {} as LocalizedValue + ); + return { ...(!isEdit && { identifier: basicInfo.identifier }), - definition: basicInfo.definition, + definition, notes: basicInfo.note.map((note) => ({ language: note.lang, value: note.value, From c45e3a2dbacb4cc894d7f02113b90d01d7b93b02 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kalle=20Kytt=C3=A4l=C3=A4?= <kalle.kyttala@rebase.fi> Date: Thu, 5 Sep 2024 08:42:11 +0300 Subject: [PATCH 13/35] fix lint error --- .../src/common/components/removal-modal/index.tsx | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/terminology-ui/src/common/components/removal-modal/index.tsx b/terminology-ui/src/common/components/removal-modal/index.tsx index 7fce61e9f..4d0593a7a 100644 --- a/terminology-ui/src/common/components/removal-modal/index.tsx +++ b/terminology-ui/src/common/components/removal-modal/index.tsx @@ -232,9 +232,12 @@ export default function RemovalModal({ } if (isError()) { - //es-lint no-unsafe-optional-chaining - if (concept?.error) { - /*const data = concept.error.data as ApiError; + if ( + concept.error && + 'data' in concept.error && + concept.error.data !== undefined + ) { + const data = concept.error.data as ApiError; return ( <InlineAlert status="error"> {translateErrroMessage(data.message, t)} @@ -250,7 +253,7 @@ export default function RemovalModal({ </div> )} </InlineAlert> - );*/ + ); } return ( From 661382c3c11b3c10caca25e31d2eef8d7d7e84c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kalle=20Kytt=C3=A4l=C3=A4?= <kalle.kyttala@rebase.fi> Date: Thu, 5 Sep 2024 10:44:45 +0300 Subject: [PATCH 14/35] fix build errors and translations. Fix collection payload generation --- terminology-ui/public/locales/en/common.json | 5 + terminology-ui/public/locales/fi/common.json | 5 + terminology-ui/public/locales/sv/common.json | 5 + .../collection/collection.slice.tsx | 1 - .../components/concept/concept.slice.tsx | 1 - .../info-dropdown/info-expander.tsx | 3 - .../relational-information-block/index.tsx | 2 +- .../relational-modal-content.tsx | 5 +- .../subscription/remove-subscription.tsx | 1 - .../common/components/term-modal/index.tsx | 6 +- .../organization-selector.tsx | 2 +- .../vocabulary/vocabulary.slice.tsx | 6 +- .../src/common/utils/get-store-data.test.tsx | 1 - .../src/common/utils/translation-helpers.ts | 13 + .../src/modules/collection/index.tsx | 2 +- .../src/modules/concept/concept-sidebar.tsx | 2 +- .../src/modules/concept/details-expander.tsx | 3 - terminology-ui/src/modules/concept/index.tsx | 8 +- .../generate-collection.test.tsx | 193 +- .../generate-collection/index.tsx | 92 +- .../src/modules/edit-collection/index.tsx | 27 +- .../concept-terms-block/index.tsx | 2 +- .../concept-terms-block/new-term-modal.tsx | 5 +- .../concept-terms-block/term-form.tsx | 6 +- .../modules/edit-concept/generate-concept.tsx | 16 +- .../generate-form-data-test-variables.tsx | 2333 +---------------- .../src/modules/edit-concept/index.tsx | 12 +- .../src/modules/edit-vocabulary/index.tsx | 4 - .../new-terminology/new-terminology-modal.tsx | 10 +- .../src/modules/own-information/index.tsx | 1 - .../src/modules/terminology-search/index.tsx | 6 +- .../collection/[collectionId]/edit.tsx | 1 - .../concept/[conceptId]/edit.tsx | 4 +- 33 files changed, 176 insertions(+), 2607 deletions(-) diff --git a/terminology-ui/public/locales/en/common.json b/terminology-ui/public/locales/en/common.json index 8e4692515..0482b816f 100644 --- a/terminology-ui/public/locales/en/common.json +++ b/terminology-ui/public/locales/en/common.json @@ -155,6 +155,11 @@ "plural": "plural", "singular": "singular" }, + "term-equivalency": { + "broader": ">", + "narrower": "<", + "close": "~" + }, "term-family": { "feminine": "feminine", "masculine": "masculine", diff --git a/terminology-ui/public/locales/fi/common.json b/terminology-ui/public/locales/fi/common.json index 0a57cb0df..f8fb6823c 100644 --- a/terminology-ui/public/locales/fi/common.json +++ b/terminology-ui/public/locales/fi/common.json @@ -155,6 +155,11 @@ "plural": "monikko", "singular": "yksikkö" }, + "term-equivalency": { + "broader": ">", + "narrower": "<", + "close": "~" + }, "term-family": { "feminine": "feminiini", "masculine": "maskuliini", diff --git a/terminology-ui/public/locales/sv/common.json b/terminology-ui/public/locales/sv/common.json index fb240fcfc..2cb5c4872 100644 --- a/terminology-ui/public/locales/sv/common.json +++ b/terminology-ui/public/locales/sv/common.json @@ -155,6 +155,11 @@ "plural": "pluralis", "singular": "singularis" }, + "term-equivalency": { + "broader": ">", + "narrower": "<", + "close": "~" + }, "term-family": { "feminine": "femininum", "masculine": "maskulinum", diff --git a/terminology-ui/src/common/components/collection/collection.slice.tsx b/terminology-ui/src/common/components/collection/collection.slice.tsx index 52bc8eb6f..e0c73d2df 100644 --- a/terminology-ui/src/common/components/collection/collection.slice.tsx +++ b/terminology-ui/src/common/components/collection/collection.slice.tsx @@ -4,7 +4,6 @@ import { ConceptCollection, ConceptCollectionInfo, } from '@app/common/interfaces/interfaces-v2'; -import { terminologyApi } from '../vocabulary/vocabulary.slice'; export const collectionApi = createApi({ reducerPath: 'collectionAPI', diff --git a/terminology-ui/src/common/components/concept/concept.slice.tsx b/terminology-ui/src/common/components/concept/concept.slice.tsx index d2ac4e41f..2a58dbdd0 100644 --- a/terminology-ui/src/common/components/concept/concept.slice.tsx +++ b/terminology-ui/src/common/components/concept/concept.slice.tsx @@ -1,5 +1,4 @@ import { createApi } from '@reduxjs/toolkit/query/react'; -import { Concepts } from '@app/common/interfaces/concepts.interface'; import { getTerminologyApiBaseQuery } from '@app/store/api-base-query'; import { Concept, diff --git a/terminology-ui/src/common/components/info-dropdown/info-expander.tsx b/terminology-ui/src/common/components/info-dropdown/info-expander.tsx index 62017c77d..54d44dc2f 100644 --- a/terminology-ui/src/common/components/info-dropdown/info-expander.tsx +++ b/terminology-ui/src/common/components/info-dropdown/info-expander.tsx @@ -33,7 +33,6 @@ import NewConceptModal from '../new-concept-modal'; import ConceptImportModal from '../concept-import'; import { useGetConceptResultQuery } from '../vocabulary/vocabulary.slice'; import useUrlState from '@app/common/utils/hooks/use-url-state'; -import { useStoreDispatch } from '@app/store'; import StatusMassEdit from '../status-mass-edit'; import isEmail from 'validator/lib/isEmail'; import { useRouter } from 'next/router'; @@ -70,8 +69,6 @@ export default function InfoExpander({ language: i18n.language, }); - const dispatch = useStoreDispatch(); - if (!data) { return null; } diff --git a/terminology-ui/src/common/components/relational-information-block/index.tsx b/terminology-ui/src/common/components/relational-information-block/index.tsx index 709f93c4e..20bdb3ba7 100644 --- a/terminology-ui/src/common/components/relational-information-block/index.tsx +++ b/terminology-ui/src/common/components/relational-information-block/index.tsx @@ -46,7 +46,7 @@ export default function RelationalInformationBlock({ const [chosen, setChosen] = useState< ConceptResponseObject[] | RelationInfoType[] >(data[infoKey] ?? []); - const { t, i18n } = useTranslation('admin'); + const { i18n } = useTranslation('admin'); const router = useRouter(); const terminologyId = Array.isArray(router.query.terminologyId) ? router.query.terminologyId[0] diff --git a/terminology-ui/src/common/components/relational-information-block/relational-modal-content.tsx b/terminology-ui/src/common/components/relational-information-block/relational-modal-content.tsx index b4a069bda..4c6efd6f8 100644 --- a/terminology-ui/src/common/components/relational-information-block/relational-modal-content.tsx +++ b/terminology-ui/src/common/components/relational-information-block/relational-modal-content.tsx @@ -10,10 +10,7 @@ import { RelationalModalBlock } from './relation-information-block.styles'; import RenderConcepts from './render-concepts'; import Search from './search'; import { getNamespace } from '@app/common/utils/namespace'; -import { - ConceptResponseObject, - SearchResponse, -} from '@app/common/interfaces/interfaces-v2'; +import { ConceptResponseObject } from '@app/common/interfaces/interfaces-v2'; interface RelationModalContentProps { fromOther?: boolean; diff --git a/terminology-ui/src/common/components/subscription/remove-subscription.tsx b/terminology-ui/src/common/components/subscription/remove-subscription.tsx index e69396f1e..64e221fc3 100644 --- a/terminology-ui/src/common/components/subscription/remove-subscription.tsx +++ b/terminology-ui/src/common/components/subscription/remove-subscription.tsx @@ -9,7 +9,6 @@ import { Text, } from 'suomifi-ui-components'; import { Resource } from '../../interfaces/subscription.interface'; -import getPrefLabel from '../../utils/get-preflabel'; import IconButton from 'yti-common-ui/icon-button'; import { useBreakpoints } from 'yti-common-ui/media-query'; import { diff --git a/terminology-ui/src/common/components/term-modal/index.tsx b/terminology-ui/src/common/components/term-modal/index.tsx index 35faddcfe..a8bec4f25 100644 --- a/terminology-ui/src/common/components/term-modal/index.tsx +++ b/terminology-ui/src/common/components/term-modal/index.tsx @@ -21,6 +21,7 @@ import { useBreakpoints } from 'yti-common-ui/media-query'; import { translateStatus, translateTermConjugation, + translateTermEquivalency, translateTermFamily, translateWordClass, } from '@app/common/utils/translation-helpers'; @@ -68,7 +69,10 @@ export default function TermModal({ data }: TermModalProps) { )} {renderInfo(t('term-modal-info'), data.term.termInfo)} {renderInfo(t('term-modal-scope'), data.term.scope)} - {renderInfo(t('term-modal-equivalency'), data.term.termEquivalency)} + {renderInfo( + t('term-modal-equivalency'), + translateTermEquivalency(data.term.termEquivalency ?? '', t) + )} {renderInfo(t('term-modal-source'), data.term.sources)} <TermExpander diff --git a/terminology-ui/src/common/components/terminology-components/organization-selector.tsx b/terminology-ui/src/common/components/terminology-components/organization-selector.tsx index a4796beac..601b5b066 100644 --- a/terminology-ui/src/common/components/terminology-components/organization-selector.tsx +++ b/terminology-ui/src/common/components/terminology-components/organization-selector.tsx @@ -78,7 +78,7 @@ export default function OrganizationSelector({ } }) .filter((org) => org) as MultiSelectData[]; - }, [organizations, isLoading, isError, user]); + }, [organizations, isLoading, isError, user, i18n.language]); useEffect(() => { if (adminOrgs?.length === 1 && selectedOrganizations.length === 0) { diff --git a/terminology-ui/src/common/components/vocabulary/vocabulary.slice.tsx b/terminology-ui/src/common/components/vocabulary/vocabulary.slice.tsx index 926cd6aa2..e23b439f6 100644 --- a/terminology-ui/src/common/components/vocabulary/vocabulary.slice.tsx +++ b/terminology-ui/src/common/components/vocabulary/vocabulary.slice.tsx @@ -1,10 +1,6 @@ import { createSlice } from '@reduxjs/toolkit'; import { createApi } from '@reduxjs/toolkit/query/react'; -import { Collection } from '@app/common/interfaces/collection.interface'; -import { - VocabulariesDTO, - VocabularyCopyInfo, -} from '@app/common/interfaces/vocabulary.interface'; +import { VocabularyCopyInfo } from '@app/common/interfaces/vocabulary.interface'; import { UrlState } from '@app/common/utils/hooks/use-url-state'; import { getTerminologyApiBaseQuery } from '@app/store/api-base-query'; import { diff --git a/terminology-ui/src/common/utils/get-store-data.test.tsx b/terminology-ui/src/common/utils/get-store-data.test.tsx index 48d820c2d..8c269777d 100644 --- a/terminology-ui/src/common/utils/get-store-data.test.tsx +++ b/terminology-ui/src/common/utils/get-store-data.test.tsx @@ -1,4 +1,3 @@ -import { getTerminology } from '../components/vocabulary/vocabulary.slice'; import { getStoreData } from './get-store-data'; describe('page-head-utils', () => { diff --git a/terminology-ui/src/common/utils/translation-helpers.ts b/terminology-ui/src/common/utils/translation-helpers.ts index 96f441453..be3fc0113 100644 --- a/terminology-ui/src/common/utils/translation-helpers.ts +++ b/terminology-ui/src/common/utils/translation-helpers.ts @@ -94,6 +94,19 @@ export function translateTermFamily(termFamily: string, t: TFunction) { } } +export function translateTermEquivalency(termEq: string, t: TFunction) { + switch (termEq) { + case 'BROADER': + return t('term-equivalency.broader', { ns: 'common' }); + case 'NARROWER': + return t('term-equivalency.narrower', { ns: 'common' }); + case 'CLOSE': + return t('term-equivalency.close', { ns: 'common' }); + default: + return termEq; + } +} + export function translateTermConjugation( termConjugation: string, t: TFunction diff --git a/terminology-ui/src/modules/collection/index.tsx b/terminology-ui/src/modules/collection/index.tsx index 5ef3ad8b4..200ef1d36 100644 --- a/terminology-ui/src/modules/collection/index.tsx +++ b/terminology-ui/src/modules/collection/index.tsx @@ -186,7 +186,7 @@ export default function Collection({ <MultilingualBlock data={collection?.label ?? {}} /> </BasicBlock> - {collection?.description && ( + {Object.keys(collection?.description ?? {}).length > 0 && ( <BasicBlock title={t('field-definition')}> <MultilingualBlock data={collection?.description ?? {}} /> </BasicBlock> diff --git a/terminology-ui/src/modules/concept/concept-sidebar.tsx b/terminology-ui/src/modules/concept/concept-sidebar.tsx index d212bce76..ff6792c26 100644 --- a/terminology-ui/src/modules/concept/concept-sidebar.tsx +++ b/terminology-ui/src/modules/concept/concept-sidebar.tsx @@ -41,7 +41,7 @@ function getReferenceValues( } export default function ConceptSidebar({ concept }: ConceptSidebarProps) { - const { t, i18n } = useTranslation('concept'); + const { t } = useTranslation('concept'); const shouldRenderDivider1 = [ diff --git a/terminology-ui/src/modules/concept/details-expander.tsx b/terminology-ui/src/modules/concept/details-expander.tsx index a4a809977..796e0cec6 100644 --- a/terminology-ui/src/modules/concept/details-expander.tsx +++ b/terminology-ui/src/modules/concept/details-expander.tsx @@ -1,4 +1,3 @@ -import { useTranslation } from 'next-i18next'; import { BasicBlock } from 'yti-common-ui/block'; import AdministrativeDetailsExpander, { hasAdministrativeDetails, @@ -13,8 +12,6 @@ export interface DetailsExpanderProps { } export default function DetailsExpander({ concept }: DetailsExpanderProps) { - const { i18n } = useTranslation('concept'); - const noDiagramsAndSources = !concept?.links.length && !concept?.sources.length; const noAdministrativeDetails = !hasAdministrativeDetails(concept); diff --git a/terminology-ui/src/modules/concept/index.tsx b/terminology-ui/src/modules/concept/index.tsx index 6692c1548..792e3824a 100644 --- a/terminology-ui/src/modules/concept/index.tsx +++ b/terminology-ui/src/modules/concept/index.tsx @@ -15,15 +15,10 @@ import { MultilingualBlock, MultilingualBlockList, } from 'yti-common-ui/block'; -import { - MultilingualPropertyBlock, - PropertyBlock, - TermBlock, -} from '@app/common/components/block'; +import { TermBlock } from '@app/common/components/block'; import { Breadcrumb, BreadcrumbLink } from 'yti-common-ui/breadcrumb'; import FormattedDate from 'yti-common-ui/formatted-date'; import { useBreakpoints } from 'yti-common-ui/media-query'; -import { getPropertyValue } from '@app/common/components/property-value/get-property-value'; import Separator from 'yti-common-ui/separator'; import DetailsExpander from './details-expander'; import ConceptSidebar from './concept-sidebar'; @@ -38,7 +33,6 @@ import { useRouter } from 'next/router'; import { setTitle } from '@app/common/components/title/title.slice'; import { useGetTerminologyQuery } from '@app/common/components/vocabulary/vocabulary.slice'; import { useGetConceptQuery } from '@app/common/components/concept/concept.slice'; -import { getProperty } from '@app/common/utils/get-property'; import { MainTitle, BadgeBar } from 'yti-common-ui/title-block'; import HasPermission from '@app/common/utils/has-permission'; import Link from 'next/link'; diff --git a/terminology-ui/src/modules/edit-collection/generate-collection/generate-collection.test.tsx b/terminology-ui/src/modules/edit-collection/generate-collection/generate-collection.test.tsx index a0bd91f00..2dcb2891e 100644 --- a/terminology-ui/src/modules/edit-collection/generate-collection/generate-collection.test.tsx +++ b/terminology-ui/src/modules/edit-collection/generate-collection/generate-collection.test.tsx @@ -1,190 +1,33 @@ import generateCollection from '.'; describe('generate-collection', () => { - it('should generate collection without concepts', () => { + it('should generate new collection with concepts', () => { const data = { - name: [ - { - lang: 'fi', - value: 'uusi käsitevalikoima', - }, - { - lang: 'en', - value: 'new collection', - }, - ], - definition: [ - { - lang: 'fi', - value: 'kuvaus', - }, - { - lang: 'en', - value: 'description', - }, - ], - concepts: [], - }; - - const now = new Date(); - - const returned = generateCollection(data, 'terminologyId'); - const collectionId = returned[0].id; - returned[0].createdDate = now.toISOString(); - returned[0].lastModifiedDate = now.toISOString(); - - expect(returned).toStrictEqual([ - { - createdBy: '', - createdDate: now.toISOString(), - id: collectionId, - lastModifiedBy: '', - lastModifiedDate: now.toISOString(), - properties: { - definition: [ - { - lang: 'fi', - regex: '(?s)^.*$', - value: 'kuvaus', - }, - { - lang: 'en', - regex: '(?s)^.*$', - value: 'description', - }, - ], - prefLabel: [ - { - lang: 'fi', - regex: '(?s)^.*$', - value: 'uusi käsitevalikoima', - }, - { - lang: 'en', - regex: '(?s)^.*$', - value: 'new collection', - }, - ], - }, - references: { - broader: [], - member: [], - }, - referrers: {}, - type: { - graph: { - id: 'terminologyId', - }, - id: 'Collection', - uri: 'http://www.w3.org/2004/02/skos/core#Collection', - }, + identifier: 'collection-1', + label: { + fi: 'uusi käsitekokoelma', + en: 'new collection', }, - ]); - }); - - it('should generate collection with concepts', () => { - const data = { - name: [ - { - lang: 'fi', - value: 'uusi käsitevalikoima', - }, - ], - definition: [], - concepts: [ - { - id: '123-123123-123', - prefLabels: { - fi: 'fi', - en: 'en', - }, - }, + description: { + fi: 'kuvaus', + en: 'description', + }, + members: [ { - id: '456-456456-456', - prefLabels: { - fi: 'fi', - en: 'en', - }, + uri: 'https://iri.suomi.fi/terminology/test/concept-1', + identifier: 'concept-1', + label: {}, }, { - id: '789-789789-789', - prefLabels: { - fi: 'fi', - en: 'en', - }, + uri: 'https://iri.suomi.fi/terminology/test/concept-2', + identifier: 'concept-2', + label: {}, }, ], }; - const returned = generateCollection(data, 'terminologyId'); - - const collectionId = returned[0].id; - - const now = new Date(); - returned[0].createdDate = now.toISOString(); - returned[0].lastModifiedDate = now.toISOString(); + const returned = generateCollection(data, false); - expect(returned).toStrictEqual([ - { - createdBy: '', - createdDate: now.toISOString(), - id: collectionId, - lastModifiedBy: '', - lastModifiedDate: now.toISOString(), - properties: { - definition: [], - prefLabel: [ - { - lang: 'fi', - regex: '(?s)^.*$', - value: 'uusi käsitevalikoima', - }, - ], - }, - references: { - broader: [], - member: [ - { - id: '123-123123-123', - type: { - graph: { - id: 'terminologyId', - }, - id: 'Concept', - uri: '', - }, - }, - { - id: '456-456456-456', - type: { - graph: { - id: 'terminologyId', - }, - id: 'Concept', - uri: '', - }, - }, - { - id: '789-789789-789', - type: { - graph: { - id: 'terminologyId', - }, - id: 'Concept', - uri: '', - }, - }, - ], - }, - referrers: {}, - type: { - graph: { - id: 'terminologyId', - }, - id: 'Collection', - uri: 'http://www.w3.org/2004/02/skos/core#Collection', - }, - }, - ]); + expect(returned).toStrictEqual([]); }); }); diff --git a/terminology-ui/src/modules/edit-collection/generate-collection/index.tsx b/terminology-ui/src/modules/edit-collection/generate-collection/index.tsx index 97a79ae04..90a04dcba 100644 --- a/terminology-ui/src/modules/edit-collection/generate-collection/index.tsx +++ b/terminology-ui/src/modules/edit-collection/generate-collection/index.tsx @@ -1,77 +1,33 @@ -import { v4 } from 'uuid'; -import { - EditCollectionFormDataType, - EditCollectionPostType, - EditCollectionProps, -} from '../edit-collection.types'; +import { LocalizedValue } from '@app/common/interfaces/interfaces-v2'; +import { CollectionFormData } from '../edit-collection.types'; export default function generateCollection( - formData: EditCollectionFormDataType, - terminologyId: string, - lastModifiedBy?: string, - collectionInfo?: EditCollectionProps['collectionInfo'] + formData: CollectionFormData, + isEdit: boolean ) { - const regex = '(?s)^.*$'; - const now = new Date(); - - const retVal: EditCollectionPostType = { - createdBy: collectionInfo?.collectionId ? collectionInfo.createdBy : '', - createdDate: now.toISOString(), - id: collectionInfo?.collectionId ? collectionInfo.collectionId : v4(), - lastModifiedBy: collectionInfo?.collectionId ? lastModifiedBy ?? '' : '', - lastModifiedDate: now.toISOString(), - properties: { - definition: - formData.definition.length > 0 - ? formData.definition - .filter((d) => d.value !== '') - .map((d) => ({ - lang: d.lang, - regex: regex, - value: d.value, - })) - : [], - prefLabel: formData.name - .filter((n) => n.value !== '') - .map((n) => ({ - lang: n.lang, - regex: regex, - value: n.value, - })), - }, - references: { - broader: [], - member: formData.concepts.map((c) => ({ - id: c.id, - type: { - graph: { - id: terminologyId, - }, - id: 'Concept', - uri: '', - }, - })), - }, - referrers: {}, - type: { - graph: { - id: terminologyId, - }, - id: 'Collection', - uri: collectionInfo?.collectionId - ? '' - : 'http://www.w3.org/2004/02/skos/core#Collection', + // remove empty values + const description = Object.keys(formData.description).reduce( + (description, lang) => { + if (formData.description[lang]) { + description[lang] = formData.description[lang]; + } + return description; }, - }; + {} as LocalizedValue + ); - if (collectionInfo?.collectionId) { - retVal.code = collectionInfo.collectionCode; - retVal.uri = collectionInfo.collectionUri; - - if (retVal.references.member[0]) { - retVal.references.member[0].type.uri = ''; + const payload = Object.assign( + {}, + { + ...formData, + description, + members: formData.members.map((member) => member.identifier), } + ); + + if (isEdit) { + Object.assign(payload, { identifier: null }); } - return [retVal]; + return payload; } diff --git a/terminology-ui/src/modules/edit-collection/index.tsx b/terminology-ui/src/modules/edit-collection/index.tsx index 35ec4a6f7..215bfec43 100644 --- a/terminology-ui/src/modules/edit-collection/index.tsx +++ b/terminology-ui/src/modules/edit-collection/index.tsx @@ -5,7 +5,7 @@ import { useGetTerminologyQuery } from '@app/common/components/vocabulary/vocabu import { useTranslation } from 'next-i18next'; import { useRouter } from 'next/router'; import { useEffect, useState } from 'react'; -import { Button, Heading, InlineAlert, TextInput } from 'suomifi-ui-components'; +import { Button, Heading, InlineAlert } from 'suomifi-ui-components'; import ConceptPicker from './concept-picker'; import { ButtonBlock, @@ -40,6 +40,7 @@ import { import { compareLocales } from 'yti-common-ui/utils/compare-locales'; import { getLanguageVersion } from 'yti-common-ui/utils/get-language-version'; import { ConceptCollectionInfo } from '@app/common/interfaces/interfaces-v2'; +import generateCollection from './generate-collection'; export default function EditCollection({ terminologyId, @@ -100,7 +101,14 @@ export default function EditCollection({ `/terminology/${terminologyId}/collection/${collectionInfo?.collectionId}` ); } - }, [result, updateResult, router, terminologyId]); + }, [ + result, + updateResult, + router, + terminologyId, + collectionInfo?.collectionId, + formData.identifier, + ]); useEffect(() => { if (exists.data) { @@ -180,26 +188,21 @@ export default function EditCollection({ getAuthenticatedMutUser(); setErrorMessages([]); - const payload = Object.assign( - {}, - { - ...formData, - members: formData.members.map((member) => member.identifier), - } - ); - if (!isFormValid(formData)) { return; } + const isEdit = !!collectionInfo?.collectionId; + const payload = generateCollection(formData, isEdit); + console.info('payload', payload); disableConfirmation(); setIsCreating(true); - if (collectionInfo?.collectionId) { + if (isEdit) { updateCollection({ collectionId: collectionInfo.collectionId, terminologyId, - payload: Object.assign(payload, { identifier: null }), + payload, }); } else { addCollection({ diff --git a/terminology-ui/src/modules/edit-concept/concept-terms-block/index.tsx b/terminology-ui/src/modules/edit-concept/concept-terms-block/index.tsx index acf1f45b3..9c8596c4f 100644 --- a/terminology-ui/src/modules/edit-concept/concept-terms-block/index.tsx +++ b/terminology-ui/src/modules/edit-concept/concept-terms-block/index.tsx @@ -161,7 +161,7 @@ export default function ConceptTermsBlock({ > {t('concept-preferred-terms-description')} </BasicBlock> - + <p>srerger</p> <BasicBlock title={ <MediumHeading variant="h3"> diff --git a/terminology-ui/src/modules/edit-concept/concept-terms-block/new-term-modal.tsx b/terminology-ui/src/modules/edit-concept/concept-terms-block/new-term-modal.tsx index f1008b921..1dd2867e7 100644 --- a/terminology-ui/src/modules/edit-concept/concept-terms-block/new-term-modal.tsx +++ b/terminology-ui/src/modules/edit-concept/concept-terms-block/new-term-modal.tsx @@ -62,7 +62,6 @@ export default function NewTermModal({ const [isHomographic, setIsHomographic] = useState(false); const [termData, setTermData] = useState<ConceptTermType>({ changeNote: '', - draftComment: '', editorialNote: [], historyNote: '', id: v4(), @@ -84,11 +83,11 @@ export default function NewTermModal({ const wordClasses = [ { - labelText: translateWordClass('adjective', t), + labelText: translateWordClass('ADJECTIVE', t), uniqueItemId: 'adjective', }, { - labelText: translateWordClass('verb', t), + labelText: translateWordClass('VERB', t), uniqueItemId: 'verb', }, ]; diff --git a/terminology-ui/src/modules/edit-concept/concept-terms-block/term-form.tsx b/terminology-ui/src/modules/edit-concept/concept-terms-block/term-form.tsx index 93bfb6bcf..1f9c226b3 100644 --- a/terminology-ui/src/modules/edit-concept/concept-terms-block/term-form.tsx +++ b/terminology-ui/src/modules/edit-concept/concept-terms-block/term-form.tsx @@ -11,7 +11,6 @@ import { useTranslation } from 'next-i18next'; import { useState } from 'react'; import { Button, - Dropdown, DropdownItem, IconRemove, SingleSelect, @@ -27,7 +26,6 @@ import { GrammaticalBlock, HomographTextInput, MediumHeading, - TermEquivalencyBlock, TermFormBottomBlock, TermFormRemoveButton, TermFormTopBlock, @@ -112,11 +110,11 @@ export default function TermForm({ const wordClasses = [ { - labelText: translateWordClass('adjective', t), + labelText: translateWordClass('ADJECTIVE', t), uniqueItemId: 'ADJECTIVE', }, { - labelText: translateWordClass('verb', t), + labelText: translateWordClass('VERB', t), uniqueItemId: 'VERB', }, ]; diff --git a/terminology-ui/src/modules/edit-concept/generate-concept.tsx b/terminology-ui/src/modules/edit-concept/generate-concept.tsx index dc5e20a6b..07e8e668d 100644 --- a/terminology-ui/src/modules/edit-concept/generate-concept.tsx +++ b/terminology-ui/src/modules/edit-concept/generate-concept.tsx @@ -82,12 +82,16 @@ function mapTerms(terms: ConceptTermType[], type: string) { historyNote: term.historyNote, changeNote: term.changeNote, termStyle: term.termStyle, - termFamily: term.termFamily !== '' ? term.termFamily : undefined, - termConjugation: - term.termConjugation !== '' ? term.termConjugation : undefined, - termEquivalency: - term.termEquivalency !== '' ? term.termEquivalency : undefined, - wordClass: term.wordClass !== '' ? term.wordClass : undefined, + termFamily: term.termFamily + ? term.termFamily.toUpperCase() + : undefined, + termConjugation: term.termConjugation + ? term.termConjugation.toUpperCase() + : undefined, + termEquivalency: term.termEquivalency + ? term.termEquivalency.toUpperCase() + : undefined, + wordClass: term.wordClass ? term.wordClass.toUpperCase() : undefined, sources: term.source.map((s) => s.value), editorialNotes: term.editorialNote.map((e) => e.value), } as Term) diff --git a/terminology-ui/src/modules/edit-concept/generate-form-data-test-variables.tsx b/terminology-ui/src/modules/edit-concept/generate-form-data-test-variables.tsx index 91bd99853..24aced479 100644 --- a/terminology-ui/src/modules/edit-concept/generate-form-data-test-variables.tsx +++ b/terminology-ui/src/modules/edit-concept/generate-form-data-test-variables.tsx @@ -1,8 +1,6 @@ import generateFormData from './generate-form-data'; -export const emptyFormReturned = generateFormData([ - { lang: 'fi', value: 'demo', regex: '' }, -]); +export const emptyFormReturned = generateFormData({ fi: 'demo' }); export const emptyFormExpected = { terms: [ @@ -45,7 +43,6 @@ export const emptyFormExpected = { }, otherInfo: { conceptClass: '', - wordClass: '', }, relationalInfo: { broaderConcept: [], @@ -65,133 +62,47 @@ export const emptyFormExpected = { // -------------------------------------------- export const simpleDataReturned = generateFormData( - [{ lang: 'fi', value: 'demo', regex: '' }], + { fi: 'demo' }, { - code: 'concept-1000', - createdBy: 'User', - createdDate: '1970-01-01T00:00:00.000Z', - id: '123123-123-123123', - identifier: { - id: '123123-123-123123', - type: { - graph: { - id: '987987-987-987987', - }, - id: 'Concept', - uri: '', - }, + identifier: '', + uri: '', + created: '', + modified: '', + creator: { + id: '', + name: '', }, - lastModifiedBy: 'User', - lastModifiedDate: '1970-01-01T00:00:00.000Z', - number: 0, - properties: { - status: [ - { - lang: '', - value: 'DRAFT', - regex: '(?s)^.*$', - }, - ], + modifier: { + id: '', + name: '', }, - references: { - prefLabelXl: [ - { - code: 'term-1000', - createdBy: 'User', - createdDate: '1970-01-01T00:00:00.000Z', - id: '456456-456-456456', - identifier: { - id: '456456-456-456456', - type: { - graph: { - id: '987987-987-987987', - }, - id: 'Term', - uri: '', - }, - }, - lastModifiedBy: 'User', - lastModifiedDate: '1970-01-01T00:00:00.000Z', - number: 0, - properties: { - prefLabel: [ - { - lang: 'fi', - regex: '(?s)^.*$', - value: 'demo', - }, - ], - status: [ - { - lang: '', - regex: '(?s)^.*$', - value: 'DRAFT', - }, - ], - }, - references: {}, - referrers: { - prefLabelXl: [ - { - code: 'concept-1000', - createdBy: 'User', - createdDate: '1970-01-01T00:00:00.000Z', - id: '321321-321-321321', - identifier: { - id: '321321-321-321321', - type: { - graph: { - id: '987987-987-987987', - }, - id: 'Concept', - uri: '', - }, - }, - lastModifiedBy: 'User', - lastModifiedDate: '1970-01-01T00:00:00.000Z', - number: 0, - properties: { - status: [ - { - lang: '', - regex: '(?s)^.*$', - value: 'DRAFT', - }, - ], - }, - references: {}, - referrers: {}, - type: { - graph: { - id: '987987-987-987987', - }, - id: 'Concept', - uri: '', - }, - uri: 'uri.suomi.fi/terminology/sanasto/concept-1000', - }, - ], - }, - type: { - graph: { - id: '987987-987-987987', - }, - id: 'Term', - uri: '', - }, - uri: 'uri.suomi.fi/terminology/sanasto/term-1000', - }, - ], - }, - referrers: {}, - type: { - graph: { - id: '987987-987-987987', - }, - id: 'Concept', - uri: '', - }, - uri: 'uri.suomi.fi/terminology/sanasto/concept-1000', + label: {}, + definition: {}, + notes: [], + examples: [], + subjectArea: '', + status: 'DRAFT', + sources: [], + links: [], + changeNote: '', + historyNote: '', + conceptClass: '', + editorialNotes: [], + recommendedTerms: [], + synonyms: [], + notRecommendedTerms: [], + searchTerms: [], + broader: [], + narrower: [], + isPartOf: [], + hasPart: [], + related: [], + broadMatch: [], + narrowMatch: [], + exactMatch: [], + closeMatch: [], + relatedMatch: [], + memberOf: [], } ); @@ -236,7 +147,6 @@ export const simpleDataExpected = { }, otherInfo: { conceptClass: '', - wordClass: '', }, relationalInfo: { broaderConcept: [], @@ -256,2168 +166,9 @@ export const simpleDataExpected = { // -------------------------------------------- export const extensiveDataReturned = generateFormData( - [{ lang: 'fi', value: 'demo', regex: '' }], - { - code: 'concept-1000', - createdBy: 'User', - createdDate: '1970-01-01T00:00:00.000Z', - id: '123123-123-123123', - identifier: { - id: '123123-123-123123', - type: { - graph: { - id: '987987-987-987987', - }, - id: 'Concept', - uri: '', - }, - }, - lastModifiedBy: 'User', - lastModifiedDate: '1970-01-01T00:00:00.000Z', - number: 0, - properties: { - changeNote: [ - { - lang: '', - value: 'muutoshistoria', - regex: '(?s)^.*$', - }, - ], - conceptClass: [ - { - lang: '', - value: 'käsitteen luokka', - regex: '(?s)^.*$', - }, - ], - definition: [ - { - lang: 'fi', - value: 'määritelmä', - regex: '(?s)^.*$', - }, - { - lang: 'en', - value: 'definition', - regex: '(?s)^.*$', - }, - ], - editorialNote: [ - { - lang: '', - value: 'ylläpitäjän muistiinpano', - regex: '(?s)^.*$', - }, - ], - example: [ - { - lang: 'fi', - value: 'esimerkki', - regex: '(?s)^.*$', - }, - ], - historyNote: [ - { - lang: '', - value: 'historiatieto', - regex: '(?s)^.*$', - }, - ], - note: [ - { - lang: '', - value: 'huomautus', - regex: '(?s)^.*$', - }, - ], - source: [ - { - lang: '', - value: 'lähde', - regex: '(?s)^.*$', - }, - ], - status: [ - { - lang: '', - value: 'DRAFT', - regex: '(?s)^.*$', - }, - ], - subjectArea: [ - { - lang: '', - value: 'aihealue', - regex: '(?s)^.*$', - }, - ], - wordClass: [ - { - lang: '', - value: 'adjective', - regex: '(?s)^.*$', - }, - ], - }, - references: { - altLabelXl: [ - { - id: 'ae47e0e8-c44d-4199-b7b4-3b8c98cda1de', - code: 'term-4001', - uri: 'http://uri.suomi.fi/terminology/sanasto/term-4001', - number: 0, - createdBy: 'Admin User', - createdDate: '1970-01-01T00:00:00.000Z', - lastModifiedBy: 'Admin User', - lastModifiedDate: '1970-01-01T00:00:00.000Z', - type: { - id: 'Term', - graph: { - id: '987987-987-987987', - }, - uri: '', - }, - properties: { - changeNote: [ - { - lang: '', - value: 'muutoshistoria', - regex: '(?s)^.*$', - }, - ], - editorialNote: [ - { - lang: '', - value: 'ylläpitäjän muistiinpano', - regex: '(?s)^.*$', - }, - ], - historyNote: [ - { - lang: '', - value: 'käytön historiatieto', - regex: '(?s)^.*$', - }, - ], - prefLabel: [ - { - lang: 'fi', - value: 'synonyymi', - regex: '(?s)^.*$', - }, - ], - scope: [ - { - lang: '', - value: 'käyttöala', - regex: '(?s)^.*$', - }, - ], - source: [ - { - lang: '', - value: 'käyttöala', - regex: '(?s)^.*$', - }, - ], - status: [ - { - lang: '', - value: 'DRAFT', - regex: '(?s)^.*$', - }, - ], - termConjugation: [ - { - lang: '', - value: 'monikko', - regex: '(?s)^.*$', - }, - ], - termEquivalency: [ - { - lang: '', - value: '~', - regex: '(?s)^.*$', - }, - ], - termFamily: [ - { - lang: '', - value: 'feminiini', - regex: '(?s)^.*$', - }, - ], - termHomographNumber: [ - { - lang: '', - value: '1', - regex: '(?s)^.*$', - }, - ], - termInfo: [ - { - lang: '', - value: 'termin lisätieto', - regex: '(?s)^.*$', - }, - ], - termStyle: [ - { - lang: '', - value: 'puhekieli', - regex: '(?s)^.*$', - }, - ], - wordClass: [ - { - lang: '', - value: 'adjective', - regex: '(?s)^.*$', - }, - ], - }, - references: {}, - referrers: {}, - identifier: { - id: 'ae47e0e8-c44d-4199-b7b4-3b8c98cda1de', - type: { - id: 'Term', - graph: { - id: '987987-987-987987', - }, - uri: '', - }, - }, - }, - ], - exactMatch: [ - { - id: '8ee81e29-e0f1-4a23-b23b-28a976fcf87f', - code: 'concept-link-1001', - uri: 'http://uri.suomi.fi/terminology/sanasto/concept-link-1001', - number: 0, - createdBy: 'Admin User', - createdDate: '1970-01-01T00:00:00.000Z', - lastModifiedBy: 'Admin User', - lastModifiedDate: '1970-01-01T00:00:00.000Z', - type: { - id: 'ConceptLink', - graph: { - id: '987987-987-987987', - }, - uri: '', - }, - properties: { - prefLabel: [ - { - lang: 'fi', - value: 'demo', - regex: '(?s)^.*$', - }, - ], - vocabularyLabel: [ - { - lang: 'fi', - value: 'testi', - regex: '(?s)^.*$', - }, - { - lang: 'en', - value: 'test', - regex: '(?s)^.*$', - }, - ], - targetId: [ - { - lang: '', - value: '7b179ea2-b28c-497e-9e81-6ff254235ea1', - regex: '(?s)^.*$', - }, - ], - targetGraph: [ - { - lang: '', - value: 'ec43f161-b85d-4786-a4b9-d0da52edfba1', - regex: '(?s)^.*$', - }, - ], - }, - references: {}, - referrers: {}, - identifier: { - id: '8ee81e29-e0f1-4a23-b23b-28a976fcf87f', - type: { - id: 'ConceptLink', - graph: { - id: '987987-987-987987', - }, - uri: '', - }, - }, - }, - ], - searchTerm: [ - { - id: 'aca34887-297a-4482-9514-1ffe45161d3e', - code: 'term-4003', - uri: 'http://uri.suomi.fi/terminology/sanasto/term-4003', - number: 0, - createdBy: 'Admin User', - createdDate: '1970-01-01T00:00:00.000Z', - lastModifiedBy: 'Admin User', - lastModifiedDate: '1970-01-01T00:00:00.000Z', - type: { - id: 'Term', - graph: { - id: '987987-987-987987', - }, - uri: '', - }, - properties: { - changeNote: [ - { - lang: '', - value: 'muutoshistoria', - regex: '(?s)^.*$', - }, - ], - editorialNote: [ - { - lang: '', - value: 'ylläpitäjän muistiinpano', - regex: '(?s)^.*$', - }, - ], - historyNote: [ - { - lang: '', - value: 'käytönhistoriatieto', - regex: '(?s)^.*$', - }, - ], - prefLabel: [ - { - lang: 'fi', - value: 'Hakusana', - regex: '(?s)^.*$', - }, - ], - scope: [ - { - lang: '', - value: 'käyttöala', - regex: '(?s)^.*$', - }, - ], - source: [ - { - lang: '', - value: 'käyttöala', - regex: '(?s)^.*$', - }, - ], - status: [ - { - lang: '', - value: 'DRAFT', - regex: '(?s)^.*$', - }, - ], - termConjugation: [ - { - lang: '', - value: 'monikko', - regex: '(?s)^.*$', - }, - ], - termEquivalency: [ - { - lang: '', - value: '~', - regex: '(?s)^.*$', - }, - ], - termFamily: [ - { - lang: '', - value: 'neutri', - regex: '(?s)^.*$', - }, - ], - termInfo: [ - { - lang: '', - value: 'termin lisätieto', - regex: '(?s)^.*$', - }, - ], - termStyle: [ - { - lang: '', - value: 'puhekieli', - regex: '(?s)^.*$', - }, - ], - wordClass: [ - { - lang: '', - value: 'adjective', - regex: '(?s)^.*$', - }, - ], - }, - references: {}, - referrers: {}, - identifier: { - id: 'aca34887-297a-4482-9514-1ffe45161d3e', - type: { - id: 'Term', - graph: { - id: '987987-987-987987', - }, - uri: '', - }, - }, - }, - ], - related: [ - { - id: '2a2b8b11-4dc9-4ddb-afde-812560a5b007', - code: '', - uri: '', - number: 0, - createdBy: 'Admin User', - createdDate: '1970-01-01T00:00:00.000Z', - lastModifiedBy: 'Admin User', - lastModifiedDate: '1970-01-01T00:00:00.000Z', - type: { - id: 'Concept', - graph: { - id: '987987-987-987987', - }, - uri: '', - }, - properties: { - definition: [ - { - lang: 'fi', - value: 'Määritelmä, suomi FI', - regex: '(?s)^.*$', - }, - { - lang: 'en', - value: 'Määritelmä, englanti EN', - regex: '(?s)^.*$', - }, - ], - historyNote: [ - { - lang: '', - value: 'Käytön historiatieto "etymologia"', - regex: '(?s)^.*$', - }, - ], - conceptClass: [ - { - lang: '', - value: 'Käsitteen luokka', - regex: '(?s)^.*$', - }, - ], - wordClass: [ - { - lang: '', - value: 'adjective', - regex: '(?s)^.*$', - }, - ], - status: [ - { - lang: '', - value: 'DRAFT', - regex: '(?s)^.*$', - }, - ], - example: [ - { - lang: 'fi', - value: 'esimerkki', - regex: '(?s)^.*$', - }, - { - lang: 'en', - value: 'example', - regex: '(?s)^.*$', - }, - ], - note: [ - { - lang: 'fi', - value: 'Huomautus', - regex: '(?s)^.*$', - }, - { - lang: 'en', - value: 'Huomautus', - regex: '(?s)^.*$', - }, - ], - changeNote: [ - { - lang: '', - value: 'Muutoshistoria', - regex: '(?s)^.*$', - }, - ], - subjectArea: [ - { - lang: '', - value: 'aihealue', - regex: '(?s)^.*$', - }, - ], - }, - references: { - prefLabelXl: [ - { - id: '7a7b337a-8e13-42fb-be87-2ba0371981f1', - code: '', - uri: '', - number: 0, - createdBy: 'Admin User', - createdDate: '1970-01-01T00:00:00.000Z', - lastModifiedBy: 'Admin User', - lastModifiedDate: '1970-01-01T00:00:00.000Z', - type: { - id: 'Term', - graph: { - id: '987987-987-987987', - }, - uri: '', - }, - properties: { - prefLabel: [ - { - lang: 'fi', - value: 'testi', - regex: '(?s)^.*$', - }, - ], - status: [ - { - lang: '', - value: 'DRAFT', - regex: '(?s)^.*$', - }, - ], - termInfo: [ - { - lang: '', - value: 'termin lisätieto', - regex: '(?s)^.*$', - }, - ], - wordClass: [ - { - lang: '', - value: 'adjective', - regex: '(?s)^.*$', - }, - ], - termConjugation: [ - { - lang: '', - value: 'singular', - regex: '(?s)^.*$', - }, - ], - editorialNote: [ - { - lang: '', - value: 'Ylläpitäjän muistiinpano', - regex: '(?s)^.*$', - }, - ], - changeNote: [ - { - lang: '', - value: 'Muutoshistoria', - regex: '(?s)^.*$', - }, - ], - historyNote: [ - { - lang: '', - value: 'Käytön historiatieto "etymologia"', - regex: '(?s)^.*$', - }, - ], - termStyle: [ - { - lang: '', - value: 'spoken-form', - regex: '(?s)^.*$', - }, - ], - source: [ - { - lang: '', - value: 'lähteet', - regex: '(?s)^.*$', - }, - ], - scope: [ - { - lang: '', - value: 'käyttöala', - regex: '(?s)^.*$', - }, - ], - termFamily: [ - { - lang: '', - value: 'neutral', - regex: '(?s)^.*$', - }, - ], - }, - references: {}, - referrers: {}, - identifier: { - id: '7a7b337a-8e13-42fb-be87-2ba0371981f1', - type: { - id: 'Term', - graph: { - id: '987987-987-987987', - }, - uri: '', - }, - }, - }, - ], - }, - referrers: {}, - identifier: { - id: '2a2b8b11-4dc9-4ddb-afde-812560a5b007', - type: { - id: 'Concept', - graph: { - id: '987987-987-987987', - }, - uri: '', - }, - }, - }, - ], - hasPart: [ - { - id: '2a2b8b11-4dc9-4ddb-afde-812560a5b007', - code: '', - uri: '', - number: 0, - createdBy: 'Admin User', - createdDate: '1970-01-01T00:00:00.000Z', - lastModifiedBy: 'Admin User', - lastModifiedDate: '1970-01-01T00:00:00.000Z', - type: { - id: 'Concept', - graph: { - id: '987987-987-987987', - }, - uri: '', - }, - properties: { - definition: [ - { - lang: 'fi', - value: 'Määritelmä, suomi FI', - regex: '(?s)^.*$', - }, - { - lang: 'en', - value: 'Määritelmä, englanti EN', - regex: '(?s)^.*$', - }, - ], - historyNote: [ - { - lang: '', - value: 'Käytön historiatieto "etymologia"', - regex: '(?s)^.*$', - }, - ], - conceptClass: [ - { - lang: '', - value: 'Käsitteen luokka', - regex: '(?s)^.*$', - }, - ], - wordClass: [ - { - lang: '', - value: 'adjective', - regex: '(?s)^.*$', - }, - ], - status: [ - { - lang: '', - value: 'DRAFT', - regex: '(?s)^.*$', - }, - ], - example: [ - { - lang: 'fi', - value: 'esimerkki', - regex: '(?s)^.*$', - }, - { - lang: 'en', - value: 'example', - regex: '(?s)^.*$', - }, - ], - note: [ - { - lang: 'fi', - value: 'Huomautus', - regex: '(?s)^.*$', - }, - { - lang: 'en', - value: 'Huomautus', - regex: '(?s)^.*$', - }, - ], - changeNote: [ - { - lang: '', - value: 'Muutoshistoria', - regex: '(?s)^.*$', - }, - ], - subjectArea: [ - { - lang: '', - value: 'aihealue', - regex: '(?s)^.*$', - }, - ], - }, - references: { - prefLabelXl: [ - { - id: '7a7b337a-8e13-42fb-be87-2ba0371981f1', - code: '', - uri: '', - number: 0, - createdBy: 'Admin User', - createdDate: '1970-01-01T00:00:00.000Z', - lastModifiedBy: 'Admin User', - lastModifiedDate: '1970-01-01T00:00:00.000Z', - type: { - id: 'Term', - graph: { - id: '987987-987-987987', - }, - uri: '', - }, - properties: { - prefLabel: [ - { - lang: 'fi', - value: 'testi', - regex: '(?s)^.*$', - }, - ], - status: [ - { - lang: '', - value: 'DRAFT', - regex: '(?s)^.*$', - }, - ], - termInfo: [ - { - lang: '', - value: 'termin lisätieto', - regex: '(?s)^.*$', - }, - ], - wordClass: [ - { - lang: '', - value: 'adjective', - regex: '(?s)^.*$', - }, - ], - termConjugation: [ - { - lang: '', - value: 'singular', - regex: '(?s)^.*$', - }, - ], - editorialNote: [ - { - lang: '', - value: 'Ylläpitäjän muistiinpano', - regex: '(?s)^.*$', - }, - ], - changeNote: [ - { - lang: '', - value: 'Muutoshistoria', - regex: '(?s)^.*$', - }, - ], - historyNote: [ - { - lang: '', - value: 'Käytön historiatieto "etymologia"', - regex: '(?s)^.*$', - }, - ], - termStyle: [ - { - lang: '', - value: 'spoken-form', - regex: '(?s)^.*$', - }, - ], - source: [ - { - lang: '', - value: 'lähteet', - regex: '(?s)^.*$', - }, - ], - scope: [ - { - lang: '', - value: 'käyttöala', - regex: '(?s)^.*$', - }, - ], - termFamily: [ - { - lang: '', - value: 'neutral', - regex: '(?s)^.*$', - }, - ], - }, - references: {}, - referrers: {}, - identifier: { - id: '7a7b337a-8e13-42fb-be87-2ba0371981f1', - type: { - id: 'Term', - graph: { - id: '987987-987-987987', - }, - uri: '', - }, - }, - }, - ], - }, - referrers: {}, - identifier: { - id: '2a2b8b11-4dc9-4ddb-afde-812560a5b007', - type: { - id: 'Concept', - graph: { - id: '987987-987-987987', - }, - uri: '', - }, - }, - }, - ], - relatedMatch: [ - { - id: 'a87ae2c2-3c16-494a-9f82-928fc1840a1e', - code: 'concept-link-1000', - uri: 'http://uri.suomi.fi/terminology/sanasto/concept-link-1000', - number: 0, - createdBy: 'Admin User', - createdDate: '1970-01-01T00:00:00.000Z', - lastModifiedBy: 'Admin User', - lastModifiedDate: '1970-01-01T00:00:00.000Z', - type: { - id: 'ConceptLink', - graph: { - id: '987987-987-987987', - }, - uri: '', - }, - properties: { - prefLabel: [ - { - lang: 'fi', - value: 'demo', - regex: '(?s)^.*$', - }, - ], - vocabularyLabel: [ - { - lang: 'fi', - value: 'testi', - regex: '(?s)^.*$', - }, - { - lang: 'en', - value: 'test', - regex: '(?s)^.*$', - }, - ], - targetId: [ - { - lang: '', - value: '7b179ea2-b28c-497e-9e81-6ff254235ea1', - regex: '(?s)^.*$', - }, - ], - targetGraph: [ - { - lang: '', - value: 'ec43f161-b85d-4786-a4b9-d0da52edfba1', - regex: '(?s)^.*$', - }, - ], - }, - references: {}, - referrers: {}, - identifier: { - id: 'a87ae2c2-3c16-494a-9f82-928fc1840a1e', - type: { - id: 'ConceptLink', - graph: { - id: '987987-987-987987', - }, - uri: '', - }, - }, - }, - ], - narrower: [ - { - id: '6e00b816-c077-4747-8597-46047005584d', - code: 'concept-3005', - uri: 'http://uri.suomi.fi/terminology/sanasto/concept-3005', - number: 0, - createdBy: 'Admin User', - createdDate: '1970-01-01T00:00:00.000Z', - lastModifiedBy: 'Admin User', - lastModifiedDate: '1970-01-01T00:00:00.000Z', - type: { - id: 'Concept', - graph: { - id: '987987-987-987987', - }, - uri: '', - }, - properties: { - definition: [ - { - lang: 'fi', - value: ' määritelmä', - regex: '(?s)^.*$', - }, - ], - note: [ - { - lang: 'fi', - value: ' huomautu', - regex: '(?s)^.*$', - }, - ], - editorialNote: [ - { - lang: '', - value: 'ylläpitäjän muistiinpano', - regex: '(?s)^.*$', - }, - ], - example: [ - { - lang: 'fi', - value: 'käyttöesimerkki', - regex: '(?s)^.*$', - }, - ], - conceptScope: [ - { - lang: '', - value: 'käyttöala', - regex: '(?s)^.*$', - }, - ], - conceptClass: [ - { - lang: '', - value: 'käsitteen luokka', - regex: '(?s)^.*$', - }, - ], - wordClass: [ - { - lang: '', - value: 'sanaluokka', - regex: '(?s)^.*$', - }, - ], - changeNote: [ - { - lang: '', - value: 'muutoshistoriatieto', - regex: '(?s)^.*$', - }, - ], - historyNote: [ - { - lang: '', - value: 'käytön historiatieto', - regex: '(?s)^.*$', - }, - ], - status: [ - { - lang: '', - value: 'DRAFT', - regex: '(?s)^.*$', - }, - ], - notation: [ - { - lang: '', - value: 'systemaattinen merkintätapa', - regex: '(?s)^.*$', - }, - ], - source: [ - { - lang: '', - value: 'lähde', - regex: '(?s)^.*$', - }, - ], - subjectArea: [ - { - lang: '', - value: 'aihealue', - regex: '(?s)^.*$', - }, - ], - }, - references: { - prefLabelXl: [ - { - id: 'fe220ad1-6874-48e0-945c-1f79fbb4d1d0', - code: 'term-3005', - uri: 'http://uri.suomi.fi/terminology/sanasto/term-3005', - number: 0, - createdBy: 'Admin User', - createdDate: '1970-01-01T00:00:00.000Z', - lastModifiedBy: 'Admin User', - lastModifiedDate: '1970-01-01T00:00:00.000Z', - type: { - id: 'Term', - graph: { - id: '987987-987-987987', - }, - uri: '', - }, - properties: { - prefLabel: [ - { - lang: 'fi', - value: 'uusi käsite', - regex: '(?s)^.*$', - }, - ], - source: [ - { - lang: '', - value: 'lähde', - regex: '(?s)^.*$', - }, - ], - scope: [ - { - lang: '', - value: 'käyttöala', - regex: '(?s)^.*$', - }, - ], - termStyle: [ - { - lang: '', - value: 'termin tyyli', - regex: '(?s)^.*$', - }, - ], - termFamily: [ - { - lang: '', - value: 'termin suku', - regex: '(?s)^.*$', - }, - ], - termConjugation: [ - { - lang: '', - value: 'termin luku', - regex: '(?s)^.*$', - }, - ], - termEquivalency: [ - { - lang: '', - value: 'termin vastaavuus', - regex: '(?s)^.*$', - }, - ], - termInfo: [ - { - lang: '', - value: 'termin lisätieto', - regex: '(?s)^.*$', - }, - ], - wordClass: [ - { - lang: '', - value: 'sanaluokka', - regex: '(?s)^.*$', - }, - ], - termHomographNumber: [ - { - lang: '', - value: '1', - regex: '(?s)^.*$', - }, - ], - editorialNote: [ - { - lang: '', - value: 'ylläpitäjän muistiinpano', - regex: '(?s)^.*$', - }, - ], - draftComment: [ - { - lang: '', - value: 'luonnosvaiheen kommentti', - regex: '(?s)^.*$', - }, - ], - historyNote: [ - { - lang: '', - value: 'käytön historiatieto', - regex: '(?s)^.*$', - }, - ], - changeNote: [ - { - lang: '', - value: 'muutoshistoriatieto', - regex: '(?s)^.*$', - }, - ], - status: [ - { - lang: '', - value: 'DRAFT', - regex: '(?s)^.*$', - }, - ], - termEquivalencyRelation: [ - { - lang: '', - value: 'termin, johon vastaavuus liitty', - regex: '(?s)^.*$', - }, - ], - }, - references: {}, - referrers: {}, - identifier: { - id: 'fe220ad1-6874-48e0-945c-1f79fbb4d1d0', - type: { - id: 'Term', - graph: { - id: '987987-987-987987', - }, - uri: '', - }, - }, - }, - { - id: 'd2bdd425-7e7e-44d5-8121-b370267febf9', - code: 'term-5000', - uri: 'http://uri.suomi.fi/terminology/sanasto/term-5000', - number: 0, - createdBy: 'Admin User', - createdDate: '1970-01-01T00:00:00.000Z', - lastModifiedBy: 'Admin User', - lastModifiedDate: '1970-01-01T00:00:00.000Z', - type: { - id: 'Term', - graph: { - id: '987987-987-987987', - }, - uri: '', - }, - properties: { - prefLabel: [ - { - lang: 'en', - value: 'new concept', - regex: '(?s)^.*$', - }, - ], - status: [ - { - lang: '', - value: 'DRAFT', - regex: '(?s)^.*$', - }, - ], - }, - references: {}, - referrers: {}, - identifier: { - id: 'd2bdd425-7e7e-44d5-8121-b370267febf9', - type: { - id: 'Term', - graph: { - id: '987987-987-987987', - }, - uri: '', - }, - }, - }, - ], - }, - referrers: {}, - identifier: { - id: '6e00b816-c077-4747-8597-46047005584d', - type: { - id: 'Concept', - graph: { - id: '987987-987-987987', - }, - uri: '', - }, - }, - }, - ], - broader: [ - { - id: '148ab016-36ee-486a-862a-87d6dde2f86f', - code: 'concept-3001', - uri: 'http://uri.suomi.fi/terminology/sanasto/concept-3001', - number: 0, - createdBy: 'Admin User', - createdDate: '1970-01-01T00:00:00.000Z', - lastModifiedBy: 'Admin User', - lastModifiedDate: '1970-01-01T00:00:00.000Z', - type: { - id: 'Concept', - graph: { - id: '987987-987-987987', - }, - uri: '', - }, - properties: { - status: [ - { - lang: '', - value: 'DRAFT', - regex: '(?s)^.*$', - }, - ], - }, - references: { - prefLabelXl: [ - { - id: '25633b86-6d29-478d-ae28-f3040d84ebc5', - code: 'term-3001', - uri: 'http://uri.suomi.fi/terminology/sanasto/term-3001', - number: 0, - createdBy: 'Admin User', - createdDate: '1970-01-01T00:00:00.000Z', - lastModifiedBy: 'Admin User', - lastModifiedDate: '1970-01-01T00:00:00.000Z', - type: { - id: 'Term', - graph: { - id: '987987-987-987987', - }, - uri: '', - }, - properties: { - prefLabel: [ - { - lang: 'fi', - value: 'emokäsite', - regex: '(?s)^.*$', - }, - ], - status: [ - { - lang: '', - value: 'DRAFT', - regex: '(?s)^.*$', - }, - ], - }, - references: {}, - referrers: {}, - identifier: { - id: '25633b86-6d29-478d-ae28-f3040d84ebc5', - type: { - id: 'Term', - graph: { - id: '987987-987-987987', - }, - uri: '', - }, - }, - }, - ], - }, - referrers: {}, - identifier: { - id: '148ab016-36ee-486a-862a-87d6dde2f86f', - type: { - id: 'Concept', - graph: { - id: '987987-987-987987', - }, - uri: '', - }, - }, - }, - ], - prefLabelXl: [ - { - id: '87713416-0a6e-4831-afa0-21fc05e3d6cd', - code: 'term-4000', - uri: 'http://uri.suomi.fi/terminology/sanasto/term-4000', - number: 0, - createdBy: 'Admin User', - createdDate: '1970-01-01T00:00:00.000Z', - lastModifiedBy: 'Admin User', - lastModifiedDate: '1970-01-01T00:00:00.000Z', - type: { - id: 'Term', - graph: { - id: '987987-987-987987', - }, - uri: '', - }, - properties: { - changeNote: [ - { - lang: '', - value: 'muutoshistoria', - regex: '(?s)^.*$', - }, - ], - editorialNote: [ - { - lang: '', - value: 'ylläpitäjän muistiinpano', - regex: '(?s)^.*$', - }, - ], - historyNote: [ - { - lang: '', - value: 'käytön historiatieto', - regex: '(?s)^.*$', - }, - ], - prefLabel: [ - { - lang: 'fi', - value: 'demo', - regex: '(?s)^.*$', - }, - ], - scope: [ - { - lang: '', - value: 'käyttöala', - regex: '(?s)^.*$', - }, - ], - source: [ - { - lang: '', - value: 'käyttöala', - regex: '(?s)^.*$', - }, - ], - status: [ - { - lang: '', - value: 'DRAFT', - regex: '(?s)^.*$', - }, - ], - termConjugation: [ - { - lang: '', - value: 'singular', - regex: '(?s)^.*$', - }, - ], - termFamily: [ - { - lang: '', - value: 'feminine', - regex: '(?s)^.*$', - }, - ], - termHomographNumber: [ - { - lang: '', - value: '1', - regex: '(?s)^.*$', - }, - ], - termInfo: [ - { - lang: '', - value: 'termin lisätieto', - regex: '(?s)^.*$', - }, - ], - termStyle: [ - { - lang: '', - value: 'spoken-form', - regex: '(?s)^.*$', - }, - ], - wordClass: [ - { - lang: '', - value: 'adjective', - regex: '(?s)^.*$', - }, - ], - }, - references: {}, - referrers: { - prefLabelXl: [ - { - id: 'c1207701-de9c-4759-ae45-8e278e08b0c1', - code: 'concept-4000', - uri: 'http://uri.suomi.fi/terminology/sanasto/concept-4000', - number: 0, - createdBy: 'Admin User', - createdDate: '1970-01-01T00:00:00.000Z', - lastModifiedBy: 'Admin User', - lastModifiedDate: '1970-01-01T00:00:00.000Z', - type: { - id: 'Concept', - graph: { - id: '987987-987-987987', - }, - uri: '', - }, - properties: { - wordClass: [ - { - lang: '', - value: 'adjective', - regex: '(?s)^.*$', - }, - ], - source: [ - { - lang: '', - value: 'lähde', - regex: '(?s)^.*$', - }, - ], - changeNote: [ - { - lang: '', - value: 'muutoshistoria', - regex: '(?s)^.*$', - }, - ], - conceptClass: [ - { - lang: '', - value: 'käsitteen luokka', - regex: '(?s)^.*$', - }, - ], - definition: [ - { - lang: 'fi', - value: 'määritelmä', - regex: '(?s)^.*$', - }, - { - lang: 'en', - value: 'definition ', - regex: '(?s)^.*$', - }, - ], - editorialNote: [ - { - lang: '', - value: 'ylläpitäjän muistiinpano', - regex: '(?s)^.*$', - }, - ], - example: [ - { - lang: 'fi', - value: 'käyttöesimerkki', - regex: '(?s)^.*$', - }, - ], - historyNote: [ - { - lang: '', - value: 'käytön historiatieto', - regex: '(?s)^.*$', - }, - ], - note: [ - { - lang: 'fi', - value: 'huomautus', - regex: '(?s)^.*$', - }, - ], - status: [ - { - lang: '', - value: 'DRAFT', - regex: '(?s)^.*$', - }, - ], - subjectArea: [ - { - lang: '', - value: 'aihealue', - regex: '(?s)^.*$', - }, - ], - }, - references: {}, - referrers: {}, - identifier: { - id: 'c1207701-de9c-4759-ae45-8e278e08b0c1', - type: { - id: 'Concept', - graph: { - id: '987987-987-987987', - }, - uri: '', - }, - }, - }, - ], - }, - identifier: { - id: '87713416-0a6e-4831-afa0-21fc05e3d6cd', - type: { - id: 'Term', - graph: { - id: '987987-987-987987', - }, - uri: '', - }, - }, - }, - ], - isPartOf: [ - { - id: '2a2b8b11-4dc9-4ddb-afde-812560a5b007', - code: '', - uri: '', - number: 0, - createdBy: 'Admin User', - createdDate: '1970-01-01T00:00:00.000Z', - lastModifiedBy: 'Admin User', - lastModifiedDate: '1970-01-01T00:00:00.000Z', - type: { - id: 'Concept', - graph: { - id: '987987-987-987987', - }, - uri: '', - }, - properties: { - definition: [ - { - lang: 'fi', - value: 'Määritelmä, suomi FI', - regex: '(?s)^.*$', - }, - { - lang: 'en', - value: 'Määritelmä, englanti EN', - regex: '(?s)^.*$', - }, - ], - historyNote: [ - { - lang: '', - value: 'Käytön historiatieto "etymologia"', - regex: '(?s)^.*$', - }, - ], - conceptClass: [ - { - lang: '', - value: 'Käsitteen luokka', - regex: '(?s)^.*$', - }, - ], - wordClass: [ - { - lang: '', - value: 'adjective', - regex: '(?s)^.*$', - }, - ], - status: [ - { - lang: '', - value: 'DRAFT', - regex: '(?s)^.*$', - }, - ], - example: [ - { - lang: 'fi', - value: 'Käyttöesimerkki', - regex: '(?s)^.*$', - }, - { - lang: 'en', - value: 'Käyttöesimerkki', - regex: '(?s)^.*$', - }, - ], - note: [ - { - lang: 'fi', - value: 'Huomautus', - regex: '(?s)^.*$', - }, - { - lang: 'en', - value: 'Huomautus', - regex: '(?s)^.*$', - }, - ], - changeNote: [ - { - lang: '', - value: 'Muutoshistoria', - regex: '(?s)^.*$', - }, - ], - subjectArea: [ - { - lang: '', - value: 'aihealue', - regex: '(?s)^.*$', - }, - ], - }, - references: { - prefLabelXl: [ - { - id: '7a7b337a-8e13-42fb-be87-2ba0371981f1', - code: '', - uri: '', - number: 0, - createdBy: 'Admin User', - createdDate: '1970-01-01T00:00:00.000Z', - lastModifiedBy: 'Admin User', - lastModifiedDate: '1970-01-01T00:00:00.000Z', - type: { - id: 'Term', - graph: { - id: '987987-987-987987', - }, - uri: '', - }, - properties: { - prefLabel: [ - { - lang: 'fi', - value: 'testi', - regex: '(?s)^.*$', - }, - ], - status: [ - { - lang: '', - value: 'DRAFT', - regex: '(?s)^.*$', - }, - ], - termInfo: [ - { - lang: '', - value: 'termin lisätieto', - regex: '(?s)^.*$', - }, - ], - wordClass: [ - { - lang: '', - value: 'adjective', - regex: '(?s)^.*$', - }, - ], - termConjugation: [ - { - lang: '', - value: 'singular', - regex: '(?s)^.*$', - }, - ], - editorialNote: [ - { - lang: '', - value: 'Ylläpitäjän muistiinpano', - regex: '(?s)^.*$', - }, - ], - changeNote: [ - { - lang: '', - value: 'Muutoshistoria', - regex: '(?s)^.*$', - }, - ], - historyNote: [ - { - lang: '', - value: 'Käytön historiatieto "etymologia"', - regex: '(?s)^.*$', - }, - ], - termStyle: [ - { - lang: '', - value: 'spoken-form', - regex: '(?s)^.*$', - }, - ], - source: [ - { - lang: '', - value: 'lähteet', - regex: '(?s)^.*$', - }, - ], - scope: [ - { - lang: '', - value: 'käyttöala', - regex: '(?s)^.*$', - }, - ], - termFamily: [ - { - lang: '', - value: 'neutral', - regex: '(?s)^.*$', - }, - ], - }, - references: {}, - referrers: {}, - identifier: { - id: '7a7b337a-8e13-42fb-be87-2ba0371981f1', - type: { - id: 'Term', - graph: { - id: '987987-987-987987', - }, - uri: '', - }, - }, - }, - ], - }, - referrers: {}, - identifier: { - id: '2a2b8b11-4dc9-4ddb-afde-812560a5b007', - type: { - id: 'Concept', - graph: { - id: '987987-987-987987', - }, - uri: '', - }, - }, - }, - ], - notRecommendedSynonym: [ - { - id: '3aa3c3ca-bc58-4e83-97be-ef748ca907bd', - code: 'term-4002', - uri: 'http://uri.suomi.fi/terminology/sanasto/term-4002', - number: 0, - createdBy: 'Admin User', - createdDate: '1970-01-01T00:00:00.000Z', - lastModifiedBy: 'Admin User', - lastModifiedDate: '1970-01-01T00:00:00.000Z', - type: { - id: 'Term', - graph: { - id: '987987-987-987987', - }, - uri: '', - }, - properties: { - changeNote: [ - { - lang: '', - value: 'muutoshistoria', - regex: '(?s)^.*$', - }, - ], - editorialNote: [ - { - lang: '', - value: 'ylläpitäjän muistiinpano', - regex: '(?s)^.*$', - }, - ], - historyNote: [ - { - lang: '', - value: 'käytön historiatieto', - regex: '(?s)^.*$', - }, - ], - prefLabel: [ - { - lang: 'fi', - value: 'Ei-suositettava synonyymi', - regex: '(?s)^.*$', - }, - ], - scope: [ - { - lang: '', - value: 'käyttöala', - regex: '(?s)^.*$', - }, - ], - source: [ - { - lang: '', - value: 'käyttöala', - regex: '(?s)^.*$', - }, - ], - status: [ - { - lang: '', - value: 'DRAFT', - regex: '(?s)^.*$', - }, - ], - termConjugation: [ - { - lang: '', - value: 'yksikkö', - regex: '(?s)^.*$', - }, - ], - termEquivalency: [ - { - lang: '', - value: '~', - regex: '(?s)^.*$', - }, - ], - termFamily: [ - { - lang: '', - value: 'neutri', - regex: '(?s)^.*$', - }, - ], - termHomographNumber: [ - { - lang: '', - value: '1', - regex: '(?s)^.*$', - }, - ], - termInfo: [ - { - lang: '', - value: 'termin lisätieto', - regex: '(?s)^.*$', - }, - ], - termStyle: [ - { - lang: '', - value: 'puhekieli', - regex: '(?s)^.*$', - }, - ], - wordClass: [ - { - lang: '', - value: 'adjective', - regex: '(?s)^.*$', - }, - ], - }, - references: {}, - referrers: {}, - identifier: { - id: '3aa3c3ca-bc58-4e83-97be-ef748ca907bd', - type: { - id: 'Term', - graph: { - id: '987987-987-987987', - }, - uri: '', - }, - }, - }, - ], - closeMatch: [ - { - id: 'a87ae2c2-3c16-494a-9f82-928fc1840a1f', - code: 'concept-link-2000', - uri: 'http://uri.suomi.fi/terminology/sanasto/concept-link-2000', - number: 0, - createdBy: 'Admin User', - createdDate: '1970-01-01T00:00:00.000Z', - lastModifiedBy: 'Admin User', - lastModifiedDate: '1970-01-01T00:00:00.000Z', - type: { - id: 'ConceptLink', - graph: { - id: '1987987-987-987987', - }, - uri: '', - }, - properties: { - prefLabel: [ - { - lang: 'fi', - value: 'demo', - regex: '(?s)^.*$', - }, - ], - vocabularyLabel: [ - { - lang: 'fi', - value: 'testi', - regex: '(?s)^.*$', - }, - { - lang: 'en', - value: 'test', - regex: '(?s)^.*$', - }, - ], - targetId: [ - { - lang: '', - value: '7b179ea2-b28c-497e-9e81-6ff254235ea1', - regex: '(?s)^.*$', - }, - ], - targetGraph: [ - { - lang: '', - value: 'ec43f161-b85d-4786-a4b9-d0da52edfba1', - regex: '(?s)^.*$', - }, - ], - }, - references: {}, - referrers: {}, - identifier: { - id: 'a87ae2c2-3c16-494a-9f82-928fc1840a1f', - type: { - id: 'ConceptLink', - graph: { - id: '987987-987-987987', - }, - uri: '', - }, - }, - }, - ], - broadMatch: [ - { - id: 'a87ae2c2-3c16-494a-9f82-928fc1840a1f', - code: 'concept-link-2000', - uri: 'http://uri.suomi.fi/terminology/sanasto/concept-link-2000', - number: 0, - createdBy: 'Admin User', - createdDate: '1970-01-01T00:00:00.000Z', - lastModifiedBy: 'Admin User', - lastModifiedDate: '1970-01-01T00:00:00.000Z', - type: { - id: 'ConceptLink', - graph: { - id: '1987987-987-987987', - }, - uri: '', - }, - properties: { - prefLabel: [ - { - lang: 'fi', - value: 'demo', - regex: '(?s)^.*$', - }, - ], - vocabularyLabel: [ - { - lang: 'fi', - value: 'testi', - regex: '(?s)^.*$', - }, - { - lang: 'en', - value: 'test', - regex: '(?s)^.*$', - }, - ], - targetId: [ - { - lang: '', - value: '7b179ea2-b28c-497e-9e81-6ff254235ea1', - regex: '(?s)^.*$', - }, - ], - targetGraph: [ - { - lang: '', - value: 'ec43f161-b85d-4786-a4b9-d0da52edfba1', - regex: '(?s)^.*$', - }, - ], - }, - references: {}, - referrers: {}, - identifier: { - id: 'a87ae2c2-3c16-494a-9f82-928fc1840a1f', - type: { - id: 'ConceptLink', - graph: { - id: '987987-987-987987', - }, - uri: '', - }, - }, - }, - ], - narrowMatch: [ - { - id: 'a87ae2c2-3c16-494a-9f82-928fc1840a1f', - code: 'concept-link-2000', - uri: 'http://uri.suomi.fi/terminology/sanasto/concept-link-2000', - number: 0, - createdBy: 'Admin User', - createdDate: '1970-01-01T00:00:00.000Z', - lastModifiedBy: 'Admin User', - lastModifiedDate: '1970-01-01T00:00:00.000Z', - type: { - id: 'ConceptLink', - graph: { - id: '1987987-987-987987', - }, - uri: '', - }, - properties: { - prefLabel: [ - { - lang: 'fi', - value: 'demo', - regex: '(?s)^.*$', - }, - ], - vocabularyLabel: [ - { - lang: 'fi', - value: 'testi', - regex: '(?s)^.*$', - }, - { - lang: 'en', - value: 'test', - regex: '(?s)^.*$', - }, - ], - targetId: [ - { - lang: '', - value: '7b179ea2-b28c-497e-9e81-6ff254235ea1', - regex: '(?s)^.*$', - }, - ], - targetGraph: [ - { - lang: '', - value: 'ec43f161-b85d-4786-a4b9-d0da52edfba1', - regex: '(?s)^.*$', - }, - ], - }, - references: {}, - referrers: {}, - identifier: { - id: 'a87ae2c2-3c16-494a-9f82-928fc1840a1f', - type: { - id: 'ConceptLink', - graph: { - id: '987987-987-987987', - }, - uri: '', - }, - }, - }, - ], - }, - referrers: {}, - type: { - graph: { - id: '987987-987-987987', - }, - id: 'Concept', - uri: '', - }, - uri: 'uri.suomi.fi/terminology/sanasto/concept-1000', - }, - { fi: 'keskeneräinen sanasto' } + { fi: 'demo' }, + //TODO: + undefined ); export const extensiveDataExpected = { diff --git a/terminology-ui/src/modules/edit-concept/index.tsx b/terminology-ui/src/modules/edit-concept/index.tsx index 7fa39790d..37c4a416d 100644 --- a/terminology-ui/src/modules/edit-concept/index.tsx +++ b/terminology-ui/src/modules/edit-concept/index.tsx @@ -15,9 +15,7 @@ import { ConceptTermType, } from './new-concept.types'; import generateFormData from './generate-form-data'; -import { useSelector } from 'react-redux'; import { - selectLogin, useGetAuthenticatedUserMutMutation, useGetAuthenticatedUserQuery, } from '@app/common/components/login/login.slice'; @@ -55,7 +53,7 @@ export default function EditConcept({ const [createConcept, createConceptStatus] = useCreateConceptMutation(); const [updateConcept, updateConceptStatus] = useUpdateConceptMutation(); const [isCreating, setIsCreating] = useState(false); - const user = useSelector(selectLogin()); + const { data: terminology } = useGetTerminologyQuery({ id: terminologyId, }); @@ -138,7 +136,13 @@ export default function EditConcept({ `/terminology/${terminologyId}/concept/${formData.basicInformation.identifier}` ); } - }, [createConceptStatus, updateConceptStatus, terminologyId, router]); + }, [ + createConceptStatus, + updateConceptStatus, + terminologyId, + router, + formData.basicInformation.identifier, + ]); useEffect(() => { if (formData.terms.some((term) => term.id === '')) { diff --git a/terminology-ui/src/modules/edit-vocabulary/index.tsx b/terminology-ui/src/modules/edit-vocabulary/index.tsx index c21c3ee03..304256de6 100644 --- a/terminology-ui/src/modules/edit-vocabulary/index.tsx +++ b/terminology-ui/src/modules/edit-vocabulary/index.tsx @@ -1,10 +1,8 @@ import { Breadcrumb, BreadcrumbLink } from 'yti-common-ui/breadcrumb'; import { - selectLogin, useGetAuthenticatedUserMutMutation, useGetAuthenticatedUserQuery, } from '@app/common/components/login/login.slice'; -import { useEditTerminologyMutation } from '@app/common/components/modify/modify.slice'; import SaveSpinner from 'yti-common-ui/save-spinner'; import Title from 'yti-common-ui/title'; import { @@ -15,7 +13,6 @@ import useConfirmBeforeLeavingPage from 'yti-common-ui/utils/hooks/use-confirm-b import { useTranslation } from 'next-i18next'; import { useRouter } from 'next/router'; import { useCallback, useEffect, useState } from 'react'; -import { useSelector } from 'react-redux'; import { Button, Heading, @@ -57,7 +54,6 @@ export default function EditVocabulary({ terminologyId }: EditVocabularyProps) { const { data: info, error: infoError } = useGetTerminologyQuery({ id: terminologyId, }); - const user = useSelector(selectLogin()); const [data, setData] = useState(generateInitialData(i18n.language, info)); const [isValid, setIsValid] = useState(true); const [userPosted, setUserPosted] = useState(false); diff --git a/terminology-ui/src/modules/new-terminology/new-terminology-modal.tsx b/terminology-ui/src/modules/new-terminology/new-terminology-modal.tsx index 19da08f36..d92f6bde1 100644 --- a/terminology-ui/src/modules/new-terminology/new-terminology-modal.tsx +++ b/terminology-ui/src/modules/new-terminology/new-terminology-modal.tsx @@ -79,7 +79,15 @@ export default function NewTerminologyModal({ setIsCreating(false); setError(true); } - }, [t, newTerminology, dispatch, handleClose, router, disableConfirmation]); + }, [ + t, + newTerminology, + dispatch, + handleClose, + router, + disableConfirmation, + manualData?.prefix, + ]); const handleCloseRequest = () => { handleClose(); diff --git a/terminology-ui/src/modules/own-information/index.tsx b/terminology-ui/src/modules/own-information/index.tsx index 174d1bd29..6a1cb2034 100644 --- a/terminology-ui/src/modules/own-information/index.tsx +++ b/terminology-ui/src/modules/own-information/index.tsx @@ -14,7 +14,6 @@ import { useSelector } from 'react-redux'; import { selectLogin } from '@app/common/components/login/login.slice'; import Separator from 'yti-common-ui/separator'; import { useGetOrganizationsQuery } from '@app/common/components/terminology-search/terminology-search.slice'; -import { getPropertyValue } from '@app/common/components/property-value/get-property-value'; import sortBy from 'lodash/sortBy'; import AccessRequest from '../../common/components/access-request'; import SubscriptionBlock from './subscription-block'; diff --git a/terminology-ui/src/modules/terminology-search/index.tsx b/terminology-ui/src/modules/terminology-search/index.tsx index a08c95245..27411bba7 100644 --- a/terminology-ui/src/modules/terminology-search/index.tsx +++ b/terminology-ui/src/modules/terminology-search/index.tsx @@ -74,7 +74,7 @@ export default function TerminologySearch() { id: o.id, label: getLanguageVersion({ data: o.label, lang: i18n.language }), })); - }, [orgsData]); + }, [orgsData, i18n.language]); const groups = useMemo(() => { if (!groupsData) { @@ -85,7 +85,7 @@ export default function TerminologySearch() { id: g.identifier, label: getLanguageVersion({ data: g.label, lang: i18n.language }), })); - }, [groupsData]); + }, [groupsData, i18n.language]); const results: SearchResultData[] = useMemo(() => { if (!data || !data.responseObjects) { @@ -113,7 +113,7 @@ export default function TerminologySearch() { titleLink: `terminology/${terminology.prefix}`, type: translateTerminologyType(terminology.type, t), })); - }, [data, t, i18n.language]); + }, [data, t, i18n.language, organizations, groups]); const extra = useMemo(() => { return ( diff --git a/terminology-ui/src/pages/terminology/[terminologyId]/collection/[collectionId]/edit.tsx b/terminology-ui/src/pages/terminology/[terminologyId]/collection/[collectionId]/edit.tsx index e8b9fe906..d7c94881b 100644 --- a/terminology-ui/src/pages/terminology/[terminologyId]/collection/[collectionId]/edit.tsx +++ b/terminology-ui/src/pages/terminology/[terminologyId]/collection/[collectionId]/edit.tsx @@ -8,7 +8,6 @@ import { } from 'yti-common-ui/common-context-provider'; import PageHead from 'yti-common-ui/page-head'; import { getStoreData } from '@app/common/utils/get-store-data'; -import { getPropertyValue } from '@app/common/components/property-value/get-property-value'; import { getTerminology, getRunningQueriesThunk, diff --git a/terminology-ui/src/pages/terminology/[terminologyId]/concept/[conceptId]/edit.tsx b/terminology-ui/src/pages/terminology/[terminologyId]/concept/[conceptId]/edit.tsx index fe8d04cf4..4fd23acf5 100644 --- a/terminology-ui/src/pages/terminology/[terminologyId]/concept/[conceptId]/edit.tsx +++ b/terminology-ui/src/pages/terminology/[terminologyId]/concept/[conceptId]/edit.tsx @@ -20,12 +20,12 @@ import { getRunningQueriesThunk as getConceptRunningQueriesThunk, } from '@app/common/components/concept/concept.slice'; import { getStoreData } from '@app/common/utils/get-store-data'; -import { Concept } from '@app/common/interfaces/concept.interface'; import { wrapper } from '@app/store'; +import { ConceptInfo } from '@app/common/interfaces/interfaces-v2'; interface NewConceptPageProps extends CommonContextState { _netI18Next: SSRConfig; - conceptData: Concept; + conceptData: ConceptInfo; } export default function EditConcept(props: NewConceptPageProps) { From a430b431bbff2fa69fcdfb09c44854f20b35b4b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kalle=20Kytt=C3=A4l=C3=A4?= <kalle.kyttala@rebase.fi> Date: Fri, 6 Sep 2024 10:12:49 +0300 Subject: [PATCH 15/35] fix and rewrite tests --- .../access-request/access-request.test.tsx | 16 +- .../info-dropdown/info-expander.test.tsx | 150 +-- .../components/term-modal/term-modal.test.tsx | 139 +-- .../src/common/interfaces/interfaces-v2.ts | 10 +- .../src/common/utils/get-store-data.test.tsx | 6 +- .../src/modules/concept/utils.test.tsx | 224 ++-- .../generate-collection.test.tsx | 13 +- .../concept-terms-block/new-term-modal.tsx | 1 - .../concept-terms-block/term-form.test.tsx | 4 - .../generate-concept-test-expected.tsx | 3 +- .../edit-concept/generate-concept.test.tsx | 1089 +++-------------- .../generate-form-data-test-variables.tsx | 458 ++----- .../edit-concept/generate-form-data.test.tsx | 14 +- .../edit-concept/new-concept.types.tsx | 1 - .../generate-initial-data.test.ts | 621 +--------- .../edit-vocabulary/generate-initial-data.ts | 11 +- .../new-terminology/file-upload.test.tsx | 4 +- .../src/store/api-base-query.test.ts | 4 +- .../terminology/[terminologyId].test.tsx | 44 +- 19 files changed, 559 insertions(+), 2253 deletions(-) diff --git a/terminology-ui/src/common/components/access-request/access-request.test.tsx b/terminology-ui/src/common/components/access-request/access-request.test.tsx index 3210bc3b2..4ad990b6a 100644 --- a/terminology-ui/src/common/components/access-request/access-request.test.tsx +++ b/terminology-ui/src/common/components/access-request/access-request.test.tsx @@ -13,22 +13,8 @@ describe('access-request', () => { const organizations = [ { - code: '', id: '123123-321321', - properties: { - prefLabel: { - lang: 'fi', - value: 'Test-org', - regex: '', - }, - }, - type: { - graph: { - id: '321321-321321', - }, - id: 'Organization', - }, - uri: '', + label: { fi: 'Test-org' }, }, ]; diff --git a/terminology-ui/src/common/components/info-dropdown/info-expander.test.tsx b/terminology-ui/src/common/components/info-dropdown/info-expander.test.tsx index 94898db3b..7f0be4817 100644 --- a/terminology-ui/src/common/components/info-dropdown/info-expander.test.tsx +++ b/terminology-ui/src/common/components/info-dropdown/info-expander.test.tsx @@ -8,12 +8,12 @@ import { loginApi, } from '@app/common/components/login/login.slice'; import mockRouter from 'next-router-mock'; -import { v4 } from 'uuid'; import { adminControlsSlice } from '../admin-controls/admin-controls.slice'; import { terminologyApi } from '../vocabulary/vocabulary.slice'; import { subscriptionApi } from '../subscription/subscription.slice'; import { screen, waitFor } from '@testing-library/react'; import { terminologySearchApi } from '../terminology-search/terminology-search.slice'; +import { TerminologyInfo } from '@app/common/interfaces/interfaces-v2'; jest.mock('next/dist/client/router', () => require('next-router-mock')); @@ -25,32 +25,49 @@ const reducers = [ terminologySearchApi, ]; -describe('infoExpander', () => { - it('should render export button', async () => { - mockRouter.setCurrentUrl('/terminology/123-123'); - - renderWithProviders( - <InfoExpander - data={ - { - properties: {}, - references: { - contributor: [{ properties: { prefLabel: [] }, id: v4() }], - inGroup: [{ properties: { prefLabel: [] } }], - }, - } as any - } - />, - reducers - ); - - await waitFor(() => { - expect( - screen.getByText('tr-vocabulary-info-vocabulary-export') - ).toBeInTheDocument(); - }); - }); +const data = { + uri: '', + prefix: '', + graphType: 'TERMINOLOGICAL_VOCABULARY', + status: 'DRAFT', + languages: ['fi'], + contact: '', + label: { fi: 'label' }, + description: {}, + organizations: [ + { + id: '123', + label: { fi: 'testi 1' }, + }, + { + id: '456', + label: { fi: 'testi 2' }, + }, + { + id: '789', + label: { fi: 'testi 3' }, + }, + ], + groups: [ + { + id: '123', + label: { fi: 'group' }, + identifier: 'P10', + }, + ], + creator: { + id: '123', + name: 'Creator', + }, + created: '1970-01-01T00:00:00.000+00:00', + modifier: { + id: '123', + name: 'Modifier', + }, + modified: '1970-01-02T00:00:00.000+00:00', +} as TerminologyInfo; +describe('infoExpander', () => { it('should render subscribe button', async () => { const loginInitialState = Object.assign({}, initialState); loginInitialState['anonymous'] = false; @@ -60,21 +77,9 @@ describe('infoExpander', () => { mockRouter.setCurrentUrl('/terminology/123-123'); - renderWithProviders( - <InfoExpander - data={ - { - properties: {}, - references: { - contributor: [{ properties: { prefLabel: [] }, id: v4() }], - inGroup: [{ properties: { prefLabel: [] } }], - }, - } as any - } - />, - reducers, - { preloadedState: { login: loginInitialState } } - ); + renderWithProviders(<InfoExpander data={data} />, reducers, { + preloadedState: { login: loginInitialState }, + }); await expect( screen.findByText('tr-email-subscription-add') @@ -90,25 +95,9 @@ describe('infoExpander', () => { mockRouter.setCurrentUrl('/terminology/123-123'); - renderWithProviders( - <InfoExpander - data={ - { - createdBy: 'Creator', - createdDate: '1970-01-01T00:00:00.000+00:00', - lastModifiedBy: 'Modifier', - lastModifiedDate: '1970-01-02T00:00:00.000+00:00', - properties: {}, - references: { - contributor: [{ properties: { prefLabel: [] }, id: v4() }], - inGroup: [{ properties: { prefLabel: [] } }], - }, - } as any - } - />, - reducers, - { preloadedState: { login: loginInitialState } } - ); + renderWithProviders(<InfoExpander data={data} />, reducers, { + preloadedState: { login: loginInitialState }, + }); await waitFor(() => { expect(screen.getByText(/1.1.1970, 0.00/)).toBeInTheDocument(); @@ -128,44 +117,9 @@ describe('infoExpander', () => { mockRouter.setCurrentUrl('/terminology/123-123'); - renderWithProviders( - <InfoExpander - data={ - { - createdBy: 'Creator', - createdDate: '1970-01-01T00:00:00.000+00:00', - lastModifiedBy: 'Modifier', - lastModifiedDate: '1970-01-02T00:00:00.000+00:00', - properties: {}, - references: { - contributor: [ - { - id: 'testi 1', - properties: { - prefLabel: [{ lang: 'en', value: 'testi 1' }], - }, - }, - { - id: 'testi 2', - properties: { - prefLabel: [{ lang: 'en', value: 'testi 2' }], - }, - }, - { - id: 'testi 3', - properties: { - prefLabel: [{ lang: 'en', value: 'testi 3' }], - }, - }, - ], - inGroup: [{ properties: { prefLabel: [] } }], - }, - } as any - } - />, - reducers, - { preloadedState: { login: loginInitialState } } - ); + renderWithProviders(<InfoExpander data={data} />, reducers, { + preloadedState: { login: loginInitialState }, + }); await waitFor(() => { expect(screen.getByText('testi 1')).toBeInTheDocument(); diff --git a/terminology-ui/src/common/components/term-modal/term-modal.test.tsx b/terminology-ui/src/common/components/term-modal/term-modal.test.tsx index 85f7754fc..96b808e48 100644 --- a/terminology-ui/src/common/components/term-modal/term-modal.test.tsx +++ b/terminology-ui/src/common/components/term-modal/term-modal.test.tsx @@ -1,8 +1,8 @@ import { fireEvent, screen } from '@testing-library/react'; import TermModal from '.'; import { renderWithProviders } from '@app/tests/test-utils'; -import { Term } from '@app/common/interfaces/term.interface'; import { initialState } from '@app/common/components/login/login.slice'; +import { Term } from '@app/common/interfaces/interfaces-v2'; describe('term-modal', () => { let appRoot: HTMLDivElement | null = null; @@ -35,7 +35,6 @@ describe('term-modal', () => { expect(screen.getByText('change note')).toBeInTheDocument(); expect(screen.getByText('history note')).toBeInTheDocument(); expect(screen.getByText('editorial note')).toBeInTheDocument(); - expect(screen.getByText('draft comment')).toBeInTheDocument(); fireEvent.click(screen.getByText('tr-term-modal-grammatic-information')); @@ -46,11 +45,10 @@ describe('term-modal', () => { }); it('should render parts of component', () => { - delete data.term.properties.changeNote; - delete data.term.properties.historyNote; - delete data.term.properties.editorialNote; - delete data.term.properties.draftComment; - delete data.term.properties.wordClass; + delete data.term.changeNote; + delete data.term.historyNote; + delete data.term.editorialNotes; + delete data.term.wordClass; renderWithProviders(<TermModal data={data} />, [], { preloadedState: { login: { ...initialState, anonymous: false } }, @@ -86,118 +84,21 @@ describe('term-modal', () => { const data = { term: { - properties: { - changeNote: [ - { - lang: 'fi', - value: 'change note', - regex: '', - }, - ], - draftComment: [ - { - lang: 'fi', - value: 'draft comment', - regex: '', - }, - ], - editorialNote: [ - { - lang: 'fi', - value: 'editorial note', - regex: '', - }, - ], - historyNote: [ - { - lang: 'fi', - value: 'history note', - regex: '', - }, - ], - prefLabel: [ - { - lang: 'fi', - value: 'pref label', - regex: '', - }, - ], - scope: [ - { - lang: 'fi', - value: 'scope', - regex: '', - }, - ], - source: [ - { - lang: 'fi', - value: 'source 1', - regex: '', - }, - { - lang: 'fi', - value: 'source 2', - regex: '', - }, - ], - status: [ - { - lang: '', - value: 'DRAFT', - regex: '', - }, - ], - termConjugation: [ - { - lang: 'fi', - value: 'term conjugation', - regex: '', - }, - ], - termEquivalency: [ - { - lang: 'fi', - value: 'term equivalency', - regex: '', - }, - ], - termFamily: [ - { - lang: 'fi', - value: 'term family', - regex: '', - }, - ], - termHomographNumber: [ - { - lang: 'fi', - value: '1', - regex: '', - }, - ], - termInfo: [ - { - lang: 'fi', - value: 'term info', - regex: '', - }, - ], - termStyle: [ - { - lang: 'fi', - value: 'term style', - regex: '', - }, - ], - wordClass: [ - { - lang: 'fi', - value: 'word class', - regex: '', - }, - ], - }, + language: 'fi', + changeNote: 'change note', + editorialNotes: ['editorial note'], + historyNote: 'history note', + label: 'pref label', + scope: 'scope', + sources: ['source 1', 'source 2'], + status: 'DRAFT', + termConjugation: 'term conjugation', + termEquivalency: 'term equivalency', + termFamily: 'term family', + homographNumber: 1, + termInfo: 'term info', + termStyle: 'term style', + wordClass: 'word class', } as Term, type: 'Preferred term', }; diff --git a/terminology-ui/src/common/interfaces/interfaces-v2.ts b/terminology-ui/src/common/interfaces/interfaces-v2.ts index c40a0c610..aa0eca0b1 100644 --- a/terminology-ui/src/common/interfaces/interfaces-v2.ts +++ b/terminology-ui/src/common/interfaces/interfaces-v2.ts @@ -29,11 +29,11 @@ export interface TerminologyInfo { modified: string; creator: UserMeta; modifier: UserMeta; - origin: string; + origin?: string; } export interface Concept { - identifier: string; + identifier?: string; definition: LocalizedValue; notes: LocalizedListItem[]; examples: LocalizedListItem[]; @@ -68,7 +68,7 @@ export interface Concept { export interface ConceptInfo { identifier: string; uri: string; - label: LocalizedValue; + label?: LocalizedValue; created: string; modified: string; creator: UserMeta; @@ -108,7 +108,7 @@ export interface ConceptInfo { export interface Term { language: string; label?: string; - homographNumber: number; + homographNumber?: number; status?: Status; termInfo?: string; scope?: string; @@ -216,7 +216,7 @@ export interface LocalizedListItem { export interface UserMeta { id: string; - name: string; + name?: string; } export enum TerminologyType { diff --git a/terminology-ui/src/common/utils/get-store-data.test.tsx b/terminology-ui/src/common/utils/get-store-data.test.tsx index 8c269777d..37b1a0121 100644 --- a/terminology-ui/src/common/utils/get-store-data.test.tsx +++ b/terminology-ui/src/common/utils/get-store-data.test.tsx @@ -4,7 +4,7 @@ describe('page-head-utils', () => { it('should return correct object', () => { const gotten = getStoreData({ state: { - vocabularyAPI: { + terminologyAPI: { queries: { getTerminology: { data: { @@ -24,7 +24,7 @@ describe('page-head-utils', () => { it('should return empty object with incorrect redux key', () => { const gotten = getStoreData({ state: { - vocabularyAPI: { + terminologyAPI: { queries: { getTerminology: { data: { @@ -44,7 +44,7 @@ describe('page-head-utils', () => { it('should return empty object with incorrect function', () => { const gotten = getStoreData({ state: { - vocabularyAPI: { + terminologyAPI: { queries: { getTerminology: { data: { diff --git a/terminology-ui/src/modules/concept/utils.test.tsx b/terminology-ui/src/modules/concept/utils.test.tsx index ea37ca8da..cb090bf8f 100644 --- a/terminology-ui/src/modules/concept/utils.test.tsx +++ b/terminology-ui/src/modules/concept/utils.test.tsx @@ -1,3 +1,4 @@ +import { ConceptInfo } from '@app/common/interfaces/interfaces-v2'; import { getBlockData } from './utils'; describe('order concept data', () => { @@ -8,10 +9,8 @@ describe('order concept data', () => { jest.fn((x) => x), concept ); - const termLabels = data.terms.map( - (t) => t.term.properties.prefLabel?.[0].value - ); - const defintions = data.definitions.map((def) => def.value); + const termLabels = data.terms.map((t) => t.term.label); + const defintions = Object.values(data.definitions); const notes = data.notes.map((note) => note.value); const expectedTermOrder = [ @@ -42,139 +41,88 @@ describe('order concept data', () => { }); const concept = { - id: '123', - properties: { - definition: [ - { - lang: 'sv', - value: 'swedish definition', - }, - { - lang: 'fi', - value: 'finnish definition', - }, - ], - note: [ - { - lang: 'fi', - value: 'first note', - }, - { - lang: 'fi', - value: 'newer note', - }, - { - lang: 'fi', - value: 'newest note', - }, - ], + identifier: 'concept-1', + uri: '', + created: '', + modified: '', + creator: { + id: '', + name: '', + }, + modifier: { + id: '', + name: '', }, - referrers: {}, - references: { - altLabelXl: [ - { - properties: { - prefLabel: [ - { - lang: 'fi', - value: 'zzz finnish synonym', - regex: '(?s)^.*$', - }, - ], - }, - }, - { - properties: { - prefLabel: [ - { - lang: 'fi', - value: 'aaa finnish synonym', - regex: '(?s)^.*$', - }, - ], - }, - }, - { - properties: { - prefLabel: [ - { - lang: 'sv', - value: 'swedish synonym', - regex: '(?s)^.*$', - }, - ], - }, - }, - ], - prefLabelXl: [ - { - properties: { - prefLabel: [ - { - lang: 'fi', - value: 'finnish pref term', - regex: '(?s)^.*$', - }, - ], - }, - }, - { - properties: { - prefLabel: [ - { - lang: 'de', - value: 'german pref term', - regex: '(?s)^.*$', - }, - ], - }, - }, - { - properties: { - prefLabel: [ - { - lang: 'sv', - value: 'swedish pref term', - regex: '(?s)^.*$', - }, - ], - }, - }, - ], - notRecommendedSynonym: [ - { - properties: { - prefLabel: [ - { - lang: 'fi', - value: 'bbb finnish not recommended', - regex: '(?s)^.*$', - }, - ], - }, - }, - { - properties: { - prefLabel: [ - { - lang: 'fi', - value: 'zzz finnish not recommended', - regex: '(?s)^.*$', - }, - ], - }, - }, - { - properties: { - prefLabel: [ - { - lang: 'fi', - value: 'aaa finnish not recommended', - regex: '(?s)^.*$', - }, - ], - }, - }, - ], + definition: { + fi: 'finnish definition', + sv: 'swedish definition', }, -}; + notes: [ + { language: 'fi', value: 'first note' }, + { language: 'fi', value: 'newer note' }, + { language: 'fi', value: 'newest note' }, + ], + examples: [], + subjectArea: '', + status: 'DRAFT', + sources: [], + links: [], + changeNote: '', + historyNote: '', + conceptClass: '', + editorialNotes: [], + recommendedTerms: [ + { + language: 'fi', + label: 'finnish pref term', + }, + { + language: 'de', + label: 'german pref term', + }, + { + language: 'sv', + label: 'swedish pref term', + }, + ], + synonyms: [ + { + language: 'fi', + label: 'zzz finnish synonym', + }, + { + language: 'fi', + label: 'aaa finnish synonym', + }, + { + language: 'sv', + label: 'swedish synonym', + }, + ], + notRecommendedTerms: [ + { + language: 'fi', + label: 'bbb finnish not recommended', + }, + { + language: 'fi', + label: 'zzz finnish not recommended', + }, + { + language: 'fi', + label: 'aaa finnish not recommended', + }, + ], + searchTerms: [], + broader: [], + narrower: [], + isPartOf: [], + hasPart: [], + related: [], + broadMatch: [], + narrowMatch: [], + exactMatch: [], + closeMatch: [], + relatedMatch: [], + memberOf: [], +} as ConceptInfo; diff --git a/terminology-ui/src/modules/edit-collection/generate-collection/generate-collection.test.tsx b/terminology-ui/src/modules/edit-collection/generate-collection/generate-collection.test.tsx index 2dcb2891e..d3b2f9864 100644 --- a/terminology-ui/src/modules/edit-collection/generate-collection/generate-collection.test.tsx +++ b/terminology-ui/src/modules/edit-collection/generate-collection/generate-collection.test.tsx @@ -28,6 +28,17 @@ describe('generate-collection', () => { const returned = generateCollection(data, false); - expect(returned).toStrictEqual([]); + expect(returned).toStrictEqual({ + description: { + en: 'description', + fi: 'kuvaus', + }, + identifier: 'collection-1', + label: { + en: 'new collection', + fi: 'uusi käsitekokoelma', + }, + members: ['concept-1', 'concept-2'], + }); }); }); diff --git a/terminology-ui/src/modules/edit-concept/concept-terms-block/new-term-modal.tsx b/terminology-ui/src/modules/edit-concept/concept-terms-block/new-term-modal.tsx index 1dd2867e7..ae1394708 100644 --- a/terminology-ui/src/modules/edit-concept/concept-terms-block/new-term-modal.tsx +++ b/terminology-ui/src/modules/edit-concept/concept-terms-block/new-term-modal.tsx @@ -72,7 +72,6 @@ export default function NewTermModal({ status: 'DRAFT', termConjugation: '', termEquivalency: '', - termEquivalencyRelation: '', termFamily: '', termHomographNumber: '', termInfo: '', diff --git a/terminology-ui/src/modules/edit-concept/concept-terms-block/term-form.test.tsx b/terminology-ui/src/modules/edit-concept/concept-terms-block/term-form.test.tsx index c98337396..c24b4e0c5 100644 --- a/terminology-ui/src/modules/edit-concept/concept-terms-block/term-form.test.tsx +++ b/terminology-ui/src/modules/edit-concept/concept-terms-block/term-form.test.tsx @@ -13,7 +13,6 @@ describe('term-form', () => { update={mockFn} term={{ changeNote: '', - draftComment: '', editorialNote: [], historyNote: '', id: v4(), @@ -24,7 +23,6 @@ describe('term-form', () => { status: 'DRAFT', termConjugation: '', termEquivalency: '', - termEquivalencyRelation: '', termFamily: '', termHomographNumber: '', termInfo: '', @@ -49,7 +47,6 @@ describe('term-form', () => { update={mockFn} term={{ changeNote: '', - draftComment: '', editorialNote: [], historyNote: '', id: v4(), @@ -60,7 +57,6 @@ describe('term-form', () => { status: 'DRAFT', termConjugation: '', termEquivalency: '', - termEquivalencyRelation: '', termFamily: '', termHomographNumber: '', termInfo: '', diff --git a/terminology-ui/src/modules/edit-concept/generate-concept-test-expected.tsx b/terminology-ui/src/modules/edit-concept/generate-concept-test-expected.tsx index b0f210316..4daede842 100644 --- a/terminology-ui/src/modules/edit-concept/generate-concept-test-expected.tsx +++ b/terminology-ui/src/modules/edit-concept/generate-concept-test-expected.tsx @@ -241,6 +241,7 @@ export const conceptWithOneTerm = { ], }; +/* export const conceptWithOneTermWithInitialData = { delete: [], save: [ @@ -1771,4 +1772,4 @@ export const removeTerm = { uri: 'http://uri.suomi.fi/terminology/212e3cf8/concept-1', }, ], -}; +};*/ diff --git a/terminology-ui/src/modules/edit-concept/generate-concept.test.tsx b/terminology-ui/src/modules/edit-concept/generate-concept.test.tsx index 4865414a1..ba91287ed 100644 --- a/terminology-ui/src/modules/edit-concept/generate-concept.test.tsx +++ b/terminology-ui/src/modules/edit-concept/generate-concept.test.tsx @@ -1,924 +1,215 @@ -import generateConcept from './generate-concept'; -import { - conceptWithInternalRelations, - conceptWithOneTerm, - conceptWithOneTermWithInitialData, - differentTerms, - removeTerm, -} from './generate-concept-test-expected'; - -describe('generate-concept', () => { - it('should generate concept with one term', () => { - const input = { - terms: [ +import { Concept } from '@app/common/interfaces/interfaces-v2'; +import generateConceptPayload from './generate-concept'; + +function getReference(id: string) { + return { + id: `https://iri.suomi.fi/terminology/test/${id}`, + label: { fi: 'test reference' }, + terminologyId: 'test', + terminologyLabel: { fi: 'terminology label' }, + }; +} + +const input = { + terms: [ + { + changeNote: 'term change', + editorialNote: [ { - changeNote: '', - draftComment: '', - editorialNote: [], - historyNote: '', - id: '0', - language: 'fi', - prefLabel: 'prefLabel', - scope: '', - source: [], - status: 'draft', - termConjugation: '', - termEquivalency: '', - termEquivalencyRelation: '', - termFamily: '', - termHomographNumber: '', - termInfo: '', - termStyle: '', - termType: 'recommended-term', - wordClass: '', + id: '', + value: 'editorial note', }, ], - basicInformation: { - definition: {}, - example: [], - status: 'DRAFT', - subject: '', - note: [], - diagramAndSource: { - diagrams: [], - sources: [], - }, - orgInfo: { - changeHistory: '', - editorialNote: [], - etymology: '', - }, - otherInfo: { - conceptClass: '', - wordClass: '', - }, - relationalInfo: { - broaderConcept: [], - narrowerConcept: [], - relatedConcept: [], - isPartOfConcept: [], - hasPartConcept: [], - relatedConceptInOther: [], - matchInOther: [], - closeMatch: [], - broadInOther: [], - narrowInOther: [], - }, - }, - }; - - const returned = generateConcept({ - data: input, - terminologyId: 'terminologyId', - }); - - const save = returned.save.map((json) => { - // Replacing generated time stamps with expected values - json.createdDate = '1970-01-01T00:00:00.000Z'; - json.lastModifiedDate = '1970-01-01T00:00:00.000Z'; - return json; - }); - - // Replacing the last object's id with expected value - // since it's randombly generated in the function - save[1].id = '1'; - - expect(returned).toStrictEqual(conceptWithOneTerm); - }); - - it('should generate concept with one term with initial data provided', () => { - const input = { - terms: [ + historyNote: 'term history', + id: '0', + language: 'fi', + prefLabel: 'pref label', + scope: 'scope', + source: [ { - changeNote: '', - draftComment: '', - editorialNote: [], - historyNote: '', - id: '789', - language: 'fi', - prefLabel: 'prefLabel', - scope: '', - source: [], - status: 'draft', - termConjugation: '', - termEquivalency: '', - termEquivalencyRelation: '', - termFamily: '', - termHomographNumber: '', - termInfo: '', - termStyle: '', - termType: 'recommended-term', - wordClass: '', + id: '', + value: 'term source', }, ], - basicInformation: { - definition: {}, - example: [], - status: 'DRAFT', - subject: '', - note: [], - diagramAndSource: { - diagrams: [], - sources: [], - }, - orgInfo: { - changeHistory: '', - editorialNote: [], - etymology: '', - }, - otherInfo: { - conceptClass: '', - wordClass: '', - }, - relationalInfo: { - broaderConcept: [], - narrowerConcept: [], - relatedConcept: [], - isPartOfConcept: [], - hasPartConcept: [], - relatedConceptInOther: [], - matchInOther: [], - closeMatch: [], - broadInOther: [], - narrowInOther: [], - }, + status: 'DRAFT', + termConjugation: 'SINGULAR', + termEquivalency: 'BROADER', + termFamily: 'NEUTRAL', + termHomographNumber: '2', + termInfo: 'info', + termStyle: 'term style', + termType: 'recommended-term', + wordClass: 'ADJECTIVE', + }, + { + changeNote: '', + editorialNote: [], + historyNote: '', + id: '0', + language: 'fi', + prefLabel: 'synonym', + scope: '', + source: [], + status: 'DRAFT', + termConjugation: '', + termEquivalency: '', + termFamily: '', + termHomographNumber: '', + termInfo: '', + termStyle: '', + termType: 'synonym', + wordClass: '', + }, + ], + basicInformation: { + identifier: 'concept-1', + definition: { + fi: 'definition', + }, + example: [ + { + id: '', + lang: 'fi', + value: 'example', }, - }; - - const returned = generateConcept({ - data: input, - terminologyId: 'terminologyId', - initialValue: { - code: 'concept-1000', - createdBy: 'Admin User', - createdDate: '1970-01-01T00:00:00.000Z', - id: '123', - identifier: { - id: '123', - type: { - graph: { - id: '456', - }, - id: 'Concept', - uri: '', - }, - }, - lastModifiedBy: 'Admin User', - lastModifiedDate: '1970-01-01T00:00:00.000Z', - number: 0, - properties: { - status: [{ lang: '', value: 'DRAFT', regex: '(?s)^.*$' }], - }, - references: { - prefLabelXl: [ - { - code: 'term-1000', - createdBy: 'Admin User', - createdDate: '1970-01-01T00:00:00.000Z', - id: '789', - identifier: { - id: '789', - type: { - graph: { - id: '456', - }, - id: 'Term', - uri: '', - }, - }, - lastModifiedBy: 'Admin User', - lastModifiedDate: '1970-01-01T00:00:00.000Z', - number: 0, - properties: { - prefLabel: [ - { - lang: 'fi', - value: 'termi', - regex: '(?s)^.*$', - }, - ], - status: [{ lang: '', value: 'DRAFT', regex: '(?s)^.*$' }], - }, - references: {}, - referrers: { - prefLabelXl: [ - { - code: 'concept-1000', - createdBy: 'Admin User', - createdDate: '1970-01-01T00:00:00.000Z', - id: '123', - identifier: { - id: '123', - type: { - graph: { - id: '456', - }, - id: 'Concept', - uri: '', - }, - }, - number: 0, - properties: { - status: [{ lang: '', value: 'DRAFT', regex: '(?s)^.*$' }], - }, - references: {}, - referreres: {}, - type: { - graph: { - id: '456', - }, - id: 'Concept', - uri: '', - }, - uri: 'sanastot.suomi.fi/sanasto/concept-1000', - }, - ], - }, - type: { - graph: { - id: '456', - }, - id: 'Term', - uri: '', - }, - uri: 'sanastot.suomi.fi/sanasto/term-1000', - }, - ], - }, - referrers: {}, - type: { - graph: { - id: '456', - }, - id: 'Concept', - uri: '', - }, - uri: 'sanastot.suomi.fi/sanasto/concept-1000', - }, - lastModifiedBy: 'Admin User', - }); - const save = returned.save.map((json) => { - // Replacing generated time stamps with expected values - json.createdDate = '1970-01-01T00:00:00.000Z'; - json.lastModifiedDate = '1970-01-01T00:00:00.000Z'; - return json; - }); - - // Replacing the last object's id with expected value - // since it's randombly generated in the function - save[1].id = '1'; - - expect(returned).toStrictEqual(conceptWithOneTermWithInitialData); - }); - - it('should generate concept with one term that has relations to same vocabulary concepts', () => { - const input = { - terms: [ + ], + status: 'DRAFT', + subject: 'subject', + note: [ + { id: '', lang: 'fi', value: 'note 1' }, + { id: '', lang: 'fi', value: 'note 2' }, + ], + diagramAndSource: { + diagrams: [], + sources: [ { - changeNote: '', - draftComment: '', - editorialNote: [], - historyNote: '', - id: '0', - language: 'fi', - prefLabel: 'prefLabel', - scope: '', - source: [], - status: 'draft', - termConjugation: '', - termEquivalency: '', - termEquivalencyRelation: '', - termFamily: '', - termHomographNumber: '', - termInfo: '', - termStyle: '', - termType: 'recommended-term', - wordClass: '', + id: '', + lang: 'fi', + value: 'concept source', }, ], - basicInformation: { - definition: {}, - example: [], - status: 'DRAFT', - subject: '', - note: [], - diagramAndSource: { - diagrams: [], - sources: [], - }, - orgInfo: { - changeHistory: '', - editorialNote: [], - etymology: '', - }, - otherInfo: { - conceptClass: '', - wordClass: '', - }, - relationalInfo: { - broaderConcept: [ - { - id: '148ab016-36ee-486a-862a-87d6dde2f86f', - label: { - fi: 'other concept', - }, - terminologyId: '747340b9-8ab6-4aa4-b4e6-5327813505e5', - terminologyLabel: { - fi: 'demo terminology', - }, - }, - ], - narrowerConcept: [ - { - id: '148ab016-36ee-486a-862a-87d6dde2f86f', - label: { - fi: 'other concept', - }, - terminologyId: '747340b9-8ab6-4aa4-b4e6-5327813505e5', - terminologyLabel: { - fi: 'demo terminology', - }, - }, - ], - relatedConcept: [ - { - id: '148ab016-36ee-486a-862a-87d6dde2f86f', - label: { - fi: 'other concept', - }, - terminologyId: '747340b9-8ab6-4aa4-b4e6-5327813505e5', - terminologyLabel: { - fi: 'demo terminology', - }, - }, - ], - isPartOfConcept: [ - { - id: '148ab016-36ee-486a-862a-87d6dde2f86f', - label: { - fi: 'other concept', - }, - terminologyId: '747340b9-8ab6-4aa4-b4e6-5327813505e5', - terminologyLabel: { - fi: 'demo terminology', - }, - }, - ], - hasPartConcept: [ - { - id: '148ab016-36ee-486a-862a-87d6dde2f86f', - label: { - fi: 'other concept', - }, - terminologyId: '747340b9-8ab6-4aa4-b4e6-5327813505e5', - terminologyLabel: { - fi: 'demo terminology', - }, - }, - ], - relatedConceptInOther: [], - matchInOther: [], - closeMatch: [], - broadInOther: [], - narrowInOther: [], - }, - }, - }; - - const returned = generateConcept({ - data: input, - terminologyId: 'terminologyId', - }); - const save = returned.save.map((json) => { - // Replacing generated ids and time stamps with known values - json.createdDate = '1970-01-01T00:00:00.000Z'; - json.lastModifiedDate = '1970-01-01T00:00:00.000Z'; - - return json; - }); - - const expected = conceptWithInternalRelations; - const lastObjectId = save[1].id; - - // Changing expected values last object's id to match - // what's returned. expected-value set id randomly - expected.save[1].id = lastObjectId; - - expect(returned).toStrictEqual(expected); - }); - - it('should generate concept with different term types filled with info', () => { - const input = { - terms: [ + }, + orgInfo: { + changeHistory: 'change concept', + editorialNote: [ { - changeNote: 'muutoshistoria', - draftComment: '', - editorialNote: [ - { - id: '5872b40f-7aa7-4b5a-9071-2c7b891d1ca2', - lang: '', - value: 'ylläpitäjän muistiinpano', - }, - ], - historyNote: 'käytön historiatieto', - id: '0', - language: 'fi', - prefLabel: 'demo', - scope: 'käyttöala', - source: [ - { - id: '6ef5a9b4-3843-4865-92ae-513d767f2636', - lang: '', - value: 'lähteet', - }, - ], - status: 'draft', - termConjugation: 'singular', - termEquivalency: '', - termEquivalencyRelation: '', - termFamily: 'feminine', - termHomographNumber: '1', - termInfo: 'termin lisätieto', - termStyle: 'spoken-form', - termType: 'recommended-term', - wordClass: 'adjective', - }, - { - changeNote: 'muutoshistoria', - draftComment: '', - editorialNote: [ - { - id: 'e313db56-8fc2-48e7-9edd-37dec25a568f', - lang: '', - value: 'ylläpitäjän muistiinpano', - }, - ], - historyNote: 'käytön historiatieto', - id: '1', - language: 'fi', - prefLabel: 'synonyymi', - scope: 'käyttöala', - source: [ - { - id: '6ef5a9b4-3843-4865-92ae-513d767f2636', - lang: '', - value: 'lähteet', - }, - ], - status: 'draft', - termConjugation: 'singular', - termEquivalency: '~', - termEquivalencyRelation: '', - termFamily: 'feminine', - termHomographNumber: '1', - termInfo: 'termin lisätieto', - termStyle: 'spoken-form', - termType: 'synonym', - wordClass: 'adjective', - }, - { - changeNote: 'muutoshistoria', - draftComment: '', - editorialNote: [ - { - id: '199cf1f5-88ee-409b-b369-4a54010112dd', - lang: '', - value: 'ylläpitäjän muistiinpano', - }, - ], - historyNote: 'käytön historiatieto', - id: '2', - language: 'fi', - prefLabel: 'ei-suositettava synonyymi', - scope: 'käyttöala', - source: [ - { - id: '6ef5a9b4-3843-4865-92ae-513d767f2636', - lang: '', - value: 'lähteet', - }, - ], - status: 'draft', - termConjugation: 'singular', - termEquivalency: '~', - termEquivalencyRelation: '', - termFamily: 'feminine', - termHomographNumber: '1', - termInfo: 'termin lisätieto', - termStyle: 'spoken-form', - termType: 'not-recommended-synonym', - wordClass: 'adjective', - }, - { - changeNote: 'muutoshistoria', - draftComment: '', - editorialNote: [ - { - id: '065d619c-ddd1-4b32-b92c-1063d84bf233', - lang: '', - value: 'ylläpitäjän muistiinpano', - }, - ], - historyNote: 'käytön historiatieto', - id: '3', - language: 'fi', - prefLabel: 'hakusana', - scope: 'käyttöala', - source: [ - { - id: '6ef5a9b4-3843-4865-92ae-513d767f2636', - lang: '', - value: 'lähteet', - }, - ], - status: 'draft', - termConjugation: 'singular', - termEquivalency: '~', - termEquivalencyRelation: '', - termFamily: 'feminine', - termHomographNumber: '1', - termInfo: 'termin lisätieto', - termStyle: 'spoken-form', - termType: 'search-term', - wordClass: 'adjective', + id: '', + lang: 'fi', + value: 'concept editorial note', }, ], - basicInformation: { - definition: { - fi: 'määritelmä', - }, - example: [ - { - id: '0', - lang: 'fi', - value: 'käyttöesimerkki', - }, - ], - status: 'VALID', - subject: 'aihealue', - note: [ - { - id: '0', - lang: 'fi', - value: 'huomautus', - }, - ], - diagramAndSource: { - diagrams: [ - { - id: '123', - name: 'käsitekaavion nimi', - url: 'käsitekaavion-verkko-osoite.fi', - description: 'kuvaus', - }, - ], - sources: [ - { - id: '0', - lang: '', - value: 'lähteet', - }, - ], - }, - orgInfo: { - changeHistory: 'muutoshistoria', - etymology: 'käytön historiatieto', - editorialNote: [ - { - id: '2967c823-b5b7-479b-9a91-61950bba511b', - lang: '', - value: 'ylläpitäjän muistiinpano', - }, - ], - }, - otherInfo: { - conceptClass: 'käsitteen luokka', - wordClass: 'adjective', - }, - relationalInfo: { - broaderConcept: [], - narrowerConcept: [], - relatedConcept: [], - isPartOfConcept: [], - hasPartConcept: [], - relatedConceptInOther: [], - matchInOther: [], - closeMatch: [], - broadInOther: [], - narrowInOther: [], - }, - }, - }; + etymology: 'etymology', + }, + otherInfo: { + conceptClass: 'class', + }, + relationalInfo: { + broaderConcept: [getReference('broader-1'), getReference('broader-2')], + narrowerConcept: [getReference('narrower-1')], + relatedConcept: [getReference('related-1')], + isPartOfConcept: [getReference('part-of-1')], + hasPartConcept: [getReference('has-part-1')], + relatedConceptInOther: [getReference('related-match-1')], + matchInOther: [getReference('exact-match-1')], + closeMatch: [getReference('close-match-1')], + broadInOther: [getReference('broad-match-1')], + narrowInOther: [getReference('narrow-match-1')], + }, + }, +}; + +const expected = { + identifier: 'concept-1', + definition: { + fi: 'definition', + }, + notes: [ + { language: 'fi', value: 'note 1' }, + { language: 'fi', value: 'note 2' }, + ], + examples: [{ language: 'fi', value: 'example' }], + subjectArea: 'subject', + sources: ['concept source'], + links: [], + changeNote: 'change concept', + historyNote: 'etymology', + conceptClass: 'class', + editorialNotes: ['concept editorial note'], + status: 'DRAFT', + recommendedTerms: [ + { + language: 'fi', + label: 'pref label', + homographNumber: 2, + status: 'DRAFT', + termInfo: 'info', + scope: 'scope', + historyNote: 'term history', + changeNote: 'term change', + termStyle: 'term style', + termFamily: 'NEUTRAL', + termConjugation: 'SINGULAR', + termEquivalency: 'BROADER', + wordClass: 'ADJECTIVE', + sources: ['term source'], + editorialNotes: ['editorial note'], + }, + ], + synonyms: [ + { + language: 'fi', + label: 'synonym', + status: 'DRAFT', + homographNumber: 0, + termInfo: '', + scope: '', + historyNote: '', + changeNote: '', + termStyle: '', + termFamily: undefined, + termConjugation: undefined, + termEquivalency: undefined, + wordClass: undefined, + sources: [], + editorialNotes: [], + }, + ], + notRecommendedTerms: [], + searchTerms: [], + broader: [ + 'https://iri.suomi.fi/terminology/test/broader-1', + 'https://iri.suomi.fi/terminology/test/broader-2', + ], + narrower: ['https://iri.suomi.fi/terminology/test/narrower-1'], + related: ['https://iri.suomi.fi/terminology/test/related-1'], + isPartOf: ['https://iri.suomi.fi/terminology/test/part-of-1'], + hasPart: ['https://iri.suomi.fi/terminology/test/has-part-1'], + exactMatch: ['https://iri.suomi.fi/terminology/test/exact-match-1'], + closeMatch: ['https://iri.suomi.fi/terminology/test/close-match-1'], + relatedMatch: ['https://iri.suomi.fi/terminology/test/related-match-1'], + narrowMatch: ['https://iri.suomi.fi/terminology/test/narrow-match-1'], + broadMatch: ['https://iri.suomi.fi/terminology/test/broad-match-1'], +} as Concept; - const returned = generateConcept({ +describe('generate-concept', () => { + it('should generate concept payload for creation', () => { + const returned = generateConceptPayload({ data: input, - terminologyId: 'terminologyId', - }); - const save = returned.save.map((json) => { - // Replacing generated ids and time stamps with known values - json.createdDate = '1970-01-01T00:00:00.000Z'; - json.lastModifiedDate = '1970-01-01T00:00:00.000Z'; - - return json; + isEdit: false, }); - const expected = differentTerms; - const lastObjectId = save[4].id; - - // Changing expected values last object's id to match - // what's returned. expected-value set id randomly - expected.save[4].id = lastObjectId; - expect(returned).toStrictEqual(expected); }); - it('should remove term properly', () => { - const input = { - terms: [ - { - changeNote: '', - draftComment: '', - editorialNote: [], - historyNote: '', - id: '0', - language: 'fi', - prefLabel: 'demo fi', - scope: '', - source: [], - status: 'draft', - termConjugation: '', - termEquivalency: '', - termEquivalencyRelation: '', - termFamily: '', - termHomographNumber: '', - termInfo: '', - termStyle: '', - termType: 'recommended-term', - wordClass: '', - }, - ], - basicInformation: { - definition: {}, - example: [], - status: 'DRAFT', - subject: '', - note: [], - diagramAndSource: { - diagrams: [], - sources: [], - }, - orgInfo: { - changeHistory: '', - editorialNote: [], - etymology: '', - }, - otherInfo: { - conceptClass: '', - wordClass: '', - }, - relationalInfo: { - broaderConcept: [], - narrowerConcept: [], - relatedConcept: [], - isPartOfConcept: [], - hasPartConcept: [], - relatedConceptInOther: [], - matchInOther: [], - closeMatch: [], - broadInOther: [], - narrowInOther: [], - }, - }, - }; - - const returned = generateConcept({ + it('should generate concept payload for edit', () => { + const returned = generateConceptPayload({ data: input, - terminologyId: 'terminologyId', - initialValue: { - id: 'abad6405-2843-42e3-bacd-9448e34fd049', - code: 'concept-1', - uri: 'http://uri.suomi.fi/terminology/212e3cf8/concept-1', - number: 0, - createdBy: '', - createdDate: '1970-01-01T00:00:00.000Z', - lastModifiedBy: '', - lastModifiedDate: '1970-01-01T00:00:00.000Z', - type: { - id: 'Concept', - graph: { - id: 'terminologyId', - }, - uri: '', - }, - properties: { - status: [ - { - lang: '', - value: 'DRAFT', - regex: '(?s)^.* $', - }, - ], - }, - references: { - prefLabelXl: [ - { - id: '0', - code: 'term-3', - uri: 'http://uri.suomi.fi/terminology/212e3cf8/term-3', - number: 0, - createdBy: '', - createdDate: '1970-01-01T00:00:00.000Z', - lastModifiedBy: '', - lastModifiedDate: '1970-01-01T00:00:00.000Z', - type: { - id: 'Term', - graph: { - id: 'terminologyId', - }, - uri: '', - }, - properties: { - prefLabel: [ - { - lang: 'fi', - value: 'demo fi', - regex: '(?s)^.* $', - }, - ], - status: [ - { - lang: '', - value: 'DRAFT', - regex: '(?s)^.* $', - }, - ], - }, - references: {}, - referrers: { - prefLabelXl: [ - { - id: 'abad6405-2843-42e3-bacd-9448e34fd049', - code: 'concept-1', - uri: 'http://uri.suomi.fi/terminology/212e3cf8/concept-1', - number: 0, - createdDate: '1970-01-01T00:00:00.000Z', - lastModifiedDate: '1970-01-01T00:00:00.000Z', - type: { - id: 'Concept', - graph: { - id: 'terminologyId', - }, - uri: '', - }, - properties: { - status: [ - { - lang: '', - value: 'DRAFT', - regex: '(?s)^.* $', - }, - ], - }, - references: {}, - referrers: {}, - identifier: { - id: 'abad6405-2843-42e3-bacd-9448e34fd049', - type: { - id: 'Concept', - graph: { - id: 'terminologyId', - }, - uri: '', - }, - }, - }, - ], - }, - identifier: { - id: 'd17ed9bb-1c1f-4b46-a63d-648130e7ba19', - type: { - id: 'Term', - graph: { - id: 'terminologyId', - }, - uri: '', - }, - }, - }, - { - id: 'ffd68efb-d5c8-4ea2-9fb8-fa7b0b688fe9', - code: 'term-4', - uri: 'http://uri.suomi.fi/terminology/212e3cf8/term-4', - number: 0, - createdBy: '', - createdDate: '2022-09-28T07:35:08.000+00:00', - lastModifiedBy: '', - lastModifiedDate: '2022-09-28T07:35:08.000+00:00', - type: { - id: 'Term', - graph: { - id: 'terminologyId', - }, - uri: '', - }, - properties: { - prefLabel: [ - { - lang: 'en', - value: 'demo en', - regex: '(?s)^.* $', - }, - ], - status: [ - { - lang: '', - value: 'DRAFT', - regex: '(?s)^.* $', - }, - ], - }, - references: {}, - referrers: { - prefLabelXl: [ - { - id: 'abad6405-2843-42e3-bacd-9448e34fd049', - code: 'concept-1', - uri: 'http://uri.suomi.fi/terminology/212e3cf8/concept-1', - number: 0, - createdDate: '2022-09-28T07:34:46.000+00: 00', - lastModifiedDate: '2022-09-28T07:35:08.000+00: 00', - type: { - id: 'Concept', - graph: { - id: 'terminologyId', - }, - uri: '', - }, - properties: { - status: [ - { - lang: '', - value: 'DRAFT', - regex: '(?s)^.* $', - }, - ], - }, - references: {}, - referrers: {}, - identifier: { - id: 'abad6405-2843-42e3-bacd-9448e34fd049', - type: { - id: 'Concept', - graph: { - id: 'terminologyId', - }, - uri: '', - }, - }, - }, - ], - }, - identifier: { - id: 'ffd68efb-d5c8-4ea2-9fb8-fa7b0b688fe9', - type: { - id: 'Term', - graph: { - id: 'terminologyId', - }, - uri: '', - }, - }, - }, - ], - }, - referrers: {}, - identifier: { - id: 'abad6405-2843-42e3-bacd-9448e34fd049', - type: { - id: 'Concept', - graph: { - id: 'terminologyId', - }, - uri: '', - }, - }, - }, + isEdit: true, }); - const save = returned.save.map((json) => { - // Replacing generated ids and time stamps with known values - json.createdDate = '1970-01-01T00:00:00.000Z'; - json.lastModifiedDate = '1970-01-01T00:00:00.000Z'; - return json; - }); - - const expected = removeTerm; - const lastObjectId = save[1].id; - - // Changing expected values last object's id to match - // what's returned. expected-value set id randomly - returned.save[1].id = lastObjectId; - - expect(returned).toStrictEqual(expected); + // edit payload should not have concept identifier set + const editPayload = Object.assign({}, expected); + delete editPayload.identifier; + expect(returned).toStrictEqual(editPayload); }); }); diff --git a/terminology-ui/src/modules/edit-concept/generate-form-data-test-variables.tsx b/terminology-ui/src/modules/edit-concept/generate-form-data-test-variables.tsx index 24aced479..a26fbfc5e 100644 --- a/terminology-ui/src/modules/edit-concept/generate-form-data-test-variables.tsx +++ b/terminology-ui/src/modules/edit-concept/generate-form-data-test-variables.tsx @@ -1,3 +1,4 @@ +import { terminologyApi } from '@app/common/components/vocabulary/vocabulary.slice'; import generateFormData from './generate-form-data'; export const emptyFormReturned = generateFormData({ fi: 'demo' }); @@ -6,7 +7,6 @@ export const emptyFormExpected = { terms: [ { changeNote: '', - draftComment: '', editorialNote: [], historyNote: '', id: emptyFormReturned.terms[0].id, @@ -27,6 +27,7 @@ export const emptyFormExpected = { }, ], basicInformation: { + identifier: '', definition: {}, example: [], status: 'DRAFT', @@ -61,11 +62,11 @@ export const emptyFormExpected = { // -------------------------------------------- -export const simpleDataReturned = generateFormData( - { fi: 'demo' }, +export const initialDataReturned = generateFormData( + {}, { - identifier: '', - uri: '', + identifier: 'concept-1', + uri: 'https://iri.suomi.fi/terminology/test/concept-1', created: '', modified: '', creator: { @@ -76,23 +77,53 @@ export const simpleDataReturned = generateFormData( id: '', name: '', }, - label: {}, - definition: {}, - notes: [], - examples: [], - subjectArea: '', + label: { + fi: 'label', + }, + definition: { + fi: 'def', + }, + notes: [{ language: 'fi', value: 'note' }], + examples: [{ language: 'fi', value: 'example' }], + subjectArea: 'subject', status: 'DRAFT', - sources: [], + sources: ['concept source'], links: [], - changeNote: '', - historyNote: '', - conceptClass: '', - editorialNotes: [], - recommendedTerms: [], + changeNote: 'change', + historyNote: 'history', + conceptClass: 'class', + editorialNotes: ['editorial note'], + recommendedTerms: [ + { + language: 'fi', + label: 'demo', + scope: 'scope', + sources: [], + status: 'DRAFT', + termConjugation: 'SINGULAR', + termEquivalency: 'BROADER', + termFamily: 'NEUTRAL', + homographNumber: 2, + termInfo: 'info', + termStyle: 'style', + wordClass: 'VERB', + changeNote: 'term change', + editorialNotes: ['term editorial'], + historyNote: 'term history', + }, + ], synonyms: [], notRecommendedTerms: [], searchTerms: [], - broader: [], + broader: [ + { + referenceURI: 'https://iri.suomi.fi/terminology/test/concept-100', + identifier: 'concept-100', + label: { fi: 'ref concept' }, + prefix: 'test', + terminologyLabel: { fi: 'terminology label' }, + }, + ], narrower: [], isPartOf: [], hasPart: [], @@ -106,385 +137,98 @@ export const simpleDataReturned = generateFormData( } ); -export const simpleDataExpected = { - terms: [ - { - changeNote: '', - draftComment: '', - editorialNote: [], - historyNote: '', - id: simpleDataReturned.terms[0].id, - language: 'fi', - prefLabel: 'demo', - scope: '', - source: [], - status: 'DRAFT', - termConjugation: '', - termEquivalency: '', - termEquivalencyRelation: '', - termFamily: '', - termHomographNumber: '', - termInfo: '', - termStyle: '', - termType: 'recommended-term', - wordClass: '', - }, - ], - basicInformation: { - definition: {}, - example: [], - status: 'DRAFT', - subject: '', - note: [], - diagramAndSource: { - diagrams: [], - sources: [], - }, - orgInfo: { - changeHistory: '', - editorialNote: [], - etymology: '', - }, - otherInfo: { - conceptClass: '', - }, - relationalInfo: { - broaderConcept: [], - narrowerConcept: [], - relatedConcept: [], - isPartOfConcept: [], - hasPartConcept: [], - relatedConceptInOther: [], - matchInOther: [], - closeMatch: [], - broadInOther: [], - narrowInOther: [], - }, - }, -}; - -// -------------------------------------------- - -export const extensiveDataReturned = generateFormData( - { fi: 'demo' }, - //TODO: - undefined -); - -export const extensiveDataExpected = { +export const initialDataExpected = { terms: [ { - changeNote: 'muutoshistoria', - draftComment: '', - editorialNote: [ - { - id: extensiveDataReturned.terms[0].editorialNote[0].id, - lang: '', - value: 'ylläpitäjän muistiinpano', - }, - ], - historyNote: 'käytön historiatieto', - id: 'ae47e0e8-c44d-4199-b7b4-3b8c98cda1de', - language: 'fi', - prefLabel: 'synonyymi', - scope: 'käyttöala', - source: [ - { - id: extensiveDataReturned.terms[0].source[0].id, - lang: '', - value: 'käyttöala', - }, - ], - status: 'DRAFT', - termConjugation: 'monikko', - termEquivalency: '~', - termEquivalencyRelation: '', - termFamily: 'feminiini', - termHomographNumber: '1', - termInfo: 'termin lisätieto', - termStyle: 'puhekieli', - termType: 'synonym', - wordClass: 'adjective', - }, - { - changeNote: 'muutoshistoria', - draftComment: '', - editorialNote: [ - { - id: extensiveDataReturned.terms[1].editorialNote[0].id, - lang: '', - value: 'ylläpitäjän muistiinpano', - }, - ], - historyNote: 'käytönhistoriatieto', - id: 'aca34887-297a-4482-9514-1ffe45161d3e', - language: 'fi', - prefLabel: 'Hakusana', - scope: 'käyttöala', - source: [ - { - id: extensiveDataReturned.terms[1].source[0].id, - lang: '', - value: 'käyttöala', - }, - ], - status: 'DRAFT', - termConjugation: 'monikko', - termEquivalency: '~', - termEquivalencyRelation: '', - termFamily: 'neutri', - termHomographNumber: '', - termInfo: 'termin lisätieto', - termStyle: 'puhekieli', - termType: 'search-term', - wordClass: 'adjective', - }, - { - changeNote: 'muutoshistoria', - draftComment: '', + changeNote: 'term change', editorialNote: [ { - id: extensiveDataReturned.terms[2].editorialNote[0].id, + id: initialDataReturned.terms[0].editorialNote[0].id, lang: '', - value: 'ylläpitäjän muistiinpano', + value: 'term editorial', }, ], - historyNote: 'käytön historiatieto', - id: '87713416-0a6e-4831-afa0-21fc05e3d6cd', + historyNote: 'term history', + id: initialDataReturned.terms[0].id, language: 'fi', prefLabel: 'demo', - scope: 'käyttöala', - source: [ - { - id: extensiveDataReturned.terms[2].source[0].id, - lang: '', - value: 'käyttöala', - }, - ], + scope: 'scope', + source: [], status: 'DRAFT', - termConjugation: 'singular', - termEquivalency: '', - termEquivalencyRelation: '', - termFamily: 'feminine', - termHomographNumber: '1', - termInfo: 'termin lisätieto', - termStyle: 'spoken-form', + termConjugation: 'SINGULAR', + termEquivalency: 'BROADER', + termFamily: 'NEUTRAL', + termHomographNumber: '2', + termInfo: 'info', + termStyle: 'style', termType: 'recommended-term', - wordClass: 'adjective', - }, - { - changeNote: 'muutoshistoria', - draftComment: '', - editorialNote: [ - { - id: extensiveDataReturned.terms[3].editorialNote[0].id, - lang: '', - value: 'ylläpitäjän muistiinpano', - }, - ], - historyNote: 'käytön historiatieto', - id: '3aa3c3ca-bc58-4e83-97be-ef748ca907bd', - language: 'fi', - prefLabel: 'Ei-suositettava synonyymi', - scope: 'käyttöala', - source: [ - { - id: extensiveDataReturned.terms[3].source[0].id, - lang: '', - value: 'käyttöala', - }, - ], - status: 'DRAFT', - termConjugation: 'yksikkö', - termEquivalency: '~', - termEquivalencyRelation: '', - termFamily: 'neutri', - termHomographNumber: '1', - termInfo: 'termin lisätieto', - termStyle: 'puhekieli', - termType: 'not-recommended-synonym', - wordClass: 'adjective', + wordClass: 'VERB', }, ], basicInformation: { - definition: { - fi: 'määritelmä', - en: 'definition', - }, - diagramAndSource: { - diagrams: [], - sources: [ - { - id: extensiveDataReturned.basicInformation.diagramAndSource.sources[0] - .id, - lang: '', - value: 'lähde', - }, - ], - }, + identifier: 'concept-1', + definition: { fi: 'def' }, example: [ { - id: extensiveDataReturned.basicInformation.example[0].id, + id: initialDataReturned.basicInformation.example[0].id, lang: 'fi', - value: 'esimerkki', + value: 'example', }, ], + status: 'DRAFT', + subject: 'subject', note: [ { - id: extensiveDataReturned.basicInformation.note[0].id, - lang: '', - value: 'huomautus', + id: initialDataReturned.basicInformation.note[0].id, + lang: 'fi', + value: 'note', }, ], + diagramAndSource: { + diagrams: [], + sources: [ + { + id: initialDataReturned.basicInformation.diagramAndSource.sources[0] + .id, + lang: '', + value: 'concept source', + }, + ], + }, orgInfo: { - changeHistory: 'muutoshistoria', + changeHistory: 'change', editorialNote: [ { - id: extensiveDataReturned.basicInformation.orgInfo.editorialNote[0] - .id, + id: initialDataReturned.basicInformation.orgInfo.editorialNote[0].id, lang: '', - value: 'ylläpitäjän muistiinpano', + value: 'editorial note', }, ], - etymology: 'historiatieto', + etymology: 'history', }, otherInfo: { - conceptClass: 'käsitteen luokka', - wordClass: 'adjective', + conceptClass: 'class', }, relationalInfo: { broaderConcept: [ { - id: '148ab016-36ee-486a-862a-87d6dde2f86f', - label: { - fi: 'emokäsite', - }, - terminologyId: '987987-987-987987', - terminologyLabel: { - fi: 'keskeneräinen sanasto', - }, - }, - ], - hasPartConcept: [ - { - id: '2a2b8b11-4dc9-4ddb-afde-812560a5b007', - label: { - fi: 'testi', - }, - terminologyId: '987987-987-987987', - terminologyLabel: { - fi: 'keskeneräinen sanasto', - }, - }, - ], - isPartOfConcept: [ - { - id: '2a2b8b11-4dc9-4ddb-afde-812560a5b007', - label: { - fi: 'testi', - }, - terminologyId: '987987-987-987987', - terminologyLabel: { - fi: 'keskeneräinen sanasto', - }, - }, - ], - matchInOther: [ - { - id: '8ee81e29-e0f1-4a23-b23b-28a976fcf87f', - label: { - fi: 'demo', - }, - terminologyId: 'ec43f161-b85d-4786-a4b9-d0da52edfba1', - terminologyLabel: { - fi: 'testi', - en: 'test', - }, - targetId: '7b179ea2-b28c-497e-9e81-6ff254235ea1', - }, - ], - narrowerConcept: [ - { - id: '6e00b816-c077-4747-8597-46047005584d', - label: { - fi: 'uusi käsite', - }, - terminologyId: '987987-987-987987', - terminologyLabel: { - fi: 'keskeneräinen sanasto', - }, - }, - ], - relatedConcept: [ - { - id: '2a2b8b11-4dc9-4ddb-afde-812560a5b007', - label: { - fi: 'testi', - }, - terminologyId: '987987-987-987987', - terminologyLabel: { - fi: 'keskeneräinen sanasto', - }, - }, - ], - relatedConceptInOther: [ - { - id: 'a87ae2c2-3c16-494a-9f82-928fc1840a1e', - label: { - fi: 'demo', - }, - terminologyId: 'ec43f161-b85d-4786-a4b9-d0da52edfba1', - terminologyLabel: { - fi: 'testi', - en: 'test', - }, - targetId: '7b179ea2-b28c-497e-9e81-6ff254235ea1', - }, - ], - closeMatch: [ - { - id: 'a87ae2c2-3c16-494a-9f82-928fc1840a1f', - label: { - fi: 'demo', - }, - terminologyId: 'ec43f161-b85d-4786-a4b9-d0da52edfba1', - terminologyLabel: { - fi: 'testi', - en: 'test', - }, - targetId: '7b179ea2-b28c-497e-9e81-6ff254235ea1', - }, - ], - broadInOther: [ - { - id: 'a87ae2c2-3c16-494a-9f82-928fc1840a1f', - label: { - fi: 'demo', - }, - terminologyId: 'ec43f161-b85d-4786-a4b9-d0da52edfba1', - terminologyLabel: { - fi: 'testi', - en: 'test', - }, - targetId: '7b179ea2-b28c-497e-9e81-6ff254235ea1', - }, - ], - narrowInOther: [ - { - id: 'a87ae2c2-3c16-494a-9f82-928fc1840a1f', + id: 'https://iri.suomi.fi/terminology/test/concept-100', label: { - fi: 'demo', + fi: 'ref concept', }, - terminologyId: 'ec43f161-b85d-4786-a4b9-d0da52edfba1', - terminologyLabel: { - fi: 'testi', - en: 'test', - }, - targetId: '7b179ea2-b28c-497e-9e81-6ff254235ea1', + terminologyId: 'test', + terminologyLabel: { fi: 'terminology label' }, }, ], + narrowerConcept: [], + relatedConcept: [], + isPartOfConcept: [], + hasPartConcept: [], + relatedConceptInOther: [], + matchInOther: [], + closeMatch: [], + broadInOther: [], + narrowInOther: [], }, - status: 'DRAFT', - subject: 'aihealue', }, }; diff --git a/terminology-ui/src/modules/edit-concept/generate-form-data.test.tsx b/terminology-ui/src/modules/edit-concept/generate-form-data.test.tsx index 1c7d4707e..b4e046270 100644 --- a/terminology-ui/src/modules/edit-concept/generate-form-data.test.tsx +++ b/terminology-ui/src/modules/edit-concept/generate-form-data.test.tsx @@ -1,10 +1,10 @@ import { emptyFormExpected, emptyFormReturned, - extensiveDataExpected, - extensiveDataReturned, - simpleDataExpected, - simpleDataReturned, + //extensiveDataExpected, + //extensiveDataReturned, + initialDataExpected, + initialDataReturned, } from './generate-form-data-test-variables'; describe('generate-form-data', () => { @@ -13,10 +13,6 @@ describe('generate-form-data', () => { }); it('should generate simple data with existing data', () => { - expect(simpleDataReturned).toStrictEqual(simpleDataExpected); - }); - - it('should generate extensive form with existing data', () => { - expect(extensiveDataReturned).toStrictEqual(extensiveDataExpected); + expect(initialDataReturned).toStrictEqual(initialDataExpected); }); }); diff --git a/terminology-ui/src/modules/edit-concept/new-concept.types.tsx b/terminology-ui/src/modules/edit-concept/new-concept.types.tsx index 6598fb0d9..a086a7fc3 100644 --- a/terminology-ui/src/modules/edit-concept/new-concept.types.tsx +++ b/terminology-ui/src/modules/edit-concept/new-concept.types.tsx @@ -12,7 +12,6 @@ export interface ConceptTermType { status: string; termConjugation: string; termEquivalency: string; - termEquivalencyRelation: string; termFamily: string; termHomographNumber: string; termInfo: string; diff --git a/terminology-ui/src/modules/edit-vocabulary/generate-initial-data.test.ts b/terminology-ui/src/modules/edit-vocabulary/generate-initial-data.test.ts index f13763368..b57231c0d 100644 --- a/terminology-ui/src/modules/edit-vocabulary/generate-initial-data.test.ts +++ b/terminology-ui/src/modules/edit-vocabulary/generate-initial-data.test.ts @@ -1,48 +1,56 @@ -import { VocabularyInfoDTO } from '@app/common/interfaces/vocabulary.interface'; import generateInitialData from './generate-initial-data'; +import { TerminologyType } from '@app/common/interfaces/interfaces-v2'; describe('generate-initial-data', () => { it('should generate initial data from input', () => { - const returned = generateInitialData('fi', dataSmall); - - const expected = { + const returned = generateInitialData('fi', { + uri: 'https://iri.suomi.fi/terminology/test/', + label: { + fi: 'testi fi', + en: 'test en', + }, + description: { + fi: 'kuvaus', + }, + prefix: 'abc1234', + graphType: TerminologyType.TERMINOLOGICAL_VOCABULARY, + status: 'DRAFT', + languages: ['fi', 'en'], contact: 'yhteentoimivuus@dvv.fi', - languages: [ + groups: [ { - description: 'kuvaus', - labelText: 'fi', - selected: true, - title: 'testi2', - uniqueItemId: 'fi', + id: 'http://group-id-1', + label: { + fi: 'Asuminen', + }, + identifier: 'P10', }, - ], - infoDomains: [ { - checked: false, - groupId: '654-789', - labelText: 'Asuminen', - name: 'Asuminen', - uniqueItemId: '654-456', + id: 'http://group-id-2', + label: { + fi: 'Demokratia', + }, + identifier: 'P11', }, ], - contributors: [ + organizations: [ { - labelText: 'Yhteentoimivuusalustan yllapito', - name: 'Yhteentoimivuusalustan yllapito', - organizationId: '456-456', - uniqueItemId: '456-123', + id: '456-456', + label: { + fi: 'Yhteentoimivuusalustan yllapito', + }, }, ], - prefix: ['abc1234', true], - status: 'DRAFT', - type: 'TERMINOLOGICAL_VOCABULARY', - }; - - expect(returned).toStrictEqual(expected); - }); + created: '', + modified: '', + creator: { + id: '', + }, + modifier: { + id: '', + }, + }); - it('should generate data from large input', () => { - const returned = generateInitialData('fi', dataLarge); const expected = { contact: 'yhteentoimivuus@dvv.fi', languages: [ @@ -50,569 +58,46 @@ describe('generate-initial-data', () => { description: 'kuvaus', labelText: 'fi', selected: true, - title: 'testi2', + title: 'testi fi', uniqueItemId: 'fi', }, { - description: 'description', + description: undefined, labelText: 'en', selected: true, - title: 'test2', + title: 'test en', uniqueItemId: 'en', }, - { - description: '', - labelText: 'sv', - selected: true, - title: 'test2', - uniqueItemId: 'sv', - }, ], - infoDomains: [ + groups: [ { checked: false, - groupId: '654-789', + groupId: 'P10', labelText: 'Asuminen', name: 'Asuminen', - uniqueItemId: '654-456', + uniqueItemId: 'P10', }, { checked: false, - groupId: '987-456', - labelText: 'Yksityinen talous ja rahoitus', - name: 'Yksityinen talous ja rahoitus', - uniqueItemId: '987-123', + groupId: 'P11', + labelText: 'Demokratia', + name: 'Demokratia', + uniqueItemId: 'P11', }, ], - contributors: [ + organizations: [ { labelText: 'Yhteentoimivuusalustan yllapito', name: 'Yhteentoimivuusalustan yllapito', organizationId: '456-456', - uniqueItemId: '456-123', + uniqueItemId: '456-456', }, ], prefix: ['abc1234', true], - status: 'VALID', - type: 'OTHER_VOCABULARY', + status: 'DRAFT', + type: 'TERMINOLOGICAL_VOCABULARY', }; expect(returned).toStrictEqual(expected); }); }); - -const dataSmall: VocabularyInfoDTO = { - id: '123', - code: 'terminological-vocabulary-0', - uri: 'uri.fi/terminology/abc1234/terminological-vocabulary-0', - number: 0, - createdBy: 'Admin User', - createdDate: '1970-01-01T00:00:00.000Z', - lastModifiedBy: 'Admin User', - lastModifiedDate: '1970-01-01T00:00:00.000Z', - type: { - id: 'TerminologicalVocabulary', - graph: { - id: '789', - }, - uri: '', - }, - properties: { - contact: [ - { - lang: '', - value: 'yhteentoimivuus@dvv.fi', - regex: '(?s)^.*$', - }, - ], - language: [ - { - lang: '', - value: 'fi', - regex: '(?s)^.*$', - }, - ], - prefLabel: [ - { - lang: 'fi', - value: 'testi2', - regex: '(?s)^.*$', - }, - ], - status: [ - { - lang: '', - value: 'DRAFT', - regex: '(?s)^.*$', - }, - ], - terminologyType: [ - { - lang: '', - value: 'TERMINOLOGICAL_VOCABULARY', - regex: '^(OTHER_VOCABULARY|TERMINOLOGICAL_VOCABULARY)$', - }, - ], - description: [ - { - lang: 'fi', - value: 'kuvaus', - regex: '(?s)^.*$', - }, - ], - }, - references: { - contributor: [ - { - id: '456-123', - code: '', - uri: '', - number: 0, - createdBy: '', - createdDate: '1970-01-01T00:00:00.000Z', - lastModifiedBy: '', - lastModifiedDate: '1970-01-01T00:00:00.000Z', - type: { - id: 'Organization', - graph: { - id: '456-456', - }, - uri: '', - }, - properties: { - prefLabel: [ - { - lang: 'en', - value: 'Interoperability platform developers', - regex: '(?s)^.*$', - }, - { - lang: 'fi', - value: 'Yhteentoimivuusalustan yllapito', - regex: '(?s)^.*$', - }, - { - lang: 'sv', - value: 'Utvecklare av interoperabilitetsplattform', - regex: '(?s)^.*$', - }, - ], - }, - references: {}, - referrers: {}, - identifier: { - id: '456-123', - type: { - id: 'Organization', - graph: { - id: '456-456', - }, - uri: '', - }, - }, - }, - ], - inGroup: [ - { - id: '654-456', - code: 'v1001', - uri: '', - number: 0, - createdBy: '', - createdDate: '1970-01-01T00:00:00.000Z', - lastModifiedBy: '', - lastModifiedDate: '1970-01-01T00:00:00.000Z', - type: { - id: 'Group', - graph: { - id: '654-789', - }, - uri: '', - }, - properties: { - notation: [ - { - lang: '', - value: 'P1', - regex: '(?s)^.*$', - }, - ], - definition: [ - { - lang: 'en', - value: '', - regex: '(?s)^.*$', - }, - { - lang: 'fi', - value: '', - regex: '(?s)^.*$', - }, - { - lang: 'sv', - value: '', - regex: '(?s)^.*$', - }, - ], - order: [ - { - lang: '', - value: '100', - regex: '(?s)^.*$', - }, - ], - prefLabel: [ - { - lang: 'en', - value: 'Housing', - regex: '(?s)^.*$', - }, - { - lang: 'sv', - value: 'Boende', - regex: '(?s)^.*$', - }, - { - lang: 'fi', - value: 'Asuminen', - regex: '(?s)^.*$', - }, - ], - }, - references: {}, - referrers: {}, - identifier: { - id: '654-456', - type: { - id: 'Group', - graph: { - id: '654-789', - }, - uri: '', - }, - }, - }, - ], - }, - referrers: {}, - identifier: { - id: '123', - type: { - id: 'TerminologicalVocabulary', - graph: { - id: '789', - }, - uri: '', - }, - }, -}; - -const dataLarge: VocabularyInfoDTO = { - id: '123', - code: 'terminological-vocabulary-0', - uri: 'uri.fi/terminology/abc1234/terminological-vocabulary-0', - number: 0, - createdBy: 'Admin User', - createdDate: '1970-01-01T00:00:00.000Z', - lastModifiedBy: 'Admin User', - lastModifiedDate: '1970-01-01T00:00:00.000Z', - type: { - id: 'TerminologicalVocabulary', - graph: { - id: '789', - }, - uri: '', - }, - properties: { - contact: [ - { - lang: '', - value: 'yhteentoimivuus@dvv.fi', - regex: '(?s)^.*$', - }, - ], - language: [ - { - lang: '', - value: 'fi', - regex: '(?s)^.*$', - }, - { - lang: '', - value: 'en', - regex: '(?s)^.*$', - }, - { - lang: '', - value: 'sv', - regex: '(?s)^.*$', - }, - ], - prefLabel: [ - { - lang: 'fi', - value: 'testi2', - regex: '(?s)^.*$', - }, - { - lang: 'en', - value: 'test2', - regex: '(?s)^.*$', - }, - { - lang: 'sv', - value: 'test2', - regex: '(?s)^.*$', - }, - ], - status: [ - { - lang: '', - value: 'VALID', - regex: '(?s)^.*$', - }, - ], - terminologyType: [ - { - lang: '', - value: 'OTHER_VOCABULARY', - regex: '^(OTHER_VOCABULARY|TERMINOLOGICAL_VOCABULARY)$', - }, - ], - description: [ - { - lang: 'fi', - value: 'kuvaus', - regex: '(?s)^.*$', - }, - { - lang: 'en', - value: 'description', - regex: '(?s)^.*$', - }, - ], - }, - references: { - contributor: [ - { - id: '456-123', - code: '', - uri: '', - number: 0, - createdBy: '', - createdDate: '1970-01-01T00:00:00.000Z', - lastModifiedBy: '', - lastModifiedDate: '1970-01-01T00:00:00.000Z', - type: { - id: 'Organization', - graph: { - id: '456-456', - }, - uri: '', - }, - properties: { - prefLabel: [ - { - lang: 'en', - value: 'Interoperability platform developers', - regex: '(?s)^.*$', - }, - { - lang: 'fi', - value: 'Yhteentoimivuusalustan yllapito', - regex: '(?s)^.*$', - }, - { - lang: 'sv', - value: 'Utvecklare av interoperabilitetsplattform', - regex: '(?s)^.*$', - }, - ], - }, - references: {}, - referrers: {}, - identifier: { - id: '456-123', - type: { - id: 'Organization', - graph: { - id: '456-456', - }, - uri: '', - }, - }, - }, - ], - inGroup: [ - { - id: '654-456', - code: 'v1001', - uri: '', - number: 0, - createdBy: '', - createdDate: '1970-01-01T00:00:00.000Z', - lastModifiedBy: '', - lastModifiedDate: '1970-01-01T00:00:00.000Z', - type: { - id: 'Group', - graph: { - id: '654-789', - }, - uri: '', - }, - properties: { - notation: [ - { - lang: '', - value: 'P1', - regex: '(?s)^.*$', - }, - ], - definition: [ - { - lang: 'en', - value: '', - regex: '(?s)^.*$', - }, - { - lang: 'fi', - value: '', - regex: '(?s)^.*$', - }, - { - lang: 'sv', - value: '', - regex: '(?s)^.*$', - }, - ], - order: [ - { - lang: '', - value: '100', - regex: '(?s)^.*$', - }, - ], - prefLabel: [ - { - lang: 'en', - value: 'Housing', - regex: '(?s)^.*$', - }, - { - lang: 'sv', - value: 'Boende', - regex: '(?s)^.*$', - }, - { - lang: 'fi', - value: 'Asuminen', - regex: '(?s)^.*$', - }, - ], - }, - references: {}, - referrers: {}, - identifier: { - id: '654-456', - type: { - id: 'Group', - graph: { - id: '654-789', - }, - uri: '', - }, - }, - }, - { - id: '987-123', - code: 'v1132', - uri: '', - number: 0, - createdBy: '', - createdDate: '1970-01-01T00:00:00.000Z', - lastModifiedBy: '', - lastModifiedDate: '1970-01-01T00:00:00.000Z', - type: { - id: 'Group', - graph: { - id: '987-456', - }, - uri: '', - }, - properties: { - notation: [ - { - lang: '', - value: 'P15', - regex: '(?s)^.*$', - }, - ], - definition: [ - { - lang: 'fi', - value: '', - regex: '(?s)^.*$', - }, - { - lang: 'en', - value: '', - regex: '(?s)^.*$', - }, - { - lang: 'sv', - value: '', - regex: '(?s)^.*$', - }, - ], - order: [ - { - lang: '', - value: '1500', - regex: '(?s)^.*$', - }, - ], - prefLabel: [ - { - lang: 'en', - value: 'Private finance and funding', - regex: '(?s)^.*$', - }, - { - lang: 'sv', - value: 'Privat ekonomi och finansiering', - regex: '(?s)^.*$', - }, - { - lang: 'fi', - value: 'Yksityinen talous ja rahoitus', - regex: '(?s)^.*$', - }, - ], - }, - references: {}, - referrers: {}, - identifier: { - id: '987-123', - type: { - id: 'Group', - graph: { - id: '987-456', - }, - uri: '', - }, - }, - }, - ], - }, - referrers: {}, - identifier: { - id: '123', - type: { - id: 'TerminologicalVocabulary', - graph: { - id: '789', - }, - uri: '', - }, - }, -}; diff --git a/terminology-ui/src/modules/edit-vocabulary/generate-initial-data.ts b/terminology-ui/src/modules/edit-vocabulary/generate-initial-data.ts index 102f77c33..bc798827d 100644 --- a/terminology-ui/src/modules/edit-vocabulary/generate-initial-data.ts +++ b/terminology-ui/src/modules/edit-vocabulary/generate-initial-data.ts @@ -61,20 +61,11 @@ export default function generateInitialData( }; }) ?? []; - const uriParts = data.uri.split('/'); - const prefix = uriParts - .map((part, idx) => { - if (uriParts[idx - 1] === 'terminology') { - return part; - } - }) - .filter((p) => p)[0]; - const obj: TerminologyForm = { contact: data.contact ?? '', languages: languages, groups: groups, - prefix: [prefix ?? '', true], + prefix: [data.prefix ?? '', true], status: data.status ?? 'DRAFT', type: data.graphType ?? TerminologyType.TERMINOLOGICAL_VOCABULARY, organizations: contributors, diff --git a/terminology-ui/src/modules/new-terminology/file-upload.test.tsx b/terminology-ui/src/modules/new-terminology/file-upload.test.tsx index f9303d01f..18359914e 100644 --- a/terminology-ui/src/modules/new-terminology/file-upload.test.tsx +++ b/terminology-ui/src/modules/new-terminology/file-upload.test.tsx @@ -19,7 +19,9 @@ describe('file-upload', () => { /> ); - expect(screen.getByText(/tr-percent-done/)).toBeInTheDocument(); + expect( + screen.getByText(/tr-import-concepts-in-progress/) + ).toBeInTheDocument(); }); it('should render try again', () => { diff --git a/terminology-ui/src/store/api-base-query.test.ts b/terminology-ui/src/store/api-base-query.test.ts index 455504fd1..879287455 100644 --- a/terminology-ui/src/store/api-base-query.test.ts +++ b/terminology-ui/src/store/api-base-query.test.ts @@ -33,7 +33,7 @@ describe('axios base query', () => { }; // any API call would be fine here - mock.onGet(/\/vocabulary\?graphId=\d+/).reply((config) => { + mock.onGet(/\/terminology\/\d+/).reply((config) => { return [ 200, 'JSESSIONID exists in headers: ' + @@ -58,7 +58,7 @@ describe('axios base query', () => { await Promise.all(store.dispatch(getRunningQueriesThunk())); // get the result from the API call - const data = store.getState().vocabularyAPI.queries[ + const data = store.getState().terminologyApi.queries[ 'getTerminology({"id":"42"})' ]?.data as string; diff --git a/terminology-ui/src/tests/terminology/[terminologyId].test.tsx b/terminology-ui/src/tests/terminology/[terminologyId].test.tsx index 3279316c5..855782a5b 100644 --- a/terminology-ui/src/tests/terminology/[terminologyId].test.tsx +++ b/terminology-ui/src/tests/terminology/[terminologyId].test.tsx @@ -12,13 +12,13 @@ describe('terminologyId page', () => { mock = new MockAdapter(axios, { onNoMatch: 'throwException' }); mock - .onGet(/\/v1\/frontend\/vocabulary\?graphId=\d+/) + .onGet(/\/v2\/terminology\/\d+/) .reply((config) => [200, 'response from vocabulary']); mock - .onGet(/.*\/v1\/frontend\/collections\?graphId=\d+/) + .onGet(/.*\/v2\/collections\?graphId=\d+/) .reply((config) => [200, 'response from collections']); mock - .onPost(/.*\/v1\/frontend\/searchConcept$/, { + .onGet(/.*\/v2\/frontend\/search-concept$/, { // eslint-disable-next-line @typescript-eslint/no-explicit-any asymmetricMatch: (actual: any) => { // these checks ensure that the API request was made with the @@ -49,12 +49,13 @@ describe('terminologyId page', () => { expect((results as any).props).toBeDefined(); // eslint-disable-line @typescript-eslint/no-explicit-any - expect(mock.history.get).toHaveLength(5); + expect(mock.history.get).toHaveLength(6); const polledUrls = [ - '/vocabulary?graphId=1234', - '/concept-counts?graphId=1234', - '/status-counts?graphId=1234', + '/terminology/1234', + '/frontend/search-concepts', + '/concept-counts?prefix=1234', + '/status-counts?prefix=1234', '/user', '/fakeable-users', ]; @@ -63,20 +64,21 @@ describe('terminologyId page', () => { mock.history.get.some((x) => x.url?.endsWith(url)) ); - expect(foundUrls).toHaveLength(5); + expect(foundUrls).toHaveLength(6); - expect(mock.history.post).toHaveLength(1); - expect(mock.history.post[0]?.data).toStrictEqual( - JSON.stringify({ - highlight: true, - pageFrom: 0, - pageSize: 50, - query: 'test', - sortDirection: 'ASC', - sortLanguage: 'en', - status: ['DRAFT'], - terminologyId: ['1234'], - }) - ); + const searchParams = mock.history.get + .filter((h) => h.url?.endsWith('/frontend/search-concepts')) + .map((get) => get.params)[0]; + + expect(searchParams).toStrictEqual({ + highlight: true, + pageFrom: 0, + pageSize: 50, + query: 'test', + sortDirection: 'ASC', + sortLanguage: 'en', + status: ['DRAFT'], + namespace: 'https://iri.suomi.fi/terminology/1234/', + }); }); }); From d1839e752a277cd6e613d150f9e973a2f535ebb0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kalle=20Kytt=C3=A4l=C3=A4?= <kalle.kyttala@rebase.fi> Date: Fri, 6 Sep 2024 12:58:44 +0300 Subject: [PATCH 16/35] remove unused code --- common-ui/utils/get-language-version.ts | 4 +- .../src/common/components/block/index.ts | 9 +- .../block/multilingual-property-block.tsx | 38 ---- .../components/block/property-block.test.tsx | 16 -- .../components/block/property-block.tsx | 42 ----- .../copy-terminology-modal.tsx | 4 +- .../common/components/modify/modify.slice.tsx | 56 ------ .../property-value/get-property-value.ts | 51 ------ .../components/property-value/index.tsx | 57 ------ .../property-value/property-value.md | 163 ------------------ .../property-value/property-value.test.tsx | 99 ----------- .../render-chosen.test.tsx | 34 +++- .../render-chosen.tsx | 18 +- .../render-concepts.tsx | 24 +-- .../removal-modal/generate-removal-data.tsx | 128 -------------- .../vocabulary/vocabulary.slice.tsx | 10 +- .../common/interfaces/collection.interface.ts | 62 ------- .../interfaces/concept-link.interface.ts | 10 -- .../common/interfaces/concept.interface.ts | 117 ------------- .../src/common/interfaces/group.interface.ts | 10 -- .../common/interfaces/new-terminology-info.ts | 21 --- .../src/common/interfaces/new-terminology.ts | 55 ------ .../interfaces/organization.interface.ts | 7 - .../src/common/interfaces/term.interface.ts | 22 --- .../interfaces/termed-data-types.interface.ts | 47 ----- .../interfaces/terminology.interface.ts | 83 --------- .../common/interfaces/vocabulary.interface.ts | 83 --------- .../src/common/utils/compare-locals.ts | 34 ---- .../utils/filter-collection-data.test.ts | 125 -------------- .../common/utils/filter-collection-data.ts | 43 ----- .../common/utils/get-diagram-values.test.ts | 40 ----- .../src/common/utils/get-diagram-values.ts | 17 -- .../src/common/utils/get-preflabel.ts | 27 --- .../src/common/utils/get-property.test.ts | 50 ------ .../src/common/utils/get-property.ts | 24 --- .../src/common/utils/to-uri.test.ts | 22 --- terminology-ui/src/common/utils/to-uri.ts | 13 -- .../concept-picker/concept-picker.types.tsx | 6 - .../concept-basic-information-types.tsx | 50 ------ .../relational-information.tsx | 3 +- .../basic-information/status-picker.tsx | 2 +- .../generate-form-data-test-variables.tsx | 1 - terminology-ui/src/pages/sitemap.xml.tsx | 40 +++-- .../src/pages/terminology/[terminologyId].tsx | 14 +- .../[terminologyId]/concept/[conceptId].tsx | 11 +- terminology-ui/src/store/index.tsx | 3 - 46 files changed, 91 insertions(+), 1704 deletions(-) delete mode 100644 terminology-ui/src/common/components/block/multilingual-property-block.tsx delete mode 100644 terminology-ui/src/common/components/block/property-block.test.tsx delete mode 100644 terminology-ui/src/common/components/block/property-block.tsx delete mode 100644 terminology-ui/src/common/components/modify/modify.slice.tsx delete mode 100644 terminology-ui/src/common/components/property-value/get-property-value.ts delete mode 100644 terminology-ui/src/common/components/property-value/index.tsx delete mode 100644 terminology-ui/src/common/components/property-value/property-value.md delete mode 100644 terminology-ui/src/common/components/property-value/property-value.test.tsx delete mode 100644 terminology-ui/src/common/components/removal-modal/generate-removal-data.tsx delete mode 100644 terminology-ui/src/common/interfaces/collection.interface.ts delete mode 100644 terminology-ui/src/common/interfaces/concept-link.interface.ts delete mode 100644 terminology-ui/src/common/interfaces/concept.interface.ts delete mode 100644 terminology-ui/src/common/interfaces/group.interface.ts delete mode 100644 terminology-ui/src/common/interfaces/new-terminology-info.ts delete mode 100644 terminology-ui/src/common/interfaces/new-terminology.ts delete mode 100644 terminology-ui/src/common/interfaces/organization.interface.ts delete mode 100644 terminology-ui/src/common/interfaces/term.interface.ts delete mode 100644 terminology-ui/src/common/interfaces/termed-data-types.interface.ts delete mode 100644 terminology-ui/src/common/interfaces/terminology.interface.ts delete mode 100644 terminology-ui/src/common/interfaces/vocabulary.interface.ts delete mode 100644 terminology-ui/src/common/utils/filter-collection-data.test.ts delete mode 100644 terminology-ui/src/common/utils/filter-collection-data.ts delete mode 100644 terminology-ui/src/common/utils/get-diagram-values.test.ts delete mode 100644 terminology-ui/src/common/utils/get-diagram-values.ts delete mode 100644 terminology-ui/src/common/utils/get-preflabel.ts delete mode 100644 terminology-ui/src/common/utils/get-property.test.ts delete mode 100644 terminology-ui/src/common/utils/get-property.ts delete mode 100644 terminology-ui/src/common/utils/to-uri.test.ts delete mode 100644 terminology-ui/src/common/utils/to-uri.ts delete mode 100644 terminology-ui/src/modules/edit-concept/basic-information/concept-basic-information-types.tsx diff --git a/common-ui/utils/get-language-version.ts b/common-ui/utils/get-language-version.ts index 8498e1107..0cc0dde33 100644 --- a/common-ui/utils/get-language-version.ts +++ b/common-ui/utils/get-language-version.ts @@ -1,12 +1,12 @@ interface getLanguageVersionProps { data?: { [key: string]: string }; - lang: string; + lang?: string; appendLocale?: boolean; } export function getLanguageVersion({ data, - lang, + lang = 'fi', appendLocale = false, }: getLanguageVersionProps) { if (!data) { diff --git a/terminology-ui/src/common/components/block/index.ts b/terminology-ui/src/common/components/block/index.ts index a2065d45c..f99d4291b 100644 --- a/terminology-ui/src/common/components/block/index.ts +++ b/terminology-ui/src/common/components/block/index.ts @@ -1,11 +1,4 @@ import MultilingualBlock from './multilingual-block'; -import MultilingualPropertyBlock from './multilingual-property-block'; -import PropertyBlock from './property-block'; import TermBlock from './term-block'; -export { - MultilingualBlock, - MultilingualPropertyBlock, - PropertyBlock, - TermBlock, -}; +export { MultilingualBlock, TermBlock }; diff --git a/terminology-ui/src/common/components/block/multilingual-property-block.tsx b/terminology-ui/src/common/components/block/multilingual-property-block.tsx deleted file mode 100644 index 797b1823f..000000000 --- a/terminology-ui/src/common/components/block/multilingual-property-block.tsx +++ /dev/null @@ -1,38 +0,0 @@ -import { Property } from '@app/common/interfaces/termed-data-types.interface'; -import MultilingualBlock, { - MultilingualBlockItemMapper, -} from './multilingual-block'; - -export interface MultilingualPropertyBlockProps { - title: React.ReactNode; - data?: Property[]; - mapper?: MultilingualBlockItemMapper<Property>; - extra?: React.ReactNode; - id?: string; -} - -const defaultMapper: MultilingualBlockItemMapper<Property> = ({ - lang, - value, -}) => ({ - language: lang, - content: value, -}); - -export default function MultilingualPropertyBlock({ - title, - data, - mapper = defaultMapper, - extra, - id, -}: MultilingualPropertyBlockProps) { - return ( - <MultilingualBlock<Property> - data={data} - title={title} - mapper={mapper} - extra={extra} - id={id} - /> - ); -} diff --git a/terminology-ui/src/common/components/block/property-block.test.tsx b/terminology-ui/src/common/components/block/property-block.test.tsx deleted file mode 100644 index e5d45cddb..000000000 --- a/terminology-ui/src/common/components/block/property-block.test.tsx +++ /dev/null @@ -1,16 +0,0 @@ -import { render, screen } from '@testing-library/react'; -import { PropertyBlock } from '.'; - -describe('propertyBlock', () => { - it('should render property', () => { - render( - <PropertyBlock - title="Title" - property={[{ lang: '', value: 'Property value', regex: '' }]} - /> - ); - - expect(screen.getByText(/Title/)).toBeInTheDocument(); - expect(screen.getByText(/Property value/)).toBeInTheDocument(); - }); -}); diff --git a/terminology-ui/src/common/components/block/property-block.tsx b/terminology-ui/src/common/components/block/property-block.tsx deleted file mode 100644 index d8a06d3e8..000000000 --- a/terminology-ui/src/common/components/block/property-block.tsx +++ /dev/null @@ -1,42 +0,0 @@ -import { useTranslation } from 'next-i18next'; -import React from 'react'; -import { BasicBlock } from 'yti-common-ui/block'; -import { Property } from '@app/common/interfaces/termed-data-types.interface'; -import { getPropertyValue } from '@app/common/components/property-value/get-property-value'; - -export interface PropertyBlockProps { - title?: React.ReactNode; - property?: Property[]; - valueAccessor?: (property: Property) => string; - delimiter?: string | false; - extra?: React.ReactNode; - id?: string; -} - -export default function PropertyBlock({ - title, - property, - valueAccessor, - delimiter = false, - extra, - id, -}: PropertyBlockProps) { - const { i18n } = useTranslation('common'); - - const children = getPropertyValue({ - property, - valueAccessor, - language: i18n.language, - delimiter, - }); - - if (!children) { - return null; - } - - return ( - <BasicBlock title={title} extra={extra} id={id}> - {children} - </BasicBlock> - ); -} diff --git a/terminology-ui/src/common/components/copy-terminology-modal/copy-terminology-modal.tsx b/terminology-ui/src/common/components/copy-terminology-modal/copy-terminology-modal.tsx index a0ce1e8e6..fffe027d0 100644 --- a/terminology-ui/src/common/components/copy-terminology-modal/copy-terminology-modal.tsx +++ b/terminology-ui/src/common/components/copy-terminology-modal/copy-terminology-modal.tsx @@ -10,7 +10,7 @@ import { Text, } from 'suomifi-ui-components'; import { useBreakpoints } from 'yti-common-ui/media-query'; -import { usePostCreateVersionMutation } from '../vocabulary/vocabulary.slice'; +import { useCreateVersionMutation } from '../vocabulary/vocabulary.slice'; import { v4 } from 'uuid'; import { useStoreDispatch } from '@app/store'; import { terminologySearchApi } from '../terminology-search/terminology-search.slice'; @@ -44,7 +44,7 @@ export default function CopyTerminologyModal({ const [userPosted, setUserPosted] = useState(false); const [newGraphId, setNewGraphId] = useState(''); const [error, setError] = useState(false); - const [postCreateVersion, createVersion] = usePostCreateVersionMutation(); + const [postCreateVersion, createVersion] = useCreateVersionMutation(); const [newCode, setNewCode] = useState(randomURL); const router = useRouter(); diff --git a/terminology-ui/src/common/components/modify/modify.slice.tsx b/terminology-ui/src/common/components/modify/modify.slice.tsx deleted file mode 100644 index b7138235c..000000000 --- a/terminology-ui/src/common/components/modify/modify.slice.tsx +++ /dev/null @@ -1,56 +0,0 @@ -import { createApi } from '@reduxjs/toolkit/query/react'; -import { getTerminologyApiBaseQuery } from '@app/store/api-base-query'; -import generateConcept from '@app/modules/edit-concept/generate-concept'; -import { EditCollectionFormDataType } from '@app/modules/edit-collection/edit-collection.types'; -import { NewTerminology } from '@app/common/interfaces/new-terminology'; - -export const modifyApi = createApi({ - reducerPath: 'modifyAPI', - baseQuery: getTerminologyApiBaseQuery(), - tagTypes: ['Modify'], - endpoints: (builder) => ({ - addConcept: builder.mutation< - ReturnType<typeof generateConcept>, - null | undefined | {} - >({ - query: (data) => ({ - url: '/modify', - method: 'POST', - data: data, - }), - }), - addCollection: builder.mutation< - EditCollectionFormDataType, - null | undefined | {} - >({ - query: (data) => ({ - url: '/modify', - method: 'POST', - data: { - delete: [], - save: data, - }, - }), - }), - editTerminology: builder.mutation<NewTerminology, null | undefined | {}>({ - query: (data) => ({ - url: '/modify', - method: 'POST', - data: { - delete: [], - save: [data], - }, - }), - }), - }), -}); - -export const { - useAddCollectionMutation, - useAddConceptMutation, - useEditTerminologyMutation, - util: { getRunningOperationPromises }, -} = modifyApi; - -export const { addCollection, addConcept, editTerminology } = - modifyApi.endpoints; diff --git a/terminology-ui/src/common/components/property-value/get-property-value.ts b/terminology-ui/src/common/components/property-value/get-property-value.ts deleted file mode 100644 index c2e1918ca..000000000 --- a/terminology-ui/src/common/components/property-value/get-property-value.ts +++ /dev/null @@ -1,51 +0,0 @@ -import { Property } from '@app/common/interfaces/termed-data-types.interface'; - -export interface GetPropertyValueParams { - property?: Property[]; - valueAccessor?: (property: Property) => string; - language?: string; - delimiter?: string | false; - fallback?: string; - stripHtml?: boolean; -} - -export function getPropertyValue({ - property, - valueAccessor = ({ value }) => value, - language = '', - delimiter = false, - fallback, - stripHtml = false, -}: GetPropertyValueParams): string { - const matchingProperties = - getMatchingProperties(property ?? [], language) ?? - getMatchingProperties(property ?? [], 'fi') ?? - getMatchingProperties(property ?? [], 'en') ?? - getMatchingProperties(property ?? [], 'sv') ?? - getMatchingProperties(property ?? [], '') ?? - []; - - let result; - if (delimiter !== false) { - result = matchingProperties.map(valueAccessor).join(delimiter); - } else { - result = matchingProperties[0] ? valueAccessor(matchingProperties[0]) : ''; - } - - if (stripHtml) { - result = result.replace(/(<([^>]+)>)/gi, ''); - } - - return result ?? fallback; -} - -function getMatchingProperties(properties: Property[], language: string) { - if (!language && properties.length) { - return properties; - } - const matchingProperties = properties.filter(({ lang }) => lang === language); - - if (matchingProperties.length) { - return matchingProperties; - } -} diff --git a/terminology-ui/src/common/components/property-value/index.tsx b/terminology-ui/src/common/components/property-value/index.tsx deleted file mode 100644 index b731f9a8c..000000000 --- a/terminology-ui/src/common/components/property-value/index.tsx +++ /dev/null @@ -1,57 +0,0 @@ -import React from 'react'; -import { useTranslation } from 'next-i18next'; -import { Property } from '@app/common/interfaces/termed-data-types.interface'; -import { getPropertyValue } from './get-property-value'; - -export interface PropertyValueProps { - property?: Property[]; - valueAccessor?: (property: Property) => string; - language?: string; - delimiter?: string | false; - fallback?: string; - stripHtml?: boolean; -} - -/** - * If property is localized, return value that matches to current locale or null if not found. - * If property is not localized, return first value that doesn't have a locale or null if not found. - * - * <PropertyValue property={[{ lang: '', value: 'DRAFT' }]} /> - * - * This renders always as 'DRAFT'. - * - * <PropertyValue property={[{ lang: 'en', value: 'This is a test.' }]} /> - * - * If current language is English, this renders as 'This is a test.'. - * If current locale is not English, this renders as null. - * - * <PropertyValue property={[{ lang: 'en', value: 'This is a test.' }]} fallbackLocale="en" /> - * - * If current language is English, this renders as 'This is a test.'. - * If current locale is not English, fallback language is used, and this renders as 'This is a test'. - * - * <PropertyValue property={[{ lang: '', value: 'Value 1' }, { lang: '', value: 'Value 2' }]} delimiter=", " /> - * - * This renders always as 'Value 1, Value 2'. - */ -export default function PropertyValue({ - property, - valueAccessor, - language, - delimiter = false, - fallback, - stripHtml = false, -}: PropertyValueProps) { - const { i18n } = useTranslation('common'); - - const value = getPropertyValue({ - property, - valueAccessor, - language: language ?? i18n.language, - delimiter, - fallback, - stripHtml, - }); - - return <>{value}</>; -} diff --git a/terminology-ui/src/common/components/property-value/property-value.md b/terminology-ui/src/common/components/property-value/property-value.md deleted file mode 100644 index 19e7b45a5..000000000 --- a/terminology-ui/src/common/components/property-value/property-value.md +++ /dev/null @@ -1,163 +0,0 @@ -# Property value - -You will find `Property[]` as a type everywhere. This is because of how the data -is stored in the back end. - -E.g. objects can have a property called `prefLabel`. It will look like this: - -```ts -interface Obj { - properties: { - prefLabel: Property[]; - }; -} - -const obj: Obj = { - properties: { - prefLabel: [ - { - lang: 'en', - value: "Object's name", - regex: '', - }, - { - lang: 'fi', - value: 'Objektin nimi', - regex: '', - }, - ], - }, -}; -``` - -## The procedure of resolving matching values - -Property is actually a list of values. The main purpose of the `PropertyValue` -is to automate the process of picking the correct value. The most common use -case is that an object has a name in multiple languages and you want to render -it in the same language as the UI. So, if the UI is in English, you want to -render the object's name in English. Or if the UI is in Finnish, you want to -render the object's name in Finnish. - -But there are also many caveats. What if the object's name is not defined in -that language? What if there is no name at all? The `PropertyValue` handles -these issues automatically for you. You just need to say that "render this -property here". - -So how matching values are resolved? Here is the procedure: - -``` -1. Try to take values in the following order. If the rule doesn't match any values, try the next rule. - 1.1. Take values whose language matches the specifically given language. - 1.2. Take values whose language matches the current language. - 1.3. Take values whose language is Finnish. - 1.4. Take values whose language is English. - 1.5. Take values whose language is Swedish. - 1.6. If none of the above rules don't match any values, just take all values regardless of their language. -2. Extract the values from properties. -3. Join them, strip HTML markup, and so on. -4. Return the result. -``` - -## PropertyValue component - -Always prefer `PropertyValue` component over `getPropertyValue` function where -possible. It is a wrapper component that uses the latter under the hood and thus -is a bit easier to use. - -### Usage - -Using `PropertyValue` component is simple. Just give it the property as a prop. -It chooses the correct text and renders it. - -```tsx -// This will render either "Object's name" or "Objektin nimi" depending on -// the current language. -<PropertyValue property={obj.properties.prefLabel} /> -``` - -In addition, you can pass other props that manipulate the result. - -### Fallback - -If the property is an empty array or undefined, it doesn't render any value. But -if you give `fallback` prop, it will be rendered instead. - -```tsx -// This will render "No name". -<PropertyValue property={[]} fallback="No name" /> -``` - -### Delimiter - -If there can be multiple values in the same language, you can join them all by -defining a delimiter. Otherwise the first will be rendered. - -```tsx -// This will render "Value 1, Value 2". -<PropertyValue - property={[ - { lang: '', value: 'Value 1', regex: '' }, - { lang: '', value: 'Value 2', regex: '' }, - ]} - delimiter=", " -/> -``` - -### StripHtml - -If there can be HTML markup in the value, you can strip them by adding stipHtml -flag. - -```tsx -// This will render "This text contains HTML markup.". -<PropertyValue property={[{ lang: '', value: 'This text contains <b>HTML</b> markup.', regex: '' }]} stripHtml /> -``` - -### Language - -Normally you don't need to give a language prop as its default value is -the current language of the UI. But if you explicitly need another language you -can override this behavior. - -```tsx -// This will render "Objektin nimi" regardless of the current language. -<PropertyValue property={obj.properties.prefLabel} language="fi" /> -``` - -### ValueAccessor - -Normally you don't need to give valueAccessor. It is a function that takes -property and returns its value. You will need this if you want to render -something else than the value of the property. - -```tsx -// This will render "sv". -<PropertyValue property={[{ lang: 'sv', value: '', regex: '' }]} valueAccessor={(property) => property.lang} /> -``` - -## GetPropertyValue function - -The `getPropertyValue` function takes almost the same arguments as -the `PropertyValue` component as its props. Language is the only difference. - -### Usage - -Using `getPropertyValue` function is simple. Just give it the property as -an argument. It returns the first value regardless of the language. - -```tsx -// This will return "Object's name" regardless of the current language. -getPropertyValue({ property: obj.properties.prefLabel }); -``` - -### Language - -Unlike the `PropertyValue` component the `getPropertyValue` function can't -access the current language. That's why you have to specify the language if you -want to get the result in some specific language. - -```tsx -// This will return "Objektin nimi". -getPropertyValue({ property: obj.properties.prefLabel, language: 'fi' }); -``` diff --git a/terminology-ui/src/common/components/property-value/property-value.test.tsx b/terminology-ui/src/common/components/property-value/property-value.test.tsx deleted file mode 100644 index ab3d49839..000000000 --- a/terminology-ui/src/common/components/property-value/property-value.test.tsx +++ /dev/null @@ -1,99 +0,0 @@ -import React from 'react'; -import { render, screen } from '@testing-library/react'; -import PropertyValue from '.'; -import { useTranslation } from 'next-i18next'; - -jest.mock('next-i18next'); -// eslint-disable-next-line @typescript-eslint/no-explicit-any -const mockedUseTranslation = useTranslation as jest.MockedFunction<any>; - -describe('propertyValue', () => { - it('should render unlocalized value even if primary language is set', () => { - mockedUseTranslation.mockReturnValue({ i18n: { language: 'en' } }); - - render( - <PropertyValue - property={[{ lang: '', value: 'Value (no-lang)', regex: '' }]} - /> - ); - - expect(screen.getByText('Value (no-lang)')).toBeInTheDocument(); - }); - - it('should render localized value in primary language when found', () => { - mockedUseTranslation.mockReturnValue({ i18n: { language: 'en' } }); - - render( - <PropertyValue - property={[ - { lang: 'en', value: 'Value (en)', regex: '' }, - { lang: 'fi', value: 'Value (fi)', regex: '' }, - ]} - /> - ); - - expect(screen.getByText('Value (en)')).toBeInTheDocument(); - expect(screen.queryByText('Value (fi)')).not.toBeInTheDocument(); - }); - - it('should render localized value in fallback language order (fi, en, sv) when found', () => { - mockedUseTranslation.mockReturnValue({ i18n: { language: 'en' } }); - - render( - <PropertyValue - property={[ - { lang: 'fi', value: 'Value (fi)', regex: '' }, - { lang: 'sv', value: 'Value (sv)', regex: '' }, - ]} - /> - ); - - expect(screen.getByText('Value (fi)')).toBeInTheDocument(); - expect(screen.queryByText('Value (sv)')).not.toBeInTheDocument(); - }); - - it('should join found values when delimiter is given', () => { - mockedUseTranslation.mockReturnValue({ i18n: { language: 'en' } }); - - render( - <PropertyValue - property={[ - { lang: 'en', value: 'Value 1', regex: '' }, - { lang: 'en', value: 'Value 2', regex: '' }, - ]} - delimiter=", " - /> - ); - - expect(screen.getByText('Value 1, Value 2')).toBeInTheDocument(); - }); - - it('should join found values when delimiter is empty string', () => { - mockedUseTranslation.mockReturnValue({ i18n: { language: 'en' } }); - - render( - <PropertyValue - property={[ - { lang: 'en', value: 'Value 1', regex: '' }, - { lang: 'en', value: 'Value 2', regex: '' }, - ]} - delimiter="" - /> - ); - - expect(screen.getByText('Value 1Value 2')).toBeInTheDocument(); - }); - - it('should ignore html elements when stripHtml is enabled', () => { - mockedUseTranslation.mockReturnValue({ i18n: { language: 'en' } }); - - render( - <PropertyValue - property={[{ lang: 'en', value: 'This <b>is</b> a test', regex: '' }]} - stripHtml - /> - ); - - expect(screen.getByText('This is a test')).toBeInTheDocument(); - }); -}); diff --git a/terminology-ui/src/common/components/relational-information-block/render-chosen.test.tsx b/terminology-ui/src/common/components/relational-information-block/render-chosen.test.tsx index e1b6eaaec..c14771ce5 100644 --- a/terminology-ui/src/common/components/relational-information-block/render-chosen.test.tsx +++ b/terminology-ui/src/common/components/relational-information-block/render-chosen.test.tsx @@ -1,6 +1,7 @@ import { renderWithProviders } from '@app/tests/test-utils'; import { fireEvent, screen } from '@testing-library/react'; import RenderChosen from './render-chosen'; +import { ConceptResponseObject } from '@app/common/interfaces/interfaces-v2'; describe('render-chosen', () => { it('should render component', () => { @@ -60,7 +61,37 @@ describe('render-chosen', () => { expect(setShowChosenMock).toHaveBeenCalledTimes(1); }); }); - +const chosen = [ + { + uri: '', + id: '1', + label: { fi: 'chosen1' }, + status: 'DRAFT', + created: '', + modified: '', + terminology: { + prefix: '', + label: {}, + }, + definition: {}, + identifier: '', + }, + { + uri: '', + id: '2', + label: { fi: 'chosen2' }, + status: 'DRAFT', + created: '', + modified: '', + terminology: { + prefix: '', + label: {}, + }, + definition: {}, + identifier: '', + }, +] as ConceptResponseObject[]; +/* const chosen = [ { altLabel: {}, @@ -97,3 +128,4 @@ const chosen = [ uri: '', }, ]; +*/ diff --git a/terminology-ui/src/common/components/relational-information-block/render-chosen.tsx b/terminology-ui/src/common/components/relational-information-block/render-chosen.tsx index 747d85847..04b87a412 100644 --- a/terminology-ui/src/common/components/relational-information-block/render-chosen.tsx +++ b/terminology-ui/src/common/components/relational-information-block/render-chosen.tsx @@ -1,9 +1,9 @@ import { RelationInfoType } from '@app/modules/edit-concept/new-concept.types'; -import { useTranslation } from 'next-i18next'; +import { i18n } from 'next-i18next'; import { Chip, Label } from 'suomifi-ui-components'; -import PropertyValue from '../property-value'; import { ChipBlock } from './relation-information-block.styles'; import { ConceptResponseObject } from '@app/common/interfaces/interfaces-v2'; +import { getLanguageVersion } from 'yti-common-ui/utils/get-language-version'; interface RenderChosenProps { chosen: ConceptResponseObject[] | RelationInfoType[]; @@ -18,7 +18,6 @@ export default function RenderChosen({ setShowChosen, chipLabel, }: RenderChosenProps) { - const { t } = useTranslation('admin'); const handleChipRemove = ( chose: ConceptResponseObject | RelationInfoType ) => { @@ -44,15 +43,10 @@ export default function RenderChosen({ onClick={() => handleChipRemove(chose)} key={chose.id} > - <PropertyValue - property={Object.keys(chose.label).map((lang) => ({ - lang, - value: chose.label[lang], - regex: '', - }))} - stripHtml - fallback={t('concept-label-undefined', { ns: 'common' })} - /> + {getLanguageVersion({ + data: chose.label, + lang: i18n?.language, + })} </Chip> ); })} diff --git a/terminology-ui/src/common/components/relational-information-block/render-concepts.tsx b/terminology-ui/src/common/components/relational-information-block/render-concepts.tsx index 1ab961a2c..aecdbc471 100644 --- a/terminology-ui/src/common/components/relational-information-block/render-concepts.tsx +++ b/terminology-ui/src/common/components/relational-information-block/render-concepts.tsx @@ -6,7 +6,6 @@ import { } from 'suomifi-ui-components'; import SanitizedTextContent from 'yti-common-ui/sanitized-text-content'; import { useTranslation } from 'next-i18next'; -import { getPropertyValue } from '@app/common/components/property-value/get-property-value'; import { translateStatus } from '@app/common/utils/translation-helpers'; import { useBreakpoints } from 'yti-common-ui/media-query'; import { useEffect, useState } from 'react'; @@ -94,25 +93,10 @@ export default function RenderConcepts({ id={`concept-result-checkbox-${concept.id}`} > <SanitizedTextContent - text={ - concept.label - ? getPropertyValue({ - property: Object.keys(concept.label).map( - (key) => { - const obj = { - lang: key, - value: concept.label[key], - regex: '', - }; - return obj; - } - ), - language: i18n.language, - }) ?? - concept.label[i18n.language] ?? - concept.label.fi - : t('concept-label-undefined', { ns: 'common' }) - } + text={getLanguageVersion({ + data: concept.label, + lang: i18n.language, + })} /> </Checkbox> </ExpanderTitle> diff --git a/terminology-ui/src/common/components/removal-modal/generate-removal-data.tsx b/terminology-ui/src/common/components/removal-modal/generate-removal-data.tsx deleted file mode 100644 index 38e9a929a..000000000 --- a/terminology-ui/src/common/components/removal-modal/generate-removal-data.tsx +++ /dev/null @@ -1,128 +0,0 @@ -import { Collection } from '@app/common/interfaces/collection.interface'; -import { Concept } from '@app/common/interfaces/concept.interface'; -import { Identifier } from '@app/common/interfaces/termed-data-types.interface'; - -export function generateConceptData(data: Concept) { - const terminologyId = data.type.graph.id; - const returnData: Identifier<string>[] = []; - - if (data.references.prefLabelXl) { - returnData.push( - ...data.references.prefLabelXl.map((label) => ({ - id: label.id, - type: { - graph: { - id: terminologyId, - }, - id: 'Term', - uri: '', - }, - })) - ); - } - - if (data.references.altLabelXl) { - returnData.push( - ...data.references.altLabelXl.map((label) => ({ - id: label.id, - type: { - graph: { - id: terminologyId, - }, - id: 'Term', - uri: '', - }, - })) - ); - } - - if (data.references.notRecommendedSynonym) { - returnData.push( - ...data.references.notRecommendedSynonym.map((label) => ({ - id: label.id, - type: { - graph: { - id: terminologyId, - }, - id: 'Term', - uri: '', - }, - })) - ); - } - - if (data.references.searchTerm) { - returnData.push( - ...data.references.searchTerm.map((label) => ({ - id: label.id, - type: { - graph: { - id: terminologyId, - }, - id: 'Term', - uri: '', - }, - })) - ); - } - - if (data.references.exactMatch) { - returnData.push( - ...data.references.exactMatch.map((label) => ({ - id: label.id, - type: { - graph: { - id: terminologyId, - }, - id: 'ConceptLink', - uri: '', - }, - })) - ); - } - - if (data.references.relatedMatch) { - returnData.push( - ...data.references.relatedMatch.map((label) => ({ - id: label.id, - type: { - graph: { - id: terminologyId, - }, - id: 'ConceptLink', - uri: '', - }, - })) - ); - } - - returnData.push({ - id: data.id, - type: { - graph: { - id: terminologyId, - }, - id: 'Concept', - uri: '', - }, - }); - - return returnData; -} - -export function generateCollectionData(data: Collection) { - const returnData = []; - - returnData.push({ - id: data.id, - type: { - graph: { - id: data.type.graph.id, - }, - id: 'Collection', - uri: '', - }, - }); - - return returnData; -} diff --git a/terminology-ui/src/common/components/vocabulary/vocabulary.slice.tsx b/terminology-ui/src/common/components/vocabulary/vocabulary.slice.tsx index e23b439f6..11aaa0006 100644 --- a/terminology-ui/src/common/components/vocabulary/vocabulary.slice.tsx +++ b/terminology-ui/src/common/components/vocabulary/vocabulary.slice.tsx @@ -1,6 +1,5 @@ import { createSlice } from '@reduxjs/toolkit'; import { createApi } from '@reduxjs/toolkit/query/react'; -import { VocabularyCopyInfo } from '@app/common/interfaces/vocabulary.interface'; import { UrlState } from '@app/common/utils/hooks/use-url-state'; import { getTerminologyApiBaseQuery } from '@app/store/api-base-query'; import { @@ -79,8 +78,11 @@ export const terminologyApi = createApi({ data: data.payload, }), }), - postCreateVersion: builder.mutation< - VocabularyCopyInfo, + createVersion: builder.mutation< + { + newGraphId: string; + uri: string; + }, { graphId: string; newCode: string } >({ query: ({ graphId, newCode }) => ({ @@ -113,7 +115,7 @@ export const { useGetTerminologyQuery, useCreateTerminologyMutation, useUpdateTerminologyMutation, - usePostCreateVersionMutation, + useCreateVersionMutation, useDeleteTerminologyMutation, useGetIfNamespaceInUseMutation, util: { getRunningQueriesThunk }, diff --git a/terminology-ui/src/common/interfaces/collection.interface.ts b/terminology-ui/src/common/interfaces/collection.interface.ts deleted file mode 100644 index 46688dd03..000000000 --- a/terminology-ui/src/common/interfaces/collection.interface.ts +++ /dev/null @@ -1,62 +0,0 @@ -import { Concept, getConceptMock } from './concept.interface'; -import { BaseEntity, Property } from './termed-data-types.interface'; - -export interface Collection extends BaseEntity<'Collection'> { - properties: { - definition?: Property[]; - prefLabel?: Property[]; - }; - - references: { - member?: Concept[]; - broader?: Concept[]; - }; -} - -export function getCollectionMock( - collectionName: string, - memberNames?: Property[] -): Collection { - return { - code: 'collection-1000', - createdBy: 'Admin User', - createdDate: '1970-01-01T00:00:00.000+00:00', - id: '0001', - identifier: { - id: '123-123', - type: { - id: 'Collection', - graph: { - id: '123-123', - }, - uri: '', - }, - }, - lastModifiedBy: 'Admin User', - lastModifiedDate: '1970-01-01T00:00:00.000+00:00', - number: 1, - properties: { - prefLabel: [ - { - lang: 'fi', - regex: '', - value: collectionName ?? 'Collection-01', - }, - ], - }, - references: { - member: memberNames - ? memberNames.map((label) => getConceptMock(label)) - : [], - }, - referrers: {}, - type: { - id: 'Collection', - graph: { - id: '123-123', - }, - uri: '', - }, - uri: '', - }; -} diff --git a/terminology-ui/src/common/interfaces/concept-link.interface.ts b/terminology-ui/src/common/interfaces/concept-link.interface.ts deleted file mode 100644 index 19ecf9739..000000000 --- a/terminology-ui/src/common/interfaces/concept-link.interface.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { BaseEntity, Property } from './termed-data-types.interface'; - -export interface ConceptLink extends BaseEntity<'ConceptLink'> { - properties: { - prefLabel?: Property[]; - targetGraph?: Property[]; - targetId?: Property[]; - vocabularyLabel?: Property[]; - }; -} diff --git a/terminology-ui/src/common/interfaces/concept.interface.ts b/terminology-ui/src/common/interfaces/concept.interface.ts deleted file mode 100644 index 23569b858..000000000 --- a/terminology-ui/src/common/interfaces/concept.interface.ts +++ /dev/null @@ -1,117 +0,0 @@ -import { Collection } from './collection.interface'; -import { ConceptLink } from './concept-link.interface'; -import { Term } from './term.interface'; -import { BaseEntity, Property } from './termed-data-types.interface'; - -export interface Concept extends BaseEntity<'Concept'> { - properties: { - changeNote?: Property[]; - conceptClass?: Property[]; - conceptScope?: Property[]; - definition?: Property[]; - externalLink?: Property[]; - editorialNote?: Property[]; - example?: Property[]; - historyNote?: Property[]; - notation?: Property[]; - note?: Property[]; - source?: Property[]; - status?: Property[]; - subjectArea?: Property[]; - wordClass?: Property[]; - }; - - references: { - altLabelXl?: Term[]; - broader?: Concept[]; - closeMatch?: ConceptLink[]; - exactMatch?: ConceptLink[]; - hasPart?: Concept[]; - hiddenTerm?: Term[]; - isPartOf?: Concept[]; - narrower?: Concept[]; - notRecommendedSynonym?: Term[]; - prefLabelXl?: Term[]; - related?: Concept[]; - relatedMatch?: ConceptLink[]; - searchTerm?: Term[]; - broadMatch?: ConceptLink[]; - narrowMatch?: ConceptLink[]; - }; - - referrers: { - broader?: BaseEntity<string>[]; // Concept[]? - member?: Collection[]; - narrower?: BaseEntity<string>[]; // Concept[]? - prefLabelXl?: Concept[]; - related?: BaseEntity<string>[]; // Concept[]? - }; -} - -export function getConceptMock(prefLabel: Property): Concept { - return { - code: '001', - createdBy: 'Admin User', - createdDate: '1970-01-01T00:00:00.000+00:00', - id: '001', - identifier: { - id: '', - type: { - id: 'Concept', - graph: { - id: '', - }, - uri: '', - }, - }, - lastModifiedBy: 'Admin User', - lastModifiedDate: '1970-01-01T00:00:00.000+00:00', - number: 1, - properties: {}, - references: { - prefLabelXl: [ - { - code: '001', - createdBy: 'Admin User', - createdDate: '1970-01-01T00:00:00.000+00:00', - id: '001', - identifier: { - id: '', - type: { - id: 'Term', - graph: { - id: '', - }, - uri: '', - }, - }, - lastModifiedBy: 'Admin User', - lastModifiedDate: '1970-01-01T00:00:00.000+00:00', - number: 1, - properties: { - prefLabel: [prefLabel], - }, - references: {}, - referrers: {}, - type: { - id: 'Term', - graph: { - id: '', - }, - uri: '', - }, - uri: '', - }, - ], - }, - referrers: {}, - type: { - id: 'Concept', - graph: { - id: '', - }, - uri: '', - }, - uri: '', - }; -} diff --git a/terminology-ui/src/common/interfaces/group.interface.ts b/terminology-ui/src/common/interfaces/group.interface.ts deleted file mode 100644 index 42b1f0e3a..000000000 --- a/terminology-ui/src/common/interfaces/group.interface.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { BaseEntity, Property } from './termed-data-types.interface'; - -export interface Group extends BaseEntity<'Group'> { - properties: { - definition?: Property[]; - notation?: Property[]; - order?: Property[]; - prefLabel?: Property[]; - }; -} diff --git a/terminology-ui/src/common/interfaces/new-terminology-info.ts b/terminology-ui/src/common/interfaces/new-terminology-info.ts deleted file mode 100644 index 7f27649d2..000000000 --- a/terminology-ui/src/common/interfaces/new-terminology-info.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { LanguageBlockType } from 'yti-common-ui/form/language-selector'; - -export interface NewTerminologyInfo { - contact: string; - languages: LanguageBlockType[]; - infoDomains: { - groupId: string; - labelText: string; - name: string; - uniqueItemId: string; - }[]; - contributors: { - labelText: string; - name: string; - organizationId: string; - uniqueItemId: string; - }[]; - prefix: [string, boolean]; - status?: string; - type: string; -} diff --git a/terminology-ui/src/common/interfaces/new-terminology.ts b/terminology-ui/src/common/interfaces/new-terminology.ts deleted file mode 100644 index 55ec1d1a7..000000000 --- a/terminology-ui/src/common/interfaces/new-terminology.ts +++ /dev/null @@ -1,55 +0,0 @@ -export interface NewTerminology { - code?: string; - createdBy: string; - createdDate: string; - id: string; - lastModifiedBy: string; - lastModifiedDate: string; - properties: { - contact: CommonDTO[]; - description: CommonDTO[]; - language: CommonDTO[]; - origin?: CommonDTO[]; - prefLabel: CommonDTO[]; - priority: CommonDTO[]; - status: CommonDTO[]; - terminologyType: CommonDTO[]; - }; - references: { - contributor: { - id: string; - type: { - graph: { - id: string; - }; - id: string; - uri?: string; - }; - }[]; - inGroup: { - id: string; - type: { - graph: { - id: string; - }; - id: string; - uri?: string; - }; - }[]; - }; - referrers: {}; - type: { - graph: { - id: string; - }; - id: string; - uri: string; - }; - uri?: string; -} - -interface CommonDTO { - lang: string; - regex: string; - value: string; -} diff --git a/terminology-ui/src/common/interfaces/organization.interface.ts b/terminology-ui/src/common/interfaces/organization.interface.ts deleted file mode 100644 index 7633d2d8a..000000000 --- a/terminology-ui/src/common/interfaces/organization.interface.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { BaseEntity, Property } from './termed-data-types.interface'; - -export interface Organization extends BaseEntity<'Organization'> { - properties: { - prefLabel?: Property[]; - }; -} diff --git a/terminology-ui/src/common/interfaces/term.interface.ts b/terminology-ui/src/common/interfaces/term.interface.ts deleted file mode 100644 index 25b8d92da..000000000 --- a/terminology-ui/src/common/interfaces/term.interface.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { BaseEntity, Property } from './termed-data-types.interface'; - -export interface Term extends BaseEntity<'Term'> { - properties: { - changeNote?: Property[]; - draftComment?: Property[]; - editorialNote?: Property[]; - historyNote?: Property[]; - prefLabel?: Property[]; - scope?: Property[]; - source?: Property[]; - status?: Property[]; - termConjugation?: Property[]; - termEquivalency?: Property[]; - termEquivalencyRelation?: Property[]; - termFamily?: Property[]; - termHomographNumber?: Property[]; - termInfo?: Property[]; - termStyle?: Property[]; - wordClass?: Property[]; - }; -} diff --git a/terminology-ui/src/common/interfaces/termed-data-types.interface.ts b/terminology-ui/src/common/interfaces/termed-data-types.interface.ts deleted file mode 100644 index 1c4ae9d2e..000000000 --- a/terminology-ui/src/common/interfaces/termed-data-types.interface.ts +++ /dev/null @@ -1,47 +0,0 @@ -export interface BaseEntity<T extends string> { - id: string; - code: string; - uri: string; - number: number; - createdBy: string; - createdDate: string; - lastModifiedBy: string; - lastModifiedDate: string; - - type: Type<T>; - identifier: Identifier<T>; - - properties: {}; - references: {}; - referrers: {}; -} - -export interface GenericEntity< - T extends string, - P extends { [key: string]: Property[] }, - R1 extends { [key: string]: BaseEntity<string>[] }, - R2 extends { [key: string]: BaseEntity<string>[] } -> extends BaseEntity<T> { - properties: P; - references: R1; - referrers: R2; -} - -export interface Property { - lang: string; - value: string; - regex: string; -} - -export interface Identifier<T extends string> { - id: string; - type: Type<T>; -} - -export interface Type<T extends string> { - id: T; - graph: { - id: string; - }; - uri: string; -} diff --git a/terminology-ui/src/common/interfaces/terminology.interface.ts b/terminology-ui/src/common/interfaces/terminology.interface.ts deleted file mode 100644 index 9dca776dd..000000000 --- a/terminology-ui/src/common/interfaces/terminology.interface.ts +++ /dev/null @@ -1,83 +0,0 @@ -import { Property } from './termed-data-types.interface'; - -export interface TerminologySimpleDTO { - id: string; - code: string; - uri: string; - status: string; - label: { [key: string]: string }; - type?: string; -} - -export interface ContributorsDTO { - id: string; - label: { [key: string]: string }; -} - -export interface InformationDomainDTO { - id: string; - label: { [key: string]: string }; -} - -export interface TerminologyDTO extends TerminologySimpleDTO { - description?: { [key: string]: string }; - contributors: ContributorsDTO[]; - informationDomains: InformationDomainDTO[]; -} - -export interface TerminologySearchResult { - totalHitCount: number; - resultStart: number; - terminologies: TerminologyDTO[] | null; - deepHits: { - [key: string]: DeepHitsDTO[]; - }; -} - -export interface GroupSearchResult { - code: string; - id: string; - properties: { - definition: Property[]; - notation: Property[]; - order: Property[]; - prefLabel: Property; - }; - type: { - graph: { - id: string; - }; - id: string; - }; - uri: string; -} - -export interface OrganizationSearchResult { - code: string; - id: string; - properties: { - prefLabel: Property; - }; - references: { - parent: OrganizationSearchResult; - }; - type: { - graph: { - id: string; - }; - id: string; - }; -} - -export interface DeepHitsDTO { - topHits: { - id: string; - label: { - [value: string]: string; - }; - status: string; - uri: string; - }[]; - totalHitCount: number; - type: string; -} diff --git a/terminology-ui/src/common/interfaces/vocabulary.interface.ts b/terminology-ui/src/common/interfaces/vocabulary.interface.ts deleted file mode 100644 index 563c9941e..000000000 --- a/terminology-ui/src/common/interfaces/vocabulary.interface.ts +++ /dev/null @@ -1,83 +0,0 @@ -import { Group } from './group.interface'; -import { Organization } from './organization.interface'; -import { BaseEntity, Property, Type } from './termed-data-types.interface'; -import { GroupSearchResult } from './terminology.interface'; - -export interface VocabularyInfoDTO - extends BaseEntity<'TerminologicalVocabulary'> { - properties: { - contact?: Property[]; - description?: Property[]; - language?: Property[]; - origin?: Property[]; - prefLabel?: Property[]; - priority?: Property[]; - status?: Property[]; - terminologyType?: Property[]; - }; - - references: { - contributor?: Organization[]; - inGroup?: Group[]; - }; -} - -export interface VocabularyCopyInfo { - newGraphId: string; - uri: string; -} - -export interface VocabularyConcepts { - concepts: VocabularyConceptDTO[]; - resultStart: number; - totalHitCount: number; -} - -export interface VocabularyConceptDTO { - broader?: string[]; - definition: { - [key: string]: string; - }; - id: string; - label: { - [key: string]: string; - }; - modified: string; - status: string; - terminology: { - id: string; - label: { - [key: string]: string; - }; - status: string; - uri: string; - }; - uri: string; -} - -export interface VocabulariesDTO { - code: string; - id: string; - properties: { - contact: Property[]; - language: Property[]; - prefLabel: Property[]; - status: Property[]; - terminologyType: Property[]; - }; - references: { - contributor: { - code: string; - id: string; - properties: { - prefLabel: Property[]; - }; - references: {}; - type: Type<string>; - uri: string; - }; - inGroup: GroupSearchResult[]; - }; - type: Type<string>; - uri: string; -} diff --git a/terminology-ui/src/common/utils/compare-locals.ts b/terminology-ui/src/common/utils/compare-locals.ts index a68764409..9e4318392 100644 --- a/terminology-ui/src/common/utils/compare-locals.ts +++ b/terminology-ui/src/common/utils/compare-locals.ts @@ -1,39 +1,5 @@ import { compareLocales } from 'yti-common-ui/utils/compare-locales'; import { LocalizedListItem } from '../interfaces/interfaces-v2'; -import { Term } from '../interfaces/term.interface'; -import { Property } from '../interfaces/termed-data-types.interface'; - -export interface TermBlockType { - term: Term; - type: string; -} - -// Prioritizes Finnish and Swedish over other languages -export function compareLocales_OLD( - t1: Property | string, - t2: Property | string -): number { - const t1Lang = typeof t1 === 'object' ? t1.lang.toLowerCase() : t1; - const t2Lang = typeof t2 === 'object' ? t2.lang.toLowerCase() : t2; - - if (t1Lang === 'fi' || t2Lang === 'fi') { - return t1Lang === 'fi' ? -1 : 1; - } - - if (t1Lang === 'sv' || t2Lang === 'sv') { - return t1Lang === 'sv' ? -1 : 1; - } - - if (t1Lang === 'en' || t2Lang === 'en') { - return t1Lang === 'en' ? -1 : 1; - } - - if (t1Lang !== t2Lang) { - return t1Lang > t2Lang ? 1 : -1; - } - - return 0; -} /** * Preserves order withing the language diff --git a/terminology-ui/src/common/utils/filter-collection-data.test.ts b/terminology-ui/src/common/utils/filter-collection-data.test.ts deleted file mode 100644 index acc7206dc..000000000 --- a/terminology-ui/src/common/utils/filter-collection-data.test.ts +++ /dev/null @@ -1,125 +0,0 @@ -import { - Collection, - getCollectionMock, -} from '../interfaces/collection.interface'; -import filterCollectionData from './filter-collection-data'; -import { initialUrlState } from './hooks/use-url-state'; - -describe('filter-collection-data', () => { - let data: Collection[]; - - beforeAll(() => { - data = [ - getCollectionMock('Collection-01', [ - { - lang: 'fi', - regex: '', - value: 'member-01', - }, - { - lang: 'fi', - regex: '', - value: 'member-02', - }, - ]), - getCollectionMock('Collection-02', [ - { - lang: 'fi', - regex: '', - value: 'member-03', - }, - { - lang: 'fi', - regex: '', - value: 'member-04', - }, - ]), - getCollectionMock('Collection-03', [ - { - lang: 'fi', - regex: '', - value: 'member-05', - }, - { - lang: 'fi', - regex: '', - value: 'member-01', - }, - ]), - ]; - }); - - it('should return data unaltered with initial urlState', () => { - const returned = filterCollectionData(data, initialUrlState, 'fi'); - - expect(returned).toStrictEqual(data); - }); - - it('should return collections with labels that match with q value', () => { - const returned = filterCollectionData( - data, - { ...initialUrlState, q: 'Collection-02' }, - 'fi' - ); - - expect(returned).toStrictEqual([ - getCollectionMock('Collection-02', [ - { - lang: 'fi', - regex: '', - value: 'member-03', - }, - { - lang: 'fi', - regex: '', - value: 'member-04', - }, - ]), - ]); - }); - - it('should return collections with labels or member labels that match with q value', () => { - const returned = filterCollectionData( - data, - { ...initialUrlState, q: '01' }, - 'fi' - ); - - expect(returned).toStrictEqual([ - getCollectionMock('Collection-01', [ - { - lang: 'fi', - regex: '', - value: 'member-01', - }, - { - lang: 'fi', - regex: '', - value: 'member-02', - }, - ]), - getCollectionMock('Collection-03', [ - { - lang: 'fi', - regex: '', - value: 'member-05', - }, - { - lang: 'fi', - regex: '', - value: 'member-01', - }, - ]), - ]); - }); - - it('should return empty array if no matches found', () => { - const returned = filterCollectionData( - data, - { ...initialUrlState, q: 'this should not match with anything' }, - 'fi' - ); - - expect(returned).toStrictEqual([]); - }); -}); diff --git a/terminology-ui/src/common/utils/filter-collection-data.ts b/terminology-ui/src/common/utils/filter-collection-data.ts deleted file mode 100644 index a133ff33f..000000000 --- a/terminology-ui/src/common/utils/filter-collection-data.ts +++ /dev/null @@ -1,43 +0,0 @@ -import { getPropertyValue } from '@app/common/components/property-value/get-property-value'; -import { Collection } from '@app/common/interfaces/collection.interface'; -import { isEqual } from 'lodash'; -import { initialUrlState, UrlState } from './hooks/use-url-state'; - -export default function filterCollectionData( - data: Collection[], - urlState: UrlState, - language: string -) { - if ( - isEqual( - { ...urlState, type: 'collection' }, - { ...initialUrlState, type: 'collection' } - ) - ) { - return data; - } - - return data.filter((collection) => { - const prefLabel = getPropertyValue({ - property: collection.properties.prefLabel, - language, - }); - - const memberLabels = - collection.references.member?.map((m) => - getPropertyValue({ - property: m.references.prefLabelXl?.[0].properties.prefLabel, - language: language, - }) - ) ?? []; - - if ( - prefLabel?.toLowerCase().includes(urlState.q.toLowerCase()) || - memberLabels.some((label) => - label.toLowerCase().includes(urlState.q.toLowerCase()) - ) - ) { - return true; - } - }); -} diff --git a/terminology-ui/src/common/utils/get-diagram-values.test.ts b/terminology-ui/src/common/utils/get-diagram-values.test.ts deleted file mode 100644 index 802e7ab03..000000000 --- a/terminology-ui/src/common/utils/get-diagram-values.test.ts +++ /dev/null @@ -1,40 +0,0 @@ -import getDiagramValues from './get-diagram-values'; - -describe('get-diagram-values', () => { - it('should generate values from valid input', () => { - const input = - '"{"name":"diagram name","url":"https://www.suomi.fi","description":"diagram description"}"'; - const gotten = getDiagramValues(input); - const expected = { - name: 'diagram name', - url: 'https://www.suomi.fi', - description: 'diagram description', - }; - - expect(gotten).toStrictEqual(expected); - }); - - it('should generate empty values from empty input', () => { - const input = ''; - const gotten = getDiagramValues(input); - const expected = { - name: '', - url: '', - description: '', - }; - - expect(gotten).toStrictEqual(expected); - }); - - it('should generate empty values from incorrect input', () => { - const input = 'this is an invalid input'; - const gotten = getDiagramValues(input); - const expected = { - name: '', - url: '', - description: '', - }; - - expect(gotten).toStrictEqual(expected); - }); -}); diff --git a/terminology-ui/src/common/utils/get-diagram-values.ts b/terminology-ui/src/common/utils/get-diagram-values.ts deleted file mode 100644 index 106f6ce13..000000000 --- a/terminology-ui/src/common/utils/get-diagram-values.ts +++ /dev/null @@ -1,17 +0,0 @@ -export default function getDiagramValues(diagram: string) { - if (diagram.length < 1) { - return { - name: '', - url: '', - description: '', - }; - } - - const name = diagram.split('{"name":"')[1]?.split('","url"')?.[0] ?? ''; - const url = - diagram.split('","url":"')[1]?.split('","description"')?.[0] ?? ''; - const description = - diagram.split('"description":"')[1]?.split('"}')?.[0] ?? ''; - - return { name, url, description }; -} diff --git a/terminology-ui/src/common/utils/get-preflabel.ts b/terminology-ui/src/common/utils/get-preflabel.ts deleted file mode 100644 index ec57eb500..000000000 --- a/terminology-ui/src/common/utils/get-preflabel.ts +++ /dev/null @@ -1,27 +0,0 @@ -interface getPrefLabelProps { - prefLabels: { [value: string]: string }; - lang: string; -} - -export default function getPrefLabel({ prefLabels, lang }: getPrefLabelProps) { - if (!prefLabels) { - return ''; - } - - if (prefLabels[lang]) { - return prefLabels[lang]; - } - - if (prefLabels['fi']) { - return prefLabels['fi'] + ' (fi)'; - } - - if (Object.keys(prefLabels).length > 0) { - return ( - prefLabels[Object.keys(prefLabels)[0]] + - ` (${Object.keys(prefLabels)[0]})` - ); - } - - return ''; -} diff --git a/terminology-ui/src/common/utils/get-property.test.ts b/terminology-ui/src/common/utils/get-property.test.ts deleted file mode 100644 index 12bf77926..000000000 --- a/terminology-ui/src/common/utils/get-property.test.ts +++ /dev/null @@ -1,50 +0,0 @@ -import { Organization } from '@app/common/interfaces/organization.interface'; -import { Term } from '@app/common/interfaces/term.interface'; -import { getProperty } from './get-property'; - -describe('getProperty', () => { - it('should work with Term', () => { - const items: Term[] = [{ properties: {} }] as Term[]; - - expect(getProperty('prefLabel', items)).toStrictEqual([]); - }); - - it('should work with Organization', () => { - const items: Organization[] = [{ properties: {} }] as Organization[]; - - expect(getProperty('prefLabel', items)).toStrictEqual([]); - }); - - it('should return correct property', () => { - const items: Term[] = [ - { - properties: { - prefLabel: [{ value: 'A' }], - changeNote: [{ value: 'B' }], - }, - }, - ] as Term[]; - - expect(getProperty('prefLabel', items)).toStrictEqual([{ value: 'A' }]); - }); - - it('should merge properties of multiple items', () => { - const items: Term[] = [ - { - properties: { - prefLabel: [{ value: 'A' }], - }, - }, - { - properties: { - prefLabel: [{ value: 'B' }], - }, - }, - ] as Term[]; - - expect(getProperty('prefLabel', items)).toStrictEqual([ - { value: 'A' }, - { value: 'B' }, - ]); - }); -}); diff --git a/terminology-ui/src/common/utils/get-property.ts b/terminology-ui/src/common/utils/get-property.ts deleted file mode 100644 index 5245de180..000000000 --- a/terminology-ui/src/common/utils/get-property.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { Organization } from '@app/common/interfaces/organization.interface'; -import { Term } from '@app/common/interfaces/term.interface'; -import { Property } from '@app/common/interfaces/termed-data-types.interface'; - -export function getProperty( - propertyName: keyof Term['properties'], - items?: Term[] -): Property[]; - -export function getProperty( - propertyName: keyof Organization['properties'], - items?: Organization[] -): Property[]; - -// eslint-disable-next-line @typescript-eslint/no-explicit-any -export function getProperty(propertyName: string, items?: any[]) { - if (!items?.length) { - return []; - } - - return items - ?.flatMap((item) => item.properties[propertyName]) - .filter((item): item is Property => !!item); -} diff --git a/terminology-ui/src/common/utils/to-uri.test.ts b/terminology-ui/src/common/utils/to-uri.test.ts deleted file mode 100644 index 82aaee946..000000000 --- a/terminology-ui/src/common/utils/to-uri.test.ts +++ /dev/null @@ -1,22 +0,0 @@ -import toUri from './to-uri'; - -describe('to-uri', () => { - it('should return itself if empty', () => { - const returned = toUri(''); - expect(returned).toBe(''); - }); - - it('should return itself when a valid uri', () => { - const returnedHttp = toUri('http://www.suomi.fi'); - const returnedHttps = toUri('https://www.suomi.fi'); - - expect(returnedHttp).toBe('http://www.suomi.fi'); - expect(returnedHttps).toBe('https://www.suomi.fi'); - }); - - it('should add https:// in the beginning if missing', () => { - const returned = toUri('suomi.fi'); - - expect(returned).toBe('https://suomi.fi'); - }); -}); diff --git a/terminology-ui/src/common/utils/to-uri.ts b/terminology-ui/src/common/utils/to-uri.ts deleted file mode 100644 index 26da5c236..000000000 --- a/terminology-ui/src/common/utils/to-uri.ts +++ /dev/null @@ -1,13 +0,0 @@ -export default function toUri(uri: string): string { - if (uri.length < 1) { - return uri; - } - - let validUri = uri.trim().toLowerCase(); - - if (!validUri.startsWith('http://') && !validUri.startsWith('https://')) { - validUri = 'https://' + validUri; - } - - return validUri; -} diff --git a/terminology-ui/src/modules/edit-collection/concept-picker/concept-picker.types.tsx b/terminology-ui/src/modules/edit-collection/concept-picker/concept-picker.types.tsx index e4a71c5c5..511db8217 100644 --- a/terminology-ui/src/modules/edit-collection/concept-picker/concept-picker.types.tsx +++ b/terminology-ui/src/modules/edit-collection/concept-picker/concept-picker.types.tsx @@ -1,4 +1,3 @@ -import { Concepts } from '@app/common/interfaces/concepts.interface'; import { EditCollectionFormDataType } from '../edit-collection.types'; export interface PickerModalProps { @@ -12,8 +11,3 @@ export interface SelectedConceptProps { selectedConcepts: EditCollectionFormDataType['concepts']; deselect: (value: string) => void; } - -export interface ExpanderConceptContent { - concept: Concepts; - terminologyId: string; -} diff --git a/terminology-ui/src/modules/edit-concept/basic-information/concept-basic-information-types.tsx b/terminology-ui/src/modules/edit-concept/basic-information/concept-basic-information-types.tsx deleted file mode 100644 index ecb2421c0..000000000 --- a/terminology-ui/src/modules/edit-concept/basic-information/concept-basic-information-types.tsx +++ /dev/null @@ -1,50 +0,0 @@ -import { Concepts } from '@app/common/interfaces/concepts.interface'; - -export interface BasicInfoUpdate { - key: string; - lang?: string; - value: string | object; -} - -export interface BasicInfo { - id: string; - lang?: string; - value: string; -} - -export interface DiagramType { - description: string; - diagramName: string; - diagramUrl: string; -} - -export interface BasicInfoType { - definition: { - [key: string]: string; - }; - example: BasicInfo[]; - subject: string; - note: BasicInfo[]; - diagramAndSource: { - diagram: DiagramType[]; - sources: string[]; - }; - orgInfo: { - changeHistory: string; - editorialNote: BasicInfo[]; - etymology: string; - }; - otherInfo: { - conceptClass: string; - wordClass: string; - }; - relationalInfo: { - broaderConcept: Concepts[]; - narrowerConcept: Concepts[]; - relatedConcept: Concepts[]; - isPartOfConcept: Concepts[]; - hasPartConcept: Concepts[]; - relatedConceptInOther: Concepts[]; - matchInOther: Concepts[]; - }; -} diff --git a/terminology-ui/src/modules/edit-concept/basic-information/relational-information.tsx b/terminology-ui/src/modules/edit-concept/basic-information/relational-information.tsx index 9d4845c9f..9801f624f 100644 --- a/terminology-ui/src/modules/edit-concept/basic-information/relational-information.tsx +++ b/terminology-ui/src/modules/edit-concept/basic-information/relational-information.tsx @@ -2,13 +2,14 @@ import Separator from 'yti-common-ui/separator'; import { useTranslation } from 'next-i18next'; import { useState } from 'react'; import { ExpanderTitleButton } from 'suomifi-ui-components'; -import { BasicInfoUpdate } from './concept-basic-information-types'; + import { ConceptExpander, ExpanderContentFitted, } from './concept-basic-information.styles'; import RelationalInformationBlock from '../../../common/components/relational-information-block'; import { BasicInfo, RelationInfoType } from '../new-concept.types'; +import { BasicInfoUpdate } from './concept-basic-information'; interface RelationalInformationProps { infoKey: string; diff --git a/terminology-ui/src/modules/edit-concept/basic-information/status-picker.tsx b/terminology-ui/src/modules/edit-concept/basic-information/status-picker.tsx index 140d7708e..37e0e4f0f 100644 --- a/terminology-ui/src/modules/edit-concept/basic-information/status-picker.tsx +++ b/terminology-ui/src/modules/edit-concept/basic-information/status-picker.tsx @@ -2,7 +2,7 @@ import { useTranslation } from 'next-i18next'; import { useState } from 'react'; import { SingleSelect } from 'suomifi-ui-components'; import { FormError } from '../validate-form'; -import { BasicInfoUpdate } from './concept-basic-information-types'; +import { BasicInfoUpdate } from './concept-basic-information'; interface StatusPickerProps { initialValue: string; diff --git a/terminology-ui/src/modules/edit-concept/generate-form-data-test-variables.tsx b/terminology-ui/src/modules/edit-concept/generate-form-data-test-variables.tsx index a26fbfc5e..c26821c43 100644 --- a/terminology-ui/src/modules/edit-concept/generate-form-data-test-variables.tsx +++ b/terminology-ui/src/modules/edit-concept/generate-form-data-test-variables.tsx @@ -1,4 +1,3 @@ -import { terminologyApi } from '@app/common/components/vocabulary/vocabulary.slice'; import generateFormData from './generate-form-data'; export const emptyFormReturned = generateFormData({ fi: 'demo' }); diff --git a/terminology-ui/src/pages/sitemap.xml.tsx b/terminology-ui/src/pages/sitemap.xml.tsx index 73041130c..685870593 100644 --- a/terminology-ui/src/pages/sitemap.xml.tsx +++ b/terminology-ui/src/pages/sitemap.xml.tsx @@ -1,26 +1,30 @@ -import { TerminologySearchResult } from '@app/common/interfaces/terminology.interface'; +import { + SearchResponse, + TerminogyResponseObject, +} from '@app/common/interfaces/interfaces-v2'; import { GetServerSideProps } from 'next'; // eslint-disable-next-line @typescript-eslint/no-empty-function const Sitemap = () => {}; export const getServerSideProps: GetServerSideProps = async ({ res }) => { - const terminologiesData: TerminologySearchResult = await fetch( - `${process.env.TERMINOLOGY_API_URL}/api/v1/frontend/searchTerminology`, - { - method: 'POST', - headers: { 'content-type': 'application/json' }, - body: JSON.stringify({ - query: '', - statuses: [], - groups: [], - searchConcepts: true, - prefLang: 'fi', - pageSize: 10000, - pageFrom: 0, - }), - } - ).then((data) => data.json()); + const terminologiesData: SearchResponse<TerminogyResponseObject> = + await fetch( + `${process.env.TERMINOLOGY_API_URL}/v2/frontend/search-terminologies`, + { + method: 'POST', + headers: { 'content-type': 'application/json' }, + body: JSON.stringify({ + query: '', + statuses: [], + groups: [], + searchConcepts: true, + prefLang: 'fi', + pageSize: 10000, + pageFrom: 0, + }), + } + ).then((data) => data.json()); const URI = 'https://sanastot.suomi.fi'; @@ -29,7 +33,7 @@ export const getServerSideProps: GetServerSideProps = async ({ res }) => { <url> <loc>${URI}</loc> </url> - ${terminologiesData.terminologies + ${terminologiesData.responseObjects ?.map( (t) => `<url> diff --git a/terminology-ui/src/pages/terminology/[terminologyId].tsx b/terminology-ui/src/pages/terminology/[terminologyId].tsx index 77c9aee13..63c911794 100644 --- a/terminology-ui/src/pages/terminology/[terminologyId].tsx +++ b/terminology-ui/src/pages/terminology/[terminologyId].tsx @@ -18,7 +18,6 @@ import { CommonContextProvider, } from 'yti-common-ui/common-context-provider'; import PageHead from 'yti-common-ui/page-head'; -import { getPropertyValue } from '@app/common/components/property-value/get-property-value'; import { getStoreData } from '@app/common/utils/get-store-data'; import { getRunningQueriesThunk as countsGetRunningQueriesThunk, @@ -26,6 +25,7 @@ import { getVocabularyCount, } from '@app/common/components/counts/counts.slice'; import { wrapper } from '@app/store'; +import { getLanguageVersion } from 'yti-common-ui/utils/get-language-version'; interface TerminologyPageProps extends CommonContextState { _netI18Next: SSRConfig; @@ -117,14 +117,14 @@ export const getServerSideProps = createCommonGetServerSideProps( functionKey: 'getTerminology', }); - const title = getPropertyValue({ - property: vocabularyData?.properties?.prefLabel, - language: locale, + const title = getLanguageVersion({ + data: vocabularyData?.properties?.prefLabel, + lang: locale ?? 'fi', }); - const description = getPropertyValue({ - property: vocabularyData?.properties?.description, - language: locale, + const description = getLanguageVersion({ + data: vocabularyData?.properties?.description, + lang: locale ?? 'fi', }); return { diff --git a/terminology-ui/src/pages/terminology/[terminologyId]/concept/[conceptId].tsx b/terminology-ui/src/pages/terminology/[terminologyId]/concept/[conceptId].tsx index 91faabf01..2a3593227 100644 --- a/terminology-ui/src/pages/terminology/[terminologyId]/concept/[conceptId].tsx +++ b/terminology-ui/src/pages/terminology/[terminologyId]/concept/[conceptId].tsx @@ -20,7 +20,6 @@ import { CommonContextProvider, } from 'yti-common-ui/common-context-provider'; import PageHead from 'yti-common-ui/page-head'; -import { getPropertyValue } from '@app/common/components/property-value/get-property-value'; import { getStoreData } from '@app/common/utils/get-store-data'; import { wrapper } from '@app/store'; import { getLanguageVersion } from 'yti-common-ui/utils/get-language-version'; @@ -103,19 +102,19 @@ export const getServerSideProps = createCommonGetServerSideProps( }; } - const vocabularyTitle = getPropertyValue({ - property: vocabularyData?.properties?.prefLabel, - language: locale, + const vocabularyTitle = getLanguageVersion({ + data: vocabularyData?.properties?.prefLabel, + lang: locale, }); const conceptTitle = getLanguageVersion({ data: conceptData.label, - lang: locale ?? 'fi', + lang: locale, }); const conceptDescription = getLanguageVersion({ data: conceptData.definition, - lang: locale ?? 'fi', + lang: locale, }); return { props: { diff --git a/terminology-ui/src/store/index.tsx b/terminology-ui/src/store/index.tsx index 0834c5fdb..95deb5d42 100644 --- a/terminology-ui/src/store/index.tsx +++ b/terminology-ui/src/store/index.tsx @@ -24,7 +24,6 @@ import { subscriptionApi } from '@app/common/components/subscription/subscriptio import { accessRequestApi } from '@app/common/components/access-request/access-request.slice'; import { adminControlsSlice } from '@app/common/components/admin-controls/admin-controls.slice'; import { importApi } from '@app/common/components/import/import.slice'; -import { modifyApi } from '@app/common/components/modify/modify.slice'; import { NextApiRequest } from 'next'; import { modifyStatusesApi } from '@app/common/components/modify-statuses/modify-statuses.slice'; import { fakeableUsersApi } from '@app/common/components/fakeable-user/fakeable-user.slice'; @@ -49,7 +48,6 @@ const reducers = { [accessRequestApi.reducerPath]: accessRequestApi.reducer, [adminControlsSlice.name]: adminControlsSlice.reducer, [importApi.reducerPath]: importApi.reducer, - [modifyApi.reducerPath]: modifyApi.reducer, [modifyStatusesApi.reducerPath]: modifyStatusesApi.reducer, [fakeableUsersApi.reducerPath]: fakeableUsersApi.reducer, [codeListApi.reducerPath]: codeListApi.reducer, @@ -74,7 +72,6 @@ export const makeStore: MakeStore<any> = ({ subscriptionApi.middleware, accessRequestApi.middleware, importApi.middleware, - modifyApi.middleware, loginApi.middleware, modifyStatusesApi.middleware, fakeableUsersApi.middleware, From 45f788cf0dcff734ee3c9f18be0d9fce49dd5cd5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kalle=20Kytt=C3=A4l=C3=A4?= <kalle.kyttala@rebase.fi> Date: Tue, 10 Sep 2024 10:57:27 +0300 Subject: [PATCH 17/35] v1 redirectinos, remove search terms from public view, code clean up --- common-ui/utils/compare-locales.ts | 3 -- terminology-ui/next.config.js | 23 ++++++++++ terminology-ui/src/modules/concept/utils.tsx | 5 --- .../edit-collection/edit-collection.types.tsx | 43 ------------------- 4 files changed, 23 insertions(+), 51 deletions(-) diff --git a/common-ui/utils/compare-locales.ts b/common-ui/utils/compare-locales.ts index 6fade3b11..9c88dcccc 100644 --- a/common-ui/utils/compare-locales.ts +++ b/common-ui/utils/compare-locales.ts @@ -1,7 +1,4 @@ export function compareLocales(locale1: string, locale2: string): number { - // const t1Lang = typeof t1 === 'object' ? t1.lang.toLowerCase() : t1; - //const t2Lang = typeof t2 === 'object' ? t2.lang.toLowerCase() : t2; - if (locale1 === 'fi' || locale2 === 'fi') { return locale1 === 'fi' ? -1 : 1; } diff --git a/terminology-ui/next.config.js b/terminology-ui/next.config.js index 0b4430a4c..69da6a507 100644 --- a/terminology-ui/next.config.js +++ b/terminology-ui/next.config.js @@ -98,6 +98,9 @@ module.exports = (phase, { defaultConfig }) => { }, }; + const uuidRegexp = + '[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}'; + if (process.env.REWRITE_PROFILE === 'local') { // if run locally in a development environment, the API may run either as a // container or as a process, but will always listen on the same port @@ -107,6 +110,16 @@ module.exports = (phase, { defaultConfig }) => { // https://nextjs.org/docs/api-reference/next.config.js/rewrites async rewrites() { return [ + { + source: `/terminology/:terminologyId(${uuidRegexp})`, + destination: + 'http://localhost:9103/terminology-api/v2/resolve/v1?termedId=:terminologyId', + }, + { + source: `/terminology/:terminologyId(${uuidRegexp})/concept/:conceptId(${uuidRegexp})`, + destination: + 'http://localhost:9103/terminology-api/v2/resolve/v1?termedId=:conceptId', + }, { source: '/terminology-api/:path*', destination: 'http://localhost:9103/terminology-api/:path*', @@ -132,6 +145,16 @@ module.exports = (phase, { defaultConfig }) => { // https://nextjs.org/docs/api-reference/next.config.js/rewrites async rewrites() { return [ + { + source: `/terminology/:terminologyId(${uuidRegexp})`, + destination: + 'http://yti-terminology-api:9103/terminology-api/v2/resolve/v1?termedId=:terminologyId', + }, + { + source: `/terminology/:terminologyId(${uuidRegexp})/concept/:conceptId(${uuidRegexp})`, + destination: + 'http://yti-terminology-api:9103/terminology-api/v2/resolve/v1?termedId=:conceptId', + }, { source: '/terminology-api/:path*', destination: diff --git a/terminology-ui/src/modules/concept/utils.tsx b/terminology-ui/src/modules/concept/utils.tsx index ea3ad921b..8a59c8c4c 100644 --- a/terminology-ui/src/modules/concept/utils.tsx +++ b/terminology-ui/src/modules/concept/utils.tsx @@ -57,11 +57,6 @@ export function getBlockData(t: TFunction, concept?: ConceptInfo) { type: t('field-terms-non-recommended', { ns: 'concept' }), compareKey: getCompareKey(term, 'notRecommendedSynonym', idx), })), - ...(concept.searchTerms ?? []).map((term) => ({ - term, - type: t('field-terms-hidden', { ns: 'concept' }), - compareKey: getCompareKey(term, 'searchTerm', 0), - })), ].sort((t1, t2) => t1.compareKey.localeCompare(t2.compareKey)); const definitions = diff --git a/terminology-ui/src/modules/edit-collection/edit-collection.types.tsx b/terminology-ui/src/modules/edit-collection/edit-collection.types.tsx index ac90d0623..70ba077f4 100644 --- a/terminology-ui/src/modules/edit-collection/edit-collection.types.tsx +++ b/terminology-ui/src/modules/edit-collection/edit-collection.types.tsx @@ -39,46 +39,3 @@ export interface EditCollectionFormDataType { label: LocalizedValue; }[]; } - -export interface EditCollectionPostType { - code?: string; - createdBy: string; - createdDate: string; - id: string; - lastModifiedBy: string; - lastModifiedDate: string; - properties: { - definition: { - lang: string; - regex: string; - value: string; - }[]; - prefLabel: { - lang: string; - regex: string; - value: string; - }[]; - }; - references: { - broader: []; - member: { - id: string; - type: { - graph: { - id: string; - }; - id: string; - uri: string; - }; - }[]; - }; - referrers: {}; - type: { - graph: { - id: string; - }; - id: string; - uri: string; - }; - uri?: string; -} From a90a45e599f0f19dc7e1d768714944bb03ef5809 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kalle=20Kytt=C3=A4l=C3=A4?= <kalle.kyttala@rebase.fi> Date: Tue, 10 Sep 2024 14:09:37 +0300 Subject: [PATCH 18/35] fix redirect config --- terminology-ui/next.config.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/terminology-ui/next.config.js b/terminology-ui/next.config.js index 69da6a507..163d403d0 100644 --- a/terminology-ui/next.config.js +++ b/terminology-ui/next.config.js @@ -148,12 +148,12 @@ module.exports = (phase, { defaultConfig }) => { { source: `/terminology/:terminologyId(${uuidRegexp})`, destination: - 'http://yti-terminology-api:9103/terminology-api/v2/resolve/v1?termedId=:terminologyId', + 'http://yti-terminology-api-v2:9107/terminology-api/v2/resolve/v1?termedId=:terminologyId', }, { source: `/terminology/:terminologyId(${uuidRegexp})/concept/:conceptId(${uuidRegexp})`, destination: - 'http://yti-terminology-api:9103/terminology-api/v2/resolve/v1?termedId=:conceptId', + 'http://yti-terminology-api-v2:9107/terminology-api/v2/resolve/v1?termedId=:conceptId', }, { source: '/terminology-api/:path*', From f453fff769e1c421a29d316cb91971d88cd6dfc0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kalle=20Kytt=C3=A4l=C3=A4?= <kalle.kyttala@rebase.fi> Date: Tue, 10 Sep 2024 14:20:37 +0300 Subject: [PATCH 19/35] fix sitemap generation --- terminology-ui/src/pages/sitemap.xml.tsx | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/terminology-ui/src/pages/sitemap.xml.tsx b/terminology-ui/src/pages/sitemap.xml.tsx index 685870593..6d3c21e00 100644 --- a/terminology-ui/src/pages/sitemap.xml.tsx +++ b/terminology-ui/src/pages/sitemap.xml.tsx @@ -10,19 +10,10 @@ const Sitemap = () => {}; export const getServerSideProps: GetServerSideProps = async ({ res }) => { const terminologiesData: SearchResponse<TerminogyResponseObject> = await fetch( - `${process.env.TERMINOLOGY_API_URL}/v2/frontend/search-terminologies`, + `${process.env.TERMINOLOGY_API_URL}/frontend/search-terminologies?pageSize=10000`, { - method: 'POST', + method: 'GET', headers: { 'content-type': 'application/json' }, - body: JSON.stringify({ - query: '', - statuses: [], - groups: [], - searchConcepts: true, - prefLang: 'fi', - pageSize: 10000, - pageFrom: 0, - }), } ).then((data) => data.json()); @@ -37,7 +28,7 @@ export const getServerSideProps: GetServerSideProps = async ({ res }) => { ?.map( (t) => `<url> - <loc>${URI}/terminology/${t.id}</loc> + <loc>${URI}/terminology/${t.prefix}</loc> </url>` ) .join('')} From 4c21cc8b46d1da8848bce0fbc4a2353f6cec5ed9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kalle=20Kytt=C3=A4l=C3=A4?= <kalle.kyttala@rebase.fi> Date: Wed, 11 Sep 2024 10:54:18 +0300 Subject: [PATCH 20/35] remove automatic prefix functionality --- common-ui/components/form/prefix.styles.tsx | 2 +- .../terminology-components/prefix.tsx | 86 +++++-------------- .../src/modules/edit-collection/index.tsx | 2 +- .../modules/new-terminology/info-manual.tsx | 7 +- 4 files changed, 29 insertions(+), 68 deletions(-) diff --git a/common-ui/components/form/prefix.styles.tsx b/common-ui/components/form/prefix.styles.tsx index b0fe52b32..45286b9f6 100644 --- a/common-ui/components/form/prefix.styles.tsx +++ b/common-ui/components/form/prefix.styles.tsx @@ -3,7 +3,7 @@ import { TextInput as TextInputDS } from 'suomifi-ui-components'; export const TextInput = styled(TextInputDS)` width: 100%; - max-width: ${(props) => (props.fullWidth ? '100%' : '450px')}; + max-width: ${(props) => (props.fullWidth ? '100%' : '480px')}; `; export const PrefixContainer = styled.div` diff --git a/terminology-ui/src/common/components/terminology-components/prefix.tsx b/terminology-ui/src/common/components/terminology-components/prefix.tsx index 96bac5992..b41148c97 100644 --- a/terminology-ui/src/common/components/terminology-components/prefix.tsx +++ b/terminology-ui/src/common/components/terminology-components/prefix.tsx @@ -1,13 +1,9 @@ import { useTranslation } from 'next-i18next'; import { useEffect, useState } from 'react'; -import { Paragraph, RadioButton, Text } from 'suomifi-ui-components'; +import { Paragraph, Text } from 'suomifi-ui-components'; import { useBreakpoints } from 'yti-common-ui/media-query'; import { useGetIfNamespaceInUseMutation } from '@app/common/components/vocabulary/vocabulary.slice'; -import { - BlankFieldset, - RadioButtonGroupSmBot, - TextInputSmBot, -} from './terminology-components.styles'; +import { BlankFieldset, TextInputSmBot } from './terminology-components.styles'; import { UpdateTerminology } from '@app/modules/new-terminology/update-terminology.interface'; import { v4 } from 'uuid'; import { TEXT_INPUT_MAX } from 'yti-common-ui/utils/constants'; @@ -23,8 +19,7 @@ export default function Prefix({ update, userPosted, disabled }: PrefixProps) { const { t } = useTranslation('admin'); const { isSmall } = useBreakpoints(); const [randomURL] = useState(v4().substring(0, 8)); - const [prefix, setPrefix] = useState(randomURL); - const [prefixType, setPrefixType] = useState(''); + const [prefix, setPrefix] = useState(''); const [status, setStatus] = useState<'default' | 'error'>('default'); const [prefixValid, setPrefixValid] = useState(true); const [initDone, setInitDone] = useState(false); @@ -50,17 +45,6 @@ export default function Prefix({ update, userPosted, disabled }: PrefixProps) { } }, [isInUse, update, prefix, status, prefixValid]); - const handlePrefixTypeChange = (value: string) => { - setPrefixType(value); - if (value !== 'manual') { - setPrefix(randomURL); - update({ key: 'prefix', data: [randomURL, true] }); - } else { - setPrefix(''); - update({ key: 'prefix', data: ['', false] }); - } - }; - const handleCustomChange = (e: string) => { setPrefix(e); const inputOnlyValid = e.match(/[a-z0-9\-_]*/g)?.join(''); @@ -81,52 +65,26 @@ export default function Prefix({ update, userPosted, disabled }: PrefixProps) { return ( <BlankFieldset> - <RadioButtonGroupSmBot + <TextInputSmBot labelText={t('prefix')} - groupHintText={t('prefix-hint')} - name="prefix" - defaultValue="automatic" - onChange={(e) => { - handlePrefixTypeChange(e); - }} - id="prefix-input-type-selector" - > - <RadioButton - value="automatic" - id="prefix-input-automatic" - disabled={disabled} - > - {t('automatic-prefix')} - </RadioButton> - <RadioButton - value="manual" - id="prefix-input-manual" - disabled={disabled} - > - {t('manual-prefix')} - </RadioButton> - </RadioButtonGroupSmBot> - {prefixType === 'manual' && ( - <TextInputSmBot - labelText={t('prefix')} - onChange={(e) => handleCustomChange(e?.toString().trim() ?? '')} - debounce={300} - $isSmall={isSmall ? true : undefined} - status={ - status === 'error' || (userPosted && !prefix) ? 'error' : 'default' - } - statusText={ - status === 'error' - ? isInUse.data === true - ? t('prefix-taken') - : t('prefix-invalid') - : '' - } - maxLength={TEXT_INPUT_MAX} - id="prefix-text-input" - disabled={disabled} - /> - )} + onChange={(e) => handleCustomChange(e?.toString().trim() ?? '')} + debounce={300} + $isSmall={isSmall ? true : undefined} + status={ + status === 'error' || (userPosted && !prefix) ? 'error' : 'default' + } + statusText={ + status === 'error' + ? isInUse.data === true + ? t('prefix-taken') + : t('prefix-invalid') + : '' + } + maxLength={TEXT_INPUT_MAX} + id="prefix-text-input" + disabled={disabled} + /> + <Paragraph> <Text variant="bold" smallScreen> {t('url-preview')} diff --git a/terminology-ui/src/modules/edit-collection/index.tsx b/terminology-ui/src/modules/edit-collection/index.tsx index 215bfec43..9bf6594da 100644 --- a/terminology-ui/src/modules/edit-collection/index.tsx +++ b/terminology-ui/src/modules/edit-collection/index.tsx @@ -194,7 +194,7 @@ export default function EditCollection({ const isEdit = !!collectionInfo?.collectionId; const payload = generateCollection(formData, isEdit); - console.info('payload', payload); + disableConfirmation(); setIsCreating(true); diff --git a/terminology-ui/src/modules/new-terminology/info-manual.tsx b/terminology-ui/src/modules/new-terminology/info-manual.tsx index c8364b492..376bf4259 100644 --- a/terminology-ui/src/modules/new-terminology/info-manual.tsx +++ b/terminology-ui/src/modules/new-terminology/info-manual.tsx @@ -18,7 +18,6 @@ import { useGetCodesQuery } from '@app/common/components/codelist/codelist.slice import { MODEL_PREFIX_MAX } from 'yti-common-ui/utils/constants'; import { TerminologyType } from '@app/common/interfaces/interfaces-v2'; import { Status } from 'yti-common-ui/interfaces/status.interface'; -import { v4 } from 'uuid'; interface InfoManualProps { setIsValid: (valid: boolean) => void; @@ -56,7 +55,7 @@ export default function InfoManual({ organizations: [], languages: [], contact: '', - prefix: [v4().slice(0, 8), true], + prefix: ['', true], type: TerminologyType.TERMINOLOGICAL_VOCABULARY, } ); @@ -96,6 +95,10 @@ export default function InfoManual({ ) { valid = false; } + + if (key && 'prefix' && (value[0] === '' || value[1] === false)) { + valid = false; + } }); } setIsValid(valid); From 065c7f1af9b6e932037aa93fc593fc4917bf478c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kalle=20Kytt=C3=A4l=C3=A4?= <kalle.kyttala@rebase.fi> Date: Thu, 12 Sep 2024 08:22:11 +0300 Subject: [PATCH 21/35] revert next config change and fix typo --- terminology-ui/next.config.js | 3 +-- terminology-ui/public/locales/en/collection.json | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/terminology-ui/next.config.js b/terminology-ui/next.config.js index 163d403d0..67f3781f4 100644 --- a/terminology-ui/next.config.js +++ b/terminology-ui/next.config.js @@ -130,8 +130,7 @@ module.exports = (phase, { defaultConfig }) => { }, { source: '/codelist-api/:path*', - destination: - 'https://koodistot.test.yti.cloud.dvv.fi/codelist-api/:path*', + destination: 'http://localhost:9601/codelist-api/:path*', }, ]; }, diff --git a/terminology-ui/public/locales/en/collection.json b/terminology-ui/public/locales/en/collection.json index 9b89348bd..5f5c9abd4 100644 --- a/terminology-ui/public/locales/en/collection.json +++ b/terminology-ui/public/locales/en/collection.json @@ -18,7 +18,7 @@ "prefLabel": "Collection name must be defined in at least one of the available languages." }, "enter-collection-description": "Enter a definition", - "enter-collection-identifier": "Enter a identifier", + "enter-collection-identifier": "Enter an identifier", "enter-collection-name": "Enter a name", "enter-search-term": "Enter search term", "field-definition": "Definition", From 778258eeb212d1988c38cd7ddfa6224900d1751e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kalle=20Kytt=C3=A4l=C3=A4?= <kalle.kyttala@rebase.fi> Date: Thu, 12 Sep 2024 12:34:13 +0300 Subject: [PATCH 22/35] use new MultiLingual component in datamodel-ui --- .../src/modules/model/model-info-view.tsx | 19 ++----------------- 1 file changed, 2 insertions(+), 17 deletions(-) diff --git a/datamodel-ui/src/modules/model/model-info-view.tsx b/datamodel-ui/src/modules/model/model-info-view.tsx index af6a68bab..4874515f8 100644 --- a/datamodel-ui/src/modules/model/model-info-view.tsx +++ b/datamodel-ui/src/modules/model/model-info-view.tsx @@ -295,26 +295,11 @@ export default function ModelInfoView({ <DrawerContent height={headerHeight}> <BasicBlock title={t('name')}> - <MultilingualBlock - data={Object.keys(modelInfo.label) - .sort((a, b) => compareLocales(a, b)) - .reduce((result, key) => { - result[key] = modelInfo.label[key]; - return result; - }, {} as { [key: string]: string })} - /> + <MultilingualBlock data={modelInfo.label} /> </BasicBlock> <BasicBlock title={t('description')}> {Object.keys(modelInfo.description).length > 0 ? ( - <MultilingualBlock - renderHtml={true} - data={Object.keys(modelInfo.description) - .sort((a, b) => compareLocales(a, b)) - .reduce((result, key) => { - result[key] = modelInfo.description[key]; - return result; - }, {} as { [key: string]: string })} - /> + <MultilingualBlock renderHtml={true} data={modelInfo.description} /> ) : ( t('not-added') )} From a4cf2d6a199b6a921a6892616b061909c3bcfb9c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kalle=20Kytt=C3=A4l=C3=A4?= <kalle.kyttala@rebase.fi> Date: Fri, 13 Sep 2024 10:23:09 +0300 Subject: [PATCH 23/35] fix prefix validation and remove unused translations --- .../components/terminology-components/prefix.tsx | 8 ++++---- .../src/common/interfaces/concepts.interface.ts | 15 --------------- .../src/modules/new-terminology/info-manual.tsx | 2 +- 3 files changed, 5 insertions(+), 20 deletions(-) delete mode 100644 terminology-ui/src/common/interfaces/concepts.interface.ts diff --git a/terminology-ui/src/common/components/terminology-components/prefix.tsx b/terminology-ui/src/common/components/terminology-components/prefix.tsx index b41148c97..8d2cd22a5 100644 --- a/terminology-ui/src/common/components/terminology-components/prefix.tsx +++ b/terminology-ui/src/common/components/terminology-components/prefix.tsx @@ -5,7 +5,6 @@ import { useBreakpoints } from 'yti-common-ui/media-query'; import { useGetIfNamespaceInUseMutation } from '@app/common/components/vocabulary/vocabulary.slice'; import { BlankFieldset, TextInputSmBot } from './terminology-components.styles'; import { UpdateTerminology } from '@app/modules/new-terminology/update-terminology.interface'; -import { v4 } from 'uuid'; import { TEXT_INPUT_MAX } from 'yti-common-ui/utils/constants'; export interface PrefixProps { @@ -15,10 +14,9 @@ export interface PrefixProps { } export default function Prefix({ update, userPosted, disabled }: PrefixProps) { - const URI = 'https://iri.suomi.fi/'; + const URI = 'https://iri.suomi.fi/terminology/'; const { t } = useTranslation('admin'); const { isSmall } = useBreakpoints(); - const [randomURL] = useState(v4().substring(0, 8)); const [prefix, setPrefix] = useState(''); const [status, setStatus] = useState<'default' | 'error'>('default'); const [prefixValid, setPrefixValid] = useState(true); @@ -58,7 +56,9 @@ export default function Prefix({ update, userPosted, disabled }: PrefixProps) { setStatus('default'); } - getIsInUse(e !== '' ? e : randomURL); + if (e !== '') { + getIsInUse(e); + } update({ key: 'prefix', data: [e, e !== ''] }); }; diff --git a/terminology-ui/src/common/interfaces/concepts.interface.ts b/terminology-ui/src/common/interfaces/concepts.interface.ts deleted file mode 100644 index 079b7f1a6..000000000 --- a/terminology-ui/src/common/interfaces/concepts.interface.ts +++ /dev/null @@ -1,15 +0,0 @@ -export interface Concepts { - altLabel: { [key: string]: string }; - definition: { [key: string]: string }; - id: string; - label: { [key: string]: string }; - modified: string; - status: string; - terminology: { - id: string; - label: { [key: string]: string }; - status: string; - uri: string; - }; - uri: string; -} diff --git a/terminology-ui/src/modules/new-terminology/info-manual.tsx b/terminology-ui/src/modules/new-terminology/info-manual.tsx index 376bf4259..c69c82db4 100644 --- a/terminology-ui/src/modules/new-terminology/info-manual.tsx +++ b/terminology-ui/src/modules/new-terminology/info-manual.tsx @@ -96,7 +96,7 @@ export default function InfoManual({ valid = false; } - if (key && 'prefix' && (value[0] === '' || value[1] === false)) { + if (key === 'prefix' && (value[0] === '' || value[1] === false)) { valid = false; } }); From d73a71f3ba727e5cad1e432f2619cbc5c0f1763f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kalle=20Kytt=C3=A4l=C3=A4?= <kalle.kyttala@rebase.fi> Date: Fri, 13 Sep 2024 11:10:09 +0300 Subject: [PATCH 24/35] remove debug, commented and unused code --- .../render-chosen.test.tsx | 38 - .../concept-terms-block/index.tsx | 2 +- .../generate-concept-test-expected.tsx | 1775 ----------------- .../edit-concept/generate-form-data.test.tsx | 2 - 4 files changed, 1 insertion(+), 1816 deletions(-) delete mode 100644 terminology-ui/src/modules/edit-concept/generate-concept-test-expected.tsx diff --git a/terminology-ui/src/common/components/relational-information-block/render-chosen.test.tsx b/terminology-ui/src/common/components/relational-information-block/render-chosen.test.tsx index c14771ce5..f3c4876dc 100644 --- a/terminology-ui/src/common/components/relational-information-block/render-chosen.test.tsx +++ b/terminology-ui/src/common/components/relational-information-block/render-chosen.test.tsx @@ -91,41 +91,3 @@ const chosen = [ identifier: '', }, ] as ConceptResponseObject[]; -/* -const chosen = [ - { - altLabel: {}, - definition: {}, - id: '1', - label: { - fi: 'chosen1', - }, - modified: '', - status: '', - terminology: { - id: '', - label: {}, - status: 'VALID', - uri: '', - }, - uri: '', - }, - { - altLabel: {}, - definition: {}, - id: '2', - label: { - fi: 'chosen2', - }, - modified: '', - status: '', - terminology: { - id: '', - label: {}, - status: 'VALID', - uri: '', - }, - uri: '', - }, -]; -*/ diff --git a/terminology-ui/src/modules/edit-concept/concept-terms-block/index.tsx b/terminology-ui/src/modules/edit-concept/concept-terms-block/index.tsx index 9c8596c4f..acf1f45b3 100644 --- a/terminology-ui/src/modules/edit-concept/concept-terms-block/index.tsx +++ b/terminology-ui/src/modules/edit-concept/concept-terms-block/index.tsx @@ -161,7 +161,7 @@ export default function ConceptTermsBlock({ > {t('concept-preferred-terms-description')} </BasicBlock> - <p>srerger</p> + <BasicBlock title={ <MediumHeading variant="h3"> diff --git a/terminology-ui/src/modules/edit-concept/generate-concept-test-expected.tsx b/terminology-ui/src/modules/edit-concept/generate-concept-test-expected.tsx deleted file mode 100644 index 4daede842..000000000 --- a/terminology-ui/src/modules/edit-concept/generate-concept-test-expected.tsx +++ /dev/null @@ -1,1775 +0,0 @@ -export const conceptWithOneTerm = { - delete: [], - save: [ - { - createdBy: '', - createdDate: '1970-01-01T00:00:00.000Z', - id: '0', - lastModifiedBy: '', - lastModifiedDate: '1970-01-01T00:00:00.000Z', - properties: { - changeNote: [ - { - lang: '', - regex: '(?s)^.*$', - value: '', - }, - ], - draftComment: [ - { - lang: '', - regex: '(?s)^.*$', - value: '', - }, - ], - editorialNote: [], - historyNote: [ - { - lang: '', - regex: '(?s)^.*$', - value: '', - }, - ], - prefLabel: [ - { - lang: 'fi', - regex: '(?s)^.*$', - value: 'prefLabel', - }, - ], - scope: [ - { - lang: '', - regex: '(?s)^.*$', - value: '', - }, - ], - source: [], - status: [ - { - lang: '', - regex: '(?s)^.*$', - value: 'DRAFT', - }, - ], - termConjugation: [ - { - lang: '', - regex: '(?s)^.*$', - value: '', - }, - ], - termEquivalency: [ - { - lang: '', - regex: '(?s)^.*$', - value: '', - }, - ], - termEquivalencyRelation: [ - { - lang: '', - regex: '(?s)^.*$', - value: '', - }, - ], - termFamily: [ - { - lang: '', - regex: '(?s)^.*$', - value: '', - }, - ], - termHomographNumber: [ - { - lang: '', - regex: '(?s)^.*$', - value: '', - }, - ], - termInfo: [ - { - lang: '', - regex: '(?s)^.*$', - value: '', - }, - ], - termStyle: [ - { - lang: '', - regex: '(?s)^.*$', - value: '', - }, - ], - wordClass: [ - { - lang: '', - regex: '(?s)^.*$', - value: '', - }, - ], - }, - references: {}, - referrers: {}, - type: { - graph: { - id: 'terminologyId', - }, - id: 'Term', - uri: 'http://www.w3.org/2008/05/skos-xl#Label', - }, - }, - { - createdBy: '', - createdDate: '1970-01-01T00:00:00.000Z', - id: '1', - lastModifiedBy: '', - lastModifiedDate: '1970-01-01T00:00:00.000Z', - properties: { - changeNote: [ - { - lang: '', - regex: '(?s)^.*$', - value: '', - }, - ], - conceptClass: [ - { - lang: '', - regex: '(?s)^.*$', - value: '', - }, - ], - conceptScope: [ - { - lang: '', - regex: '(?s)^.*$', - value: '', - }, - ], - definition: [], - editorialNote: [], - example: [], - externalLink: [ - { - lang: '', - regex: '(?s)^.*$', - value: '', - }, - ], - historyNote: [ - { - lang: '', - regex: '(?s)^.*$', - value: '', - }, - ], - notation: [ - { - lang: '', - regex: '(?s)^.*$', - value: '', - }, - ], - note: [], - source: [ - { - lang: '', - regex: '(?s)^.*$', - value: '', - }, - ], - status: [ - { - lang: '', - regex: '(?s)^.*$', - value: 'DRAFT', - }, - ], - subjectArea: [ - { - lang: '', - regex: '(?s)^.*$', - value: '', - }, - ], - wordClass: [ - { - lang: '', - regex: '(?s)^.*$', - value: '', - }, - ], - }, - references: { - altLabelXl: [], - broader: [], - closeMatch: [], - exactMatch: [], - hasPart: [], - hiddenTerm: [], - isPartOf: [], - narrower: [], - notRecommendedSynonym: [], - prefLabelXl: [ - { - id: '0', - type: { - graph: { - id: 'terminologyId', - }, - id: 'Term', - uri: 'http://www.w3.org/2008/05/skos-xl#Label', - }, - }, - ], - related: [], - relatedMatch: [], - searchTerm: [], - broadMatch: [], - narrowMatch: [], - }, - referrers: {}, - type: { - graph: { - id: 'terminologyId', - }, - id: 'Concept', - uri: 'http://www.w3.org/2004/02/skos/core#Concept', - }, - }, - ], -}; - -/* -export const conceptWithOneTermWithInitialData = { - delete: [], - save: [ - { - code: 'term-1000', - createdBy: 'Admin User', - createdDate: '1970-01-01T00:00:00.000Z', - id: '789', - lastModifiedBy: 'Admin User', - lastModifiedDate: '1970-01-01T00:00:00.000Z', - properties: { - changeNote: [ - { - lang: '', - regex: '(?s)^.*$', - value: '', - }, - ], - draftComment: [ - { - lang: '', - regex: '(?s)^.*$', - value: '', - }, - ], - editorialNote: [], - historyNote: [ - { - lang: '', - regex: '(?s)^.*$', - value: '', - }, - ], - prefLabel: [ - { - lang: 'fi', - regex: '(?s)^.*$', - value: 'prefLabel', - }, - ], - scope: [ - { - lang: '', - regex: '(?s)^.*$', - value: '', - }, - ], - source: [], - status: [ - { - lang: '', - regex: '(?s)^.*$', - value: 'DRAFT', - }, - ], - termConjugation: [ - { - lang: '', - regex: '(?s)^.*$', - value: '', - }, - ], - termEquivalency: [ - { - lang: '', - regex: '(?s)^.*$', - value: '', - }, - ], - termEquivalencyRelation: [ - { - lang: '', - regex: '(?s)^.*$', - value: '', - }, - ], - termFamily: [ - { - lang: '', - regex: '(?s)^.*$', - value: '', - }, - ], - termHomographNumber: [ - { - lang: '', - regex: '(?s)^.*$', - value: '', - }, - ], - termInfo: [ - { - lang: '', - regex: '(?s)^.*$', - value: '', - }, - ], - termStyle: [ - { - lang: '', - regex: '(?s)^.*$', - value: '', - }, - ], - wordClass: [ - { - lang: '', - regex: '(?s)^.*$', - value: '', - }, - ], - }, - references: {}, - referrers: { - prefLabelXl: [ - { - id: '123', - type: { - graph: { - id: 'terminologyId', - }, - id: 'Concept', - uri: '', - }, - }, - ], - }, - type: { - graph: { - id: 'terminologyId', - }, - id: 'Term', - uri: '', - }, - uri: 'sanastot.suomi.fi/sanasto/term-1000', - }, - { - code: 'concept-1000', - createdBy: 'Admin User', - createdDate: '1970-01-01T00:00:00.000Z', - id: '1', - lastModifiedBy: '', - lastModifiedDate: '1970-01-01T00:00:00.000Z', - properties: { - changeNote: [ - { - lang: '', - regex: '(?s)^.*$', - value: '', - }, - ], - conceptClass: [ - { - lang: '', - regex: '(?s)^.*$', - value: '', - }, - ], - conceptScope: [ - { - lang: '', - regex: '(?s)^.*$', - value: '', - }, - ], - definition: [], - editorialNote: [], - example: [], - externalLink: [ - { - lang: '', - regex: '(?s)^.*$', - value: '', - }, - ], - historyNote: [ - { - lang: '', - regex: '(?s)^.*$', - value: '', - }, - ], - notation: [ - { - lang: '', - regex: '(?s)^.*$', - value: '', - }, - ], - note: [], - source: [ - { - lang: '', - regex: '(?s)^.*$', - value: '', - }, - ], - status: [ - { - lang: '', - regex: '(?s)^.*$', - value: 'DRAFT', - }, - ], - subjectArea: [ - { - lang: '', - regex: '(?s)^.*$', - value: '', - }, - ], - wordClass: [ - { - lang: '', - regex: '(?s)^.*$', - value: '', - }, - ], - }, - references: { - altLabelXl: [], - broader: [], - closeMatch: [], - exactMatch: [], - hasPart: [], - hiddenTerm: [], - isPartOf: [], - narrower: [], - notRecommendedSynonym: [], - prefLabelXl: [ - { - id: '789', - type: { - graph: { - id: 'terminologyId', - }, - id: 'Term', - uri: '', - }, - }, - ], - related: [], - relatedMatch: [], - searchTerm: [], - broadMatch: [], - narrowMatch: [], - }, - referrers: {}, - type: { - graph: { - id: 'terminologyId', - }, - id: 'Concept', - uri: '', - }, - uri: 'sanastot.suomi.fi/sanasto/concept-1000', - }, - ], -}; - -export const conceptWithInternalRelations = { - delete: [], - save: [ - { - createdBy: '', - createdDate: '1970-01-01T00:00:00.000Z', - id: '0', - lastModifiedBy: '', - lastModifiedDate: '1970-01-01T00:00:00.000Z', - properties: { - changeNote: [ - { - lang: '', - regex: '(?s)^.*$', - value: '', - }, - ], - draftComment: [ - { - lang: '', - regex: '(?s)^.*$', - value: '', - }, - ], - editorialNote: [], - historyNote: [ - { - lang: '', - regex: '(?s)^.*$', - value: '', - }, - ], - prefLabel: [ - { - lang: 'fi', - regex: '(?s)^.*$', - value: 'prefLabel', - }, - ], - scope: [ - { - lang: '', - regex: '(?s)^.*$', - value: '', - }, - ], - source: [], - status: [ - { - lang: '', - regex: '(?s)^.*$', - value: 'DRAFT', - }, - ], - termConjugation: [ - { - lang: '', - regex: '(?s)^.*$', - value: '', - }, - ], - termEquivalency: [ - { - lang: '', - regex: '(?s)^.*$', - value: '', - }, - ], - termEquivalencyRelation: [ - { - lang: '', - regex: '(?s)^.*$', - value: '', - }, - ], - termFamily: [ - { - lang: '', - regex: '(?s)^.*$', - value: '', - }, - ], - termHomographNumber: [ - { - lang: '', - regex: '(?s)^.*$', - value: '', - }, - ], - termInfo: [ - { - lang: '', - regex: '(?s)^.*$', - value: '', - }, - ], - termStyle: [ - { - lang: '', - regex: '(?s)^.*$', - value: '', - }, - ], - wordClass: [ - { - lang: '', - regex: '(?s)^.*$', - value: '', - }, - ], - }, - references: {}, - referrers: {}, - type: { - graph: { - id: 'terminologyId', - }, - id: 'Term', - uri: 'http://www.w3.org/2008/05/skos-xl#Label', - }, - }, - { - createdBy: '', - createdDate: '1970-01-01T00:00:00.000Z', - id: '', - lastModifiedBy: '', - lastModifiedDate: '1970-01-01T00:00:00.000Z', - properties: { - changeNote: [ - { - lang: '', - regex: '(?s)^.*$', - value: '', - }, - ], - conceptClass: [ - { - lang: '', - regex: '(?s)^.*$', - value: '', - }, - ], - conceptScope: [ - { - lang: '', - regex: '(?s)^.*$', - value: '', - }, - ], - definition: [], - editorialNote: [], - example: [], - externalLink: [ - { - lang: '', - regex: '(?s)^.*$', - value: '', - }, - ], - historyNote: [ - { - lang: '', - regex: '(?s)^.*$', - value: '', - }, - ], - notation: [ - { - lang: '', - regex: '(?s)^.*$', - value: '', - }, - ], - note: [], - source: [ - { - lang: '', - regex: '(?s)^.*$', - value: '', - }, - ], - status: [ - { - lang: '', - regex: '(?s)^.*$', - value: 'DRAFT', - }, - ], - subjectArea: [ - { - lang: '', - regex: '(?s)^.*$', - value: '', - }, - ], - wordClass: [ - { - lang: '', - regex: '(?s)^.*$', - value: '', - }, - ], - }, - references: { - altLabelXl: [], - broader: [ - { - id: '148ab016-36ee-486a-862a-87d6dde2f86f', - type: { - graph: { - id: 'terminologyId', - }, - id: 'Concept', - uri: '', - }, - }, - ], - closeMatch: [], - exactMatch: [], - hasPart: [ - { - id: '148ab016-36ee-486a-862a-87d6dde2f86f', - type: { - graph: { - id: 'terminologyId', - }, - id: 'Concept', - uri: '', - }, - }, - ], - hiddenTerm: [], - isPartOf: [ - { - id: '148ab016-36ee-486a-862a-87d6dde2f86f', - type: { - graph: { - id: 'terminologyId', - }, - id: 'Concept', - uri: '', - }, - }, - ], - narrower: [ - { - id: '148ab016-36ee-486a-862a-87d6dde2f86f', - type: { - graph: { - id: 'terminologyId', - }, - id: 'Concept', - uri: '', - }, - }, - ], - notRecommendedSynonym: [], - prefLabelXl: [ - { - id: '0', - type: { - graph: { - id: 'terminologyId', - }, - id: 'Term', - uri: 'http://www.w3.org/2008/05/skos-xl#Label', - }, - }, - ], - related: [ - { - id: '148ab016-36ee-486a-862a-87d6dde2f86f', - type: { - graph: { - id: 'terminologyId', - }, - id: 'Concept', - uri: '', - }, - }, - ], - relatedMatch: [], - searchTerm: [], - broadMatch: [], - narrowMatch: [], - }, - referrers: {}, - type: { - graph: { - id: 'terminologyId', - }, - id: 'Concept', - uri: 'http://www.w3.org/2004/02/skos/core#Concept', - }, - }, - ], -}; - -export const differentTerms = { - delete: [], - save: [ - { - createdBy: '', - createdDate: '1970-01-01T00:00:00.000Z', - id: '0', - lastModifiedBy: '', - lastModifiedDate: '1970-01-01T00:00:00.000Z', - properties: { - changeNote: [ - { - lang: '', - regex: '(?s)^.*$', - value: 'muutoshistoria', - }, - ], - draftComment: [ - { - lang: '', - regex: '(?s)^.*$', - value: '', - }, - ], - editorialNote: [ - { - lang: '', - regex: '(?s)^.*$', - value: 'ylläpitäjän muistiinpano', - }, - ], - historyNote: [ - { - lang: '', - regex: '(?s)^.*$', - value: 'käytön historiatieto', - }, - ], - prefLabel: [ - { - lang: 'fi', - regex: '(?s)^.*$', - value: 'demo', - }, - ], - scope: [ - { - lang: '', - regex: '(?s)^.*$', - value: 'käyttöala', - }, - ], - source: [ - { - lang: '', - regex: '(?s)^.*$', - value: 'lähteet', - }, - ], - status: [ - { - lang: '', - regex: '(?s)^.*$', - value: 'DRAFT', - }, - ], - termConjugation: [ - { - lang: '', - regex: '(?s)^.*$', - value: 'singular', - }, - ], - termEquivalency: [ - { - lang: '', - regex: '(?s)^.*$', - value: '', - }, - ], - termEquivalencyRelation: [ - { - lang: '', - regex: '(?s)^.*$', - value: '', - }, - ], - termFamily: [ - { - lang: '', - regex: '(?s)^.*$', - value: 'feminine', - }, - ], - termHomographNumber: [ - { - lang: '', - regex: '(?s)^.*$', - value: '1', - }, - ], - termInfo: [ - { - lang: '', - regex: '(?s)^.*$', - value: 'termin lisätieto', - }, - ], - termStyle: [ - { - lang: '', - regex: '(?s)^.*$', - value: 'spoken-form', - }, - ], - wordClass: [ - { - lang: '', - regex: '(?s)^.*$', - value: 'adjective', - }, - ], - }, - references: {}, - referrers: {}, - type: { - graph: { - id: 'terminologyId', - }, - id: 'Term', - uri: 'http://www.w3.org/2008/05/skos-xl#Label', - }, - }, - { - createdBy: '', - createdDate: '1970-01-01T00:00:00.000Z', - id: '1', - lastModifiedBy: '', - lastModifiedDate: '1970-01-01T00:00:00.000Z', - properties: { - changeNote: [ - { - lang: '', - regex: '(?s)^.*$', - value: 'muutoshistoria', - }, - ], - draftComment: [ - { - lang: '', - regex: '(?s)^.*$', - value: '', - }, - ], - editorialNote: [ - { - lang: '', - regex: '(?s)^.*$', - value: 'ylläpitäjän muistiinpano', - }, - ], - historyNote: [ - { - lang: '', - regex: '(?s)^.*$', - value: 'käytön historiatieto', - }, - ], - prefLabel: [ - { - lang: 'fi', - regex: '(?s)^.*$', - value: 'synonyymi', - }, - ], - scope: [ - { - lang: '', - regex: '(?s)^.*$', - value: 'käyttöala', - }, - ], - source: [ - { - lang: '', - regex: '(?s)^.*$', - value: 'lähteet', - }, - ], - status: [ - { - lang: '', - regex: '(?s)^.*$', - value: 'DRAFT', - }, - ], - termConjugation: [ - { - lang: '', - regex: '(?s)^.*$', - value: 'singular', - }, - ], - termEquivalency: [ - { - lang: '', - regex: '(?s)^.*$', - value: '~', - }, - ], - termEquivalencyRelation: [ - { - lang: '', - regex: '(?s)^.*$', - value: '', - }, - ], - termFamily: [ - { - lang: '', - regex: '(?s)^.*$', - value: 'feminine', - }, - ], - termHomographNumber: [ - { - lang: '', - regex: '(?s)^.*$', - value: '1', - }, - ], - termInfo: [ - { - lang: '', - regex: '(?s)^.*$', - value: 'termin lisätieto', - }, - ], - termStyle: [ - { - lang: '', - regex: '(?s)^.*$', - value: 'spoken-form', - }, - ], - wordClass: [ - { - lang: '', - regex: '(?s)^.*$', - value: 'adjective', - }, - ], - }, - references: {}, - referrers: {}, - type: { - graph: { - id: 'terminologyId', - }, - id: 'Term', - uri: 'http://www.w3.org/2008/05/skos-xl#Label', - }, - }, - { - createdBy: '', - createdDate: '1970-01-01T00:00:00.000Z', - id: '2', - lastModifiedBy: '', - lastModifiedDate: '1970-01-01T00:00:00.000Z', - properties: { - changeNote: [ - { - lang: '', - regex: '(?s)^.*$', - value: 'muutoshistoria', - }, - ], - draftComment: [ - { - lang: '', - regex: '(?s)^.*$', - value: '', - }, - ], - editorialNote: [ - { - lang: '', - regex: '(?s)^.*$', - value: 'ylläpitäjän muistiinpano', - }, - ], - historyNote: [ - { - lang: '', - regex: '(?s)^.*$', - value: 'käytön historiatieto', - }, - ], - prefLabel: [ - { - lang: 'fi', - regex: '(?s)^.*$', - value: 'ei-suositettava synonyymi', - }, - ], - scope: [ - { - lang: '', - regex: '(?s)^.*$', - value: 'käyttöala', - }, - ], - source: [ - { - lang: '', - regex: '(?s)^.*$', - value: 'lähteet', - }, - ], - status: [ - { - lang: '', - regex: '(?s)^.*$', - value: 'DRAFT', - }, - ], - termConjugation: [ - { - lang: '', - regex: '(?s)^.*$', - value: 'singular', - }, - ], - termEquivalency: [ - { - lang: '', - regex: '(?s)^.*$', - value: '~', - }, - ], - termEquivalencyRelation: [ - { - lang: '', - regex: '(?s)^.*$', - value: '', - }, - ], - termFamily: [ - { - lang: '', - regex: '(?s)^.*$', - value: 'feminine', - }, - ], - termHomographNumber: [ - { - lang: '', - regex: '(?s)^.*$', - value: '1', - }, - ], - termInfo: [ - { - lang: '', - regex: '(?s)^.*$', - value: 'termin lisätieto', - }, - ], - termStyle: [ - { - lang: '', - regex: '(?s)^.*$', - value: 'spoken-form', - }, - ], - wordClass: [ - { - lang: '', - regex: '(?s)^.*$', - value: 'adjective', - }, - ], - }, - references: {}, - referrers: {}, - type: { - graph: { - id: 'terminologyId', - }, - id: 'Term', - uri: 'http://www.w3.org/2008/05/skos-xl#Label', - }, - }, - { - createdBy: '', - createdDate: '1970-01-01T00:00:00.000Z', - id: '3', - lastModifiedBy: '', - lastModifiedDate: '1970-01-01T00:00:00.000Z', - properties: { - changeNote: [ - { - lang: '', - regex: '(?s)^.*$', - value: 'muutoshistoria', - }, - ], - draftComment: [ - { - lang: '', - regex: '(?s)^.*$', - value: '', - }, - ], - editorialNote: [ - { - lang: '', - regex: '(?s)^.*$', - value: 'ylläpitäjän muistiinpano', - }, - ], - historyNote: [ - { - lang: '', - regex: '(?s)^.*$', - value: 'käytön historiatieto', - }, - ], - prefLabel: [ - { - lang: 'fi', - regex: '(?s)^.*$', - value: 'hakusana', - }, - ], - scope: [ - { - lang: '', - regex: '(?s)^.*$', - value: 'käyttöala', - }, - ], - source: [ - { - lang: '', - regex: '(?s)^.*$', - value: 'lähteet', - }, - ], - status: [ - { - lang: '', - regex: '(?s)^.*$', - value: 'DRAFT', - }, - ], - termConjugation: [ - { - lang: '', - regex: '(?s)^.*$', - value: 'singular', - }, - ], - termEquivalency: [ - { - lang: '', - regex: '(?s)^.*$', - value: '~', - }, - ], - termEquivalencyRelation: [ - { - lang: '', - regex: '(?s)^.*$', - value: '', - }, - ], - termFamily: [ - { - lang: '', - regex: '(?s)^.*$', - value: 'feminine', - }, - ], - termHomographNumber: [ - { - lang: '', - regex: '(?s)^.*$', - value: '1', - }, - ], - termInfo: [ - { - lang: '', - regex: '(?s)^.*$', - value: 'termin lisätieto', - }, - ], - termStyle: [ - { - lang: '', - regex: '(?s)^.*$', - value: 'spoken-form', - }, - ], - wordClass: [ - { - lang: '', - regex: '(?s)^.*$', - value: 'adjective', - }, - ], - }, - references: {}, - referrers: {}, - type: { - graph: { - id: 'terminologyId', - }, - id: 'Term', - uri: 'http://www.w3.org/2008/05/skos-xl#Label', - }, - }, - { - createdBy: '', - createdDate: '1970-01-01T00:00:00.000Z', - id: '6e00b816-c077-4747-8597-46047005584d', - lastModifiedBy: '', - lastModifiedDate: '1970-01-01T00:00:00.000Z', - properties: { - changeNote: [ - { - lang: '', - regex: '(?s)^.*$', - value: 'muutoshistoria', - }, - ], - conceptClass: [ - { - lang: '', - regex: '(?s)^.*$', - value: 'käsitteen luokka', - }, - ], - conceptScope: [ - { - lang: '', - regex: '(?s)^.*$', - value: '', - }, - ], - definition: [ - { - lang: 'fi', - regex: '(?s)^.*$', - value: 'määritelmä', - }, - ], - editorialNote: [ - { - lang: '', - regex: '(?s)^.*$', - value: 'ylläpitäjän muistiinpano', - }, - ], - example: [ - { - lang: 'fi', - regex: '(?s)^.*$', - value: 'käyttöesimerkki', - }, - ], - externalLink: [ - { - lang: '', - regex: '(?s)^.*$', - value: - '"{"name":"käsitekaavion nimi","url":"https://käsitekaavion-verkko-osoite.fi","description":"kuvaus"}"', - }, - ], - historyNote: [ - { - lang: '', - regex: '(?s)^.*$', - value: 'käytön historiatieto', - }, - ], - notation: [ - { - lang: '', - regex: '(?s)^.*$', - value: '', - }, - ], - note: [ - { - lang: 'fi', - regex: '(?s)^.*$', - value: 'huomautus', - }, - ], - source: [ - { - lang: '', - regex: '(?s)^.*$', - value: 'lähteet', - }, - ], - status: [ - { - lang: '', - regex: '(?s)^.*$', - value: 'VALID', - }, - ], - subjectArea: [ - { - lang: '', - regex: '(?s)^.*$', - value: 'aihealue', - }, - ], - wordClass: [ - { - lang: '', - regex: '(?s)^.*$', - value: '', - }, - ], - }, - references: { - altLabelXl: [ - { - id: '1', - type: { - graph: { - id: 'terminologyId', - }, - id: 'Term', - uri: 'http://www.w3.org/2008/05/skos-xl#Label', - }, - }, - ], - broader: [], - closeMatch: [], - exactMatch: [], - hasPart: [], - hiddenTerm: [], - isPartOf: [], - narrower: [], - notRecommendedSynonym: [ - { - id: '2', - type: { - graph: { - id: 'terminologyId', - }, - id: 'Term', - uri: 'http://www.w3.org/2008/05/skos-xl#Label', - }, - }, - ], - prefLabelXl: [ - { - id: '0', - type: { - graph: { - id: 'terminologyId', - }, - id: 'Term', - uri: 'http://www.w3.org/2008/05/skos-xl#Label', - }, - }, - ], - related: [], - relatedMatch: [], - searchTerm: [ - { - id: '3', - type: { - graph: { - id: 'terminologyId', - }, - id: 'Term', - uri: 'http://www.w3.org/2008/05/skos-xl#Label', - }, - }, - ], - broadMatch: [], - narrowMatch: [], - }, - referrers: {}, - type: { - graph: { - id: 'terminologyId', - }, - id: 'Concept', - uri: 'http://www.w3.org/2004/02/skos/core#Concept', - }, - }, - ], -}; - -export const removeTerm = { - delete: [ - { - id: 'ffd68efb-d5c8-4ea2-9fb8-fa7b0b688fe9', - type: { - graph: { - id: 'terminologyId', - }, - id: 'Term', - uri: '', - }, - }, - ], - save: [ - { - code: 'term-3', - createdBy: '', - createdDate: '1970-01-01T00:00:00.000Z', - id: '0', - lastModifiedBy: '', - lastModifiedDate: '1970-01-01T00:00:00.000Z', - properties: { - changeNote: [ - { - lang: '', - regex: '(?s)^.*$', - value: '', - }, - ], - draftComment: [ - { - lang: '', - regex: '(?s)^.*$', - value: '', - }, - ], - editorialNote: [], - historyNote: [ - { - lang: '', - regex: '(?s)^.*$', - value: '', - }, - ], - prefLabel: [ - { - lang: 'fi', - regex: '(?s)^.*$', - value: 'demo fi', - }, - ], - scope: [ - { - lang: '', - regex: '(?s)^.*$', - value: '', - }, - ], - source: [], - status: [ - { - lang: '', - regex: '(?s)^.*$', - value: 'DRAFT', - }, - ], - termConjugation: [ - { - lang: '', - regex: '(?s)^.*$', - value: '', - }, - ], - termEquivalency: [ - { - lang: '', - regex: '(?s)^.*$', - value: '', - }, - ], - termEquivalencyRelation: [ - { - lang: '', - regex: '(?s)^.*$', - value: '', - }, - ], - termFamily: [ - { - lang: '', - regex: '(?s)^.*$', - value: '', - }, - ], - termHomographNumber: [ - { - lang: '', - regex: '(?s)^.*$', - value: '', - }, - ], - termInfo: [ - { - lang: '', - regex: '(?s)^.*$', - value: '', - }, - ], - termStyle: [ - { - lang: '', - regex: '(?s)^.*$', - value: '', - }, - ], - wordClass: [ - { - lang: '', - regex: '(?s)^.*$', - value: '', - }, - ], - }, - references: {}, - referrers: { - prefLabelXl: [ - { - id: 'abad6405-2843-42e3-bacd-9448e34fd049', - type: { - graph: { - id: 'terminologyId', - }, - id: 'Concept', - uri: '', - }, - }, - ], - }, - type: { - graph: { - id: 'terminologyId', - }, - id: 'Term', - uri: '', - }, - uri: 'http://uri.suomi.fi/terminology/212e3cf8/term-3', - }, - { - code: 'concept-1', - createdBy: '', - createdDate: '1970-01-01T00:00:00.000Z', - id: 'abad6405-2843-42e3-bacd-9448e34fd049', - lastModifiedBy: '', - lastModifiedDate: '1970-01-01T00:00:00.000Z', - properties: { - changeNote: [ - { - lang: '', - regex: '(?s)^.*$', - value: '', - }, - ], - conceptClass: [ - { - lang: '', - regex: '(?s)^.*$', - value: '', - }, - ], - conceptScope: [ - { - lang: '', - regex: '(?s)^.*$', - value: '', - }, - ], - definition: [], - editorialNote: [], - example: [], - externalLink: [ - { - lang: '', - regex: '(?s)^.*$', - value: '', - }, - ], - historyNote: [ - { - lang: '', - regex: '(?s)^.*$', - value: '', - }, - ], - notation: [ - { - lang: '', - regex: '(?s)^.*$', - value: '', - }, - ], - note: [], - source: [ - { - lang: '', - regex: '(?s)^.*$', - value: '', - }, - ], - status: [ - { - lang: '', - regex: '(?s)^.*$', - value: 'DRAFT', - }, - ], - subjectArea: [ - { - lang: '', - regex: '(?s)^.*$', - value: '', - }, - ], - wordClass: [ - { - lang: '', - regex: '(?s)^.*$', - value: '', - }, - ], - }, - references: { - altLabelXl: [], - broader: [], - closeMatch: [], - exactMatch: [], - hasPart: [], - hiddenTerm: [], - isPartOf: [], - narrower: [], - notRecommendedSynonym: [], - prefLabelXl: [ - { - id: '0', - type: { - graph: { - id: 'terminologyId', - }, - id: 'Term', - uri: '', - }, - }, - ], - related: [], - relatedMatch: [], - searchTerm: [], - broadMatch: [], - narrowMatch: [], - }, - referrers: {}, - type: { - graph: { - id: 'terminologyId', - }, - id: 'Concept', - uri: '', - }, - uri: 'http://uri.suomi.fi/terminology/212e3cf8/concept-1', - }, - ], -};*/ diff --git a/terminology-ui/src/modules/edit-concept/generate-form-data.test.tsx b/terminology-ui/src/modules/edit-concept/generate-form-data.test.tsx index b4e046270..a70f69ed3 100644 --- a/terminology-ui/src/modules/edit-concept/generate-form-data.test.tsx +++ b/terminology-ui/src/modules/edit-concept/generate-form-data.test.tsx @@ -1,8 +1,6 @@ import { emptyFormExpected, emptyFormReturned, - //extensiveDataExpected, - //extensiveDataReturned, initialDataExpected, initialDataReturned, } from './generate-form-data-test-variables'; From 21506f26d1c95f79b1c554b09e74c490b59a291c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kalle=20Kytt=C3=A4l=C3=A4?= <kalle.kyttala@rebase.fi> Date: Wed, 9 Oct 2024 08:42:16 +0300 Subject: [PATCH 25/35] fix term family translation --- terminology-ui/public/locales/en/common.json | 2 +- terminology-ui/public/locales/fi/common.json | 2 +- terminology-ui/public/locales/sv/common.json | 2 +- terminology-ui/src/common/utils/translation-helpers.ts | 4 ++-- .../edit-concept/concept-terms-block/new-term-modal.tsx | 4 ++-- .../modules/edit-concept/concept-terms-block/term-form.tsx | 4 ++-- .../src/modules/edit-concept/generate-concept.test.tsx | 4 ++-- .../edit-concept/generate-form-data-test-variables.tsx | 4 ++-- 8 files changed, 13 insertions(+), 13 deletions(-) diff --git a/terminology-ui/public/locales/en/common.json b/terminology-ui/public/locales/en/common.json index 0482b816f..d2f16e449 100644 --- a/terminology-ui/public/locales/en/common.json +++ b/terminology-ui/public/locales/en/common.json @@ -163,7 +163,7 @@ "term-family": { "feminine": "feminine", "masculine": "masculine", - "neutral": "neutral" + "neuter": "neuter" }, "term-style": { "spoken-form": "spoken form" diff --git a/terminology-ui/public/locales/fi/common.json b/terminology-ui/public/locales/fi/common.json index f8fb6823c..76d4fe35b 100644 --- a/terminology-ui/public/locales/fi/common.json +++ b/terminology-ui/public/locales/fi/common.json @@ -163,7 +163,7 @@ "term-family": { "feminine": "feminiini", "masculine": "maskuliini", - "neutral": "neutri" + "neuter": "neutri" }, "term-style": { "spoken-form": "puhekieli" diff --git a/terminology-ui/public/locales/sv/common.json b/terminology-ui/public/locales/sv/common.json index 2cb5c4872..bc8d94754 100644 --- a/terminology-ui/public/locales/sv/common.json +++ b/terminology-ui/public/locales/sv/common.json @@ -163,7 +163,7 @@ "term-family": { "feminine": "femininum", "masculine": "maskulinum", - "neutral": "neutrum" + "neuter": "neutrum" }, "term-style": { "spoken-form": "talspråk" diff --git a/terminology-ui/src/common/utils/translation-helpers.ts b/terminology-ui/src/common/utils/translation-helpers.ts index be3fc0113..5b31e5def 100644 --- a/terminology-ui/src/common/utils/translation-helpers.ts +++ b/terminology-ui/src/common/utils/translation-helpers.ts @@ -85,8 +85,8 @@ export function translateTermFamily(termFamily: string, t: TFunction) { switch (termFamily) { case 'MASCULINE': return t('term-family.masculine', { ns: 'common' }); - case 'NEUTRAL': - return t('term-family.neutral', { ns: 'common' }); + case 'NEUTER': + return t('term-family.neuter', { ns: 'common' }); case 'FEMININE': return t('term-family.feminine', { ns: 'common' }); default: diff --git a/terminology-ui/src/modules/edit-concept/concept-terms-block/new-term-modal.tsx b/terminology-ui/src/modules/edit-concept/concept-terms-block/new-term-modal.tsx index ae1394708..61970413e 100644 --- a/terminology-ui/src/modules/edit-concept/concept-terms-block/new-term-modal.tsx +++ b/terminology-ui/src/modules/edit-concept/concept-terms-block/new-term-modal.tsx @@ -374,8 +374,8 @@ export default function NewTermModal({ uniqueItemId: 'masculine', }, { - labelText: t('term-family.neutral', { ns: 'common' }), - uniqueItemId: 'neutral', + labelText: t('term-family.neuter', { ns: 'common' }), + uniqueItemId: 'neuter', }, { labelText: t('term-family.feminine', { ns: 'common' }), diff --git a/terminology-ui/src/modules/edit-concept/concept-terms-block/term-form.tsx b/terminology-ui/src/modules/edit-concept/concept-terms-block/term-form.tsx index 1f9c226b3..c3416e28a 100644 --- a/terminology-ui/src/modules/edit-concept/concept-terms-block/term-form.tsx +++ b/terminology-ui/src/modules/edit-concept/concept-terms-block/term-form.tsx @@ -73,8 +73,8 @@ export default function TermForm({ uniqueItemId: 'MASCULINE', }, { - labelText: t('term-family.neutral', { ns: 'common' }), - uniqueItemId: 'NEUTRAL', + labelText: t('term-family.neuter', { ns: 'common' }), + uniqueItemId: 'NEUTER', }, { labelText: t('term-family.feminine', { ns: 'common' }), diff --git a/terminology-ui/src/modules/edit-concept/generate-concept.test.tsx b/terminology-ui/src/modules/edit-concept/generate-concept.test.tsx index ba91287ed..e13b1af3c 100644 --- a/terminology-ui/src/modules/edit-concept/generate-concept.test.tsx +++ b/terminology-ui/src/modules/edit-concept/generate-concept.test.tsx @@ -34,7 +34,7 @@ const input = { status: 'DRAFT', termConjugation: 'SINGULAR', termEquivalency: 'BROADER', - termFamily: 'NEUTRAL', + termFamily: 'NEUTER', termHomographNumber: '2', termInfo: 'info', termStyle: 'term style', @@ -147,7 +147,7 @@ const expected = { historyNote: 'term history', changeNote: 'term change', termStyle: 'term style', - termFamily: 'NEUTRAL', + termFamily: 'NEUTER', termConjugation: 'SINGULAR', termEquivalency: 'BROADER', wordClass: 'ADJECTIVE', diff --git a/terminology-ui/src/modules/edit-concept/generate-form-data-test-variables.tsx b/terminology-ui/src/modules/edit-concept/generate-form-data-test-variables.tsx index c26821c43..48c3ff2a3 100644 --- a/terminology-ui/src/modules/edit-concept/generate-form-data-test-variables.tsx +++ b/terminology-ui/src/modules/edit-concept/generate-form-data-test-variables.tsx @@ -101,7 +101,7 @@ export const initialDataReturned = generateFormData( status: 'DRAFT', termConjugation: 'SINGULAR', termEquivalency: 'BROADER', - termFamily: 'NEUTRAL', + termFamily: 'NEUTER', homographNumber: 2, termInfo: 'info', termStyle: 'style', @@ -156,7 +156,7 @@ export const initialDataExpected = { status: 'DRAFT', termConjugation: 'SINGULAR', termEquivalency: 'BROADER', - termFamily: 'NEUTRAL', + termFamily: 'NEUTER', termHomographNumber: '2', termInfo: 'info', termStyle: 'style', From 7f019788db1a415432ca67b475268b2640442a48 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kalle=20Kytt=C3=A4l=C3=A4?= <kalle.kyttala@rebase.fi> Date: Mon, 4 Nov 2024 08:49:58 +0200 Subject: [PATCH 26/35] fix build error --- terminology-ui/src/modules/new-terminology/info-manual.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/terminology-ui/src/modules/new-terminology/info-manual.tsx b/terminology-ui/src/modules/new-terminology/info-manual.tsx index c69c82db4..7c8c575bb 100644 --- a/terminology-ui/src/modules/new-terminology/info-manual.tsx +++ b/terminology-ui/src/modules/new-terminology/info-manual.tsx @@ -18,6 +18,7 @@ import { useGetCodesQuery } from '@app/common/components/codelist/codelist.slice import { MODEL_PREFIX_MAX } from 'yti-common-ui/utils/constants'; import { TerminologyType } from '@app/common/interfaces/interfaces-v2'; import { Status } from 'yti-common-ui/interfaces/status.interface'; +import { MODEL_PREFIX_MAX } from 'yti-common-ui/utils/constants'; interface InfoManualProps { setIsValid: (valid: boolean) => void; From 6adcc898e48fd105e220b689a85c17a85b8a06f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kalle=20Kytt=C3=A4l=C3=A4?= <kalle.kyttala@rebase.fi> Date: Tue, 29 Oct 2024 15:34:01 +0200 Subject: [PATCH 27/35] fix error handling in excel import --- terminology-ui/public/locales/en/admin.json | 5 ++-- terminology-ui/public/locales/fi/admin.json | 5 ++-- terminology-ui/public/locales/sv/admin.json | 5 ++-- .../common/components/import/excel.error.tsx | 27 ++++++++----------- .../src/common/utils/translation-helpers.ts | 18 +++++-------- .../modules/new-terminology/file-upload.tsx | 2 +- 6 files changed, 25 insertions(+), 37 deletions(-) diff --git a/terminology-ui/public/locales/en/admin.json b/terminology-ui/public/locales/en/admin.json index b71ce7448..b69674b9f 100644 --- a/terminology-ui/public/locales/en/admin.json +++ b/terminology-ui/public/locales/en/admin.json @@ -81,11 +81,10 @@ "concept-diagrams-and-sources": "Concept diagrams and sources", "concept-identifier": "Identifier", "concept-import": { + "column-missing-language": "Property key is missing language suffix", + "duplicate-key-value": "Column name is already in use", "prefLabel-column-missing": "prefLabel column is missing", "prefLabel-row-missing": "preflabel needs to be defined in at least 1 language", - "property-missing-language-suffix": "Property key is missing language suffix", - "status-column-missing": "Status column is missing", - "term-missing-language-suffix": "Term key is missing language suffix", "terminology-no-language": "Language does not exist in terminology", "undefined-error": "There was an error importing the concepts", "value-not-valid": "Value in cell was not valid" diff --git a/terminology-ui/public/locales/fi/admin.json b/terminology-ui/public/locales/fi/admin.json index 3fda071f8..8f14e05b9 100644 --- a/terminology-ui/public/locales/fi/admin.json +++ b/terminology-ui/public/locales/fi/admin.json @@ -81,11 +81,10 @@ "concept-diagrams-and-sources": "Käsitekaaviot ja lähteet", "concept-identifier": "Tunnus", "concept-import": { + "column-missing-language": "Sarakkeen otsikosta puuttuu kielen koodiarvo", + "duplicate-key-value": "Sarakkeen nimi on jo käytössä", "prefLabel-column-missing": "prefLabel kenttä puuttuu", "prefLabel-row-missing": "prefLabel pitää määrittää vähintään yhdellä kielellä", - "property-missing-language-suffix": "Käsitteen tiedon avaimesta puuttuu kielen koodiarvo", - "status-column-missing": "Tila kenttä puuttuu", - "term-missing-language-suffix": "Termin avaimesta puuttuu kielen koodiarvo", "terminology-no-language": "Sanastossa ei ole annettua kieltä", "undefined-error": "Käsitteiden tuonnissa tapahtui virhe", "value-not-valid": "Arvo solussa on virheellinen" diff --git a/terminology-ui/public/locales/sv/admin.json b/terminology-ui/public/locales/sv/admin.json index 8c800350d..2b723734a 100644 --- a/terminology-ui/public/locales/sv/admin.json +++ b/terminology-ui/public/locales/sv/admin.json @@ -81,11 +81,10 @@ "concept-diagrams-and-sources": "Begreppsdiagram och källor", "concept-identifier": "Identifierare", "concept-import": { + "column-missing-language": "Språksuffix fattas från rubriken av kolumnet", + "duplicate-key-value": "Rubriken av kolumnet är redan i bruk", "prefLabel-column-missing": "prefLabel fältet har inte definierats", "prefLabel-row-missing": "prefLabel måste anges åtminstone på ett språk", - "property-missing-language-suffix": "Språksuffix fattas från rubriken av Begrepp-kolumnet", - "status-column-missing": "Status-fältet har inte definierats", - "term-missing-language-suffix": "Språksuffix fattas från rubriken av Term-kolumnet", "terminology-no-language": "Språket har inte definierats för ordlistan", "undefined-error": "Ett problem uppstod med importering av begreppen", "value-not-valid": "Värdet i fältet är felaktigt" diff --git a/terminology-ui/src/common/components/import/excel.error.tsx b/terminology-ui/src/common/components/import/excel.error.tsx index b0889e3e2..b33833d79 100644 --- a/terminology-ui/src/common/components/import/excel.error.tsx +++ b/terminology-ui/src/common/components/import/excel.error.tsx @@ -9,11 +9,10 @@ export interface ExcelError { export interface ExcelErrorDetails { message: string; - errorDetails?: { - sheet: string; - row: number; - column: string; - }; + key: string; + sheet: string; + rowNumber: number; + column: string; } type ExcelErrorDetailBlockProps = { @@ -25,25 +24,21 @@ export const ExcelErrorDetailBlock = ({ }: ExcelErrorDetailBlockProps) => { const { t } = useTranslation('admin'); - if (!errorInfo.data.errorDetails) { + if (!errorInfo.data) { return <></>; } return ( <> <ul> - {errorInfo.data.errorDetails?.sheet && ( - <li>{`${t('excel-sheet')}: ${ - errorInfo.data.errorDetails?.sheet - }`}</li> + {errorInfo.data?.sheet && ( + <li>{`${t('excel-sheet')}: ${errorInfo.data?.sheet}`}</li> )} - {errorInfo.data.errorDetails?.row != undefined && ( - <li>{`${t('excel-row')}: ${errorInfo.data.errorDetails?.row}`}</li> + {errorInfo.data?.rowNumber != undefined && ( + <li>{`${t('excel-row')}: ${errorInfo.data?.rowNumber}`}</li> )} - {errorInfo.data.errorDetails?.column && ( - <li>{`${t('excel-column')}: ${ - errorInfo.data.errorDetails?.column - }`}</li> + {errorInfo.data?.column && ( + <li>{`${t('excel-column')}: ${errorInfo.data?.column}`}</li> )} </ul> </> diff --git a/terminology-ui/src/common/utils/translation-helpers.ts b/terminology-ui/src/common/utils/translation-helpers.ts index 5b31e5def..4a2bd56ac 100644 --- a/terminology-ui/src/common/utils/translation-helpers.ts +++ b/terminology-ui/src/common/utils/translation-helpers.ts @@ -204,22 +204,18 @@ export function translateHttpError( export function translateExcelParseError(message: string, t: TFunction) { switch (message) { - case 'terminology-no-language': + case 'terminology-missing-language': return t('concept-import.terminology-no-language', { ns: 'admin' }); - case 'term-missing-language-suffix': - return t('concept-import.term-missing-language-suffix', { ns: 'admin' }); + case 'column-missing-language': + return t('concept-import.column-missing-language', { ns: 'admin' }); case 'value-not-valid': return t('concept-import.value-not-valid', { ns: 'admin' }); - case 'property-missing-language-suffix': - return t('concept-import.property-missing-language-suffix', { - ns: 'admin', - }); - case 'status-column-missing': - return t('concept-import.status-column-missing', { ns: 'admin' }); - case 'prefLabel-column-missing': + case 'pref-label-column-missing': return t('concept-import.prefLabel-column-missing', { ns: 'admin' }); - case 'prefLabel-row-missing': + case 'pref-label-row-missing': return t('concept-import.prefLabel-row-missing', { ns: 'admin' }); + case 'duplicate-key-value': + return t('concept-import.duplicate-key-value', { ns: 'admin' }); default: return t('concept-import.undefined-error', { ns: 'admin' }); } diff --git a/terminology-ui/src/modules/new-terminology/file-upload.tsx b/terminology-ui/src/modules/new-terminology/file-upload.tsx index f1a6cb9e1..6969aeabf 100644 --- a/terminology-ui/src/modules/new-terminology/file-upload.tsx +++ b/terminology-ui/src/modules/new-terminology/file-upload.tsx @@ -64,7 +64,7 @@ export default function FileUpload({ </> ) : ( <> - {translateExcelParseError(errorInfo.data.message, t)} + {translateExcelParseError(errorInfo.data.key, t)} <ExcelErrorDetailBlock errorInfo={errorInfo} /> </> )} From 837704171285a7448e4d4a89747b9827c0f4255d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kalle=20Kytt=C3=A4l=C3=A4?= <kalle.kyttala@rebase.fi> Date: Mon, 4 Nov 2024 16:22:43 +0200 Subject: [PATCH 28/35] fix search parameter prefLang to sortlang --- .../components/terminology-search/terminology-search.slice.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/terminology-ui/src/common/components/terminology-search/terminology-search.slice.tsx b/terminology-ui/src/common/components/terminology-search/terminology-search.slice.tsx index 60d6da400..71cb7976b 100644 --- a/terminology-ui/src/common/components/terminology-search/terminology-search.slice.tsx +++ b/terminology-ui/src/common/components/terminology-search/terminology-search.slice.tsx @@ -37,7 +37,7 @@ export const terminologySearchApi = createApi({ ? [value.urlState.organization] : [], searchConcepts: true, - prefLang: value.language ? value.language : 'fi', + sortLang: value.language ? value.language : 'fi', pageSize: 50, pageFrom: Math.max(0, (value.urlState.page - 1) * 50), language: value.urlState.lang ? value.urlState.lang : null, From 0f3c1dee57a4cd034de5ddadc11a73168bacd80a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kalle=20Kytt=C3=A4l=C3=A4?= <kalle.kyttala@rebase.fi> Date: Tue, 12 Nov 2024 08:01:59 +0200 Subject: [PATCH 29/35] fix conflicts --- terminology-ui/src/modules/new-terminology/info-manual.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/terminology-ui/src/modules/new-terminology/info-manual.tsx b/terminology-ui/src/modules/new-terminology/info-manual.tsx index 7c8c575bb..c69c82db4 100644 --- a/terminology-ui/src/modules/new-terminology/info-manual.tsx +++ b/terminology-ui/src/modules/new-terminology/info-manual.tsx @@ -18,7 +18,6 @@ import { useGetCodesQuery } from '@app/common/components/codelist/codelist.slice import { MODEL_PREFIX_MAX } from 'yti-common-ui/utils/constants'; import { TerminologyType } from '@app/common/interfaces/interfaces-v2'; import { Status } from 'yti-common-ui/interfaces/status.interface'; -import { MODEL_PREFIX_MAX } from 'yti-common-ui/utils/constants'; interface InfoManualProps { setIsValid: (valid: boolean) => void; From 19ccb958c182b163f58aa27de91e58ea888efcea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kalle=20Kytt=C3=A4l=C3=A4?= <kalle.kyttala@rebase.fi> Date: Fri, 8 Nov 2024 11:20:36 +0200 Subject: [PATCH 30/35] rename categories to types in count response --- terminology-ui/src/common/interfaces/counts.interface.ts | 2 +- .../src/modules/vocabulary/terminology-list-filter.tsx | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/terminology-ui/src/common/interfaces/counts.interface.ts b/terminology-ui/src/common/interfaces/counts.interface.ts index 90f466f8f..69b5cef38 100644 --- a/terminology-ui/src/common/interfaces/counts.interface.ts +++ b/terminology-ui/src/common/interfaces/counts.interface.ts @@ -1,7 +1,7 @@ export interface Counts { totalHitCount: number; counts: { - categories: { [key: string]: number }; + types: { [key: string]: number }; statuses: { [key: string]: number }; groups: { [key: string]: number }; languages?: { [key: string]: number }; diff --git a/terminology-ui/src/modules/vocabulary/terminology-list-filter.tsx b/terminology-ui/src/modules/vocabulary/terminology-list-filter.tsx index 4a4d2ff1a..e16285f15 100644 --- a/terminology-ui/src/modules/vocabulary/terminology-list-filter.tsx +++ b/terminology-ui/src/modules/vocabulary/terminology-list-filter.tsx @@ -59,8 +59,8 @@ export function TerminologyListFilter({ title={t('vocabulary-filter-show-only')} isModal={isModal} counts={{ - concepts: counts?.counts.categories?.Concept, - collections: counts?.counts.categories?.Collection, + concepts: counts?.counts.types?.Concept, + collections: counts?.counts.types?.Collection, }} /> {shouldRenderStatusFilter && ( From b44cac328aaed580a7ab4c417318707c17006626 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kalle=20Kytt=C3=A4l=C3=A4?= <kalle.kyttala@rebase.fi> Date: Fri, 8 Nov 2024 11:21:06 +0200 Subject: [PATCH 31/35] allow html tags in terminology description --- .../src/common/components/info-dropdown/info-expander.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/terminology-ui/src/common/components/info-dropdown/info-expander.tsx b/terminology-ui/src/common/components/info-dropdown/info-expander.tsx index 54d44dc2f..facf4c2be 100644 --- a/terminology-ui/src/common/components/info-dropdown/info-expander.tsx +++ b/terminology-ui/src/common/components/info-dropdown/info-expander.tsx @@ -85,7 +85,7 @@ export default function InfoExpander({ <BasicBlock title={t('vocabulary-info-description')}> {Object.keys(data.description).length > 0 ? ( - <MultilingualBlock data={data.description} /> + <MultilingualBlock data={data.description} renderHtml={true} /> ) : ( <></> )} From f33d19f826fefe9bbfa81b293798cd593b245f02 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kalle=20Kytt=C3=A4l=C3=A4?= <kalle.kyttala@rebase.fi> Date: Fri, 8 Nov 2024 11:21:25 +0200 Subject: [PATCH 32/35] fix user requests api endpoint --- .../common/components/access-request/access-request-modal.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/terminology-ui/src/common/components/access-request/access-request-modal.tsx b/terminology-ui/src/common/components/access-request/access-request-modal.tsx index ac7be2d4a..23a4687d3 100644 --- a/terminology-ui/src/common/components/access-request/access-request-modal.tsx +++ b/terminology-ui/src/common/components/access-request/access-request-modal.tsx @@ -206,7 +206,7 @@ export default function AccessRequestModal({ ); function sendPost() { - let uri = `/request?organizationId=${chosenOrganization}`; + let uri = `/requests?organizationId=${chosenOrganization}`; Object.keys(services).map((key) => { if (services[key]) { From 4cb463f747cf572b21f958eff95576898e66caeb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kalle=20Kytt=C3=A4l=C3=A4?= <kalle.kyttala@rebase.fi> Date: Tue, 12 Nov 2024 09:41:59 +0200 Subject: [PATCH 33/35] sort languages list --- .../modules/terminology-search/search-page-filter.tsx | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/terminology-ui/src/modules/terminology-search/search-page-filter.tsx b/terminology-ui/src/modules/terminology-search/search-page-filter.tsx index 3fa5811c3..94226d7e7 100644 --- a/terminology-ui/src/modules/terminology-search/search-page-filter.tsx +++ b/terminology-ui/src/modules/terminology-search/search-page-filter.tsx @@ -12,6 +12,7 @@ import { FilterTopPartBlock } from './terminology-search.styles'; import { Organization } from 'yti-common-ui/interfaces/organization.interface'; import { Group } from 'yti-common-ui/interfaces/group.interface'; import { getLanguageVersion } from 'yti-common-ui/utils/get-language-version'; +import { compareLocales } from 'yti-common-ui/utils/compare-locales'; export interface SearchPageFilterProps { isModal?: boolean; @@ -60,10 +61,12 @@ export function SearchPageFilter({ labelText={t('filter-by-language')} languages={ counts && counts.counts.languages - ? Object.keys(counts?.counts.languages).map((key) => ({ - labelText: key, - uniqueItemId: key, - })) + ? Object.keys(counts?.counts.languages) + .sort(compareLocales) + .map((key) => ({ + labelText: key, + uniqueItemId: key, + })) : [] } /> From 01bdbb77410bafc1dbb9bceabe66b199a19af79f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kalle=20Kytt=C3=A4l=C3=A4?= <kalle.kyttala@rebase.fi> Date: Wed, 13 Nov 2024 14:14:05 +0200 Subject: [PATCH 34/35] fix page titles --- .../src/pages/terminology/[terminologyId].tsx | 8 ++++---- .../[terminologyId]/concept/[conceptId].tsx | 13 +++++++------ 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/terminology-ui/src/pages/terminology/[terminologyId].tsx b/terminology-ui/src/pages/terminology/[terminologyId].tsx index 63c911794..dd8bb60e3 100644 --- a/terminology-ui/src/pages/terminology/[terminologyId].tsx +++ b/terminology-ui/src/pages/terminology/[terminologyId].tsx @@ -111,19 +111,19 @@ export const getServerSideProps = createCommonGetServerSideProps( await Promise.all(store.dispatch(getRunningQueriesThunk())); await Promise.all(store.dispatch(countsGetRunningQueriesThunk())); - const vocabularyData = getStoreData({ + const terminologyData = getStoreData({ state: store.getState(), - reduxKey: 'terminologyAPI', + reduxKey: 'terminologyApi', functionKey: 'getTerminology', }); const title = getLanguageVersion({ - data: vocabularyData?.properties?.prefLabel, + data: terminologyData?.label, lang: locale ?? 'fi', }); const description = getLanguageVersion({ - data: vocabularyData?.properties?.description, + data: terminologyData?.description, lang: locale ?? 'fi', }); diff --git a/terminology-ui/src/pages/terminology/[terminologyId]/concept/[conceptId].tsx b/terminology-ui/src/pages/terminology/[terminologyId]/concept/[conceptId].tsx index 2a3593227..469a47552 100644 --- a/terminology-ui/src/pages/terminology/[terminologyId]/concept/[conceptId].tsx +++ b/terminology-ui/src/pages/terminology/[terminologyId]/concept/[conceptId].tsx @@ -23,6 +23,7 @@ import PageHead from 'yti-common-ui/page-head'; import { getStoreData } from '@app/common/utils/get-store-data'; import { wrapper } from '@app/store'; import { getLanguageVersion } from 'yti-common-ui/utils/get-language-version'; +import { Term } from '@app/common/interfaces/interfaces-v2'; interface ConceptPageProps extends CommonContextState { _netI18Next: SSRConfig; @@ -83,7 +84,7 @@ export const getServerSideProps = createCommonGetServerSideProps( const vocabularyData = getStoreData({ state: store.getState(), - reduxKey: 'terminologyAPI', + reduxKey: 'terminologyApi', functionKey: 'getTerminology', }); @@ -103,14 +104,14 @@ export const getServerSideProps = createCommonGetServerSideProps( } const vocabularyTitle = getLanguageVersion({ - data: vocabularyData?.properties?.prefLabel, + data: vocabularyData?.label, lang: locale, }); - const conceptTitle = getLanguageVersion({ - data: conceptData.label, - lang: locale, - }); + const recommendedTerm = + conceptData.recommendedTerms.find((t: Term) => t.language === locale) ?? + conceptData.recommendedTerms[0]; + const conceptTitle = recommendedTerm.label ?? ''; const conceptDescription = getLanguageVersion({ data: conceptData.definition, From 354c2388658398c540771ea888c4756953d06594 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kalle=20Kytt=C3=A4l=C3=A4?= <kalle.kyttala@rebase.fi> Date: Thu, 14 Nov 2024 08:48:30 +0200 Subject: [PATCH 35/35] add hos header to sitemap request --- terminology-ui/src/pages/sitemap.xml.tsx | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/terminology-ui/src/pages/sitemap.xml.tsx b/terminology-ui/src/pages/sitemap.xml.tsx index 6d3c21e00..18757d557 100644 --- a/terminology-ui/src/pages/sitemap.xml.tsx +++ b/terminology-ui/src/pages/sitemap.xml.tsx @@ -7,13 +7,16 @@ import { GetServerSideProps } from 'next'; // eslint-disable-next-line @typescript-eslint/no-empty-function const Sitemap = () => {}; -export const getServerSideProps: GetServerSideProps = async ({ res }) => { +export const getServerSideProps: GetServerSideProps = async ({ req, res }) => { const terminologiesData: SearchResponse<TerminogyResponseObject> = await fetch( `${process.env.TERMINOLOGY_API_URL}/frontend/search-terminologies?pageSize=10000`, { method: 'GET', - headers: { 'content-type': 'application/json' }, + headers: { + 'content-type': 'application/json', + host: req.headers['host'] ?? 'sanastot.suomi.fi', + }, } ).then((data) => data.json());