Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
121 changes: 114 additions & 7 deletions src/activity/preview/sections/APME.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,14 @@ class APME extends Component {
null, { fieldClass: styles.noborder })}
{buildSimpleField(`${ActivityConstants.INDICATORS}~${ActivityConstants.RISK}`, true, null, false, indicator,
null, { fieldClass: styles.noborder })}
{ActivityConstants.ME_SECTIONS
<div className={styles.box_field_name} style={{ marginTop: 8, marginBottom: 4 }}>
{this.props.translate('Value Tracking')}
</div>
{(!indicator[ActivityConstants.DISAGGREGATION_VALUES] || !indicator[ActivityConstants.DISAGGREGATION_VALUES].length)
&& ActivityConstants.ME_SECTIONS
? ActivityConstants.ME_SECTIONS.map(s => this._generateValueOrValuesTable(s, indicator[s]))
: null}
{this._generateDisaggregationTable(indicator)}
</div>);
}

Expand Down Expand Up @@ -121,17 +126,119 @@ class APME extends Component {
);
}

_renderGlobalValue(gv) {
if (!gv) return null;
const { translate } = this.props;
return (
<span>
{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]})` : ''}
</span>
);
}

_generateDisaggregationTable(indicator) {
const disaggValues = indicator[ActivityConstants.DISAGGREGATION_VALUES];
if (!disaggValues || !disaggValues.length) return null;
const { translate } = this.props;
return (
<table key={Math.random()} className={[styles.box_table, styles.section_group_class].join(' ')}
style={{ marginTop: 6, borderTop: '1px solid #ccc', width: '100%' }}>
<thead>
<tr>
<th colSpan={8} style={{ textAlign: 'left', padding: '4px 0' }}>
{translate('Disaggregation Values')}
</th>
</tr>
<tr>
<th>{translate('Category')}</th>
<th>{translate('Sub-Category')}</th>
<th>{translate('Base Value')}</th>
<th>{translate('Base Date')}</th>
<th>{translate('Target Value')}</th>
<th>{translate('Target Date')}</th>
<th>{translate('Actual Value')}</th>
<th>{translate('Actual Date')}</th>
</tr>
</thead>
<tbody>
{disaggValues.map(dv => {
const base = dv[ActivityConstants.BASE_VALUE];
const target = dv[ActivityConstants.TARGET_VALUE];
const actuals = dv[ActivityConstants.ACTUAL_VALUES] || [];
const rowCount = Math.max(actuals.length, 1);
return Array.from({ length: rowCount }).map((_, i) => {
const av = actuals[i];
return (
<tr key={`${dv.id}-${i}`}>
{i === 0 && <td rowSpan={rowCount}>{dv[ActivityConstants.PARENT_CATEGORY_NAME] || '\u2014'}</td>}
{i === 0 && <td rowSpan={rowCount}>{dv[ActivityConstants.CHILD_CATEGORY_NAME] || '\u2014'}</td>}
{i === 0 && <td rowSpan={rowCount}>{base ? base[ActivityConstants.ORIGINAL_VALUE] : '\u2014'}</td>}
{i === 0 && <td rowSpan={rowCount}>{base ? (base[ActivityConstants.ORIGINAL_VALUE_DATE] || '\u2014') : '\u2014'}</td>}
{i === 0 && <td rowSpan={rowCount}>{target ? target[ActivityConstants.ORIGINAL_VALUE] : '\u2014'}</td>}
{i === 0 && <td rowSpan={rowCount}>{target ? (target[ActivityConstants.ORIGINAL_VALUE_DATE] || '\u2014') : '\u2014'}</td>}
<td>{av ? (av[ActivityConstants.ORIGINAL_VALUE] != null ? av[ActivityConstants.ORIGINAL_VALUE] : '\u2014') : '\u2014'}</td>
<td>{av ? (av[ActivityConstants.ORIGINAL_VALUE_DATE] || '\u2014') : '\u2014'}</td>
</tr>
);
});
})}
</tbody>
</table>
);
}

render() {
const { activity } = this.props;
return (<div>
{activity[ActivityConstants.INDICATORS] ?
activity[ActivityConstants.INDICATORS].map(indicator => (this._generateTable(indicator))) :
null}
</div>);
const { activity, translate } = this.props;
const indicators = activity[ActivityConstants.INDICATORS];
if (!indicators || !indicators.length) return null;

const isMulticountry = indicators.some(ind => {
const raw = ind[ActivityConstants.ACTIVITY_LOCATION];
return raw && (typeof raw === 'object' ? raw.id : raw);
});

if (!isMulticountry) {
return (<div>
{indicators.map(indicator => this._generateTable(indicator))}
</div>);
}

// Group by activity_location id; null/undefined → "Common" group.
// activity_location may be hydrated to {id, value} so normalise to a numeric key.
const groups = new Map();
indicators.forEach(ind => {
const raw = ind[ActivityConstants.ACTIVITY_LOCATION];
const locIdNum = raw && (typeof raw === 'object' ? raw.id : raw) || null;
if (!groups.has(locIdNum)) groups.set(locIdNum, []);
groups.get(locIdNum).push(ind);
});

const sections = [];
groups.forEach((inds, locId) => {
// activity_location is now AmpCategoryValueLocations — after hydration its .value is the location name.
const raw = inds[0][ActivityConstants.ACTIVITY_LOCATION];
const locationName = raw && typeof raw === 'object' ? (raw.value || null) : null;
sections.push(
<div key={locId || 'common'}>
<div className={styles.box_field_name} style={{ background: '#e8e8e8', padding: '4px 6px', marginTop: 8 }}>
{locationName || translate('Common Indicators')}
</div>
{inds.map(ind => this._generateTable(ind))}
</div>
);
});

return <div>{sections}</div>;
}
}

export default Section(APME, { SectionTitle: 'M&E',
useEncapsulateHeader: true,
sID: 'APME'
});


22 changes: 22 additions & 0 deletions src/modules/util/ActivityConstants.js
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,17 @@ const TARGET = 'target';
const REVISED = 'revised';
const CURRENT = 'actual';
const ME_SECTIONS = [BASE, TARGET, REVISED, CURRENT];
const ACTIVITY_LOCATION = 'activity_location';
const DISAGGREGATION_VALUES = 'disaggregation_values';
const PARENT_CATEGORY_NAME = 'parent_category_name';
const CHILD_CATEGORY_NAME = 'child_category_name';
const BASE_VALUE = 'base_value';
const TARGET_VALUE = 'target_value';
const ACTUAL_VALUES = 'actual_values';
const ORIGINAL_VALUE = 'original_value';
const ORIGINAL_VALUE_DATE = 'original_value_date';
const REVISED_VALUE = 'revised_value';
const REVISED_VALUE_DATE = 'revised_value_date';
const COMPONENT_TYPE = 'component_type';
const COMPONENT_TITLE = 'component_title';
const COMPONENT_FUNDING = 'funding';
Expand Down Expand Up @@ -563,6 +574,17 @@ export default Object.freeze({
REVISED,
CURRENT,
ME_SECTIONS,
ACTIVITY_LOCATION,
DISAGGREGATION_VALUES,
PARENT_CATEGORY_NAME,
CHILD_CATEGORY_NAME,
BASE_VALUE,
TARGET_VALUE,
ACTUAL_VALUES,
ORIGINAL_VALUE,
ORIGINAL_VALUE_DATE,
REVISED_VALUE,
REVISED_VALUE_DATE,
COMPONENT_TYPE,
COMPONENT_TITLE,
COMPONENT_FUNDING,
Expand Down