Skip to content

Commit

Permalink
feat: add data element group New and Edit views
Browse files Browse the repository at this point in the history
  • Loading branch information
Mohammer5 committed Jan 22, 2024
1 parent 30bd07a commit ced3ce0
Show file tree
Hide file tree
Showing 28 changed files with 1,094 additions and 40 deletions.
79 changes: 41 additions & 38 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: 2023-11-23T14:09:23.068Z\n"
"PO-Revision-Date: 2023-11-23T14:09:23.068Z\n"
"POT-Creation-Date: 2024-01-22T08:44:17.768Z\n"
"PO-Revision-Date: 2024-01-22T08:44:17.768Z\n"

msgid "schemas"
msgstr "schemas"
Expand Down Expand Up @@ -612,9 +612,6 @@ msgstr "Name"
msgid "Sharing"
msgstr "Sharing"

msgid "Value type"
msgstr "Value type"

msgid "Owner"
msgstr "Owner"

Expand All @@ -627,21 +624,54 @@ msgstr "This field requires a unique value, please choose another one"
msgid "Required"
msgstr "Required"

msgid "Custom attributes"
msgstr "Custom attributes"

msgid "Something went wrong when submitting the form"
msgstr "Something went wrong when submitting the form"

msgid "Save and close"
msgstr "Save and close"

msgid "Exit without saving"
msgstr "Exit without saving"

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

msgid "Selected legends"
msgstr "Selected legends"

msgid "Refresh list"
msgstr "Refresh list"

msgid "Add new"
msgstr "Add new"

msgid "Description"
msgstr "Description"

msgid "Explain the purpose of this data element and how it's measured."
msgstr "Explain the purpose of this data element and how it's measured."

msgid "{{fieldLabel}} (required)"
msgstr "{{fieldLabel}} (required)"

msgid "A data element name should be concise and easy to recognize."
msgstr "A data element name should be concise and easy to recognize."

msgid "Often used in reports where space is limited"
msgstr "Often used in reports where space is limited"

msgid "Basic information"
msgstr "Basic information"

msgid "Set up the information for this data element group"
msgstr "Set up the information for this data element group"

msgid "@TODO"
msgstr "@TODO"

msgid "Custom attributes"
msgstr "Custom attributes"

msgid "Save and close"
msgstr "Save and close"

msgid "Choose how this data element is disaggregated"
msgstr "Choose how this data element is disaggregated"

Expand All @@ -651,9 +681,6 @@ 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 "{{fieldLabel}} (required)"
msgstr "{{fieldLabel}} (required)"

msgid "Aggregation type"
msgstr "Aggregation type"

Expand All @@ -673,12 +700,6 @@ msgstr "Loading custom attributes"
msgid "Something went wrong with retrieving the custom attributes"
msgstr "Something went wrong with retrieving the custom attributes"

msgid "Description"
msgstr "Description"

msgid "Explain the purpose of this data element and how it's measured."
msgstr "Explain the purpose of this data element and how it's measured."

msgid "A data element can either be aggregated or tracked data."
msgstr "A data element can either be aggregated or tracked data."

Expand All @@ -697,18 +718,6 @@ msgstr "StandardForm name"
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"

msgid "Refresh list"
msgstr "Refresh list"

msgid "Add new"
msgstr "Add new"

msgid "A data element name should be concise and easy to recognize."
msgstr "A data element name should be concise and easy to recognize."

msgid "Option set comment"
msgstr "Option set comment"

Expand All @@ -718,9 +727,6 @@ msgstr "Choose a set of predefined comment for data entry"
msgid "Choose a set of predefined options for data entry"
msgstr "Choose a set of predefined options for data entry"

msgid "Often used in reports where space is limited"
msgstr "Often used in reports where space is limited"

msgid "Url"
msgstr "Url"

Expand All @@ -740,9 +746,6 @@ msgstr ""
msgid "Store zero data values"
msgstr "Store zero data values"

msgid "Basic information"
msgstr "Basic information"

msgid "Set up the information for this data element"
msgstr "Set up the information for this data element"

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
import i18n from '@dhis2/d2-i18n'
import { Transfer } from '@dhis2/ui'
import React, {
ReactElement,
forwardRef,
useCallback,
useImperativeHandle,
useRef,
useState,
} from 'react'
import { SelectOption } from '../../../types'
import { useInitialOptionQuery } from './useInitialOptionQuery'
import { useOptionsQuery } from './useOptionsQuery'

function computeDisplayOptions({
selected,
selectedOptions,
options,
}: {
options: SelectOption[]
selected: string[]
selectedOptions: SelectOption[]
}): SelectOption[] {
// This happens only when we haven't fetched the lable for an initially
// selected value. Don't show anything to prevent error that an option is
// missing
if (!selectedOptions.length && selected.length) {
return []
}

const missingSelectedOptions = selectedOptions.filter((selectedOption) => {
return !options?.find((option) => option.value === selectedOption.value)
})

return [...options, ...missingSelectedOptions]
}

interface DataElementsSelectProps {
onChange: ({ selected }: { selected: string[] }) => void
selected: string[]
rightHeader?: ReactElement
rightFooter?: ReactElement
leftFooter?: ReactElement
leftHeader?: ReactElement
}

export const DataElementsTransfer = forwardRef(function DataElementsSelect(
{
onChange,
selected,
rightHeader,
rightFooter,
leftFooter,
leftHeader,
}: DataElementsSelectProps,
ref
) {
// Using a ref because we don't want to react to changes.
// We're using this value only when imperatively calling `refetch`,
// nothing that depends on the render-cycle depends on this value
const [searchTerm, setSearchTerm] = useState('')
const pageRef = useRef(0)

// We need to persist the selected option so we can display an <Option />
// when the current list doesn't contain the selected option (e.g. when
// the page with the selected option hasn't been reached yet or when
// filtering)
const [selectedOptions, setSelectedOptions] = useState<SelectOption[]>([])

const optionsQuery = useOptionsQuery()
const initialOptionQuery = useInitialOptionQuery({
selected,
onComplete: setSelectedOptions,
})

const { refetch, data } = optionsQuery
const pager = data?.pager
const page = pager?.page || 0
const pageCount = pager?.pageCount || 0

const loading =
optionsQuery.fetching ||
optionsQuery.loading ||
initialOptionQuery.loading
const error =

Check warning on line 85 in src/components/metadataFormControls/DataElementsTransfer/DataElementsTransfer.tsx

View workflow job for this annotation

GitHub Actions / lint

'error' is assigned a value but never used
optionsQuery.error || initialOptionQuery.error
? // @TODO: Ask Joe what do do here!
'An error has occurred. Please try again'
: ''

useImperativeHandle(
ref,
() => ({
refetch: () => {
pageRef.current = 1
refetch({ page: pageRef.current, filter: searchTerm })
},
}),
[refetch, searchTerm]
)

const adjustQueryParamsWithChangedFilter = useCallback(
({ value }: { value: string | undefined }) => {
value = value ?? ''
setSearchTerm(value)
pageRef.current = 1
refetch({ page: pageRef.current, filter: value })
},
[refetch]
)

const incrementPage = useCallback(() => {
if (optionsQuery.loading || page === pageCount) {
return
}

pageRef.current = page + 1
refetch({ page: pageRef.current, filter: searchTerm })
}, [refetch, page, optionsQuery.loading, searchTerm, pageCount])

const displayOptions = computeDisplayOptions({
selected,
selectedOptions,
options: data?.result || [],
})

return (
<Transfer
filterable
filterPlaceholder={i18n.t('Filter data elements')}
searchTerm={searchTerm}
options={displayOptions}
selected={selected}
loading={loading}
onChange={({ selected }: { selected: string[] }) => {
const nextSelectedOptions = displayOptions.filter(({ value }) =>
selected.includes(value)
)
setSelectedOptions(nextSelectedOptions)
onChange({ selected })
}}
onEndReached={incrementPage}
onFilterChange={adjustQueryParamsWithChangedFilter}
rightHeader={rightHeader}
rightFooter={rightFooter}
leftHeader={leftHeader}
leftFooter={leftFooter}
/>
)
})
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { DataElementsTransfer } from './DataElementsTransfer'
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { DataElement, GistCollectionResponse } from '../../../types/generated'

const filterFields = ['id', 'displayName'] as const //(name is translated by default in /gist)
export type FilteredDataElement = Pick<
DataElement,
(typeof filterFields)[number]
>
export type DataElementsQueryResult =
GistCollectionResponse<FilteredDataElement>
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { useDataQuery } from '@dhis2/app-runtime'
import { useRef } from 'react'
import { SelectOption } from '../../../types'
import { FilteredDataElement } from './types'

type InitialDataElementsQueryResult = {
dataElements: {
dataElements: FilteredDataElement[]
}
}

export function useInitialOptionQuery({
selected,
onComplete,
}: {
onComplete: (options: SelectOption[]) => void
selected: string[]
}) {
const initialSelected = useRef(selected)
const query = {
dataElements: {
resource: 'dataElements',
params: {
paging: false,
fields: ['id', 'displayName'],
filter: `id:in:[${initialSelected.current.join(',')}]`,
},
},
}

return useDataQuery<InitialDataElementsQueryResult>(query, {
lazy: !initialSelected.current,
variables: { id: selected },
onComplete: (data) => {
const { dataElements } = data.dataElements
const options = dataElements.map(({ id, displayName }) => ({
value: id,
label: displayName,
}))

onComplete(options)
},
})
}
Loading

0 comments on commit ced3ce0

Please sign in to comment.