@ampath/esm-reports-app 1.0.0-next.7 → 1.0.0-next.71
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/197.js +1 -1
- package/dist/282.js +1 -1
- package/dist/282.js.map +1 -1
- package/dist/295.css +1 -0
- package/dist/295.css.map +1 -0
- package/dist/295.js +1 -0
- package/dist/295.js.map +1 -0
- package/dist/299.js +2 -0
- package/dist/299.js.LICENSE.txt +3 -0
- package/dist/299.js.map +1 -0
- package/dist/300.js +1 -1
- package/dist/311.js +1 -1
- package/dist/311.js.map +1 -1
- package/dist/335.js +1 -1
- package/dist/353.js +1 -1
- package/dist/353.js.map +1 -1
- package/dist/354.js +2 -0
- package/dist/354.js.LICENSE.txt +20 -0
- package/dist/354.js.map +1 -0
- package/dist/410.js +1 -0
- package/dist/410.js.map +1 -0
- package/dist/457.js +2 -0
- package/dist/457.js.LICENSE.txt +261 -0
- package/dist/457.js.map +1 -0
- package/dist/478.js +1 -1
- package/dist/478.js.map +1 -1
- package/dist/480.js +1 -0
- package/dist/540.js +1 -1
- package/dist/540.js.map +1 -1
- package/dist/55.js +1 -1
- package/dist/580.js +1 -0
- package/dist/580.js.map +1 -0
- package/dist/600.js +2 -0
- package/dist/600.js.LICENSE.txt +35 -0
- package/dist/600.js.map +1 -0
- package/dist/652.js +1 -1
- package/dist/781.js +1 -0
- package/dist/781.js.map +1 -0
- package/dist/919.css +1 -0
- package/dist/919.css.map +1 -0
- package/dist/919.js +1 -0
- package/dist/919.js.map +1 -0
- package/dist/961.js +1 -1
- package/dist/961.js.map +1 -1
- package/dist/99.js +1 -1
- package/dist/99.js.map +1 -0
- package/dist/ampath-esm-reports-app.js +1 -1
- package/dist/ampath-esm-reports-app.js.map +1 -1
- package/dist/routes.json +1 -1
- package/package.json +4 -2
- package/src/common/daily-summary-table.tsx +55 -0
- package/src/common/linelist-table/index.ts +3 -0
- package/src/common/linelist-table/linelist-columns.ts +48 -0
- package/src/common/linelist-table/linelist-table.component.tsx +442 -0
- package/src/common/linelist-table/linelist-table.scss +306 -0
- package/src/common/linelist-table/linelist-table.test.tsx +120 -0
- package/src/common/linelist-table/use-all-rows.ts +93 -0
- package/src/common/linelist-table/use-column-layout.ts +123 -0
- package/src/common/report-error/index.ts +1 -0
- package/src/common/report-error/show-report-error.ts +25 -0
- package/src/common/report-filters/date-range.ts +137 -0
- package/src/common/report-filters/index.ts +4 -0
- package/src/common/report-filters/report-filters.component.tsx +317 -99
- package/src/common/report-filters/report-filters.scss +255 -32
- package/src/common/report-filters/types.ts +79 -0
- package/src/common/report-filters/use-restore-period.ts +43 -0
- package/src/common/report-header/index.ts +1 -0
- package/src/common/report-header/use-location-details.ts +58 -0
- package/src/common/report-page/download-sheet-pdf.ts +59 -0
- package/src/common/report-page/index.ts +3 -0
- package/src/common/report-page/report-page.component.tsx +32 -0
- package/src/common/report-page/report-page.scss +45 -0
- package/src/common/report-page/use-page-scale.ts +60 -0
- package/src/common/report-placeholder/has-report-data.ts +15 -0
- package/src/common/report-placeholder/index.ts +3 -0
- package/src/common/report-placeholder/report-placeholder.component.tsx +81 -0
- package/src/common/report-placeholder/report-placeholder.scss +48 -0
- package/src/common/report-placeholder/use-generated-location.ts +62 -0
- package/src/common/report-register/index.ts +6 -0
- package/src/common/report-register/register-columns.ts +46 -0
- package/src/common/report-register/register-layout.component.tsx +265 -0
- package/src/common/report-register/register-layout.scss +150 -0
- package/src/common/report-register/register-table.scss +71 -0
- package/src/common/report-register/report-parents.ts +27 -0
- package/src/common/report-register/rows-to-csv.ts +89 -0
- package/src/common/report-register/use-location-name.ts +54 -0
- package/src/common/report-register/use-patient-list.ts +92 -0
- package/src/common/report-skeleton/index.ts +1 -0
- package/src/common/report-skeleton/report-skeleton.component.tsx +46 -0
- package/src/common/report-skeleton/report-skeleton.scss +29 -0
- package/src/config-schema.ts +9 -1
- package/src/dashboard/report-catalog.ts +173 -0
- package/src/dashboard/reports-dasboard.tsx +189 -18
- package/src/dashboard/reports-dashboard.module.scss +357 -14
- package/src/index.ts +13 -1
- package/src/registers/moh-240/moh-240-register.scss +41 -0
- package/src/registers/moh-240/moh-240.component.test.tsx +166 -0
- package/src/registers/moh-240/moh-240.component.tsx +164 -0
- package/src/registers/moh-362/moh-362-lab-register.component.tsx +392 -0
- package/src/registers/moh-362/moh-362-linkage-register.component.tsx +120 -0
- package/src/registers/moh-362/moh-362-register.scss +222 -0
- package/src/registers/moh-362/moh-362.component.tsx +99 -0
- package/src/registers/moh-362/moh-362.scss +11 -0
- package/src/registers/moh-362/moh-362.types.ts +97 -0
- package/src/registers/register-catalog.ts +96 -0
- package/src/registers/tb-lab-register/tb-lab-register-table.component.tsx +116 -0
- package/src/registers/tb-lab-register/tb-lab-register-table.scss +123 -0
- package/src/registers/tb-lab-register/tb-lab-register.columns.ts +164 -0
- package/src/registers/tb-lab-register/tb-lab-register.component.test.tsx +232 -0
- package/src/registers/tb-lab-register/tb-lab-register.component.tsx +153 -0
- package/src/reports/datatable-wrapper/datatable-wrapper.component.tsx +63 -48
- package/src/reports/moh-240/moh-240-header.component.tsx +62 -0
- package/src/reports/moh-240/moh-240.columns.ts +113 -0
- package/src/reports/moh-240/moh-240.component.tsx +72 -9
- package/src/reports/moh-240/moh-240.scss +40 -0
- package/src/reports/moh-240/sub-reports/moh-240-register.component.tsx +31 -37
- package/src/reports/moh-240/sub-reports/page-summary.component.tsx +143 -106
- package/src/reports/moh-505/moh-505-header.component.tsx +67 -0
- package/src/reports/moh-505/moh-505.component.tsx +441 -0
- package/src/reports/moh-505/moh-505.scss +117 -0
- package/src/reports/moh-705B/moh-204b-register.component.tsx +244 -0
- package/src/reports/moh-705B/moh-204b.columns.ts +39 -0
- package/src/reports/moh-705B/moh-705b.component.tsx +659 -0
- package/src/reports/moh-705B/moh-705b.scss +172 -0
- package/src/reports/moh-705a/moh-705a.component.tsx +661 -0
- package/src/reports/moh-705a/moh-705a.scss +172 -0
- package/src/reports/moh-705a/registers/moh-204a-register.component.tsx +329 -0
- package/src/reports/moh-705a/registers/moh-204a.columns.ts +57 -0
- package/src/reports/moh-705a/type.ts +6 -0
- package/src/reports/moh-706/moh-706-header.component.tsx +66 -0
- package/src/reports/moh-706/moh-706.component.tsx +150 -24
- package/src/reports/moh-706/moh-706.scss +119 -11
- package/src/reports/moh-706/sub-reports/bacteriology/bacteriology.component.tsx +54 -62
- package/src/reports/moh-706/sub-reports/blood-chemistry/blood-chemistry.component.tsx +228 -317
- package/src/reports/moh-706/sub-reports/drug-susceptibility-testing/drug-susceptibility-testing.component.tsx +31 -46
- package/src/reports/moh-706/sub-reports/haematology/haematology.component.tsx +47 -55
- package/src/reports/moh-706/sub-reports/histology-and-cytology/histology-and-cytology.component.tsx +43 -58
- package/src/reports/moh-706/sub-reports/parasitology/parasitology.component.tsx +31 -39
- package/src/reports/moh-706/sub-reports/serology/serology.component.tsx +26 -34
- package/src/reports/moh-706/sub-reports/specimen-referral-to-higher-levels/specimen-referral-to-higher-levels.component.tsx +9 -24
- package/src/reports/moh-706/sub-reports/urine-analysis/urine-analysis.component.tsx +43 -51
- package/src/reports/moh-710/moh-710.component.tsx +799 -632
- package/src/reports/moh-710/moh-710.scss +142 -21
- package/src/reports/moh-710/registers/moh-510/moh-510.columns.ts +40 -0
- package/src/reports/moh-710/registers/moh-510/moh-510.component.tsx +144 -0
- package/src/reports/moh-710/registers/moh-510/moh-510.scss +130 -0
- package/src/reports/moh-710/registers/moh-511-710-register.component.tsx +268 -0
- package/src/reports/moh-710/registers/moh-511-710.columns.ts +52 -0
- package/src/reports/moh-710/registers/type.ts +31 -0
- package/src/reports/moh-711/moh-711.component.tsx +138 -1269
- package/src/reports/moh-711/moh711.scss +135 -20
- package/src/reports/moh-711/registers/moh-333-register.component.tsx +637 -0
- package/src/reports/moh-711/registers/moh-333.columns.ts +119 -0
- package/src/reports/moh-711/registers/moh-405-register.component.tsx +520 -0
- package/src/reports/moh-711/registers/moh-405.columns.ts +120 -0
- package/src/reports/moh-711/registers/moh-406-register.component.tsx +530 -0
- package/src/reports/moh-711/registers/moh-406.columns.ts +133 -0
- package/src/reports/moh-711/registers/moh-510-register.component.tsx +55 -0
- package/src/reports/moh-711/registers/moh-511-register.component.tsx +270 -0
- package/src/reports/moh-711/registers/moh-511.columns.ts +59 -0
- package/src/reports/moh-711/registers/type.ts +192 -0
- package/src/reports/moh-711/sections/anc.component.tsx +159 -0
- package/src/reports/moh-711/sections/cervical-cancer.component.tsx +148 -0
- package/src/reports/moh-711/sections/chanis.component.tsx +366 -0
- package/src/reports/moh-711/sections/family-planning.component.tsx +221 -0
- package/src/reports/moh-711/sections/gbv.component.tsx +115 -0
- package/src/reports/moh-711/sections/maternity.component.tsx +321 -0
- package/src/reports/moh-711/sections/medical-social-work.component.tsx +73 -0
- package/src/reports/moh-711/sections/other.component.tsx +47 -0
- package/src/reports/moh-711/sections/physiotherapy.component.tsx +61 -0
- package/src/reports/moh-711/sections/pnc.component.tsx +127 -0
- package/src/reports/moh-711/sections/post-abortion.component.tsx +42 -0
- package/src/reports/moh-711/sections/rehabilitation.component.tsx +57 -0
- package/src/reports/moh-711/sections/report-compiled-by.component.tsx +50 -0
- package/src/reports/moh-711/sections/tb-screening.component.tsx +57 -0
- package/src/reports/moh-717/moh-717.component.tsx +149 -985
- package/src/reports/moh-717/moh717.scss +217 -21
- package/src/reports/moh-717/registers/nutrition-register.component.tsx +48 -0
- package/src/reports/moh-717/sections/finance.component.tsx +42 -0
- package/src/reports/moh-717/sections/inpatient.component.tsx +374 -0
- package/src/reports/moh-717/sections/maternity.component.tsx +126 -0
- package/src/reports/moh-717/sections/medical-records.component.tsx +35 -0
- package/src/reports/moh-717/sections/mortuary.component.tsx +43 -0
- package/src/reports/moh-717/sections/operations.component.tsx +53 -0
- package/src/reports/moh-717/sections/orthopaedic-trauma.component.tsx +98 -0
- package/src/reports/moh-717/sections/outpatient.component.tsx +597 -0
- package/src/reports/moh-717/sections/pharmacy.component.tsx +43 -0
- package/src/reports/moh-717/sections/preparedby.component.tsx +52 -0
- package/src/reports/moh-717/sections/special-services.component.tsx +114 -0
- package/src/reports/moh-731/linelist/moh-731-indicators.ts +1122 -0
- package/src/reports/moh-731/linelist/moh-731-linelist-sections.ts +472 -0
- package/src/reports/moh-731/linelist/moh-731-linelist.component.tsx +102 -0
- package/src/reports/moh-731/linelist/use-open-moh-731-linelist.ts +50 -0
- package/src/reports/moh-731/moh-731.component.tsx +266 -0
- package/src/reports/moh-731/moh-731.scss +499 -0
- package/src/reports/moh-731/registers/moh-366.columns.ts +38 -0
- package/src/reports/moh-731/registers/moh-366.component.tsx +471 -0
- package/src/reports/moh-731/registers/prep-register.component.tsx +284 -0
- package/src/reports/moh-731/registers/prep.columns.ts +34 -0
- package/src/reports/moh-731/sections/hiv-and-tb-treatment.component.tsx +1206 -0
- package/src/reports/moh-731/sections/hiv-testing-and-pre-exposure.component.tsx +727 -0
- package/src/reports/moh-731/sections/pmtct.component.tsx +572 -0
- package/src/reports/moh-731/summary/moh-731-summary.component.tsx +313 -0
- package/src/reports/moh-731/summary/moh-731-summary.config.ts +529 -0
- package/src/reports/moh-731/summary/moh-731-summary.scss +242 -0
- package/src/reports/moh-740/moh-740.component.scss +202 -0
- package/src/reports/moh-740/moh-740.component.tsx +1332 -0
- package/src/reports/moh-740/moh-740.resource.ts +54 -0
- package/src/reports/moh-740/registers/moh-222-daily-register/moh-222-daily-register.tsx +149 -0
- package/src/reports/moh-740/registers/moh-270-permanent-register/moh-270-permanent-register.tsx +123 -0
- package/src/reports/moh-740/registers/moh-740-patient-list.scss +0 -0
- package/src/reports/moh-740/registers/moh-740-patient-list.tsx +60 -0
- package/src/reports/moh-740/registers/moh-740.columns.ts +54 -0
- package/src/reports/moh-740/shared/data-cell/data-cell.scss +6 -0
- package/src/reports/moh-740/shared/data-cell/data-cell.tsx +23 -0
- package/src/reports/moh-740/shared/utils/indicator-register-map.ts +159 -0
- package/src/reports/moh-740/types/index.ts +223 -0
- package/src/reports/moh-745/moh-745.component.tsx +1021 -0
- package/src/reports/moh-745/moh-745.scss +132 -0
- package/src/reports/moh-745/registers/moh-412-register.component.tsx +249 -0
- package/src/reports/moh-745/registers/moh-412.columns.ts +52 -0
- package/src/reports/moh-745/registers/type.ts +181 -0
- package/src/reports/table-wrapper/table-row-mapper.component.tsx +57 -0
- package/src/reports/table-wrapper/table-wrapper.component.tsx +23 -14
- package/src/reports/table-wrapper/table-wrapper.scss +1 -16
- package/src/resources/moh-362.resource.ts +27 -0
- package/src/resources/moh-505.resource.ts +69 -0
- package/src/resources/moh-705.resource.ts +106 -0
- package/src/resources/moh-706.resource.ts +80 -0
- package/src/resources/moh-710.resource.ts +32 -9
- package/src/resources/moh-711.resource.ts +137 -9
- package/src/resources/moh-717.resource.ts +26 -9
- package/src/resources/moh-731.resource.ts +90 -0
- package/src/resources/moh-745.resource.ts +76 -0
- package/src/resources/patient-list-page.ts +65 -0
- package/src/root.component.tsx +59 -8
- package/src/root.scss +36 -0
- package/src/routes.json +3 -1
- package/src/utils/format-indicator.ts +39 -0
- package/src/utils/get-base-url.ts +39 -7
- package/src/utils/utils.ts +36 -0
- package/tsconfig.json +1 -1
- package/webpack.config.js +22 -1
- package/dist/138.js +0 -2
- package/dist/138.js.LICENSE.txt +0 -20
- package/dist/138.js.map +0 -1
- package/dist/184.js +0 -2
- package/dist/184.js.LICENSE.txt +0 -14
- package/dist/184.js.map +0 -1
- package/dist/205.js +0 -1
- package/dist/205.js.map +0 -1
- package/dist/711.js +0 -1
- package/dist/711.js.map +0 -1
- package/dist/812.js +0 -1
- package/dist/812.js.map +0 -1
- package/dist/ampath-esm-reports-app.js.buildmanifest.json +0 -539
- package/dist/main.js +0 -1
- package/dist/main.js.map +0 -1
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { render, screen, waitFor, within } from '@testing-library/react';
|
|
3
|
+
import userEvent from '@testing-library/user-event';
|
|
4
|
+
import { MemoryRouter } from 'react-router-dom';
|
|
5
|
+
|
|
6
|
+
import Moh240LabRegister from './moh-240.component';
|
|
7
|
+
import { MOH_240_HEADERS, MOH_240_SUB_HEADERS } from '../../reports/moh-240/moh-240.columns';
|
|
8
|
+
import { downloadCsv } from '../../common/report-register';
|
|
9
|
+
import { getMoh706PatientList } from '../../resources/moh-706.resource';
|
|
10
|
+
|
|
11
|
+
jest.mock('../../common/report-register', () => ({
|
|
12
|
+
...jest.requireActual('../../common/report-register'),
|
|
13
|
+
downloadCsv: jest.fn(),
|
|
14
|
+
}));
|
|
15
|
+
|
|
16
|
+
jest.mock('../../resources/moh-706.resource', () => ({
|
|
17
|
+
getMoh706PatientList: jest.fn(),
|
|
18
|
+
}));
|
|
19
|
+
|
|
20
|
+
jest.mock('@openmrs/esm-framework', () => ({
|
|
21
|
+
useSession: () => ({ sessionLocation: { uuid: 'loc-uuid', display: 'Test Facility' } }),
|
|
22
|
+
openmrsFetch: jest.fn().mockResolvedValue({ ok: false }),
|
|
23
|
+
showSnackbar: jest.fn(),
|
|
24
|
+
}));
|
|
25
|
+
|
|
26
|
+
/* The filter bar is Carbon-heavy and its period picking is not what this page
|
|
27
|
+
is about, so it is stood in by a button that generates a fixed period. The
|
|
28
|
+
restore-from-URL hook stays real, as it is part of the page's behaviour. */
|
|
29
|
+
jest.mock('../../common/report-filters', () => {
|
|
30
|
+
const actual = jest.requireActual('../../common/report-filters');
|
|
31
|
+
return {
|
|
32
|
+
...actual,
|
|
33
|
+
ReportFiltersComponent: ({
|
|
34
|
+
onGenerate,
|
|
35
|
+
}: {
|
|
36
|
+
onGenerate: (period: { startDate: string; endDate: string; label?: string }) => void;
|
|
37
|
+
}) => (
|
|
38
|
+
<button onClick={() => onGenerate({ startDate: '2026-10-01', endDate: '2026-10-02', label: 'period' })}>
|
|
39
|
+
generate
|
|
40
|
+
</button>
|
|
41
|
+
),
|
|
42
|
+
};
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
/** A row carrying every register field, so a missing column shows as a blank cell */
|
|
46
|
+
const aRow = (overrides: Record<string, unknown> = {}) => {
|
|
47
|
+
const row: Record<string, any> = {};
|
|
48
|
+
MOH_240_HEADERS.forEach(({ key }, index) => {
|
|
49
|
+
row[key] = `${key}-${index}`;
|
|
50
|
+
});
|
|
51
|
+
return { ...row, ...overrides };
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
const renderRegister = () =>
|
|
55
|
+
render(
|
|
56
|
+
<MemoryRouter>
|
|
57
|
+
<Moh240LabRegister />
|
|
58
|
+
</MemoryRouter>,
|
|
59
|
+
);
|
|
60
|
+
|
|
61
|
+
/** The register table itself: the page also holds the masthead and page
|
|
62
|
+
summary tables, so the queries are scoped to it rather than to the page */
|
|
63
|
+
const registerTable = (container: HTMLElement) => {
|
|
64
|
+
const table = container.querySelector('table.table');
|
|
65
|
+
expect(table).not.toBeNull();
|
|
66
|
+
return table as HTMLTableElement;
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
const page = (results: Array<Record<string, unknown>>, total = results.length) => ({
|
|
70
|
+
results,
|
|
71
|
+
total,
|
|
72
|
+
isTotalExact: true,
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
describe('Moh240LabRegister', () => {
|
|
76
|
+
beforeEach(() => {
|
|
77
|
+
jest.mocked(downloadCsv).mockClear();
|
|
78
|
+
jest.mocked(getMoh706PatientList).mockReset();
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
it('asks for the register to be generated before showing anything', () => {
|
|
82
|
+
renderRegister();
|
|
83
|
+
|
|
84
|
+
expect(screen.getByText('No report generated yet')).toBeInTheDocument();
|
|
85
|
+
expect(getMoh706PatientList).not.toHaveBeenCalled();
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
it('renders the printed register under its own columns (A-AA) once generated', async () => {
|
|
89
|
+
jest
|
|
90
|
+
.mocked(getMoh706PatientList)
|
|
91
|
+
.mockResolvedValue(page([aRow({ date: '01/10/2026', full_name: 'Jane Akinyi' }), aRow()]));
|
|
92
|
+
|
|
93
|
+
const { container } = renderRegister();
|
|
94
|
+
await userEvent.click(screen.getByRole('button', { name: 'generate' }));
|
|
95
|
+
|
|
96
|
+
await waitFor(() => expect(screen.getByText('2 records')).toBeInTheDocument());
|
|
97
|
+
|
|
98
|
+
/* the whole register asked for, not a drill-down's indicator subset */
|
|
99
|
+
expect(getMoh706PatientList).toHaveBeenCalledWith(
|
|
100
|
+
expect.objectContaining({ locationUuids: 'loc-uuid', startDate: '2026-10-01', endDate: '2026-10-02' }),
|
|
101
|
+
);
|
|
102
|
+
|
|
103
|
+
const table = within(registerTable(container));
|
|
104
|
+
const headerRows = within(table.getAllByRole('rowgroup')[0]).getAllByRole('row');
|
|
105
|
+
expect(
|
|
106
|
+
headerRows.map((row) =>
|
|
107
|
+
within(row)
|
|
108
|
+
.getAllByRole('columnheader')
|
|
109
|
+
.map((cell) => cell.textContent),
|
|
110
|
+
),
|
|
111
|
+
).toEqual([MOH_240_HEADERS.map(({ header }) => header), [...MOH_240_SUB_HEADERS]]);
|
|
112
|
+
|
|
113
|
+
const firstRow = within(table.getAllByRole('rowgroup')[1]).getAllByRole('row')[0];
|
|
114
|
+
const cells = within(firstRow)
|
|
115
|
+
.getAllByRole('cell')
|
|
116
|
+
.map((cell) => cell.textContent);
|
|
117
|
+
expect(cells[0]).toBe('01/10/2026');
|
|
118
|
+
expect(cells[4]).toBe('Jane Akinyi');
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
it('exports every page as the form\u2019s columns with the letter row beneath', async () => {
|
|
122
|
+
jest
|
|
123
|
+
.mocked(getMoh706PatientList)
|
|
124
|
+
.mockImplementation(({ startIndex }: any) =>
|
|
125
|
+
Promise.resolve(page([aRow({ lab_no: '001234' })], startIndex === 0 ? 2 : 2)),
|
|
126
|
+
);
|
|
127
|
+
|
|
128
|
+
renderRegister();
|
|
129
|
+
await userEvent.click(screen.getByRole('button', { name: 'generate' }));
|
|
130
|
+
await waitFor(() => expect(screen.getByRole('button', { name: /Export CSV/ })).toBeInTheDocument());
|
|
131
|
+
|
|
132
|
+
await userEvent.click(screen.getByRole('button', { name: /Export CSV/ }));
|
|
133
|
+
|
|
134
|
+
await waitFor(() => expect(jest.mocked(downloadCsv)).toHaveBeenCalledTimes(1));
|
|
135
|
+
const [csv, filename] = jest.mocked(downloadCsv).mock.calls[0];
|
|
136
|
+
const lines = (csv as string).split('\r\n');
|
|
137
|
+
expect(lines[0]).toBe(MOH_240_HEADERS.map(({ header }) => header).join(','));
|
|
138
|
+
expect(lines[1]).toBe(MOH_240_SUB_HEADERS.join(','));
|
|
139
|
+
/* identifier columns survive Excel as text */
|
|
140
|
+
expect(lines[2]).toContain('="001234"');
|
|
141
|
+
expect(filename).toBe('laboratory-lab-register-moh-240-2026-10-01-2026-10-02.csv');
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
it('pagers past the first page when the period holds more rows than one page', async () => {
|
|
145
|
+
jest.mocked(getMoh706PatientList).mockResolvedValue(
|
|
146
|
+
page(
|
|
147
|
+
Array.from({ length: 50 }, () => aRow()),
|
|
148
|
+
120,
|
|
149
|
+
),
|
|
150
|
+
);
|
|
151
|
+
|
|
152
|
+
renderRegister();
|
|
153
|
+
await userEvent.click(screen.getByRole('button', { name: 'generate' }));
|
|
154
|
+
|
|
155
|
+
await waitFor(() => expect(screen.getByText(/120 items/)).toBeInTheDocument());
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
it('falls back to the placeholder when the register cannot be fetched', async () => {
|
|
159
|
+
jest.mocked(getMoh706PatientList).mockRejectedValue(new Error('etl down'));
|
|
160
|
+
|
|
161
|
+
renderRegister();
|
|
162
|
+
await userEvent.click(screen.getByRole('button', { name: 'generate' }));
|
|
163
|
+
|
|
164
|
+
await waitFor(() => expect(screen.getByText('No report generated yet')).toBeInTheDocument());
|
|
165
|
+
});
|
|
166
|
+
});
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import React, { useState } from 'react';
|
|
2
|
+
import { Button, Layer, Pagination } from '@carbon/react';
|
|
3
|
+
import { Document, Download } from '@carbon/react/icons';
|
|
4
|
+
import { useSession } from '@openmrs/esm-framework';
|
|
5
|
+
|
|
6
|
+
import { ReportFiltersComponent, type ReportPeriod, useRestorePeriod } from '../../common/report-filters';
|
|
7
|
+
import { showReportError } from '../../common/report-error';
|
|
8
|
+
import { ReportPlaceholder, useGeneratedLocation } from '../../common/report-placeholder';
|
|
9
|
+
import { ReportSkeleton } from '../../common/report-skeleton';
|
|
10
|
+
import { columnsToCsv, downloadCsv, usePatientList } from '../../common/report-register';
|
|
11
|
+
import { getMoh706PatientList } from '../../resources/moh-706.resource';
|
|
12
|
+
import { MOH_240_SUB_HEADERS, moh240Columns } from '../../reports/moh-240/moh-240.columns';
|
|
13
|
+
import MOH240Header from '../../reports/moh-240/moh-240-header.component';
|
|
14
|
+
import sheetStyles from '../../reports/moh-240/moh-240.scss';
|
|
15
|
+
import Moh240PageSummary from '../../reports/moh-240/sub-reports/page-summary.component';
|
|
16
|
+
import Moh240Register from '../../reports/moh-240/sub-reports/moh-240-register.component';
|
|
17
|
+
import styles from './moh-240-register.scss';
|
|
18
|
+
|
|
19
|
+
/** The register's own columns (A-AA). No drill-down indicator runs here, so the
|
|
20
|
+
results column reads the row's translated `results` field. */
|
|
21
|
+
const columns = moh240Columns('');
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Laboratory (LAB) Register MOH 240, opened from the landing page's Registers
|
|
25
|
+
* tab. It shares the reports' filter bar and serves the same printed register
|
|
26
|
+
* -- masthead, columns A-AA and the page summary -- that the MOH 505 and
|
|
27
|
+
* MOH 706 drill-downs open, but over the laboratory's whole workload for the
|
|
28
|
+
* period rather than one indicator's rows.
|
|
29
|
+
*/
|
|
30
|
+
const Moh240LabRegister: React.FC = () => {
|
|
31
|
+
const [period, setPeriod] = useState<{ startDate: string; endDate: string }>({ startDate: '', endDate: '' });
|
|
32
|
+
const [isExporting, setIsExporting] = useState(false);
|
|
33
|
+
/* A fetch that failed leaves the page on the placeholder rather than showing
|
|
34
|
+
an empty register, so the two are told apart here. */
|
|
35
|
+
const [hasError, setHasError] = useState(false);
|
|
36
|
+
|
|
37
|
+
const session = useSession();
|
|
38
|
+
const locationUuids = session?.sessionLocation?.uuid ?? '';
|
|
39
|
+
/* Only a register for the current location counts */
|
|
40
|
+
const { isForCurrentLocation, hasLocationChanged, markGenerated } = useGeneratedLocation('moh240RegisterLocation');
|
|
41
|
+
const isGenerated = Boolean(period.startDate && period.endDate) && isForCurrentLocation && !hasError;
|
|
42
|
+
|
|
43
|
+
const { rows, total, isTotalExact, isLoading, page, pageSize, onPageChange, fetchAll } = usePatientList(
|
|
44
|
+
({ startIndex, limit }) => {
|
|
45
|
+
/* Nothing is fetched before a period is chosen: the mount-time load and
|
|
46
|
+
an unset session land here rather than querying the endpoint's defaults. */
|
|
47
|
+
if (!period.startDate || !period.endDate) {
|
|
48
|
+
return Promise.resolve({ results: [], total: 0, isTotalExact: true });
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return getMoh706PatientList({
|
|
52
|
+
locationUuids,
|
|
53
|
+
startDate: period.startDate,
|
|
54
|
+
endDate: period.endDate,
|
|
55
|
+
startIndex,
|
|
56
|
+
limit,
|
|
57
|
+
}).then(
|
|
58
|
+
(result) => {
|
|
59
|
+
setHasError(false);
|
|
60
|
+
return result;
|
|
61
|
+
},
|
|
62
|
+
(error) => {
|
|
63
|
+
setHasError(true);
|
|
64
|
+
throw error;
|
|
65
|
+
},
|
|
66
|
+
);
|
|
67
|
+
},
|
|
68
|
+
[locationUuids, period.startDate, period.endDate],
|
|
69
|
+
);
|
|
70
|
+
|
|
71
|
+
const generate = ({ startDate, endDate }: ReportPeriod) => {
|
|
72
|
+
setHasError(false);
|
|
73
|
+
setPeriod({ startDate, endDate });
|
|
74
|
+
markGenerated(locationUuids);
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
useRestorePeriod(generate);
|
|
78
|
+
|
|
79
|
+
/* Exports every page, not the one on screen: with pagination the DOM only
|
|
80
|
+
holds one page, and the file must match the printed form column for column. */
|
|
81
|
+
const exportCsv = async () => {
|
|
82
|
+
setIsExporting(true);
|
|
83
|
+
|
|
84
|
+
try {
|
|
85
|
+
const all = await fetchAll();
|
|
86
|
+
const slug = ['laboratory-lab-register-moh-240', period.startDate, period.endDate]
|
|
87
|
+
.filter(Boolean)
|
|
88
|
+
.join('-')
|
|
89
|
+
.toLowerCase();
|
|
90
|
+
downloadCsv(columnsToCsv(all, columns, { subHeaderRow: MOH_240_SUB_HEADERS }), `${slug}.csv`);
|
|
91
|
+
} catch (error) {
|
|
92
|
+
showReportError('the register export', error);
|
|
93
|
+
} finally {
|
|
94
|
+
setIsExporting(false);
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
return (
|
|
99
|
+
<>
|
|
100
|
+
<ReportFiltersComponent
|
|
101
|
+
reportName="Laboratory (LAB) Register MOH 240"
|
|
102
|
+
mode="both"
|
|
103
|
+
onGenerate={generate}
|
|
104
|
+
isLoading={isLoading}
|
|
105
|
+
isReportGenerated={isGenerated}
|
|
106
|
+
generatedPeriod={period}
|
|
107
|
+
defaultTab="custom"
|
|
108
|
+
backLink={{ label: 'All registers', to: '/?tab=registers' }}
|
|
109
|
+
/>
|
|
110
|
+
{isGenerated && isLoading && rows.length === 0 && <ReportSkeleton />}
|
|
111
|
+
{isGenerated && !isLoading && (
|
|
112
|
+
<>
|
|
113
|
+
<div className={styles.toolbar}>
|
|
114
|
+
<span className={styles.total}>
|
|
115
|
+
{total.toLocaleString()}
|
|
116
|
+
{isTotalExact ? '' : '+'} records
|
|
117
|
+
</span>
|
|
118
|
+
<Button kind="tertiary" size="sm" renderIcon={Download} onClick={exportCsv} disabled={isExporting}>
|
|
119
|
+
{isExporting ? 'Preparing…' : 'Export CSV'}
|
|
120
|
+
</Button>
|
|
121
|
+
</div>
|
|
122
|
+
{rows.length > 0 ? (
|
|
123
|
+
<>
|
|
124
|
+
<div className={sheetStyles.sheet}>
|
|
125
|
+
<MOH240Header
|
|
126
|
+
facility={session?.sessionLocation?.display ?? ''}
|
|
127
|
+
startDate={period.startDate}
|
|
128
|
+
endDate={period.endDate}
|
|
129
|
+
/>
|
|
130
|
+
<Moh240Register patientList={rows} />
|
|
131
|
+
<Moh240PageSummary rows={rows} />
|
|
132
|
+
</div>
|
|
133
|
+
{typeof total === 'number' && total > pageSize && (
|
|
134
|
+
<Pagination
|
|
135
|
+
className={styles.pager}
|
|
136
|
+
page={page}
|
|
137
|
+
pageSize={pageSize}
|
|
138
|
+
pageSizes={[25, 50, 100, 200]}
|
|
139
|
+
totalItems={total}
|
|
140
|
+
size="sm"
|
|
141
|
+
onChange={onPageChange}
|
|
142
|
+
/>
|
|
143
|
+
)}
|
|
144
|
+
</>
|
|
145
|
+
) : (
|
|
146
|
+
<Layer className={styles.empty}>
|
|
147
|
+
<Document size={32} className={styles.emptyIcon} />
|
|
148
|
+
<h4>No records to list</h4>
|
|
149
|
+
<p>No laboratory entries matched the selected period and facility.</p>
|
|
150
|
+
</Layer>
|
|
151
|
+
)}
|
|
152
|
+
</>
|
|
153
|
+
)}
|
|
154
|
+
{!isGenerated && (
|
|
155
|
+
<ReportPlaceholder
|
|
156
|
+
reportName="MOH 240 laboratory register"
|
|
157
|
+
isLocationChanged={Boolean(period.startDate) && hasLocationChanged}
|
|
158
|
+
/>
|
|
159
|
+
)}
|
|
160
|
+
</>
|
|
161
|
+
);
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
export default Moh240LabRegister;
|