@ampath/esm-reports-app 1.0.0-next.79 → 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.
Files changed (69) hide show
  1. package/dist/0.css +1 -0
  2. package/dist/0.css.map +1 -0
  3. package/dist/0.js +1 -0
  4. package/dist/0.js.map +1 -0
  5. package/dist/410.js +1 -1
  6. package/dist/410.js.map +1 -1
  7. package/dist/600.js +1 -1
  8. package/dist/600.js.map +1 -1
  9. package/dist/757.js +1 -0
  10. package/dist/757.js.map +1 -0
  11. package/dist/ampath-esm-reports-app.js +1 -1
  12. package/dist/ampath-esm-reports-app.js.map +1 -1
  13. package/dist/routes.json +1 -1
  14. package/package.json +1 -1
  15. package/src/common/report-filters/report-filters.scss +15 -6
  16. package/src/common/report-page/download-sheet-pdf.ts +193 -27
  17. package/src/common/report-register/report-parents.ts +2 -0
  18. package/src/dashboard/report-catalog.ts +18 -0
  19. package/src/registers/moh-267/moh-267-register.component.tsx +407 -0
  20. package/src/registers/moh-267/moh-267-register.scss +298 -0
  21. package/src/registers/moh-267/moh-267-summary.ts +102 -0
  22. package/src/registers/moh-267/moh-267.component.tsx +94 -0
  23. package/src/registers/moh-267/moh-267.types.ts +47 -0
  24. package/src/registers/moh-362/moh-362-lab-register.component.tsx +469 -310
  25. package/src/registers/moh-362/moh-362-lab-register.scss +412 -0
  26. package/src/registers/moh-362/moh-362-linkage-register.component.tsx +188 -93
  27. package/src/registers/moh-362/moh-362-linkage-register.scss +91 -0
  28. package/src/registers/moh-362/moh-362.component.tsx +33 -15
  29. package/src/registers/moh-362/moh-362.types.ts +15 -25
  30. package/src/registers/presumptive-tb/presumptive-tb-register.component.tsx +391 -0
  31. package/src/registers/presumptive-tb/presumptive-tb-register.scss +262 -0
  32. package/src/registers/presumptive-tb/presumptive-tb.component.tsx +92 -0
  33. package/src/registers/presumptive-tb/presumptive-tb.types.ts +88 -0
  34. package/src/registers/register-catalog.ts +37 -3
  35. package/src/registers/register-file-name.ts +18 -0
  36. package/src/registers/tb4/tb4-parts.tsx +164 -0
  37. package/src/registers/tb4/tb4-patient-sheet.component.tsx +418 -0
  38. package/src/registers/tb4/tb4-register.scss +282 -0
  39. package/src/registers/tb4/tb4-treatment-sheet.component.tsx +349 -0
  40. package/src/registers/tb4/tb4.component.tsx +120 -0
  41. package/src/registers/tb4/tb4.types.ts +129 -0
  42. package/src/reports/acf-summary/acf-summary-form.component.tsx +176 -0
  43. package/src/reports/acf-summary/acf-summary-linelist.component.tsx +86 -0
  44. package/src/reports/acf-summary/acf-summary.component.tsx +97 -0
  45. package/src/reports/acf-summary/acf-summary.counts.ts +37 -0
  46. package/src/reports/acf-summary/acf-summary.scss +162 -0
  47. package/src/reports/moh-729b/moh-729b-form.component.tsx +385 -0
  48. package/src/reports/moh-729b/moh-729b-linelist.component.tsx +205 -0
  49. package/src/reports/moh-729b/moh-729b-summary.component.tsx +258 -0
  50. package/src/reports/moh-729b/moh-729b-summary.config.ts +229 -0
  51. package/src/reports/moh-729b/moh-729b.component.tsx +157 -0
  52. package/src/reports/moh-729b/moh-729b.config.ts +235 -0
  53. package/src/reports/moh-729b/moh-729b.scss +276 -0
  54. package/src/reports/moh-729b/moh-729b.types.ts +111 -0
  55. package/src/reports/moh-731/moh-731.component.tsx +10 -7
  56. package/src/reports/moh-731/moh-731.scss +3 -6
  57. package/src/reports/moh-731/summary/moh-731-summary.config.ts +2 -4
  58. package/src/reports/moh-731/summary/moh-731-summary.scss +5 -0
  59. package/src/resources/moh-267.resource.ts +27 -0
  60. package/src/resources/moh-729b.resource.ts +25 -0
  61. package/src/resources/presumptive-tb.resource.ts +27 -0
  62. package/src/root.component.tsx +14 -0
  63. package/dist/177.css +0 -1
  64. package/dist/177.css.map +0 -1
  65. package/dist/177.js +0 -1
  66. package/dist/177.js.map +0 -1
  67. package/dist/580.js +0 -1
  68. package/dist/580.js.map +0 -1
  69. package/src/registers/moh-362/moh-362-register.scss +0 -222
@@ -1,117 +1,212 @@
1
1
  import React from 'react';
2
2
 
3
+ import { usePageScale } from '../../common/report-page';
3
4
  import { clientName, formDate, text, type Moh362Row } from './moh-362.types';
4
- import styles from './moh-362-register.scss';
5
+ import sheet from './moh-362-lab-register.scss';
6
+ import styles from './moh-362-linkage-register.scss';
5
7
 
6
8
  interface Moh362LinkageRegisterProps {
7
9
  rows: Moh362Row[];
8
10
  }
9
11
 
12
+ /** Rows ruled on each page of the paper form */
13
+ const ROWS_PER_PAGE = 15;
14
+
15
+ /** A4 landscape less its 8mm margins: the page as drawn on screen, inside the report card */
16
+ const PAGE_WIDTH_MM = 297 - 2 * 8;
17
+
10
18
  const COLUMN_IDS = 'abcdefghij'.split('');
11
19
 
20
+ /** Column widths, measured off page 6 of the form, as fractions of the sheet */
21
+ const COLUMN_WIDTHS = [82, 204, 271, 180, 134, 193, 286, 147, 134, 182];
22
+ const COLUMN_TOTAL = COLUMN_WIDTHS.reduce((sum, width) => sum + width, 0);
23
+
24
+ /** Columns split into an upper and a lower cell: (b), (d) and (g) */
25
+ const SPLIT_COLUMNS = new Set(['b', 'd', 'g']);
26
+
27
+ const paginate = (rows: Moh362Row[]) => {
28
+ const pages: Moh362Row[][] = [];
29
+ for (let start = 0; start < rows.length; start += ROWS_PER_PAGE) {
30
+ pages.push(rows.slice(start, start + ROWS_PER_PAGE));
31
+ }
32
+ return pages.length ? pages : [[]];
33
+ };
34
+
35
+ /** A heading holding two labels: upper cell over lower cell */
36
+ const SplitHeader: React.FC<{ upper: React.ReactNode; lower: React.ReactNode }> = ({ upper, lower }) => (
37
+ <th className={styles.splitHeader}>
38
+ <div className={styles.splitStack}>
39
+ <span>{upper}</span>
40
+ <span>{lower}</span>
41
+ </div>
42
+ </th>
43
+ );
44
+
45
+ /** One client: two ruled lines, columns (b), (d) and (g) split into upper and lower cells */
46
+ const ClientRows: React.FC<{ row?: Moh362Row; serial: number }> = ({ row, serial }) => {
47
+ if (!row) {
48
+ return (
49
+ <>
50
+ <tr className={styles.clientUpper}>
51
+ {COLUMN_IDS.map((id) =>
52
+ SPLIT_COLUMNS.has(id) ? <td key={id} className={sheet.upperCell} /> : <td key={id} rowSpan={2} />,
53
+ )}
54
+ </tr>
55
+ <tr className={styles.clientLower}>
56
+ {[...SPLIT_COLUMNS].map((id) => (
57
+ <td key={id} className={sheet.lowerCell} />
58
+ ))}
59
+ </tr>
60
+ </>
61
+ );
62
+ }
63
+
64
+ return (
65
+ <>
66
+ <tr className={styles.clientUpper}>
67
+ <td rowSpan={2}>{serial}</td>
68
+ <td className={sheet.upperCell}>{text(row.id_number)}</td>
69
+ <td rowSpan={2} className={sheet.left}>
70
+ <span className={sheet.clamp}>{clientName(row)}</span>
71
+ </td>
72
+ <td className={sheet.upperCell}>{text(row.telephone)}</td>
73
+ <td rowSpan={2}>{text(row.client_tested_as)}</td>
74
+ <td rowSpan={2} className={sheet.left}>
75
+ <span className={sheet.clamp}>{text(row.referral_to)}</span>
76
+ </td>
77
+ <td className={`${sheet.upperCell} ${sheet.left}`}>{text(row.handed_to)}</td>
78
+ <td rowSpan={2}>{formDate(row.art_start_date)}</td>
79
+ <td rowSpan={2}>{text(row.ccc_number)}</td>
80
+ <td rowSpan={2} className={sheet.left}>
81
+ <span className={sheet.clamp}>{text(row.screening_remarks)}</span>
82
+ </td>
83
+ </tr>
84
+ <tr className={styles.clientLower}>
85
+ <td className={sheet.lowerCell}>{text(row.nupi)}</td>
86
+ <td className={`${sheet.lowerCell} ${sheet.left}`}>{text(row.residence)}</td>
87
+ {/* the receiving health worker's cadre is not recorded */}
88
+ <td className={sheet.lowerCell} />
89
+ </tr>
90
+ </>
91
+ );
92
+ };
93
+
12
94
  /**
13
- * REFERRAL AND LINKAGE REGISTER (Ver. July 2023), page 6 of the MOH 362 form:
14
- * the clients who tested HIV positive and were linked to care (started on ART),
15
- * columns (a) to (j), with the total linked at the foot.
95
+ * One A4 landscape page of the register: 15 ruled client rows, and on the last
96
+ * page the total of clients linked across the whole register, in one frame.
16
97
  */
17
- const Moh362LinkageRegister: React.FC<Moh362LinkageRegisterProps> = ({ rows }) => {
18
- const linked = rows.length;
98
+ const LinkageRegisterPage: React.FC<{
99
+ rows: Moh362Row[];
100
+ firstSerial: number;
101
+ page: number;
102
+ pages: number;
103
+ scale: number;
104
+ /** clients linked in the whole register, given only to the last page */
105
+ totalLinked?: number;
106
+ }> = ({ rows, firstSerial, page, pages, scale, totalLinked }) => (
107
+ <section className={sheet.page} data-register-page style={{ '--page-zoom': scale } as React.CSSProperties}>
108
+ <div className={sheet.runningHead}>
109
+ <span>Ministry Of Health</span>
110
+ <span>MOH 362 HTS LAB, REFERRAL AND LINKAGE REGISTER</span>
111
+ <span>Ver. July 2023</span>
112
+ </div>
113
+ <h4 className={`${sheet.title} ${styles.title}`}>REFERRAL AND LINKAGE REGISTER</h4>
19
114
 
20
- return (
21
- <div className={styles.sheet}>
22
- <div className={styles.runningHead}>
23
- <span>Ministry Of Health</span>
24
- <span>MOH 362 HTS LAB, REFERRAL AND LINKAGE REGISTER</span>
25
- <span>Ver. July 2023</span>
26
- </div>
27
- <h4 className={`${styles.sheetTitle} ${styles.serifTitle}`}>REFERRAL AND LINKAGE REGISTER</h4>
28
-
29
- <div className={styles.tableScroll}>
30
- <table className={styles.register}>
31
- <thead>
32
- <tr>
33
- <th>
34
- Serial
35
- <br />
36
- Number
37
- </th>
38
- <th className={styles.splitHeader}>
39
- <span>National Identification Number</span>
40
- <span>National Unique Patient Identifier (NUPI) Number</span>
41
- </th>
42
- <th className={styles.wide}>Client Names</th>
43
- <th className={styles.splitHeader}>
44
- <span>Telephone Number</span>
45
- <span>Residence</span>
46
- </th>
47
- <th>
48
- Identification
49
- <br />
50
- Strategy
51
- <br />
52
- (VCT/IT/HIVST
53
- <br />
54
- /SNS)
55
- </th>
56
- <th className={styles.wide}>Patient referred to</th>
57
- <th className={styles.wide}>
58
- Handed over to:
59
- <br />
115
+ <div className={sheet.frame}>
116
+ <table className={`${sheet.register} ${styles.linkageTable}`}>
117
+ <colgroup>
118
+ {COLUMN_WIDTHS.map((width, index) => (
119
+ <col key={index} style={{ width: `${(width / COLUMN_TOTAL) * 100}%` }} />
120
+ ))}
121
+ </colgroup>
122
+ <thead>
123
+ <tr className={styles.headingRow}>
124
+ <th>
125
+ Serial
126
+ <br />
127
+ Number
128
+ </th>
129
+ <SplitHeader
130
+ upper="National Identification Number"
131
+ lower="National Unique Patient Identifier (NUPI) Number"
132
+ />
133
+ <th>Client Names</th>
134
+ <SplitHeader upper="Telephone Number" lower="Residence" />
135
+ <th>
136
+ Identification Strategy
137
+ <br />
138
+ <span className={styles.regular}>(VCT/IT/HIVST /SNS)</span>
139
+ </th>
140
+ <th>Patient referred to</th>
141
+ <th>
142
+ Handed over to:
143
+ <br />
144
+ <span className={styles.regular}>
60
145
  (Indicate Name (upper cell)
61
146
  <br />
62
147
  and Cadre (lower cell)
63
- </th>
64
- <th>
65
- Date started on
66
- <br />
67
- ART
68
- </th>
69
- <th>CCC Number</th>
70
- <th className={styles.wide}>Remarks</th>
71
- </tr>
72
- <tr className={styles.columnIds}>
73
- {COLUMN_IDS.map((id) => (
74
- <th key={id}>({id})</th>
75
- ))}
76
- </tr>
77
- </thead>
78
- <tbody>
79
- {rows.map((row, index) => (
80
- <React.Fragment key={`${row.patient_id}-${row.serial_number}-${index}`}>
81
- <tr>
82
- <td rowSpan={2}>{index + 1}</td>
83
- <td className={styles.upperCell}>{text(row.id_number)}</td>
84
- <td rowSpan={2} className={styles.left}>
85
- {clientName(row)}
86
- </td>
87
- <td className={styles.upperCell}>{text(row.telephone)}</td>
88
- <td rowSpan={2}>{text(row.client_tested_as)}</td>
89
- <td rowSpan={2} className={styles.left}>
90
- {text(row.referral_to)}
91
- </td>
92
- <td className={`${styles.upperCell} ${styles.left}`}>{text(row.handed_to)}</td>
93
- <td rowSpan={2}>{formDate(row.art_start_date)}</td>
94
- <td rowSpan={2}>{text(row.ccc_number)}</td>
95
- <td rowSpan={2} className={styles.left}>
96
- {text(row.screening_remarks)}
97
- </td>
98
- </tr>
99
- <tr>
100
- <td className={styles.lowerCell}>{text(row.nupi)}</td>
101
- <td className={styles.lowerCell}>{text(row.residence)}</td>
102
- {/* the receiving health worker's cadre is not recorded */}
103
- <td className={styles.lowerCell} />
104
- </tr>
105
- </React.Fragment>
148
+ </span>
149
+ </th>
150
+ <th>Date started on ART</th>
151
+ <th>CCC Number</th>
152
+ <th>Remarks</th>
153
+ </tr>
154
+ <tr className={styles.idRow}>
155
+ {COLUMN_IDS.map((id) => (
156
+ <th key={id}>({id})</th>
106
157
  ))}
158
+ </tr>
159
+ </thead>
160
+ <tbody>
161
+ {Array.from({ length: ROWS_PER_PAGE }, (_, index) => (
162
+ <ClientRows key={index} row={rows[index]} serial={firstSerial + index} />
163
+ ))}
164
+ {totalLinked !== undefined && (
107
165
  <tr className={styles.totalRow}>
108
- <th colSpan={9} className={styles.left}>
166
+ <th colSpan={3} className={styles.totalLabel}>
109
167
  Totals Clients Linked
110
168
  </th>
111
- <td>{linked}</td>
169
+ <td colSpan={6} className={styles.totalFill} />
170
+ <td className={styles.totalFigure}>{totalLinked}</td>
112
171
  </tr>
113
- </tbody>
114
- </table>
172
+ )}
173
+ </tbody>
174
+ </table>
175
+ </div>
176
+
177
+ <div className={sheet.pageFoot}>
178
+ <span>
179
+ Page {page} of {pages}
180
+ </span>
181
+ <span>Ver. July 2023</span>
182
+ </div>
183
+ </section>
184
+ );
185
+
186
+ /**
187
+ * REFERRAL AND LINKAGE REGISTER (Ver. July 2023), page 6 of the MOH 362 form,
188
+ * as A4 landscape pages: the clients who tested HIV positive and were linked to
189
+ * care (started on ART), columns (a) to (j), with the total linked at the foot
190
+ * of the last page.
191
+ */
192
+ const Moh362LinkageRegister: React.FC<Moh362LinkageRegisterProps> = ({ rows }) => {
193
+ const { ref, scale } = usePageScale<HTMLDivElement>(PAGE_WIDTH_MM);
194
+ const pages = paginate(rows);
195
+
196
+ return (
197
+ <div className={sheet.reportFrame}>
198
+ <div className={sheet.viewport} ref={ref}>
199
+ {pages.map((pageRows, index) => (
200
+ <LinkageRegisterPage
201
+ key={index}
202
+ rows={pageRows}
203
+ firstSerial={index * ROWS_PER_PAGE + 1}
204
+ page={index + 1}
205
+ pages={pages.length}
206
+ scale={scale}
207
+ totalLinked={index === pages.length - 1 ? rows.length : undefined}
208
+ />
209
+ ))}
115
210
  </div>
116
211
  </div>
117
212
  );
@@ -0,0 +1,91 @@
1
+ /* REFERRAL AND LINKAGE REGISTER as A4 landscape pages. The page, frame, ruled
2
+ table and split cells come from the lab register's sheet styles; these are
3
+ page 6's own measurements: a shorter heading in larger bold type, and taller
4
+ client rows, 15 to a page, with the total of clients linked on the last. */
5
+
6
+ $ink: #000;
7
+ $rule: 0.6pt solid $ink;
8
+ $thick: 1.4pt solid $ink;
9
+ $dotted: 0.6pt dotted $ink;
10
+ $shade: #f2f2f2; // the lower cells and the totals row
11
+
12
+ $heading-h: 21mm;
13
+ $ids-h: 5.2mm;
14
+ $line-h: 4.6mm; // half a client row: each client takes two lines
15
+ $total-h: 6.7mm;
16
+
17
+ /* the title is set in a serif on this page of the form */
18
+ .title {
19
+ font-family: Georgia, 'Times New Roman', serif;
20
+ font-size: 10pt;
21
+ line-height: 1.2;
22
+ }
23
+
24
+ .linkageTable .headingRow th {
25
+ height: $heading-h;
26
+ font-size: 7.5pt;
27
+ font-weight: 700;
28
+ }
29
+
30
+ /* the strategy and handed-over headings keep their codes in regular type */
31
+ .linkageTable .headingRow .regular {
32
+ font-weight: 400;
33
+ }
34
+
35
+ .linkageTable .idRow th {
36
+ height: $ids-h;
37
+ font-size: 8pt;
38
+ font-weight: 700;
39
+ border-top: $thick;
40
+ border-bottom: $thick;
41
+ }
42
+
43
+ .linkageTable .clientUpper td,
44
+ .linkageTable .clientLower td {
45
+ height: $line-h;
46
+ font-size: 7pt;
47
+ white-space: nowrap;
48
+ }
49
+
50
+ /* a heading holding two labels: upper cell over lower cell, split by a dotted rule */
51
+ .splitHeader {
52
+ padding: 0 !important;
53
+ }
54
+
55
+ .splitStack {
56
+ display: flex;
57
+ flex-direction: column;
58
+ height: $heading-h;
59
+
60
+ span {
61
+ display: flex;
62
+ flex: 1;
63
+ align-items: center;
64
+ justify-content: center;
65
+ padding: 0 0.5mm;
66
+ }
67
+
68
+ span:first-child {
69
+ border-bottom: $dotted;
70
+ }
71
+ }
72
+
73
+ .linkageTable .totalRow > * {
74
+ height: $total-h;
75
+ font-size: 7pt;
76
+ font-weight: 700;
77
+ border-top: $thick;
78
+ }
79
+
80
+ .totalLabel {
81
+ text-align: left !important;
82
+ }
83
+
84
+ .totalFill,
85
+ .totalFigure {
86
+ background-color: $shade;
87
+ }
88
+
89
+ .totalFigure {
90
+ font-size: 8pt !important;
91
+ }
@@ -1,10 +1,12 @@
1
- import React, { useState } from 'react';
1
+ import React, { useRef, useState } from 'react';
2
2
  import { useSearchParams } from 'react-router-dom';
3
3
  import { Tab, TabList, TabPanel, TabPanels, Tabs } from '@carbon/react';
4
4
  import { useSession } from '@openmrs/esm-framework';
5
5
 
6
6
  import { ReportFiltersComponent, type ReportPeriod, useRestorePeriod } from '../../common/report-filters';
7
7
  import { showReportError } from '../../common/report-error';
8
+ import { downloadSheetsPdf } from '../../common/report-page';
9
+ import { registerFileName } from '../register-file-name';
8
10
  import { ReportPlaceholder, useGeneratedLocation } from '../../common/report-placeholder';
9
11
  import { ReportSkeleton } from '../../common/report-skeleton';
10
12
  import { getMoh362 } from '../../resources/moh-362.resource';
@@ -47,6 +49,19 @@ const Moh362Register: React.FC = () => {
47
49
 
48
50
  useRestorePeriod(generate);
49
51
 
52
+ /* Download PDF saves the whole register, whichever sheet is open: the HTS lab
53
+ register's pages, then the referral and linkage register's, each an A4
54
+ landscape page. The file is made from copies, so the pages on screen are
55
+ left as they are. */
56
+ const sheetsRef = useRef<HTMLDivElement>(null);
57
+ const downloadPdf = async () => {
58
+ const pages = Array.from(sheetsRef.current?.querySelectorAll<HTMLElement>('[data-register-page]') ?? []);
59
+ const name = registerFileName('MOH-362', session?.sessionLocation?.display ?? '', period);
60
+ /* the register is styled by its own stylesheets in Arial and Georgia only,
61
+ and runs to many pages */
62
+ await downloadSheetsPdf(pages, name, { orientation: 'landscape', stylesheetsOnly: true, image: 'jpeg' });
63
+ };
64
+
50
65
  /* the open sheet is kept in the URL */
51
66
  const [searchParams, setSearchParams] = useSearchParams();
52
67
  const selectedSheet = searchParams.get('sheet') === 'linkage' ? 1 : 0;
@@ -71,26 +86,29 @@ const Moh362Register: React.FC = () => {
71
86
  generatedPeriod={period}
72
87
  defaultTab="custom"
73
88
  backLink={{ label: 'All registers', to: '/?tab=registers' }}
89
+ onDownloadPdf={downloadPdf}
74
90
  />
75
91
  {isLoading && <ReportSkeleton />}
76
92
  {!isLoading && !isGenerated && (
77
93
  <ReportPlaceholder reportName="MOH 362 register" isLocationChanged={rows !== null && hasLocationChanged} />
78
94
  )}
79
95
  {!isLoading && isGenerated && (
80
- <Tabs selectedIndex={selectedSheet} onChange={selectSheet}>
81
- <TabList aria-label="MOH 362 registers" className={styles.sheetTabs}>
82
- <Tab>HTS Lab Register</Tab>
83
- <Tab>Referral and Linkage Register</Tab>
84
- </TabList>
85
- <TabPanels>
86
- <TabPanel className={styles.sheetPanel}>
87
- <Moh362LabRegister rows={rows} />
88
- </TabPanel>
89
- <TabPanel className={styles.sheetPanel}>
90
- <Moh362LinkageRegister rows={rows.filter(isLinked)} />
91
- </TabPanel>
92
- </TabPanels>
93
- </Tabs>
96
+ <div ref={sheetsRef}>
97
+ <Tabs selectedIndex={selectedSheet} onChange={selectSheet}>
98
+ <TabList aria-label="MOH 362 registers" className={styles.sheetTabs}>
99
+ <Tab>HTS Lab Register</Tab>
100
+ <Tab>Referral and Linkage Register</Tab>
101
+ </TabList>
102
+ <TabPanels>
103
+ <TabPanel className={styles.sheetPanel}>
104
+ <Moh362LabRegister rows={rows} />
105
+ </TabPanel>
106
+ <TabPanel className={styles.sheetPanel}>
107
+ <Moh362LinkageRegister rows={rows.filter(isLinked)} />
108
+ </TabPanel>
109
+ </TabPanels>
110
+ </Tabs>
111
+ </div>
94
112
  )}
95
113
  </>
96
114
  );
@@ -19,25 +19,32 @@ export interface Moh362Row {
19
19
  telephone?: string | null;
20
20
  date_of_visit?: string | null;
21
21
  hts_provider?: string | null;
22
- marital_status?: number | string | null;
23
- population_type?: number | string | null;
22
+ /** Married, Widowed, Single, Divorced or Separated */
23
+ marital_status?: string | null;
24
+ /** Gen Pop or KVP */
25
+ population_type?: string | null;
24
26
  sub_population?: string | null;
25
- setting?: number | string | null;
26
- hiv_test1?: number | string | null;
27
+ /** Facility or Community */
28
+ setting?: string | null;
29
+ /** Negative, Positive or Inconclusive */
30
+ hiv_test1?: string | null;
27
31
  kit1_name?: string | null;
28
32
  lot1?: string | null;
29
33
  expiry1?: string | null;
30
- hiv_test2?: number | string | null;
34
+ hiv_test2?: string | null;
31
35
  kit2_name?: string | null;
32
36
  lot2?: string | null;
33
37
  expiry2?: string | null;
34
- hiv_test3?: number | string | null;
38
+ hiv_test3?: string | null;
35
39
  kit3_name?: string | null;
36
40
  lot3?: string | null;
37
41
  expiry3?: string | null;
42
+ /** Positive, Negative or Inconclusive */
38
43
  final_result?: string | null;
44
+ /** Yes, No or NA */
39
45
  discordant_couple?: string | null;
40
- referral_services?: number | string | null;
46
+ /** PrEP, PEP, VMMC, PMTCT, as a list, or NA */
47
+ referral_services?: string | null;
41
48
  client_tested_as?: string | null;
42
49
  referral_to?: string | null;
43
50
  residence?: string | null;
@@ -58,24 +65,7 @@ export const clientName = (row: Moh362Row) =>
58
65
  row.full_name || [row.first_name, row.middle_name, row.last_name].filter(Boolean).join(' ');
59
66
 
60
67
  /** Tested positive and linked to care: started on ART */
61
- export const isLinked = (row: Moh362Row) => row.final_result === 'P' && Boolean(row.art_start_date);
62
-
63
- /** An HIV test result (the concept the ETL returns) as the form's code: N, P, I */
64
- export const testResult = (value: unknown) => {
65
- switch (String(value ?? '')) {
66
- case '664':
67
- return 'N';
68
- case '703':
69
- return 'P';
70
- case '1138':
71
- return 'I';
72
- default:
73
- return text(value);
74
- }
75
- };
76
-
77
- /** The final result as the form's code: N, P, Ic (inconclusive), NA */
78
- export const finalResult = (value: unknown) => (String(value ?? '') === 'I' ? 'Ic' : text(value));
68
+ export const isLinked = (row: Moh362Row) => row.final_result === 'Positive' && Boolean(row.art_start_date);
79
69
 
80
70
  export const ageOf = (row: Moh362Row) => {
81
71
  const age = Number(row.age);