From eaee780c7f924e520dfd7699f0d335f87037acc3 Mon Sep 17 00:00:00 2001 From: brianbrix Date: Fri, 19 Jun 2026 07:14:06 +0300 Subject: [PATCH 1/7] AMP-31133 : Remove RISK and LOGFRAME from Preview --- src/activity/preview/sections/APME.jsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/activity/preview/sections/APME.jsx b/src/activity/preview/sections/APME.jsx index 6c31024..aeb146c 100644 --- a/src/activity/preview/sections/APME.jsx +++ b/src/activity/preview/sections/APME.jsx @@ -30,10 +30,10 @@ class APME extends Component { return (
{buildSimpleField(`${ActivityConstants.INDICATORS}~${ActivityConstants.INDICATOR}`, true, null, false, indicator, null, { noTitle: true, fieldValueClass: styles.sector_title })} - {buildSimpleField(`${ActivityConstants.INDICATORS}~${ActivityConstants.LOG_FRAME}`, true, null, false, indicator, - null, { fieldClass: styles.noborder })} - {buildSimpleField(`${ActivityConstants.INDICATORS}~${ActivityConstants.RISK}`, true, null, false, indicator, - null, { fieldClass: styles.noborder })} + {/*{buildSimpleField(`${ActivityConstants.INDICATORS}~${ActivityConstants.LOG_FRAME}`, true, null, false, indicator,*/} + {/* null, { fieldClass: styles.noborder })}*/} + {/*{buildSimpleField(`${ActivityConstants.INDICATORS}~${ActivityConstants.RISK}`, true, null, false, indicator,*/} + {/* null, { fieldClass: styles.noborder })}*/}
{this.props.translate('Value Tracking')}
From b033cf93099abd3fd308a1d3080f95ac9ddbb5fd Mon Sep 17 00:00:00 2001 From: brianbrix Date: Fri, 19 Jun 2026 07:33:55 +0300 Subject: [PATCH 2/7] AMP-31133 : Improve actual values table --- src/activity/preview/sections/APME.jsx | 38 ++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/src/activity/preview/sections/APME.jsx b/src/activity/preview/sections/APME.jsx index aeb146c..3c130fb 100644 --- a/src/activity/preview/sections/APME.jsx +++ b/src/activity/preview/sections/APME.jsx @@ -46,6 +46,10 @@ class APME extends Component { } _generateValueOrValuesTable(sectionName, value) { + // For the 'actual' section we may have multiple entries — render as a single structured table. + if (sectionName === ActivityConstants.CURRENT && Array.isArray(value) && value.length > 0) { + return this._generateActualsTable(value); + } return (
{Array.isArray(value) ? value.map(v => this._generateValueTable(sectionName, v)) @@ -53,6 +57,40 @@ class APME extends Component {
); } + _generateActualsTable(values) { + const { translate } = this.props; + const hasComment = values.some(v => v[ActivityConstants.INDICATOR_COMMENT]); + return ( + + + + + + {hasComment && } + + + + {values.map((v, idx) => ( + + + + {hasComment && ( + + )} + + ))} + +
{translate('Actual Date')}{translate('Actual Value')}{translate('Comment')}
+ {v[ActivityConstants.INDICATOR_DATE] || '\u2014'} + + {v[ActivityConstants.INDICATOR_VALUE] != null ? v[ActivityConstants.INDICATOR_VALUE] : '\u2014'} + + {v[ActivityConstants.INDICATOR_COMMENT] || '\u2014'} +
+ ); + } + _generateValueTable(sectionName, value) { if (!value) { return null; From 7386db9f9c75f2679c0c5185a7364c2807002cf8 Mon Sep 17 00:00:00 2001 From: brianbrix Date: Wed, 1 Jul 2026 19:16:00 +0100 Subject: [PATCH 3/7] AMP-31140: fix getFieldDef for 3+ level field paths (indirect_programs~program) getFieldDef used i += 2 in the loop body which consumed two path parts per iteration (the for-loop's own i++ added a third advance). For a 3-part path like 'national_plan_objective~indirect_programs~program', only parts[0] and parts[1] were processed, returning the definition for 'indirect_programs' instead of 'program'. Since 'indirect_programs' has no id_only: true, the HydratorHelper in reamp never collected indirect program IDs, so the id-values POST request never included 'national_plan_objective~indirect_programs~program', and hence that key was absent from the id-values response. Fix: traverse one level at a time. Each iteration finds the current part in the current list, advances the list to that definition's children if needed, and moves to the next part. Correctly handles paths of any depth. --- src/modules/field/FieldsManager.js | 47 ++++++++++-------------------- 1 file changed, 16 insertions(+), 31 deletions(-) diff --git a/src/modules/field/FieldsManager.js b/src/modules/field/FieldsManager.js index 9c0d8db..3fb012c 100644 --- a/src/modules/field/FieldsManager.js +++ b/src/modules/field/FieldsManager.js @@ -165,40 +165,25 @@ export default class FieldsManager { return pList.find(itemList => itemList.field_name === pPart); } - let result; - let fieldsDef = this._fieldsDef; - if (fieldPath) { - let tmpDefinition; - let partToSearch; - let listDefinitions = fieldsDef; - const parts = fieldPath.split('~'); + if (!fieldPath) { + return undefined; + } - for (let i = 0; i < parts.length; i++) { - partToSearch = parts[i]; - tmpDefinition = _searchDefInList(listDefinitions, partToSearch); - if (tmpDefinition !== undefined) { - if (tmpDefinition.children !== undefined) { - if (parts[i + 1] !== undefined) { - partToSearch = parts[i + 1]; - listDefinitions = tmpDefinition.children; + const parts = fieldPath.split('~'); + let listDefinitions = this._fieldsDef; + let result; - tmpDefinition = _searchDefInList(listDefinitions, partToSearch); - if (tmpDefinition !== undefined) { - result = tmpDefinition; - i += 2; - } - } else { - result = tmpDefinition; - break; - } - } else { // It doesn't have children - result = tmpDefinition; - break; - } - } + for (let i = 0; i < parts.length; i++) { + const tmpDefinition = _searchDefInList(listDefinitions, parts[i]); + if (tmpDefinition === undefined) { + return undefined; + } + result = tmpDefinition; + if (tmpDefinition.children !== undefined && i + 1 < parts.length) { + listDefinitions = tmpDefinition.children; + } else { + break; } - } else { - fieldsDef = { children: fieldsDef }; // Deprecated?? } return result; } From 751e1a665783af3ccdfd99f6505c6fb742aaced5 Mon Sep 17 00:00:00 2001 From: brianbrix Date: Sun, 5 Jul 2026 10:58:40 +0300 Subject: [PATCH 4/7] AMP-31155 Show base/target in AP M&E disaggregation --- src/activity/preview/sections/APME.jsx | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/activity/preview/sections/APME.jsx b/src/activity/preview/sections/APME.jsx index 3c130fb..961c0fa 100644 --- a/src/activity/preview/sections/APME.jsx +++ b/src/activity/preview/sections/APME.jsx @@ -222,13 +222,15 @@ class APME extends Component { style={{ marginTop: 6, borderTop: '1px solid #ccc', width: '100%' }}> - + {translate('Disaggregation Values')} {translate('Category')} {translate('Sub-Category')} + {translate('Base Value')} + {translate('Target Value')} {translate('Actual Value')} {translate('Actual Date')} @@ -244,6 +246,16 @@ class APME extends Component { {row.dv[ActivityConstants.CHILD_CATEGORY_NAME] || '\u2014'} )} + {row.isFirstDvRow && ( + + {this._renderGlobalValue(row.dv[ActivityConstants.BASE_VALUE]) || '\u2014'} + + )} + {row.isFirstDvRow && ( + + {this._renderGlobalValue(row.dv[ActivityConstants.TARGET_VALUE]) || '\u2014'} + + )} {row.av && row.av[ActivityConstants.ORIGINAL_VALUE] != null ? row.av[ActivityConstants.ORIGINAL_VALUE] : '\u2014'} From ace8bfe424cb5f9e887a4d0c8c305cc86d147fd4 Mon Sep 17 00:00:00 2001 From: brianbrix Date: Sun, 5 Jul 2026 12:24:38 +0300 Subject: [PATCH 5/7] AMP-31155 Improve M&E disaggregation table readability --- src/activity/preview/ActivityPreview.css | 53 ++++++++++++++++++++++++ src/activity/preview/sections/APME.jsx | 51 +++++++++++++---------- 2 files changed, 83 insertions(+), 21 deletions(-) diff --git a/src/activity/preview/ActivityPreview.css b/src/activity/preview/ActivityPreview.css index 5d0672b..ff3a295 100644 --- a/src/activity/preview/ActivityPreview.css +++ b/src/activity/preview/ActivityPreview.css @@ -251,6 +251,59 @@ border: 1px solid #ebebeb; } +.me_disagg_table { + table-layout: fixed; +} + +.me_disagg_title { + text-align: left; + padding: 8px 10px; +} + +.me_disagg_header { + text-align: left; + padding: 8px 10px; + vertical-align: top; + line-height: 1.35; +} + +.me_disagg_cell { + padding: 8px 10px !important; + vertical-align: top; + line-height: 1.35; + word-break: break-word; + overflow-wrap: anywhere; +} + +.me_disagg_category_col { + width: 13%; +} + +.me_disagg_subcategory_col { + width: 13%; +} + +.me_disagg_value_col { + width: 25%; +} + +.me_disagg_actual_col { + width: 12%; +} + +.me_disagg_date_col { + width: 12%; + white-space: nowrap; +} + +.me_disagg_global_value { + min-height: 34px; +} + +.me_disagg_global_value_revised { + margin-top: 4px; +} + .two_box_table { border-top: 1px solid #ebebeb; border-bottom: 1px solid #ebebeb; diff --git a/src/activity/preview/sections/APME.jsx b/src/activity/preview/sections/APME.jsx index 961c0fa..bbcb2ce 100644 --- a/src/activity/preview/sections/APME.jsx +++ b/src/activity/preview/sections/APME.jsx @@ -167,14 +167,23 @@ class APME extends Component { _renderGlobalValue(gv) { if (!gv) return null; const { translate } = this.props; + const original = [ + gv[ActivityConstants.ORIGINAL_VALUE] != null ? gv[ActivityConstants.ORIGINAL_VALUE] : '—', + gv[ActivityConstants.ORIGINAL_VALUE_DATE] ? `(${gv[ActivityConstants.ORIGINAL_VALUE_DATE]})` : null, + ].filter(Boolean).join(' '); + + const revised = gv[ActivityConstants.REVISED_VALUE] != null + ? [ + `${translate('Revised')}: ${gv[ActivityConstants.REVISED_VALUE]}`, + gv[ActivityConstants.REVISED_VALUE_DATE] ? `(${gv[ActivityConstants.REVISED_VALUE_DATE]})` : null, + ].filter(Boolean).join(' ') + : null; + return ( - - {gv[ActivityConstants.ORIGINAL_VALUE] != null ? gv[ActivityConstants.ORIGINAL_VALUE] : '—'} - {gv[ActivityConstants.ORIGINAL_VALUE_DATE] ? ` (${gv[ActivityConstants.ORIGINAL_VALUE_DATE]})` : ''} - {gv[ActivityConstants.REVISED_VALUE] != null - ? ` / ${translate('Revised')}: ${gv[ActivityConstants.REVISED_VALUE]}` : ''} - {gv[ActivityConstants.REVISED_VALUE_DATE] ? ` (${gv[ActivityConstants.REVISED_VALUE_DATE]})` : ''} - +
+
{original}
+ {revised ?
{revised}
: null} +
); } @@ -218,49 +227,49 @@ class APME extends Component { }); return ( - - - - - - - - + + + + + + {rows.map((row, idx) => ( {row.isFirstGroupRow && ( - + )} {row.isFirstDvRow && ( - )} {row.isFirstDvRow && ( - )} {row.isFirstDvRow && ( - )} - - From 3d9e5e77fc050708375e1b7c83378cff34cb96d9 Mon Sep 17 00:00:00 2001 From: brianbrix Date: Sun, 5 Jul 2026 13:00:20 +0300 Subject: [PATCH 6/7] AMP-31155 Display revised base/target values in M&E disaggregation (not original) --- src/activity/preview/sections/APME.jsx | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/activity/preview/sections/APME.jsx b/src/activity/preview/sections/APME.jsx index bbcb2ce..89d36bf 100644 --- a/src/activity/preview/sections/APME.jsx +++ b/src/activity/preview/sections/APME.jsx @@ -166,23 +166,23 @@ class APME extends Component { _renderGlobalValue(gv) { if (!gv) return null; - const { translate } = this.props; - const original = [ - gv[ActivityConstants.ORIGINAL_VALUE] != null ? gv[ActivityConstants.ORIGINAL_VALUE] : '—', - gv[ActivityConstants.ORIGINAL_VALUE_DATE] ? `(${gv[ActivityConstants.ORIGINAL_VALUE_DATE]})` : null, - ].filter(Boolean).join(' '); + // Display revised value if available, otherwise original (matching activity form behavior) + const value = gv[ActivityConstants.REVISED_VALUE] != null + ? gv[ActivityConstants.REVISED_VALUE] + : (gv[ActivityConstants.ORIGINAL_VALUE] != null ? gv[ActivityConstants.ORIGINAL_VALUE] : '—'); - const revised = gv[ActivityConstants.REVISED_VALUE] != null - ? [ - `${translate('Revised')}: ${gv[ActivityConstants.REVISED_VALUE]}`, - gv[ActivityConstants.REVISED_VALUE_DATE] ? `(${gv[ActivityConstants.REVISED_VALUE_DATE]})` : null, - ].filter(Boolean).join(' ') - : null; + const valueDate = gv[ActivityConstants.REVISED_VALUE] != null + ? gv[ActivityConstants.REVISED_VALUE_DATE] + : gv[ActivityConstants.ORIGINAL_VALUE_DATE]; + + const display = [ + value, + valueDate ? `(${valueDate})` : null, + ].filter(Boolean).join(' '); return (
-
{original}
- {revised ?
{revised}
: null} +
{display}
); } From 0c77c783f9f652f22cb02fd99ed0aa34a101132a Mon Sep 17 00:00:00 2001 From: brianbrix Date: Wed, 15 Jul 2026 15:29:38 +0300 Subject: [PATCH 7/7] AMP-31133 : Improve me values table --- src/activity/preview/sections/APME.jsx | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/src/activity/preview/sections/APME.jsx b/src/activity/preview/sections/APME.jsx index 89d36bf..6c72ceb 100644 --- a/src/activity/preview/sections/APME.jsx +++ b/src/activity/preview/sections/APME.jsx @@ -231,15 +231,13 @@ class APME extends Component { style={{ marginTop: 6, borderTop: '1px solid #ccc', width: '100%' }}>
- - - @@ -255,16 +253,6 @@ class APME extends Component { {row.dv[ActivityConstants.CHILD_CATEGORY_NAME] || '\u2014'} )} - {row.isFirstDvRow && ( - - )} - {row.isFirstDvRow && ( - - )}
+ {translate('Disaggregation Values')}
{translate('Category')}{translate('Sub-Category')}{translate('Base Value')}{translate('Target Value')}{translate('Actual Value')}{translate('Actual Date')}{translate('Category')}{translate('Sub-Category')}{translate('Base Value')}{translate('Target Value')}{translate('Actual Value')}{translate('Actual Date')}
{row.groupKey}{row.groupKey} + {row.dv[ActivityConstants.CHILD_CATEGORY_NAME] || '\u2014'} + {this._renderGlobalValue(row.dv[ActivityConstants.BASE_VALUE]) || '\u2014'} + {this._renderGlobalValue(row.dv[ActivityConstants.TARGET_VALUE]) || '\u2014'} + {row.av && row.av[ActivityConstants.ORIGINAL_VALUE] != null ? row.av[ActivityConstants.ORIGINAL_VALUE] : '\u2014'} + {row.av && row.av[ActivityConstants.ORIGINAL_VALUE_DATE] ? row.av[ActivityConstants.ORIGINAL_VALUE_DATE] : '\u2014'}
+ {translate('Disaggregation Values')}
{translate('Category')} {translate('Sub-Category')}{translate('Base Value')}{translate('Target Value')} {translate('Actual Value')} {translate('Actual Date')}
- {this._renderGlobalValue(row.dv[ActivityConstants.BASE_VALUE]) || '\u2014'} - - {this._renderGlobalValue(row.dv[ActivityConstants.TARGET_VALUE]) || '\u2014'} - {row.av && row.av[ActivityConstants.ORIGINAL_VALUE] != null ? row.av[ActivityConstants.ORIGINAL_VALUE] : '\u2014'}