From c671cb61af4cb1e49f8d131d8f6a9dd5012b6bdb Mon Sep 17 00:00:00 2001 From: Hendrik de Graaf Date: Thu, 28 May 2026 10:10:35 +0200 Subject: [PATCH 1/2] fix: lookup ID values in metadata items --- i18n/en.pot | 46 ++++++----------- .../response/event/__tests__/default.spec.js | 49 +++++++++++++++++++ .../response/event/__tests__/response.spec.js | 47 ++++++++++++++++++ src/modules/response/event/default.js | 21 ++++++-- 4 files changed, 126 insertions(+), 37 deletions(-) diff --git a/i18n/en.pot b/i18n/en.pot index a23b4f8e7..69942d84a 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-03-23T09:34:56.311Z\n" -"PO-Revision-Date: 2026-03-23T09:34:56.312Z\n" +"POT-Creation-Date: 2026-05-28T08:40:06.387Z\n" +"PO-Revision-Date: 2026-05-28T08:40:06.388Z\n" msgid "view only" msgstr "view only" @@ -38,11 +38,6 @@ msgstr "Created {{time}} by {{author}}" msgid "Created {{time}}" msgstr "Created {{time}}" -msgid "Viewed {{count}} times" -msgid_plural "Viewed {{count}} times" -msgstr[0] "Viewed 1 time" -msgstr[1] "Viewed {{count}} times" - msgid "Notifications" msgstr "Notifications" @@ -587,11 +582,6 @@ msgstr "Could not load interpretations" msgid "Reply" msgstr "Reply" -msgid "{{count}} replies" -msgid_plural "{{count}} replies" -msgstr[0] "{{count}} reply" -msgstr[1] "{{count}} replies" - msgid "View replies" msgstr "View replies" @@ -771,21 +761,6 @@ msgstr "New line list" msgid "Hide" msgstr "Hide" -msgid "{{count}} org units" -msgid_plural "{{count}} org units" -msgstr[0] "{{count}} org unit" -msgstr[1] "{{count}} org units" - -msgid "{{count}} levels" -msgid_plural "{{count}} levels" -msgstr[0] "{{count}} level" -msgstr[1] "{{count}} levels" - -msgid "{{count}} groups" -msgid_plural "{{count}} groups" -msgstr[0] "{{count}} group" -msgstr[1] "{{count}} groups" - msgid "Selected: {{commaSeparatedListOfOrganisationUnits}}" msgstr "Selected: {{commaSeparatedListOfOrganisationUnits}}" @@ -1336,6 +1311,18 @@ msgstr "Assigned Categories" msgid "No value" msgstr "No value" +msgid "Active" +msgstr "Active" + +msgid "Completed" +msgstr "Completed" + +msgid "Scheduled" +msgstr "Scheduled" + +msgid "Cancelled" +msgstr "Cancelled" + msgid "Text" msgstr "Text" @@ -1492,10 +1479,5 @@ msgstr "Base" msgid "Axis {{axisId}}" msgstr "Axis {{axisId}}" -msgid "{{count}} items" -msgid_plural "{{count}} items" -msgstr[0] "{{count}} item" -msgstr[1] "{{count}} items" - msgid "Reset zoom" msgstr "Reset zoom" diff --git a/src/modules/response/event/__tests__/default.spec.js b/src/modules/response/event/__tests__/default.spec.js index ea1be6b47..e9dab6f71 100644 --- a/src/modules/response/event/__tests__/default.spec.js +++ b/src/modules/response/event/__tests__/default.spec.js @@ -101,6 +101,55 @@ describe('default', () => { }, }) }) + + it('resolves names from the items map when value matches an item id', () => { + const items = { + C6nZpLKjEJr: { + name: 'African Medical and Research Foundation', + }, + CW81uF03hvV: { name: 'AIDSRelief Consortium' }, + } + expect( + getItems( + ['C6nZpLKjEJr', 'CW81uF03hvV'], + 'kO3z4Dhc038.LFsZ8v5v7rq', + { items } + ) + ).toEqual({ + [`kO3z4Dhc038.LFsZ8v5v7rq${PREFIX_SEPARATOR}C6nZpLKjEJr`]: { + name: 'African Medical and Research Foundation', + }, + [`kO3z4Dhc038.LFsZ8v5v7rq${PREFIX_SEPARATOR}CW81uF03hvV`]: { + name: 'AIDSRelief Consortium', + }, + }) + }) + + it('falls back to the raw value when no matching item exists (free text)', () => { + const items = { + jfuXZB3A1ko: { name: 'Stage 1 - Repeatable' }, + } + expect( + getItems(['email@address.com'], 'jfuXZB3A1ko.RUZ2EBP6HQn', { + items, + }) + ).toEqual({ + [`jfuXZB3A1ko.RUZ2EBP6HQn${PREFIX_SEPARATOR}email@address.com`]: + { name: 'email@address.com' }, + }) + }) + + it('prefers the formatter over the items lookup when both are provided', () => { + const items = { 1: { name: 'should not be used' } } + expect( + getItems(['1'], 'foo', { + items, + itemFormatter: () => 'Yes', + }) + ).toEqual({ + [`foo${PREFIX_SEPARATOR}1`]: { name: 'Yes' }, + }) + }) }) describe('getDimensions', () => { diff --git a/src/modules/response/event/__tests__/response.spec.js b/src/modules/response/event/__tests__/response.spec.js index 452391d05..800704570 100644 --- a/src/modules/response/event/__tests__/response.spec.js +++ b/src/modules/response/event/__tests__/response.spec.js @@ -224,5 +224,52 @@ describe('response', () => { ).toEqual(responseEventstatusHideNa) }) }) + + describe('category (non-optionset id column)', () => { + // Regression: a meta column whose cell values are ids (e.g. a + // CATEGORY data element) that are already named in metaData.items + // must surface those names, not the raw ids. + const headerName = 'kO3z4Dhc038.LFsZ8v5v7rq' + const response = { + headers: [ + { + name: headerName, + column: 'Implementing Partner', + valueType: 'TEXT', + type: 'java.lang.String', + hidden: false, + meta: true, + }, + ], + metaData: { + items: { + C6nZpLKjEJr: { + name: 'African Medical and Research Foundation', + }, + CW81uF03hvV: { name: 'AIDSRelief Consortium' }, + [headerName]: { name: 'Implementing Partner' }, + }, + dimensions: { + [headerName]: ['C6nZpLKjEJr', 'CW81uF03hvV'], + }, + }, + rows: [['C6nZpLKjEJr'], ['CW81uF03hvV']], + } + + it('resolves row-value ids to their metaData.items names', () => { + const result = transformResponse(response) + + expect( + result.metaData.items[`${headerName}_C6nZpLKjEJr`] + ).toEqual({ + name: 'African Medical and Research Foundation', + }) + expect( + result.metaData.items[`${headerName}_CW81uF03hvV`] + ).toEqual({ + name: 'AIDSRelief Consortium', + }) + }) + }) }) }) diff --git a/src/modules/response/event/default.js b/src/modules/response/event/default.js index f98e71e67..602b68ccd 100644 --- a/src/modules/response/event/default.js +++ b/src/modules/response/event/default.js @@ -20,12 +20,22 @@ export const getValuesUniqueSortedAsc = (values, valueType = VALUE_TYPE_TEXT) => export const getPrefixedValue = (value, prefix) => `${prefix}${PREFIX_SEPARATOR}${value}` -export const getItems = (values, dimensionId, { itemFormatter } = {}) => - values.reduce((items, value) => { - items[getPrefixedValue(value, dimensionId)] = { - name: itemFormatter ? itemFormatter(value) : value, +const resolveName = (value, itemFormatter, items) => { + if (itemFormatter) { + return itemFormatter(value) + } + // Look up the value in metaData.items: id-valued columns (e.g. a + // CATEGORY data element) carry their label there keyed by the id. + // Free-text columns miss the lookup and fall back to the raw value. + return items?.[value]?.name ?? value +} + +export const getItems = (values, dimensionId, { itemFormatter, items } = {}) => + values.reduce((acc, value) => { + acc[getPrefixedValue(value, dimensionId)] = { + name: resolveName(value, itemFormatter, items), } - return items + return acc }, {}) export const getDimensions = (values, dimensionId) => ({ @@ -70,6 +80,7 @@ export const applyDefaultHandler = ( ...response.metaData.items, ...getItems(uniqueSortedValuesAsc, header.name, { itemFormatter, + items: response.metaData.items, }), }, dimensions: { From cf630b482a76a593dfcaf4e69c0bcea8b593e394 Mon Sep 17 00:00:00 2001 From: Hendrik de Graaf Date: Thu, 28 May 2026 14:44:15 +0200 Subject: [PATCH 2/2] chore: clarify comment --- src/modules/response/event/default.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/modules/response/event/default.js b/src/modules/response/event/default.js index 602b68ccd..3aabc7ba4 100644 --- a/src/modules/response/event/default.js +++ b/src/modules/response/event/default.js @@ -24,9 +24,9 @@ const resolveName = (value, itemFormatter, items) => { if (itemFormatter) { return itemFormatter(value) } - // Look up the value in metaData.items: id-valued columns (e.g. a - // CATEGORY data element) carry their label there keyed by the id. - // Free-text columns miss the lookup and fall back to the raw value. + /* Assume the value could be an ID, which means the name should + * be looked up in `metaData.items`. If that lookup fails the + * value is used directly. */ return items?.[value]?.name ?? value }