Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add relation section to dataset form #884

Merged
merged 3 commits into from
Dec 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@ import { getDatasetById } from '../../../../../actions/actions';
import { DatasetForm } from '../../../../../../components/dataset-form';

import { Params } from 'next/dist/shared/lib/router/utils/route-matcher';
import { getTranslateText, localization } from '@catalog-frontend/utils';
import { getTranslateText, getValidSession, localization } from '@catalog-frontend/utils';
import { Organization } from '@catalog-frontend/types';
import {
getAllDatasetSeries,
getDatasetTypes,
getDataThemes,
getFrequencies,
Expand All @@ -21,6 +22,9 @@ export default async function EditDatasetPage({ params }: Params) {
const referenceDataEnv = process.env.FDK_BASE_URI ?? '';
const dataset = await getDatasetById(catalogId, datasetId);
const organization: Organization = await getOrganization(catalogId).then((res) => res.json());
const session = await getValidSession();
const accessToken = session?.accessToken;
const datasetSeries = await getAllDatasetSeries(catalogId, accessToken).then((res) => res.json());

const [
losThemesResponse,
Expand Down Expand Up @@ -79,6 +83,7 @@ export default async function EditDatasetPage({ params }: Params) {
searchEnv={searchEnv}
referenceDataEnv={referenceDataEnv}
referenceData={referenceData}
datasetSeries={datasetSeries._embedded.datasets}
></DatasetForm>
</div>
</>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@ import { Breadcrumbs, BreadcrumbType, PageBanner } from '@catalog-frontend/ui';
import { DatasetForm } from '../../../../../components/dataset-form';
import { datasetToBeCreatedTemplate } from '../../../../../components/dataset-form/utils/dataset-initial-values';
import { Params } from 'next/dist/shared/lib/router/utils/route-matcher';
import { getTranslateText, localization } from '@catalog-frontend/utils';
import { getTranslateText, getValidSession, localization } from '@catalog-frontend/utils';
import { Organization } from '@catalog-frontend/types';
import {
getAllDatasetSeries,
getDatasetTypes,
getDataThemes,
getFrequencies,
Expand All @@ -20,6 +21,9 @@ export default async function NewDatasetPage({ params }: Params) {
const organization: Organization = await getOrganization(catalogId).then((res) => res.json());
const searchEnv = process.env.FDK_SEARCH_SERVICE_BASE_URI ?? '';
const referenceDataEnv = process.env.FDK_BASE_URI ?? '';
const session = await getValidSession();
const accessToken = session?.accessToken;
const datasetSeries = await getAllDatasetSeries(catalogId, accessToken).then((res) => res.json());

const [
losThemesResponse,
Expand Down Expand Up @@ -74,6 +78,7 @@ export default async function NewDatasetPage({ params }: Params) {
referenceData={referenceData}
searchEnv={searchEnv}
referenceDataEnv={referenceDataEnv}
datasetSeries={datasetSeries._embedded.datasets}
></DatasetForm>
</div>
</>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
import { useState } from 'react';
import { Dataset, DatasetSeries } from '@catalog-frontend/types';
import { AddButton, DeleteButton, FormContainer } from '@catalog-frontend/ui';
import { getTranslateText, localization } from '@catalog-frontend/utils';
import { Heading, Combobox, Textfield } from '@digdir/designsystemet-react';
import { Field, FieldArray, useFormikContext } from 'formik';
import relations from '../utils/relations.json';
import { useSearchDatasetsByUri, useSearchDatasetSuggestions } from '../../../hooks/useSearchService';

type TitleSectionProps = {
searchEnv: string;
datasetSeries: DatasetSeries[];
};

export const RelationsSection = ({ searchEnv, datasetSeries }: TitleSectionProps) => {
const { setFieldValue, values, errors } = useFormikContext<Dataset>();
const [searchQuery, setSearchQuery] = useState('');

const getUriList = () => {
return values.references?.map((reference) => reference.source.uri).filter((uri) => uri) ?? [];
};

const { data: searchHits, isLoading: searching } = useSearchDatasetSuggestions(searchEnv, searchQuery);
const { data: selectedValues, isLoading } = useSearchDatasetsByUri(searchEnv, getUriList());

const comboboxOptions = [
...new Map(
[
...(searchHits ?? []),
...(selectedValues ?? []),
...(getUriList() ?? []).map((uri) => {
const foundItem =
searchHits?.find((item) => item.uri === uri) || selectedValues?.find((item) => item.uri === uri);

return {
uri,
title: foundItem?.title ?? null,
};
}),
].map((option) => [option.uri, option]),
).values(),
];

return (
<div>
<Heading
size='sm'
spacing
>
{localization.datasetForm.heading.relations}
</Heading>
<FormContainer>
<FormContainer.Header
title={localization.datasetForm.heading.relationsDataset}
subtitle={localization.datasetForm.helptext.relationsDataset}
/>

<FieldArray name='references'>
{({ remove, push }) => (
<div>
{values.references?.map((_, index) => (
<div key={index}>
<Combobox
label={localization.datasetForm.fieldLabel.relationType}
onValueChange={(value) =>
setFieldValue(`references[${index}].referenceType.code`, value.toString())
}
value={
values.references?.[index]?.referenceType?.code
? [values.references?.[index]?.referenceType?.code]
: []
}
placeholder={`${localization.datasetForm.fieldLabel.choseRelation}...`}
>
<Combobox.Empty>{localization.search.noHits}</Combobox.Empty>
{relations.map((relation) => (
<Combobox.Option
key={relation?.code}
value={relation?.code}
description={`${relation?.uriAsPrefix} (${relation?.uri})`}
>
{getTranslateText(relation?.label)}
</Combobox.Option>
))}
</Combobox>

{!isLoading && (
<Combobox
label={localization.datasetForm.fieldLabel.dataset}
onChange={(input: any) => setSearchQuery(input.target.value)}
onValueChange={(value) => {
setFieldValue(`references.${[index]}.source.uri`, value.toString());
}}
loading={searching}
value={values.references?.[index]?.source?.uri ? [values.references?.[index]?.source?.uri] : []}
placeholder={`${localization.search.search}...`}
>
<Combobox.Empty>{localization.search.noHits}</Combobox.Empty>
{comboboxOptions?.map((dataset) => (
<Combobox.Option
key={dataset.uri}
value={dataset.uri}
>
{dataset?.title ? getTranslateText(dataset?.title) : dataset.uri}
</Combobox.Option>
))}
</Combobox>
)}
<DeleteButton onClick={() => remove(index)}></DeleteButton>
</div>
))}

<AddButton onClick={() => push({ type: { code: '' }, source: { uri: '' } })}></AddButton>
</div>
)}
</FieldArray>
<FormContainer.Header
title={localization.datasetForm.heading.relationDatasetSeries}
subtitle={localization.datasetForm.helptext.relationDatasetSeries}
/>
{datasetSeries && (
<Combobox
label={localization.datasetForm.fieldLabel.datasetSeries}
onValueChange={(value) => setFieldValue('inSeries', value.toString())}
value={values.inSeries ? [values.inSeries] : []}
initialValue={values?.inSeries ? [values?.inSeries] : []}
placeholder={`${localization.search.search}...`}
>
<Combobox.Empty>{localization.search.noHits}</Combobox.Empty>
{datasetSeries.map((dataset) => (
<Combobox.Option
value={dataset.id}
key={dataset.id}
>
{getTranslateText(dataset.title)}
</Combobox.Option>
))}
</Combobox>
)}

<FormContainer.Header
title={localization.datasetForm.heading.relatedResources}
subtitle={localization.datasetForm.helptext.relatedResources}
/>
<FieldArray name='relations'>
{({ push, remove }) => (
<div>
{values.relations?.map((_, index) => (
<div key={index}>
<Field
as={Textfield}
label={localization.title}
name={`relations[${index}].prefLabel.nb`}
/>
<Field
as={Textfield}
label={localization.link}
name={`relations[${index}].uri`}
// @ts-expect-error uri exsists in object relations
error={errors?.relations?.[index].uri}
/>

<DeleteButton onClick={() => remove(index)}></DeleteButton>
</div>
))}

<AddButton
onClick={() =>
push({
prefLabel: { nb: '' },
uri: '',
})
}
></AddButton>
</div>
)}
</FieldArray>
</FormContainer>
</div>
);
};
22 changes: 20 additions & 2 deletions apps/dataset-catalog/components/dataset-form/index.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
'use client';
import { localization, trimObjectWhitespace } from '@catalog-frontend/utils';
import { Button } from '@digdir/designsystemet-react';
import { Dataset, DatasetToBeCreated, ReferenceData } from '@catalog-frontend/types';
import { Dataset, DatasetSeries, DatasetToBeCreated, ReferenceData } from '@catalog-frontend/types';
import { FormLayout, useWarnIfUnsavedChanges } from '@catalog-frontend/ui';
import { Formik, Form } from 'formik';
import { useParams } from 'next/navigation';
Expand All @@ -20,16 +20,25 @@ import { GeographySection } from './components/dataset-form-geography-section';
import { InformationModelSection } from './components/dataset-form-information-model-section';
import { QualifiedAttributionsSection } from './components/dataset-form-qualified-attributions-section';
import { ExampleDataSection } from './components/dataset-form-example-data-section';
import { RelationsSection } from './components/dataset-form-relations-section';

type Props = {
initialValues: DatasetToBeCreated | Dataset;
submitType: 'create' | 'update';
searchEnv: string; // Environment variable to search service
referenceDataEnv: string; // Environment variable to reference data
referenceData: ReferenceData;
datasetSeries: DatasetSeries[];
};

export const DatasetForm = ({ initialValues, submitType, referenceData, searchEnv, referenceDataEnv }: Props) => {
export const DatasetForm = ({
initialValues,
submitType,
referenceData,
searchEnv,
referenceDataEnv,
datasetSeries,
}: Props) => {
const { catalogId, datasetId } = useParams();
const [isDirty, setIsDirty] = useState(false);
const { losThemes, dataThemes, provenanceStatements, datasetTypes, frequencies, languages } = referenceData;
Expand Down Expand Up @@ -196,6 +205,15 @@ export const DatasetForm = ({ initialValues, submitType, referenceData, searchEn
>
<ExampleDataSection referenceDataEnv={referenceDataEnv} />
</FormLayout.Section>
<FormLayout.Section
id='relation-section'
title={localization.datasetForm.heading.relations}
>
<RelationsSection
searchEnv={searchEnv}
datasetSeries={datasetSeries}
/>
</FormLayout.Section>
</FormLayout>
</Form>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export const datasetTemplate = (dataset: Dataset): Dataset => {
landingPage:
dataset.landingPage && dataset?.landingPage?.length > 0 && dataset.landingPage.every((page) => page !== null)
? dataset.landingPage
: [''],
: [],
losThemeList: dataset.theme ? dataset.theme.filter((t) => t.uri && t.uri.includes('/los/')).map((t) => t.uri) : [],
euThemeList: dataset.theme
? dataset.theme.filter((t) => t.uri && t.uri.includes('/data-theme/')).map((t) => t.uri)
Expand Down Expand Up @@ -55,6 +55,9 @@ export const datasetTemplate = (dataset: Dataset): Dataset => {
mediaType: [],
},
],
references: dataset.references ?? [{ source: { uri: '' }, referenceType: { code: '' } }],
relations: dataset.relations ?? [{ uri: '', prefLabel: { nb: '' } }],
inSeries: dataset.inSeries ?? '',
};
};

Expand All @@ -71,7 +74,7 @@ export const datasetToBeCreatedTemplate = (): DatasetToBeCreated => {
en: '',
},
registrationStatus: PublicationStatus.DRAFT,
landingPage: [''],
landingPage: [],
accessRights: { uri: '' },
legalBasisForAccess: [{ uri: '', prefLabel: { nb: '' } }],
legalBasisForProcessing: [{ uri: '', prefLabel: { nb: '' } }],
Expand Down Expand Up @@ -108,5 +111,8 @@ export const datasetToBeCreatedTemplate = (): DatasetToBeCreated => {
mediaType: [],
},
],
references: [{ source: { uri: '' }, referenceType: { code: '' } }],
relations: [{ uri: '', prefLabel: { nb: '' } }],
inSeries: '',
};
};
Loading