From 66f3843ef9c65ee7bddede159ae6f1f865c13a9c Mon Sep 17 00:00:00 2001 From: Flaminia Cavallo Date: Tue, 3 Dec 2024 08:30:55 +0100 Subject: [PATCH 1/5] feat: introduce route path --- src/app/routes/Router.tsx | 2 +- src/lib/constants/sections.ts | 3 ++- src/lib/routeUtils/routePaths.ts | 3 ++- src/types/section.ts | 1 + 4 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/app/routes/Router.tsx b/src/app/routes/Router.tsx index 02e4b343..6d3df47c 100644 --- a/src/app/routes/Router.tsx +++ b/src/app/routes/Router.tsx @@ -74,7 +74,7 @@ function createSectionLazyRouteFunction( return async () => { try { return await import( - `../../pages/${section.namePlural}/${componentFileName}` + `../../pages/${section.routeName || section.namePlural}/${componentFileName}` ) } catch (e) { // means the component is not implemented yet diff --git a/src/lib/constants/sections.ts b/src/lib/constants/sections.ts index e3e77a3d..4ff5629d 100644 --- a/src/lib/constants/sections.ts +++ b/src/lib/constants/sections.ts @@ -88,7 +88,8 @@ export const SCHEMA_SECTIONS = { // @ts-expect-error temporary route for testing dataSetWIP: { name: SchemaName.dataSet, - namePlural: 'dataSetsWip', + routeName: 'dataSetsWip', + namePlural: 'dataSets', title: i18n.t('Data set'), titlePlural: i18n.t('Data sets'), parentSectionKey: 'dataSet', diff --git a/src/lib/routeUtils/routePaths.ts b/src/lib/routeUtils/routePaths.ts index 55b21728..75543861 100644 --- a/src/lib/routeUtils/routePaths.ts +++ b/src/lib/routeUtils/routePaths.ts @@ -13,7 +13,8 @@ export const getSectionPath = (section: Section | string) => { if (typeof section === 'string') { return section } - return section.namePlural + return section.routeName || section.namePlural + } export const getSectionNewPath = (section: Section | string) => { diff --git a/src/types/section.ts b/src/types/section.ts index 2a5c95e7..d4c3c290 100644 --- a/src/types/section.ts +++ b/src/types/section.ts @@ -22,6 +22,7 @@ export type NonSchemaSection = SectionBase & { export type OverviewSection = SectionBase & { componentName: string + routeName?: string } export type ModelSection = SchemaSection | NonSchemaSection From 9da2cdf43db4505556ba7914fc288b433bf1227b Mon Sep 17 00:00:00 2001 From: Flaminia Cavallo Date: Wed, 4 Dec 2024 11:09:56 +0100 Subject: [PATCH 2/5] feat: add setup and required fields to data set form --- i18n/en.pot | 152 +++++++++--------- src/app/routes/Router.tsx | 4 +- .../SearchableSingleSelect.tsx | 33 ++-- .../CategoryComboSelect.tsx | 8 +- ...ionsQuery.ts => useCategoryCombosQuery.ts} | 2 +- ...ery.ts => useInitialCategoryComboQuery.ts} | 29 ++-- .../BaseModelSingleSelect.tsx | 2 +- .../ModelSingleSelect/ModelSingleSelect.tsx | 7 +- src/lib/optionSet/useOptionSetsQuery.ts | 17 +- src/lib/routeUtils/routePaths.ts | 1 - .../dataSetsWip/form/CategoryComboField.tsx | 47 ++++++ .../dataSetsWip/form/DataSetFormContents.tsx | 16 +- .../dataSetsWip/form/PeriodTypeField.tsx | 58 +++++++ .../dataSetsWip/form/dataSetFormSchema.ts | 13 +- src/types/section.ts | 2 +- 15 files changed, 267 insertions(+), 124 deletions(-) rename src/components/metadataFormControls/CategoryComboSelect/{useOptionsQuery.ts => useCategoryCombosQuery.ts} (98%) rename src/components/metadataFormControls/CategoryComboSelect/{useInitialOptionQuery.ts => useInitialCategoryComboQuery.ts} (52%) create mode 100644 src/pages/dataSetsWip/form/CategoryComboField.tsx create mode 100644 src/pages/dataSetsWip/form/PeriodTypeField.tsx diff --git a/i18n/en.pot b/i18n/en.pot index 3bcac785..100f2586 100644 --- a/i18n/en.pot +++ b/i18n/en.pot @@ -5,8 +5,8 @@ msgstr "" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1)\n" -"POT-Creation-Date: 2024-12-10T14:51:58.114Z\n" -"PO-Revision-Date: 2024-12-10T14:51:58.114Z\n" +"POT-Creation-Date: 2024-12-18T13:07:04.607Z\n" +"PO-Revision-Date: 2024-12-18T13:07:04.607Z\n" msgid "schemas" msgstr "schemas" @@ -48,8 +48,8 @@ msgstr "No menu items found for" msgid "Overview" msgstr "Overview" -msgid "Search for menu items" -msgstr "Search for menu items" +msgid "Search menu items" +msgstr "Search menu items" msgid "Search icons" msgstr "Search icons" @@ -96,8 +96,8 @@ msgstr "" msgid "Attributes" msgstr "Attributes" -msgid "Set up information for the attributes assigned to {{modelName}}" -msgstr "Set up information for the attributes assigned to {{modelName}}" +msgid "Set up information for the attributes assigned to {{modelName}}." +msgstr "Set up information for the attributes assigned to {{modelName}}." msgid "Code" msgstr "Code" @@ -120,8 +120,8 @@ msgstr "Name" msgid "Select organisation units" msgstr "Select organisation units" -msgid "Often used in reports where space is limited" -msgstr "Often used in reports where space is limited" +msgid "Often used in reports where space is limited." +msgstr "Often used in reports where space is limited." msgid "Short name" msgstr "Short name" @@ -147,6 +147,9 @@ msgstr "Filter data elements" msgid "Filter legend sets" msgstr "Filter legend sets" +msgid "Loading..." +msgstr "Loading..." + msgid "Filter available items" msgstr "Filter available items" @@ -936,21 +939,21 @@ msgstr "Explain the purpose of this category option group." msgid "Data configuration" msgstr "Data configuration" -msgid "Choose how this category option group will be used to capture and analyze" -msgstr "Choose how this category option group will be used to capture and analyze" +msgid "" +"Choose how this category option group will be used to capture and analyze " +"data." +msgstr "" +"Choose how this category option group will be used to capture and analyze " +"data." msgid "Use as data dimension" msgstr "Use as data dimension" -msgid "" -"Category option group will be available to the analytics as another " -"dimension" -msgstr "" -"Category option group will be available to the analytics as another " -"dimension" +msgid "Make available to analytics apps as a selectable dimension." +msgstr "Make available to analytics apps as a selectable dimension." -msgid "Choose the category options to include in this category option group." -msgstr "Choose the category options to include in this category option group." +msgid "Choose the options to include in this category." +msgstr "Choose the options to include in this category." msgid "Available category options" msgstr "Available category options" @@ -1001,14 +1004,21 @@ msgstr "" " It is strongly discouraged to have more than one Category " "combination with the same categories." -msgid "Choose how this category combo will be used to capture and analyze data." -msgstr "Choose how this category combo will be used to capture and analyze data." +msgid "Set up the basic information for this category combination." +msgstr "Set up the basic information for this category combination." + +msgid "" +"Choose how this category combination will be used to capture and analyze " +"data." +msgstr "" +"Choose how this category combination will be used to capture and analyze " +"data." msgid "Skip category total in reports" msgstr "Skip category total in reports" -msgid "Choose the categories to include in this category combo." -msgstr "Choose the categories to include in this category combo." +msgid "Choose the categories to include in this category combination." +msgstr "Choose the categories to include in this category combination." msgid "At least one category is required" msgstr "At least one category is required" @@ -1020,6 +1030,9 @@ msgstr "" "The number of generated category option combinations exceeds the limit of " "{{limit}}" +msgid "Set up the basic information for this category option combination." +msgstr "Set up the basic information for this category option combination." + msgid "Set up the basic information for this category option group set." msgstr "Set up the basic information for this category option group set." @@ -1028,20 +1041,10 @@ msgstr "Explain the purpose of this category option group set." msgid "" "Choose how this category option group set will be used to capture and " -"analyze" +"analyze data." msgstr "" "Choose how this category option group set will be used to capture and " -"analyze" - -msgid "" -"Category option group set will be available to the analytics as another " -"dimension" -msgstr "" -"Category option group set will be available to the analytics as another " -"dimension" - -msgid "Category option Groups" -msgstr "Category option Groups" +"analyze data." msgid "" "Choose the category option groups to include in this category option group " @@ -1065,11 +1068,11 @@ msgstr "Filter selected category option groups" msgid "Set up the basic information for this category option group." msgstr "Set up the basic information for this category option group." -msgid "Choose how this category option will be used to capture and analyze" -msgstr "Choose how this category option will be used to capture and analyze" +msgid "Choose how this category option will be used to capture and analyze data." +msgstr "Choose how this category option will be used to capture and analyze data." -msgid "Choose the category options to include in this category." -msgstr "Choose the category options to include in this category." +msgid "Choose the options to include in this category option group." +msgstr "Choose the options to include in this category option group." msgid "Set up the basic information for this category option." msgstr "Set up the basic information for this category option." @@ -1081,10 +1084,10 @@ msgid "Availability configuration" msgstr "Availability configuration" msgid "" -"Choose when, and for which organisation units this category option will be " +"Choose when, and for which organisation units, this category option will be " "available." msgstr "" -"Choose when, and for which organisation units this category option will be " +"Choose when, and for which organisation units, this category option will be " "available." msgid "Form name should not exceed 230 characters" @@ -1108,27 +1111,33 @@ msgstr "Data dimension" msgid "Selected data element groups" msgstr "Selected data element groups" -msgid "Set up the information for this data element group" -msgstr "Set up the information for this data element group" +msgid "Set up the information for this data element group set." +msgstr "Set up the information for this data element group set." -msgid "Explain the purpose of this data element group." -msgstr "Explain the purpose of this data element group." +msgid "Explain the purpose of this data element group set." +msgstr "Explain the purpose of this data element group set." msgid "Selected data elements" msgstr "Selected data elements" +msgid "Set up the information for this data element group." +msgstr "Set up the information for this data element group." + +msgid "Explain the purpose of this data element group." +msgstr "Explain the purpose of this data element group." + msgid "Create data element" msgstr "Create data element" -msgid "Choose how this data element is disaggregated" -msgstr "Choose how this data element is disaggregated" - msgid "The default way to aggregate this data element in analytics." msgstr "The default way to aggregate this data element in analytics." msgid "Disabled for the selected value type." msgstr "Disabled for the selected value type." +msgid "Choose how this data element is disaggregated." +msgstr "Choose how this data element is disaggregated." + msgid "Color and icon" msgstr "Color and icon" @@ -1160,17 +1169,17 @@ msgstr "Selected legends" msgid "Option set comment" msgstr "Option set comment" -msgid "Choose a set of predefined comment for data entry" -msgstr "Choose a set of predefined comment for data entry" +msgid "Choose a set of predefined comments for data entry." +msgstr "Choose a set of predefined comments for data entry." -msgid "Choose a set of predefined options for data entry" -msgstr "Choose a set of predefined options for data entry" +msgid "Choose a set of predefined options for data entry." +msgstr "Choose a set of predefined options for data entry." msgid "Url" msgstr "Url" -msgid "A web link that provides extra information" -msgstr "A web link that provides extra information" +msgid "A web link that provides extra information." +msgstr "A web link that provides extra information." msgid "The type of data that will be recorded." msgstr "The type of data that will be recorded." @@ -1185,8 +1194,8 @@ msgstr "" msgid "Store zero data values" msgstr "Store zero data values" -msgid "Set up the information for this data element" -msgstr "Set up the information for this data element" +msgid "Set up the information for this data element." +msgstr "Set up the information for this data element." msgid "Explain the purpose of this data element." msgstr "Explain the purpose of this data element." @@ -1201,29 +1210,25 @@ msgid "Legend set" msgstr "Legend set" msgid "" -"Visualize values for this data element in Analytics app. Multiple legendSet " +"Visualize values for this data element in Analytics app. Multiple legends " "can be applied." msgstr "" -"Visualize values for this data element in Analytics app. Multiple legendSet " +"Visualize values for this data element in Analytics app. Multiple legends " "can be applied." msgid "Aggregation levels" msgstr "Aggregation levels" msgid "" -"By default, the aggregation will start at the lowest assigned organisation " -"unit. If you for example select \"Chiefdom\", it means that \"Chiefdom\", " -"\"District\" and \"National\" aggregates use \"Chiefdom\" (the highest " -"aggregation level available) as the data source, and PHU data will not be " -"included. PHU will still be available for the PHU level, but not included " -"in the aggregations to the levels above." +"By default, aggregation starts at the lowest assigned organisation unit. " +"You can override this by choosing a different level. Choosing a higher " +"level means that data from lower levels will not be included in the " +"aggregation." msgstr "" -"By default, the aggregation will start at the lowest assigned organisation " -"unit. If you for example select \"Chiefdom\", it means that \"Chiefdom\", " -"\"District\" and \"National\" aggregates use \"Chiefdom\" (the highest " -"aggregation level available) as the data source, and PHU data will not be " -"included. PHU will still be available for the PHU level, but not included " -"in the aggregations to the levels above." +"By default, aggregation starts at the lowest assigned organisation unit. " +"You can override this by choosing a different level. Choosing a higher " +"level means that data from lower levels will not be included in the " +"aggregation." msgid "Set up the basic information for this data set." msgstr "Set up the basic information for this data set." @@ -1234,6 +1239,12 @@ msgstr "Configure data elements" msgid "Choose what data is collected for this data set." msgstr "Choose what data is collected for this data set." +msgid "Data set disaggregation" +msgstr "Data set disaggregation" + +msgid "Choose an optional category combination to disaggregate the entire data set." +msgstr "Choose an optional category combination to disaggregate the entire data set." + msgid "Configure data entry periods" msgstr "Configure data entry periods" @@ -1328,9 +1339,6 @@ msgstr "Contact person" msgid "Address" msgstr "Address" -msgid "A web link that provides extra information." -msgstr "A web link that provides extra information." - msgid "Location" msgstr "Location" diff --git a/src/app/routes/Router.tsx b/src/app/routes/Router.tsx index 6d3df47c..cf1c28b9 100644 --- a/src/app/routes/Router.tsx +++ b/src/app/routes/Router.tsx @@ -74,7 +74,9 @@ function createSectionLazyRouteFunction( return async () => { try { return await import( - `../../pages/${section.routeName || section.namePlural}/${componentFileName}` + `../../pages/${ + section.routeName || section.namePlural + }/${componentFileName}` ) } catch (e) { // means the component is not implemented yet diff --git a/src/components/SearchableSingleSelect/SearchableSingleSelect.tsx b/src/components/SearchableSingleSelect/SearchableSingleSelect.tsx index 6d5d9f21..aaccd225 100644 --- a/src/components/SearchableSingleSelect/SearchableSingleSelect.tsx +++ b/src/components/SearchableSingleSelect/SearchableSingleSelect.tsx @@ -49,7 +49,7 @@ type OnChange = ({ selected }: { selected: string }) => void type OnFilterChange = ({ value }: { value: string }) => void export interface SearchableSingleSelectPropTypes { onChange: OnChange - onFilterChange: OnFilterChange + onFilterChange?: OnFilterChange onEndReached?: () => void onRetryClick: () => void dense?: boolean @@ -65,6 +65,7 @@ export interface SearchableSingleSelectPropTypes { showAllOption?: boolean onBlur?: () => void onFocus?: () => void + searchable?: boolean } export const SearchableSingleSelect = ({ @@ -85,13 +86,15 @@ export const SearchableSingleSelect = ({ selected, showAllOption, showEndLoader, + searchable = true, }: SearchableSingleSelectPropTypes) => { const [loadingSpinnerRef, setLoadingSpinnerRef] = useState() const { liveValue: filter, setValue: setFilterValue } = useDebouncedState({ initialValue: '', - onSetDebouncedValue: (value: string) => onFilterChange({ value }), + onSetDebouncedValue: (value: string) => onFilterChange && onFilterChange({ value }) , + }) useEffect(() => { @@ -137,18 +140,22 @@ export const SearchableSingleSelect = ({ onFocus={onFocus} dense={dense} > -
-
- setFilterValue(value ?? '')} - placeholder={i18n.t('Filter options')} - type="search" - /> + {searchable && ( +
+
+ + setFilterValue(value ?? '') + } + placeholder={i18n.t('Filter options')} + type="search" + /> +
-
+ )} {withAllOptions.map(({ value, label }) => ( diff --git a/src/components/metadataFormControls/CategoryComboSelect/CategoryComboSelect.tsx b/src/components/metadataFormControls/CategoryComboSelect/CategoryComboSelect.tsx index 4207a08d..58aee015 100644 --- a/src/components/metadataFormControls/CategoryComboSelect/CategoryComboSelect.tsx +++ b/src/components/metadataFormControls/CategoryComboSelect/CategoryComboSelect.tsx @@ -2,8 +2,8 @@ import i18n from '@dhis2/d2-i18n' import React, { forwardRef } from 'react' import { ModelSingleSelectLegacy } from '../ModelSingleSelect' import type { ModelSingleSelectLegacyProps } from '../ModelSingleSelect' -import { useInitialOptionQuery } from './useInitialOptionQuery' -import { useOptionsQuery } from './useOptionsQuery' +import { useInitialCategoryComboQuery } from './useInitialCategoryComboQuery' +import { useCategoryCombosQuery } from './useCategoryCombosQuery' type CategoryComboSelectProps = Omit< ModelSingleSelectLegacyProps, @@ -30,8 +30,8 @@ export const CategoryComboSelect = forwardRef(function CategoryComboSelect( required={required} invalid={invalid} disabled={disabled} - useInitialOptionQuery={useInitialOptionQuery} - useOptionsQuery={useOptionsQuery} + useInitialOptionQuery={useInitialCategoryComboQuery} + useOptionsQuery={useCategoryCombosQuery} placeholder={placeholder} showAllOption={showAllOption} onChange={onChange} diff --git a/src/components/metadataFormControls/CategoryComboSelect/useOptionsQuery.ts b/src/components/metadataFormControls/CategoryComboSelect/useCategoryCombosQuery.ts similarity index 98% rename from src/components/metadataFormControls/CategoryComboSelect/useOptionsQuery.ts rename to src/components/metadataFormControls/CategoryComboSelect/useCategoryCombosQuery.ts index 506b22d7..0c5e942f 100644 --- a/src/components/metadataFormControls/CategoryComboSelect/useOptionsQuery.ts +++ b/src/components/metadataFormControls/CategoryComboSelect/useCategoryCombosQuery.ts @@ -44,7 +44,7 @@ const DEFAULT_CATEGORY_SELECT_OPTION = { label: DEFAULT_CATEGORY_COMBO.displayName, } -export function useOptionsQuery() { +export function useCategoryCombosQuery() { const [loadedOptions, setLoadedOptions] = useState([]) // The gist doesn't include the `isDefault` value, need to use `useDataQuery` const queryResult = useDataQuery( diff --git a/src/components/metadataFormControls/CategoryComboSelect/useInitialOptionQuery.ts b/src/components/metadataFormControls/CategoryComboSelect/useInitialCategoryComboQuery.ts similarity index 52% rename from src/components/metadataFormControls/CategoryComboSelect/useInitialOptionQuery.ts rename to src/components/metadataFormControls/CategoryComboSelect/useInitialCategoryComboQuery.ts index b9795def..48a02d8f 100644 --- a/src/components/metadataFormControls/CategoryComboSelect/useInitialOptionQuery.ts +++ b/src/components/metadataFormControls/CategoryComboSelect/useInitialCategoryComboQuery.ts @@ -8,7 +8,7 @@ type InitialCategoryComboQueryResult = { categoryCombo: FilteredCategoryCombo } -const INITIAL_OPTION_QUERY = { +const INITIAL_CATEGORY_COMBO_QUERY = { categoryCombo: { resource: 'categoryCombos', id: (variables: Record) => variables.id, @@ -18,7 +18,7 @@ const INITIAL_OPTION_QUERY = { }, } -export function useInitialOptionQuery({ +export function useInitialCategoryComboQuery({ selected, onComplete, }: { @@ -26,15 +26,18 @@ export function useInitialOptionQuery({ selected?: string }) { const initialSelected = useRef(selected) - return useDataQuery(INITIAL_OPTION_QUERY, { - lazy: - !initialSelected.current || - initialSelected.current === DEFAULT_CATEGORY_COMBO.id, - variables: { id: selected }, - onComplete: (data) => { - const categoryCombo = data.categoryCombo - const { id: value, displayName: label } = categoryCombo - onComplete({ value, label }) - }, - }) + return useDataQuery( + INITIAL_CATEGORY_COMBO_QUERY, + { + lazy: + !initialSelected.current || + initialSelected.current === DEFAULT_CATEGORY_COMBO.id, + variables: { id: selected }, + onComplete: (data) => { + const categoryCombo = data.categoryCombo + const { id: value, displayName: label } = categoryCombo + onComplete({ value, label }) + }, + } + ) } diff --git a/src/components/metadataFormControls/ModelSingleSelect/BaseModelSingleSelect.tsx b/src/components/metadataFormControls/ModelSingleSelect/BaseModelSingleSelect.tsx index de78dfb0..978204ec 100644 --- a/src/components/metadataFormControls/ModelSingleSelect/BaseModelSingleSelect.tsx +++ b/src/components/metadataFormControls/ModelSingleSelect/BaseModelSingleSelect.tsx @@ -39,7 +39,7 @@ export const BaseModelSingleSelect = < const { allModelsMap, allSingleSelectOptions } = useMemo(() => { const allModelsMap = new Map(available.map((o) => [o.id, o])) // due to pagination, the selected model might not be in the available list, so add it - if (selected && !allModelsMap.get(selected.id)) { + if (selected && selected.id && !allModelsMap.get(selected.id)) { allModelsMap.set(selected.id, selected) } const allSingleSelectOptions = Array.from(allModelsMap).map( diff --git a/src/components/metadataFormControls/ModelSingleSelect/ModelSingleSelect.tsx b/src/components/metadataFormControls/ModelSingleSelect/ModelSingleSelect.tsx index 8f0550ed..34187957 100644 --- a/src/components/metadataFormControls/ModelSingleSelect/ModelSingleSelect.tsx +++ b/src/components/metadataFormControls/ModelSingleSelect/ModelSingleSelect.tsx @@ -68,13 +68,14 @@ export const ModelSingleSelect = < queryFn: queryFn>, keepPreviousData: true, getNextPageParam: (lastPage) => - lastPage.pager.nextPage ? lastPage.pager.page + 1 : undefined, + lastPage.pager?.nextPage ? lastPage.pager.page + 1 : undefined, getPreviousPageParam: (firstPage) => - firstPage.pager.prevPage ? firstPage.pager.page - 1 : undefined, + firstPage.pager?.prevPage ? firstPage.pager.page - 1 : undefined, staleTime: 60 * 1000, }) - const shouldFetchSelected = !!selected && selected.displayName === undefined + const shouldFetchSelected = + !!selected && selected.displayName === undefined && !!selected.id // if we just have the ID - fetch the displayName const selectedQuery = useQuery({ queryKey: [ diff --git a/src/lib/optionSet/useOptionSetsQuery.ts b/src/lib/optionSet/useOptionSetsQuery.ts index a719d2ed..57858069 100644 --- a/src/lib/optionSet/useOptionSetsQuery.ts +++ b/src/lib/optionSet/useOptionSetsQuery.ts @@ -10,7 +10,7 @@ type OptionSetQueryResult = { } } -const CATEGORY_COMBOS_QUERY = { +const OPTION_SETS_QUERY = { optionSets: { resource: 'optionSets', params: (variables: Record) => { @@ -35,15 +35,12 @@ const CATEGORY_COMBOS_QUERY = { export function useOptionSetsQuery() { const [loadedOptions, setLoadedOptions] = useState([]) - const queryResult = useDataQuery( - CATEGORY_COMBOS_QUERY, - { - variables: { - page: 1, - filter: '', - }, - } - ) + const queryResult = useDataQuery(OPTION_SETS_QUERY, { + variables: { + page: 1, + filter: '', + }, + }) const { data } = queryResult // Must be done in `useEffect` and not in `onComplete`, as `onComplete` diff --git a/src/lib/routeUtils/routePaths.ts b/src/lib/routeUtils/routePaths.ts index 75543861..b1f1a466 100644 --- a/src/lib/routeUtils/routePaths.ts +++ b/src/lib/routeUtils/routePaths.ts @@ -14,7 +14,6 @@ export const getSectionPath = (section: Section | string) => { return section } return section.routeName || section.namePlural - } export const getSectionNewPath = (section: Section | string) => { diff --git a/src/pages/dataSetsWip/form/CategoryComboField.tsx b/src/pages/dataSetsWip/form/CategoryComboField.tsx new file mode 100644 index 00000000..49e280c5 --- /dev/null +++ b/src/pages/dataSetsWip/form/CategoryComboField.tsx @@ -0,0 +1,47 @@ +import i18n from '@dhis2/d2-i18n' +import { Field } from '@dhis2/ui' +import React from 'react' +import { useField } from 'react-final-form' +import { ModelSingleSelect } from '../../../components/metadataFormControls/ModelSingleSelect' +import { DEFAULT_CATEGORY_COMBO } from '../../../lib' + +export function CategoryComboField() { + const { input, meta } = useField('categoryCombo') + + const CATEGORY_COMBOS_QUERY = { + resource: 'categoryCombos', + params: { + filter: ['dataDimensionType:eq:ATTRIBUTE'], + }, + } + + const DEFAULT_CATEGORY_SELECT_OPTION = { + id: DEFAULT_CATEGORY_COMBO.id, + displayName: DEFAULT_CATEGORY_COMBO.displayName, + } + + return ( + + { + input.onChange(selected) + }} + transform={(catCombos) => [ + ...catCombos, + DEFAULT_CATEGORY_SELECT_OPTION, + ]} + /> + + ) +} diff --git a/src/pages/dataSetsWip/form/DataSetFormContents.tsx b/src/pages/dataSetsWip/form/DataSetFormContents.tsx index d4fe1660..52528994 100644 --- a/src/pages/dataSetsWip/form/DataSetFormContents.tsx +++ b/src/pages/dataSetsWip/form/DataSetFormContents.tsx @@ -19,13 +19,15 @@ import { useSyncSelectedSectionWithScroll, } from '../../../lib' import { DataSetFormDescriptor } from './formDescriptor' +import { CategoryComboField } from './CategoryComboField' +import { PeriodTypeField } from './PeriodTypeField' +import { ColorAndIconField } from '../../dataElements/fields' const section = SECTIONS_MAP.dataSet export const DataSetFormContents = () => { const descriptor = useSectionedFormContext() useSyncSelectedSectionWithScroll() - const [selectedSection] = useSelectedSection() return ( <> @@ -42,6 +44,7 @@ export const DataSetFormContents = () => { + @@ -60,6 +63,16 @@ export const DataSetFormContents = () => { }} /> +
+ + {i18n.t('Data set disaggregation')} + + + {i18n.t( + 'Choose an optional category combination to disaggregate the entire data set.' + )} + + { 'Choose for what time periods data can be entered for this data set' )} + , + }) + + const options = useMemo(() => { + return data + ? data.periodTypes.map((period) => ({ + label: i18n.t(period.name), + value: period.name, + })) + : [] + }, [data]) + + return ( + + input.onChange(selected)} + searchable={false} + options={options} + loading={isLoading} + onRetryClick={refetch} + showEndLoader={false} + /> + + ) +} diff --git a/src/pages/dataSetsWip/form/dataSetFormSchema.ts b/src/pages/dataSetsWip/form/dataSetFormSchema.ts index 0f8dd16f..b87838ee 100644 --- a/src/pages/dataSetsWip/form/dataSetFormSchema.ts +++ b/src/pages/dataSetsWip/form/dataSetFormSchema.ts @@ -1,5 +1,9 @@ import { z } from 'zod' -import { getDefaults, modelFormSchemas } from '../../../lib' +import { + DEFAULT_CATEGORY_COMBO, + getDefaults, + modelFormSchemas, +} from '../../../lib' import { createFormValidate } from '../../../lib/form/validate' const { withAttributeValues, identifiable, style, referenceCollection } = @@ -10,10 +14,13 @@ export const dataSetFormSchema = identifiable .extend({ id: z.string().optional(), code: z.string().trim().optional(), - description: z.string().trim().optional(), + description: z.string().trim().max(2000).optional(), style, dataElements: referenceCollection.default([]), - // categoryCombo: z.object({ id: z.string() }), + categoryCombo: z + .object({ id: z.string() }) + .default({ id: DEFAULT_CATEGORY_COMBO.id }), + periodType: z.string().default('Monthly'), }) export const initialValues = getDefaults(dataSetFormSchema) diff --git a/src/types/section.ts b/src/types/section.ts index d4c3c290..6bdfcaa6 100644 --- a/src/types/section.ts +++ b/src/types/section.ts @@ -6,6 +6,7 @@ export interface SectionBase { namePlural: string titlePlural: string title: string + routeName?: string } // SchemaSection is a section that can be mapped directly to a schema by the name @@ -22,7 +23,6 @@ export type NonSchemaSection = SectionBase & { export type OverviewSection = SectionBase & { componentName: string - routeName?: string } export type ModelSection = SchemaSection | NonSchemaSection From 07b12a33814916af9eff35e37e769053898f1f9b Mon Sep 17 00:00:00 2001 From: Flaminia Cavallo Date: Thu, 19 Dec 2024 14:25:32 +0100 Subject: [PATCH 3/5] feat: translate period types --- i18n/en.pot | 67 ++++++++++++++++++- .../SearchableSingleSelect.tsx | 4 +- .../dataSetsWip/form/PeriodTypeField.tsx | 32 +++++++-- 3 files changed, 94 insertions(+), 9 deletions(-) diff --git a/i18n/en.pot b/i18n/en.pot index 100f2586..bb989147 100644 --- a/i18n/en.pot +++ b/i18n/en.pot @@ -5,8 +5,8 @@ msgstr "" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1)\n" -"POT-Creation-Date: 2024-12-18T13:07:04.607Z\n" -"PO-Revision-Date: 2024-12-18T13:07:04.607Z\n" +"POT-Creation-Date: 2024-12-19T13:21:57.978Z\n" +"PO-Revision-Date: 2024-12-19T13:21:57.978Z\n" msgid "schemas" msgstr "schemas" @@ -1269,6 +1269,69 @@ msgstr "Advanced options" msgid "These options are used for advanced data set configurations." msgstr "These options are used for advanced data set configurations." +msgid "BiMonthly" +msgstr "BiMonthly" + +msgid "BiWeekly" +msgstr "BiWeekly" + +msgid "Daily" +msgstr "Daily" + +msgid "FinancialApril" +msgstr "FinancialApril" + +msgid "FinancialJuly" +msgstr "FinancialJuly" + +msgid "FinancialNov" +msgstr "FinancialNov" + +msgid "FinancialOct" +msgstr "FinancialOct" + +msgid "Monthly" +msgstr "Monthly" + +msgid "Quarterly" +msgstr "Quarterly" + +msgid "QuarterlyNov" +msgstr "QuarterlyNov" + +msgid "SixMonthlyApril" +msgstr "SixMonthlyApril" + +msgid "SixMonthlyNov" +msgstr "SixMonthlyNov" + +msgid "SixMonthly" +msgstr "SixMonthly" + +msgid "TwoYearly" +msgstr "TwoYearly" + +msgid "Weekly" +msgstr "Weekly" + +msgid "WeeklySaturday" +msgstr "WeeklySaturday" + +msgid "WeeklySunday" +msgstr "WeeklySunday" + +msgid "WeeklyThursday" +msgstr "WeeklyThursday" + +msgid "WeeklyWednesday" +msgstr "WeeklyWednesday" + +msgid "Yearly" +msgstr "Yearly" + +msgid "Something went wrong" +msgstr "Something went wrong" + msgid "Setup" msgstr "Setup" diff --git a/src/components/SearchableSingleSelect/SearchableSingleSelect.tsx b/src/components/SearchableSingleSelect/SearchableSingleSelect.tsx index aaccd225..99289212 100644 --- a/src/components/SearchableSingleSelect/SearchableSingleSelect.tsx +++ b/src/components/SearchableSingleSelect/SearchableSingleSelect.tsx @@ -93,8 +93,8 @@ export const SearchableSingleSelect = ({ const { liveValue: filter, setValue: setFilterValue } = useDebouncedState({ initialValue: '', - onSetDebouncedValue: (value: string) => onFilterChange && onFilterChange({ value }) , - + onSetDebouncedValue: (value: string) => + onFilterChange && onFilterChange({ value }), }) useEffect(() => { diff --git a/src/pages/dataSetsWip/form/PeriodTypeField.tsx b/src/pages/dataSetsWip/form/PeriodTypeField.tsx index fe4b534c..87291235 100644 --- a/src/pages/dataSetsWip/form/PeriodTypeField.tsx +++ b/src/pages/dataSetsWip/form/PeriodTypeField.tsx @@ -5,9 +5,30 @@ import { useField } from 'react-final-form' import { useQuery } from 'react-query' import { SearchableSingleSelect } from '../../../components' import { useBoundResourceQueryFn } from '../../../lib/query/useBoundQueryFn' -import {Category, PickWithFieldFilters} from "../../../types/generated"; -export type PeriodTypes = {periodTypes: [{name:string}]} +export type PeriodTypes = { periodTypes: [{ name: string }] } +const periodTypesToTranslatedValues: Record = { + BiMonthly: i18n.t('BiMonthly'), + BiWeekly: i18n.t('BiWeekly'), + Daily: i18n.t('Daily'), + FinancialApril: i18n.t('FinancialApril'), + FinancialJuly: i18n.t('FinancialJuly'), + FinancialNov: i18n.t('FinancialNov'), + FinancialOct: i18n.t('FinancialOct'), + Monthly: i18n.t('Monthly'), + Quarterly: i18n.t('Quarterly'), + QuarterlyNov: i18n.t('QuarterlyNov'), + SixMonthlyApril: i18n.t('SixMonthlyApril'), + SixMonthlyNov: i18n.t('SixMonthlyNov'), + SixMonthly: i18n.t('SixMonthly'), + TwoYearly: i18n.t('TwoYearly'), + Weekly: i18n.t('Weekly'), + WeeklySaturday: i18n.t('WeeklySaturday'), + WeeklySunday: i18n.t('WeeklySunday'), + WeeklyThursday: i18n.t('WeeklyThursday'), + WeeklyWednesday: i18n.t('WeeklyWednesday'), + Yearly: i18n.t('Yearly'), +} export function PeriodTypeField() { const { input, meta } = useField('periodType') @@ -28,7 +49,8 @@ export function PeriodTypeField() { const options = useMemo(() => { return data ? data.periodTypes.map((period) => ({ - label: i18n.t(period.name), + label: + periodTypesToTranslatedValues[period.name] || period.name, value: period.name, })) : [] @@ -39,12 +61,12 @@ export function PeriodTypeField() { required name="periodType" label={i18n.t('Period type')} - error={(meta.touched && !!meta.error)} + error={meta.touched && !!meta.error} validationText={meta.touched ? meta.error : undefined} > input.onChange(selected)} searchable={false} From 89ae19bddceaa6ccaa9d769b21e046f8bb77d09a Mon Sep 17 00:00:00 2001 From: Flaminia Cavallo Date: Thu, 19 Dec 2024 14:29:35 +0100 Subject: [PATCH 4/5] fix: fix lint --- .../CategoryComboSelect/CategoryComboSelect.tsx | 2 +- src/pages/dataSetsWip/form/DataSetFormContents.tsx | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/metadataFormControls/CategoryComboSelect/CategoryComboSelect.tsx b/src/components/metadataFormControls/CategoryComboSelect/CategoryComboSelect.tsx index 58aee015..7444a7af 100644 --- a/src/components/metadataFormControls/CategoryComboSelect/CategoryComboSelect.tsx +++ b/src/components/metadataFormControls/CategoryComboSelect/CategoryComboSelect.tsx @@ -2,8 +2,8 @@ import i18n from '@dhis2/d2-i18n' import React, { forwardRef } from 'react' import { ModelSingleSelectLegacy } from '../ModelSingleSelect' import type { ModelSingleSelectLegacyProps } from '../ModelSingleSelect' -import { useInitialCategoryComboQuery } from './useInitialCategoryComboQuery' import { useCategoryCombosQuery } from './useCategoryCombosQuery' +import { useInitialCategoryComboQuery } from './useInitialCategoryComboQuery' type CategoryComboSelectProps = Omit< ModelSingleSelectLegacyProps, diff --git a/src/pages/dataSetsWip/form/DataSetFormContents.tsx b/src/pages/dataSetsWip/form/DataSetFormContents.tsx index 52528994..3943eca8 100644 --- a/src/pages/dataSetsWip/form/DataSetFormContents.tsx +++ b/src/pages/dataSetsWip/form/DataSetFormContents.tsx @@ -18,10 +18,10 @@ import { useSelectedSection, useSyncSelectedSectionWithScroll, } from '../../../lib' -import { DataSetFormDescriptor } from './formDescriptor' import { CategoryComboField } from './CategoryComboField' -import { PeriodTypeField } from './PeriodTypeField' import { ColorAndIconField } from '../../dataElements/fields' +import { DataSetFormDescriptor } from './formDescriptor' +import { PeriodTypeField } from './PeriodTypeField' const section = SECTIONS_MAP.dataSet From 0e4b9074a1c1214bf8aeb5d851d3f87272a2e2ab Mon Sep 17 00:00:00 2001 From: Flaminia Cavallo Date: Mon, 23 Dec 2024 09:23:25 +0100 Subject: [PATCH 5/5] fix: small fixxes --- .../ModelSingleSelectField.tsx | 1 + src/lib/constants/translatedModelConstants.ts | 24 +++++++++ .../dataSetsWip/form/CategoryComboField.tsx | 52 +++++++------------ .../dataSetsWip/form/DataSetFormContents.tsx | 3 +- .../dataSetsWip/form/PeriodTypeField.tsx | 28 ++-------- .../dataSetsWip/form/dataSetFormSchema.ts | 4 +- 6 files changed, 50 insertions(+), 62 deletions(-) diff --git a/src/components/metadataFormControls/ModelSingleSelect/ModelSingleSelectField.tsx b/src/components/metadataFormControls/ModelSingleSelect/ModelSingleSelectField.tsx index cc7654bb..0d2dc81a 100644 --- a/src/components/metadataFormControls/ModelSingleSelect/ModelSingleSelectField.tsx +++ b/src/components/metadataFormControls/ModelSingleSelect/ModelSingleSelectField.tsx @@ -66,6 +66,7 @@ export function ModelSingleSelectField({ input.onBlur() onChange?.(selected) }} + invalid={meta.touched && !!meta.error} /> ) diff --git a/src/lib/constants/translatedModelConstants.ts b/src/lib/constants/translatedModelConstants.ts index 4b1c01e3..71c00bee 100644 --- a/src/lib/constants/translatedModelConstants.ts +++ b/src/lib/constants/translatedModelConstants.ts @@ -86,6 +86,29 @@ export const FORM_TYPE = { SECTION_MULTIORG: i18n.t('Section Multi-org'), } +const PERIOD_TYPE = { + BiMonthly: i18n.t('BiMonthly'), + BiWeekly: i18n.t('BiWeekly'), + Daily: i18n.t('Daily'), + FinancialApril: i18n.t('FinancialApril'), + FinancialJuly: i18n.t('FinancialJuly'), + FinancialNov: i18n.t('FinancialNov'), + FinancialOct: i18n.t('FinancialOct'), + Monthly: i18n.t('Monthly'), + Quarterly: i18n.t('Quarterly'), + QuarterlyNov: i18n.t('QuarterlyNov'), + SixMonthlyApril: i18n.t('SixMonthlyApril'), + SixMonthlyNov: i18n.t('SixMonthlyNov'), + SixMonthly: i18n.t('SixMonthly'), + TwoYearly: i18n.t('TwoYearly'), + Weekly: i18n.t('Weekly'), + WeeklySaturday: i18n.t('WeeklySaturday'), + WeeklySunday: i18n.t('WeeklySunday'), + WeeklyThursday: i18n.t('WeeklyThursday'), + WeeklyWednesday: i18n.t('WeeklyWednesday'), + Yearly: i18n.t('Yearly'), +} + const allConstantTranslations: Record = { ...AGGREGATION_TYPE, ...DOMAIN_TYPE, @@ -93,6 +116,7 @@ const allConstantTranslations: Record = { ...DATA_DIMENSION_TYPE, ...GEOMETRY_TYPE, ...FORM_TYPE, + ...PERIOD_TYPE, } export const getConstantTranslation = (constant: string): string => { diff --git a/src/pages/dataSetsWip/form/CategoryComboField.tsx b/src/pages/dataSetsWip/form/CategoryComboField.tsx index 49e280c5..194f302a 100644 --- a/src/pages/dataSetsWip/form/CategoryComboField.tsx +++ b/src/pages/dataSetsWip/form/CategoryComboField.tsx @@ -1,47 +1,33 @@ import i18n from '@dhis2/d2-i18n' -import { Field } from '@dhis2/ui' import React from 'react' -import { useField } from 'react-final-form' -import { ModelSingleSelect } from '../../../components/metadataFormControls/ModelSingleSelect' +import { ModelSingleSelectField } from '../../../components/metadataFormControls/ModelSingleSelect' import { DEFAULT_CATEGORY_COMBO } from '../../../lib' -export function CategoryComboField() { - const { input, meta } = useField('categoryCombo') - - const CATEGORY_COMBOS_QUERY = { - resource: 'categoryCombos', - params: { - filter: ['dataDimensionType:eq:ATTRIBUTE'], - }, - } +const CATEGORY_COMBOS_QUERY = { + resource: 'categoryCombos', + params: { + filter: ['dataDimensionType:eq:ATTRIBUTE'], + }, +} - const DEFAULT_CATEGORY_SELECT_OPTION = { - id: DEFAULT_CATEGORY_COMBO.id, - displayName: DEFAULT_CATEGORY_COMBO.displayName, - } +const DEFAULT_CATEGORY_SELECT_OPTION = { + id: DEFAULT_CATEGORY_COMBO.id, + displayName: DEFAULT_CATEGORY_COMBO.displayName, +} +export function CategoryComboField() { return ( - - { - input.onChange(selected) - }} - transform={(catCombos) => [ - ...catCombos, - DEFAULT_CATEGORY_SELECT_OPTION, - ]} - /> - + query={CATEGORY_COMBOS_QUERY} + transform={(catCombos) => [ + DEFAULT_CATEGORY_SELECT_OPTION, + ...catCombos, + ]} + /> ) } diff --git a/src/pages/dataSetsWip/form/DataSetFormContents.tsx b/src/pages/dataSetsWip/form/DataSetFormContents.tsx index 3943eca8..a0eae02a 100644 --- a/src/pages/dataSetsWip/form/DataSetFormContents.tsx +++ b/src/pages/dataSetsWip/form/DataSetFormContents.tsx @@ -15,11 +15,10 @@ import { import { SECTIONS_MAP, useSectionedFormContext, - useSelectedSection, useSyncSelectedSectionWithScroll, } from '../../../lib' -import { CategoryComboField } from './CategoryComboField' import { ColorAndIconField } from '../../dataElements/fields' +import { CategoryComboField } from './CategoryComboField' import { DataSetFormDescriptor } from './formDescriptor' import { PeriodTypeField } from './PeriodTypeField' diff --git a/src/pages/dataSetsWip/form/PeriodTypeField.tsx b/src/pages/dataSetsWip/form/PeriodTypeField.tsx index 87291235..434dcbc6 100644 --- a/src/pages/dataSetsWip/form/PeriodTypeField.tsx +++ b/src/pages/dataSetsWip/form/PeriodTypeField.tsx @@ -1,34 +1,13 @@ import i18n from '@dhis2/d2-i18n' -import { Field, SingleSelect, SingleSelectOption } from '@dhis2/ui' +import { Field } from '@dhis2/ui' import React, { useMemo } from 'react' import { useField } from 'react-final-form' import { useQuery } from 'react-query' import { SearchableSingleSelect } from '../../../components' +import { getConstantTranslation } from '../../../lib' import { useBoundResourceQueryFn } from '../../../lib/query/useBoundQueryFn' export type PeriodTypes = { periodTypes: [{ name: string }] } -const periodTypesToTranslatedValues: Record = { - BiMonthly: i18n.t('BiMonthly'), - BiWeekly: i18n.t('BiWeekly'), - Daily: i18n.t('Daily'), - FinancialApril: i18n.t('FinancialApril'), - FinancialJuly: i18n.t('FinancialJuly'), - FinancialNov: i18n.t('FinancialNov'), - FinancialOct: i18n.t('FinancialOct'), - Monthly: i18n.t('Monthly'), - Quarterly: i18n.t('Quarterly'), - QuarterlyNov: i18n.t('QuarterlyNov'), - SixMonthlyApril: i18n.t('SixMonthlyApril'), - SixMonthlyNov: i18n.t('SixMonthlyNov'), - SixMonthly: i18n.t('SixMonthly'), - TwoYearly: i18n.t('TwoYearly'), - Weekly: i18n.t('Weekly'), - WeeklySaturday: i18n.t('WeeklySaturday'), - WeeklySunday: i18n.t('WeeklySunday'), - WeeklyThursday: i18n.t('WeeklyThursday'), - WeeklyWednesday: i18n.t('WeeklyWednesday'), - Yearly: i18n.t('Yearly'), -} export function PeriodTypeField() { const { input, meta } = useField('periodType') @@ -49,8 +28,7 @@ export function PeriodTypeField() { const options = useMemo(() => { return data ? data.periodTypes.map((period) => ({ - label: - periodTypesToTranslatedValues[period.name] || period.name, + label: getConstantTranslation(period.name), value: period.name, })) : [] diff --git a/src/pages/dataSetsWip/form/dataSetFormSchema.ts b/src/pages/dataSetsWip/form/dataSetFormSchema.ts index b87838ee..e1adbe7a 100644 --- a/src/pages/dataSetsWip/form/dataSetFormSchema.ts +++ b/src/pages/dataSetsWip/form/dataSetFormSchema.ts @@ -18,8 +18,8 @@ export const dataSetFormSchema = identifiable style, dataElements: referenceCollection.default([]), categoryCombo: z - .object({ id: z.string() }) - .default({ id: DEFAULT_CATEGORY_COMBO.id }), + .object({ id: z.string(), displayName: z.string() }) + .default({ ...DEFAULT_CATEGORY_COMBO }), periodType: z.string().default('Monthly'), })