From 865cae3953cf159987ad111fd7dd12c483b60212 Mon Sep 17 00:00:00 2001 From: Birk Johansson Date: Wed, 11 Dec 2024 19:03:22 +0100 Subject: [PATCH] fix: use partialLoadedDisplayAbleModel type --- .../BaseModelSingleSelect.tsx | 8 ++-- .../ModelSingleSelect/ModelSingleSelect.tsx | 43 ++++++++++++++++--- src/types/models.ts | 5 +++ 3 files changed, 47 insertions(+), 9 deletions(-) diff --git a/src/components/metadataFormControls/ModelSingleSelect/BaseModelSingleSelect.tsx b/src/components/metadataFormControls/ModelSingleSelect/BaseModelSingleSelect.tsx index b0a8207d..b60d905b 100644 --- a/src/components/metadataFormControls/ModelSingleSelect/BaseModelSingleSelect.tsx +++ b/src/components/metadataFormControls/ModelSingleSelect/BaseModelSingleSelect.tsx @@ -1,13 +1,13 @@ import i18n from '@dhis2/d2-i18n' import React, { useCallback, useMemo } from 'react' -import { DisplayableModel } from '../../../types/models' +import { DisplayableModel, PartialLoadedDisplayableModel } from '../../../types/models' import { SearchableSingleSelect, SearchableSingleSelectPropTypes, } from '../../SearchableSingleSelect' -const toDisplayOption = (model: DisplayableModel) => ({ +const toDisplayOption = (model: PartialLoadedDisplayableModel) => ({ value: model.id, label: model.displayName || i18n.t('Loading...'), }) @@ -19,14 +19,14 @@ type OwnProps = { noValueOption?: { value: string; label: string } | boolean } -export type BaseModelSingleSelectProps = Omit< +export type BaseModelSingleSelectProps = Omit< SearchableSingleSelectPropTypes, keyof OwnProps | 'options' | 'selected' > & OwnProps /* Simple wrapper component handle generic models with SingleSelect-component. */ -export const BaseModelSingleSelect = ({ +export const BaseModelSingleSelect = ({ available, selected, onChange, diff --git a/src/components/metadataFormControls/ModelSingleSelect/ModelSingleSelect.tsx b/src/components/metadataFormControls/ModelSingleSelect/ModelSingleSelect.tsx index 9bed173d..d8f756fd 100644 --- a/src/components/metadataFormControls/ModelSingleSelect/ModelSingleSelect.tsx +++ b/src/components/metadataFormControls/ModelSingleSelect/ModelSingleSelect.tsx @@ -1,10 +1,12 @@ -import React, { useMemo, useState } from 'react' -import { useInfiniteQuery } from 'react-query' +import React, { useEffect, useMemo, useState } from 'react' +import { useInfiniteQuery, useQuery } from 'react-query' import { useDebouncedCallback } from 'use-debounce' import { useBoundResourceQueryFn } from '../../../lib/query/useBoundQueryFn' import { PlainResourceQuery } from '../../../types' import { PagedResponse } from '../../../types/generated' -import { DisplayableModel } from '../../../types/models' +import { + PartialLoadedDisplayableModel +} from '../../../types/models' import { BaseModelSingleSelect, BaseModelSingleSelectProps, @@ -20,7 +22,9 @@ const defaultQuery = { }, } satisfies Omit -export type ModelSingleSelectProps = Omit< +export type ModelSingleSelectProps< + TModel extends PartialLoadedDisplayableModel = PartialLoadedDisplayableModel +> = Omit< BaseModelSingleSelectProps, | 'available' | 'onFilterChange' @@ -35,7 +39,9 @@ export type ModelSingleSelectProps TModel[] } -export const ModelSingleSelect = ({ +export const ModelSingleSelect = < + TModel extends PartialLoadedDisplayableModel +>({ selected, query, transform, @@ -43,6 +49,7 @@ export const ModelSingleSelect = ({ }: ModelSingleSelectProps) => { const queryFn = useBoundResourceQueryFn() const [searchTerm, setSearchTerm] = useState('') + const onChange = baseModelSingleSelectProps.onChange const searchFilter = `identifiable:token:${searchTerm}` const filter: string[] = searchTerm ? [searchFilter] : [] @@ -68,6 +75,23 @@ export const ModelSingleSelect = ({ firstPage.pager.prevPage ? firstPage.pager.page - 1 : undefined, }) + const shouldFetchSelected = selected && selected.displayName === undefined + // if we just have the ID - fetch the displayName + const selectedQuery = useQuery({ + queryKey: [ + { + resource: query.resource, + id: selected?.id, + params: { + fields: queryObject.params.fields, + order: queryObject.params.order, + }, + }, + ], + queryFn: queryFn, + enabled: shouldFetchSelected, + }) + const allDataMap = useMemo(() => { const flatData = queryResult.data?.pages.flatMap((page) => page[modelName]) ?? [] @@ -78,6 +102,15 @@ export const ModelSingleSelect = ({ return transform ? transform(allDataMap) : allDataMap }, [allDataMap, transform]) + useEffect(() => { + if (!selectedQuery.data || selected?.displayName !== undefined) { + return + } + // if we had to fetch the selected model, call the onChange + // to update store with the full model + onChange?.(selectedQuery.data) + }, [selectedQuery.data, selected, onChange]) + const handleFilterChange = useDebouncedCallback(({ value }) => { if (value != undefined) { setSearchTerm(value) diff --git a/src/types/models.ts b/src/types/models.ts index 87702648..5754c339 100644 --- a/src/types/models.ts +++ b/src/types/models.ts @@ -6,3 +6,8 @@ export type DisplayableModel = { id: string displayName: string } + +export type PartialLoadedDisplayableModel = { + id: string + displayName?: string +}