Skip to content

Commit

Permalink
feat: add geography combobox in dataset form
Browse files Browse the repository at this point in the history
  • Loading branch information
hegeaal committed Oct 25, 2024
1 parent f555d6e commit 37eea0b
Show file tree
Hide file tree
Showing 17 changed files with 816 additions and 17 deletions.
2 changes: 2 additions & 0 deletions apps/dataset-catalog/app/actions/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ export async function createDataset(values: DatasetToBeCreated, catalogId: strin
],
keyword: values?.keywordList ? transformToLocalizedStrings(values?.keywordList) : '',
concepts: values?.conceptList ? convertListToListOfObjects(values.conceptList, 'uri') : [],
spatial: values?.spatialList ? convertListToListOfObjects(values.spatialList, 'uri') : [],
};
const datasetNoEmptyValues = removeEmptyValues(newDataset);

Expand Down Expand Up @@ -103,6 +104,7 @@ export async function updateDataset(catalogId: string, initialDataset: Dataset,
],
keyword: values?.keywordList ? transformToLocalizedStrings(values?.keywordList) : '',
concepts: values?.conceptList ? convertListToListOfObjects(values.conceptList, 'uri') : [],
spatial: values?.spatialList ? convertListToListOfObjects(values.spatialList, 'uri') : [],
});

const diff = compare(initialDataset, updatedDataset);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {
export default async function EditDatasetPage({ params }: Params) {
const { catalogId, datasetId } = params;
const searchEnv = process.env.FDK_SEARCH_SERVICE_BASE_URI ?? '';
const referenceDataEnv = process.env.FDK_BASE_URI ?? '';
const dataset = await getDatasetById(catalogId, datasetId);
const organization: Organization = await getOrganization(catalogId).then((res) => res.json());

Expand Down Expand Up @@ -69,6 +70,7 @@ export default async function EditDatasetPage({ params }: Params) {
initialValues={dataset}
submitType={'update'}
searchEnv={searchEnv}
referenceDataEnv={referenceDataEnv}
referenceData={referenceData}
></DatasetForm>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ export default async function NewDatasetPage({ params }: Params) {
const { catalogId } = 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 [
losThemesResponse,
Expand Down Expand Up @@ -65,6 +66,7 @@ export default async function NewDatasetPage({ params }: Params) {
submitType={'create'}
referenceData={referenceData}
searchEnv={searchEnv}
referenceDataEnv={referenceDataEnv}
></DatasetForm>
</div>
</>
Expand Down
24 changes: 13 additions & 11 deletions apps/dataset-catalog/app/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Layout, NextAuthProvider } from '@catalog-frontend/ui';
import { Layout, NextAuthProvider, ReactQueryClientProvider } from '@catalog-frontend/ui';
import { localization } from '@catalog-frontend/utils';
import { Metadata } from 'next';

Expand All @@ -12,16 +12,18 @@ const RootLayout = ({ children }: { children: React.ReactNode }) => {
<html lang={localization.getLanguage()}>
<body>
<NextAuthProvider>
<Layout
catalogAdminUrl={process.env.CATALOG_ADMIN_BASE_URI}
fdkRegistrationBaseUrl={`${process.env.CATALOG_PORTAL_BASE_URI}/catalogs`}
adminGuiBaseUrl={process.env.ADMIN_GUI_BASE_URI}
fdkCommunityBaseUrl={process.env.FDK_COMMUNITY_BASE_URI}
fdkBaseUrl={process.env.FDK_BASE_URI}
catalogTitle={localization.catalogType.service}
>
{children}
</Layout>
<ReactQueryClientProvider>
<Layout
catalogAdminUrl={process.env.CATALOG_ADMIN_BASE_URI}
fdkRegistrationBaseUrl={`${process.env.CATALOG_PORTAL_BASE_URI}/catalogs`}
adminGuiBaseUrl={process.env.ADMIN_GUI_BASE_URI}
fdkCommunityBaseUrl={process.env.FDK_COMMUNITY_BASE_URI}
fdkBaseUrl={process.env.FDK_BASE_URI}
catalogTitle={localization.catalogType.service}
>
{children}
</Layout>
</ReactQueryClientProvider>
</NextAuthProvider>
</body>
</html>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ export const ConceptSection = ({ searchEnv }: Props) => {
multiple
value={values.conceptList}
placeholder={localization.datasetForm.helptext.searchConcept}
filter={() => true} // Deactivate filter, handled by backend
>
{suggestions.map((suggestion) => (
<Combobox.Option
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
'use client';
import { FormContainer } from '@catalog-frontend/ui';
import { getTranslateText, localization } from '@catalog-frontend/utils';
import { Combobox, Heading } from '@digdir/designsystemet-react';
import { useCallback, useState } from 'react';
import { useSearchAdministrativeUnits, useSearchAdministrativeUnitsByUri } from '../../hooks/useReferenceDataSearch';
import { useFormikContext } from 'formik';
import { Dataset } from '@catalog-frontend/types';
import { debounce } from 'lodash';

interface Props {
envVariable: string;
}

export const GeographySection = ({ envVariable }: Props) => {
const [searchTerm, setSearchTerm] = useState<string>('');
const { values, setFieldValue } = useFormikContext<Dataset>();

const { data: searchHits, isLoading: isSearching } = useSearchAdministrativeUnits(searchTerm, envVariable);
const { data: selectedValues, isLoading: isLoadingselectedValues } = useSearchAdministrativeUnitsByUri(
values?.spatialList,
envVariable,
);

const debouncedSetSearchTerm = debounce((term: string) => {
setSearchTerm(term);
}, 300);

const handleSearchChange = useCallback((input: any) => {
debouncedSetSearchTerm(input.target.value);
}, []);

const getLocationType = (uri: string): string => {
if (uri.includes('kommune')) return localization.spatial.municipality;
if (uri.includes('fylke')) return localization.spatial.country;
if (uri.includes('nasjon')) return localization.spatial.country;
return '';
};

const comboboxOptions = [
// Combines selectedValues and searchHits, and add uri's for values not found in selectedValues
...new Map(
[
...(selectedValues ?? []),
...(searchHits ?? []),
...(values.spatialList ?? []).map((uri) => ({
uri,
label:
(selectedValues?.find((item) => item.uri === uri) || searchHits?.find((item) => item.uri === uri))?.label ??
null,
})),
].map((spatial) => [spatial.uri, spatial]),
).values(),
];

return (
<div>
<Heading
size='sm'
spacing
>
{localization.datasetForm.heading.geography}
</Heading>
<FormContainer>
<FormContainer.Header
title={localization.datasetForm.heading.spatial}
subtitle={localization.datasetForm.helptext.spatial}
/>
{!isLoadingselectedValues && (
<Combobox
loading={isSearching || isLoadingselectedValues}
onChange={handleSearchChange}
placeholder={`${localization.search.search}...`}
virtual
multiple
onValueChange={(selectedValues) => setFieldValue('spatialList', selectedValues)}
value={values.spatialList || []}
filter={() => true} // Deactivates filter. Filtering is handled in the backend.
>
<Combobox.Empty>
{searchTerm.length < 2
? localization.datasetForm.validation.searchString
: `${localization.search.noHits}...`}
</Combobox.Empty>
{comboboxOptions.map((location) => (
<Combobox.Option
value={location.uri}
key={location.uri}
description={getLocationType(location.uri)}
>
{location.label ? getTranslateText(location.label) : location.uri}
</Combobox.Option>
))}
</Combobox>
)}
<FormContainer.Header
title={localization.datasetForm.heading.temporal}
subtitle={localization.datasetForm.helptext.temporal}
/>
<FormContainer.Header
title={localization.datasetForm.heading.releaseDate}
subtitle={localization.datasetForm.helptext.releaseDate}
/>
<FormContainer.Header
title={localization.datasetForm.heading.language}
subtitle={localization.datasetForm.helptext.language}
/>
</FormContainer>
</div>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { AccessRights, Dataset, DatasetToBeCreated, PublicationStatus } from '@c
import { groupByKeys } from '@catalog-frontend/utils';

export const datasetTemplate = (dataset: Dataset): Dataset => {
console.log(dataset.theme);
return {
id: dataset?.id ?? '',
catalogId: dataset?.catalogId ?? '',
Expand All @@ -25,8 +26,10 @@ export const datasetTemplate = (dataset: Dataset): Dataset => {
dataset.landingPage && dataset?.landingPage?.length > 0 && dataset.landingPage.every((page) => page !== null)
? dataset.landingPage
: [''],
losThemeList: dataset.theme ? dataset.theme.filter((t) => t.uri.includes('/los/')).map((t) => t.uri) : [],
euThemeList: dataset.theme ? dataset.theme.filter((t) => t.uri.includes('/data-theme/')).map((t) => t.uri) : [],
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)
: [],
type: dataset.type ?? '',
keywordList: dataset.keyword ? groupByKeys(dataset.keyword) : { nb: [] },
conceptList: dataset.concepts ? dataset.concepts.map((concept) => concept.uri) : [],
Expand All @@ -39,6 +42,7 @@ export const datasetTemplate = (dataset: Dataset): Dataset => {
hasCompletenessAnnotation: { hasBody: { nb: dataset.hasCompletenessAnnotation?.hasBody?.nb ?? '' } },
hasAccuracyAnnotation: { hasBody: { nb: dataset.hasAccuracyAnnotation?.hasBody?.nb ?? '' } },
hasAvailabilityAnnotation: { hasBody: { nb: dataset.hasAvailabilityAnnotation?.hasBody?.nb ?? '' } },
spatialList: dataset.spatial ? dataset.spatial.map((spatial) => spatial.uri) : [],
};
};

Expand Down Expand Up @@ -74,5 +78,6 @@ export const datasetToBeCreatedTemplate = (): DatasetToBeCreated => {
hasCompletenessAnnotation: { hasBody: { nb: '' } },
hasAccuracyAnnotation: { hasBody: { nb: '' } },
hasAvailabilityAnnotation: { hasBody: { nb: '' } },
spatialList: [],
};
};
5 changes: 4 additions & 1 deletion apps/dataset-catalog/components/dataset-form/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,17 @@ import { TypeSection } from './dataset-form-type-sections';
import { ConceptSection } from './dataset-form-concept-section';
import { ProvenanceSection } from './dataset-form-provenance-section';
import { ContentSection } from './dataset-form-content-section';
import { GeographySection } from './dataset-form-geography-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;
};

export const DatasetForm = ({ initialValues, submitType, referenceData, searchEnv }: Props) => {
export const DatasetForm = ({ initialValues, submitType, referenceData, searchEnv, referenceDataEnv }: Props) => {
const { catalogId, datasetId } = useParams();
const [isDirty, setIsDirty] = useState(false);
const { losThemes, dataThemes, provenanceStatements, datasetTypes, frequencies } = referenceData;
Expand Down Expand Up @@ -106,6 +108,7 @@ export const DatasetForm = ({ initialValues, submitType, referenceData, searchEn
/>
<TypeSection datasetTypes={datasetTypes} />
<ConceptSection searchEnv={searchEnv} />
<GeographySection envVariable={referenceDataEnv} />
<ProvenanceSection data={{ provenanceStatements, frequencies }} />
<ContentSection />
</div>
Expand Down
28 changes: 28 additions & 0 deletions apps/dataset-catalog/hooks/useReferenceDataSearch.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { getAdministrativeUnits, getAdministrativeUnitsByUri } from '@catalog-frontend/data-access';
import { useQuery } from '@tanstack/react-query';

export const useSearchAdministrativeUnits = (searchQuery: string, envVariable: string) => {
return useQuery({
queryKey: ['ADMINISTRATIVE_ENHETER', searchQuery],
queryFn: async () => {
const data = await getAdministrativeUnits(searchQuery, envVariable);
return data;
},
enabled: !!searchQuery,
});
};

export const useSearchAdministrativeUnitsByUri = (uriList: string[] | undefined, envVariable: string) => {
return useQuery({
queryKey: ['ADMINISTRATIVE_ENHETER', uriList],
queryFn: async () => {
if (!uriList || uriList.length === 0) {
return [];
}

const data = await getAdministrativeUnitsByUri(uriList, envVariable);
return data;
},
enabled: Array.isArray(uriList) && uriList.length > 0,
});
};
15 changes: 14 additions & 1 deletion codegen.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@ hooks:
afterOneFileWrite:
- prettier --write
generates:
# Service messages
./libs/data-access/src/lib/strapi/generated/graphql.ts:
schema: https://cms.staging.fellesdatakatalog.digdir.no/graphql
documents: './libs/**/*.graphql'
documents: './libs/data-access/src/lib/strapi/graphql/service-message.graphql'
config:
withHooks: true
withComponent: false
Expand All @@ -14,3 +15,15 @@ generates:
- typescript
- typescript-operations
- typescript-react-apollo

# Reference-data
./libs/data-access/src/lib/reference-data/generated/graphql.ts:
schema: https://raw.githubusercontent.com/Informasjonsforvaltning/fdk-reference-data/refs/heads/main/src/main/resources/graphql/schema.graphqls
documents: './libs/data-access/src/lib/reference-data/graphql/administrative-units.graphql'
config:
withHooks: true
withComponent: false
withHOC: false
plugins:
- typescript
- typescript-operations
Loading

0 comments on commit 37eea0b

Please sign in to comment.