Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

feat: new org unit form #421

Merged
merged 33 commits into from
Oct 22, 2024
Merged
Show file tree
Hide file tree
Changes from 28 commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
377d952
feat: add details panel to org list
flaminic Sep 12, 2024
7ce1d6c
feat: add tests for additionsl org unit list features
flaminic Sep 16, 2024
3a911e2
feat: first version of teh add org unit form
flaminic Sep 18, 2024
e2645ab
feat: add validations to fields, and image component
flaminic Sep 26, 2024
1e9fb31
feat: add org unit parent selector for new org unit form
flaminic Oct 3, 2024
27effe2
feat: merge remote-tracking branch origin
flaminic Oct 3, 2024
bef36c0
feat: change org unit form to use new format
flaminic Oct 3, 2024
900cb4a
feat: fix typing and lint
flaminic Oct 8, 2024
4764832
feat(categoryOptions): add categoryOptions form
Birkbjo Aug 29, 2024
181f915
feat: add filters to categoryOption list
Birkbjo Aug 29, 2024
895aa1f
fix: add form-name field, add availability header
Birkbjo Oct 1, 2024
e96f6f8
fix: blur datefield on date select
Birkbjo Oct 4, 2024
2adbf48
fix: improve getDefaults type
Birkbjo Oct 4, 2024
0529801
refactor: use common attributeValues field-filter
Birkbjo Oct 4, 2024
e3b68a6
fix(datefield): useSystemSettings instead of settings
Birkbjo Oct 8, 2024
74fd9fe
fix(datefield): fix inputWidth
Birkbjo Oct 9, 2024
953d835
fix: update multi-calendar-dates dep
Birkbjo Oct 10, 2024
9123ed4
fix(orgunitfield): make orgUnit model available
Birkbjo Oct 10, 2024
b3bbbbf
fix(categoryOption): fix endDate validation
Birkbjo Oct 10, 2024
624b38d
fix: update ui to alpha
Birkbjo Oct 10, 2024
66a93d6
style: sort imports
Birkbjo Oct 10, 2024
c20ef4e
style: fix lint
Birkbjo Oct 10, 2024
b7af2cd
style: cleanup
Birkbjo Oct 10, 2024
2028d54
fix: minor cleanup
Birkbjo Oct 11, 2024
a138a3f
feat: merge branch category-options-form
flaminic Oct 15, 2024
1795788
feat: refactor component to use shared components created in other pr
flaminic Oct 15, 2024
bbb94fa
fix: fix image selector when there is no image and org unit selectors…
flaminic Oct 16, 2024
8bd6709
feat: merge remote-tracking branch origin
flaminic Oct 17, 2024
8d62c32
fix: minor changes to address pr comments
flaminic Oct 21, 2024
042e3b2
fix: fix uploadFile mutation
Birkbjo Oct 21, 2024
8afd1cc
fix(ImageField): add image preview
Birkbjo Oct 21, 2024
71f9266
fix: add missing ImageField
Birkbjo Oct 22, 2024
dbd0df2
feat: small fixes in org unit and image field
flaminic Oct 22, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
118 changes: 95 additions & 23 deletions i18n/en.pot
Original file line number Diff line number Diff line change
Expand Up @@ -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-09-30T15:09:50.864Z\n"
"PO-Revision-Date: 2024-09-30T15:09:50.864Z\n"
"POT-Creation-Date: 2024-10-16T11:01:15.583Z\n"
"PO-Revision-Date: 2024-10-16T11:01:15.584Z\n"

msgid "schemas"
msgstr "schemas"
Expand Down Expand Up @@ -108,6 +108,9 @@ msgstr "{{fieldLabel}} (required)"
msgid "Name"
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"

Expand All @@ -120,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)"

Expand Down Expand Up @@ -234,6 +222,9 @@ msgstr "Created"
msgid "Last updated by"
msgstr "Last updated by"

msgid "Last updated"
msgstr "Last updated"

msgid "Id"
msgstr "Id"

Expand All @@ -255,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}}"

Expand Down Expand Up @@ -294,6 +288,9 @@ msgstr "Clear all filters"
msgid "Category"
msgstr "Category"

msgid "Category option group"
msgstr "Category option group"

msgid "Type to filter options"
msgstr "Type to filter options"

Expand Down Expand Up @@ -447,9 +444,6 @@ msgstr "Category option combination"
msgid "Category option combinations"
msgstr "Category option combinations"

msgid "Category option group"
msgstr "Category option group"

msgid "Category option groups"
msgstr "Category option groups"

Expand Down Expand Up @@ -933,6 +927,25 @@ 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."
msgstr "Set up the basic information for this category option."

msgid "An alternative name used in section or automatic data entry forms."
msgstr "An alternative name used in section or automatic data entry forms."

msgid "Availability configuration"
msgstr "Availability configuration"

msgid ""
"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 "
"available."

msgid "End date should be after start date"
msgstr "End date should be after start date"

msgid "Create data element group"
msgstr "Create data element group"

Expand Down Expand Up @@ -991,9 +1004,6 @@ msgstr "Use a pattern to limit what information can be entered."
msgid "e.g. 999-000-0000"
msgstr "e.g. 999-000-0000"

msgid "An alternative name used in section or automatic data entry forms."
msgstr "An alternative name used in section or automatic data entry forms."

msgid "Selected legends"
msgstr "Selected legends"

Expand Down Expand Up @@ -1065,6 +1075,68 @@ msgstr ""
"included. PHU will still be available for the PHU level, but not included "
"in the aggregations to the levels above."

msgid "Upload an image"
msgstr "Upload an image"

msgid "Remove"
msgstr "Remove"

msgid "Placement in hierarchy"
msgstr "Placement in hierarchy"

msgid ""
"Choose where this new organisation unit should be placed in the existing "
"hierarchy"
msgstr ""
"Choose where this new organisation unit should be placed in the existing "
"hierarchy"

msgid "Set up the basic information for this organisation unit."
msgstr "Set up the basic information for this organisation unit."

msgid "Opening date"
msgstr "Opening date"

msgid "Closed date"
msgstr "Closed date"

msgid "Comment"
msgstr "Comment"

msgid "Contact person"
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"

msgid "Set up the organisation unit location."
msgstr "Set up the organisation unit location."

msgid "Latitude"
msgstr "Latitude"

msgid "Longitude"
msgstr "Longitude"

msgid "New organisation unit will be created inside"
msgstr "New organisation unit will be created inside"

msgid "Creating first organisation unit"
msgstr "Creating first organisation unit"

msgid ""
"This is the first organisation unit and will be created as the root of the "
"hierarchy."
msgstr ""
"This is the first organisation unit and will be created as the root of the "
"hierarchy."

msgid "No organisation units available"
msgstr "No organisation units available"

Expand Down
13 changes: 13 additions & 0 deletions src/components/form/attributes/CustomAttributes.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,19 @@ function CustomAttribute({ attribute, index }: CustomAttributeProps) {
)
}

if (attribute.valueType === 'GEOJSON') {
return (
<StandardFormSection key={attribute.id}>
<FieldRFF
component={TextAreaFieldFF}
required={required}
inputWidth={inputWidth}
label={attribute.displayFormName}
name={name}
/>
</StandardFormSection>
)
}
// @TODO: Verify that all value types have been covered!
throw new Error(`Implement value type "${attribute.valueType}"!`)
}
Expand Down
7 changes: 4 additions & 3 deletions src/components/form/fields/CodeField.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@ import i18n from '@dhis2/d2-i18n'
import { InputFieldFF } from '@dhis2/ui'
import React from 'react'
import { Field as FieldRFF } from 'react-final-form'
import { SchemaSection, useCheckMaxLengthFromSchema } from '../../../lib'
import { SchemaSection } from '../../../lib'
import { useValidator } from '../../../lib/models/useFieldValidators'

export function CodeField({ schemaSection }: { schemaSection: SchemaSection }) {
const validate = useCheckMaxLengthFromSchema(schemaSection.name, 'code')
const validator = useValidator({ schemaSection, property: 'code' })

return (
<FieldRFF
Expand All @@ -15,7 +16,7 @@ export function CodeField({ schemaSection }: { schemaSection: SchemaSection }) {
name="code"
label={i18n.t('Code')}
validateFields={[]}
validate={validate}
validate={(code?: string) => validator(code)}
/>
)
}
24 changes: 19 additions & 5 deletions src/components/form/fields/OrganisationUnitField.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,20 +10,28 @@ import { useCurrentUserRootOrgUnits } from '../../../lib/user/currentUserStore'

type OrganisationUnitFieldProps = {
name?: string
singleSelection?: boolean
onChange?: (orgUnits: OrganisationUnitFormValue[]) => void
}

type OrganisationUnitFormValue = {
export type OrganisationUnitFormValue = {
id: string
path: string
displayName: string
}

export const OrganisationUnitField = ({ name }: OrganisationUnitFieldProps) => {
export const OrganisationUnitField = ({
name,
onChange,
singleSelection = false,
}: OrganisationUnitFieldProps) => {
const { input, meta } = useField<
OrganisationUnitFormValue[],
OrganisationUnitFormValue[] | '',
HTMLElement,
OrganisationUnitFormValue[]
>(name ?? 'organisationUnits')
>(name ?? 'organisationUnits', {
format: (value) => (value === '' ? [] : value),
})

const roots = useCurrentUserRootOrgUnits()

Expand All @@ -35,14 +43,19 @@ export const OrganisationUnitField = ({ name }: OrganisationUnitFieldProps) => {
id,
path,
}) => {
const prevSelected = new Map(input.value.map((ou) => [ou.path, ou]))
const prevSelected = input.value
? new Map(input.value.map((ou) => [ou.path, ou]))
: new Map()
const newSelected = selected.map((selectedPath) => {
const prev = prevSelected.get(selectedPath)
return prev ?? { id, path, displayName }
})

input.onChange(newSelected)
input.onBlur()
if (onChange) {
onChange(newSelected)
}
}

const selectedPaths = input.value?.map((ou) => ou.path) ?? []
Expand All @@ -55,6 +68,7 @@ export const OrganisationUnitField = ({ name }: OrganisationUnitFieldProps) => {
>
<OrganisationUnitTree
roots={rootIds}
singleSelection={singleSelection}
onChange={handleChange}
selected={selectedPaths}
initiallyExpanded={rootIds}
Expand Down
7 changes: 6 additions & 1 deletion src/lib/form/useOnSubmit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,10 @@ export const useOnSubmitEdit = <TFormValues extends IdentifiableObject>({

export const useOnSubmitNew = <TFormValues>({
section,
valueFormatter,
}: {
section: ModelSection
valueFormatter?: (values: TFormValues) => Record<string, unknown>
Birkbjo marked this conversation as resolved.
Show resolved Hide resolved
}) => {
const createModel = useCreateModel(section.namePlural)
const saveAlert = useAlert(
Expand All @@ -80,7 +82,10 @@ export const useOnSubmitNew = <TFormValues>({
})
return
}
const errors = await createModel(values)
const formattedValues = valueFormatter
? valueFormatter(values)
: values
const errors = await createModel(formattedValues)
if (errors) {
return errors
}
Expand Down
10 changes: 8 additions & 2 deletions src/lib/models/useCheckMaxLengthFromSchema.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,20 @@
import { createMaxCharacterLength } from '@dhis2/ui'
import { useMemo } from 'react'
import { SchemaName } from '../../types'
import { SchemaFieldProperty, SchemaName } from '../../types'
import { useSchema } from '../schemas'

export function useCheckMaxLengthFromSchema(
Birkbjo marked this conversation as resolved.
Show resolved Hide resolved
model: SchemaName,
property: string
) {
const schema = useSchema(model)
const maxLength = schema.properties[property].length
return useCheckMaxLengthFromProperty(schema.properties[property])
}

export function useCheckMaxLengthFromProperty(
Birkbjo marked this conversation as resolved.
Show resolved Hide resolved
propertyDetails: SchemaFieldProperty
) {
const maxLength = propertyDetails.length
const checkMaxLength = useMemo(
() =>
maxLength == undefined
Expand Down
43 changes: 43 additions & 0 deletions src/lib/models/useFieldValidators.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { Validator } from '@dhis2/ui'
import { useMemo } from 'react'
import { useParams } from 'react-router-dom'
import { SchemaFieldPropertyType, SchemaSection } from '../../types'
import { composeAsyncValidators, required } from '../form'
import { useSchema } from '../schemas'
import { useCheckMaxLengthFromProperty } from './useCheckMaxLengthFromSchema'
import { useIsFieldValueUnique } from './useIsFieldValueUnique'

export function useValidator({
schemaSection,
property,
}: {
schemaSection: SchemaSection
property: string
}) {
const schema = useSchema(schemaSection.name)
const propertyDetails = schema.properties[property]

const validators = useMemo(() => [] as Validator[], [])
const params = useParams()
const modelId = params.id as string
const checkMaxLength = useCheckMaxLengthFromProperty(propertyDetails)
const checkIsValueTaken = useIsFieldValueUnique({
model: schemaSection.namePlural,
field: property,
id: modelId,
}) as Validator
if (propertyDetails.propertyType === SchemaFieldPropertyType.REFERENCE) {
validators.push(checkMaxLength)
}
if (propertyDetails.unique) {
validators.push(checkIsValueTaken)
}
if (propertyDetails.required) {
validators.push(required)
}

return useMemo(
() => composeAsyncValidators<string>(validators),
[validators]
)
}
1 change: 0 additions & 1 deletion src/pages/dataElements/form/dataElementSchema.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { z } from 'zod'
import { DataElement } from '../../../types/generated'

export const dataElementSchema = z
.object({
Expand Down
Loading
Loading