Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
7dac0dc
add chart Target vs Actual - Benefits - Column Chart
eperedo Mar 13, 2026
ac2d481
add visualization Target vs Actual - People - Column Chart
eperedo Mar 13, 2026
642e97e
add chart MER - Target vs Actual - Benefits - Column Chart
eperedo Mar 13, 2026
c633a61
add chart MER - Target vs Actual - People - Column Chart
eperedo Mar 13, 2026
d85ab95
remove chart Target vs Actual - Unique People
eperedo Mar 13, 2026
c83c0d6
add chart Column Chart Disaggregated By Indicator
eperedo Mar 14, 2026
5f07094
add chart Column Chart Disaggregated By Indicator
eperedo Mar 14, 2026
4354b7f
add chart Target vs Actual - Benefits - Line Chart
eperedo Mar 14, 2026
e97d395
add chart Target vs Actual - People - Line Chart
eperedo Mar 14, 2026
4e39683
add chart Line Chart - Male Only
eperedo Mar 14, 2026
f930180
add chart female only
eperedo Mar 14, 2026
e2a463d
add chart Target vs Actual - People - Line/Column Chart - Male Only
eperedo Mar 14, 2026
4192374
add chart People - Line/Column Chart - Female Only
eperedo Mar 14, 2026
9466a4d
remove chart achieved monthly
eperedo Mar 14, 2026
1f2f653
fix visualizations order
eperedo Mar 14, 2026
fcf80fd
Merge branch 'feature/project-code-16-869c5haka' of https://github.co…
eperedo Mar 18, 2026
6bd6063
add script to update dashboards
eperedo Mar 27, 2026
738a885
fix tests
eperedo Mar 27, 2026
d3d25b7
remove comments
eperedo Mar 27, 2026
6681a08
regenerate award dashboard from action
eperedo Apr 8, 2026
ff3c0c0
add all mer indicators in MER - Target vs Actual - Benefits - Column …
eperedo Apr 10, 2026
9cbc2af
update metadata for tests
eperedo Apr 10, 2026
565fe5a
remove targetvsactual benefits/people column chart and sort de by code
eperedo Apr 29, 2026
6075ec9
reorder male only vis. and update targetvsactual mer charts
eperedo Apr 29, 2026
a098347
update metadata test
eperedo Apr 29, 2026
66d8980
reorder indicators in MER charts
eperedo Apr 30, 2026
82680f6
sorted only selected items for MER visualizations
eperedo Apr 30, 2026
deca3f7
fix reorder charts
eperedo May 1, 2026
c88edee
add new charts and text items
eperedo May 1, 2026
0b9d8c8
add unique id for text dashboard items
eperedo May 4, 2026
d4a63f5
include spacer if visualization is not present
eperedo May 4, 2026
6e42e17
add dashboard info
eperedo May 22, 2026
120cbd1
updates locales
eperedo May 22, 2026
531af6b
add texts, spacer and reorder dashboard items
eperedo May 22, 2026
3c27d4c
fix tests
eperedo May 22, 2026
87ebd7d
update metadata for tests
eperedo May 22, 2026
187761e
update metadata for tests
eperedo May 22, 2026
272b85c
fix dashboard items size and update styles for texts
eperedo May 22, 2026
4fe847b
update metadata for tests
eperedo May 22, 2026
3453ab9
update locales
eperedo May 22, 2026
9f5d4c4
fix tests
eperedo May 22, 2026
f42a5e7
fix tests
eperedo May 22, 2026
0a68abd
fix email address in dashboard info modal
Ramon-Jimenez May 25, 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
115 changes: 109 additions & 6 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-03-16T13:43:15.193Z\n"
"PO-Revision-Date: 2026-03-16T13:43:15.193Z\n"
"POT-Creation-Date: 2026-05-22T20:15:03.101Z\n"
"PO-Revision-Date: 2026-05-22T20:15:03.101Z\n"

msgid "Validating Project"
msgstr ""
Expand Down Expand Up @@ -72,6 +72,52 @@ msgstr ""
msgid "Cannot load dashboard"
msgstr ""

msgid "Purpose of Platform Dashboards"
msgstr ""

msgid "Encourage data informed decisions."
msgstr ""

msgid ""
"Help implementers identify phenomena in data that can initiate "
"opportunities for conversations and decision-making on how to engage in "
"adaptive program management."
msgstr ""

msgid ""
"Be incorporated into the monthly quality process. Ideally, this check "
"should be happening when preparing MERs for your regional teams."
msgstr ""

msgid "Conversions by topic"
msgstr ""

msgid "Errors identified"
msgstr ""

msgid "Divergence from targets"
msgstr ""

msgid "Bullets on what to highlight in the MER with mitigation measures"
msgstr ""

msgid "Potential points to escalate"
msgstr ""

msgid "Determining if site visits should be made"
msgstr ""

msgid "Reviewing budget for spent, pending, and remaining"
msgstr ""

msgid ""
"If there is anything that can be improved, please let us know by sending an "
"email to "
msgstr ""

msgid "Close"
msgstr ""

msgid "Data Entry"
msgstr ""

Expand Down Expand Up @@ -129,9 +175,6 @@ msgstr ""
msgid "Validation results"
msgstr ""

msgid "Close"
msgstr ""

msgid "Comments saved"
msgstr ""

Expand Down Expand Up @@ -594,12 +637,72 @@ msgstr ""
msgid "Target vs Actual - Benefits"
msgstr ""

msgid "Target vs Actual - Benefits - Pivot Table"
msgstr ""

msgid "Target vs Actual - Benefits - Column Chart"
msgstr ""

msgid "Target vs Actual - People - Column Chart"
msgstr ""

msgid "MER - Target vs Actual - Benefits - Column Chart"
msgstr ""

msgid "MER - Target vs Actual - People - Column Chart"
msgstr ""

msgid "Target vs Actual - Benefits - Line Chart"
msgstr ""

msgid "Target vs Actual - People - Line Chart"
msgstr ""

msgid "Target vs Actual - People - Line Chart - Male Only"
msgstr ""

msgid "Target vs Actual - People - Line Chart - Gender New Only"
msgstr ""

msgid "Target vs Actual - People - Line Chart - New Only"
msgstr ""

msgid "Target vs Actual - People - Line Chart - Female Only"
msgstr ""

msgid "Target vs Actual - People - Line/Column Chart - Male Only"
msgstr ""

msgid "Target vs Actual - People - Line/Column Chart - Female Only"
msgstr ""

msgid "Target vs Actual - Benefits (Disaggregated)"
msgstr ""

msgid "Target vs Actual - People"
msgstr ""

msgid "Target vs Actual - People - Pivot Table"
msgstr ""

msgid "Target vs Actual - People - Month by Month"
msgstr ""

msgid "Target vs Actual - Benefits - Month by Month"
msgstr ""

msgid "Target vs Actual - People - Male Only - Pivot Table"
msgstr ""

msgid "Target vs Actual - People - Female Only - Pivot Table"
msgstr ""

msgid "Target vs Actual - People - Male Only - Column Chart"
msgstr ""

msgid "Target vs Actual - People - Female Only - Column Chart"
msgstr ""

msgid "Target vs Actual - Unique People"
msgstr ""

Expand All @@ -609,7 +712,7 @@ msgstr ""
msgid "Achieved (%) - Benefits"
msgstr ""

msgid "Achieved (%) - People"
msgid "Achieved to date (%) - People"
msgstr ""

msgid "Achieved total to date (%) - People"
Expand Down
77 changes: 77 additions & 0 deletions src/components/dashboard-info-modal/DashboardInfoModal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import React from "react";
import Button from "@material-ui/core/Button";
import Dialog from "@material-ui/core/Dialog";
import DialogActions from "@material-ui/core/DialogActions";
import DialogContent from "@material-ui/core/DialogContent";
import DialogTitle from "@material-ui/core/DialogTitle";

import i18n from "../../locales";
import { makeStyles } from "@material-ui/styles";

type DashboardInfoModalProps = {
isOpen: boolean;
onClose: () => void;
};

export const DashboardInfoModal: React.FC<DashboardInfoModalProps> = props => {
const { isOpen, onClose } = props;
const classes = useStyles();

return (
<Dialog open={isOpen} onClose={onClose} fullWidth maxWidth="md">
<DialogTitle>{i18n.t("Purpose of Platform Dashboards")}</DialogTitle>
<DialogContent className={classes.text} dividers>
<p>{i18n.t("These dashboards are meant to:")}</p>
<ul>
<li>{i18n.t("Encourage data informed decisions.")}</li>
<li>
{i18n.t(
"Help implementers identify phenomena in data that can initiate opportunities for conversations and decision-making on how to engage in adaptive program management."
)}
</li>
<li>
{i18n.t(
"Be incorporated into the monthly quality process. Ideally, this check should be happening when preparing MERs for your regional teams."
)}
</li>
<li>{i18n.t("Create a task list with some common follow on actions:")}</li>
<ul>
<li>{i18n.t("Conversions by topic")}</li>
<li>{i18n.t("Errors identified")}</li>
<li>{i18n.t("Divergence from targets")}</li>
<li>
{i18n.t(
"Bullets on what to highlight in the MER with mitigation measures"
)}
</li>
<li>{i18n.t("Potential points to escalate")}</li>
<li>{i18n.t("Determining if site visits should be made")}</li>
<li>{i18n.t("Reviewing budget for spent, pending, and remaining")}</li>
</ul>
</ul>
<p className={classes.bold}>
{i18n.t(
"If there is anything that can be improved, please let us know by sending an email to "
)}
<a href="mailto:pmt@samaritan.org">pmt@samaritan.org</a>.
</p>
</DialogContent>
<DialogActions>
<Button onClick={onClose} color="primary">
{i18n.t("Close")}
</Button>
</DialogActions>
</Dialog>
);
};

const useStyles = makeStyles({
text: {
"& p, & li": {
fontSize: 16,
},
},
bold: {
fontWeight: "bold",
},
});
4 changes: 4 additions & 0 deletions src/components/dashboard/Dashboard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import i18n from "../../locales";
import PageHeader from "../../components/page-header/PageHeader";
import { useAppHistory } from "../../utils/use-app-history";
import { useAppContext } from "../../contexts/api-context";
import { DashboardInfoModal } from "../dashboard-info-modal/DashboardInfoModal";

function getTranslations(name: string) {
return {
Expand Down Expand Up @@ -39,6 +40,7 @@ const Dashboard: React.FC<DashboardProps> = props => {
const dashboardUrl = dashboardUrlBase + `/#/${id}`;
const translations = getTranslations(name);
const appHistory = useAppHistory(backUrl);
const [openModal, setOpenModal] = React.useState(true);

React.useEffect(() => {
const iframe = iframeRef.current;
Expand Down Expand Up @@ -74,6 +76,8 @@ const Dashboard: React.FC<DashboardProps> = props => {
</div>
)}

<DashboardInfoModal isOpen={openModal} onClose={() => setOpenModal(false)} />

<div style={isLoading ? styles.wrapperHidden : styles.wrapperVisible}>
<iframe
ref={iframeRef}
Expand Down
9 changes: 7 additions & 2 deletions src/models/Dashboard.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
import { Maybe } from "../types/utils";
import { getUid, getRefs } from "../utils/dhis2";
import { D2Sharing } from "./Sharing";
import { PeriodStrategy } from "./Period";

export const dimensions = {
period: { id: "pe" },
Expand All @@ -29,6 +30,8 @@ interface Item {

export interface VisualizationDefinition {
type: "chart" | "table";
chartType?: Exclude<D2Visualization["type"], "PIVOT_TABLE">;
periodStrategy?: PeriodStrategy;
key: string;
name: string;
items: Item[];
Expand Down Expand Up @@ -84,7 +87,7 @@ export function getD2Visualization(visualization: Visualization): MaybeD2Visuali

const d2Table: PartialPersistedModel<D2Visualization> = {
id: getUid(visualization.key, ""),
type: visualization.type === "table" ? "PIVOT_TABLE" : "COLUMN",
type: visualization.type === "table" ? "PIVOT_TABLE" : visualization.chartType || "COLUMN",
name: visualization.name,
numberType: "VALUE",
legendDisplayStyle: "FILL",
Expand Down Expand Up @@ -172,9 +175,11 @@ export function positionItems(items: DashboardItem[], options: PositionItemsOpti

return items.reduce<{ pos: Pos; outputItems: DashboardItem[] }>(
({ pos, outputItems }, item) => {
const isText = item.type === "TEXT";
const width = Math.min(item.width || defaultWidth, maxWidth);
const itemPos = pos.x + width > maxWidth ? { x: 0, y: pos.y + defaultHeight } : pos;
const newItem = { ...item, width, height: defaultHeight, ...itemPos };
const height = item.height ?? (isText ? 6 : defaultHeight);
const newItem = { ...item, width, height, ...itemPos };
const newPos = { x: itemPos.x + newItem.width, y: itemPos.y };
return { pos: newPos, outputItems: [...outputItems, newItem] };
},
Expand Down
32 changes: 32 additions & 0 deletions src/models/Period.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
import moment from "moment";
import _ from "lodash";
import { Maybe } from "../types/utils";
import { getMonthsRange } from "../utils/date";

export type Period = string;
export type PeriodStrategy = "monthly" | "yearly" | "monthly_interleaved_by_month";

export const monthPeriod = "YYYYMM";
export const yearPeriod = "YYYY";

export function getPeriodsFromRange(start: Maybe<Date>, end: Maybe<Date>): Period[] {
if (!start || !end) return [];
Expand All @@ -16,3 +19,32 @@ export function filterPeriods(periods: Period[], options: { toDate?: boolean } =
const now = moment();
return !options.toDate ? periods : periods.filter(period => moment(period, monthPeriod) <= now);
}

export function getVisualizationPeriods(
periods: Period[],
options: { toDate?: boolean; periodStrategy?: PeriodStrategy } = {}
): Period[] {
const { toDate, periodStrategy = "monthly" } = options;

const transformedPeriods =
periodStrategy === "yearly"
? _.uniq(periods.map(period => moment(period, monthPeriod).format(yearPeriod))).sort()
: periodStrategy === "monthly_interleaved_by_month"
? _(periods)
.groupBy(period => moment(period, monthPeriod).format("MM"))
.toPairs()
.sortBy(([month]) => month)
.flatMap(([_month, monthPeriods]) => _.sortBy(monthPeriods))
.value()
: periods;

if (!toDate) return transformedPeriods;

const now = moment();

return transformedPeriods.filter(period =>
periodStrategy === "yearly"
? moment(period, yearPeriod).year() <= now.year()
: moment(period, monthPeriod) <= now
);
}
3 changes: 1 addition & 2 deletions src/models/Project.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,13 @@ import _ from "lodash";
import { D2Api, Id, Ref } from "../types/d2-api";
// @ts-ignore
import { generateUid } from "d2/uid";
import { TableSorting } from "@eyeseetea/d2-ui-components";

import i18n from "../locales";
import DataElementsSet, { PeopleOrBenefit, DataElement, SelectionInfo } from "./dataElementsSet";
import ProjectDb from "./ProjectDb";
import { toISOString, getMonthsRange } from "../utils/date";
import ProjectDownload from "./ProjectDownload";
import ProjectList, { ProjectForList, FiltersForList } from "./ProjectsList";
import ProjectList, { ProjectForList, FiltersForList, TableSorting } from "./ProjectsList";
import ProjectDataSet from "./ProjectDataSet";
import ProjectDelete from "./ProjectDelete";
import {
Expand Down
Loading
Loading