@ampath/esm-reports-app 1.0.0-next.8 → 1.0.0-next.80
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/0.css +1 -0
- package/dist/0.css.map +1 -0
- package/dist/0.js +1 -0
- package/dist/0.js.map +1 -0
- 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/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/757.js +1 -0
- package/dist/757.js.map +1 -0
- package/dist/781.js +1 -0
- package/dist/781.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 +264 -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 +225 -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 +29 -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 +191 -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-267/moh-267-register.component.tsx +407 -0
- package/src/registers/moh-267/moh-267-register.scss +298 -0
- package/src/registers/moh-267/moh-267-summary.ts +102 -0
- package/src/registers/moh-267/moh-267.component.tsx +94 -0
- package/src/registers/moh-267/moh-267.types.ts +47 -0
- package/src/registers/moh-362/moh-362-lab-register.component.tsx +551 -0
- package/src/registers/moh-362/moh-362-lab-register.scss +412 -0
- package/src/registers/moh-362/moh-362-linkage-register.component.tsx +215 -0
- package/src/registers/moh-362/moh-362-linkage-register.scss +91 -0
- package/src/registers/moh-362/moh-362.component.tsx +117 -0
- package/src/registers/moh-362/moh-362.scss +11 -0
- package/src/registers/moh-362/moh-362.types.ts +87 -0
- package/src/registers/presumptive-tb/presumptive-tb-register.component.tsx +391 -0
- package/src/registers/presumptive-tb/presumptive-tb-register.scss +262 -0
- package/src/registers/presumptive-tb/presumptive-tb.component.tsx +92 -0
- package/src/registers/presumptive-tb/presumptive-tb.types.ts +88 -0
- package/src/registers/register-catalog.ts +202 -0
- package/src/registers/register-file-name.ts +18 -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/registers/tb4/tb4-parts.tsx +164 -0
- package/src/registers/tb4/tb4-patient-sheet.component.tsx +418 -0
- package/src/registers/tb4/tb4-register.scss +282 -0
- package/src/registers/tb4/tb4-treatment-sheet.component.tsx +349 -0
- package/src/registers/tb4/tb4.component.tsx +120 -0
- package/src/registers/tb4/tb4.types.ts +129 -0
- package/src/reports/acf-summary/acf-summary-form.component.tsx +176 -0
- package/src/reports/acf-summary/acf-summary-linelist.component.tsx +86 -0
- package/src/reports/acf-summary/acf-summary.component.tsx +97 -0
- package/src/reports/acf-summary/acf-summary.counts.ts +37 -0
- package/src/reports/acf-summary/acf-summary.scss +162 -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 +246 -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 +331 -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 +35 -28
- package/src/reports/moh-706/sub-reports/blood-chemistry/blood-chemistry.component.tsx +229 -305
- package/src/reports/moh-706/sub-reports/haematology/haematology.component.tsx +26 -19
- package/src/reports/moh-706/sub-reports/histology-and-cytology/histology-and-cytology.component.tsx +23 -23
- package/src/reports/moh-706/sub-reports/parasitology/parasitology.component.tsx +25 -18
- package/src/reports/moh-706/sub-reports/serology/serology.component.tsx +21 -14
- package/src/reports/moh-706/sub-reports/specimen-referral-to-higher-levels/specimen-referral-to-higher-levels.component.tsx +2 -2
- package/src/reports/moh-706/sub-reports/urine-analysis/urine-analysis.component.tsx +27 -20
- package/src/reports/moh-710/moh-710.component.tsx +815 -632
- package/src/reports/moh-710/moh-710.scss +142 -21
- package/src/reports/moh-710/registers/moh-413/moh-413-columns.ts +22 -0
- package/src/reports/moh-710/registers/moh-413/moh-413.component.tsx +140 -0
- package/src/reports/moh-710/registers/moh-413/moh-413.scss +64 -0
- 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 +191 -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 +153 -1269
- package/src/reports/moh-711/moh711.scss +135 -20
- package/src/reports/moh-711/registers/moh-333-register.component.tsx +680 -0
- package/src/reports/moh-711/registers/moh-333.columns.ts +119 -0
- package/src/reports/moh-711/registers/moh-405-register.component.tsx +566 -0
- package/src/reports/moh-711/registers/moh-405.columns.ts +120 -0
- package/src/reports/moh-711/registers/moh-406-register.component.tsx +581 -0
- package/src/reports/moh-711/registers/moh-406.columns.ts +133 -0
- package/src/reports/moh-711/registers/moh-407-register.component.tsx +157 -0
- package/src/reports/moh-711/registers/moh-407.columns.ts +24 -0
- package/src/reports/moh-711/registers/moh-407.scss +64 -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/moh-512-columns.ts +94 -0
- package/src/reports/moh-711/registers/moh-512.component.tsx +191 -0
- package/src/reports/moh-711/registers/moh-512.scss +64 -0
- package/src/reports/moh-711/registers/tb-register-columns.ts +17 -0
- package/src/reports/moh-711/registers/tb-register.component.tsx +96 -0
- package/src/reports/moh-711/registers/tb-register.scss +64 -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 +389 -0
- package/src/reports/moh-711/sections/family-planning.component.tsx +299 -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 +94 -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-729b/moh-729b-form.component.tsx +385 -0
- package/src/reports/moh-729b/moh-729b-linelist.component.tsx +205 -0
- package/src/reports/moh-729b/moh-729b-summary.component.tsx +258 -0
- package/src/reports/moh-729b/moh-729b-summary.config.ts +229 -0
- package/src/reports/moh-729b/moh-729b.component.tsx +157 -0
- package/src/reports/moh-729b/moh-729b.config.ts +235 -0
- package/src/reports/moh-729b/moh-729b.scss +276 -0
- package/src/reports/moh-729b/moh-729b.types.ts +111 -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 +269 -0
- package/src/reports/moh-731/moh-731.scss +496 -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 +527 -0
- package/src/reports/moh-731/summary/moh-731-summary.scss +247 -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 +2058 -0
- package/src/reports/moh-745/moh-745.scss +142 -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 +223 -0
- package/src/reports/table-wrapper/table-row-mapper.component.tsx +20 -5
- 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-267.resource.ts +27 -0
- 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 +118 -0
- package/src/resources/moh-706.resource.ts +80 -0
- package/src/resources/moh-710.resource.ts +60 -9
- package/src/resources/moh-711.resource.ts +206 -9
- package/src/resources/moh-717.resource.ts +26 -9
- package/src/resources/moh-729b.resource.ts +25 -0
- 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 +66 -0
- package/src/resources/presumptive-tb.resource.ts +27 -0
- package/src/root.component.tsx +79 -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 +30 -1
- 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/812.js +0 -1
- package/dist/812.js.map +0 -1
- package/dist/879.js +0 -1
- package/dist/879.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,205 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { useSearchParams } from 'react-router-dom';
|
|
3
|
+
|
|
4
|
+
import { type LinelistColumn, LinelistTable, useAllRows } from '../../common/linelist-table';
|
|
5
|
+
import { RegisterLayout, useLocationName } from '../../common/report-register';
|
|
6
|
+
import { getMoh729b } from '../../resources/moh-729b.resource';
|
|
7
|
+
import { decodeCell, describeCell, inCells, PEP_CODES, type Moh729bCell, type Moh729bRow } from './moh-729b.types';
|
|
8
|
+
|
|
9
|
+
const formatDate = (value: unknown) => (value ? new Date(String(value)).toLocaleDateString('en-GB') : '');
|
|
10
|
+
|
|
11
|
+
/** Clients on ART */
|
|
12
|
+
const ART_COLUMNS: LinelistColumn[] = [
|
|
13
|
+
{ header: 'Name', field: 'full_name' },
|
|
14
|
+
{ header: 'CCC Number', field: 'ccc_number', text: true },
|
|
15
|
+
{ header: 'AMRS Number', field: 'amrs_number', text: true },
|
|
16
|
+
{ header: 'Sex', field: 'gender' },
|
|
17
|
+
{ header: 'Age', field: 'age' },
|
|
18
|
+
{ header: 'Weight (kg)', field: 'weight' },
|
|
19
|
+
{ header: 'Regimen', field: 'regimen', wrap: true },
|
|
20
|
+
{ header: 'Regimen Code', field: 'regimen_code' },
|
|
21
|
+
{ header: 'ARV Line', field: 'arv_line' },
|
|
22
|
+
{ header: 'RTC Date', value: (row) => formatDate(row.rtc_date), sortValue: (row) => row.rtc_date },
|
|
23
|
+
/* Available from the Columns menu */
|
|
24
|
+
{ header: 'Pregnant', field: 'pregnant', hidden: true },
|
|
25
|
+
{ header: 'Weight Band', field: 'weight_band', hidden: true },
|
|
26
|
+
{ header: 'ARV Concepts', field: 'cur_arv_meds', text: true, hidden: true },
|
|
27
|
+
{
|
|
28
|
+
header: 'Date of Birth',
|
|
29
|
+
value: (row) => formatDate(row.birthdate),
|
|
30
|
+
sortValue: (row) => row.birthdate,
|
|
31
|
+
hidden: true,
|
|
32
|
+
},
|
|
33
|
+
];
|
|
34
|
+
|
|
35
|
+
/** Clients on PrEP */
|
|
36
|
+
const PREP_COLUMNS: LinelistColumn[] = [
|
|
37
|
+
{ header: 'Name', field: 'full_name' },
|
|
38
|
+
{ header: 'PrEP Number', field: 'prep_number', text: true },
|
|
39
|
+
{ header: 'AMRS Number', field: 'amrs_number', text: true },
|
|
40
|
+
{ header: 'Sex', field: 'gender' },
|
|
41
|
+
{ header: 'Age', field: 'age' },
|
|
42
|
+
{ header: 'PrEP Regimen', field: 'regimen', wrap: true },
|
|
43
|
+
{ header: 'Regimen Code', field: 'regimen_code' },
|
|
44
|
+
{ header: 'PrEP Status', field: 'prep_status' },
|
|
45
|
+
{
|
|
46
|
+
header: 'Last Visit',
|
|
47
|
+
value: (row) => formatDate(row.last_visit_date),
|
|
48
|
+
sortValue: (row) => row.last_visit_date,
|
|
49
|
+
},
|
|
50
|
+
/* Available from the Columns menu */
|
|
51
|
+
{
|
|
52
|
+
header: 'Date of Birth',
|
|
53
|
+
value: (row) => formatDate(row.birthdate),
|
|
54
|
+
sortValue: (row) => row.birthdate,
|
|
55
|
+
hidden: true,
|
|
56
|
+
},
|
|
57
|
+
];
|
|
58
|
+
|
|
59
|
+
/** Clients given PEP */
|
|
60
|
+
const PEP_COLUMNS: LinelistColumn[] = [
|
|
61
|
+
{ header: 'Name', field: 'full_name' },
|
|
62
|
+
{ header: 'CCC Number', field: 'ccc_number', text: true },
|
|
63
|
+
{ header: 'AMRS Number', field: 'amrs_number', text: true },
|
|
64
|
+
{ header: 'Sex', field: 'gender' },
|
|
65
|
+
{ header: 'Age', field: 'age' },
|
|
66
|
+
{
|
|
67
|
+
header: 'Exposure Date',
|
|
68
|
+
value: (row) => formatDate(row.exposure_date),
|
|
69
|
+
sortValue: (row) => row.exposure_date,
|
|
70
|
+
},
|
|
71
|
+
{ header: 'Exposure Type', field: 'exposure_type', wrap: true },
|
|
72
|
+
{ header: 'PEP Regimen', field: 'regimen', wrap: true },
|
|
73
|
+
{ header: 'Regimen Code', field: 'regimen_code' },
|
|
74
|
+
/* Available from the Columns menu */
|
|
75
|
+
{ header: 'ARV Concepts', field: 'cur_arv_meds', text: true, hidden: true },
|
|
76
|
+
];
|
|
77
|
+
|
|
78
|
+
/** Hepatitis B patients who are HIV-ve */
|
|
79
|
+
const HBV_COLUMNS: LinelistColumn[] = [
|
|
80
|
+
{ header: 'Name', field: 'full_name' },
|
|
81
|
+
{ header: 'AMRS Number', field: 'amrs_number', text: true },
|
|
82
|
+
{ header: 'Sex', field: 'gender' },
|
|
83
|
+
{ header: 'Age', field: 'age' },
|
|
84
|
+
{ header: 'ANC Visit', value: (row) => formatDate(row.anc_date), sortValue: (row) => row.anc_date },
|
|
85
|
+
{ header: 'HIV Result', field: 'hiv_result' },
|
|
86
|
+
{ header: 'HBV Result', field: 'hbv_result' },
|
|
87
|
+
{
|
|
88
|
+
header: 'HBV Test Date',
|
|
89
|
+
value: (row) => formatDate(row.hbv_test_date),
|
|
90
|
+
sortValue: (row) => row.hbv_test_date,
|
|
91
|
+
},
|
|
92
|
+
{ header: 'Hep B Regimen', field: 'regimen' },
|
|
93
|
+
{ header: 'Regimen Code', field: 'regimen_code' },
|
|
94
|
+
];
|
|
95
|
+
|
|
96
|
+
/** Clients on ART who started TPT */
|
|
97
|
+
const TPT_COLUMNS: LinelistColumn[] = [
|
|
98
|
+
{ header: 'Name', field: 'full_name' },
|
|
99
|
+
{ header: 'CCC Number', field: 'ccc_number', text: true },
|
|
100
|
+
{ header: 'AMRS Number', field: 'amrs_number', text: true },
|
|
101
|
+
{ header: 'Sex', field: 'gender' },
|
|
102
|
+
{ header: 'Age', field: 'age' },
|
|
103
|
+
{
|
|
104
|
+
header: 'ART Start Date',
|
|
105
|
+
value: (row) => formatDate(row.arv_first_regimen_start_date),
|
|
106
|
+
sortValue: (row) => row.arv_first_regimen_start_date,
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
header: 'TB Screening Date',
|
|
110
|
+
value: (row) => formatDate(row.tb_screening_date),
|
|
111
|
+
sortValue: (row) => row.tb_screening_date,
|
|
112
|
+
},
|
|
113
|
+
{ header: 'TB Screening Result', field: 'tb_screening_result' },
|
|
114
|
+
{
|
|
115
|
+
header: 'TPT Start Date',
|
|
116
|
+
value: (row) => formatDate(row.ipt_start_date),
|
|
117
|
+
sortValue: (row) => row.ipt_start_date,
|
|
118
|
+
},
|
|
119
|
+
{ header: 'TPT Regimen', field: 'regimen' },
|
|
120
|
+
{ header: 'Regimen Code', field: 'regimen_code' },
|
|
121
|
+
];
|
|
122
|
+
|
|
123
|
+
/** Each group's columns, its saved column layout and what its clients are */
|
|
124
|
+
const GROUPS = {
|
|
125
|
+
art: { columns: ART_COLUMNS, layoutKey: 'moh-729b', title: 'Current active clients on ART' },
|
|
126
|
+
prep: { columns: PREP_COLUMNS, layoutKey: 'moh-729b:prep', title: 'Current active clients on PrEP' },
|
|
127
|
+
pep: { columns: PEP_COLUMNS, layoutKey: 'moh-729b:pep', title: 'Clients given PEP this month' },
|
|
128
|
+
tpt: { columns: TPT_COLUMNS, layoutKey: 'moh-729b:tpt', title: 'HIV positive clients started on TPT this month' },
|
|
129
|
+
hbv: { columns: HBV_COLUMNS, layoutKey: 'moh-729b:hbv', title: 'Hepatitis B patients who are HIV-ve' },
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* The clients behind a count on the MOH 729B form or its Summary: the month's
|
|
134
|
+
* clients on ART, PrEP, PEP, Hep B or TPT (the same rows the form is counted
|
|
135
|
+
* from), narrowed to the count's regimen code and, where the form splits it,
|
|
136
|
+
* weight band or line. A total of several counts (`f`, one per count, with a
|
|
137
|
+
* `label`) lists the clients of all of them; its counts never overlap.
|
|
138
|
+
*/
|
|
139
|
+
const Moh729bLinelist: React.FC = () => {
|
|
140
|
+
const [searchParams] = useSearchParams();
|
|
141
|
+
const endDate = searchParams.get('endDate') ?? '';
|
|
142
|
+
const locationUuids = searchParams.get('locationUuids') ?? '';
|
|
143
|
+
const figures = searchParams.getAll('f');
|
|
144
|
+
const cells: Moh729bCell[] = figures.length
|
|
145
|
+
? figures.map(decodeCell)
|
|
146
|
+
: [
|
|
147
|
+
{
|
|
148
|
+
code: searchParams.get('code') ?? '',
|
|
149
|
+
band: searchParams.get('band') ?? undefined,
|
|
150
|
+
line: Number(searchParams.get('line')) || undefined,
|
|
151
|
+
},
|
|
152
|
+
];
|
|
153
|
+
const label = searchParams.get('label');
|
|
154
|
+
const heading = `${label ?? describeCell(cells[0])} linelist`;
|
|
155
|
+
/* the counts of a total are all of one kind, so the first decides the columns */
|
|
156
|
+
const cell = cells[0];
|
|
157
|
+
const group = cell.code.startsWith('PRP')
|
|
158
|
+
? GROUPS.prep
|
|
159
|
+
: cell.code.startsWith('HPB')
|
|
160
|
+
? GROUPS.hbv
|
|
161
|
+
: /^[AC]TPT1[AB]$/.test(cell.code)
|
|
162
|
+
? GROUPS.tpt
|
|
163
|
+
: PEP_CODES.has(cell.code)
|
|
164
|
+
? GROUPS.pep
|
|
165
|
+
: GROUPS.art;
|
|
166
|
+
|
|
167
|
+
const locationName = useLocationName(locationUuids);
|
|
168
|
+
/* the month's clients come back in one response; they are fetched once and
|
|
169
|
+
handed to the table in the batches it reads */
|
|
170
|
+
let clients: Promise<Moh729bRow[]> | undefined;
|
|
171
|
+
const { rows, isLoading } = useAllRows(
|
|
172
|
+
({ startIndex, limit }) => {
|
|
173
|
+
clients ??= getMoh729b({ locationUuids, endDate }).then((all) =>
|
|
174
|
+
(all as unknown as Moh729bRow[]).filter((row) => inCells(row, cells)),
|
|
175
|
+
);
|
|
176
|
+
return clients.then((matching) => ({
|
|
177
|
+
results: matching.slice(startIndex, startIndex + limit),
|
|
178
|
+
total: matching.length,
|
|
179
|
+
isTotalExact: true,
|
|
180
|
+
}));
|
|
181
|
+
},
|
|
182
|
+
[searchParams.toString()],
|
|
183
|
+
);
|
|
184
|
+
|
|
185
|
+
return (
|
|
186
|
+
<RegisterLayout
|
|
187
|
+
parentLabel="MOH 729B F-MAPS Report"
|
|
188
|
+
parentPath="/moh-729b"
|
|
189
|
+
title={`MOH 729B F-MAPS · ${group.title}`}
|
|
190
|
+
heading={heading}
|
|
191
|
+
location={locationName}
|
|
192
|
+
isLoading={isLoading}
|
|
193
|
+
isEmpty={rows.length === 0}
|
|
194
|
+
>
|
|
195
|
+
<LinelistTable
|
|
196
|
+
rows={rows}
|
|
197
|
+
columns={group.columns}
|
|
198
|
+
layoutKey={group.layoutKey}
|
|
199
|
+
exportName={`MOH 729B ${heading} ${endDate}`}
|
|
200
|
+
/>
|
|
201
|
+
</RegisterLayout>
|
|
202
|
+
);
|
|
203
|
+
};
|
|
204
|
+
|
|
205
|
+
export default Moh729bLinelist;
|
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
import React, { useState } from 'react';
|
|
2
|
+
import classNames from 'classnames';
|
|
3
|
+
import { Button, Tab, TabList, TabPanel, TabPanels, Tabs, Toggle } from '@carbon/react';
|
|
4
|
+
import { Download } from '@carbon/react/icons';
|
|
5
|
+
|
|
6
|
+
import { columnsToCsv, downloadCsv } from '../../common/report-register';
|
|
7
|
+
import {
|
|
8
|
+
columnCells,
|
|
9
|
+
MOH_729B_SUMMARY,
|
|
10
|
+
NOT_YET_COUNTED,
|
|
11
|
+
rowCells,
|
|
12
|
+
type SummarySection,
|
|
13
|
+
type SummaryTable,
|
|
14
|
+
tableCells,
|
|
15
|
+
} from './moh-729b-summary.config';
|
|
16
|
+
import { countedCode, type Moh729bCell } from './moh-729b.types';
|
|
17
|
+
/* drawn exactly like the MOH 731 Summary */
|
|
18
|
+
import styles from '../moh-731/summary/moh-731-summary.scss';
|
|
19
|
+
|
|
20
|
+
interface Moh729bSummaryProps {
|
|
21
|
+
/** Clients in any of the counts */
|
|
22
|
+
count: (cells: Moh729bCell[]) => number;
|
|
23
|
+
/** Opens the linelist of the counts, under a heading */
|
|
24
|
+
openLinelist: (cells: Moh729bCell[], label: string) => void;
|
|
25
|
+
/** The section tab shown, by id */
|
|
26
|
+
sectionId: string;
|
|
27
|
+
onSectionChange: (sectionId: string) => void;
|
|
28
|
+
/** Used to name the export */
|
|
29
|
+
period: string;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const SHOW_CODES_KEY = 'ampath-moh729b-summary-show-codes';
|
|
33
|
+
|
|
34
|
+
const readShowCodes = () => {
|
|
35
|
+
try {
|
|
36
|
+
return localStorage.getItem(SHOW_CODES_KEY) === 'true';
|
|
37
|
+
} catch {
|
|
38
|
+
return false;
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
/** The form's code for a single count, e.g. `CF2G` or `PM12 · 2nd Line` */
|
|
43
|
+
const codeOf = (cells: Moh729bCell[]) =>
|
|
44
|
+
cells.length === 1 && !cells[0].band ? countedCode(cells[0].code) : undefined;
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* The MOH 729B report regrouped for reading, as the MOH 731 Summary: a tab per
|
|
48
|
+
* group of the form, each part as a compact table with a Total column and a
|
|
49
|
+
* Total row. Every number opens its linelist, and a total opens the clients of
|
|
50
|
+
* the counts it adds up.
|
|
51
|
+
*/
|
|
52
|
+
const Moh729bSummary: React.FC<Moh729bSummaryProps> = ({ count, openLinelist, sectionId, onSectionChange, period }) => {
|
|
53
|
+
const [showCodes, setShowCodes] = useState(readShowCodes);
|
|
54
|
+
const selectedIndex = Math.max(
|
|
55
|
+
0,
|
|
56
|
+
MOH_729B_SUMMARY.findIndex((section) => section.id === sectionId),
|
|
57
|
+
);
|
|
58
|
+
|
|
59
|
+
const toggleCodes = (checked: boolean) => {
|
|
60
|
+
setShowCodes(checked);
|
|
61
|
+
try {
|
|
62
|
+
localStorage.setItem(SHOW_CODES_KEY, String(checked));
|
|
63
|
+
} catch {
|
|
64
|
+
/* not remembered, still applied */
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
/** A count or total: its value, opening its linelist; the form's code on hover (or shown) */
|
|
69
|
+
const value = (cells: Moh729bCell[], label: string, options: { strong?: boolean; code?: boolean } = {}) => {
|
|
70
|
+
if (cells.length === 0) {
|
|
71
|
+
return <span className={styles.none}>–</span>;
|
|
72
|
+
}
|
|
73
|
+
const amount = count(cells);
|
|
74
|
+
const code = codeOf(cells);
|
|
75
|
+
return (
|
|
76
|
+
<button
|
|
77
|
+
type="button"
|
|
78
|
+
className={classNames(styles.value, { [styles.zero]: amount === 0, [styles.strong]: options.strong })}
|
|
79
|
+
onClick={() => openLinelist(cells, label)}
|
|
80
|
+
title={code ? `${code} · open the linelist` : `Open the ${label} linelist`}
|
|
81
|
+
>
|
|
82
|
+
{amount.toLocaleString()}
|
|
83
|
+
{showCodes && code && options.code !== false && <span className={styles.code}>{code}</span>}
|
|
84
|
+
</button>
|
|
85
|
+
);
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
const exportCsv = () => {
|
|
89
|
+
const lines: Array<Record<string, unknown>> = [];
|
|
90
|
+
MOH_729B_SUMMARY.forEach((section) =>
|
|
91
|
+
section.cards.forEach((card) =>
|
|
92
|
+
card.tables.forEach((table) => {
|
|
93
|
+
const part = [card.title, table.title].filter(Boolean).join(' · ');
|
|
94
|
+
table.rows.forEach((row) => {
|
|
95
|
+
row.cells.forEach((cells, index) => {
|
|
96
|
+
if (cells.length) {
|
|
97
|
+
lines.push({ section: section.tab, part, row: row.label, column: table.columns[index], cells });
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
lines.push({ section: section.tab, part, row: row.label, column: 'Total', cells: rowCells(row) });
|
|
101
|
+
});
|
|
102
|
+
if (table.totalLabel) {
|
|
103
|
+
lines.push({
|
|
104
|
+
section: section.tab,
|
|
105
|
+
part,
|
|
106
|
+
row: table.totalLabel,
|
|
107
|
+
column: 'Total',
|
|
108
|
+
cells: tableCells(table),
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
}),
|
|
112
|
+
),
|
|
113
|
+
);
|
|
114
|
+
downloadCsv(
|
|
115
|
+
columnsToCsv(lines, [
|
|
116
|
+
{ header: 'Section', value: (line) => line.section },
|
|
117
|
+
{ header: 'Part', value: (line) => line.part },
|
|
118
|
+
{ header: 'Row', value: (line) => line.row },
|
|
119
|
+
{ header: 'Column', value: (line) => line.column },
|
|
120
|
+
{ header: 'MOH 729B Code', value: (line) => codeOf(line.cells as Moh729bCell[]) ?? '', text: true },
|
|
121
|
+
{ header: 'Value', value: (line) => count(line.cells as Moh729bCell[]) },
|
|
122
|
+
]),
|
|
123
|
+
`moh-729b-summary-${period}.csv`.replace(/[^a-z0-9.-]+/gi, '-').toLowerCase(),
|
|
124
|
+
);
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
const renderTable = (table: SummaryTable, caption: string, key: number) => (
|
|
128
|
+
<div key={key} className={styles.tableScroll}>
|
|
129
|
+
{table.title && <p className={styles.tableTitle}>{table.title}</p>}
|
|
130
|
+
<table className={classNames(styles.table, { [styles.single]: table.columns.length === 0 })}>
|
|
131
|
+
<thead>
|
|
132
|
+
<tr>
|
|
133
|
+
<th scope="col" className={styles.labelCol} />
|
|
134
|
+
{table.columns.map((column) => (
|
|
135
|
+
<th scope="col" key={column}>
|
|
136
|
+
{column}
|
|
137
|
+
</th>
|
|
138
|
+
))}
|
|
139
|
+
<th scope="col" className={styles.totalCol}>
|
|
140
|
+
{table.columns.length ? 'Total' : 'Number'}
|
|
141
|
+
</th>
|
|
142
|
+
</tr>
|
|
143
|
+
</thead>
|
|
144
|
+
<tbody>
|
|
145
|
+
{table.rows.map((row) => (
|
|
146
|
+
<tr key={row.label}>
|
|
147
|
+
<th scope="row" className={styles.labelCol}>
|
|
148
|
+
{row.label}
|
|
149
|
+
</th>
|
|
150
|
+
{table.columns.map((column, index) => (
|
|
151
|
+
<td key={column}>{value(row.cells[index] ?? [], `${caption} · ${row.label} · ${column}`)}</td>
|
|
152
|
+
))}
|
|
153
|
+
<td className={styles.totalCol}>
|
|
154
|
+
{/* the code is beside the row's own counts already; only a count
|
|
155
|
+
shown in the Total column alone carries it here */}
|
|
156
|
+
{value(rowCells(row), `${caption} · ${row.label}`, { code: table.columns.length === 0 })}
|
|
157
|
+
</td>
|
|
158
|
+
</tr>
|
|
159
|
+
))}
|
|
160
|
+
</tbody>
|
|
161
|
+
{table.totalLabel && (
|
|
162
|
+
<tfoot>
|
|
163
|
+
<tr>
|
|
164
|
+
<th scope="row" className={styles.labelCol}>
|
|
165
|
+
{table.totalLabel}
|
|
166
|
+
</th>
|
|
167
|
+
{table.columns.map((column, index) => (
|
|
168
|
+
<td key={column}>
|
|
169
|
+
{value(columnCells(table, index), `${caption} · ${table.totalLabel} · ${column}`, {
|
|
170
|
+
strong: true,
|
|
171
|
+
code: false,
|
|
172
|
+
})}
|
|
173
|
+
</td>
|
|
174
|
+
))}
|
|
175
|
+
<td className={styles.totalCol}>
|
|
176
|
+
{value(tableCells(table), `${caption} · ${table.totalLabel}`, { strong: true, code: false })}
|
|
177
|
+
</td>
|
|
178
|
+
</tr>
|
|
179
|
+
</tfoot>
|
|
180
|
+
)}
|
|
181
|
+
</table>
|
|
182
|
+
</div>
|
|
183
|
+
);
|
|
184
|
+
|
|
185
|
+
const renderSection = (section: SummarySection) => (
|
|
186
|
+
<>
|
|
187
|
+
<div className={styles.strip}>
|
|
188
|
+
{section.headlines.map((headline) => (
|
|
189
|
+
<button
|
|
190
|
+
type="button"
|
|
191
|
+
key={headline.label}
|
|
192
|
+
className={styles.headline}
|
|
193
|
+
onClick={() => openLinelist(headline.cells, headline.label)}
|
|
194
|
+
>
|
|
195
|
+
<span className={styles.headlineLabel}>{headline.label}</span>
|
|
196
|
+
<span className={styles.headlineValue}>{count(headline.cells).toLocaleString()}</span>
|
|
197
|
+
</button>
|
|
198
|
+
))}
|
|
199
|
+
</div>
|
|
200
|
+
<div className={styles.grid}>
|
|
201
|
+
{section.cards.map((card) => (
|
|
202
|
+
<div key={card.title} className={styles.card}>
|
|
203
|
+
<h5 className={styles.cardTitle}>{card.title}</h5>
|
|
204
|
+
{card.note && <p className={styles.note}>{card.note}</p>}
|
|
205
|
+
{card.tables.map((table, index) => renderTable(table, card.title, index))}
|
|
206
|
+
</div>
|
|
207
|
+
))}
|
|
208
|
+
{section.id === 'ois' && (
|
|
209
|
+
<div className={styles.card}>
|
|
210
|
+
<h5 className={styles.cardTitle}>Not counted yet</h5>
|
|
211
|
+
<p className={styles.note}>{NOT_YET_COUNTED}</p>
|
|
212
|
+
</div>
|
|
213
|
+
)}
|
|
214
|
+
</div>
|
|
215
|
+
</>
|
|
216
|
+
);
|
|
217
|
+
|
|
218
|
+
return (
|
|
219
|
+
<div className={styles.summary}>
|
|
220
|
+
<Tabs
|
|
221
|
+
selectedIndex={selectedIndex}
|
|
222
|
+
onChange={({ selectedIndex: index }) => onSectionChange(MOH_729B_SUMMARY[index].id)}
|
|
223
|
+
>
|
|
224
|
+
<div className={styles.bar}>
|
|
225
|
+
<TabList aria-label="MOH 729B sections" className={styles.sectionTabs}>
|
|
226
|
+
{MOH_729B_SUMMARY.map((section) => (
|
|
227
|
+
<Tab key={section.id}>{section.tab}</Tab>
|
|
228
|
+
))}
|
|
229
|
+
</TabList>
|
|
230
|
+
<div className={styles.tools}>
|
|
231
|
+
<Toggle
|
|
232
|
+
id="moh729b-summary-codes"
|
|
233
|
+
size="sm"
|
|
234
|
+
labelText="Show MOH codes"
|
|
235
|
+
hideLabel
|
|
236
|
+
labelA="MOH codes"
|
|
237
|
+
labelB="MOH codes"
|
|
238
|
+
toggled={showCodes}
|
|
239
|
+
onToggle={toggleCodes}
|
|
240
|
+
/>
|
|
241
|
+
<Button kind="ghost" size="sm" renderIcon={Download} onClick={exportCsv}>
|
|
242
|
+
Export CSV
|
|
243
|
+
</Button>
|
|
244
|
+
</div>
|
|
245
|
+
</div>
|
|
246
|
+
<TabPanels>
|
|
247
|
+
{MOH_729B_SUMMARY.map((section) => (
|
|
248
|
+
<TabPanel key={section.id} className={styles.panel}>
|
|
249
|
+
{renderSection(section)}
|
|
250
|
+
</TabPanel>
|
|
251
|
+
))}
|
|
252
|
+
</TabPanels>
|
|
253
|
+
</Tabs>
|
|
254
|
+
</div>
|
|
255
|
+
);
|
|
256
|
+
};
|
|
257
|
+
|
|
258
|
+
export default Moh729bSummary;
|
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
import { WEIGHT_BANDS } from './moh-729b.config';
|
|
2
|
+
import { type Moh729bCell } from './moh-729b.types';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* The layout of the MOH 729B Summary tab: each section as a set of tables, each
|
|
6
|
+
* cell one or more counts of the form (a regimen code, narrowed to a weight
|
|
7
|
+
* band or line where the form splits it).
|
|
8
|
+
*
|
|
9
|
+
* The counts in a table never overlap -- a client has one regimen code, one
|
|
10
|
+
* weight band and one line -- so a Total column or row is the clients of its
|
|
11
|
+
* cells together, and opens their linelists as one.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
export interface SummaryRow {
|
|
15
|
+
label: string;
|
|
16
|
+
/** The counts in each column (Total excluded); empty where the form has no box */
|
|
17
|
+
cells: Moh729bCell[][];
|
|
18
|
+
/** What the row's Total counts, where that is not its cells together (a child
|
|
19
|
+
without a weight is in no band but still in the regimen's Total) */
|
|
20
|
+
total?: Moh729bCell[];
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface SummaryTable {
|
|
24
|
+
title?: string;
|
|
25
|
+
/** Column headings, Total excluded; none for a single "Number" column */
|
|
26
|
+
columns: string[];
|
|
27
|
+
rows: SummaryRow[];
|
|
28
|
+
/** Label of the Total row; omitted for no Total row */
|
|
29
|
+
totalLabel?: string;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export interface SummaryCard {
|
|
33
|
+
/** As on the form, e.g. `Adult ART`; also the linelist caption */
|
|
34
|
+
title: string;
|
|
35
|
+
note?: string;
|
|
36
|
+
tables: SummaryTable[];
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export interface SummarySection {
|
|
40
|
+
id: string;
|
|
41
|
+
tab: string;
|
|
42
|
+
/** Totals shown in the strip above the section */
|
|
43
|
+
headlines: Array<{ label: string; cells: Moh729bCell[] }>;
|
|
44
|
+
cards: SummaryCard[];
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const LINES = ['1st Line', '2nd Line', '3rd Line'];
|
|
48
|
+
|
|
49
|
+
/** A row of codes, one per column; null where the form has no such regimen */
|
|
50
|
+
const byCode = (label: string, codes: Array<string | null>): SummaryRow => ({
|
|
51
|
+
label,
|
|
52
|
+
cells: codes.map((code) => (code ? [{ code }] : [])),
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
/** A single figure, for a table with one "Number" column */
|
|
56
|
+
const single = (label: string, code: string): SummaryRow => ({ label, cells: [], total: [{ code }] });
|
|
57
|
+
|
|
58
|
+
/** Every count in a table's rows */
|
|
59
|
+
export const rowCells = (row: SummaryRow) => row.total ?? row.cells.flat();
|
|
60
|
+
export const tableCells = (table: SummaryTable) => table.rows.flatMap(rowCells);
|
|
61
|
+
export const columnCells = (table: SummaryTable, index: number) => table.rows.flatMap((row) => row.cells[index] ?? []);
|
|
62
|
+
|
|
63
|
+
const ADULT_ART: SummaryTable = {
|
|
64
|
+
columns: LINES,
|
|
65
|
+
rows: [
|
|
66
|
+
byCode('TDF + 3TC + DTG', ['AF2E', 'AS2B', 'AT2G']),
|
|
67
|
+
byCode('TAF + 3TC + DTG', ['AS2G', null, null]),
|
|
68
|
+
byCode('TDF + 3TC + EFV', ['AF2B', null, null]),
|
|
69
|
+
byCode('AZT + 3TC + DTG', ['AF1D', 'AS1C', 'AT2H']),
|
|
70
|
+
byCode('ABC + 3TC + DTG', ['AF4C', 'AS5C', null]),
|
|
71
|
+
byCode('TDF + 3TC + ATV/r', ['AF2D', 'AS2C', null]),
|
|
72
|
+
byCode('TDF + 3TC + LPV/r', ['AF2F', 'AS2A', null]),
|
|
73
|
+
byCode('AZT + 3TC + LPV/r', ['AF1E', 'AS1A', null]),
|
|
74
|
+
byCode('AZT + 3TC + ATV/r', ['AF1F', 'AS1B', null]),
|
|
75
|
+
byCode('ABC + 3TC + LPV/r', [null, 'AS5A', null]),
|
|
76
|
+
byCode('ABC + 3TC + ATV/r', [null, 'AS5B', null]),
|
|
77
|
+
byCode('TDF + 3TC + DTG + DRV + RTV', [null, null, 'AT2D']),
|
|
78
|
+
byCode('TDF + 3TC + DTG + ETV + DRV + RTV', [null, null, 'AT2F']),
|
|
79
|
+
byCode('AZT + 3TC + DTG + DRV + RTV', [null, null, 'AT2I']),
|
|
80
|
+
byCode('Any other regimen', ['AF5X', 'AS6X', 'AT2X']),
|
|
81
|
+
],
|
|
82
|
+
totalLabel: 'Total adults',
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
const PAEDIATRIC_LINES: Array<[string, string[]]> = [
|
|
86
|
+
['1st Line', ['CF2G', 'CF1D', 'CF4E', 'CF5X']],
|
|
87
|
+
['2nd Line', ['CS1A', 'CS1B', 'CS2A', 'CS2B', 'CS4X']],
|
|
88
|
+
['3rd Line', ['CT1H', 'CT2D', 'CT3X']],
|
|
89
|
+
];
|
|
90
|
+
|
|
91
|
+
const PAEDIATRIC_ART: SummaryTable = {
|
|
92
|
+
columns: LINES,
|
|
93
|
+
rows: [
|
|
94
|
+
byCode('ABC + 3TC + DTG', ['CF2G', 'CS2B', null]),
|
|
95
|
+
byCode('AZT + 3TC + DTG', ['CF1D', 'CS1B', null]),
|
|
96
|
+
byCode('TDF + 3TC + DTG', ['CF4E', null, null]),
|
|
97
|
+
byCode('AZT + 3TC + LPV/r', [null, 'CS1A', null]),
|
|
98
|
+
byCode('ABC + 3TC + LPV/r', [null, 'CS2A', null]),
|
|
99
|
+
byCode('AZT + 3TC + DRV + RTV + RAL', [null, null, 'CT1H']),
|
|
100
|
+
byCode('ABC + 3TC + DRV + RTV + RAL', [null, null, 'CT2D']),
|
|
101
|
+
byCode('Any other regimen', ['CF5X', 'CS4X', 'CT3X']),
|
|
102
|
+
],
|
|
103
|
+
totalLabel: 'Total children',
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
const PAEDIATRIC_WEIGHT: SummaryTable = {
|
|
107
|
+
title: 'By weight band',
|
|
108
|
+
columns: WEIGHT_BANDS,
|
|
109
|
+
rows: PAEDIATRIC_LINES.map(([label, codes]) => ({
|
|
110
|
+
label,
|
|
111
|
+
cells: WEIGHT_BANDS.map((band) => codes.map((code) => ({ code, band }))),
|
|
112
|
+
total: codes.map((code) => ({ code })),
|
|
113
|
+
})),
|
|
114
|
+
totalLabel: 'Total children',
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
const PMTCT_MOTHERS: SummaryTable = {
|
|
118
|
+
columns: LINES,
|
|
119
|
+
rows: (
|
|
120
|
+
[
|
|
121
|
+
['TDF + 3TC + DTG', 'PM12'],
|
|
122
|
+
['TDF + 3TC + EFV', 'PM9'],
|
|
123
|
+
['AZT + 3TC + DTG', 'PM13'],
|
|
124
|
+
['ABC + 3TC + DTG', 'PM14'],
|
|
125
|
+
['TDF + 3TC + ATV/r', 'PM11'],
|
|
126
|
+
['TDF + 3TC + LPV/r', 'PM7'],
|
|
127
|
+
['AZT + 3TC + ATV/r', 'PM10'],
|
|
128
|
+
['AZT + 3TC + LPV/r', 'PM5'],
|
|
129
|
+
['Any other PMTCT regimen', 'PM1X'],
|
|
130
|
+
] as Array<[string, string]>
|
|
131
|
+
).map(([label, code]) => ({ label, cells: [1, 2, 3].map((line) => [{ code, line }]), total: [{ code }] })),
|
|
132
|
+
totalLabel: 'Total pregnant women',
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
const PREP: SummaryTable = {
|
|
136
|
+
columns: [],
|
|
137
|
+
rows: [
|
|
138
|
+
single('TDF + FTC', 'PRP1A'),
|
|
139
|
+
single('TDF + 3TC', 'PRP1B'),
|
|
140
|
+
single('Dapivirine ring', 'PRP1D'),
|
|
141
|
+
single('Cabotegravir', 'PRP1E'),
|
|
142
|
+
single('Lenacapavir', 'PRP1F'),
|
|
143
|
+
single('Others', 'PRP1X'),
|
|
144
|
+
],
|
|
145
|
+
totalLabel: 'Total on PrEP',
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
const PEP: SummaryTable = {
|
|
149
|
+
columns: ['Adults', 'Children'],
|
|
150
|
+
rows: [
|
|
151
|
+
byCode('TDF + 3TC + DTG', ['PA3D', null]),
|
|
152
|
+
byCode('TDF + 3TC + ATV/r', ['PA3C', null]),
|
|
153
|
+
byCode('AZT + 3TC + ATV/r', ['PA1C', null]),
|
|
154
|
+
byCode('ABC + 3TC + LPV/r', [null, 'PC3A']),
|
|
155
|
+
byCode('ABC + 3TC + DTG', [null, 'PC3C']),
|
|
156
|
+
byCode('Any other PEP regimen', ['PA4X', 'PC4X']),
|
|
157
|
+
],
|
|
158
|
+
totalLabel: 'Total given PEP',
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
const HEPATITIS_B: SummaryTable = {
|
|
162
|
+
columns: [],
|
|
163
|
+
rows: [single('TDF + 3TC', 'HPB1A'), single('TDF + FTC', 'HPB1B'), single('Any other Hep B regimen', 'HPB1C')],
|
|
164
|
+
totalLabel: 'Total Hep B (HIV-ve)',
|
|
165
|
+
};
|
|
166
|
+
|
|
167
|
+
const TPT: SummaryTable = {
|
|
168
|
+
columns: ['6H', '3HP'],
|
|
169
|
+
rows: [byCode('Adults (≥15 yrs)', ['ATPT1A', 'ATPT1B']), byCode('Children (<15 yrs)', ['CTPT1A', 'CTPT1B'])],
|
|
170
|
+
totalLabel: 'Total started TPT',
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
const all = (table: SummaryTable) => tableCells(table);
|
|
174
|
+
|
|
175
|
+
/** Not counted yet: shown on the form blank, listed here so the gap is visible */
|
|
176
|
+
export const NOT_YET_COUNTED =
|
|
177
|
+
'Not counted yet: PMTCT infants (PC8, PC7, PC1X), universal prophylaxis (OI1A, OI1C, OI2A), TPT for HIV-negative clients (ATPT1C) and Cryptococcal Meningitis (OI6B, OI6X). These are blank on the form too, until their queries are added.';
|
|
178
|
+
|
|
179
|
+
export const MOH_729B_SUMMARY: SummarySection[] = [
|
|
180
|
+
{
|
|
181
|
+
id: 'art',
|
|
182
|
+
tab: 'ART',
|
|
183
|
+
headlines: [
|
|
184
|
+
{ label: 'Adults on ART', cells: all(ADULT_ART) },
|
|
185
|
+
{ label: 'Children on ART', cells: all(PAEDIATRIC_ART) },
|
|
186
|
+
{ label: 'Pregnant women on ART', cells: all(PMTCT_MOTHERS) },
|
|
187
|
+
{ label: 'Total on ART', cells: [...all(ADULT_ART), ...all(PAEDIATRIC_ART), ...all(PMTCT_MOTHERS)] },
|
|
188
|
+
],
|
|
189
|
+
cards: [
|
|
190
|
+
{ title: 'Adult ART', tables: [ADULT_ART] },
|
|
191
|
+
{
|
|
192
|
+
title: 'Paediatric ART',
|
|
193
|
+
note: 'Under 15 years. Weight is from the latest clinical visit.',
|
|
194
|
+
tables: [PAEDIATRIC_ART, PAEDIATRIC_WEIGHT],
|
|
195
|
+
},
|
|
196
|
+
{ title: 'PMTCT · Pregnant women', note: 'Women pregnant this month.', tables: [PMTCT_MOTHERS] },
|
|
197
|
+
],
|
|
198
|
+
},
|
|
199
|
+
{
|
|
200
|
+
id: 'prevention',
|
|
201
|
+
tab: 'PrEP, PEP & Hep B',
|
|
202
|
+
headlines: [
|
|
203
|
+
{ label: 'On PrEP', cells: all(PREP) },
|
|
204
|
+
{ label: 'Given PEP', cells: all(PEP) },
|
|
205
|
+
{ label: 'Hep B (HIV-ve)', cells: all(HEPATITIS_B) },
|
|
206
|
+
],
|
|
207
|
+
cards: [
|
|
208
|
+
{ title: 'Pre-Exposure Prophylaxis (PrEP)', note: 'Seen for PrEP this month, not discontinued.', tables: [PREP] },
|
|
209
|
+
{ title: 'Post Exposure Prophylaxis (PEP)', note: 'Given PEP this month; children are under 15.', tables: [PEP] },
|
|
210
|
+
{
|
|
211
|
+
title: 'Hepatitis B patients who are HIV-ve',
|
|
212
|
+
note: 'HBV positive at ANC this month (MOH 731 2.4). No Hep B regimen is recorded yet, so all are "any other".',
|
|
213
|
+
tables: [HEPATITIS_B],
|
|
214
|
+
},
|
|
215
|
+
],
|
|
216
|
+
},
|
|
217
|
+
{
|
|
218
|
+
id: 'ois',
|
|
219
|
+
tab: 'OIs & TPT',
|
|
220
|
+
headlines: [{ label: 'Started TPT (HIV positive)', cells: all(TPT) }],
|
|
221
|
+
cards: [
|
|
222
|
+
{
|
|
223
|
+
title: 'TPT HIV Positive',
|
|
224
|
+
note: 'Started ART and TPT this month (MOH 731 3.4).',
|
|
225
|
+
tables: [TPT],
|
|
226
|
+
},
|
|
227
|
+
],
|
|
228
|
+
},
|
|
229
|
+
];
|