diff --git a/.nvmrc b/.nvmrc new file mode 100644 index 0000000000..209e3ef4b6 --- /dev/null +++ b/.nvmrc @@ -0,0 +1 @@ +20 diff --git a/CHANGELOG.md b/CHANGELOG.md index 07c3751112..5873557998 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,31 @@ +## [102.0.6](https://github.com/dhis2/aggregate-data-entry-app/compare/v102.0.5...v102.0.6) (2026-05-13) + + +### Bug Fixes + +* dedupe dhis2-ui ([#558](https://github.com/dhis2/aggregate-data-entry-app/issues/558)) ([dda5851](https://github.com/dhis2/aggregate-data-entry-app/commit/dda58514ede0c38f1c5d9f1e9395ae918980af73)) + +## [102.0.5](https://github.com/dhis2/aggregate-data-entry-app/compare/v102.0.4...v102.0.5) (2026-05-13) + + +### Bug Fixes + +* use gregorian calendar for iso to handle chrome bug ([#557](https://github.com/dhis2/aggregate-data-entry-app/issues/557)) ([2de0805](https://github.com/dhis2/aggregate-data-entry-app/commit/2de0805ba4cbea829ed8b61b329b0baa5b614181)) + +## [102.0.4](https://github.com/dhis2/aggregate-data-entry-app/compare/v102.0.3...v102.0.4) (2026-05-07) + + +### Bug Fixes + +* useHighlightedFieldStore coc logic [DHIS2-20741] ([5635287](https://github.com/dhis2/aggregate-data-entry-app/commit/56352871faad48912b5dfe3ea99db16a1c1f6701)) + +## [102.0.3](https://github.com/dhis2/aggregate-data-entry-app/compare/v102.0.2...v102.0.3) (2026-05-05) + + +### Bug Fixes + +* show delete limits button [DHIS2-19517] ([#552](https://github.com/dhis2/aggregate-data-entry-app/issues/552)) ([8782429](https://github.com/dhis2/aggregate-data-entry-app/commit/8782429b9867b78c3a7af3b1cde91400d8fbc6f6)) + ## [102.0.2](https://github.com/dhis2/aggregate-data-entry-app/compare/v102.0.1...v102.0.2) (2026-04-14) diff --git a/d2.config.js b/d2.config.js index 106d553128..efb2591f28 100644 --- a/d2.config.js +++ b/d2.config.js @@ -22,7 +22,7 @@ const config = { }, entryPoints: { app: './src/app/app-wrapper.jsx', - plugin: './src/plugin-legacy-custom-forms/index.jsx', + plugin: './src/plugin-legacy-custom-forms/main.jsx', }, direction: 'auto', } diff --git a/docs/custom-forms-legacy-plugin.md b/docs/custom-forms-legacy-plugin.md index d052d8a495..ba8dccb83a 100644 --- a/docs/custom-forms-legacy-plugin.md +++ b/docs/custom-forms-legacy-plugin.md @@ -1,6 +1,7 @@ -# Custom Forms support +# Support for JavaScript in custom forms -> Custom Forms support in the Data Entry app was reinstated from version `102.0.0`. To upgrade to this version, go to App Management, Search for "Data Entry" app, then upgrade to version `102.0.0` or later. This is the default version that ships with DHIS2 v43, but also works with earlier versions of DHIS2. +> **Important** +> Support for JavaScript in custom forms was reinstated from version `102.0.0` of the Data Entry app. To upgrade to this version, go to App Management, Search for "Data Entry" app, then upgrade to version `102.0.0` or later. This is the default version that ships with DHIS2 v43, but also works with earlier versions of DHIS2. ![Stable release in App Management app](./data-entry-app-management.png) @@ -63,10 +64,12 @@ For example, all of these global properties remain available, even though the wa We also _tried_ to patch some of these objects so that they look similar to the old app - for example, data sets required a `periodId` field that comes as `period` in the new app. In this case, we patch the object so that both properties are avaialable. +> **Note** > While we tried to anticipate some of these object differences, there is always a slight chance that your forms relied on a property that doesn't exist anymore. In that case, you can update the form accordingly or raise an issue if you think this is a common use case that the shim should handle. - API base URLs: Custom forms had a variety of ways for handling API requests and deciding on the base URL for the DHIS2 instance. We consolidated these so that the shim appends your requests to the correct DHIS2 BASE_URL as defined the instance config. So if you have a call to `/me`, you can just leave it as `/me` and the plugin will append the correct base URL to make the call to `https://play.dhis2.org/42/me`, for example (if your call already specified the base URL correctly, then appending the base URL will be ignored). +> **Important** > This ability to identify and append the base URL only works if you used jQuery AJAX methods (i.e. `$.get` or `jQuery.post`). This seems to be the case for the majority of forms we have seen. If you are doing requests in a different way (using `fetch` for example), then it is your responsibility to construct the URL properly. > > We make a property available in the global window context of the form to make this easier: `window.DHIS2_BASE_URL` @@ -79,6 +82,7 @@ The namespace is necessary to expose certain functionality from the modern app's ### Things that might not work out of the box +> **Tip** > It is important to note that -- for anything that doesn't work -- you now have the ability to change the custom form so that it works (for example, so that it doesn't rely on a method that doesn't exist anyore). The aim of the plugin is mainly to minimise the amount of these changes that you need to apply on a form. #### Relying on internal methods diff --git a/docs/index.yml b/docs/index.yml index b0616abac0..989dcc8d02 100644 --- a/docs/index.yml +++ b/docs/index.yml @@ -1 +1 @@ -- Custom Forms support: 'custom-forms-legacy-plugin.md' +- Support for JavaScript in custom forms: 'custom-forms-legacy-plugin.md' diff --git a/i18n/en.pot b/i18n/en.pot index 01cf251f89..3b4999d3ee 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: 2026-01-26T16:17:18.280Z\n" -"PO-Revision-Date: 2026-01-26T16:17:18.280Z\n" +"POT-Creation-Date: 2026-05-21T08:02:54.840Z\n" +"PO-Revision-Date: 2026-05-21T08:02:54.841Z\n" msgid "There was a problem loading metadata" msgstr "There was a problem loading metadata" @@ -71,6 +71,9 @@ msgstr "Couldn't validate the form. This does not effect form completion!" msgid "The form can't be completed while invalid" msgstr "The form can't be completed while invalid" +msgid "There are compulsory fields which have not been filled out" +msgstr "There are compulsory fields which have not been filled out" + msgid "Compulsory fields must be filled out before completing the form" msgstr "Compulsory fields must be filled out before completing the form" @@ -596,6 +599,12 @@ msgstr "" "Please make sure the value of this input is more than the value in " "\"{{otherField}}\"." +msgid "Error occurred while saving dataValues" +msgstr "Error occurred while saving dataValues" + +msgid "Fill with zeros" +msgstr "Fill with zeros" + msgid "Type here to filter rows in this section" msgstr "Type here to filter rows in this section" @@ -635,6 +644,50 @@ msgstr "Org unit is closed" msgid "Show all items" msgstr "Show all items" +msgid "" +"The value of the following data element is less than the specified minimum " +"value." +msgstr "" +"The value of the following data element is less than the specified minimum " +"value." + +msgid "" +"The value of the following data element is greater than the specified " +"maximum value." +msgstr "" +"The value of the following data element is greater than the specified " +"maximum value." + +msgid "Value is too long" +msgstr "Value is too long" + +msgid "Value must be a number" +msgstr "Value must be a number" + +msgid "Value must be an integer" +msgstr "Value must be an integer" + +msgid "Value must be a positive integer" +msgstr "Value must be a positive integer" + +msgid "Value must be a negative integer" +msgstr "Value must be a negative integer" + +msgid "Value must be zero or a positive integer" +msgstr "Value must be zero or a positive integer" + +msgid "Value must be a coordinate (longitude, latitude)" +msgstr "Value must be a coordinate (longitude, latitude)" + +msgid "Value must be a unit interval (between 0 and 1)" +msgstr "Value must be a unit interval (between 0 and 1)" + +msgid "Value must be a percentage (between 0 and 100)" +msgstr "Value must be a percentage (between 0 and 100)" + +msgid "Value must be a valid url" +msgstr "Value must be a valid url" + msgid "" "Here we could override the message for E2017 error with a custom message in " "the FE" diff --git a/package.json b/package.json index 7cb1ca387a..a2fb02c880 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "aggregate-data-entry-app", - "version": "102.0.2", + "version": "102.0.6-ocba-3", "description": "", "license": "BSD-3-Clause", "private": true, @@ -25,7 +25,8 @@ "cypress:live": "yarn cypress:prepare 'yarn cypress:open:live'", "cypress:capture": "yarn cypress:prepare 'yarn cypress:run:capture'", "cypress:stub": "yarn cypress:prepare 'yarn cypress:run:stub'", - "jgs:start": "BROWSER=none yarn start --proxy https://debug.dhis2.org/dev" + "jgs:start": "BROWSER=none yarn start --proxy https://debug.dhis2.org/dev", + "postinstall": "patch-package" }, "devDependencies": { "@babel/plugin-syntax-dynamic-import": "^7.8.3", @@ -51,8 +52,8 @@ }, "dependencies": { "@dhis2/app-runtime": "^3.14.5", - "@dhis2/multi-calendar-dates": "^1.3.2", - "@dhis2/ui": "^10.1.11", + "@dhis2/multi-calendar-dates": "^2.1.2", + "@dhis2/ui": "^10.14.0", "@dhis2/ui-forms": "7.16.3", "@tanstack/react-query": "4.24.10", "@tanstack/react-query-devtools": "4.24.14", @@ -68,6 +69,8 @@ "html-react-parser": "1.4.14", "idb-keyval": "6.2.0", "lodash.throttle": "4.1.1", + "patch-package": "^8.0.1", + "postinstall-postinstall": "^2.1.0", "prop-types": "15.8.1", "query-string": "7.1.3", "re-reselect": "4.0.1", diff --git a/patches/@dhis2+multi-calendar-dates+2.1.2.patch b/patches/@dhis2+multi-calendar-dates+2.1.2.patch new file mode 100644 index 0000000000..1918a904a4 --- /dev/null +++ b/patches/@dhis2+multi-calendar-dates+2.1.2.patch @@ -0,0 +1,26 @@ +diff --git a/node_modules/@dhis2/multi-calendar-dates/build/cjs/constants/dhis2CalendarsMap.js b/node_modules/@dhis2/multi-calendar-dates/build/cjs/constants/dhis2CalendarsMap.js +index 3ed2119..0f0a1b4 100644 +--- a/node_modules/@dhis2/multi-calendar-dates/build/cjs/constants/dhis2CalendarsMap.js ++++ b/node_modules/@dhis2/multi-calendar-dates/build/cjs/constants/dhis2CalendarsMap.js +@@ -12,7 +12,7 @@ const dhis2CalendarsMap = { + coptic: 'coptic', + gregorian: 'gregory', + islamic: 'islamic', +- iso8601: 'iso8601', ++ iso8601: 'gregory', + // 'Julian': 'julian', // this is not supported by Temporal + nepali: 'nepali', + thai: 'buddhist', +diff --git a/node_modules/@dhis2/multi-calendar-dates/build/es/constants/dhis2CalendarsMap.js b/node_modules/@dhis2/multi-calendar-dates/build/es/constants/dhis2CalendarsMap.js +index 0116053..93710d2 100644 +--- a/node_modules/@dhis2/multi-calendar-dates/build/es/constants/dhis2CalendarsMap.js ++++ b/node_modules/@dhis2/multi-calendar-dates/build/es/constants/dhis2CalendarsMap.js +@@ -6,7 +6,7 @@ export const dhis2CalendarsMap = { + coptic: 'coptic', + gregorian: 'gregory', + islamic: 'islamic', +- iso8601: 'iso8601', ++ iso8601: 'gregory', + // 'Julian': 'julian', // this is not supported by Temporal + nepali: 'nepali', + thai: 'buddhist', diff --git a/src/context-selection/context-selection/context-selection.jsx b/src/context-selection/context-selection/context-selection.jsx index 180648863d..315406ee95 100644 --- a/src/context-selection/context-selection/context-selection.jsx +++ b/src/context-selection/context-selection/context-selection.jsx @@ -6,6 +6,7 @@ import { useClearEntireSelection, useManageInterParamDependencies, } from '../../shared/index.js' +import { usePluginOptions } from '../../shared/plugin-options/index.js' import { AttributeOptionComboSelectorBarItem } from '../attribute-option-combo-selector-bar-item/index.js' import { DataSetSelectorBarItem } from '../data-set-selector-bar-item/index.js' import { OrgUnitSetSelectorBarItem } from '../org-unit-selector-bar-item/index.js' @@ -19,6 +20,7 @@ export default function ContextSelector({ setSelectionHasNoFormMessage }) { useManageInterParamDependencies() const { hide } = useRightHandPanelContext() + const { hideClearSelectionsButton } = usePluginOptions() const hideClearButton = useShouldHideClearButton() const clearEntireSelection = useClearEntireSelection() const onClearSelectionClick = () => { @@ -33,7 +35,11 @@ export default function ContextSelector({ setSelectionHasNoFormMessage }) { return (
} > diff --git a/src/context-selection/data-set-selector-bar-item/data-set-selector-bar-item.jsx b/src/context-selection/data-set-selector-bar-item/data-set-selector-bar-item.jsx index 4aa5e83c82..8dfaa42327 100644 --- a/src/context-selection/data-set-selector-bar-item/data-set-selector-bar-item.jsx +++ b/src/context-selection/data-set-selector-bar-item/data-set-selector-bar-item.jsx @@ -15,6 +15,7 @@ import { useDataSetId, useOrgUnitId, } from '../../shared/index.js' +import { usePluginOptions } from '../../shared/plugin-options/index.js' import styles from './data-set-selector-bar-item.module.css' const FiltrableMenuItems = ({ @@ -156,6 +157,7 @@ DataSetSelectorBarDropDownContent.propTypes = { } export default function DataSetSelectorBarItem() { + const { hideDataSetSelector } = usePluginOptions() const { data: metadata } = useMetadata() const [dataSetOpen, setDataSetOpen] = useState(false) const [dataSetId, setDataSetId] = useDataSetId() @@ -181,6 +183,10 @@ export default function DataSetSelectorBarItem() { } }, [dataSets, setDataSetId]) + if (hideDataSetSelector) { + return null + } + return (
{ + if (dataSetId && filter) { + const dataSetPathSet = new Set(dataSetOrgUnitPaths.data || []) + return (filteredOrgUnitPaths || []).filter((path) => + dataSetPathSet.has(path) + ) + } else if (dataSetId) { + return dataSetOrgUnitPaths.data || [] + } + return filteredOrgUnitPaths || [] + }, [dataSetId, dataSetOrgUnitPaths.data, filter, filteredOrgUnitPaths]) +} + const UnclickableLabel = ({ label }) => { return (
@@ -64,13 +80,25 @@ export default function OrganisationUnitSetSelectorBarItem() { const orgUnit = useOrgUnit() const userOrgUnits = useUserOrgUnits() + const dataSetOrgUnitPaths = useDataSetOrgUnitPaths() + const { hideUnassignedOrgUnits } = usePluginOptions() const selectorBarItemValue = useSelectorBarItemValue() const selected = orgUnit.data ? [orgUnit.data.path] : [] const filteredOrgUnitPaths = filter ? orgUnitPathsByName.data : [] + + const treeFilterPaths = useTreeFilterPaths( + hideUnassignedOrgUnits ? dataSetId : undefined, + dataSetOrgUnitPaths, + filter, + filteredOrgUnitPaths + ) + const orgUnitPathsByNameLoading = // offline levels need to be prefetched before rendering the org-unit-tree prefetchedOrganisationUnits.loading || + // dataset org unit paths must be loaded before filtering the tree + (!!hideUnassignedOrgUnits && !!dataSetId && dataSetOrgUnitPaths.loading) || // Either a filter has been set but the hook // hasn't been called yet (filter !== '' && !orgUnitPathsByName.called) || @@ -124,19 +152,22 @@ export default function OrganisationUnitSetSelectorBarItem() { {!orgUnitPathsByNameLoading && (orgUnitPathsByName.error || - prefetchedOrganisationUnits.error) && ( + prefetchedOrganisationUnits.error || + (hideUnassignedOrgUnits && + dataSetOrgUnitPaths.error)) && ( )} {!orgUnitPathsByNameLoading && !!filter && - !filteredOrgUnitPaths.length && ( + !treeFilterPaths.length && (
{i18n.t( 'No organisation units could be found' @@ -145,11 +176,11 @@ export default function OrganisationUnitSetSelectorBarItem() { )} {!orgUnitPathsByNameLoading && - (!filter || !!filteredOrgUnitPaths.length) && ( + (!filter || !!treeFilterPaths.length) && ( { + const dataInputPeriods = dataSet?.dataInputPeriods + if (!dataInputPeriods?.length) { + return periods + } + const validIds = new Set(dataInputPeriods.map((dip) => dip.period.id)) + return periods.filter((p) => validIds.has(p.id)) + }, [periods, dataSet?.dataInputPeriods]) +} + const getYear = (date) => { // return null if date is undefined (for example) if (typeof date !== 'string') { @@ -86,6 +98,11 @@ export const PeriodSelectorBarItem = () => { : year, }) + const filteredPeriods = useDataInputFilteredPeriods(periods, dataSet) + + const periodsWithCategoryOptions = + usePeriodsWithCategoryOptions(filteredPeriods) + useEffect(() => { const selectedPeriodYear = getYear(selectedPeriod?.startDate) if (selectedPeriodYear) { @@ -181,7 +198,7 @@ export const PeriodSelectorBarItem = () => { )} { setPeriodId(selected) setPeriodOpen(false) diff --git a/src/context-selection/period-selector-bar-item/use-periods-with-category-options.js b/src/context-selection/period-selector-bar-item/use-periods-with-category-options.js new file mode 100644 index 0000000000..1c177545cc --- /dev/null +++ b/src/context-selection/period-selector-bar-item/use-periods-with-category-options.js @@ -0,0 +1,46 @@ +import { useConfig } from '@dhis2/app-runtime' +import { useMemo } from 'react' +import { + selectors, + useDataSetId, + useMetadata, + useOrgUnit, + useOrgUnitId, +} from '../../shared/index.js' + +export default function usePeriodsWithCategoryOptions(periods) { + const { systemInfo = {} } = useConfig() + const { calendar = 'gregory' } = systemInfo + const { data: metadata } = useMetadata() + const [dataSetId] = useDataSetId() + const [orgUnitId] = useOrgUnitId() + const { data: orgUnitData } = useOrgUnit() + const orgUnitPath = orgUnitData?.path + + return useMemo(() => { + if (!orgUnitId || !dataSetId || !metadata) { + return periods + } + const categoryCombo = selectors.getCategoryComboByDataSetId( + metadata, + dataSetId + ) + if (!categoryCombo || categoryCombo.isDefault) { + return periods + } + return periods.filter((period) => { + const categoriesWithOptions = + selectors.getCategoriesWithOptionsWithinPeriodWithOrgUnit( + metadata, + dataSetId, + period.id, + orgUnitId, + orgUnitPath, + calendar + ) + return categoriesWithOptions.every( + (cat) => cat.categoryOptions.length > 0 + ) + }) + }, [periods, orgUnitId, orgUnitPath, dataSetId, metadata, calendar]) +} diff --git a/src/context-selection/section-filter-selector-bar-item/section-filter-selector-bar-item.jsx b/src/context-selection/section-filter-selector-bar-item/section-filter-selector-bar-item.jsx index 2d37834c53..027d1e7e54 100644 --- a/src/context-selection/section-filter-selector-bar-item/section-filter-selector-bar-item.jsx +++ b/src/context-selection/section-filter-selector-bar-item/section-filter-selector-bar-item.jsx @@ -7,12 +7,14 @@ import { useDataSetId, useSectionFilter, } from '../../shared/index.js' +import { usePluginOptions } from '../../shared/plugin-options/index.js' import { MenuSelect } from '../menu-select/index.js' import useOnDependentParamsChange from './use-on-dependent-params-change.js' import useSelectorBarItemValue from './use-selector-bar-item-value.js' import useShouldComponentRenderNull from './use-should-component-render-null.js' export default function SectionFilterSelectorBarItem() { + const { hideTabSectionSelector } = usePluginOptions() const [open, setOpen] = useState(false) const [sectionFilter, setSectionFilter] = useSectionFilter() const deselect = useCallback( @@ -46,7 +48,7 @@ export default function SectionFilterSelectorBarItem() { }, [dataSet, sectionFilter, setSectionFilter]) const shouldComponentRenderNull = useShouldComponentRenderNull() - if (shouldComponentRenderNull) { + if (shouldComponentRenderNull || hideTabSectionSelector) { return null } diff --git a/src/data-workspace/category-combo-table-body/category-combo-table-body-greyed-columns.test.jsx b/src/data-workspace/category-combo-table-body/category-combo-table-body-greyed-columns.test.jsx new file mode 100644 index 0000000000..6ac8d25a31 --- /dev/null +++ b/src/data-workspace/category-combo-table-body/category-combo-table-body-greyed-columns.test.jsx @@ -0,0 +1,284 @@ +import { Table } from '@dhis2/ui' +import { getAllByTestId, getByTestId } from '@testing-library/react' +import React from 'react' +import { useMetadata } from '../../shared/metadata/use-metadata.js' +import { useDataSetId } from '../../shared/use-context-selection/use-context-selection.js' +import { render } from '../../test-utils/index.js' +import { CategoryComboTableBody } from './category-combo-table-body.jsx' + +jest.mock( + '../../shared/use-context-selection/use-context-selection.js', + () => ({ + ...jest.requireActual( + '../../shared/use-context-selection/use-context-selection.js' + ), + useDataSetId: jest.fn(), + }) +) + +jest.mock('../../shared/metadata/use-metadata.js', () => ({ + useMetadata: jest.fn(), +})) + +const MOCK_VALUES = { + s46m5MS0hxu: { + Prlt0C1RF0s: { + value: '10', + }, + psbwp3CQEhs: { + value: '20', + }, + V6L425pT3A0: { + value: '30', + }, + hEFKSsPV5et: { + value: '40', + }, + }, +} + +jest.mock('../../shared/stores/data-value-store.js', () => ({ + useValueStore: jest.fn().mockImplementation((func) => { + const state = { + getInitialDataValue: ({ dataElementId, categoryOptionComboId }) => { + return MOCK_VALUES?.[dataElementId]?.[categoryOptionComboId] + ?.value + }, + hasComment: () => false, + getMinMaxValues: () => [], + getDataValue: () => '', + getDataValues: () => { + return MOCK_VALUES + }, + } + + return func(state) + }), +})) + +const categories = { + fMZEcRHuamy: { + categoryOptions: ['qkPbeWaFsnU', 'wbrDrL2aYEc'], + displayFormName: 'Location Fixed/Outreach', + id: 'fMZEcRHuamy', + }, + YNZyaJHiHYq: { + categoryOptions: ['btOyqprQ9e8', 'GEqzEKCHoGA'], + displayFormName: 'EPI/nutrition age', + id: 'YNZyaJHiHYq', + }, +} + +const categoryOptions = { + qkPbeWaFsnU: { + displayFormName: 'Fixed', + displayName: 'Fixed', + id: 'qkPbeWaFsnU', + isDefault: false, + }, + wbrDrL2aYEc: { + displayFormName: 'Outreach', + displayName: 'Outreach', + id: 'wbrDrL2aYEc', + isDefault: false, + }, + btOyqprQ9e8: { + displayFormName: '<1y', + displayName: '<1y', + id: 'btOyqprQ9e8', + isDefault: false, + }, + GEqzEKCHoGA: { + displayFormName: '>1y', + displayName: '>1y', + id: 'GEqzEKCHoGA', + isDefault: false, + }, +} + +const dataElements = { + s46m5MS0hxu: { + categoryCombo: { id: 'dzjKKQq0cSO' }, + displayFormName: 'BCG doses given', + displayName: 'BCG doses given', + id: 's46m5MS0hxu', + valueType: 'INTEGER', + }, +} + +const categoryCombos = { + dzjKKQq0cSO: { + id: 'dzjKKQq0cSO', + categories: ['fMZEcRHuamy', 'YNZyaJHiHYq'], + categoryOptionCombos: [ + { + categoryOptions: ['wbrDrL2aYEc', 'btOyqprQ9e8'], + displayName: 'Outreach, <1y', + id: 'V6L425pT3A0', + }, + { + categoryOptions: ['wbrDrL2aYEc', 'GEqzEKCHoGA'], + displayName: 'Outreach, >1y', + id: 'hEFKSsPV5et', + }, + { + categoryOptions: ['qkPbeWaFsnU', 'btOyqprQ9e8'], + displayName: 'Fixed, <1y', + id: 'Prlt0C1RF0s', + }, + { + categoryOptions: ['qkPbeWaFsnU', 'GEqzEKCHoGA'], + displayName: 'Fixed, >1y', + id: 'psbwp3CQEhs', + }, + ], + }, +} + +const metadata = { + categories, + categoryOptions, + categoryCombos, + dataElements, +} + +describe(' greyed columns', () => { + useMetadata.mockReturnValue({ data: metadata }) + useDataSetId.mockReturnValue(['dataSet1']) + + it('should hide a column when all cells in that column are greyed', () => { + const tableDataElements = [dataElements.s46m5MS0hxu] + const greyedFields = new Set(['s46m5MS0hxu.Prlt0C1RF0s']) + + const result = render( + + +
, + { + wrapper: ({ children }) => <>{children}, + } + ) + + const inputRows = result.getAllByTestId('dhis2-dataentry-tableinputrow') + const inputCells = getAllByTestId( + inputRows[0], + 'dhis2-dataentryapp-dataentrycell' + ) + const paddingCells = result.queryAllByTestId( + 'dhis2-dataentry-paddingcell' + ) + + expect(inputCells.length).toBe(3) + expect(paddingCells.length).toBe(0) + }) + + it('should include hidden greyed values in row total and total sum', () => { + const tableDataElements = [dataElements.s46m5MS0hxu] + const greyedFields = new Set(['s46m5MS0hxu.Prlt0C1RF0s']) + + const result = render( + + +
, + { + wrapper: ({ children }) => <>{children}, + } + ) + + const inputRows = result.getAllByTestId('dhis2-dataentry-tableinputrow') + const rowTotalCell = getByTestId( + inputRows[0], + 'dhis2-dataentry-totalcell' + ) + expect(rowTotalCell.textContent).toBe('100') + + const columnTotalsRow = result.getByTestId( + 'dhis2-dataentry-columntotals' + ) + const totalsCells = getAllByTestId( + columnTotalsRow, + 'dhis2-dataentry-totalcell' + ) + + expect(totalsCells.length).toBe(4) + expect(totalsCells[3].textContent).toBe('100') + }) + + it('should keep separate labels for same category option under different parent groups', () => { + const tableDataElements = [dataElements.s46m5MS0hxu] + const greyedFields = new Set(['s46m5MS0hxu.V6L425pT3A0']) + + const result = render( + + +
, + { + wrapper: ({ children }) => <>{children}, + } + ) + + const gtOneHeaders = result.getAllByText('>1y') + expect(gtOneHeaders.length).toBe(2) + }) + + it('should not render cells when all columns marked as greyed fields', () => { + const tableDataElements = [dataElements.s46m5MS0hxu] + const greyedFields = new Set([ + 's46m5MS0hxu.Prlt0C1RF0s', + 's46m5MS0hxu.psbwp3CQEhs', + 's46m5MS0hxu.V6L425pT3A0', + 's46m5MS0hxu.hEFKSsPV5et', + ]) + + const result = render( + + +
, + { + wrapper: ({ children }) => <>{children}, + } + ) + + const inputRows = result.getAllByTestId('dhis2-dataentry-tableinputrow') + const inputCells = result.queryAllByTestId( + 'dhis2-dataentryapp-dataentrycell' + ) + const rowTotalCell = getByTestId( + inputRows[0], + 'dhis2-dataentry-totalcell' + ) + const columnTotalsRow = result.getByTestId( + 'dhis2-dataentry-columntotals' + ) + const totalsCells = getAllByTestId( + columnTotalsRow, + 'dhis2-dataentry-totalcell' + ) + + expect(inputCells.length).toBe(0) + expect(rowTotalCell.textContent).toBe('100') + expect(totalsCells.length).toBe(1) + expect(totalsCells[0].textContent).toBe('100') + }) +}) diff --git a/src/data-workspace/category-combo-table-body/category-combo-table-body-header-visible.jsx b/src/data-workspace/category-combo-table-body/category-combo-table-body-header-visible.jsx new file mode 100644 index 0000000000..056f15d15e --- /dev/null +++ b/src/data-workspace/category-combo-table-body/category-combo-table-body-header-visible.jsx @@ -0,0 +1,107 @@ +import i18n from '@dhis2/d2-i18n' +import { TableRowHead, TableCellHead } from '@dhis2/ui' +import cx from 'classnames' +import PropTypes from 'prop-types' +import React, { useMemo } from 'react' +import { useMetadata } from '../../shared/index.js' +import { useActiveCell } from '../data-entry-cell/index.js' +import styles from '../table-body.module.css' +import { getVisibleCategoryColumns } from './get-visible-category-columns.js' +import { PaddingCell } from './padding-cell.jsx' +import { TotalHeader } from './total-cells.jsx' + +export const CategoryComboTableBodyHeaderVisible = ({ + renderRowTotals, + paddingCells, + categoryOptionCombos, + categories, + checkTableActive, + hideRowTotalsDueToNonNumberValueTypes, +}) => { + const { deId: activeDeId, cocId: activeCocId } = useActiveCell() + const { data: metadata } = useMetadata() + + const { rows, categoryOptionsLookup } = useMemo( + () => + getVisibleCategoryColumns({ + categories, + categoryOptionCombos, + metadata, + }), + [categories, categoryOptionCombos, metadata] + ) + + const isHeaderActive = (startColumnIndex, headerColSpan) => { + const activeCellColIdx = categoryOptionCombos.findIndex( + (coc) => activeCocId === coc.id + ) + const idxDiff = activeCellColIdx - startColumnIndex + return ( + checkTableActive(activeDeId) && + idxDiff < headerColSpan && + idxDiff >= 0 + ) + } + + return rows.map((row, rowIndex) => { + const { category, columns } = row + return ( + + + {category.displayFormName !== 'default' && + category.displayFormName} + + {columns.map((column) => { + const categoryOption = + categoryOptionsLookup[column.categoryOptionId] || {} + return ( + + {categoryOption.isDefault + ? i18n.t('Value') + : categoryOption.displayFormName ?? + categoryOption.displayName} + + ) + })} + {paddingCells.map((_, index) => ( + + ))} + {renderRowTotals && rowIndex === 0 && ( + <> + {hideRowTotalsDueToNonNumberValueTypes ? ( + + ) : ( + + )} + + )} + + ) + }) +} + +CategoryComboTableBodyHeaderVisible.propTypes = { + categories: PropTypes.array, + categoryOptionCombos: PropTypes.arrayOf( + PropTypes.shape({ + categoryOptions: PropTypes.arrayOf(PropTypes.string), + id: PropTypes.string, + }) + ), + checkTableActive: PropTypes.func, + hideRowTotalsDueToNonNumberValueTypes: PropTypes.bool, + paddingCells: PropTypes.array, + renderRowTotals: PropTypes.bool, +} diff --git a/src/data-workspace/category-combo-table-body/category-combo-table-body.jsx b/src/data-workspace/category-combo-table-body/category-combo-table-body.jsx index eb39e964b2..fd051a3d54 100644 --- a/src/data-workspace/category-combo-table-body/category-combo-table-body.jsx +++ b/src/data-workspace/category-combo-table-body/category-combo-table-body.jsx @@ -7,8 +7,11 @@ import { DataEntryCell, DataEntryField } from '../data-entry-cell/index.js' import { getFieldId } from '../get-field-id.jsx' import { TableBodyHiddenByFiltersRow } from '../table-body-hidden-by-filter-row.jsx' import styles from '../table-body.module.css' +import { CategoryComboTableBodyHeaderVisible } from './category-combo-table-body-header-visible.jsx' import { CategoryComboTableBodyHeader } from './category-combo-table-body-header.jsx' +import { ColumnTotalsVisibleWithFullSum } from './column-totals-visible-with-full-sum.jsx' import { DataElementCell } from './data-element-cell.jsx' +import { getVisibleCOCs } from './get-visible-cocs.js' import { ColumnTotals, RowTotal } from './total-cells.jsx' export const CategoryComboTableBody = React.memo( @@ -34,6 +37,17 @@ export const CategoryComboTableBody = React.memo( metadata, categoryCombo.id ) + const visibleCOCs = useMemo( + () => + getVisibleCOCs({ + sortedCOCs, + dataElements, + greyedFields, + getFieldId, + }), + [sortedCOCs, dataElements, greyedFields] + ) + const hasGreyedColumnsHidden = visibleCOCs.length !== sortedCOCs.length const checkTableActive = useCallback( (activeDeId) => dataElements.some(({ id }) => id === activeDeId), @@ -78,16 +92,29 @@ export const CategoryComboTableBody = React.memo( [styles.sectionRowCollapsed]: collapsed, })} > - + {hasGreyedColumnsHidden ? ( + + ) : ( + + )} {dataElements.map((de, i) => { const hidden = filteredDeIds.has(de.id) return ( @@ -100,7 +127,7 @@ export const CategoryComboTableBody = React.memo( className={cx({ [styles.hidden]: hidden })} > - {sortedCOCs.map((coc) => ( + {visibleCOCs.map((coc) => ( + <> + {hasGreyedColumnsHidden ? ( + + ) : ( + + )} + )} {hiddenItemsCount > 0 && ( { + const visibleMatrix = useValueMatrix( + dataElements, + visibleCategoryOptionCombos + ) + const visibleColumnTotals = useMemo( + () => calculateColumnTotals(visibleMatrix), + [visibleMatrix] + ) + const sumMatrix = useValueMatrix(dataElements, categoryOptionCombos) + const totalSum = useMemo( + () => + calculateColumnTotals(sumMatrix).reduce( + (acc, curr) => acc + curr, + 0 + ), + [sumMatrix] + ) + + return ( + + + {i18n.t('Totals')} + + {visibleColumnTotals.map((value, index) => ( + {value} + ))} + {paddingCells.map((_, index) => ( + + ))} + {renderTotalSum && {totalSum}} + + ) +} + +ColumnTotalsVisibleWithFullSum.propTypes = { + categoryOptionCombos: PropTypes.array, + dataElements: PropTypes.array, + paddingCells: PropTypes.array, + renderTotalSum: PropTypes.bool, + visibleCategoryOptionCombos: PropTypes.array, +} diff --git a/src/data-workspace/category-combo-table-body/get-visible-category-columns.js b/src/data-workspace/category-combo-table-body/get-visible-category-columns.js new file mode 100644 index 0000000000..568f08fd5e --- /dev/null +++ b/src/data-workspace/category-combo-table-body/get-visible-category-columns.js @@ -0,0 +1,79 @@ +import { selectors } from '../../shared/index.js' + +export const getVisibleCategoryColumns = ({ + categories, + categoryOptionCombos, + metadata, +}) => { + const categoryOptionsLookup = categories.reduce((acc, category) => { + const categoryOptions = selectors.getCategoryOptionsByCategoryId( + metadata, + category.id + ) + return categoryOptions.reduce( + (innerAcc, option) => ({ + ...innerAcc, + [option.id]: option, + }), + acc + ) + }, {}) + + const getCategoryOptionId = (coc, category) => { + const categoryOptionIds = new Set(category.categoryOptions) + return coc.categoryOptions?.find((id) => categoryOptionIds.has(id)) + } + + const rows = categories.map((category, categoryIndex) => { + const parentCategories = categories.slice(0, categoryIndex) + const categoryOptionIds = new Set(category.categoryOptions) + const columns = categoryOptionCombos.reduce((acc, coc, cocIndex) => { + const categoryOptionId = coc.categoryOptions?.find((id) => + categoryOptionIds.has(id) + ) + if (!categoryOptionId) { + return acc + } + + const previousColumn = acc[acc.length - 1] + const parentSignature = parentCategories + .map((parentCategory) => + getCategoryOptionId(coc, parentCategory) + ) + .join('|') + if ( + previousColumn && + previousColumn.categoryOptionId === categoryOptionId && + previousColumn.parentSignature === parentSignature + ) { + return [ + ...acc.slice(0, -1), + { + ...previousColumn, + span: previousColumn.span + 1, + }, + ] + } + + return [ + ...acc, + { + categoryOptionId, + parentSignature, + span: 1, + startColumnIndex: cocIndex, + }, + ] + }, []) + + return { + category, + columns, + } + }) + + return { + rows, + categoryOptionsLookup, + } +} diff --git a/src/data-workspace/category-combo-table-body/get-visible-category-columns.test.js b/src/data-workspace/category-combo-table-body/get-visible-category-columns.test.js new file mode 100644 index 0000000000..6c4152838d --- /dev/null +++ b/src/data-workspace/category-combo-table-body/get-visible-category-columns.test.js @@ -0,0 +1,178 @@ +import { selectors } from '../../shared/index.js' +import { getVisibleCategoryColumns } from './get-visible-category-columns.js' + +jest.mock('../../shared/index.js', () => ({ + selectors: { + getCategoryOptionsByCategoryId: jest.fn(), + }, +})) + +describe('getVisibleCategoryColumns', () => { + const metadata = { id: 'metadata' } + const cat1 = { + id: 'cat1', + categoryOptions: ['opt1', 'opt2'], + displayFormName: 'Cat1', + } + const cat2 = { + id: 'cat2', + categoryOptions: ['opt3', 'opt4'], + displayFormName: 'Cat2', + } + const categories = [cat1, cat2] + + const opt1 = { id: 'opt1', displayFormName: 'Option 1' } + const opt2 = { id: 'opt2', displayFormName: 'Option 2' } + const opt3 = { id: 'opt3', displayFormName: 'Option 3' } + const opt4 = { id: 'opt4', displayFormName: 'Option 4' } + + beforeEach(() => { + selectors.getCategoryOptionsByCategoryId.mockImplementation( + (_, catId) => { + if (catId === 'cat1') { + return [opt1, opt2] + } + if (catId === 'cat2') { + return [opt3, opt4] + } + return [] + } + ) + }) + + it('should correctly group headers when all columns are present', () => { + // COCs: (opt1, opt3), (opt1, opt4), (opt2, opt3), (opt2, opt4) + const categoryOptionCombos = [ + { id: 'coc1', categoryOptions: ['opt1', 'opt3'] }, + { id: 'coc2', categoryOptions: ['opt1', 'opt4'] }, + { id: 'coc3', categoryOptions: ['opt2', 'opt3'] }, + { id: 'coc4', categoryOptions: ['opt2', 'opt4'] }, + ] + + const { rows } = getVisibleCategoryColumns({ + categories, + categoryOptionCombos, + metadata, + }) + + expect(rows).toHaveLength(2) + + // Row 1 (Cat1) + // Should be: opt1 (span 2), opt2 (span 2) + expect(rows[0].columns).toHaveLength(2) + expect(rows[0].columns[0]).toMatchObject({ + categoryOptionId: 'opt1', + span: 2, + }) + expect(rows[0].columns[1]).toMatchObject({ + categoryOptionId: 'opt2', + span: 2, + }) + + // Row 2 (Cat2) + // Should be: opt3 (span 1), opt4 (span 1), opt3 (span 1), opt4 (span 1) + expect(rows[1].columns).toHaveLength(4) + expect(rows[1].columns[0]).toMatchObject({ + categoryOptionId: 'opt3', + span: 1, + }) + expect(rows[1].columns[1]).toMatchObject({ + categoryOptionId: 'opt4', + span: 1, + }) + expect(rows[1].columns[2]).toMatchObject({ + categoryOptionId: 'opt3', + span: 1, + }) + expect(rows[1].columns[3]).toMatchObject({ + categoryOptionId: 'opt4', + span: 1, + }) + }) + + it('should correctly regroup headers when a column is missing (middle of group)', () => { + // Remove coc2 (opt1, opt4) + // COCs: (opt1, opt3), (opt2, opt3), (opt2, opt4) + const categoryOptionCombos = [ + { id: 'coc1', categoryOptions: ['opt1', 'opt3'] }, + // coc2 removed + { id: 'coc3', categoryOptions: ['opt2', 'opt3'] }, + { id: 'coc4', categoryOptions: ['opt2', 'opt4'] }, + ] + + const { rows } = getVisibleCategoryColumns({ + categories, + categoryOptionCombos, + metadata, + }) + + // Row 1 (Cat1) + // Should be: opt1 (span 1), opt2 (span 2) + expect(rows[0].columns).toHaveLength(2) + expect(rows[0].columns[0]).toMatchObject({ + categoryOptionId: 'opt1', + span: 1, + }) + expect(rows[0].columns[1]).toMatchObject({ + categoryOptionId: 'opt2', + span: 2, + }) + + // Row 2 (Cat2) + // Should be: opt3 (span 1), opt3 (span 1), opt4 (span 1) + expect(rows[1].columns).toHaveLength(3) + expect(rows[1].columns[0]).toMatchObject({ + categoryOptionId: 'opt3', + span: 1, + }) + expect(rows[1].columns[1]).toMatchObject({ + categoryOptionId: 'opt3', + span: 1, + }) + expect(rows[1].columns[2]).toMatchObject({ + categoryOptionId: 'opt4', + span: 1, + }) + }) + + it('should correctly regroup headers when a column is missing (start of group)', () => { + // Remove coc1 (opt1, opt3) + // COCs: (opt1, opt4), (opt2, opt3), (opt2, opt4) + const categoryOptionCombos = [ + // coc1 removed + { id: 'coc2', categoryOptions: ['opt1', 'opt4'] }, + { id: 'coc3', categoryOptions: ['opt2', 'opt3'] }, + { id: 'coc4', categoryOptions: ['opt2', 'opt4'] }, + ] + + const { rows } = getVisibleCategoryColumns({ + categories, + categoryOptionCombos, + metadata, + }) + + // Row 1 (Cat1) + // Should be: opt1 (span 1), opt2 (span 2) + expect(rows[0].columns).toHaveLength(2) + expect(rows[0].columns[0]).toMatchObject({ + categoryOptionId: 'opt1', + span: 1, + }) + expect(rows[0].columns[1]).toMatchObject({ + categoryOptionId: 'opt2', + span: 2, + }) + }) + + it('should handle completely empty columns', () => { + const categoryOptionCombos = [] + const { rows } = getVisibleCategoryColumns({ + categories, + categoryOptionCombos, + metadata, + }) + + expect(rows[0].columns).toHaveLength(0) + expect(rows[1].columns).toHaveLength(0) + }) +}) diff --git a/src/data-workspace/category-combo-table-body/get-visible-cocs.js b/src/data-workspace/category-combo-table-body/get-visible-cocs.js new file mode 100644 index 0000000000..4ea7e731f7 --- /dev/null +++ b/src/data-workspace/category-combo-table-body/get-visible-cocs.js @@ -0,0 +1,18 @@ +export const getVisibleCOCs = ({ + sortedCOCs = [], + dataElements = [], + greyedFields, + getFieldId, +}) => { + if (!greyedFields || greyedFields.size === 0) { + return sortedCOCs + } + + if (dataElements.length === 0) { + return sortedCOCs + } + + return sortedCOCs.filter((coc) => + dataElements.some((de) => !greyedFields.has(getFieldId(de.id, coc.id))) + ) +} diff --git a/src/data-workspace/category-combo-table-body/get-visible-cocs.test.js b/src/data-workspace/category-combo-table-body/get-visible-cocs.test.js new file mode 100644 index 0000000000..9cc24b3285 --- /dev/null +++ b/src/data-workspace/category-combo-table-body/get-visible-cocs.test.js @@ -0,0 +1,86 @@ +import { getVisibleCOCs } from './get-visible-cocs.js' + +describe('getVisibleCOCs', () => { + const getFieldId = (deId, cocId) => `${deId}.${cocId}` + const coc1 = { id: 'coc1' } + const coc2 = { id: 'coc2' } + const coc3 = { id: 'coc3' } + const sortedCOCs = [coc1, coc2, coc3] + const de1 = { id: 'de1' } + const de2 = { id: 'de2' } + const dataElements = [de1, de2] + + it('should return all COCs if greyedFields is empty', () => { + const greyedFields = new Set() + const result = getVisibleCOCs({ + sortedCOCs, + dataElements, + greyedFields, + getFieldId, + }) + expect(result).toEqual(sortedCOCs) + }) + + it('should return all COCs if greyedFields is null/undefined', () => { + const result = getVisibleCOCs({ + sortedCOCs, + dataElements, + greyedFields: null, + getFieldId, + }) + expect(result).toEqual(sortedCOCs) + }) + + it('should return all COCs if dataElements is empty', () => { + const greyedFields = new Set(['de1.coc1']) + const result = getVisibleCOCs({ + sortedCOCs, + dataElements: [], + greyedFields, + getFieldId, + }) + expect(result).toEqual(sortedCOCs) + }) + + it('should filter out COCs where all cells are greyed', () => { + // Grey out coc2 for both data elements + const greyedFields = new Set(['de1.coc2', 'de2.coc2']) + const result = getVisibleCOCs({ + sortedCOCs, + dataElements, + greyedFields, + getFieldId, + }) + expect(result).toEqual([coc1, coc3]) + }) + + it('should NOT filter out COCs where only some cells are greyed', () => { + // Grey out coc2 for only de1 + const greyedFields = new Set(['de1.coc2']) + const result = getVisibleCOCs({ + sortedCOCs, + dataElements, + greyedFields, + getFieldId, + }) + expect(result).toEqual(sortedCOCs) + }) + + it('should return empty array if all COCs are fully greyed', () => { + const greyedFields = new Set([ + 'de1.coc1', + 'de2.coc1', + 'de1.coc2', + 'de2.coc2', + 'de1.coc3', + 'de2.coc3', + ]) + const result = getVisibleCOCs({ + sortedCOCs, + dataElements, + greyedFields, + getFieldId, + }) + expect(result).toEqual([]) + }) +}) diff --git a/src/data-workspace/data-details-sidebar/limits.jsx b/src/data-workspace/data-details-sidebar/limits.jsx index 3864169aac..4ba0435204 100644 --- a/src/data-workspace/data-details-sidebar/limits.jsx +++ b/src/data-workspace/data-details-sidebar/limits.jsx @@ -8,6 +8,7 @@ import { useUnsavedDataStore, useContextSelectionId, getCellId, + useFeatureToggleContext, } from '../../shared/index.js' import { useMinMaxLimits } from '../use-min-max-limits.js' import LimitsDisplay from './limits-display.jsx' @@ -20,6 +21,7 @@ export default function Limits({ dataValue }) { const [open, setOpen] = useState(true) const [editing, setEditing] = useState(false) const contextSelectionId = useContextSelectionId() + const { minMaxDeleteAuthorityExists } = useFeatureToggleContext() const cellId = getCellId({ item: dataValue, contextSelectionId }) const unsavedLimits = useUnsavedDataStore((state) => { @@ -34,7 +36,10 @@ export default function Limits({ dataValue }) { const { data: userInfo } = useUserInfo() - const canDelete = userInfoSelectors.getCanDeleteMinMax(userInfo) + const canDelete = userInfoSelectors.getCanDeleteMinMax( + userInfo, + minMaxDeleteAuthorityExists + ) const canAdd = userInfoSelectors.getCanAddMinMax(userInfo) useEffect(() => { diff --git a/src/data-workspace/data-details-sidebar/limits.test.jsx b/src/data-workspace/data-details-sidebar/limits.test.jsx index f52527c459..b8c1e82696 100644 --- a/src/data-workspace/data-details-sidebar/limits.test.jsx +++ b/src/data-workspace/data-details-sidebar/limits.test.jsx @@ -1,3 +1,4 @@ +import { useConfig } from '@dhis2/app-runtime' import userEvent from '@testing-library/user-event' import React from 'react' import { useUserInfo, useCanUserEditFields } from '../../shared/index.js' @@ -5,6 +6,11 @@ import { render } from '../../test-utils/index.js' import { useMinMaxLimits } from '../use-min-max-limits.js' import Limits from './limits.jsx' +jest.mock('@dhis2/app-runtime', () => ({ + ...jest.requireActual('@dhis2/app-runtime'), + useConfig: jest.fn(), +})) + jest.mock('../use-min-max-limits.js', () => ({ useMinMaxLimits: jest.fn(), })) @@ -28,6 +34,9 @@ describe('', () => { authorities: ['ALL'], }, })) + useConfig.mockReturnValue({ + serverVersion: { major: 2, minor: 43, patch: undefined }, + }) }) it('is expanded by default', () => { @@ -75,6 +84,97 @@ describe('', () => { }) }) + describe('shows delete limits button based on version-dependent authorities', () => { + beforeAll(() => { + useMinMaxLimits.mockReturnValue({ min: 3, max: 4 }) + }) + + it('shows delete button in v43 if user has F_MINMAX_DATAELEMENT_ADD authority', async () => { + useUserInfo.mockImplementation(() => ({ + data: { + authorities: ['F_MINMAX_DATAELEMENT_ADD'], + }, + })) + useConfig.mockReturnValue({ + serverVersion: { major: 2, minor: 43, patch: undefined }, + }) + const { getByRole } = render( + + ) + + expect( + getByRole('button', { name: 'Edit limits' }) + ).toBeInTheDocument() + expect( + getByRole('button', { name: 'Delete limits' }) + ).toBeInTheDocument() + }) + + it('does not show delete button in v40 if user only has F_MINMAX_DATAELEMENT_ADD authority', async () => { + useUserInfo.mockImplementation(() => ({ + data: { + authorities: ['F_MINMAX_DATAELEMENT_ADD'], + }, + })) + useConfig.mockReturnValue({ + serverVersion: { major: 2, minor: 40, patch: undefined }, + }) + const { getByRole, queryByRole } = render( + + ) + + expect( + getByRole('button', { name: 'Edit limits' }) + ).toBeInTheDocument() + expect(queryByRole('button', { name: 'Delete limits' })).toBeNull() + }) + + it('shows delete button in v40 if user has F_MINMAX_DATAELEMENT_DELETE authority', async () => { + useUserInfo.mockImplementation(() => ({ + data: { + authorities: [ + 'F_MINMAX_DATAELEMENT_ADD', + 'F_MINMAX_DATAELEMENT_DELETE', + ], + }, + })) + useConfig.mockReturnValue({ + serverVersion: { major: 2, minor: 43, patch: undefined }, + }) + const { getByRole } = render( + + ) + + expect( + getByRole('button', { name: 'Edit limits' }) + ).toBeInTheDocument() + expect( + getByRole('button', { name: 'Delete limits' }) + ).toBeInTheDocument() + }) + }) + describe('when has no limits to render', () => { beforeAll(() => { useMinMaxLimits.mockReturnValue({}) @@ -83,6 +183,9 @@ describe('', () => { authorities: ['ALL'], }, })) + useConfig.mockReturnValue({ + serverVersion: { major: 2, minor: 43, patch: undefined }, + }) }) it(`is disabled if value of itemType prop is not 'numerical'`, () => { const { getByText } = render( diff --git a/src/data-workspace/filter-field.jsx b/src/data-workspace/filter-field.jsx index 8064ab0c41..733a793ae5 100644 --- a/src/data-workspace/filter-field.jsx +++ b/src/data-workspace/filter-field.jsx @@ -3,11 +3,18 @@ import { Button, InputField } from '@dhis2/ui' import classNames from 'classnames' import PropTypes from 'prop-types' import React from 'react' +import { usePluginOptions } from '../shared/plugin-options/index.js' import { FORM_TYPES } from './constants.js' import styles from './entry-form.module.css' export default function FilterField({ value, setFilterText, formType }) { + const { hideFilterField } = usePluginOptions() const wrapperClasses = classNames(styles.filterWrapper, styles.hideForPrint) + + if (hideFilterField) { + return null + } + return (
data, +} + +const fillWithZerosFailedMessage = i18n.t( + 'Error occurred while saving dataValues' +) + +const normalizeValueForInput = (value) => { + if (value === undefined || value === null) { + return value + } + + return String(value) +} + +const mapDataValuesStoreToInitialValues = (dataValues = {}) => + Object.entries(dataValues).reduce( + (acc, [dataElementId, categoryOptionCombos]) => ({ + ...acc, + [dataElementId]: Object.entries(categoryOptionCombos || {}).reduce( + (innerAcc, [categoryOptionComboId, dataValue]) => ({ + ...innerAcc, + [categoryOptionComboId]: normalizeValueForInput( + dataValue?.value + ), + }), + {} + ), + }), + {} + ) + +const buildDataValues = ({ + dataElementId, + categoryOptionComboId, + dataSetId, + orgUnitId, + periodId, + attributeCombo, + attributeOptions, +}) => { + const variables = { + de: dataElementId, + co: categoryOptionComboId, + ds: dataSetId, + ou: orgUnitId, + pe: periodId, + value: '0', + } + + if (attributeCombo) { + variables.cc = attributeCombo + variables.cp = attributeOptions.join(';') + } + + return variables +} + +export const FillZerosButton = ({ + dataSetId, + sectionId, + onFillComplete, + greyedFields, +}) => { + const [isSaving, setIsSaving] = useState(false) + const queryClient = useQueryClient() + const engine = useDataEngine() + const { show: showErrorAlert } = useAlert((message) => message, { + critical: true, + }) + const dataValueSetQueryKey = useDataValueSetQueryKey() + const formKey = useContextSelectionId() + const [{ orgUnitId, periodId }] = useContextSelection() + const { attributeCombo, attributeOptions } = useApiAttributeParams() || {} + const zeroFillCandidates = useZeroFillCandidates({ dataSetId, sectionId }) + const isOrgUnitClosed = useIsOrgUnitClosed() + const getInitialDataValues = useValueStore( + (state) => state.getInitialDataValues + ) + const setInitialDataValues = useValueStore( + (state) => state.setInitialDataValues + ) + const getDataValue = useValueStore((state) => state.getDataValue) + const getDataValues = useValueStore((state) => state.getDataValues) + + const showButton = !isOrgUnitClosed && zeroFillCandidates.length > 0 + + const onFillWithZeros = useCallback(async () => { + if (isSaving || !periodId) { + return + } + + setIsSaving(true) + + try { + const currentInitialDataValues = + getInitialDataValues()?.values || {} + const currentStoreDataValues = mapDataValuesStoreToInitialValues( + getDataValues() + ) + const hasCurrentStoreValues = + Object.keys(currentStoreDataValues).length > 0 + const currentBaseValues = hasCurrentStoreValues + ? currentStoreDataValues + : currentInitialDataValues + const candidateValuesByFieldId = new Map() + + const emptyCandidates = zeroFillCandidates.filter( + ({ dataElementId, categoryOptionComboId }) => { + const fieldId = getFieldId( + dataElementId, + categoryOptionComboId + ) + + if (greyedFields?.has(fieldId)) { + return false + } + + const currentDataValue = getDataValue({ + dataElementId, + categoryOptionComboId, + }) + const currentValue = + currentDataValue?.value ?? + currentBaseValues[dataElementId]?.[ + categoryOptionComboId + ] + candidateValuesByFieldId.set(fieldId, currentValue) + + return ( + currentValue === undefined || + currentValue === null || + currentValue === '' + ) + } + ) + + if (emptyCandidates.length === 0) { + return + } + + await Promise.all( + emptyCandidates.map( + ({ dataElementId, categoryOptionComboId }) => + engine.mutate(SET_DATA_VALUE_MUTATION, { + variables: buildDataValues({ + dataElementId, + categoryOptionComboId, + dataSetId, + orgUnitId, + periodId, + attributeCombo, + attributeOptions: attributeOptions || [], + }), + }) + ) + ) + + const nextInitialDataValues = { ...currentBaseValues } + + zeroFillCandidates.forEach( + ({ dataElementId, categoryOptionComboId }) => { + const fieldId = getFieldId( + dataElementId, + categoryOptionComboId + ) + const currentValue = candidateValuesByFieldId.get(fieldId) + + if ( + currentValue !== undefined && + currentValue !== null && + currentValue !== '' + ) { + nextInitialDataValues[dataElementId] = { + ...(nextInitialDataValues[dataElementId] || {}), + [categoryOptionComboId]: `${currentValue}`, + } + } + } + ) + + emptyCandidates.forEach( + ({ dataElementId, categoryOptionComboId }) => { + nextInitialDataValues[dataElementId] = { + ...(nextInitialDataValues[dataElementId] || {}), + [categoryOptionComboId]: '0', + } + } + ) + + setInitialDataValues(nextInitialDataValues, formKey) + onFillComplete?.() + + await queryClient.invalidateQueries(dataValueSetQueryKey) + } catch { + showErrorAlert(fillWithZerosFailedMessage) + await queryClient.invalidateQueries(dataValueSetQueryKey) + } finally { + setIsSaving(false) + } + }, [ + isSaving, + periodId, + zeroFillCandidates, + engine, + dataSetId, + orgUnitId, + attributeCombo, + attributeOptions, + getInitialDataValues, + getDataValue, + getDataValues, + setInitialDataValues, + formKey, + onFillComplete, + greyedFields, + showErrorAlert, + queryClient, + dataValueSetQueryKey, + ]) + + if (!showButton) { + return null + } + + return ( + + ) +} + +FillZerosButton.propTypes = { + dataSetId: PropTypes.string, + greyedFields: PropTypes.instanceOf(Set), + sectionId: PropTypes.string, + onFillComplete: PropTypes.func, +} + +export { buildDataValues as buildZeroMutationVariables } diff --git a/src/data-workspace/section-form/fill-zeros-button.test.jsx b/src/data-workspace/section-form/fill-zeros-button.test.jsx new file mode 100644 index 0000000000..eaacbba15e --- /dev/null +++ b/src/data-workspace/section-form/fill-zeros-button.test.jsx @@ -0,0 +1,444 @@ +import { fireEvent, render, screen, waitFor } from '@testing-library/react' +import React from 'react' +import { + FillZerosButton, + buildZeroMutationVariables, +} from './fill-zeros-button.jsx' +import { useIsOrgUnitClosed } from './use-is-org-unit-closed.js' +import { useZeroFillCandidates } from './use-zero-fill-candidates.js' + +const mockMutate = jest.fn() +const mockInvalidateQueries = jest.fn() +const mockSetInitialDataValues = jest.fn() +const mockGetInitialDataValues = jest.fn(() => ({ values: {} })) +const mockGetDataValue = jest.fn(() => undefined) +const mockGetDataValues = jest.fn(() => ({})) +const mockShowErrorAlert = jest.fn() + +jest.mock('@dhis2/app-runtime', () => ({ + useDataEngine: () => ({ mutate: mockMutate }), + useAlert: jest.fn(() => ({ show: mockShowErrorAlert })), +})) + +jest.mock('@tanstack/react-query', () => ({ + useQueryClient: () => ({ invalidateQueries: mockInvalidateQueries }), +})) + +jest.mock('../../shared/use-data-value-set/index.js', () => ({ + useDataValueSetQueryKey: jest.fn(() => ['dataValueSet']), +})) + +jest.mock('../../shared/use-context-selection/index.js', () => ({ + useContextSelection: jest.fn(() => [ + { orgUnitId: 'ou1', periodId: '202401' }, + ]), + useContextSelectionId: jest.fn(() => 'form-key-1'), +})) + +jest.mock('../../shared/index.js', () => ({ + useValueStore: jest.fn((selector) => + selector({ + getInitialDataValues: mockGetInitialDataValues, + setInitialDataValues: mockSetInitialDataValues, + getDataValue: mockGetDataValue, + getDataValues: mockGetDataValues, + }) + ), +})) + +jest.mock('../../shared/use-api-attribute-params.js', () => ({ + useApiAttributeParams: jest.fn(() => ({ + attributeCombo: 'cc1', + attributeOptions: ['ao1', 'ao2'], + })), +})) + +jest.mock('./use-is-org-unit-closed.js', () => ({ + useIsOrgUnitClosed: jest.fn(() => false), +})) + +jest.mock('./use-zero-fill-candidates.js', () => ({ + useZeroFillCandidates: jest.fn(() => [ + { dataElementId: 'de1', categoryOptionComboId: 'coc1' }, + { dataElementId: 'de2', categoryOptionComboId: 'coc2' }, + ]), +})) + +describe('FillZerosButton', () => { + beforeEach(() => { + jest.clearAllMocks() + mockMutate.mockResolvedValue({}) + mockInvalidateQueries.mockResolvedValue({}) + mockSetInitialDataValues.mockClear() + mockShowErrorAlert.mockClear() + mockGetInitialDataValues.mockReturnValue({ values: {} }) + mockGetDataValue.mockReturnValue(undefined) + mockGetDataValues.mockReturnValue({}) + useIsOrgUnitClosed.mockReturnValue(false) + useZeroFillCandidates.mockReturnValue([ + { dataElementId: 'de1', categoryOptionComboId: 'coc1' }, + { dataElementId: 'de2', categoryOptionComboId: 'coc2' }, + ]) + }) + + it('should render when section has zero-fill candidates and org unit is open', () => { + render() + + expect(screen.getByText('Fill with zeros')).toBeInTheDocument() + }) + + it('should not render when org unit is closed', () => { + useIsOrgUnitClosed.mockReturnValue(true) + + render() + + expect(screen.queryByText('Fill with zeros')).not.toBeInTheDocument() + }) + + it('should not render when section has no eligible data elements', () => { + useZeroFillCandidates.mockReturnValue([]) + + render() + + expect(screen.queryByText('Fill with zeros')).not.toBeInTheDocument() + }) + + it('should save zero values for all candidates when clicked', async () => { + render() + + fireEvent.click(screen.getByText('Fill with zeros')) + + await waitFor(() => { + expect(mockMutate).toHaveBeenCalledTimes(2) + }) + + expect(mockMutate).toHaveBeenNthCalledWith( + 1, + { + resource: 'dataValues', + type: 'create', + data: expect.any(Function), + }, + { + variables: { + de: 'de1', + co: 'coc1', + ds: 'ds1', + ou: 'ou1', + pe: '202401', + value: '0', + cc: 'cc1', + cp: 'ao1;ao2', + }, + } + ) + + expect(mockInvalidateQueries).toHaveBeenCalledWith(['dataValueSet']) + expect(mockSetInitialDataValues).toHaveBeenCalledWith( + { + de1: { coc1: '0' }, + de2: { coc2: '0' }, + }, + 'form-key-1' + ) + }) + + it('should call onFillComplete after saving values', async () => { + const onFillComplete = jest.fn() + + render( + + ) + + fireEvent.click(screen.getByText('Fill with zeros')) + + await waitFor(() => { + expect(onFillComplete).toHaveBeenCalledTimes(1) + }) + }) + + it('should only fill empty cells', async () => { + mockGetDataValue.mockImplementation( + ({ dataElementId, categoryOptionComboId }) => { + if ( + dataElementId === 'de1' && + categoryOptionComboId === 'coc1' + ) { + return { value: '5' } + } + + return undefined + } + ) + + render() + + fireEvent.click(screen.getByText('Fill with zeros')) + + await waitFor(() => { + expect(mockMutate).toHaveBeenCalledTimes(1) + }) + + expect(mockMutate).toHaveBeenCalledWith( + { + resource: 'dataValues', + type: 'create', + data: expect.any(Function), + }, + { + variables: { + de: 'de2', + co: 'coc2', + ds: 'ds1', + ou: 'ou1', + pe: '202401', + value: '0', + cc: 'cc1', + cp: 'ao1;ao2', + }, + } + ) + + expect(mockSetInitialDataValues).toHaveBeenCalledWith( + { + de1: { coc1: '5' }, + de2: { coc2: '0' }, + }, + 'form-key-1' + ) + }) + + it('should not update cells that already have 0', async () => { + mockGetDataValue.mockImplementation( + ({ dataElementId, categoryOptionComboId }) => { + if ( + dataElementId === 'de1' && + categoryOptionComboId === 'coc1' + ) { + return { value: '0' } + } + + if ( + dataElementId === 'de2' && + categoryOptionComboId === 'coc2' + ) { + return { value: 0 } + } + + return undefined + } + ) + + render() + + fireEvent.click(screen.getByText('Fill with zeros')) + + await waitFor(() => { + expect(mockMutate).not.toHaveBeenCalled() + }) + + expect(mockSetInitialDataValues).not.toHaveBeenCalled() + expect(mockInvalidateQueries).not.toHaveBeenCalled() + }) + + it('should not update cells that are greyed', async () => { + render( + + ) + + fireEvent.click(screen.getByText('Fill with zeros')) + + await waitFor(() => { + expect(mockMutate).toHaveBeenCalledTimes(1) + }) + + expect(mockMutate).toHaveBeenCalledWith( + { + resource: 'dataValues', + type: 'create', + data: expect.any(Function), + }, + { + variables: { + de: 'de1', + co: 'coc1', + ds: 'ds1', + ou: 'ou1', + pe: '202401', + value: '0', + cc: 'cc1', + cp: 'ao1;ao2', + }, + } + ) + + expect(mockSetInitialDataValues).toHaveBeenCalledWith( + { + de1: { coc1: '0' }, + }, + 'form-key-1' + ) + }) + + it('should preserve non-empty current values when updating initial values', async () => { + mockGetInitialDataValues.mockReturnValue({ + values: { + de1: { coc1: '12' }, + de2: { coc2: '14' }, + de3: { coc3: '0' }, + }, + }) + + mockGetDataValues.mockReturnValue({ + de1: { coc1: { value: '' } }, + de2: { coc2: { value: '' } }, + de3: { coc3: { value: '5' } }, + }) + + useZeroFillCandidates.mockReturnValue([ + { dataElementId: 'de1', categoryOptionComboId: 'coc1' }, + { dataElementId: 'de2', categoryOptionComboId: 'coc2' }, + { dataElementId: 'de3', categoryOptionComboId: 'coc3' }, + ]) + + mockGetDataValue.mockImplementation( + ({ dataElementId, categoryOptionComboId }) => { + if ( + dataElementId === 'de1' && + categoryOptionComboId === 'coc1' + ) { + return { value: '' } + } + + if ( + dataElementId === 'de2' && + categoryOptionComboId === 'coc2' + ) { + return { value: '' } + } + + if ( + dataElementId === 'de3' && + categoryOptionComboId === 'coc3' + ) { + return { value: '5' } + } + + return undefined + } + ) + + render() + + fireEvent.click(screen.getByText('Fill with zeros')) + + await waitFor(() => { + expect(mockMutate).toHaveBeenCalledTimes(2) + }) + + expect(mockSetInitialDataValues).toHaveBeenCalledWith( + { + de1: { coc1: '0' }, + de2: { coc2: '0' }, + de3: { coc3: '5' }, + }, + 'form-key-1' + ) + }) + + it('should not restore stale values for non-eligible fields', async () => { + mockGetInitialDataValues.mockReturnValue({ + values: { + de1: { coc1: '1' }, + de2: { coc2: '2' }, + de3: { coc3: '0' }, + de4: { coc4: '0' }, + }, + }) + + mockGetDataValues.mockReturnValue({ + de1: { coc1: { value: '1' } }, + de2: { coc2: { value: '' } }, + de3: { coc3: { value: '' } }, + de4: { coc4: { value: '' } }, + }) + + useZeroFillCandidates.mockReturnValue([ + { dataElementId: 'de1', categoryOptionComboId: 'coc1' }, + { dataElementId: 'de3', categoryOptionComboId: 'coc3' }, + { dataElementId: 'de4', categoryOptionComboId: 'coc4' }, + ]) + + render() + + fireEvent.click(screen.getByText('Fill with zeros')) + + await waitFor(() => { + expect(mockMutate).toHaveBeenCalledTimes(2) + }) + + expect(mockSetInitialDataValues).toHaveBeenCalledWith( + { + de1: { coc1: '1' }, + de2: { coc2: '' }, + de3: { coc3: '0' }, + de4: { coc4: '0' }, + }, + 'form-key-1' + ) + }) + + it('should build mutation variables without attribute params for default combo', () => { + const result = buildZeroMutationVariables({ + dataElementId: 'de1', + categoryOptionComboId: 'coc1', + dataSetId: 'ds1', + orgUnitId: 'ou1', + periodId: '202401', + attributeCombo: undefined, + attributeOptions: [], + }) + + expect(result).toEqual({ + de: 'de1', + co: 'coc1', + ds: 'ds1', + ou: 'ou1', + pe: '202401', + value: '0', + }) + }) + + it('should show an error alert and reset loading when save fails', async () => { + const onFillComplete = jest.fn() + mockMutate.mockRejectedValue(new Error('Network error')) + + render( + + ) + + const button = screen.getByText('Fill with zeros') + fireEvent.click(button) + + await waitFor(() => { + expect(mockShowErrorAlert).toHaveBeenCalledTimes(1) + }) + + expect(button).not.toBeDisabled() + expect(mockSetInitialDataValues).not.toHaveBeenCalled() + expect(onFillComplete).not.toHaveBeenCalled() + expect(mockInvalidateQueries).toHaveBeenCalledWith(['dataValueSet']) + }) +}) diff --git a/src/data-workspace/section-form/section.jsx b/src/data-workspace/section-form/section.jsx index 79a611d62a..b42d26fee1 100644 --- a/src/data-workspace/section-form/section.jsx +++ b/src/data-workspace/section-form/section.jsx @@ -18,6 +18,7 @@ import { PivotedCategoryComboTableBody } from '../category-combo-table-body-pivo import { getFieldId } from '../get-field-id.jsx' import { IndicatorsTableBody } from '../indicators-table-body/indicators-table-body.jsx' import { getDisplayOptions } from './displayOptions.js' +import { FillZerosButton } from './fill-zeros-button.jsx' import { SanitizedText } from './sanitized-text.jsx' import styles from './section.module.css' @@ -30,6 +31,7 @@ export function SectionFormSection({ // Could potentially build table via props instead of rendering children const [filterText, setFilterText] = useState('') const [showSectionContent, setShowSectionContent] = useState(true) + const [refreshCells, setRefreshCells] = useState(0) const { data } = useMetadata() @@ -95,11 +97,24 @@ export function SectionFormSection({ e.key === 'Enter' && onSectionHeadClicked() } + const fillZerosSectionClass = classNames( + styles.fillWithZerosSection, + styles.fillWithZerosSection + ) + return (
{beforeSectionText} +
+ setRefreshCells((value) => value + 1)} + /> +
@@ -167,7 +182,7 @@ export function SectionFormSection({ {groupedDataElements.map( ({ categoryCombo, dataElements }, i) => ( { + if (!orgUnitClosedDate || !periodEndDate) { + return false + } + + return !isDateAGreaterThanDateB( + { date: orgUnitClosedDate, calendar }, + { date: periodEndDate, calendar }, + { + calendar, + inclusive: true, + } + ) +} + +export const useIsOrgUnitClosed = () => { + const { data: { closedDate: orgUnitClosedDate } = {} } = useOrgUnit() + const [periodId] = usePeriodId() + const selectedPeriod = usePeriod(periodId) + const { systemInfo = {} } = useConfig() + const { calendar } = systemInfo + + return useMemo( + () => + isOrgUnitClosedForPeriod({ + orgUnitClosedDate, + periodEndDate: selectedPeriod?.endDate, + calendar, + }), + [orgUnitClosedDate, selectedPeriod?.endDate, calendar] + ) +} diff --git a/src/data-workspace/section-form/use-is-org-unit-closed.test.js b/src/data-workspace/section-form/use-is-org-unit-closed.test.js new file mode 100644 index 0000000000..149adbc671 --- /dev/null +++ b/src/data-workspace/section-form/use-is-org-unit-closed.test.js @@ -0,0 +1,43 @@ +import { isOrgUnitClosedForPeriod } from './use-is-org-unit-closed.js' + +describe('isOrgUnitClosedForPeriod', () => { + it('should return false when there is no closed date', () => { + expect( + isOrgUnitClosedForPeriod({ + orgUnitClosedDate: null, + periodEndDate: '2024-02-01', + calendar: 'iso8601', + }) + ).toBe(false) + }) + + it('should return false when org unit closes after period end', () => { + expect( + isOrgUnitClosedForPeriod({ + orgUnitClosedDate: '2024-03-01', + periodEndDate: '2024-02-01', + calendar: 'iso8601', + }) + ).toBe(false) + }) + + it('should return false when org unit closes on period end', () => { + expect( + isOrgUnitClosedForPeriod({ + orgUnitClosedDate: '2024-02-01', + periodEndDate: '2024-02-01', + calendar: 'iso8601', + }) + ).toBe(false) + }) + + it('should return true when org unit closes before period end', () => { + expect( + isOrgUnitClosedForPeriod({ + orgUnitClosedDate: '2024-01-20', + periodEndDate: '2024-02-01', + calendar: 'iso8601', + }) + ).toBe(true) + }) +}) diff --git a/src/data-workspace/section-form/use-zero-fill-candidates.js b/src/data-workspace/section-form/use-zero-fill-candidates.js new file mode 100644 index 0000000000..387082ee80 --- /dev/null +++ b/src/data-workspace/section-form/use-zero-fill-candidates.js @@ -0,0 +1,55 @@ +import { useMemo } from 'react' +import { useMetadata, selectors } from '../../shared/index.js' +import { VALUE_TYPES } from '../../shared/value-types.js' + +const ZERO_FILL_VALUE_TYPES = new Set([ + VALUE_TYPES.NUMBER, + VALUE_TYPES.INTEGER, + VALUE_TYPES.INTEGER_ZERO_OR_POSITIVE, +]) + +export const isZeroFillDataElement = (dataElement) => + Boolean( + dataElement?.zeroIsSignificant && + ZERO_FILL_VALUE_TYPES.has(dataElement?.valueType) + ) + +export const getZeroFillCandidates = ({ metadata, dataSetId, sectionId }) => { + if (!metadata || !dataSetId || !sectionId) { + return [] + } + + const sectionDataElements = + selectors.getDataElementsBySection(metadata, dataSetId, sectionId) || [] + + return sectionDataElements.flatMap((dataElement) => { + if ( + !isZeroFillDataElement(dataElement) || + !dataElement?.categoryCombo?.id + ) { + return [] + } + + const cocs = + selectors.getSortedCoCsByCatComboId( + metadata, + dataElement.categoryCombo.id + ) || [] + + return cocs + .filter((coc) => Boolean(coc?.id)) + .map((coc) => ({ + dataElementId: dataElement.id, + categoryOptionComboId: coc.id, + })) + }) +} + +export const useZeroFillCandidates = ({ dataSetId, sectionId }) => { + const { data: metadata } = useMetadata() + + return useMemo( + () => getZeroFillCandidates({ metadata, dataSetId, sectionId }), + [metadata, dataSetId, sectionId] + ) +} diff --git a/src/data-workspace/section-form/use-zero-fill-candidates.test.js b/src/data-workspace/section-form/use-zero-fill-candidates.test.js new file mode 100644 index 0000000000..7db3c60de8 --- /dev/null +++ b/src/data-workspace/section-form/use-zero-fill-candidates.test.js @@ -0,0 +1,94 @@ +import { selectors } from '../../shared/index.js' +import { + getZeroFillCandidates, + isZeroFillDataElement, +} from './use-zero-fill-candidates.js' + +jest.mock('../../shared/index.js', () => ({ + selectors: { + getDataElementsBySection: jest.fn(), + getSortedCoCsByCatComboId: jest.fn(), + }, + useMetadata: jest.fn(), +})) + +describe('use-zero-fill-candidates', () => { + beforeEach(() => { + jest.clearAllMocks() + }) + + it('should identify zero-fill supported data elements', () => { + expect( + isZeroFillDataElement({ + zeroIsSignificant: true, + valueType: 'INTEGER_ZERO_OR_POSITIVE', + }) + ).toBe(true) + + expect( + isZeroFillDataElement({ + zeroIsSignificant: false, + valueType: 'INTEGER_ZERO_OR_POSITIVE', + }) + ).toBe(false) + + expect( + isZeroFillDataElement({ + zeroIsSignificant: true, + valueType: 'PERCENTAGE', + }) + ).toBe(false) + }) + + it('should return zero-fill candidates only for valid data elements', () => { + selectors.getDataElementsBySection.mockReturnValue([ + { + id: 'de1', + zeroIsSignificant: true, + valueType: 'INTEGER', + categoryCombo: { id: 'cc1' }, + }, + { + id: 'de2', + zeroIsSignificant: true, + valueType: 'PERCENTAGE', + categoryCombo: { id: 'cc2' }, + }, + { + id: 'de3', + zeroIsSignificant: false, + valueType: 'NUMBER', + categoryCombo: { id: 'cc3' }, + }, + ]) + + selectors.getSortedCoCsByCatComboId.mockImplementation((_, ccId) => { + if (ccId === 'cc1') { + return [{ id: 'coc1' }, { id: 'coc2' }] + } + + return [] + }) + + const result = getZeroFillCandidates({ + metadata: { id: 'meta' }, + dataSetId: 'ds1', + sectionId: 'sec1', + }) + + expect(result).toEqual([ + { dataElementId: 'de1', categoryOptionComboId: 'coc1' }, + { dataElementId: 'de1', categoryOptionComboId: 'coc2' }, + ]) + }) + + it('should return an empty list when required inputs are missing', () => { + expect( + getZeroFillCandidates({ + metadata: null, + dataSetId: 'ds1', + sectionId: 'sec1', + }) + ).toEqual([]) + }) +}) diff --git a/src/plugin-legacy-custom-forms/app-plugin.jsx b/src/plugin-legacy-custom-forms/app-plugin.jsx new file mode 100644 index 0000000000..ea177554a6 --- /dev/null +++ b/src/plugin-legacy-custom-forms/app-plugin.jsx @@ -0,0 +1,11 @@ +import React from 'react' +import AppWrapper from '../app/app-wrapper.jsx' +import { PluginOptionsContext } from '../shared/plugin-options/index.js' + +export default function AppPlugin(props) { + return ( + + + + ) +} diff --git a/src/plugin-legacy-custom-forms/main.jsx b/src/plugin-legacy-custom-forms/main.jsx new file mode 100644 index 0000000000..8c2d817f5a --- /dev/null +++ b/src/plugin-legacy-custom-forms/main.jsx @@ -0,0 +1,26 @@ +import PropTypes from 'prop-types' +import React from 'react' +import AppPlugin from './app-plugin.jsx' +import LegacyCustomFormPlugin from './index.jsx' + +const MODES = { + CUSTOM_FORM: 'custom-form', + APP: 'app', +} + +// Custom EST mode so we can use the plugin mode not to render custom forms but to +// render a controlled version of the whole app, without header bar and with some +// custom options (coming from plugin-options-context) +export default function Plugin(props) { + const { mode = MODES.CUSTOM_FORM } = props + + if (mode === MODES.APP) { + return + } else { + return + } +} + +Plugin.propTypes = { + mode: PropTypes.oneOf(Object.values(MODES)), +} diff --git a/src/shared/feature-toggle/use-feature-toggle-context.js b/src/shared/feature-toggle/use-feature-toggle-context.js index b26ea4f160..ff5993f4ec 100644 --- a/src/shared/feature-toggle/use-feature-toggle-context.js +++ b/src/shared/feature-toggle/use-feature-toggle-context.js @@ -19,5 +19,6 @@ export function useFeatureToggleContext() { return { utilizeGistApiForPrefetchedOrganisationUnits: shouldUtilizeGistApiForPrefetchedOrganisationUnits(serverVersion), + minMaxDeleteAuthorityExists: serverVersion?.minor <= 40, } } diff --git a/src/shared/feature-toggle/use-feature-toggle-context.test.js b/src/shared/feature-toggle/use-feature-toggle-context.test.js index a7794a0ce3..67828eeb75 100644 --- a/src/shared/feature-toggle/use-feature-toggle-context.test.js +++ b/src/shared/feature-toggle/use-feature-toggle-context.test.js @@ -33,4 +33,25 @@ describe('useFeatureToggle', () => { ).toBe(outcome) } ) + it.each([ + [false, { major: 2, minor: 43, patch: undefined }], + [false, { major: 2, minor: 43, patch: 0 }], + [false, { major: 2, minor: 43, patch: 3 }], + [false, { major: 2, minor: 42, patch: 2 }], + [false, { major: 2, minor: 42, patch: 1 }], + [false, { major: 2, minor: 41, patch: 5 }], + [false, { major: 2, minor: 41, patch: 6 }], + [false, { major: 2, minor: 41, patch: 4 }], + [true, { major: 2, minor: 40, patch: 9 }], + [true, { major: 2, minor: 40, patch: 11 }], + [true, { major: 2, minor: 40, patch: 8 }], + ])( + 'sets minMaxDeleteAuthorityExists to %s when server version is %s', + async (outcome, serverVersion) => { + useConfig.mockReturnValue({ serverVersion }) + const { result } = renderHook(() => useFeatureToggleContext()) + expect(result.current).toHaveProperty('minMaxDeleteAuthorityExists') + expect(result.current.minMaxDeleteAuthorityExists).toBe(outcome) + } + ) }) diff --git a/src/shared/highlighted-field/use-highlighted-field.js b/src/shared/highlighted-field/use-highlighted-field.js index 208cad49b0..6f8cd9bd32 100644 --- a/src/shared/highlighted-field/use-highlighted-field.js +++ b/src/shared/highlighted-field/use-highlighted-field.js @@ -17,19 +17,16 @@ function gatherHighlightedFieldData({ const canHaveLimits = optionSet ? false : CAN_HAVE_LIMITS_TYPES.includes(valueType) - const categoryCombo = selectors.getCategoryComboById( - metadata, - dataElement.categoryCombo.id - ) - const categoryOptionCombo = selectors.getCategoryOptionCombo( - metadata, - categoryCombo.id, - categoryOptionComboId - ) - const categoryOptionComboName = categoryCombo.isDefault + const categoryOptionCombo = + selectors.getCategoryOptionComboByCategoryOptionComboId( + metadata, + categoryOptionComboId + ) + + const categoryOptionComboName = categoryOptionCombo?.isDefault ? '' - : categoryOptionCombo.displayName + : categoryOptionCombo?.displayName if (dataValue) { return { diff --git a/src/shared/metadata/selectors.js b/src/shared/metadata/selectors.js index 57168a78a2..1cff44a7d6 100644 --- a/src/shared/metadata/selectors.js +++ b/src/shared/metadata/selectors.js @@ -683,3 +683,41 @@ export const getDataSetsByOrgUnitId = createCachedSelector( ) ) )((_, organisationUnitId) => organisationUnitId) + +/** + * @param {*} metadata + */ +export const getCategoryOptionCombosMap = createSelector( + getCategoryCombos, + (categoryCombos) => { + const arrayOfCoCs = Object.values(categoryCombos ?? []).flatMap( + (categoryCombo) => { + const categoryOptionCombos = + categoryCombo?.categoryOptionCombos ?? [] + + return categoryOptionCombos.map(({ id, displayName }) => [ + id, + { + id, + displayName, + isDefault: categoryCombo?.isDefault, + }, + ]) + } + ) + + return Object.fromEntries(arrayOfCoCs) + } +) + +/** + * @param {*} metadata + * @param {string} organisationUnitId + */ +export const getCategoryOptionComboByCategoryOptionComboId = + createCachedSelector( + getCategoryOptionCombosMap, + (_, categoryOptionComboId) => categoryOptionComboId, + (categoryOptionCombosMap, categoryOptionComboId) => + categoryOptionCombosMap?.[categoryOptionComboId] + )((_, categoryOptionComboId) => categoryOptionComboId) diff --git a/src/shared/metadata/selectors.test.js b/src/shared/metadata/selectors.test.js index 25473edad9..c4fee21e65 100644 --- a/src/shared/metadata/selectors.test.js +++ b/src/shared/metadata/selectors.test.js @@ -18,6 +18,7 @@ import { getCategoryCombos, getCategoryOptionById, getCategoryOptionCombosByCategoryComboId, + getCategoryOptionComboByCategoryOptionComboId, getCategoryOptions, getCategoryOptionsByCategoryId, getCategoryOptionsByCategoryOptionComboId, @@ -484,6 +485,52 @@ describe('getCategoryOptionCombosByCategoryComboId', () => { }) }) +describe('getCategoryOptionComboByCategoryOptionComboId', () => { + it('returns the expected data', () => { + const categoryComboId = 'categoryComboId' + const categoryOptionCombo = { + id: 'id', + displayName: 'my category optionCombo', + } + const data = { + categoryCombos: { + [categoryComboId]: { + categoryOptionCombos: [categoryOptionCombo], + isDefault: false, + }, + }, + } + const expected = { ...categoryOptionCombo, isDefault: false } + + expect( + getCategoryOptionComboByCategoryOptionComboId( + data, + categoryOptionCombo.id + ) + ).toEqual(expected) + }) + + it('returns undefined if there is no categoryOptionCombo', () => { + const categoryComboId = 'categoryComboId' + const categoryOptionCombo = { + id: 'id', + displayName: 'my category optionCombo', + } + const data = { + categoryCombos: { + [categoryComboId]: { + categoryOptionCombos: [categoryOptionCombo], + isDefault: false, + }, + }, + } + + expect( + getCategoryOptionComboByCategoryOptionComboId(data, 'invalid_id') + ).toBeUndefined() + }) +}) + describe('getDataElementsByDataSetId', () => { it('returns undefined when there is no matching dataSet', () => { const dataSetId = 'dataSetId' diff --git a/src/shared/plugin-options/index.js b/src/shared/plugin-options/index.js new file mode 100644 index 0000000000..897112e193 --- /dev/null +++ b/src/shared/plugin-options/index.js @@ -0,0 +1,4 @@ +export { + PluginOptionsContext, + usePluginOptions, +} from './plugin-options-context.js' diff --git a/src/shared/plugin-options/plugin-options-context.js b/src/shared/plugin-options/plugin-options-context.js new file mode 100644 index 0000000000..9159c4f09a --- /dev/null +++ b/src/shared/plugin-options/plugin-options-context.js @@ -0,0 +1,11 @@ +import { createContext, useContext } from 'react' + +export const PluginOptionsContext = createContext({ + hideDataSetSelector: false, + hideTabSectionSelector: false, + hideClearSelectionsButton: false, + hideFilterField: false, + hideUnassignedOrgUnits: false, +}) + +export const usePluginOptions = () => useContext(PluginOptionsContext) diff --git a/src/shared/use-user-info/user-info-selectors.js b/src/shared/use-user-info/user-info-selectors.js index 72686ecf7f..3fca456001 100644 --- a/src/shared/use-user-info/user-info-selectors.js +++ b/src/shared/use-user-info/user-info-selectors.js @@ -6,9 +6,14 @@ const EDIT_EXPIRED = 'F_EDIT_EXPIRED' export const getAuthorities = (userInfo) => userInfo.authorities -export const getCanDeleteMinMax = (userInfo) => +export const getCanDeleteMinMax = (userInfo, minMaxDeleteAuthorityExists) => userInfo.authorities.some((auth) => - [DELETE_MIN_MAX_AUTHORITY, ALL_AUTHORITY].includes(auth) + [ + minMaxDeleteAuthorityExists + ? DELETE_MIN_MAX_AUTHORITY + : ADD_MIN_MAX_AUTHORITY, + ALL_AUTHORITY, + ].includes(auth) ) export const getCanAddMinMax = (userInfo) => diff --git a/yarn.lock b/yarn.lock index ae3c3240f3..f339073406 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1486,25 +1486,25 @@ debug "^3.1.0" lodash.once "^4.1.1" -"@dhis2-ui/alert@10.12.11": - version "10.12.11" - resolved "https://registry.yarnpkg.com/@dhis2-ui/alert/-/alert-10.12.11.tgz#3638cde4f8004c4cb8c124caf5c0831e0f8d5f40" - integrity sha512-WYF9Bpd0lalwsSEwp5Oim/WCcmpIpBEEmcFEScG4ucnaY9Kr/qkBwPhFt7aKRFbr5alRw9iao5Q+i1eqm8kPVA== +"@dhis2-ui/alert@10.14.0": + version "10.14.0" + resolved "https://registry.yarnpkg.com/@dhis2-ui/alert/-/alert-10.14.0.tgz#741739f73cfc7d04e617edb1670f345f53ca78e7" + integrity sha512-oEMLZD5DcdT1eoDMqj4IPkyBv4ywWFnMcUswfXxOXC6tVKak6BcSyByrxSCo0gf8Lje1ZSbIJ/dzsd9Ws6Pigg== dependencies: - "@dhis2-ui/portal" "10.12.11" + "@dhis2-ui/portal" "10.14.0" "@dhis2/prop-types" "^3.1.2" - "@dhis2/ui-constants" "10.12.11" - "@dhis2/ui-icons" "10.12.11" + "@dhis2/ui-constants" "10.14.0" + "@dhis2/ui-icons" "10.14.0" classnames "^2.3.1" prop-types "^15.7.2" -"@dhis2-ui/box@10.12.11": - version "10.12.11" - resolved "https://registry.yarnpkg.com/@dhis2-ui/box/-/box-10.12.11.tgz#faa115297cb08a49e15c05a40c436b5c51b72cfe" - integrity sha512-kW8u9zaNbeGCk6OkDQaT+sF3Ptxb7TIiq8XuNGHldMbJxzWMCQysTtkh83kZZ4fKY2cnMIg9wMd/3Pjam0RTBg== +"@dhis2-ui/box@10.14.0": + version "10.14.0" + resolved "https://registry.yarnpkg.com/@dhis2-ui/box/-/box-10.14.0.tgz#55322942edd13f7373a46e8f24145c71cdb60386" + integrity sha512-KggFQ2f1tbyxbH2aH0aXN42Poa5mZDGPA/aKEqVhGcsSgmYNYZsqqjZLz2l0GrzYMwPfRW9+NS1w9g35bPV+kA== dependencies: "@dhis2/prop-types" "^3.1.2" - "@dhis2/ui-constants" "10.12.11" + "@dhis2/ui-constants" "10.14.0" classnames "^2.3.1" prop-types "^15.7.2" @@ -1518,17 +1518,17 @@ classnames "^2.3.1" prop-types "^15.7.2" -"@dhis2-ui/button@10.12.11": - version "10.12.11" - resolved "https://registry.yarnpkg.com/@dhis2-ui/button/-/button-10.12.11.tgz#85a2d48c19d1f3475346ef1fffa38459d8d99ace" - integrity sha512-jpx21d4EObepmZrb38rxzTsF8AQRIgd7NzZd6lwLw7ED/YVDudG2NYhPyd1JMOMJYKOnys9twZ1jUkSLk9/weg== +"@dhis2-ui/button@10.14.0": + version "10.14.0" + resolved "https://registry.yarnpkg.com/@dhis2-ui/button/-/button-10.14.0.tgz#be8fb23714c31ae98660e0ee80819a12e6e87d1c" + integrity sha512-LjBSng7JmL+4TMsPILvHVmI/6rYDr4nkfYbb8VbxoH/1rXLrA+nnr9LugC/U21Cnbg2wWMgj/wJxP4O+DeUT8Q== dependencies: - "@dhis2-ui/layer" "10.12.11" - "@dhis2-ui/loader" "10.12.11" - "@dhis2-ui/popper" "10.12.11" + "@dhis2-ui/layer" "10.14.0" + "@dhis2-ui/loader" "10.14.0" + "@dhis2-ui/popper" "10.14.0" "@dhis2/prop-types" "^3.1.2" - "@dhis2/ui-constants" "10.12.11" - "@dhis2/ui-icons" "10.12.11" + "@dhis2/ui-constants" "10.14.0" + "@dhis2/ui-icons" "10.14.0" classnames "^2.3.1" prop-types "^15.7.2" @@ -1546,30 +1546,30 @@ classnames "^2.3.1" prop-types "^15.7.2" -"@dhis2-ui/calendar@10.12.11": - version "10.12.11" - resolved "https://registry.yarnpkg.com/@dhis2-ui/calendar/-/calendar-10.12.11.tgz#e694ebbeaad836d4b561821bf115b9dc60fccfbd" - integrity sha512-H059KrImig/feY1hXz/m/hY2o+fXDXzqj1VfFXhiVT9AGowLdfOo59uMwNjVC4GNXkBBinP7S/qPQyODyK5tHQ== +"@dhis2-ui/calendar@10.14.0": + version "10.14.0" + resolved "https://registry.yarnpkg.com/@dhis2-ui/calendar/-/calendar-10.14.0.tgz#50aa38ab9f6f153785d573eff8e0798dbcb40dac" + integrity sha512-jcUreo7ukJHff0gDKw8eHB4vVuebMjkfbj7PS5dOylweWAdb2hnAa8cQ5YUbioM1+iJiORLTOEAzS5kkUCHSUw== dependencies: - "@dhis2-ui/button" "10.12.11" - "@dhis2-ui/card" "10.12.11" - "@dhis2-ui/input" "10.12.11" - "@dhis2-ui/layer" "10.12.11" - "@dhis2-ui/popper" "10.12.11" + "@dhis2-ui/button" "10.14.0" + "@dhis2-ui/card" "10.14.0" + "@dhis2-ui/input" "10.14.0" + "@dhis2-ui/layer" "10.14.0" + "@dhis2-ui/popper" "10.14.0" "@dhis2/multi-calendar-dates" "2.1.2" "@dhis2/prop-types" "^3.1.2" - "@dhis2/ui-constants" "10.12.11" - "@dhis2/ui-icons" "10.12.11" + "@dhis2/ui-constants" "10.14.0" + "@dhis2/ui-icons" "10.14.0" classnames "^2.3.1" prop-types "^15.7.2" -"@dhis2-ui/card@10.12.11": - version "10.12.11" - resolved "https://registry.yarnpkg.com/@dhis2-ui/card/-/card-10.12.11.tgz#e7a9aad846438d109ed8b56ead00bd56b8f9972e" - integrity sha512-49MY/54sQTJe7f9effNkBiAt1wytPzsuRgVZ6CyJuTVSTAgj+C6Oge+6P1gi9P1vRz56ANPTxRH4M0nWm3wwcQ== +"@dhis2-ui/card@10.14.0": + version "10.14.0" + resolved "https://registry.yarnpkg.com/@dhis2-ui/card/-/card-10.14.0.tgz#dc4c9a9cbe64e44738fdad4fddea55b24cb8ef0a" + integrity sha512-Hlk3BZS8gnABTJM5JJODGiRiWkcpDk4Y6txab83JKY5ygvmOZlDeG4I8yJQe2gKHnZ1AVuIFhqNLGJOcbuAs8Q== dependencies: "@dhis2/prop-types" "^3.1.2" - "@dhis2/ui-constants" "10.12.11" + "@dhis2/ui-constants" "10.14.0" classnames "^2.3.1" prop-types "^15.7.2" @@ -1583,25 +1583,25 @@ classnames "^2.3.1" prop-types "^15.7.2" -"@dhis2-ui/center@10.12.11": - version "10.12.11" - resolved "https://registry.yarnpkg.com/@dhis2-ui/center/-/center-10.12.11.tgz#2b39ac187b8b29f4872363f0e5c9cf13a70eebd2" - integrity sha512-1Je5UhSGezc5zNxmfxigj2xU3iCsgHqzM2CrPRK7UTzm7IlK8yvsit4Yj1tOQIpYM33W2bKffatr7znW6C+o0w== +"@dhis2-ui/center@10.14.0": + version "10.14.0" + resolved "https://registry.yarnpkg.com/@dhis2-ui/center/-/center-10.14.0.tgz#c64f8b0133da181092c85b5146fd59f4416669ce" + integrity sha512-7y5R+Y17dDhKTU/XJ3QH7YlQ2Dx11mExAUAkX3oaziX5SFOg+S7WVyzr0w8tl3Q4K47G4aiz/VBPOvGTDzB+tQ== dependencies: "@dhis2/prop-types" "^3.1.2" - "@dhis2/ui-constants" "10.12.11" + "@dhis2/ui-constants" "10.14.0" classnames "^2.3.1" prop-types "^15.7.2" -"@dhis2-ui/checkbox@10.12.11": - version "10.12.11" - resolved "https://registry.yarnpkg.com/@dhis2-ui/checkbox/-/checkbox-10.12.11.tgz#ffe6c70500cd9cd5403416c1d820af180b9c16f4" - integrity sha512-dVXO3o9QEkJjUxWrcWxscsfaMPtpR/yAatqbiLTeb0LKuz0H3lRbls6/fZ8EgW+EYp2DhmWKkHsH6IJZOEpaow== +"@dhis2-ui/checkbox@10.14.0": + version "10.14.0" + resolved "https://registry.yarnpkg.com/@dhis2-ui/checkbox/-/checkbox-10.14.0.tgz#e58c0f862ecc054ff7796fe0d6639d6ed12a1394" + integrity sha512-8e+eotFh1yc4U4dBBePXhWRDZ+A37VNuP213hBS49pap4vPcKIxLPSjtui7R1uCnWK9pcCDIUhXaCbc4lsVvnQ== dependencies: - "@dhis2-ui/field" "10.12.11" - "@dhis2-ui/required" "10.12.11" + "@dhis2-ui/field" "10.14.0" + "@dhis2-ui/required" "10.14.0" "@dhis2/prop-types" "^3.1.2" - "@dhis2/ui-constants" "10.12.11" + "@dhis2/ui-constants" "10.14.0" classnames "^2.3.1" prop-types "^15.7.2" @@ -1617,13 +1617,13 @@ classnames "^2.3.1" prop-types "^15.7.2" -"@dhis2-ui/chip@10.12.11": - version "10.12.11" - resolved "https://registry.yarnpkg.com/@dhis2-ui/chip/-/chip-10.12.11.tgz#2db68f9575fbeb57c18a2a222fda7449edf0da17" - integrity sha512-mve32kqN38jKIdPuOWN5G0JdzNWxzRcpqttBuN03ZyJrGZ9O+w/7S6n2KnUF857fjDKH2sgpJH1IC+V8Gr0tbg== +"@dhis2-ui/chip@10.14.0": + version "10.14.0" + resolved "https://registry.yarnpkg.com/@dhis2-ui/chip/-/chip-10.14.0.tgz#a186b33a7cb3ca6c1701d4906eb372253c8a7dab" + integrity sha512-sYHV9KUWCSDmBZj0G2MFxVCma1bF8qK8gOoXDZwlwsEWt6VqN0tlufC0uuYSv9Tb1PDYLELu1f1u4n1maSQn6g== dependencies: "@dhis2/prop-types" "^3.1.2" - "@dhis2/ui-constants" "10.12.11" + "@dhis2/ui-constants" "10.14.0" classnames "^2.3.1" prop-types "^15.7.2" @@ -1637,46 +1637,46 @@ classnames "^2.3.1" prop-types "^15.7.2" -"@dhis2-ui/cover@10.12.11": - version "10.12.11" - resolved "https://registry.yarnpkg.com/@dhis2-ui/cover/-/cover-10.12.11.tgz#be50a8013ac2783277c36ce40413ac1e681c67c4" - integrity sha512-NWVpX+AxgBQBFQVpN088RKGff62lSjAwQf2yV8gLlPS//el2p/SLbUIeQmrDsMkiHf+lsgdkcLLvI3gt6N492g== +"@dhis2-ui/cover@10.14.0": + version "10.14.0" + resolved "https://registry.yarnpkg.com/@dhis2-ui/cover/-/cover-10.14.0.tgz#baeffc510476d4b71ff8da587f9f95ca959213bb" + integrity sha512-3Ig74+8NEj2YIZ228Ixwu+aYYadIZ1E2n8yJhIKYVnAW8Ja5jo1GNnYaidvU3Ni6qIH/egiOiTDvPjaTYZRYvw== dependencies: "@dhis2/prop-types" "^3.1.2" - "@dhis2/ui-constants" "10.12.11" + "@dhis2/ui-constants" "10.14.0" classnames "^2.3.1" prop-types "^15.7.2" -"@dhis2-ui/css@10.12.11": - version "10.12.11" - resolved "https://registry.yarnpkg.com/@dhis2-ui/css/-/css-10.12.11.tgz#0d6607a1c2aad54645ea4e2c0a4be7a2b88581d9" - integrity sha512-Wm0xOb8xPm+3M9t94bBrKl21KJ+b+N6QoOlU8Bnn02AHPLtLMR/3v0Ff2ZG3CwY/vzFIs85aH2jstRC/WYaFsw== +"@dhis2-ui/css@10.14.0": + version "10.14.0" + resolved "https://registry.yarnpkg.com/@dhis2-ui/css/-/css-10.14.0.tgz#398ceb4d6ac850cc29cd0ef746104b33e33fc9ea" + integrity sha512-qTg86LK+BiZTyWMhuImNtHehj/lAevNixomNWW1T82cj1tLMBwRsH/E32WLzrBkYcFXQl7vyNyFPG1KfYLEwcg== dependencies: "@dhis2/prop-types" "^3.1.2" - "@dhis2/ui-constants" "10.12.11" + "@dhis2/ui-constants" "10.14.0" classnames "^2.3.1" prop-types "^15.7.2" -"@dhis2-ui/divider@10.12.11": - version "10.12.11" - resolved "https://registry.yarnpkg.com/@dhis2-ui/divider/-/divider-10.12.11.tgz#681d5da5f6fc414a31e4f12b233ab3afe844747c" - integrity sha512-baSo7fW9h+LeY6eoatEXwHVB0poG7FXeuYZykkQyTN5SMvpBnNnQskYPyNkl9Z90DHf9+Y+shCwmYLILK8Mvzg== +"@dhis2-ui/divider@10.14.0": + version "10.14.0" + resolved "https://registry.yarnpkg.com/@dhis2-ui/divider/-/divider-10.14.0.tgz#2bf356dd82624ed7c33b941650c3694a016f4c16" + integrity sha512-1PFCuYzyxpZ9iQJ3bg0FlKsN9f0gLENep+NCO8R5g7qZfDAkmhLkhM6zrDQ4H/74cwrZ5jVfFTzevv4sG0YCEA== dependencies: "@dhis2/prop-types" "^3.1.2" - "@dhis2/ui-constants" "10.12.11" + "@dhis2/ui-constants" "10.14.0" classnames "^2.3.1" prop-types "^15.7.2" -"@dhis2-ui/field@10.12.11": - version "10.12.11" - resolved "https://registry.yarnpkg.com/@dhis2-ui/field/-/field-10.12.11.tgz#117d00e4e815d0f7d616c6de05eca7ec7638e668" - integrity sha512-KdocrY3Ij6c6dtrAZQhcRcPw5nni0Fk4ihzMUeojIpFbApNaivgRP1ech9HNbb5da6FDRdJ5EV/N7N74XvQDbw== +"@dhis2-ui/field@10.14.0": + version "10.14.0" + resolved "https://registry.yarnpkg.com/@dhis2-ui/field/-/field-10.14.0.tgz#a267c5012c32a07ff0d1f96d5f904dd22bd5aa5d" + integrity sha512-aOjg/Md9xrGnxhdJH3SD9uuXfASuYPKpkF9KC3K6jwxyji4HjMpClb6FkAKs9nw466x2LQ1DZ3xPf+XpPsDB0A== dependencies: - "@dhis2-ui/box" "10.12.11" - "@dhis2-ui/help" "10.12.11" - "@dhis2-ui/label" "10.12.11" + "@dhis2-ui/box" "10.14.0" + "@dhis2-ui/help" "10.14.0" + "@dhis2-ui/label" "10.14.0" "@dhis2/prop-types" "^3.1.2" - "@dhis2/ui-constants" "10.12.11" + "@dhis2/ui-constants" "10.14.0" classnames "^2.3.1" prop-types "^15.7.2" @@ -1693,19 +1693,19 @@ classnames "^2.3.1" prop-types "^15.7.2" -"@dhis2-ui/file-input@10.12.11": - version "10.12.11" - resolved "https://registry.yarnpkg.com/@dhis2-ui/file-input/-/file-input-10.12.11.tgz#0c93f6d60f8dbd7999395f9859d003e11c4cd1ff" - integrity sha512-xTOOnDiu/WmGZgub0Kn16+b/cDNfgQvuA8WpIF0kjIEeTXvZRt/h5dfprPmxHLssQXEC2sNPPhvOZzx8mH7hxA== +"@dhis2-ui/file-input@10.14.0": + version "10.14.0" + resolved "https://registry.yarnpkg.com/@dhis2-ui/file-input/-/file-input-10.14.0.tgz#bed43d44d7164c6c16e2f635607a0f474af17cb2" + integrity sha512-85Vqse39QeYUYDjHE+DhfoDmefX8ibg74IrJlWdW+MgKp5RQ6NyjYqePn0N4XnsJysiK2uXdp0wQy6QLCbvA2Q== dependencies: - "@dhis2-ui/button" "10.12.11" - "@dhis2-ui/field" "10.12.11" - "@dhis2-ui/label" "10.12.11" - "@dhis2-ui/loader" "10.12.11" - "@dhis2-ui/status-icon" "10.12.11" + "@dhis2-ui/button" "10.14.0" + "@dhis2-ui/field" "10.14.0" + "@dhis2-ui/label" "10.14.0" + "@dhis2-ui/loader" "10.14.0" + "@dhis2-ui/status-icon" "10.14.0" "@dhis2/prop-types" "^3.1.2" - "@dhis2/ui-constants" "10.12.11" - "@dhis2/ui-icons" "10.12.11" + "@dhis2/ui-constants" "10.14.0" + "@dhis2/ui-icons" "10.14.0" classnames "^2.3.1" prop-types "^15.7.2" @@ -1725,37 +1725,37 @@ classnames "^2.3.1" prop-types "^15.7.2" -"@dhis2-ui/header-bar@10.12.11": - version "10.12.11" - resolved "https://registry.yarnpkg.com/@dhis2-ui/header-bar/-/header-bar-10.12.11.tgz#0ed8fd7eb38ac6eb721ecd455113e8197795f5ea" - integrity sha512-5N46eaI8LK1tR7vIb9If5B0qtpk93Ql2fPDh3mKRAv33V3vKmi4V8W8EO8aVBWdxD4sqbHq+YKYZxEc3qGC6AA== - dependencies: - "@dhis2-ui/box" "10.12.11" - "@dhis2-ui/button" "10.12.11" - "@dhis2-ui/card" "10.12.11" - "@dhis2-ui/center" "10.12.11" - "@dhis2-ui/divider" "10.12.11" - "@dhis2-ui/input" "10.12.11" - "@dhis2-ui/layer" "10.12.11" - "@dhis2-ui/loader" "10.12.11" - "@dhis2-ui/logo" "10.12.11" - "@dhis2-ui/menu" "10.12.11" - "@dhis2-ui/modal" "10.12.11" - "@dhis2-ui/user-avatar" "10.12.11" +"@dhis2-ui/header-bar@10.14.0": + version "10.14.0" + resolved "https://registry.yarnpkg.com/@dhis2-ui/header-bar/-/header-bar-10.14.0.tgz#1a3eb45f3b2e53b702891b6b137c8b82e5231f73" + integrity sha512-B70/BWmRAf/l/OTau0TFQS3ik9WhR3ygQvhxxvqN3Tsu3wxBx+BNQeiHGsrRRGxgIYu6kiWtn+gBsK9TyG96XA== + dependencies: + "@dhis2-ui/box" "10.14.0" + "@dhis2-ui/button" "10.14.0" + "@dhis2-ui/card" "10.14.0" + "@dhis2-ui/center" "10.14.0" + "@dhis2-ui/divider" "10.14.0" + "@dhis2-ui/input" "10.14.0" + "@dhis2-ui/layer" "10.14.0" + "@dhis2-ui/loader" "10.14.0" + "@dhis2-ui/logo" "10.14.0" + "@dhis2-ui/menu" "10.14.0" + "@dhis2-ui/modal" "10.14.0" + "@dhis2-ui/user-avatar" "10.14.0" "@dhis2/prop-types" "^3.1.2" - "@dhis2/ui-constants" "10.12.11" - "@dhis2/ui-icons" "10.12.11" + "@dhis2/ui-constants" "10.14.0" + "@dhis2/ui-icons" "10.14.0" classnames "^2.3.1" moment "^2.29.1" prop-types "^15.7.2" -"@dhis2-ui/help@10.12.11": - version "10.12.11" - resolved "https://registry.yarnpkg.com/@dhis2-ui/help/-/help-10.12.11.tgz#cd6daa0b89b250384e3f0d7752edb39af3dd6081" - integrity sha512-pv7uNPLyKQBXX1nuoZ5l4mTx9NkIGGXKy/SAzj0UR6nwux/qb5qrgGtMhQhoQlSninfSR2q4xHwTT1nFtNvGFA== +"@dhis2-ui/help@10.14.0": + version "10.14.0" + resolved "https://registry.yarnpkg.com/@dhis2-ui/help/-/help-10.14.0.tgz#f642c892fcaac8f52da75812dc06bd592c06a3a1" + integrity sha512-kq9FWvLrrgxzvBD+gY6l5OepDqVZg/TxNGfzqr78DL22zCBJ295Io/xnCAdWFkCF07E/EnopfcEi7alM04SC2g== dependencies: "@dhis2/prop-types" "^3.1.2" - "@dhis2/ui-constants" "10.12.11" + "@dhis2/ui-constants" "10.14.0" classnames "^2.3.1" prop-types "^15.7.2" @@ -1769,19 +1769,19 @@ classnames "^2.3.1" prop-types "^15.7.2" -"@dhis2-ui/input@10.12.11": - version "10.12.11" - resolved "https://registry.yarnpkg.com/@dhis2-ui/input/-/input-10.12.11.tgz#c7de7e5480de4df078272f6bd4e4c5e20367aadc" - integrity sha512-54YFhDJQZrlOxoxXmCrZRUuInzPD2IcmR7OOropeDc44siKzPucA0y9LpAq8qiU25JhMyJs2BAVCOzBPmGjZCg== +"@dhis2-ui/input@10.14.0": + version "10.14.0" + resolved "https://registry.yarnpkg.com/@dhis2-ui/input/-/input-10.14.0.tgz#57a3f2e302122fdf314aa7238c0119320e96f87a" + integrity sha512-Dlj4iYKcqp8fsSyxvZFo/25oq+T+RT2seGZ94QN+lnO0j66LNSSwdo1JJvvumP9T6VQA9tw7l1lTE7dqPJm5WQ== dependencies: - "@dhis2-ui/box" "10.12.11" - "@dhis2-ui/field" "10.12.11" - "@dhis2-ui/input" "10.12.11" - "@dhis2-ui/loader" "10.12.11" - "@dhis2-ui/status-icon" "10.12.11" + "@dhis2-ui/box" "10.14.0" + "@dhis2-ui/field" "10.14.0" + "@dhis2-ui/input" "10.14.0" + "@dhis2-ui/loader" "10.14.0" + "@dhis2-ui/status-icon" "10.14.0" "@dhis2/prop-types" "^3.1.2" - "@dhis2/ui-constants" "10.12.11" - "@dhis2/ui-icons" "10.12.11" + "@dhis2/ui-constants" "10.14.0" + "@dhis2/ui-icons" "10.14.0" classnames "^2.3.1" prop-types "^15.7.2" @@ -1801,24 +1801,24 @@ classnames "^2.3.1" prop-types "^15.7.2" -"@dhis2-ui/intersection-detector@10.12.11": - version "10.12.11" - resolved "https://registry.yarnpkg.com/@dhis2-ui/intersection-detector/-/intersection-detector-10.12.11.tgz#be9aa0ca67bbe0ea2182c428a0b65163456de3db" - integrity sha512-ULoV4ryXHMDdK4GfNAtj8/xYvmevv3XQ90SQJyDPX+nrhp/KMjOMdiBE2ax04Ck8P7SldQwob2mnjXR3JKka0Q== +"@dhis2-ui/intersection-detector@10.14.0": + version "10.14.0" + resolved "https://registry.yarnpkg.com/@dhis2-ui/intersection-detector/-/intersection-detector-10.14.0.tgz#3996d7e88097ec7c57abe90324931965db6caf6f" + integrity sha512-oTRM52IOFYI+v8T1T6yj5XCk3ZZk4t8OUWA4yy4P0JGaxbTdL+0KbLpaK3k5ob1sKe7aEuIelUQ1jmkVgHDM8Q== dependencies: "@dhis2/prop-types" "^3.1.2" - "@dhis2/ui-constants" "10.12.11" + "@dhis2/ui-constants" "10.14.0" classnames "^2.3.1" prop-types "^15.7.2" -"@dhis2-ui/label@10.12.11": - version "10.12.11" - resolved "https://registry.yarnpkg.com/@dhis2-ui/label/-/label-10.12.11.tgz#f5f9a5b79cb151709e9532531e07c6822f3e3be2" - integrity sha512-ujKLw13E4Zqdku8Kle4km/xp/SmRxAkiIXxe1PxkgoZ5GXetYTIIABnjF3oYULxNJpYRis5b6L0r/wfiz3wbTw== +"@dhis2-ui/label@10.14.0": + version "10.14.0" + resolved "https://registry.yarnpkg.com/@dhis2-ui/label/-/label-10.14.0.tgz#805cf9319f03f50906b5c0ace99aa969c898f5f0" + integrity sha512-2/DtqMurofkd5CK3tQ1UmaJHy0RxAAYBXxFjhjEESEA9wk2PrzjegqnMWjpFkvpXRQ3/0Ma8Gr/1iJyeCbj7ow== dependencies: - "@dhis2-ui/required" "10.12.11" + "@dhis2-ui/required" "10.14.0" "@dhis2/prop-types" "^3.1.2" - "@dhis2/ui-constants" "10.12.11" + "@dhis2/ui-constants" "10.14.0" classnames "^2.3.1" prop-types "^15.7.2" @@ -1833,14 +1833,14 @@ classnames "^2.3.1" prop-types "^15.7.2" -"@dhis2-ui/layer@10.12.11": - version "10.12.11" - resolved "https://registry.yarnpkg.com/@dhis2-ui/layer/-/layer-10.12.11.tgz#db1e6cb4e7958eca793260024c834e984da56959" - integrity sha512-N8kBcfYbMIZFDqcHFPP8Mg+jNClQYNSqhrVRw+nzGEeGX/ifVD8CSLqbnY/zl/M0/XkBwyOOGZW4RMVkNfYgFw== +"@dhis2-ui/layer@10.14.0": + version "10.14.0" + resolved "https://registry.yarnpkg.com/@dhis2-ui/layer/-/layer-10.14.0.tgz#d6d9c5f7661a4bd401c424a0c9d062abc1dc99c9" + integrity sha512-bj1A3dhIT9dTSj+vckaMRLbZXfRFrWFBRhASLI/fPYL16THVCssxyv29poUCOfSuTKneaMjDN8vt5t8T8PaPjQ== dependencies: - "@dhis2-ui/portal" "10.12.11" + "@dhis2-ui/portal" "10.14.0" "@dhis2/prop-types" "^3.1.2" - "@dhis2/ui-constants" "10.12.11" + "@dhis2/ui-constants" "10.14.0" classnames "^2.3.1" prop-types "^15.7.2" @@ -1855,24 +1855,24 @@ classnames "^2.3.1" prop-types "^15.7.2" -"@dhis2-ui/legend@10.12.11": - version "10.12.11" - resolved "https://registry.yarnpkg.com/@dhis2-ui/legend/-/legend-10.12.11.tgz#967c6a5e29515f360cb4342918c5c3ab3dd5022a" - integrity sha512-774xaQlaW9KhlNv0M8Aw06/e5DLUgT3glFZwxyUjllATyAtPt42ICJCiLVc9MKBTX1oJzcZoTHpCuUwco4HM1w== +"@dhis2-ui/legend@10.14.0": + version "10.14.0" + resolved "https://registry.yarnpkg.com/@dhis2-ui/legend/-/legend-10.14.0.tgz#8ceab91db4e1ea3250c1a267091f834993f0063a" + integrity sha512-Yhz2+QTwUmwmzcfRT4t3rzRrn4rAh0RzWMR05wnE8L2Cx7XBh3UdXd2DNv96BGaD5n9w2p1JkS+i975Rt6g9tg== dependencies: - "@dhis2-ui/required" "10.12.11" + "@dhis2-ui/required" "10.14.0" "@dhis2/prop-types" "^3.1.2" - "@dhis2/ui-constants" "10.12.11" + "@dhis2/ui-constants" "10.14.0" classnames "^2.3.1" prop-types "^15.7.2" -"@dhis2-ui/loader@10.12.11": - version "10.12.11" - resolved "https://registry.yarnpkg.com/@dhis2-ui/loader/-/loader-10.12.11.tgz#246fe75b35a85eab6589f43ff65751ce1202c60e" - integrity sha512-PcR0Xdym5oznEIvr2xIGuN4trEGUzv7ussIrXC3iBEYyZ0JQYrpRkuEyvka/brzBlYM8XtOQ2GfEuXjFu1vTFQ== +"@dhis2-ui/loader@10.14.0": + version "10.14.0" + resolved "https://registry.yarnpkg.com/@dhis2-ui/loader/-/loader-10.14.0.tgz#2257c95a9c35ded065200ed4bf114990b8d00965" + integrity sha512-0tUtTos7kF7LH0I4wzHC/1YuypSPx4Whibbk/oNaWCevEbzC7uaZ+aNNWiILSXmyiW8RCiKJqbdHHWKTgBIXLQ== dependencies: "@dhis2/prop-types" "^3.1.2" - "@dhis2/ui-constants" "10.12.11" + "@dhis2/ui-constants" "10.14.0" classnames "^2.3.1" prop-types "^15.7.2" @@ -1886,115 +1886,115 @@ classnames "^2.3.1" prop-types "^15.7.2" -"@dhis2-ui/logo@10.12.11": - version "10.12.11" - resolved "https://registry.yarnpkg.com/@dhis2-ui/logo/-/logo-10.12.11.tgz#d105bf885429d3dfac5b5607321c5390d2ad8e77" - integrity sha512-WKBrYhbTqL/f4taWoKgchvmKe0oPNTvjfN+Qzm37F9ClLW2w5Yk29HgvwsIVDTs6BIR0UK7PPVF7o1JIsqcnRw== +"@dhis2-ui/logo@10.14.0": + version "10.14.0" + resolved "https://registry.yarnpkg.com/@dhis2-ui/logo/-/logo-10.14.0.tgz#90161059d2a9706164262427ca7f918fa16fa2de" + integrity sha512-hLPyDrSRKOHFB6VzJovXhSyuXfyYSkMWg4oudw4kbmqKS1I9pb+YjY6l0K0fb9JOU2TktQa0NJhA0REyiA1zZQ== dependencies: "@dhis2/prop-types" "^3.1.2" - "@dhis2/ui-constants" "10.12.11" + "@dhis2/ui-constants" "10.14.0" classnames "^2.3.1" prop-types "^15.7.2" -"@dhis2-ui/menu@10.12.11": - version "10.12.11" - resolved "https://registry.yarnpkg.com/@dhis2-ui/menu/-/menu-10.12.11.tgz#920c10be6b3478a5e30e3f13df5fd91c29cb74bc" - integrity sha512-loM7qS4UBpUzhwCTUHOlXtSTnZpGJoyNiF+rz3qxMVZ6i3+9JWK71kybV5XYtrBe0r/A9G53jc/5iR62gZ3QLA== +"@dhis2-ui/menu@10.14.0": + version "10.14.0" + resolved "https://registry.yarnpkg.com/@dhis2-ui/menu/-/menu-10.14.0.tgz#7c2a34f418543ec0db181413fdbb7b9c5cf5928f" + integrity sha512-K0fiSDs2Iagp1rJZTfJs8F8vEToaj8WZzlSTT4cpy0dZeSPz/tQHAqqm2i1nppBn66KhlUwyA/alJzwqeVkcUQ== dependencies: - "@dhis2-ui/card" "10.12.11" - "@dhis2-ui/divider" "10.12.11" - "@dhis2-ui/layer" "10.12.11" - "@dhis2-ui/popper" "10.12.11" - "@dhis2-ui/portal" "10.12.11" + "@dhis2-ui/card" "10.14.0" + "@dhis2-ui/divider" "10.14.0" + "@dhis2-ui/layer" "10.14.0" + "@dhis2-ui/popper" "10.14.0" + "@dhis2-ui/portal" "10.14.0" "@dhis2/prop-types" "^3.1.2" - "@dhis2/ui-constants" "10.12.11" - "@dhis2/ui-icons" "10.12.11" + "@dhis2/ui-constants" "10.14.0" + "@dhis2/ui-icons" "10.14.0" classnames "^2.3.1" prop-types "^15.7.2" -"@dhis2-ui/modal@10.12.11": - version "10.12.11" - resolved "https://registry.yarnpkg.com/@dhis2-ui/modal/-/modal-10.12.11.tgz#de2b7d7643fc94e9eb5ebd2925d1d489e02de1f5" - integrity sha512-8oH2xMxT7/jhiFcNDYN/2sF3aE3gtoyORya0/gEaJJGOLWfoYhk0i2t6y6l9OkoOht9x5aKZ1x3sSoYynzBi3Q== +"@dhis2-ui/modal@10.14.0": + version "10.14.0" + resolved "https://registry.yarnpkg.com/@dhis2-ui/modal/-/modal-10.14.0.tgz#9c92478633b011fe420158ccb44128f1a6b903aa" + integrity sha512-3ab1ZC9VMsnDVvfrWYW3mZIGugSvA2QZ7+KUZXrb7MQKoTu4dgUEWMAW92yv6fjRcqSog0ha4eqxwt1uDqL6GA== dependencies: - "@dhis2-ui/card" "10.12.11" - "@dhis2-ui/center" "10.12.11" - "@dhis2-ui/layer" "10.12.11" - "@dhis2-ui/portal" "10.12.11" + "@dhis2-ui/card" "10.14.0" + "@dhis2-ui/center" "10.14.0" + "@dhis2-ui/layer" "10.14.0" + "@dhis2-ui/portal" "10.14.0" "@dhis2/prop-types" "^3.1.2" - "@dhis2/ui-constants" "10.12.11" - "@dhis2/ui-icons" "10.12.11" + "@dhis2/ui-constants" "10.14.0" + "@dhis2/ui-icons" "10.14.0" classnames "^2.3.1" prop-types "^15.7.2" -"@dhis2-ui/node@10.12.11": - version "10.12.11" - resolved "https://registry.yarnpkg.com/@dhis2-ui/node/-/node-10.12.11.tgz#68c69383bbfe88500589e7dd6ee33d2b2ac8102f" - integrity sha512-FdXWOwmyPcwlWX93CIgMKDlBEPhRUEQyx7GS3T0PGM3kMcGy+BYXdpT+OcNVgX1+hs+rq9aYTkJMdpbsxxNizQ== +"@dhis2-ui/node@10.14.0": + version "10.14.0" + resolved "https://registry.yarnpkg.com/@dhis2-ui/node/-/node-10.14.0.tgz#0d227be9045cca1afdc324c6c1c8f6ce72c2f0df" + integrity sha512-+3/FCUsLyDptcEw0UE0icS0+/6WKupbw4ncHI/lv4yHrQHBm6AlPJJSo0ap74wo/O6FGdCaZwaVMZTUSU/tteg== dependencies: - "@dhis2-ui/loader" "10.12.11" + "@dhis2-ui/loader" "10.14.0" "@dhis2/prop-types" "^3.1.2" - "@dhis2/ui-constants" "10.12.11" + "@dhis2/ui-constants" "10.14.0" classnames "^2.3.1" prop-types "^15.7.2" -"@dhis2-ui/notice-box@10.12.11": - version "10.12.11" - resolved "https://registry.yarnpkg.com/@dhis2-ui/notice-box/-/notice-box-10.12.11.tgz#3c0e3d7d537783811d2819e8b269c34a331af1d8" - integrity sha512-2Mls58ofFX4eBwpS+2xr+Nl3YHf/CizNrT28MYXW9Yw1qx1o0DqXlT9M8A1NTN25IgW1l8e6kSnJlAYElaYGKA== +"@dhis2-ui/notice-box@10.14.0": + version "10.14.0" + resolved "https://registry.yarnpkg.com/@dhis2-ui/notice-box/-/notice-box-10.14.0.tgz#c900867743e54a5e658ec3e353d7cd6f1809d112" + integrity sha512-ck4XQd9/tsQgnSJlNhvYe04d532uKqTjwDoFrN2HDhBGC84nOLVGDknFyUsZHR7xjwzsA8qgBcTGzyBXeE2V7A== dependencies: "@dhis2/prop-types" "^3.1.2" - "@dhis2/ui-constants" "10.12.11" - "@dhis2/ui-icons" "10.12.11" + "@dhis2/ui-constants" "10.14.0" + "@dhis2/ui-icons" "10.14.0" classnames "^2.3.1" prop-types "^15.7.2" -"@dhis2-ui/organisation-unit-tree@10.12.11": - version "10.12.11" - resolved "https://registry.yarnpkg.com/@dhis2-ui/organisation-unit-tree/-/organisation-unit-tree-10.12.11.tgz#c33b45acbcab415733a9849901b680507780e49e" - integrity sha512-XMELAXxn8goGKfW111X5xC1SqnjqBXeGCRA9LkHuoTa3v94d7DxEUwDOIWzSArIu8Dh+hKimkSAxx4xvDgqy4A== +"@dhis2-ui/organisation-unit-tree@10.14.0": + version "10.14.0" + resolved "https://registry.yarnpkg.com/@dhis2-ui/organisation-unit-tree/-/organisation-unit-tree-10.14.0.tgz#ba7cd108608c2cf055cb197a5830fcc12b4d9161" + integrity sha512-1BSQTdDMFH9Ic14gR3ITUtqxB56ybVumE+5G1DSQJahJQhjZs01C0B5S7CBXo9I9NjkKgmVd0A3R8xgH132hbQ== dependencies: - "@dhis2-ui/button" "10.12.11" - "@dhis2-ui/checkbox" "10.12.11" - "@dhis2-ui/loader" "10.12.11" - "@dhis2-ui/node" "10.12.11" + "@dhis2-ui/button" "10.14.0" + "@dhis2-ui/checkbox" "10.14.0" + "@dhis2-ui/loader" "10.14.0" + "@dhis2-ui/node" "10.14.0" "@dhis2/prop-types" "^3.1.2" - "@dhis2/ui-constants" "10.12.11" + "@dhis2/ui-constants" "10.14.0" classnames "^2.3.1" prop-types "^15.7.2" -"@dhis2-ui/pagination@10.12.11": - version "10.12.11" - resolved "https://registry.yarnpkg.com/@dhis2-ui/pagination/-/pagination-10.12.11.tgz#8beda5c154ab9652b64e822bb8503a6eab572297" - integrity sha512-Mv6O2qrQQzEkiUNhLkn35J1C5vbOXbQPFkgJmKXsXIMxtImbfN1WaWGXGHVSZcxjP6R2t1eU3u81ZZrSlwATFQ== +"@dhis2-ui/pagination@10.14.0": + version "10.14.0" + resolved "https://registry.yarnpkg.com/@dhis2-ui/pagination/-/pagination-10.14.0.tgz#1a9bde1db232bdd7be36dc80c533dee828db4912" + integrity sha512-fag7LRMIJ9anisiMCAoeXzYEXcAzr9jOzg8UqAmJvSLeALIXAylMp7f0Z7ycZj5MBd/UEHUrbUVjQ8g8ZyZyOA== dependencies: - "@dhis2-ui/button" "10.12.11" - "@dhis2-ui/select" "10.12.11" + "@dhis2-ui/button" "10.14.0" + "@dhis2-ui/select" "10.14.0" "@dhis2/prop-types" "^3.1.2" - "@dhis2/ui-constants" "10.12.11" - "@dhis2/ui-icons" "10.12.11" + "@dhis2/ui-constants" "10.14.0" + "@dhis2/ui-icons" "10.14.0" classnames "^2.3.1" prop-types "^15.7.2" -"@dhis2-ui/popover@10.12.11": - version "10.12.11" - resolved "https://registry.yarnpkg.com/@dhis2-ui/popover/-/popover-10.12.11.tgz#fdcf8666b5eec44ca737464c17639073e2df247c" - integrity sha512-0uPF84U0VupRBVyLPEVMZBLba+PAA68ufNhiF2m9QHvjJmEuj6IwSzipnePR0KLxch2fUcAjIqmGYyBa+gXMWQ== +"@dhis2-ui/popover@10.14.0": + version "10.14.0" + resolved "https://registry.yarnpkg.com/@dhis2-ui/popover/-/popover-10.14.0.tgz#3093e28320f2ddf1f022bd3544cd3b0e0596f617" + integrity sha512-XlPOvzFTMWnbccG5GfOcVK33rnaLYYmr2TsBL1y414KDy/ufzlT1+rJ+wIElHE2OSLFN1eeXepAGx0RTUIvk0g== dependencies: - "@dhis2-ui/layer" "10.12.11" - "@dhis2-ui/popper" "10.12.11" + "@dhis2-ui/layer" "10.14.0" + "@dhis2-ui/popper" "10.14.0" "@dhis2/prop-types" "^3.1.2" - "@dhis2/ui-constants" "10.12.11" + "@dhis2/ui-constants" "10.14.0" classnames "^2.3.1" prop-types "^15.7.2" -"@dhis2-ui/popper@10.12.11": - version "10.12.11" - resolved "https://registry.yarnpkg.com/@dhis2-ui/popper/-/popper-10.12.11.tgz#88fe1730870ce4732adb93c9a2ae445016d91b4f" - integrity sha512-Yz8qsLbh3wUfCRkBXGdyuW6nbZg8xoSBb11Rk/2S02euAb3wFnIdDfgq2Mhp73h2qlXbYDyyxxWSFv/foeB8Ew== +"@dhis2-ui/popper@10.14.0": + version "10.14.0" + resolved "https://registry.yarnpkg.com/@dhis2-ui/popper/-/popper-10.14.0.tgz#0d7dcd9e5dfa0ecd4bba6479c70d6c9bd735387c" + integrity sha512-tN3gVtITEKX9dcdQJYLpzSaBUmzkc7K4LDHIprwFbmBVolOsDapO6qr2fnCcUmQRw0FQUUnYKKrwvEw8zw/XFA== dependencies: "@dhis2/prop-types" "^3.1.2" - "@dhis2/ui-constants" "10.12.11" + "@dhis2/ui-constants" "10.14.0" "@popperjs/core" "^2.11.8" classnames "^2.3.1" prop-types "^15.7.2" @@ -2014,10 +2014,10 @@ react-popper "^2.2.5" resize-observer-polyfill "^1.5.1" -"@dhis2-ui/portal@10.12.11": - version "10.12.11" - resolved "https://registry.yarnpkg.com/@dhis2-ui/portal/-/portal-10.12.11.tgz#5fc0f1b8b61bc962c690b9286cd276a77c4e2759" - integrity sha512-0EBGazI1wo850RxvDByGIdJqFMcq5j119igtRbATAvdAOFb0qx+dKntv606BzRFEO96O9qkFACpBT4jLUrJ2uQ== +"@dhis2-ui/portal@10.14.0": + version "10.14.0" + resolved "https://registry.yarnpkg.com/@dhis2-ui/portal/-/portal-10.14.0.tgz#dbd4e7eca10c6f0443d6bf4541becea832858705" + integrity sha512-+gYKomRJtJIbHaz21RcUJjOTNUA7Gasmfweaw41UqOmHuUNpTbMcqdcB3D0b8tnsCDoXqapZHncYA4zMhks6jg== dependencies: classnames "^2.3.1" prop-types "^15.7.2" @@ -2030,13 +2030,13 @@ classnames "^2.3.1" prop-types "^15.7.2" -"@dhis2-ui/radio@10.12.11": - version "10.12.11" - resolved "https://registry.yarnpkg.com/@dhis2-ui/radio/-/radio-10.12.11.tgz#cccb0a4246fee01bd310b94267b42bdce11c3377" - integrity sha512-zgIKcxEacrM8uHaaV/FKWJOxHTxGGdnHB0BvzanelHIKKqAYtBZxP4Fzn/58hHL4F0d3R+6muxYYV7RXaAXXvg== +"@dhis2-ui/radio@10.14.0": + version "10.14.0" + resolved "https://registry.yarnpkg.com/@dhis2-ui/radio/-/radio-10.14.0.tgz#5702cf78a4e5f6a2230eb412ef1d5b5af55360a0" + integrity sha512-TdbzrvMzA7xWIbjQUNTaLkEL3fsrs14uYXJ+99jaY5v3DRgkKJina61oRtwFd7PW8nbXBeRy/8vmfOnB1pJsew== dependencies: "@dhis2/prop-types" "^3.1.2" - "@dhis2/ui-constants" "10.12.11" + "@dhis2/ui-constants" "10.14.0" classnames "^2.3.1" prop-types "^15.7.2" @@ -2050,13 +2050,13 @@ classnames "^2.3.1" prop-types "^15.7.2" -"@dhis2-ui/required@10.12.11": - version "10.12.11" - resolved "https://registry.yarnpkg.com/@dhis2-ui/required/-/required-10.12.11.tgz#89cd61be6b99807ed64d5982a1fad254f5e68c23" - integrity sha512-l/DUBdRrSSNoH0BkpFi/x2SlK6rMl44ZtmOjP/e4oQrOoHKc0ajPG2bhf9pVa8sdb+eFNKpZtVtohOjJgwAptw== +"@dhis2-ui/required@10.14.0": + version "10.14.0" + resolved "https://registry.yarnpkg.com/@dhis2-ui/required/-/required-10.14.0.tgz#1d0a7493d9f25a13a7401e25ac9261b3329c045c" + integrity sha512-3I2Y51bLNV/iZa6YXXGHcVHz9TB6qmIASA6sKcZrwfBkjYgJVmKEP4xuA/0JYhYWHbSkLVluiutBB4eLPhMbCA== dependencies: "@dhis2/prop-types" "^3.1.2" - "@dhis2/ui-constants" "10.12.11" + "@dhis2/ui-constants" "10.14.0" classnames "^2.3.1" prop-types "^15.7.2" @@ -2070,36 +2070,36 @@ classnames "^2.3.1" prop-types "^15.7.2" -"@dhis2-ui/segmented-control@10.12.11": - version "10.12.11" - resolved "https://registry.yarnpkg.com/@dhis2-ui/segmented-control/-/segmented-control-10.12.11.tgz#f087bf5ec3dbb410d60c11ae4424d7fc9da50799" - integrity sha512-8nsTVTqjGwvxNAyoerZvP4gEzEKDoDU89uWbjkzjTm2NQspIAi1NxWZvqFtzXXK8eqb2VnDYwsPeWrn7gy978g== +"@dhis2-ui/segmented-control@10.14.0": + version "10.14.0" + resolved "https://registry.yarnpkg.com/@dhis2-ui/segmented-control/-/segmented-control-10.14.0.tgz#b07626efb28a5371b69bc79e6d78924ed5f73476" + integrity sha512-t5U1t7yMQ9eF7USMW2SQtNg2xGFsVKFlQ/NTCTUiEgpQW+AAvmX+2Ib/pz6E3lwFfydyE1HCalNWamRTQg2bGw== dependencies: "@dhis2/prop-types" "^3.1.2" - "@dhis2/ui-constants" "10.12.11" + "@dhis2/ui-constants" "10.14.0" classnames "^2.3.1" prop-types "^15.7.2" -"@dhis2-ui/select@10.12.11": - version "10.12.11" - resolved "https://registry.yarnpkg.com/@dhis2-ui/select/-/select-10.12.11.tgz#69ecae6a1e16cd987cdb1b91614255cb734c292c" - integrity sha512-z7Dzap4HdqN5P+Pz5FnkoFNSF7U3lZVDQ71ZDeVNE+1eUWDv+lhxXoauARD3WSqgtyBf+Muhz/bIuFPUTY/HDA== - dependencies: - "@dhis2-ui/box" "10.12.11" - "@dhis2-ui/button" "10.12.11" - "@dhis2-ui/card" "10.12.11" - "@dhis2-ui/checkbox" "10.12.11" - "@dhis2-ui/chip" "10.12.11" - "@dhis2-ui/field" "10.12.11" - "@dhis2-ui/input" "10.12.11" - "@dhis2-ui/layer" "10.12.11" - "@dhis2-ui/loader" "10.12.11" - "@dhis2-ui/popper" "10.12.11" - "@dhis2-ui/status-icon" "10.12.11" - "@dhis2-ui/tooltip" "10.12.11" +"@dhis2-ui/select@10.14.0": + version "10.14.0" + resolved "https://registry.yarnpkg.com/@dhis2-ui/select/-/select-10.14.0.tgz#d93687550a5c1fbbf309c4b173e7be0d3cc4e7b1" + integrity sha512-ze1B/Gw8gKf+z/44z9g39w2NcQvu70scTHJWnzFVw9HSn+xiBZPbqK64vZ+RBt84NijP/t3B3/H5218MPR+LnA== + dependencies: + "@dhis2-ui/box" "10.14.0" + "@dhis2-ui/button" "10.14.0" + "@dhis2-ui/card" "10.14.0" + "@dhis2-ui/checkbox" "10.14.0" + "@dhis2-ui/chip" "10.14.0" + "@dhis2-ui/field" "10.14.0" + "@dhis2-ui/input" "10.14.0" + "@dhis2-ui/layer" "10.14.0" + "@dhis2-ui/loader" "10.14.0" + "@dhis2-ui/popper" "10.14.0" + "@dhis2-ui/status-icon" "10.14.0" + "@dhis2-ui/tooltip" "10.14.0" "@dhis2/prop-types" "^3.1.2" - "@dhis2/ui-constants" "10.12.11" - "@dhis2/ui-icons" "10.12.11" + "@dhis2/ui-constants" "10.14.0" + "@dhis2/ui-icons" "10.14.0" classnames "^2.3.1" prop-types "^15.7.2" @@ -2125,55 +2125,55 @@ classnames "^2.3.1" prop-types "^15.7.2" -"@dhis2-ui/selector-bar@10.12.11": - version "10.12.11" - resolved "https://registry.yarnpkg.com/@dhis2-ui/selector-bar/-/selector-bar-10.12.11.tgz#6c5448a7745eaa8a21c2d05eff42e84e0a968d59" - integrity sha512-QRdWJDD5M8R3N1zpL43NMLe9P94tUBJ2/hgtUz79ioVJDTgUCTSBFp6C3cb6zSl6Tn8jDe/CUAsVL7/gITZDSQ== - dependencies: - "@dhis2-ui/button" "10.12.11" - "@dhis2-ui/card" "10.12.11" - "@dhis2-ui/layer" "10.12.11" - "@dhis2-ui/popper" "10.12.11" - "@dhis2/ui-constants" "10.12.11" - "@dhis2/ui-icons" "10.12.11" +"@dhis2-ui/selector-bar@10.14.0": + version "10.14.0" + resolved "https://registry.yarnpkg.com/@dhis2-ui/selector-bar/-/selector-bar-10.14.0.tgz#8a160348f873726c16c520e7b4e9f6f248fa73b7" + integrity sha512-n0O28/uGzukbsMHTp85ijkuNEOFGL5jlxlWCzWLW2Tf5kvPzff8nJJ86JX3aBaR9k1QgZZZv95vuanmN2K/0hg== + dependencies: + "@dhis2-ui/button" "10.14.0" + "@dhis2-ui/card" "10.14.0" + "@dhis2-ui/layer" "10.14.0" + "@dhis2-ui/popper" "10.14.0" + "@dhis2/ui-constants" "10.14.0" + "@dhis2/ui-icons" "10.14.0" classnames "^2.3.1" prop-types "^15.7.2" -"@dhis2-ui/sharing-dialog@10.12.11": - version "10.12.11" - resolved "https://registry.yarnpkg.com/@dhis2-ui/sharing-dialog/-/sharing-dialog-10.12.11.tgz#5470269438e87b1265975791f79f3bcee6dccd47" - integrity sha512-I+thKzlD0p7Prgr0AdmJznVdWB/1ITBaBPwdc2ITXiH2v4l9NFbu/gV1cdde0NtxUNOb1ZI63q2PZ047br65bw== - dependencies: - "@dhis2-ui/box" "10.12.11" - "@dhis2-ui/button" "10.12.11" - "@dhis2-ui/card" "10.12.11" - "@dhis2-ui/divider" "10.12.11" - "@dhis2-ui/input" "10.12.11" - "@dhis2-ui/layer" "10.12.11" - "@dhis2-ui/menu" "10.12.11" - "@dhis2-ui/modal" "10.12.11" - "@dhis2-ui/notice-box" "10.12.11" - "@dhis2-ui/popper" "10.12.11" - "@dhis2-ui/select" "10.12.11" - "@dhis2-ui/tab" "10.12.11" - "@dhis2-ui/tooltip" "10.12.11" - "@dhis2-ui/user-avatar" "10.12.11" +"@dhis2-ui/sharing-dialog@10.14.0": + version "10.14.0" + resolved "https://registry.yarnpkg.com/@dhis2-ui/sharing-dialog/-/sharing-dialog-10.14.0.tgz#afecab4c7426badbf780b78770140f1cbf12ed44" + integrity sha512-t2GSIjaWjy/sDo0w970wpdQSRaocNy5YLFPNWOKgFuAPWWwRG4sUOpQFNSTJ08Com3pL/Iih4/D1h8MIe/VmIA== + dependencies: + "@dhis2-ui/box" "10.14.0" + "@dhis2-ui/button" "10.14.0" + "@dhis2-ui/card" "10.14.0" + "@dhis2-ui/divider" "10.14.0" + "@dhis2-ui/input" "10.14.0" + "@dhis2-ui/layer" "10.14.0" + "@dhis2-ui/menu" "10.14.0" + "@dhis2-ui/modal" "10.14.0" + "@dhis2-ui/notice-box" "10.14.0" + "@dhis2-ui/popper" "10.14.0" + "@dhis2-ui/select" "10.14.0" + "@dhis2-ui/tab" "10.14.0" + "@dhis2-ui/tooltip" "10.14.0" + "@dhis2-ui/user-avatar" "10.14.0" "@dhis2/prop-types" "^3.1.2" - "@dhis2/ui-constants" "10.12.11" - "@dhis2/ui-icons" "10.12.11" + "@dhis2/ui-constants" "10.14.0" + "@dhis2/ui-icons" "10.14.0" "@react-hook/size" "^2.1.2" classnames "^2.3.1" prop-types "^15.7.2" -"@dhis2-ui/status-icon@10.12.11": - version "10.12.11" - resolved "https://registry.yarnpkg.com/@dhis2-ui/status-icon/-/status-icon-10.12.11.tgz#1ed0e54af8c98a4927b8ecfaa4805408b2b65f3d" - integrity sha512-fN5NseDPppj/0Rtf4HTGLWuDb0kmCg2kVbZZ49EWyGOU67ZcoFLcL/ohteUuVwl9/yZkU8r+vocXOEofb9bhaw== +"@dhis2-ui/status-icon@10.14.0": + version "10.14.0" + resolved "https://registry.yarnpkg.com/@dhis2-ui/status-icon/-/status-icon-10.14.0.tgz#add1b9653606c9f8220ba0dbe65be5f0a850f86b" + integrity sha512-X0fsoW3PtsyBc0aWm2yCX3iNH1JFSfxO4l5L9/ttJ1F/AJmoXUsKfR77IbktP+IbnMXVxVocr+ePBXbbalOnvA== dependencies: - "@dhis2-ui/loader" "10.12.11" + "@dhis2-ui/loader" "10.14.0" "@dhis2/prop-types" "^3.1.2" - "@dhis2/ui-constants" "10.12.11" - "@dhis2/ui-icons" "10.12.11" + "@dhis2/ui-constants" "10.14.0" + "@dhis2/ui-icons" "10.14.0" classnames "^2.3.1" prop-types "^15.7.2" @@ -2189,15 +2189,15 @@ classnames "^2.3.1" prop-types "^15.7.2" -"@dhis2-ui/switch@10.12.11": - version "10.12.11" - resolved "https://registry.yarnpkg.com/@dhis2-ui/switch/-/switch-10.12.11.tgz#73f5aa524343350e4e934e8eaf3a7dbc83902f22" - integrity sha512-hHwap08Iu9bIX1ip19KrPsbaA9GYzGpcNaXBhggWXlqtZVmH0D2E0OASfdJmI5lROGr3H627r+2vyBZcdZf3HA== +"@dhis2-ui/switch@10.14.0": + version "10.14.0" + resolved "https://registry.yarnpkg.com/@dhis2-ui/switch/-/switch-10.14.0.tgz#6f60de1c5ef5f08a313d5b06125bfcb379d97fa6" + integrity sha512-wxATS/jO553l8F4V+9TBlg1Q84diT1KoWL6NRYRKCUxQAEB1o29sglqk8H0CwV0c3+s5WZHHAVF/gya8YCmjIA== dependencies: - "@dhis2-ui/field" "10.12.11" - "@dhis2-ui/required" "10.12.11" + "@dhis2-ui/field" "10.14.0" + "@dhis2-ui/required" "10.14.0" "@dhis2/prop-types" "^3.1.2" - "@dhis2/ui-constants" "10.12.11" + "@dhis2/ui-constants" "10.14.0" classnames "^2.3.1" prop-types "^15.7.2" @@ -2213,51 +2213,51 @@ classnames "^2.3.1" prop-types "^15.7.2" -"@dhis2-ui/tab@10.12.11": - version "10.12.11" - resolved "https://registry.yarnpkg.com/@dhis2-ui/tab/-/tab-10.12.11.tgz#04ef7abe613cb66aca770657578b7cc8d4e4d787" - integrity sha512-IhA7sxj+VM/WHGaxfs+eWFwa5h+QT/ajLg4ruAHdT+paYjMlJbBdD26zrvd9e2iWVngW1tqOTS0T237WZXOcmQ== +"@dhis2-ui/tab@10.14.0": + version "10.14.0" + resolved "https://registry.yarnpkg.com/@dhis2-ui/tab/-/tab-10.14.0.tgz#472e5456648d8ed69da52d4225ad7df8dbbd190e" + integrity sha512-QvxS8I8hrdamD9Gh0qMCXSThgl1ix8sYfHR7bAFoLbwfn8gw6SlFCUc4QeIB/swnI0hgYl8dc41ind1JnxOvjw== dependencies: - "@dhis2-ui/tooltip" "10.12.11" + "@dhis2-ui/tooltip" "10.14.0" "@dhis2/prop-types" "^3.1.2" - "@dhis2/ui-constants" "10.12.11" - "@dhis2/ui-icons" "10.12.11" + "@dhis2/ui-constants" "10.14.0" + "@dhis2/ui-icons" "10.14.0" classnames "^2.3.1" prop-types "^15.7.2" -"@dhis2-ui/table@10.12.11": - version "10.12.11" - resolved "https://registry.yarnpkg.com/@dhis2-ui/table/-/table-10.12.11.tgz#6fb33a2496e5df77ed75f192b8f3033a9d59cbd6" - integrity sha512-N/o1mqmS+QCRyLmNfYUVwkAPm2CtHKQihJ5I7eMZKnA67Vkj3z6kQDn75+58hqSJRXiMsh6rgijW58iPjLkHVQ== +"@dhis2-ui/table@10.14.0": + version "10.14.0" + resolved "https://registry.yarnpkg.com/@dhis2-ui/table/-/table-10.14.0.tgz#6595f823baff22892087b98bae59fe9e3054bd39" + integrity sha512-QM1i83bBpdRus2mF0QcpTC9eMJNTbBqpwxYlHsKWnUGR8ldKsF8/W5slaM1E7fKdQ29JugXkzlgZDIr5vhKHyA== dependencies: "@dhis2/prop-types" "^3.1.2" - "@dhis2/ui-constants" "10.12.11" - "@dhis2/ui-icons" "10.12.11" + "@dhis2/ui-constants" "10.14.0" + "@dhis2/ui-icons" "10.14.0" classnames "^2.3.1" prop-types "^15.7.2" -"@dhis2-ui/tag@10.12.11": - version "10.12.11" - resolved "https://registry.yarnpkg.com/@dhis2-ui/tag/-/tag-10.12.11.tgz#3ce2d1f60c0f3a3ec1699249fedb613376d546e4" - integrity sha512-H6/RvBXQe/bn70NTQ7ys0iQzs+rvLPytiBst+4ulMlSk2SHQ36CqM8en7jgk8QqJyms6s+UqxI4NaY1SQGR9DA== +"@dhis2-ui/tag@10.14.0": + version "10.14.0" + resolved "https://registry.yarnpkg.com/@dhis2-ui/tag/-/tag-10.14.0.tgz#3e51e68a1dfdbaee4b6ee1c03ceb90a301759cb2" + integrity sha512-U3vX1wk6amBvSmepdvxfW3d6neVaZvJ26AkcvGWBuNVG85SrZjBrqJBsVN6bDmdXZbDDW3RJ7Qtcj4MKD9sBmw== dependencies: "@dhis2/prop-types" "^3.1.2" - "@dhis2/ui-constants" "10.12.11" + "@dhis2/ui-constants" "10.14.0" classnames "^2.3.1" prop-types "^15.7.2" -"@dhis2-ui/text-area@10.12.11": - version "10.12.11" - resolved "https://registry.yarnpkg.com/@dhis2-ui/text-area/-/text-area-10.12.11.tgz#07f707640dcc21a10ba9667a0b9ba5bb33606954" - integrity sha512-TLZnd1gcHTG4f5A0VjbpUS3XAqinERCLQfzLVhmtsB2LetgmfeDoZPn8VTF4lRVMt2an6zKOgaNu8k/3s2IAuQ== +"@dhis2-ui/text-area@10.14.0": + version "10.14.0" + resolved "https://registry.yarnpkg.com/@dhis2-ui/text-area/-/text-area-10.14.0.tgz#8dd0eb80b54d5eed30efb9df9dc46598a32da136" + integrity sha512-F/g8cP+6ifdCyyzCGcg2Yq9qrAgsudHO/XINHE1RtZaUwj9HBOTWoPtln8S9ZKWmv+eOJPKYNzkyfL526hR5Og== dependencies: - "@dhis2-ui/box" "10.12.11" - "@dhis2-ui/field" "10.12.11" - "@dhis2-ui/loader" "10.12.11" - "@dhis2-ui/status-icon" "10.12.11" + "@dhis2-ui/box" "10.14.0" + "@dhis2-ui/field" "10.14.0" + "@dhis2-ui/loader" "10.14.0" + "@dhis2-ui/status-icon" "10.14.0" "@dhis2/prop-types" "^3.1.2" - "@dhis2/ui-constants" "10.12.11" - "@dhis2/ui-icons" "10.12.11" + "@dhis2/ui-constants" "10.14.0" + "@dhis2/ui-icons" "10.14.0" classnames "^2.3.1" prop-types "^15.7.2" @@ -2276,40 +2276,41 @@ classnames "^2.3.1" prop-types "^15.7.2" -"@dhis2-ui/tooltip@10.12.11": - version "10.12.11" - resolved "https://registry.yarnpkg.com/@dhis2-ui/tooltip/-/tooltip-10.12.11.tgz#90e24135bedd23da3ba26917dcbcf422d0a721fe" - integrity sha512-GPtBgDQ9SPilyG521t1XKbSyhjCsOOYb/Pytfgy3rA7foAes/AoEdQsyx3cwEbjnnJ+nrR9nkOWElB2zAn4Law== +"@dhis2-ui/tooltip@10.14.0": + version "10.14.0" + resolved "https://registry.yarnpkg.com/@dhis2-ui/tooltip/-/tooltip-10.14.0.tgz#797d627b33aebb39861ca000c96406775cf29f08" + integrity sha512-hqpIIhyeqrbsEsvTmjj7HDay238t6w0wtoOAPiGg2K87is2U4+yy3pXGizDBFAGzkUkGqLr9ihdBC99JPVbCiw== dependencies: - "@dhis2-ui/popper" "10.12.11" - "@dhis2-ui/portal" "10.12.11" + "@dhis2-ui/popper" "10.14.0" + "@dhis2-ui/portal" "10.14.0" "@dhis2/prop-types" "^3.1.2" - "@dhis2/ui-constants" "10.12.11" + "@dhis2/ui-constants" "10.14.0" classnames "^2.3.1" prop-types "^15.7.2" -"@dhis2-ui/transfer@10.12.11": - version "10.12.11" - resolved "https://registry.yarnpkg.com/@dhis2-ui/transfer/-/transfer-10.12.11.tgz#4d547d8df76fdc6c32387fc8840a6da93b34c625" - integrity sha512-Asa1uZl+OKj+yJKXpBhI06W+p86bqAjJy/DfV6hi9qEbyev4alkmFN3tZwBVAruBu/o4+fNjYGWUbeyraI+KMA== - dependencies: - "@dhis2-ui/button" "10.12.11" - "@dhis2-ui/field" "10.12.11" - "@dhis2-ui/input" "10.12.11" - "@dhis2-ui/intersection-detector" "10.12.11" - "@dhis2-ui/loader" "10.12.11" +"@dhis2-ui/transfer@10.14.0": + version "10.14.0" + resolved "https://registry.yarnpkg.com/@dhis2-ui/transfer/-/transfer-10.14.0.tgz#7a6abb8f4d6e88bef636bfcc8c1d18a0146bd3e1" + integrity sha512-RSxQ7YV6Mr3A/QEitVYCnUxEmmf/82Jto6tfTopV9pHO4pBFpRIiKPVyvUGrgRzPLNxUJrR04qMTlARr0mUP0g== + dependencies: + "@dhis2-ui/button" "10.14.0" + "@dhis2-ui/field" "10.14.0" + "@dhis2-ui/input" "10.14.0" + "@dhis2-ui/intersection-detector" "10.14.0" + "@dhis2-ui/loader" "10.14.0" + "@dhis2-ui/tooltip" "10.14.0" "@dhis2/prop-types" "^3.1.2" - "@dhis2/ui-constants" "10.12.11" + "@dhis2/ui-constants" "10.14.0" classnames "^2.3.1" prop-types "^15.7.2" -"@dhis2-ui/user-avatar@10.12.11": - version "10.12.11" - resolved "https://registry.yarnpkg.com/@dhis2-ui/user-avatar/-/user-avatar-10.12.11.tgz#ce873470b01ec79d84d34f926d1071f04ca140a9" - integrity sha512-LbsrGTEM0VNQn89hMPNsA5kk0yo/P6fUJ0StRIxMriHVakmRyXALA+dJpMwbMWJIVGkAt0GnPg7n3IqGAYS8+Q== +"@dhis2-ui/user-avatar@10.14.0": + version "10.14.0" + resolved "https://registry.yarnpkg.com/@dhis2-ui/user-avatar/-/user-avatar-10.14.0.tgz#708807b2e574ec3cda6e0be7ba28637d09d11a7a" + integrity sha512-ViXgLjLM42FPHzSvDDBaG6Mcuhb4e5O6a2A5NOg1IDSZHFWLsS+y4Z62lgUv0keliVa/0vZao0ifcNtUWDofwg== dependencies: "@dhis2/prop-types" "^3.1.2" - "@dhis2/ui-constants" "10.12.11" + "@dhis2/ui-constants" "10.14.0" classnames "^2.3.1" prop-types "^15.7.2" @@ -2523,7 +2524,7 @@ i18next "^10.3" moment "^2.24.0" -"@dhis2/multi-calendar-dates@2.1.2": +"@dhis2/multi-calendar-dates@2.1.2", "@dhis2/multi-calendar-dates@^2.1.2": version "2.1.2" resolved "https://registry.yarnpkg.com/@dhis2/multi-calendar-dates/-/multi-calendar-dates-2.1.2.tgz#c3dd01e358c3dda4354c6722bb72e077b3ddd9b8" integrity sha512-7/EuYZFi266QwyRpK+s79iEiwdOUVBwM+QjKwNUHN3zdYMDmQcWLTo5TujJFg1XnnQ8UhdiRqESq5rgoJkM2fQ== @@ -2532,15 +2533,6 @@ "@js-temporal/polyfill" "0.4.3" classnames "^2.3.2" -"@dhis2/multi-calendar-dates@^1.3.2": - version "1.3.2" - resolved "https://registry.yarnpkg.com/@dhis2/multi-calendar-dates/-/multi-calendar-dates-1.3.2.tgz#34e5896f7fdfb761a2ee6035d848cba00446cde5" - integrity sha512-H37EptumkqZeHUpbR4wl3y2NZjeipxUNUI2VaHX28z2fbVD7O9H+k1InSskeP5nNWzTLMdPAM4lt/zQP8oRbrg== - dependencies: - "@dhis2/d2-i18n" "^1.1.3" - "@js-temporal/polyfill" "0.4.3" - classnames "^2.3.2" - "@dhis2/prop-types@^3.0.0-beta.1", "@dhis2/prop-types@^3.1.2": version "3.1.2" resolved "https://registry.yarnpkg.com/@dhis2/prop-types/-/prop-types-3.1.2.tgz#65b8ad2da8cd2f72bc8b951049a6c9d1b97af3e9" @@ -2556,10 +2548,10 @@ workbox-routing "^7.1.0" workbox-strategies "^7.1.0" -"@dhis2/ui-constants@10.12.11": - version "10.12.11" - resolved "https://registry.yarnpkg.com/@dhis2/ui-constants/-/ui-constants-10.12.11.tgz#fab0e375ee4d0a85ae19d7bff89cec427415c18b" - integrity sha512-mmUSzO6cR/RCJkTrj7j5u9jmBGLUc+FnuIhffSxCADvOyshau/aiDIV6vuL3tvx5YUuJjPHyAHg4782Elr8NUQ== +"@dhis2/ui-constants@10.14.0": + version "10.14.0" + resolved "https://registry.yarnpkg.com/@dhis2/ui-constants/-/ui-constants-10.14.0.tgz#09ee584fe25414210d789978316de074ac790b71" + integrity sha512-HmHKsYZ9eelHQZsv9g75iP9YNWpMpWP6+fBBBPE1EBNUHWItQYQsqZf5zX9zZGTDeX6REQEAEZVRrD4r2De/2Q== dependencies: prop-types "^15.7.2" @@ -2570,20 +2562,20 @@ dependencies: prop-types "^15.7.2" -"@dhis2/ui-forms@10.12.11": - version "10.12.11" - resolved "https://registry.yarnpkg.com/@dhis2/ui-forms/-/ui-forms-10.12.11.tgz#63acd36f6e089b16266502169d406ec92d019ada" - integrity sha512-OyI51G/zamI5U8BemzKIignYY0GCysmH9K5RaQmVSf3Ti0BEaDDr/Bf5NTDo1xCg29nAI951OTJf3shSFjhikg== - dependencies: - "@dhis2-ui/button" "10.12.11" - "@dhis2-ui/checkbox" "10.12.11" - "@dhis2-ui/field" "10.12.11" - "@dhis2-ui/file-input" "10.12.11" - "@dhis2-ui/input" "10.12.11" - "@dhis2-ui/radio" "10.12.11" - "@dhis2-ui/select" "10.12.11" - "@dhis2-ui/switch" "10.12.11" - "@dhis2-ui/text-area" "10.12.11" +"@dhis2/ui-forms@10.14.0": + version "10.14.0" + resolved "https://registry.yarnpkg.com/@dhis2/ui-forms/-/ui-forms-10.14.0.tgz#b59d383589d447d8cb61ae29c8c6abcd61980147" + integrity sha512-liZSvlkIOITK0YWf6ZQ20B0gnulQrDYA4QPHY0xIp3su3bTgcvO4EL4P2MOWxxQpqJunWOy5MIrwhW3A/csM3Q== + dependencies: + "@dhis2-ui/button" "10.14.0" + "@dhis2-ui/checkbox" "10.14.0" + "@dhis2-ui/field" "10.14.0" + "@dhis2-ui/file-input" "10.14.0" + "@dhis2-ui/input" "10.14.0" + "@dhis2-ui/radio" "10.14.0" + "@dhis2-ui/select" "10.14.0" + "@dhis2-ui/switch" "10.14.0" + "@dhis2-ui/text-area" "10.14.0" "@dhis2/prop-types" "^3.1.2" classnames "^2.3.1" final-form "^4.20.2" @@ -2610,69 +2602,69 @@ prop-types "^15.7.2" react-final-form "^6.5.3" -"@dhis2/ui-icons@10.12.11": - version "10.12.11" - resolved "https://registry.yarnpkg.com/@dhis2/ui-icons/-/ui-icons-10.12.11.tgz#0bd7a0cd48f1394bad19a2fd917a5f88daf1e783" - integrity sha512-bQWxZz8qZuM4WuQYcfehekwKC4vwR5eBgPvyebF82azblT3I5Z24FGYINn/2/YcycghPxsD1KD/U9Sk0B61I7Q== +"@dhis2/ui-icons@10.14.0": + version "10.14.0" + resolved "https://registry.yarnpkg.com/@dhis2/ui-icons/-/ui-icons-10.14.0.tgz#4f8f3e5db0854f3bec86a11aa4ec9d16596c2416" + integrity sha512-tWzOIMePpNU7+FgZFyp+ilutabc0JHHK4arPyLwUkTbFODLUOENFbqjN4Tjtooys/mZFF4ewWaFeIpgMMEskZA== "@dhis2/ui-icons@7.16.3": version "7.16.3" resolved "https://registry.yarnpkg.com/@dhis2/ui-icons/-/ui-icons-7.16.3.tgz#86ecd617688f9d9d63de3b29ca5d8a236aa03190" integrity sha512-G+/a74qCAxV04aVFOxCbP9s0sEMXUPGGMZn2kLRnrRsLNcSy6/d6h2EoCa1ASs0nOXvacl1mOHyfv2QXMEy7RA== -"@dhis2/ui@^10.1.11", "@dhis2/ui@^10.9.2": - version "10.12.11" - resolved "https://registry.yarnpkg.com/@dhis2/ui/-/ui-10.12.11.tgz#ba1d073957806dbd26729e15477573de9c1b21ba" - integrity sha512-an87p+86MnSUkhe2H8OaKPMcF+Hed+jriJXoZ3kzulQqEimuGC2QYXkGnWldLY6AxxRYMOhOWxp8m8CVdg+Kaw== - dependencies: - "@dhis2-ui/alert" "10.12.11" - "@dhis2-ui/box" "10.12.11" - "@dhis2-ui/button" "10.12.11" - "@dhis2-ui/calendar" "10.12.11" - "@dhis2-ui/card" "10.12.11" - "@dhis2-ui/center" "10.12.11" - "@dhis2-ui/checkbox" "10.12.11" - "@dhis2-ui/chip" "10.12.11" - "@dhis2-ui/cover" "10.12.11" - "@dhis2-ui/css" "10.12.11" - "@dhis2-ui/divider" "10.12.11" - "@dhis2-ui/field" "10.12.11" - "@dhis2-ui/file-input" "10.12.11" - "@dhis2-ui/header-bar" "10.12.11" - "@dhis2-ui/help" "10.12.11" - "@dhis2-ui/input" "10.12.11" - "@dhis2-ui/intersection-detector" "10.12.11" - "@dhis2-ui/label" "10.12.11" - "@dhis2-ui/layer" "10.12.11" - "@dhis2-ui/legend" "10.12.11" - "@dhis2-ui/loader" "10.12.11" - "@dhis2-ui/logo" "10.12.11" - "@dhis2-ui/menu" "10.12.11" - "@dhis2-ui/modal" "10.12.11" - "@dhis2-ui/node" "10.12.11" - "@dhis2-ui/notice-box" "10.12.11" - "@dhis2-ui/organisation-unit-tree" "10.12.11" - "@dhis2-ui/pagination" "10.12.11" - "@dhis2-ui/popover" "10.12.11" - "@dhis2-ui/popper" "10.12.11" - "@dhis2-ui/portal" "10.12.11" - "@dhis2-ui/radio" "10.12.11" - "@dhis2-ui/required" "10.12.11" - "@dhis2-ui/segmented-control" "10.12.11" - "@dhis2-ui/select" "10.12.11" - "@dhis2-ui/selector-bar" "10.12.11" - "@dhis2-ui/sharing-dialog" "10.12.11" - "@dhis2-ui/switch" "10.12.11" - "@dhis2-ui/tab" "10.12.11" - "@dhis2-ui/table" "10.12.11" - "@dhis2-ui/tag" "10.12.11" - "@dhis2-ui/text-area" "10.12.11" - "@dhis2-ui/tooltip" "10.12.11" - "@dhis2-ui/transfer" "10.12.11" - "@dhis2-ui/user-avatar" "10.12.11" - "@dhis2/ui-constants" "10.12.11" - "@dhis2/ui-forms" "10.12.11" - "@dhis2/ui-icons" "10.12.11" +"@dhis2/ui@^10.14.0", "@dhis2/ui@^10.9.2": + version "10.14.0" + resolved "https://registry.yarnpkg.com/@dhis2/ui/-/ui-10.14.0.tgz#69a8182fb3f85fc7c5dc3f5d867b36e7e4b3e015" + integrity sha512-CfoLOCHs1KMjt9DQ7Hdi7vXmf4XMdAFdCOHS7AM/e8/2VPPILCrmNZslnLqJZkX7pbT/ZC8qm4XwLP8JE+4slA== + dependencies: + "@dhis2-ui/alert" "10.14.0" + "@dhis2-ui/box" "10.14.0" + "@dhis2-ui/button" "10.14.0" + "@dhis2-ui/calendar" "10.14.0" + "@dhis2-ui/card" "10.14.0" + "@dhis2-ui/center" "10.14.0" + "@dhis2-ui/checkbox" "10.14.0" + "@dhis2-ui/chip" "10.14.0" + "@dhis2-ui/cover" "10.14.0" + "@dhis2-ui/css" "10.14.0" + "@dhis2-ui/divider" "10.14.0" + "@dhis2-ui/field" "10.14.0" + "@dhis2-ui/file-input" "10.14.0" + "@dhis2-ui/header-bar" "10.14.0" + "@dhis2-ui/help" "10.14.0" + "@dhis2-ui/input" "10.14.0" + "@dhis2-ui/intersection-detector" "10.14.0" + "@dhis2-ui/label" "10.14.0" + "@dhis2-ui/layer" "10.14.0" + "@dhis2-ui/legend" "10.14.0" + "@dhis2-ui/loader" "10.14.0" + "@dhis2-ui/logo" "10.14.0" + "@dhis2-ui/menu" "10.14.0" + "@dhis2-ui/modal" "10.14.0" + "@dhis2-ui/node" "10.14.0" + "@dhis2-ui/notice-box" "10.14.0" + "@dhis2-ui/organisation-unit-tree" "10.14.0" + "@dhis2-ui/pagination" "10.14.0" + "@dhis2-ui/popover" "10.14.0" + "@dhis2-ui/popper" "10.14.0" + "@dhis2-ui/portal" "10.14.0" + "@dhis2-ui/radio" "10.14.0" + "@dhis2-ui/required" "10.14.0" + "@dhis2-ui/segmented-control" "10.14.0" + "@dhis2-ui/select" "10.14.0" + "@dhis2-ui/selector-bar" "10.14.0" + "@dhis2-ui/sharing-dialog" "10.14.0" + "@dhis2-ui/switch" "10.14.0" + "@dhis2-ui/tab" "10.14.0" + "@dhis2-ui/table" "10.14.0" + "@dhis2-ui/tag" "10.14.0" + "@dhis2-ui/text-area" "10.14.0" + "@dhis2-ui/tooltip" "10.14.0" + "@dhis2-ui/transfer" "10.14.0" + "@dhis2-ui/user-avatar" "10.14.0" + "@dhis2/ui-constants" "10.14.0" + "@dhis2/ui-forms" "10.14.0" + "@dhis2/ui-icons" "10.14.0" prop-types "^15.7.2" "@dual-bundle/import-meta-resolve@^4.1.0": @@ -5220,7 +5212,7 @@ ci-info@^2.0.0: resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-2.0.0.tgz#67a9e964be31a51e15e5010d58e6f12834002f46" integrity sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ== -ci-info@^3.2.0: +ci-info@^3.2.0, ci-info@^3.7.0: version "3.9.0" resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.9.0.tgz#4279a62028a7b1f262f3473fc9605f5e218c59b4" integrity sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ== @@ -7254,6 +7246,13 @@ find-up@^5.0.0: locate-path "^6.0.0" path-exists "^4.0.0" +find-yarn-workspace-root@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/find-yarn-workspace-root/-/find-yarn-workspace-root-2.0.0.tgz#f47fb8d239c900eb78179aa81b66673eac88f7bd" + integrity sha512-1IMnbjt4KzsQfnhnzNd8wUEgXZ44IzZaZmnLYx7D5FZlaHt2gW20Cri8Q+E/t5tIj4+epTBub+2Zxu/vNILzqQ== + dependencies: + micromatch "^4.0.2" + flat-cache@^3.0.4: version "3.2.0" resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-3.2.0.tgz#2c0c2d5040c99b1632771a9d105725c0115363ee" @@ -8401,6 +8400,11 @@ is-descriptor@^1.0.0, is-descriptor@^1.0.2: is-accessor-descriptor "^1.0.1" is-data-descriptor "^1.0.1" +is-docker@^2.0.0: + version "2.2.1" + resolved "https://registry.yarnpkg.com/is-docker/-/is-docker-2.2.1.tgz#33eeabe23cfe86f14bde4408a02c0cfb853acdaa" + integrity sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ== + is-extendable@^0.1.0, is-extendable@^0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472e301dfc89" @@ -8706,6 +8710,13 @@ is-windows@^1.0.1, is-windows@^1.0.2: resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d" integrity sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA== +is-wsl@^2.1.1: + version "2.2.0" + resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-2.2.0.tgz#74a4c76e77ca9fd3f932f290c17ea326cd157271" + integrity sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww== + dependencies: + is-docker "^2.0.0" + is-yarn-global@^0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/is-yarn-global/-/is-yarn-global-0.3.0.tgz#d502d3382590ea3004893746754c89139973e232" @@ -9266,9 +9277,9 @@ js-yaml@^4.1.0: argparse "^2.0.1" jsbi@^4.1.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/jsbi/-/jsbi-4.3.0.tgz#b54ee074fb6fcbc00619559305c8f7e912b04741" - integrity sha512-SnZNcinB4RIcnEyZqFPdGPVgrg2AcnykiBy0sHVJQKHYeaLUvi3Exj+iaPpLnFVkDPZIV4U0yvgC9/R4uEAZ9g== + version "4.3.2" + resolved "https://registry.yarnpkg.com/jsbi/-/jsbi-4.3.2.tgz#8a4d05d4e09907d73042135b6aa55a6d161ee955" + integrity sha512-9fqMSQbhJykSeii05nxKl4m6Eqn2P6rOlYiS+C5Dr/HPIU/7yZxu5qzbs40tgaFORiw2Amd0mirjxatXYMkIew== jsbn@~0.1.0: version "0.1.1" @@ -9378,6 +9389,17 @@ json-stable-stringify-without-jsonify@^1.0.1: resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" integrity sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw== +json-stable-stringify@^1.0.2: + version "1.3.0" + resolved "https://registry.yarnpkg.com/json-stable-stringify/-/json-stable-stringify-1.3.0.tgz#8903cfac42ea1a0f97f35d63a4ce0518f0cc6a70" + integrity sha512-qtYiSSFlwot9XHtF9bD9c7rwKjr+RecWT//ZnPvSmEjpV5mmPOCN4j8UjY5hbjNkOwZ/jQv3J6R1/pL7RwgMsg== + dependencies: + call-bind "^1.0.8" + call-bound "^1.0.4" + isarray "^2.0.5" + jsonify "^0.0.1" + object-keys "^1.1.1" + json-stringify-safe@~5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" @@ -9411,6 +9433,11 @@ jsonfile@^6.0.1: optionalDependencies: graceful-fs "^4.1.6" +jsonify@^0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/jsonify/-/jsonify-0.0.1.tgz#2aa3111dae3d34a0f151c63f3a45d995d9420978" + integrity sha512-2/Ki0GcmuqSrgFyelQq9M05y7PS0mEwuIzrf3f1fPqkVDVRvZrPZtVSMHxdgo8Aq0sxAOb/cr2aqqA3LeWHVPg== + jsonparse@^1.2.0: version "1.3.1" resolved "https://registry.yarnpkg.com/jsonparse/-/jsonparse-1.3.1.tgz#3f4dae4a91fac315f71062f8521cc239f1366280" @@ -9491,6 +9518,13 @@ kind-of@^6.0.2, kind-of@^6.0.3: resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd" integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw== +klaw-sync@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/klaw-sync/-/klaw-sync-6.0.0.tgz#1fd2cfd56ebb6250181114f0a581167099c2b28c" + integrity sha512-nIeuVSzdCCs6TDPTqI8w1Yre34sSq7AkZ4B3sfOBbI2CgVSB4Du4aLQijFU2+lhAFCwt9+42Hel6lQNIv6AntQ== + dependencies: + graceful-fs "^4.1.11" + known-css-properties@^0.35.0: version "0.35.0" resolved "https://registry.yarnpkg.com/known-css-properties/-/known-css-properties-0.35.0.tgz#f6f8e40ab4e5700fa32f5b2ef5218a56bc853bd6" @@ -9878,7 +9912,7 @@ micromatch@^3.1.10: snapdragon "^0.8.1" to-regex "^3.0.2" -micromatch@^4.0.4, micromatch@^4.0.8: +micromatch@^4.0.2, micromatch@^4.0.4, micromatch@^4.0.8: version "4.0.8" resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.8.tgz#d66fa18f3a47076789320b9b1af32bd86d9fa202" integrity sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA== @@ -10355,6 +10389,14 @@ onetime@^5.1.0, onetime@^5.1.2: dependencies: mimic-fn "^2.1.0" +open@^7.4.2: + version "7.4.2" + resolved "https://registry.yarnpkg.com/open/-/open-7.4.2.tgz#b8147e26dcf3e426316c730089fd71edd29c2321" + integrity sha512-MVHddDVweXZF3awtlAS+6pgKLlm/JgxZ90+/NBurBoQctVOOB/zDdVjcyPzQ+0laDGbsWgrRkflI65sQeOgT9Q== + dependencies: + is-docker "^2.0.0" + is-wsl "^2.1.1" + optionator@^0.9.3: version "0.9.4" resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.4.tgz#7ea1c1a5d91d764fb282139c88fe11e182a3a734" @@ -10527,6 +10569,26 @@ pascalcase@^0.1.1: resolved "https://registry.yarnpkg.com/pascalcase/-/pascalcase-0.1.1.tgz#b363e55e8006ca6fe21784d2db22bd15d7917f14" integrity sha512-XHXfu/yOQRy9vYOtUDVMN60OEJjW013GoObG1o+xwQTpB9eYJX/BjXMsdW13ZDPruFhYYn0AG22w0xgQMwl3Nw== +patch-package@^8.0.1: + version "8.0.1" + resolved "https://registry.yarnpkg.com/patch-package/-/patch-package-8.0.1.tgz#79d02f953f711e06d1f8949c8a13e5d3d7ba1a60" + integrity sha512-VsKRIA8f5uqHQ7NGhwIna6Bx6D9s/1iXlA1hthBVBEbkq+t4kXD0HHt+rJhf/Z+Ci0F/HCB2hvn0qLdLG+Qxlw== + dependencies: + "@yarnpkg/lockfile" "^1.1.0" + chalk "^4.1.2" + ci-info "^3.7.0" + cross-spawn "^7.0.3" + find-yarn-workspace-root "^2.0.0" + fs-extra "^10.0.0" + json-stable-stringify "^1.0.2" + klaw-sync "^6.0.0" + minimist "^1.2.6" + open "^7.4.2" + semver "^7.5.3" + slash "^2.0.0" + tmp "^0.2.4" + yaml "^2.2.2" + path-dirname@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/path-dirname/-/path-dirname-1.0.2.tgz#cc33d24d525e099a5388c0336c6e32b9160609e0" @@ -10749,6 +10811,11 @@ postcss@^8.4.33, postcss@^8.4.38, postcss@^8.4.43, postcss@^8.5.3: picocolors "^1.1.1" source-map-js "^1.2.1" +postinstall-postinstall@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/postinstall-postinstall/-/postinstall-postinstall-2.1.0.tgz#4f7f77441ef539d1512c40bd04c71b06a4704ca3" + integrity sha512-7hQX6ZlZXIoRiWNrbMQaLzUUfH+sSx39u8EJ9HYuDc1kLo9IXKWjM5RSquZN1ad5GnH8CGFM78fsAAQi3OKEEQ== + prelude-ls@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" @@ -11818,6 +11885,11 @@ signal-exit@^4.0.1: resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-4.1.0.tgz#952188c1cbd546070e2dd20d0f41c0ae0530cb04" integrity sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw== +slash@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/slash/-/slash-2.0.0.tgz#de552851a1759df3a8f206535442f5ec4ddeab44" + integrity sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A== + slash@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" @@ -12642,10 +12714,10 @@ tmp@^0.0.33: dependencies: os-tmpdir "~1.0.2" -tmp@^0.2.1, tmp@~0.2.1: - version "0.2.3" - resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.2.3.tgz#eb783cc22bc1e8bebd0671476d46ea4eb32a79ae" - integrity sha512-nZD7m9iCPC5g0pYmcaxogYKggSfLsdxl8of3Q/oIbqCqLLIO9IAF0GWjX1z9NZRHPiXv8Wex4yDCaZsgEw0Y8w== +tmp@^0.2.1, tmp@^0.2.4, tmp@~0.2.1: + version "0.2.5" + resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.2.5.tgz#b06bcd23f0f3c8357b426891726d16015abfd8f8" + integrity sha512-voyz6MApa1rQGUxT3E+BK7/ROe8itEx7vD8/HEvt4xwXucvQ5G5oeEiHkmHZJuBO21RpOf+YYm9MOivj709jow== tmpl@1.0.5: version "1.0.5" @@ -13790,6 +13862,11 @@ yaml@1.10.2, yaml@^1.10.0: resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b" integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg== +yaml@^2.2.2: + version "2.9.0" + resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.9.0.tgz#78274afd93598a1dfdd6130df6a566defcbf9aa4" + integrity sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA== + yargs-parser@^13.1.2: version "13.1.2" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-13.1.2.tgz#130f09702ebaeef2650d54ce6e3e5706f7a4fb38"