Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
a7ff5af
feat: hide columns when all cells are marked as grey fields
eperedo Feb 21, 2026
8e29c39
feat: add button 'fill with zeros' for each section
eperedo Feb 22, 2026
9665a7b
avoid restoring old dataValues for non numeric dataElements
eperedo Feb 22, 2026
2302b20
refactor datavalues function and remove unnused react variable
eperedo Feb 24, 2026
b19ceaf
fix lint warnings and add test if all cocs are marked as greyed fields
eperedo Feb 27, 2026
49e1be9
add React to import, remove console log and improve testing describe
eperedo Feb 27, 2026
810eaa5
add error handling when updating dataValues
eperedo Feb 27, 2026
a310004
sort propTypes alphabetically
eperedo Mar 2, 2026
ff339e5
Merge pull request #2 from EyeSeeTea/feature/hide-greyed-columns-869c…
adrianq Mar 2, 2026
f28a836
Merge pull request #3 from EyeSeeTea/feature/fillwithzeros-869c2p9q2
adrianq Mar 2, 2026
d4c2b82
bump version
adrianq Mar 2, 2026
7db54fd
chore: suggestions to style the admonitions in docs
Philip-Larsen-Donnelly Apr 14, 2026
b95d977
docs: update title for custom forms doc
kabaros Apr 15, 2026
ce98dc2
Merge tag 'v102.0.2' into develop-ocba
tokland Apr 21, 2026
a8a8ef4
feat(plugin): add "app" plugin mode with hideable UI via plugin options
tokland Apr 22, 2026
8782429
fix: show delete limits button [DHIS2-19517] (#552)
tomzemp May 5, 2026
4bbbbc3
chore(release): cut 102.0.3 [skip release]
dhis2-bot May 5, 2026
5635287
fix: useHighlightedFieldStore coc logic [DHIS2-20741]
tomzemp May 7, 2026
c326236
chore(release): cut 102.0.4 [skip release]
dhis2-bot May 7, 2026
2de0805
fix: use gregorian calendar for iso to handle chrome bug (#557)
KaiVandivier May 13, 2026
f7ba724
chore(release): cut 102.0.5 [skip release]
dhis2-bot May 13, 2026
dda5851
fix: dedupe dhis2-ui (#558)
tomzemp May 13, 2026
fa4aa3b
chore(release): cut 102.0.6 [skip release]
dhis2-bot May 13, 2026
9d9c80d
Merge tag 'v102.0.6' into develop-ocba
tokland May 20, 2026
92c95ff
feat: filter period selector by dataInputPeriods and valid category o…
tokland May 20, 2026
f496078
feat: filter org unit tree by dataset-assigned org units
tokland May 21, 2026
b5ea021
infra: bump version
tokland May 21, 2026
57023d5
infra: Update en.pot
tokland May 21, 2026
cc506b5
feat: add hideUnassignedOrgUnits plugin option
tokland May 26, 2026
7f949d2
Bump version
tokland May 26, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .nvmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
20
28 changes: 28 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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)


Expand Down
2 changes: 1 addition & 1 deletion d2.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -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',
}
Expand Down
8 changes: 6 additions & 2 deletions docs/custom-forms-legacy-plugin.md
Original file line number Diff line number Diff line change
@@ -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)

Expand Down Expand Up @@ -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`
Expand All @@ -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
Expand Down
2 changes: 1 addition & 1 deletion docs/index.yml
Original file line number Diff line number Diff line change
@@ -1 +1 @@
- Custom Forms support: 'custom-forms-legacy-plugin.md'
- Support for JavaScript in custom forms: 'custom-forms-legacy-plugin.md'
57 changes: 55 additions & 2 deletions i18n/en.pot
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ msgstr ""
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1)\n"
"POT-Creation-Date: 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"
Expand Down Expand Up @@ -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"

Expand Down Expand Up @@ -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"

Expand Down Expand Up @@ -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"
Expand Down
11 changes: 7 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
@@ -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,
Expand All @@ -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",
Expand All @@ -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",
Expand All @@ -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",
Expand Down
26 changes: 26 additions & 0 deletions patches/@dhis2+multi-calendar-dates+2.1.2.patch
Original file line number Diff line number Diff line change
@@ -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',
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand All @@ -19,6 +20,7 @@ export default function ContextSelector({ setSelectionHasNoFormMessage }) {
useManageInterParamDependencies()

const { hide } = useRightHandPanelContext()
const { hideClearSelectionsButton } = usePluginOptions()
const hideClearButton = useShouldHideClearButton()
const clearEntireSelection = useClearEntireSelection()
const onClearSelectionClick = () => {
Expand All @@ -33,7 +35,11 @@ export default function ContextSelector({ setSelectionHasNoFormMessage }) {
return (
<div className={styles.hideForPrint}>
<SelectorBar
onClearSelectionClick={onClearSelectionClick}
onClearSelectionClick={
hideClearSelectionsButton
? undefined
: onClearSelectionClick
}
additionalContent={<RightHandSideContent />}
>
<DataSetSelectorBarItem />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 = ({
Expand Down Expand Up @@ -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()
Expand All @@ -181,6 +183,10 @@ export default function DataSetSelectorBarItem() {
}
}, [dataSets, setDataSetId])

if (hideDataSetSelector) {
return null
}

return (
<div data-test="data-set-selector">
<SelectorBarItem
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,27 +2,43 @@
import i18n from '@dhis2/d2-i18n'
import { SelectorBarItem, Divider, Tooltip } from '@dhis2/ui'
import PropTypes from 'prop-types'
import React, { useEffect, useState } from 'react'
import React, { useEffect, useMemo, useState } from 'react'
import {
selectors,
useMetadata,
useDataSetId,
useOrgUnitId,
useOrgUnit,
} from '../../shared/index.js'
import { usePluginOptions } from '../../shared/plugin-options/index.js'
import DebouncedSearchInput from './debounced-search-input.jsx'
import css from './org-unit-selector-bar-item.module.css'
import {
OrganisationUnitTree,
OrganisationUnitTreeRootError,
OrganisationUnitTreeRootLoading,
} from './organisation-unit-tree/index.js'
import useDataSetOrgUnitPaths from './use-data-set-org-unit-paths.js'
import useExpandedState from './use-expanded-state.js'
import useOrgUnitPathsByName from './use-org-unit-paths-by-name.js'
import usePrefetchedOrganisationUnits from './use-prefetched-organisation-units.js'
import useSelectorBarItemValue from './use-select-bar-item-value.js'
import useUserOrgUnits from './use-user-org-units.js'

function useTreeFilterPaths(dataSetId, dataSetOrgUnitPaths, filter, filteredOrgUnitPaths) {

Check failure on line 28 in src/context-selection/org-unit-selector-bar-item/org-unit-selector-bar-item.jsx

View workflow job for this annotation

GitHub Actions / lint / lint

Function 'useTreeFilterPaths' has too many parameters (4). Maximum allowed is 3
return useMemo(() => {
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 (
<div className={css.disabled}>
Expand Down Expand Up @@ -64,13 +80,25 @@

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) ||
Expand Down Expand Up @@ -124,19 +152,22 @@

{!orgUnitPathsByNameLoading &&
(orgUnitPathsByName.error ||
prefetchedOrganisationUnits.error) && (
prefetchedOrganisationUnits.error ||
(hideUnassignedOrgUnits &&
dataSetOrgUnitPaths.error)) && (
<OrganisationUnitTreeRootError
dataTest="org-unit-selector-error"
error={
orgUnitPathsByName.error ||
prefetchedOrganisationUnits.error
prefetchedOrganisationUnits.error ||
dataSetOrgUnitPaths.error
}
/>
)}

{!orgUnitPathsByNameLoading &&
!!filter &&
!filteredOrgUnitPaths.length && (
!treeFilterPaths.length && (
<div data-test="org-unit-selector-none-found">
{i18n.t(
'No organisation units could be found'
Expand All @@ -145,11 +176,11 @@
)}

{!orgUnitPathsByNameLoading &&
(!filter || !!filteredOrgUnitPaths.length) && (
(!filter || !!treeFilterPaths.length) && (
<OrganisationUnitTree
dataTest="org-unit-selector-tree"
singleSelection
filter={filteredOrgUnitPaths}
filter={treeFilterPaths}
roots={userOrgUnits.data || []}
selected={selected}
expanded={expanded}
Expand Down
Loading
Loading