diff --git a/CHANGELOG.md b/CHANGELOG.md index 1a60c6ba..2d5c19b8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,11 @@ +## [0.10.2](https://github.com/dhis2/maintenance-app-beta/compare/v0.10.1...v0.10.2) (2024-10-30) + + +### Bug Fixes + +* **categoryOptionCombo:** add ignore approval field ([#423](https://github.com/dhis2/maintenance-app-beta/issues/423)) ([a5c7ac5](https://github.com/dhis2/maintenance-app-beta/commit/a5c7ac5613fa87afb4100aff26b0901c4262509c)) +* **manageView:** stable initialValues for form ([#424](https://github.com/dhis2/maintenance-app-beta/issues/424)) ([2c5066a](https://github.com/dhis2/maintenance-app-beta/commit/2c5066aa28c2cea30117a24d6cca1d6f31dde358)) + ## [0.10.1](https://github.com/dhis2/maintenance-app-beta/compare/v0.10.0...v0.10.1) (2024-10-24) diff --git a/package.json b/package.json index 619f763c..bcf9008f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "maintenance-app", - "version": "0.10.1", + "version": "0.10.2", "description": "", "license": "BSD-3-Clause", "private": true, diff --git a/src/components/sectionList/filters/DynamicFilters.tsx b/src/components/sectionList/filters/DynamicFilters.tsx index e8c66bbe..6f4ef7b6 100644 --- a/src/components/sectionList/filters/DynamicFilters.tsx +++ b/src/components/sectionList/filters/DynamicFilters.tsx @@ -3,13 +3,15 @@ import { ConfigurableFilterKey } from './../../../lib' import { AggregationTypeFilter, CategoryComboFilter, + Categoryfilter, + CategoryOptionFilter, + CategoryOptionGroupFilter, + DataDimensionTypeFilter, DataSetFilter, DomainTypeSelectionFilter, - ValueTypeSelectionFilter, + IgnoreApprovalFilter, PublicAccessFilter, - DataDimensionTypeFilter, - Categoryfilter, - CategoryOptionGroupFilter, + ValueTypeSelectionFilter, } from './filterSelectors' import { useFilterKeys } from './useFilterKeys' @@ -17,6 +19,7 @@ type FilterKeyToComponentMap = Partial> const filterKeyToComponentMap: FilterKeyToComponentMap = { category: Categoryfilter, + categoryOption: CategoryOptionFilter, categoryCombo: CategoryComboFilter, categoryOptionGroup: CategoryOptionGroupFilter, dataSet: DataSetFilter, @@ -25,6 +28,7 @@ const filterKeyToComponentMap: FilterKeyToComponentMap = { aggregationType: AggregationTypeFilter, publicAccess: PublicAccessFilter, dataDimensionType: DataDimensionTypeFilter, + ignoreApproval: IgnoreApprovalFilter, } export const DynamicFilters = () => { diff --git a/src/components/sectionList/filters/filterSelectors/BooleanFilters.tsx b/src/components/sectionList/filters/filterSelectors/BooleanFilters.tsx new file mode 100644 index 00000000..4cdf3b9c --- /dev/null +++ b/src/components/sectionList/filters/filterSelectors/BooleanFilters.tsx @@ -0,0 +1,35 @@ +import i18n from '@dhis2/d2-i18n' +import { CheckboxField } from '@dhis2/ui' +import React from 'react' +import { + BooleanFilterKey, + getTranslatedProperty, + useSectionListFilter, +} from '../../../../lib' + +type BooleanFilterProps = { + label: string + filterKey: BooleanFilterKey +} + +export const BooleanFilter = ({ filterKey, label }: BooleanFilterProps) => { + const [filter, setFilter] = useSectionListFilter(filterKey) + + return ( + setFilter(payload.checked)} + /> + ) +} + +export const IgnoreApprovalFilter = () => { + return ( + + ) +} diff --git a/src/components/sectionList/filters/filterSelectors/CategoryOptionFilter.tsx b/src/components/sectionList/filters/filterSelectors/CategoryOptionFilter.tsx new file mode 100644 index 00000000..efe50696 --- /dev/null +++ b/src/components/sectionList/filters/filterSelectors/CategoryOptionFilter.tsx @@ -0,0 +1,24 @@ +import i18n from '@dhis2/d2-i18n' +import React from 'react' +import { useSectionListFilter } from '../../../../lib' +import { createFilterDataQuery } from './createFilterDataQuery' +import { ModelFilterSelect } from './ModelFilter' + +const query = createFilterDataQuery('categoryOptions') + +export const CategoryOptionFilter = () => { + const [filter, setFilter] = useSectionListFilter('categoryOption') + + const selected = filter?.[0] + + return ( + + setFilter(selected ? [selected] : undefined) + } + /> + ) +} diff --git a/src/components/sectionList/filters/filterSelectors/ConstantSelectionFilter.tsx b/src/components/sectionList/filters/filterSelectors/ConstantSelectionFilter.tsx index b6932eef..c04af896 100644 --- a/src/components/sectionList/filters/filterSelectors/ConstantSelectionFilter.tsx +++ b/src/components/sectionList/filters/filterSelectors/ConstantSelectionFilter.tsx @@ -1,14 +1,14 @@ import i18n from '@dhis2/d2-i18n' import { SingleSelect, SingleSelectOption } from '@dhis2/ui' import React from 'react' -import { FilterKey, useSectionListFilter } from '../../../../lib' +import { StringFilterKey, useSectionListFilter } from '../../../../lib' import { SelectOnChangeObject } from '../../../../types' import css from './Filters.module.css' type ConstantSelectionFilterProps = { label: string constants: Record - filterKey: FilterKey + filterKey: StringFilterKey filterable?: boolean formatFilter?: (filter: string | undefined) => string | undefined } diff --git a/src/components/sectionList/filters/filterSelectors/index.ts b/src/components/sectionList/filters/filterSelectors/index.ts index f6b09ff6..6c0cd7e9 100644 --- a/src/components/sectionList/filters/filterSelectors/index.ts +++ b/src/components/sectionList/filters/filterSelectors/index.ts @@ -6,3 +6,5 @@ export * from './ConstantSelectionFilter' export * from './PublicAccessFilter' export * from './CategoryFilter' export * from './CategoryOptionGroupFilter' +export * from './BooleanFilters' +export * from './CategoryOptionFilter' diff --git a/src/components/sectionList/listView/ManageListView.tsx b/src/components/sectionList/listView/ManageListView.tsx index e78269c7..3f16db8b 100644 --- a/src/components/sectionList/listView/ManageListView.tsx +++ b/src/components/sectionList/listView/ManageListView.tsx @@ -61,11 +61,20 @@ export const ManageListView = ({ const section = useModelSectionHandleOrThrow() const { saveView } = useMutateModelListViews() - const columnsConfig = getColumnsForSection(section.name) - const filtersConfig = getFiltersForSection(section.name) + const { defaultColumns, defaultFilters, columnsConfig, filtersConfig } = + useMemo(() => { + const columnsConfig = getColumnsForSection(section.name) + const filtersConfig = getFiltersForSection(section.name) - const defaultColumns = columnsConfig.default.map(toPath) - const defaultFilters = filtersConfig.default.map(toFilterKey) + const defaultColumns = columnsConfig.default.map(toPath) + const defaultFilters = filtersConfig.default.map(toFilterKey) + return { + defaultColumns, + defaultFilters, + columnsConfig, + filtersConfig, + } + }, [section.name]) const handleSave = async (values: FormValues) => { const isDefault = (arr: string[], def: string[]) => diff --git a/src/lib/constants/translatedModelProperties.ts b/src/lib/constants/translatedModelProperties.ts index 6a51e8fe..0e9e10b9 100644 --- a/src/lib/constants/translatedModelProperties.ts +++ b/src/lib/constants/translatedModelProperties.ts @@ -3,6 +3,7 @@ import i18n from '@dhis2/d2-i18n' const TRANSLATED_PROPERTY: Record = { aggregationType: i18n.t('Aggregation type'), categoryCombo: i18n.t('Category combination'), + categoryOption: i18n.t('Category option'), code: i18n.t('Code'), createdBy: i18n.t('Created by'), description: i18n.t('Description'), @@ -22,6 +23,7 @@ const TRANSLATED_PROPERTY: Record = { user: i18n.t('Owner'), // user refers to the owner of the object zeroIsSignificant: i18n.t('Zero is significant'), dataDimensionType: i18n.t('Data dimension type'), + ignoreApproval: i18n.t('Ignore data approval'), } const camelCaseToSentenceCase = (camelCase: string) => diff --git a/src/lib/sectionList/filters/filterConfig.tsx b/src/lib/sectionList/filters/filterConfig.tsx index fafb00eb..18b174be 100644 --- a/src/lib/sectionList/filters/filterConfig.tsx +++ b/src/lib/sectionList/filters/filterConfig.tsx @@ -1,6 +1,7 @@ -import { StringParam } from 'use-query-params' +import { BooleanParam, StringParam } from 'use-query-params' import { z } from 'zod' import { Category, DataElement } from '../../../types/generated' +import { KeysOfValue } from '../../../types/utility' import { IDENTIFIABLE_FILTER_KEY } from '../../constants' import { isValidUid, parseAccessString } from '../../models' import { CustomDelimitedArrayParam } from './customParams' @@ -14,6 +15,7 @@ export const filterParamsSchema = z aggregationType: z.array(z.nativeEnum(DataElement.aggregationType)), categoryCombo: zodArrayIds, category: zodArrayIds, + categoryOption: zodArrayIds, categoryOptionGroup: zodArrayIds, dataSet: zodArrayIds, domainType: z.array(z.nativeEnum(DataElement.domainType)), @@ -22,6 +24,7 @@ export const filterParamsSchema = z ), valueType: z.array(z.nativeEnum(DataElement.valueType)), dataDimensionType: z.nativeEnum(Category.dataDimensionType), + ignoreApproval: z.boolean(), }) .partial() @@ -34,10 +37,12 @@ export const filterQueryParamType = { valueType: CustomDelimitedArrayParam, dataSet: CustomDelimitedArrayParam, category: CustomDelimitedArrayParam, + categoryOption: CustomDelimitedArrayParam, categoryCombo: CustomDelimitedArrayParam, categoryOptionGroup: CustomDelimitedArrayParam, publicAccess: CustomDelimitedArrayParam, dataDimensionType: StringParam, + ignoreApproval: BooleanParam, } as const satisfies QueryParamsConfigMap export const validFilterKeys = Object.keys(filterQueryParamType) @@ -47,6 +52,8 @@ export type ParsedFilterParams = z.infer type MapZodTypeToQueryParamConfig = TZodResultType extends string ? typeof StringParam + : TZodResultType extends boolean + ? typeof BooleanParam : typeof CustomDelimitedArrayParam /* Type is just used to verify that the ParamType-config matches the zod schema @@ -64,4 +71,14 @@ export type ConfigurableFilterKey = Exclude< typeof IDENTIFIABLE_FILTER_KEY > +export type BooleanFilterKey = KeysOfValue< + ParsedFilterParams, + boolean | undefined +> + +export type StringFilterKey = KeysOfValue< + ParsedFilterParams, + string[] | string | undefined +> + export type FilterKeys = FilterKey[] diff --git a/src/lib/sectionList/filters/parseFiltersToQueryParams.ts b/src/lib/sectionList/filters/parseFiltersToQueryParams.ts index bae10113..b421a5a2 100644 --- a/src/lib/sectionList/filters/parseFiltersToQueryParams.ts +++ b/src/lib/sectionList/filters/parseFiltersToQueryParams.ts @@ -31,6 +31,7 @@ const defaultFilter = ( const filterToQueryParamMap: FilterToQueryParamsMap = { identifiable: (value) => `identifiable:token:${value}`, category: (value) => inFilter('categories.id', value), + categoryOption: (value) => inFilter('categoryOptions.id', value), categoryCombo: (value, section) => { if (section.name === 'category') { return inFilter('categoryCombos.id', value) diff --git a/src/lib/sectionList/listViews/sectionListViewsConfig.ts b/src/lib/sectionList/listViews/sectionListViewsConfig.ts index 4d0cbec6..98288a06 100644 --- a/src/lib/sectionList/listViews/sectionListViewsConfig.ts +++ b/src/lib/sectionList/listViews/sectionListViewsConfig.ts @@ -163,6 +163,21 @@ export const modelListViewsConfig = { }, filters: { default: ['dataDimensionType', 'category'], + available: ['ignoreApproval'], + }, + }, + categoryOptionCombo: { + columns: { + default: ['name', 'code', 'lastUpdated'], + available: ['categoryCombo', 'ignoreApproval'], + // categoryOptionCombo does not have publicAccess + overrideDefaultAvailable: true, + }, + filters: { + default: ['categoryOption', 'categoryCombo'], + available: ['ignoreApproval'], + // categoryOptionCombo does not have publicAccess + overrideDefaultAvailable: true, }, }, } satisfies SectionListViewConfig diff --git a/src/pages/categoryOptionCombos/Edit.tsx b/src/pages/categoryOptionCombos/Edit.tsx index bf2d50b6..abbfc4b3 100644 --- a/src/pages/categoryOptionCombos/Edit.tsx +++ b/src/pages/categoryOptionCombos/Edit.tsx @@ -20,6 +20,7 @@ const fieldFilters = [ ...ATTRIBUTE_VALUES_FIELD_FILTERS, 'name', 'code', + 'ignoreApproval', ] as const export type CategoryOptionComboFormValues = PickWithFieldFilters< diff --git a/src/pages/categoryOptionCombos/form/CategoryOptionComboFormFields.tsx b/src/pages/categoryOptionCombos/form/CategoryOptionComboFormFields.tsx index e885dcb1..a9ee12b5 100644 --- a/src/pages/categoryOptionCombos/form/CategoryOptionComboFormFields.tsx +++ b/src/pages/categoryOptionCombos/form/CategoryOptionComboFormFields.tsx @@ -1,5 +1,5 @@ import i18n from '@dhis2/d2-i18n' -import { InputFieldFF } from '@dhis2/ui' +import { CheckboxFieldFF, InputFieldFF } from '@dhis2/ui' import React from 'react' import { Field } from 'react-final-form' import { @@ -39,6 +39,15 @@ export const CategoryOptionComboFormFields = () => { + + + diff --git a/src/pages/categoryOptionCombos/form/categoryOptionComboSchema.ts b/src/pages/categoryOptionCombos/form/categoryOptionComboSchema.ts index 8f71b4ce..ae71abbc 100644 --- a/src/pages/categoryOptionCombos/form/categoryOptionComboSchema.ts +++ b/src/pages/categoryOptionCombos/form/categoryOptionComboSchema.ts @@ -8,6 +8,7 @@ const { withAttributeValues } = modelFormSchemas export const categoryOptionComboSchema = withAttributeValues.extend({ id: z.string(), code: z.string().trim().optional(), + ignoreApproval: z.boolean().optional().default(false), }) export const initialValues = getDefaults(categoryOptionComboSchema) diff --git a/src/types/utility.ts b/src/types/utility.ts index 74b22519..41d3a3e4 100644 --- a/src/types/utility.ts +++ b/src/types/utility.ts @@ -1 +1,5 @@ export type * from './generated/utility' + +export type KeysOfValue = keyof { + [K in keyof T as T[K] extends TCondition ? K : never]: K +}