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
5 changes: 4 additions & 1 deletion packages/esm-form-engine-app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,10 @@
"@carbon/react": "^1.83.0",
"@openmrs/esm-form-engine-lib": "next",
"lodash-es": "^4.17.21",
"react-error-boundary": "^4.0.13"
"lodash.findlast": "^4.6.0",
"react-error-boundary": "^4.0.13",
"tabbable": "^6.2.0",
"use-resize-observer": "^9.1.0"
},
"peerDependencies": {
"@openmrs/esm-framework": "8.x",
Expand Down
34 changes: 34 additions & 0 deletions packages/esm-patient-growth-chart-app/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# @openmrs/esm-patient-growth-chart-app

## Description
The Growth Chart is designed for the OpenMRS 3.x Patient Chart. Its primary purpose is to allow clinicians to visualize pediatric growth trends (Weight-for-Age, Height-for-Age) against standardized normative curves (WHO & CDC) to identify malnutrition or growth abnormalities early.

## Scope
**Phase 1**
- **Project Initialization**: Scaffolding the app and registering it within the O3 shell.
- **Data Integration**: Fetching Patient Demographics (DOB) and Clinical Observations (Weight, Height).
- **Logic Layer**: Integrating official WHO datasets and implementing Z-score calculation logic.
- **Visualization**: Developing both a Longitudinal Table View and an Interactive Graph View.
- **Quality Assurance**: Implementing E2E testing for the core flows.

## Implementation Plan
- **Architecture**: Built as a micro-frontend within the openmrs-esm-patient-chart monorepo.
- **Tech Stack**: React, TypeScript, Carbon Design System, and Carbon Charts.
- **Data Strategy**: Normative data (WHO/CDC) will be converted from CSV to optimized JSON files stored within the app.

## Current Status
- **Project Initialization**
1.Created the openmrs-esm-growth-chart package structure.
2.Configured the module entry point (index.ts).
3.Registered the "Growth Chart" link in the Patient Chart left navigation bar.
4.Confirmed the skeleton component loads successfully.

- **Data Integration**
1.Patient's Height and Weight observations are fetched from the OpenMRS API.
2.Displayed in a table format.

- **WHO Dataset**
1.Integrated the data set for girls and boys of weight to age who standards. (Birth-5 years).
2.Converted original WHO .xlsx data to JSON format.
3.JSON includes L, M, S parameters for Z-score calculation and P-values for graphing.
4.Link to the WHO data - https://www.who.int/tools/child-growth-standards/standards/weight-for-age
3 changes: 3 additions & 0 deletions packages/esm-patient-growth-chart-app/jest.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
const rootConfig = require('../../jest.config.js');

module.exports = rootConfig;
57 changes: 57 additions & 0 deletions packages/esm-patient-growth-chart-app/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
{
"name": "@openmrs/esm-patient-growth-chart-app",
"version": "1.0.0",
"license": "MPL-2.0",
"description": "patient-growth-chart-app frontend module for O3",
"browser": "dist/openmrs-esm-patient-growth-chart-app.js",
"main": "src/index.ts",
"source": true,
"scripts": {
"start": "openmrs develop",
"serve": "webpack serve --mode=development",
"debug": "npm run serve",
"build": "webpack --mode production --color",
"analyze": "webpack --mode=production --env analyze=true",
"lint": "cross-env eslint src --ext tsx,ts --fix --max-warnings=0",
"test": "cross-env TZ=UTC jest --config jest.config.js --verbose false --passWithNoTests --color",
"test:watch": "cross-env TZ=UTC jest --watch --config jest.config.js --color",
"coverage": "yarn test --coverage",
"typescript": "tsc",
"extract-translations": "i18next 'src/**/*.component.tsx' 'src/**/*.modal.tsx' 'src/**/*.extension.tsx' 'src/**/*.workspace.tsx' 'src/**/*.hook.tsx' 'src/index.ts' --config ../../tools/i18next-parser.config.js"
},
"browserslist": [
"extends browserslist-config-openmrs"
],
"keywords": [
"openmrs"
],
"homepage": "https://github.com/openmrs/openmrs-esm-patient-chart#readme",
"publishConfig": {
"access": "public"
},
"repository": {
"type": "git",
"url": "git+https://github.com/openmrs/openmrs-esm-patient-chart.git"
},
"bugs": {
"url": "https://github.com/openmrs/openmrs-esm-patient-chart/issues"
},
"dependencies": {
"@carbon/react": "^1.83.0"
},
"peerDependencies": {
"@openmrs/esm-framework": "8.x",
"@openmrs/esm-patient-common-lib": "11.x",
"dayjs": "1.x",
"react": "18.x",
"react-i18next": "16.x",
"react-router-dom": "6.x",
"rxjs": "6.x",
"swr": "2.x"
},
"devDependencies": {
"@openmrs/esm-patient-common-lib": "workspace:*",
"webpack": "^5.99.9"
},
"packageManager": "[email protected]"
}
19 changes: 19 additions & 0 deletions packages/esm-patient-growth-chart-app/src/config-schema.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { Type, validators } from '@openmrs/esm-framework';

export const configSchema = {
concepts: {
_type: Type.Object,
_description: 'Concepts used in the Growth Chart app',
_default: {
heightUuid: '5090AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA',
weightUuid: '5089AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA',
},
},
};

export type ConfigObject = {
concepts: {
heightUuid: string;
weightUuid: string;
};
};
1 change: 1 addition & 0 deletions packages/esm-patient-growth-chart-app/src/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const moduleName = '@openmrs/esm-patient-growth-chart-app';
8 changes: 8 additions & 0 deletions packages/esm-patient-growth-chart-app/src/dashboard.meta.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { type DashboardLinkConfig } from '@openmrs/esm-patient-common-lib';

export const dashboardMeta: DashboardLinkConfig & { slot: string } = {
slot: 'patient-chart-growth-dashboard-slot',
path: 'Growth Chart',
title: 'Growth Chart',
icon: 'omrs-icon-activity',
};
9 changes: 9 additions & 0 deletions packages/esm-patient-growth-chart-app/src/declarations.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
declare module '*.scss' {
const content: { [className: string]: string };
export default content;
}

declare module '*.css' {
const content: { [className: string]: string };
export default content;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import React from 'react';
import { useTranslation } from 'react-i18next';
import { Layer, Tile } from '@carbon/react';
import styles from './growth-chart-main.scss';
import GrowthChart from './growth-chart.component';

interface PatientGrowthChartAppProps {
patientUuid: string;
}

const PatientGrowthChartApp: React.FC<PatientGrowthChartAppProps> = ({ patientUuid }) => {
const { t } = useTranslation();

return (
<div className={styles.container}>
<GrowthChart patientUuid={patientUuid} />
</div>
);
};

export default PatientGrowthChartApp;
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
@use '@carbon/layout';
@use '@carbon/type';

.container {
padding: layout.$spacing-05;
}

.tile {
padding: layout.$spacing-06;
margin-bottom: layout.$spacing-05;
}

.heading {
@include type.type-style('heading-03');
margin-bottom: layout.$spacing-04;
color: var(--cds-text-primary);
}

.content {
@include type.type-style('body-01');
color: var(--cds-text-secondary);
line-height: 1.5;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
import React, { useMemo } from 'react';
import { useTranslation } from 'react-i18next';
import { useGrowthChartData } from './growth-chart.resource';
import {
DataTable,
Table,
TableHead,
TableRow,
TableHeader,
TableBody,
TableCell,
TableContainer,
DataTableSkeleton,
Tile,
} from '@carbon/react';
import { CardHeader, PatientChartPagination } from '@openmrs/esm-patient-common-lib';
import { transformGrowthChartData, type TableRowData } from './growth-chart.utils';
import { usePagination } from '@openmrs/esm-framework';
import styles from './growth-chart-main.scss';

interface GrowthChartProps {
patientUuid: string;
}

const GrowthChart: React.FC<GrowthChartProps> = ({ patientUuid }) => {
const { t } = useTranslation();
const { data, isLoading, isError } = useGrowthChartData(patientUuid);

const tableHeaders = [
{ key: 'date', header: t('date', 'Date') },
{ key: 'height', header: t('height', 'Height') },
{ key: 'weight', header: t('weight', 'Weight') },
];

const tableRows: TableRowData[] = useMemo(() => {
if (!data) return [];

const { heights, weights } = data;
return transformGrowthChartData(heights, weights);
}, [data]);

const pageSizes = [10, 20, 30, 40, 50];
const [pageSize, setPageSize] = React.useState(10);
const { results: paginatedRows, goTo, currentPage } = usePagination(tableRows, pageSize);

if (isLoading) {
return <DataTableSkeleton />;
}

if (isError || !data?.patient) {
return <Tile>{t('errorLoadingData', 'Error loading growth chart data')}</Tile>;
}

return (
<div className={styles.container}>
<CardHeader title={t('growthChart', 'Growth Chart')}>
<></>
</CardHeader>
<div style={{ marginTop: '1rem' }}>
<DataTable rows={paginatedRows} headers={tableHeaders} isSortable>
{({ rows, headers, getHeaderProps, getTableProps }) => (
<TableContainer title={t('growthData', 'Growth Data')}>
<Table {...getTableProps()}>
<TableHead>
<TableRow>
{headers.map((header) => (
<TableHeader {...getHeaderProps({ header })}>{header.header}</TableHeader>
))}
</TableRow>
</TableHead>
<TableBody>
{rows.map((row) => (
<TableRow key={row.id}>
{row.cells.map((cell) => (
<TableCell key={cell.id}>{cell.value}</TableCell>
))}
</TableRow>
))}
</TableBody>
</Table>
</TableContainer>
)}
</DataTable>
<PatientChartPagination
currentItems={paginatedRows.length}
pageNumber={currentPage}
pageSize={pageSize}
totalItems={tableRows.length}
onPageNumberChange={({ page, pageSize }) => {
goTo(page);
setPageSize(pageSize);
}}
/>
</div>
</div>
);
};

export default GrowthChart;
Loading