From 37b4051e7ff081a43a2fd399ee94272922808d98 Mon Sep 17 00:00:00 2001 From: Chisom Chima <87203527+Chisomchima@users.noreply.github.com> Date: Tue, 5 Nov 2024 07:00:10 +0100 Subject: [PATCH 1/5] feat: category option group sets (#427) * feat: add category option group sets list page * feat: add create form * feat: add edit form * feat: remove default filters * feat: remove default filters * Update src/pages/categoryOptionGroupSets/form/CategoryOptionGroupSetFormFields.tsx Co-authored-by: Birk Johansson * Update src/pages/categoryOptionGroupSets/form/CategoryOptionGroupSetFormFields.tsx Co-authored-by: Birk Johansson * Update src/pages/categoryOptionGroupSets/form/CategoryOptionGroupSetFormFields.tsx Co-authored-by: Birk Johansson * Update src/pages/categoryOptionGroupSets/form/categoryOptionGroupSetSchema.ts Co-authored-by: Birk Johansson * Update src/lib/sectionList/listViews/sectionListViewsConfig.ts Co-authored-by: Birk Johansson * chore: clean up * chore: clean up * Update src/pages/categoryOptionGroupSets/form/CategoryOptionGroupSetFormFields.tsx Co-authored-by: Birk Johansson * Update src/pages/categoryOptionGroupSets/form/CategoryOptionGroupSetFormFields.tsx Co-authored-by: Birk Johansson * chore: lint error fix --------- Co-authored-by: Birk Johansson --- i18n/en.pot | 62 +++++++-- .../ModelTransfer/ModelTransfer.tsx | 2 +- .../listViews/sectionListViewsConfig.ts | 13 ++ src/pages/categoryOptionGroupSets/Edit.tsx | 62 +++++++++ src/pages/categoryOptionGroupSets/List.tsx | 4 + src/pages/categoryOptionGroupSets/New.tsx | 23 ++++ .../form/CategoryOptionGroupSetFormFields.tsx | 119 ++++++++++++++++++ .../form/categoryOptionGroupSetSchema.ts | 25 ++++ 8 files changed, 301 insertions(+), 9 deletions(-) create mode 100644 src/pages/categoryOptionGroupSets/Edit.tsx create mode 100644 src/pages/categoryOptionGroupSets/List.tsx create mode 100644 src/pages/categoryOptionGroupSets/New.tsx create mode 100644 src/pages/categoryOptionGroupSets/form/CategoryOptionGroupSetFormFields.tsx create mode 100644 src/pages/categoryOptionGroupSets/form/categoryOptionGroupSetSchema.ts diff --git a/i18n/en.pot b/i18n/en.pot index 9866e147..fcdcdc0c 100644 --- a/i18n/en.pot +++ b/i18n/en.pot @@ -858,6 +858,9 @@ msgstr "Data dimension type" msgid "This field requires a unique value, please choose another one" msgstr "This field requires a unique value, please choose another one" +msgid "{{label}} (required)" +msgstr "{{label}} (required)" + msgid "No changes to be saved" msgstr "No changes to be saved" @@ -879,23 +882,27 @@ msgstr "Basic information" msgid "Set up the basic information for this category." msgstr "Set up the basic information for this category." -msgid "Explain the purpose of this category." -msgstr "Explain the purpose of this category." +msgid "Explain the purpose of this category option group." +msgstr "Explain the purpose of this category option group." msgid "Data configuration" msgstr "Data configuration" -msgid "Choose how this category will be used to capture and analyze" -msgstr "Choose how this category will be used to capture and analyze" +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 "Use as data dimension" msgstr "Use as data dimension" -msgid "Category will be available to the analytics as another dimension" -msgstr "Category will be available to the analytics as another 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 "Choose the category options to include in this category." -msgstr "Choose the category options to include in this category." +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 "Available category options" msgstr "Available category options" @@ -933,6 +940,45 @@ msgstr "Filter selected categories" msgid "At least one category is required" msgstr "At least one category is required" +msgid "Set up the basic information for this category option group set." +msgstr "Set up the basic information for this category option group set." + +msgid "Explain the purpose of this category option group set." +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" +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" + +msgid "" +"Choose the category option Groups to include in this category option group " +"set." +msgstr "" +"Choose the category option Groups to include in this category option group " +"set." + +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 the category options to include in this category." +msgstr "Choose the category options to include in this category." + msgid "Set up the basic information for this category option." msgstr "Set up the basic information for this category option." diff --git a/src/components/metadataFormControls/ModelTransfer/ModelTransfer.tsx b/src/components/metadataFormControls/ModelTransfer/ModelTransfer.tsx index 8629c43a..1f28e5da 100644 --- a/src/components/metadataFormControls/ModelTransfer/ModelTransfer.tsx +++ b/src/components/metadataFormControls/ModelTransfer/ModelTransfer.tsx @@ -92,7 +92,7 @@ const BaseModelTransfer = ( [queryResult.data, modelName] ) - const selectedOptions = selected.map(toDisplayOption) + const selectedOptions = selected ? selected?.map(toDisplayOption) : [] const loadedOptions = Array.from(allDataMap.values()).map(toDisplayOption) // always include selected options const allOptions = selectedOptions.concat(loadedOptions || []) diff --git a/src/lib/sectionList/listViews/sectionListViewsConfig.ts b/src/lib/sectionList/listViews/sectionListViewsConfig.ts index 6af76cbb..076618df 100644 --- a/src/lib/sectionList/listViews/sectionListViewsConfig.ts +++ b/src/lib/sectionList/listViews/sectionListViewsConfig.ts @@ -126,6 +126,19 @@ export const modelListViewsConfig = { default: ['dataDimensionType', 'categoryCombo'], }, }, + categoryOptionGroupSet: { + columns: { + default: [ + 'name', + 'dataDimensionType', + DESCRIPTORS.publicAccess, + 'lastUpdated', + ], + }, + filters: { + default: ['dataDimensionType'], + }, + }, categoryOptionGroup: { columns: { default: [ diff --git a/src/pages/categoryOptionGroupSets/Edit.tsx b/src/pages/categoryOptionGroupSets/Edit.tsx new file mode 100644 index 00000000..b37d89c6 --- /dev/null +++ b/src/pages/categoryOptionGroupSets/Edit.tsx @@ -0,0 +1,62 @@ +import React from 'react' +import { useQuery } from 'react-query' +import { useParams } from 'react-router-dom' +import { DefaultEditFormContents, FormBase } from '../../components' +import { + ATTRIBUTE_VALUES_FIELD_FILTERS, + DEFAULT_FIELD_FILTERS, + SECTIONS_MAP, + useOnSubmitEdit, +} from '../../lib' +import { useBoundResourceQueryFn } from '../../lib/query/useBoundQueryFn' +import { PickWithFieldFilters } from '../../types/generated' +import { CategoryOptionGroupSet } from '../../types/models' +import CategoryOptionGroupSetFormFields from './form/CategoryOptionGroupSetFormFields' +import { validate } from './form/categoryOptionGroupSetSchema' + +const fieldFilters = [ + ...DEFAULT_FIELD_FILTERS, + ...ATTRIBUTE_VALUES_FIELD_FILTERS, + 'name', + 'shortName', + 'code', + 'description', + 'categoryOptionGroups[id,displayName]', + 'dataDimension', + 'dataDimensionType', +] as const + +export type CategoryOptionGroupSetFormValues = PickWithFieldFilters< + CategoryOptionGroupSet, + typeof fieldFilters +> + +export const Component = () => { + const section = SECTIONS_MAP.categoryOptionGroupSet + const queryFn = useBoundResourceQueryFn() + const modelId = useParams().id as string + const query = { + resource: 'categoryOptionGroupSets', + id: modelId, + params: { + fields: fieldFilters.concat(), + }, + } + const categoryOptionGroupSetQuery = useQuery({ + queryKey: [query], + queryFn: queryFn, + }) + + return ( + + + + + + ) +} diff --git a/src/pages/categoryOptionGroupSets/List.tsx b/src/pages/categoryOptionGroupSets/List.tsx new file mode 100644 index 00000000..b310e1bd --- /dev/null +++ b/src/pages/categoryOptionGroupSets/List.tsx @@ -0,0 +1,4 @@ +import React from 'react' +import { DefaultSectionList } from '../DefaultSectionList' + +export const Component = () => diff --git a/src/pages/categoryOptionGroupSets/New.tsx b/src/pages/categoryOptionGroupSets/New.tsx new file mode 100644 index 00000000..3907d4d2 --- /dev/null +++ b/src/pages/categoryOptionGroupSets/New.tsx @@ -0,0 +1,23 @@ +import React from 'react' +import { FormBase } from '../../components' +import { DefaultNewFormContents } from '../../components/form/DefaultFormContents' +import { SECTIONS_MAP, useOnSubmitNew } from '../../lib' +import CategoryOptionGroupSetFormFields from './form/CategoryOptionGroupSetFormFields' +import { initialValues, validate } from './form/categoryOptionGroupSetSchema' + +const section = SECTIONS_MAP.categoryOptionGroupSet + +export const Component = () => { + return ( + + + + + + ) +} diff --git a/src/pages/categoryOptionGroupSets/form/CategoryOptionGroupSetFormFields.tsx b/src/pages/categoryOptionGroupSets/form/CategoryOptionGroupSetFormFields.tsx new file mode 100644 index 00000000..4880425c --- /dev/null +++ b/src/pages/categoryOptionGroupSets/form/CategoryOptionGroupSetFormFields.tsx @@ -0,0 +1,119 @@ +import i18n from '@dhis2/d2-i18n' +import { RadioFieldFF, CheckboxFieldFF } from '@dhis2/ui' +import React from 'react' +import { Field } from 'react-final-form' +import { + DefaultIdentifiableFields, + DescriptionField, + HorizontalFieldGroup, + ModelTransferField, + StandardFormField, + StandardFormSection, + StandardFormSectionDescription, + StandardFormSectionTitle, +} from '../../../components' +import { SECTIONS_MAP } from '../../../lib' + +function CategoryOptionGroupSetFormFields() { + const section = SECTIONS_MAP.categoryOptionGroupSet + return ( + <> + + + {i18n.t('Basic information')} + + + {i18n.t( + 'Set up the basic information for this category option group set.' + )} + + + + + + + + {i18n.t('Data configuration')} + + + {i18n.t( + 'Choose how this category option group set will be used to capture and analyze' + )} + + + + + name="dataDimensionType" + component={RadioFieldFF} + label={i18n.t('Disaggregation')} + type="radio" + value={'DISAGGREGATION'} + /> + + name="dataDimensionType" + component={RadioFieldFF} + label={i18n.t('Attribute')} + type="radio" + value={'ATTRIBUTE'} + /> + + + + + + + + + + + + + {i18n.t( + 'Choose the category option groups to include in this category option group set.' + )} + + + + + + + + + ) +} + +export default CategoryOptionGroupSetFormFields diff --git a/src/pages/categoryOptionGroupSets/form/categoryOptionGroupSetSchema.ts b/src/pages/categoryOptionGroupSets/form/categoryOptionGroupSetSchema.ts new file mode 100644 index 00000000..954af986 --- /dev/null +++ b/src/pages/categoryOptionGroupSets/form/categoryOptionGroupSetSchema.ts @@ -0,0 +1,25 @@ +import { z } from 'zod' +import { getDefaults, createFormValidate, modelFormSchemas } from '../../../lib' +import { CategoryOptionGroupSet } from '../../../types/generated' + +const { identifiable, referenceCollection, withAttributeValues } = + modelFormSchemas + +export const categoryOptionGroupSetSchema = identifiable + .merge(withAttributeValues) + .extend({ + shortName: z.string().trim(), + code: z.string().trim().optional(), + description: z.string().trim().optional(), + dataDimensionType: z + .nativeEnum(CategoryOptionGroupSet.dataDimensionType) + .default(CategoryOptionGroupSet.dataDimensionType.DISAGGREGATION), + dataDimension: z.boolean().default(true), + categoryOptionGroups: referenceCollection + .min(1, 'At least one category option group is required') + .default([]), + }) + +export const initialValues = getDefaults(categoryOptionGroupSetSchema) + +export const validate = createFormValidate(categoryOptionGroupSetSchema) From 4eccf6d85a1409fbf583d129647b122baa36522b Mon Sep 17 00:00:00 2001 From: "@dhis2-bot" Date: Tue, 5 Nov 2024 06:03:09 +0000 Subject: [PATCH 2/5] chore(release): cut 0.11.0 [skip release] # [0.11.0](https://github.com/dhis2/maintenance-app-beta/compare/v0.10.2...v0.11.0) (2024-11-05) ### Features * category option group sets ([#427](https://github.com/dhis2/maintenance-app-beta/issues/427)) ([37b4051](https://github.com/dhis2/maintenance-app-beta/commit/37b4051e7ff081a43a2fd399ee94272922808d98)) --- CHANGELOG.md | 7 +++++++ package.json | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2d5c19b8..6838f021 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +# [0.11.0](https://github.com/dhis2/maintenance-app-beta/compare/v0.10.2...v0.11.0) (2024-11-05) + + +### Features + +* category option group sets ([#427](https://github.com/dhis2/maintenance-app-beta/issues/427)) ([37b4051](https://github.com/dhis2/maintenance-app-beta/commit/37b4051e7ff081a43a2fd399ee94272922808d98)) + ## [0.10.2](https://github.com/dhis2/maintenance-app-beta/compare/v0.10.1...v0.10.2) (2024-10-30) diff --git a/package.json b/package.json index bcf9008f..dd29e5ef 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "maintenance-app", - "version": "0.10.2", + "version": "0.11.0", "description": "", "license": "BSD-3-Clause", "private": true, From 79a18921c876a36d357edee88f38114bc412a0a9 Mon Sep 17 00:00:00 2001 From: Flaminia Date: Tue, 5 Nov 2024 08:31:34 +0100 Subject: [PATCH 3/5] feat: add org unit edit form (#430) * feat: add org unit edit form * feat: small changes to org unit select and image select * fix(orgunit): fix image preview url * fix(orgunit): create default valueFormatter * fix: fix type issue * feat: minor fixes --------- Co-authored-by: Birk Johansson --- i18n/en.pot | 95 ++++++++++++------- .../form/DefaultFormErrorNotice.tsx | 2 +- src/lib/constants/translatedModelConstants.ts | 11 +++ src/lib/form/createJsonPatchOperations.ts | 3 +- src/lib/form/useOnSubmit.ts | 27 +++++- src/pages/organisationUnits/Edit.tsx | 80 ++++++++++++++++ src/pages/organisationUnits/New.tsx | 19 ---- .../organisationUnits/form/GeometryFields.tsx | 78 +++++++++++++++ .../form/ImageField.module.css | 2 - .../organisationUnits/form/ImageField.tsx | 5 +- .../form/OrganisationUnitFormFields.tsx | 24 +---- .../form/OrganisationUnitSelector.tsx | 13 ++- .../form/organisationUnitSchema.ts | 16 +++- 13 files changed, 284 insertions(+), 91 deletions(-) create mode 100644 src/pages/organisationUnits/Edit.tsx create mode 100644 src/pages/organisationUnits/form/GeometryFields.tsx diff --git a/i18n/en.pot b/i18n/en.pot index fcdcdc0c..17fadb7c 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-10-23T11:07:53.225Z\n" -"PO-Revision-Date: 2024-10-23T11:07:53.225Z\n" +"POT-Creation-Date: 2024-11-05T07:23:15.731Z\n" +"PO-Revision-Date: 2024-11-05T07:23:15.732Z\n" msgid "schemas" msgstr "schemas" @@ -123,21 +123,6 @@ msgstr "Failed to load {{label}}" msgid "Failed to load" msgstr "Failed to load" -msgid "Download" -msgstr "Download" - -msgid "Merge" -msgstr "Merge" - -msgid "Delete source data element values" -msgstr "Delete source data element values" - -msgid "Last updated" -msgstr "Last updated" - -msgid "Discard" -msgstr "Discard" - msgid "Aggregation level(s)" msgstr "Aggregation level(s)" @@ -237,6 +222,9 @@ msgstr "Created" msgid "Last updated by" msgstr "Last updated by" +msgid "Last updated" +msgstr "Last updated" + msgid "Id" msgstr "Id" @@ -258,6 +246,9 @@ msgstr "Details" msgid "Failed to load details" msgstr "Failed to load details" +msgid "Download" +msgstr "Download" + msgid "Download {{section}}" msgstr "Download {{section}}" @@ -297,6 +288,9 @@ msgstr "Clear all filters" msgid "Category" msgstr "Category" +msgid "Category option" +msgstr "Category option" + msgid "Category option group" msgstr "Category option group" @@ -435,9 +429,6 @@ msgstr "Search for a user or group" msgid "Categories" msgstr "Categories" -msgid "Category option" -msgstr "Category option" - msgid "Category options" msgstr "Category options" @@ -828,6 +819,27 @@ msgstr "GeoJSON" msgid "Disaggregation" msgstr "Disaggregation" +msgid "Point" +msgstr "Point" + +msgid "MultiPoint" +msgstr "MultiPoint" + +msgid "LineString" +msgstr "LineString" + +msgid "MultiLineString" +msgstr "MultiLineString" + +msgid "Polygon" +msgstr "Polygon" + +msgid "MultiPolygon" +msgstr "MultiPolygon" + +msgid "GeometryCollection" +msgstr "GeometryCollection" + msgid "Aggregation type" msgstr "Aggregation type" @@ -855,6 +867,9 @@ msgstr "Zero is significant" msgid "Data dimension type" msgstr "Data dimension type" +msgid "Ignore data approval" +msgstr "Ignore data approval" + msgid "This field requires a unique value, please choose another one" msgstr "This field requires a unique value, please choose another one" @@ -964,12 +979,24 @@ msgid "Category option Groups" msgstr "Category option Groups" msgid "" -"Choose the category option Groups to include in this category option group " +"Choose the category option groups to include in this category option group " "set." msgstr "" -"Choose the category option Groups to include in this category option group " +"Choose the category option groups to include in this category option group " "set." +msgid "Available category option groups" +msgstr "Available category option groups" + +msgid "Selected category option groups" +msgstr "Selected category option groups" + +msgid "Filter available category option groups" +msgstr "Filter available category option groups" + +msgid "Filter selected category option groups" +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." @@ -1127,6 +1154,12 @@ msgstr "" "included. PHU will still be available for the PHU level, but not included " "in the aggregations to the levels above." +msgid "Longitude" +msgstr "Longitude" + +msgid "{{type}} coordinates are not editable" +msgstr "{{type}} coordinates are not editable" + msgid "Upload an image" msgstr "Upload an image" @@ -1176,17 +1209,11 @@ msgstr "Location" msgid "Set up the organisation unit location." msgstr "Set up the organisation unit location." -msgid "Latitude" -msgstr "Latitude" - -msgid "Longitude" -msgstr "Longitude" - -msgid "Reference assignments" -msgstr "Reference assignments" +msgid "Reference assignment" +msgstr "Reference assignment" -msgid "Assign the organisation unit to related models." -msgstr "Assign the organisation unit to related models." +msgid "Assign the organisation unit to related objects." +msgstr "Assign the organisation unit to related objects." msgid "Available data sets" msgstr "Available data sets" @@ -1212,8 +1239,8 @@ msgstr "Filter available programs" msgid "Filter selected programs" msgstr "Filter selected programs" -msgid "New organisation unit will be created inside {{displayName}}" -msgstr "New organisation unit will be created inside {{displayName}}" +msgid "Organisation unit will be positioned inside {{displayName}}" +msgstr "Organisation unit will be positioned inside {{displayName}}" msgid "Creating first organisation unit" msgstr "Creating first organisation unit" diff --git a/src/components/form/DefaultFormErrorNotice.tsx b/src/components/form/DefaultFormErrorNotice.tsx index c5102749..9ba043bc 100644 --- a/src/components/form/DefaultFormErrorNotice.tsx +++ b/src/components/form/DefaultFormErrorNotice.tsx @@ -87,7 +87,7 @@ const ErrorList = ({ errors }: { errors: Record }) => { > {labels.get(key) || key}: - {value} + {JSON.stringify(value)} ) })} diff --git a/src/lib/constants/translatedModelConstants.ts b/src/lib/constants/translatedModelConstants.ts index e3ed1315..f46b332e 100644 --- a/src/lib/constants/translatedModelConstants.ts +++ b/src/lib/constants/translatedModelConstants.ts @@ -69,11 +69,22 @@ export const DATA_DIMENSION_TYPE = { ATTRIBUTE: i18n.t('Attribute'), } +export const GEOMETRY_TYPE = { + POINT: i18n.t('Point'), + MULTIPOINT: i18n.t('MultiPoint'), + LINESTRING: i18n.t('LineString'), + MULTILINESTRING: i18n.t('MultiLineString'), + POLYGON: i18n.t('Polygon'), + MULTIPOLYGON: i18n.t('MultiPolygon'), + GEOMETRYCOLLECTION: i18n.t('GeometryCollection'), +} + const allConstantTranslations: Record = { ...AGGREGATION_TYPE, ...DOMAIN_TYPE, ...VALUE_TYPE, ...DATA_DIMENSION_TYPE, + ...GEOMETRY_TYPE, } export const getConstantTranslation = (constant: string): string => { diff --git a/src/lib/form/createJsonPatchOperations.ts b/src/lib/form/createJsonPatchOperations.ts index 4c55b061..451f47cc 100644 --- a/src/lib/form/createJsonPatchOperations.ts +++ b/src/lib/form/createJsonPatchOperations.ts @@ -15,7 +15,8 @@ type PatchAttributeValue = { value: AttributeValue['value'] } -type ModelWithAttributeValues = IdentifiableObject & { +export type ModelWithAttributeValues = { + id?: string attributeValues?: PatchAttributeValue[] } diff --git a/src/lib/form/useOnSubmit.ts b/src/lib/form/useOnSubmit.ts index 1e3f80fd..67e7e476 100644 --- a/src/lib/form/useOnSubmit.ts +++ b/src/lib/form/useOnSubmit.ts @@ -6,7 +6,10 @@ import { useNavigate } from 'react-router-dom' import { ModelSection } from '../../types' import { IdentifiableObject } from '../../types/generated' import { getSectionPath, useNavigateWithSearchState } from '../routeUtils' -import { createJsonPatchOperations } from './createJsonPatchOperations' +import { + createJsonPatchOperations, + ModelWithAttributeValues, +} from './createJsonPatchOperations' import { useCreateModel } from './useCreateModel' import { usePatchModel } from './usePatchModel' @@ -57,9 +60,25 @@ export const useOnSubmitEdit = ({ ) } -export const useOnSubmitNew = ({ +export const defaultNewValueFormatter = < + TFormValues extends ModelWithAttributeValues +>( + values: TFormValues +) => { + if (values.attributeValues) { + return { + ...values, + attributeValues: values.attributeValues.filter( + ({ value }) => !!value + ), + } + } + return values +} + +export const useOnSubmitNew = ({ section, - valueFormatter, + valueFormatter = defaultNewValueFormatter, }: { section: ModelSection valueFormatter?: (values: TFormValues) => Record @@ -96,6 +115,6 @@ export const useOnSubmitNew = ({ }) navigate(`/${getSectionPath(section)}`) }, - [createModel, saveAlert, navigate, section] + [createModel, saveAlert, navigate, section, valueFormatter] ) } diff --git a/src/pages/organisationUnits/Edit.tsx b/src/pages/organisationUnits/Edit.tsx new file mode 100644 index 00000000..81b22b2b --- /dev/null +++ b/src/pages/organisationUnits/Edit.tsx @@ -0,0 +1,80 @@ +import React from 'react' +import { useQuery } from 'react-query' +import { useParams } from 'react-router-dom' +import { DefaultEditFormContents, FormBase } from '../../components' +import { + ATTRIBUTE_VALUES_FIELD_FILTERS, + DEFAULT_FIELD_FILTERS, + SECTIONS_MAP, + useOnSubmitEdit, + validate, +} from '../../lib' +import { useBoundResourceQueryFn } from '../../lib/query/useBoundQueryFn' +import { OrganisationUnit, PickWithFieldFilters } from '../../types/generated' +import { OrganisationUnitFormField, organisationUnitSchema } from './form' + +const fieldFilters = [ + ...DEFAULT_FIELD_FILTERS, + ...ATTRIBUTE_VALUES_FIELD_FILTERS, + 'name', + 'code', + 'shortName', + 'openingDate', + 'closedDate', + 'comment', + 'image[id,name]', + 'description', + 'contactPerson', + 'address', + 'email', + 'phoneNumber', + 'url', + 'geometry', + 'dataSets', + 'programs', + 'level', + 'path', + 'parent[id,path, displayName]', +] as const + +export type OrgUnitFormValues = PickWithFieldFilters< + OrganisationUnit, + typeof fieldFilters +> + +const section = SECTIONS_MAP.organisationUnit + +export const Component = () => { + const queryFn = useBoundResourceQueryFn() + const modelId = useParams().id as string + + const query = { + resource: 'organisationUnits', + id: modelId, + params: { + fields: fieldFilters.concat(), + }, + } + const orgUnit = useQuery({ + queryKey: [query], + queryFn: queryFn, + }) + + return ( + { + return validate(organisationUnitSchema, values) + }} + > + + + + + ) +} diff --git a/src/pages/organisationUnits/New.tsx b/src/pages/organisationUnits/New.tsx index d50a541a..95649477 100644 --- a/src/pages/organisationUnits/New.tsx +++ b/src/pages/organisationUnits/New.tsx @@ -9,24 +9,6 @@ import { organisationUnitSchema, } from './form' -const formatFormValues: (values: FormValues) => Record = ( - values -) => { - return { - ...values, - geometry: - values.geometry?.longitude && values.geometry?.latitude - ? { - type: 'Point', - coordinates: [ - values.geometry?.longitude, - values.geometry?.latitude, - ], - } - : undefined, - attributeValues: values.attributeValues.filter(({ value }) => !!value), - } -} const section = SECTIONS_MAP.organisationUnit export const Component = () => { @@ -34,7 +16,6 @@ export const Component = () => { { diff --git a/src/pages/organisationUnits/form/GeometryFields.tsx b/src/pages/organisationUnits/form/GeometryFields.tsx new file mode 100644 index 00000000..9598f932 --- /dev/null +++ b/src/pages/organisationUnits/form/GeometryFields.tsx @@ -0,0 +1,78 @@ +import i18n from '@dhis2/d2-i18n' +import { Field, InputField } from '@dhis2/ui' +import React from 'react' +import { useField } from 'react-final-form' +import { getConstantTranslation } from '../../../lib' + +export function GeometryFields() { + const fieldName = 'geometry' + const { input, meta } = useField(fieldName) + + const handleChange = ({ + longitude, + latitude, + }: { + longitude?: number + latitude?: number + }) => { + const geometry = { + type: 'Point', + coordinates: [longitude || undefined, latitude || undefined], + } + + input.onChange(geometry) + } + + return !input.value || input.value?.type === 'Point' ? ( + <> + + + handleChange({ + longitude: e.value + ? parseFloat(e.value) + : undefined, + latitude: input.value?.coordinates?.[1], + }) + } + label={i18n.t('Longitude')} + inputWidth="400px" + name="longitude" + type="number" + value={input.value.coordinates?.[0]?.toString()} + min="-90" + max="90" + step="any" + /> + + handleChange({ + longitude: input.value?.coordinates?.[0], + latitude: e.value ? parseFloat(e.value) : undefined, + }) + } + inputWidth="400px" + label={i18n.t('Longitude')} + name="latitude" + type="number" + value={input.value?.coordinates?.[1]?.toString()} + min="-180" + max="180" + step="any" + /> + + + ) : ( + + ) +} diff --git a/src/pages/organisationUnits/form/ImageField.module.css b/src/pages/organisationUnits/form/ImageField.module.css index c1be1b9f..821cd5d3 100644 --- a/src/pages/organisationUnits/form/ImageField.module.css +++ b/src/pages/organisationUnits/form/ImageField.module.css @@ -1,8 +1,6 @@ .fileInputWrapper { display: flex; flex-wrap: wrap; - flex-direction: column; - max-width: 400px; gap: var(--spacers-dp4); } diff --git a/src/pages/organisationUnits/form/ImageField.tsx b/src/pages/organisationUnits/form/ImageField.tsx index e130c283..9607af76 100644 --- a/src/pages/organisationUnits/form/ImageField.tsx +++ b/src/pages/organisationUnits/form/ImageField.tsx @@ -43,7 +43,7 @@ export function ImageField() { const postResponse = (await dataEngine.mutate({ resource: 'fileResources', type: 'create', - data: { file: fileToUpload }, + data: { file: fileToUpload, domain: 'ORG_UNIT' }, })) as { response: { fileResource: { id: string; storageStatus: string } @@ -103,7 +103,6 @@ export function ImageField() { valid={!!(input.value && input.value.id)} /> - {input.value?.id && ( } diff --git a/src/pages/organisationUnits/form/OrganisationUnitFormFields.tsx b/src/pages/organisationUnits/form/OrganisationUnitFormFields.tsx index 8a660411..49e6a074 100644 --- a/src/pages/organisationUnits/form/OrganisationUnitFormFields.tsx +++ b/src/pages/organisationUnits/form/OrganisationUnitFormFields.tsx @@ -13,9 +13,10 @@ import { import { DefaultIdentifiableFields, DescriptionField, -} from '../../../components/form' +} from '../../../components' import { DateField } from '../../../components/form/fields/DateField' import { SCHEMA_SECTIONS, useSystemSetting } from '../../../lib' +import { GeometryFields } from './GeometryFields' import { ImageField } from './ImageField' import { OrganisationUnitSelector } from './OrganisationUnitSelector' @@ -126,26 +127,7 @@ export function OrganisationUnitFormField() { {i18n.t('Set up the organisation unit location.')} - - - component={InputFieldFF} - inputWidth="400px" - label={i18n.t('Latitude')} - name="geometry.latitude" - type="number" - min="-90" - max="90" - /> - - - component={InputFieldFF} - inputWidth="400px" - label={i18n.t('Longitude')} - name="geometry.longitude" - type="number" - min="-180" - max="180" - /> + {allowReferenceAssignments && ( diff --git a/src/pages/organisationUnits/form/OrganisationUnitSelector.tsx b/src/pages/organisationUnits/form/OrganisationUnitSelector.tsx index 16ba244a..0d0172af 100644 --- a/src/pages/organisationUnits/form/OrganisationUnitSelector.tsx +++ b/src/pages/organisationUnits/form/OrganisationUnitSelector.tsx @@ -1,7 +1,7 @@ import i18n from '@dhis2/d2-i18n' import { Field, NoticeBox, OrganisationUnitTree } from '@dhis2/ui' import { IconInfo16 } from '@dhis2/ui-icons' -import React, { useState } from 'react' +import React, { useEffect, useState } from 'react' import { useField } from 'react-final-form' import { useCurrentUserRootOrgUnits } from '../../../lib/user/currentUserStore' import classes from './OrganisationUnitSelector.module.css' @@ -11,7 +11,9 @@ export function OrganisationUnitSelector() { const { input, meta } = useField(fieldName, { format: (value) => value }) const userRootOrgUnits = useCurrentUserRootOrgUnits() const userRootOrgUnitsIds = userRootOrgUnits.map((unit) => `/${unit.id}`) - const [selected, setSelected] = useState<[string] | []>([]) + const [selected, setSelected] = useState<[string] | []>( + input.value?.path ? [input.value.path] : [] + ) const handleChange = (orgUnit: { displayName: string @@ -40,7 +42,10 @@ export function OrganisationUnitSelector() { singleSelection roots={userRootOrgUnitsIds} selected={selected} - initiallyExpanded={userRootOrgUnitsIds} + initiallyExpanded={[ + ...userRootOrgUnitsIds, + ...selected, + ]} /> {input.value?.displayName && ( @@ -48,7 +53,7 @@ export function OrganisationUnitSelector() {

{i18n.t( - 'New organisation unit will be created inside {{displayName}}', + 'Organisation unit will be positioned inside {{displayName}}', { displayName: input.value.displayName } )}

diff --git a/src/pages/organisationUnits/form/organisationUnitSchema.ts b/src/pages/organisationUnits/form/organisationUnitSchema.ts index 320f4ba7..cf3f5af3 100644 --- a/src/pages/organisationUnits/form/organisationUnitSchema.ts +++ b/src/pages/organisationUnits/form/organisationUnitSchema.ts @@ -21,9 +21,21 @@ export const organisationUnitSchema = identifiable parent: z.object({ id: z.string() }).optional(), geometry: z .object({ - longitude: z.string().optional(), - latitude: z.string().optional(), + type: z.literal('Point'), + coordinates: z.array(z.number()).length(2), }) + .or( + z.object({ + type: z.union([ + z.literal('Multipoint'), + z.literal('Linestring'), + z.literal('Multilinestring'), + z.literal('Polygon'), + z.literal('Multipolygon'), + z.literal('Geometrycollection'), + ]), + }) + ) .optional(), programs: referenceCollection.optional().default([]), dataSets: referenceCollection.optional().default([]), From de9f34d9d4a9ed42d92ab50ce0eeb8d08d4bb166 Mon Sep 17 00:00:00 2001 From: Chisomchima Date: Thu, 7 Nov 2024 07:11:58 +0100 Subject: [PATCH 4/5] feat: add dataset lists --- src/pages/dataSets/List.tsx | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 src/pages/dataSets/List.tsx diff --git a/src/pages/dataSets/List.tsx b/src/pages/dataSets/List.tsx new file mode 100644 index 00000000..7133f024 --- /dev/null +++ b/src/pages/dataSets/List.tsx @@ -0,0 +1,6 @@ +import React from 'react' +import { DefaultSectionList } from '../DefaultSectionList' + +export const Component = () => ( + +) From 9d306d515be17beca6f2df2ddcb9332fac91383a Mon Sep 17 00:00:00 2001 From: "@dhis2-bot" Date: Thu, 7 Nov 2024 06:16:59 +0000 Subject: [PATCH 5/5] chore(release): cut 0.12.0 [skip release] # [0.12.0](https://github.com/dhis2/maintenance-app-beta/compare/v0.11.0...v0.12.0) (2024-11-07) ### Features * add dataset lists ([de9f34d](https://github.com/dhis2/maintenance-app-beta/commit/de9f34d9d4a9ed42d92ab50ce0eeb8d08d4bb166)) * add org unit edit form ([#430](https://github.com/dhis2/maintenance-app-beta/issues/430)) ([79a1892](https://github.com/dhis2/maintenance-app-beta/commit/79a18921c876a36d357edee88f38114bc412a0a9)) --- CHANGELOG.md | 8 ++++++++ package.json | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6838f021..1c3adfa7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,11 @@ +# [0.12.0](https://github.com/dhis2/maintenance-app-beta/compare/v0.11.0...v0.12.0) (2024-11-07) + + +### Features + +* add dataset lists ([de9f34d](https://github.com/dhis2/maintenance-app-beta/commit/de9f34d9d4a9ed42d92ab50ce0eeb8d08d4bb166)) +* add org unit edit form ([#430](https://github.com/dhis2/maintenance-app-beta/issues/430)) ([79a1892](https://github.com/dhis2/maintenance-app-beta/commit/79a18921c876a36d357edee88f38114bc412a0a9)) + # [0.11.0](https://github.com/dhis2/maintenance-app-beta/compare/v0.10.2...v0.11.0) (2024-11-05) diff --git a/package.json b/package.json index dd29e5ef..d35a365f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "maintenance-app", - "version": "0.11.0", + "version": "0.12.0", "description": "", "license": "BSD-3-Clause", "private": true,