@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.
Files changed (302) 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/197.js +1 -1
  6. package/dist/282.js +1 -1
  7. package/dist/282.js.map +1 -1
  8. package/dist/295.css +1 -0
  9. package/dist/295.css.map +1 -0
  10. package/dist/295.js +1 -0
  11. package/dist/295.js.map +1 -0
  12. package/dist/299.js +2 -0
  13. package/dist/299.js.LICENSE.txt +3 -0
  14. package/dist/299.js.map +1 -0
  15. package/dist/300.js +1 -1
  16. package/dist/311.js +1 -1
  17. package/dist/311.js.map +1 -1
  18. package/dist/335.js +1 -1
  19. package/dist/353.js +1 -1
  20. package/dist/353.js.map +1 -1
  21. package/dist/354.js +2 -0
  22. package/dist/354.js.LICENSE.txt +20 -0
  23. package/dist/354.js.map +1 -0
  24. package/dist/410.js +1 -0
  25. package/dist/410.js.map +1 -0
  26. package/dist/457.js +2 -0
  27. package/dist/457.js.LICENSE.txt +261 -0
  28. package/dist/457.js.map +1 -0
  29. package/dist/478.js +1 -1
  30. package/dist/478.js.map +1 -1
  31. package/dist/480.js +1 -0
  32. package/dist/540.js +1 -1
  33. package/dist/540.js.map +1 -1
  34. package/dist/55.js +1 -1
  35. package/dist/600.js +2 -0
  36. package/dist/600.js.LICENSE.txt +35 -0
  37. package/dist/600.js.map +1 -0
  38. package/dist/652.js +1 -1
  39. package/dist/757.js +1 -0
  40. package/dist/757.js.map +1 -0
  41. package/dist/781.js +1 -0
  42. package/dist/781.js.map +1 -0
  43. package/dist/961.js +1 -1
  44. package/dist/961.js.map +1 -1
  45. package/dist/99.js +1 -1
  46. package/dist/99.js.map +1 -0
  47. package/dist/ampath-esm-reports-app.js +1 -1
  48. package/dist/ampath-esm-reports-app.js.map +1 -1
  49. package/dist/routes.json +1 -1
  50. package/package.json +4 -2
  51. package/src/common/daily-summary-table.tsx +55 -0
  52. package/src/common/linelist-table/index.ts +3 -0
  53. package/src/common/linelist-table/linelist-columns.ts +48 -0
  54. package/src/common/linelist-table/linelist-table.component.tsx +442 -0
  55. package/src/common/linelist-table/linelist-table.scss +306 -0
  56. package/src/common/linelist-table/linelist-table.test.tsx +120 -0
  57. package/src/common/linelist-table/use-all-rows.ts +93 -0
  58. package/src/common/linelist-table/use-column-layout.ts +123 -0
  59. package/src/common/report-error/index.ts +1 -0
  60. package/src/common/report-error/show-report-error.ts +25 -0
  61. package/src/common/report-filters/date-range.ts +137 -0
  62. package/src/common/report-filters/index.ts +4 -0
  63. package/src/common/report-filters/report-filters.component.tsx +317 -99
  64. package/src/common/report-filters/report-filters.scss +264 -32
  65. package/src/common/report-filters/types.ts +79 -0
  66. package/src/common/report-filters/use-restore-period.ts +43 -0
  67. package/src/common/report-header/index.ts +1 -0
  68. package/src/common/report-header/use-location-details.ts +58 -0
  69. package/src/common/report-page/download-sheet-pdf.ts +225 -0
  70. package/src/common/report-page/index.ts +3 -0
  71. package/src/common/report-page/report-page.component.tsx +32 -0
  72. package/src/common/report-page/report-page.scss +45 -0
  73. package/src/common/report-page/use-page-scale.ts +60 -0
  74. package/src/common/report-placeholder/has-report-data.ts +15 -0
  75. package/src/common/report-placeholder/index.ts +3 -0
  76. package/src/common/report-placeholder/report-placeholder.component.tsx +81 -0
  77. package/src/common/report-placeholder/report-placeholder.scss +48 -0
  78. package/src/common/report-placeholder/use-generated-location.ts +62 -0
  79. package/src/common/report-register/index.ts +6 -0
  80. package/src/common/report-register/register-columns.ts +46 -0
  81. package/src/common/report-register/register-layout.component.tsx +265 -0
  82. package/src/common/report-register/register-layout.scss +150 -0
  83. package/src/common/report-register/register-table.scss +71 -0
  84. package/src/common/report-register/report-parents.ts +29 -0
  85. package/src/common/report-register/rows-to-csv.ts +89 -0
  86. package/src/common/report-register/use-location-name.ts +54 -0
  87. package/src/common/report-register/use-patient-list.ts +92 -0
  88. package/src/common/report-skeleton/index.ts +1 -0
  89. package/src/common/report-skeleton/report-skeleton.component.tsx +46 -0
  90. package/src/common/report-skeleton/report-skeleton.scss +29 -0
  91. package/src/config-schema.ts +9 -1
  92. package/src/dashboard/report-catalog.ts +191 -0
  93. package/src/dashboard/reports-dasboard.tsx +189 -18
  94. package/src/dashboard/reports-dashboard.module.scss +357 -14
  95. package/src/index.ts +13 -1
  96. package/src/registers/moh-240/moh-240-register.scss +41 -0
  97. package/src/registers/moh-240/moh-240.component.test.tsx +166 -0
  98. package/src/registers/moh-240/moh-240.component.tsx +164 -0
  99. package/src/registers/moh-267/moh-267-register.component.tsx +407 -0
  100. package/src/registers/moh-267/moh-267-register.scss +298 -0
  101. package/src/registers/moh-267/moh-267-summary.ts +102 -0
  102. package/src/registers/moh-267/moh-267.component.tsx +94 -0
  103. package/src/registers/moh-267/moh-267.types.ts +47 -0
  104. package/src/registers/moh-362/moh-362-lab-register.component.tsx +551 -0
  105. package/src/registers/moh-362/moh-362-lab-register.scss +412 -0
  106. package/src/registers/moh-362/moh-362-linkage-register.component.tsx +215 -0
  107. package/src/registers/moh-362/moh-362-linkage-register.scss +91 -0
  108. package/src/registers/moh-362/moh-362.component.tsx +117 -0
  109. package/src/registers/moh-362/moh-362.scss +11 -0
  110. package/src/registers/moh-362/moh-362.types.ts +87 -0
  111. package/src/registers/presumptive-tb/presumptive-tb-register.component.tsx +391 -0
  112. package/src/registers/presumptive-tb/presumptive-tb-register.scss +262 -0
  113. package/src/registers/presumptive-tb/presumptive-tb.component.tsx +92 -0
  114. package/src/registers/presumptive-tb/presumptive-tb.types.ts +88 -0
  115. package/src/registers/register-catalog.ts +202 -0
  116. package/src/registers/register-file-name.ts +18 -0
  117. package/src/registers/tb-lab-register/tb-lab-register-table.component.tsx +116 -0
  118. package/src/registers/tb-lab-register/tb-lab-register-table.scss +123 -0
  119. package/src/registers/tb-lab-register/tb-lab-register.columns.ts +164 -0
  120. package/src/registers/tb-lab-register/tb-lab-register.component.test.tsx +232 -0
  121. package/src/registers/tb-lab-register/tb-lab-register.component.tsx +153 -0
  122. package/src/registers/tb4/tb4-parts.tsx +164 -0
  123. package/src/registers/tb4/tb4-patient-sheet.component.tsx +418 -0
  124. package/src/registers/tb4/tb4-register.scss +282 -0
  125. package/src/registers/tb4/tb4-treatment-sheet.component.tsx +349 -0
  126. package/src/registers/tb4/tb4.component.tsx +120 -0
  127. package/src/registers/tb4/tb4.types.ts +129 -0
  128. package/src/reports/acf-summary/acf-summary-form.component.tsx +176 -0
  129. package/src/reports/acf-summary/acf-summary-linelist.component.tsx +86 -0
  130. package/src/reports/acf-summary/acf-summary.component.tsx +97 -0
  131. package/src/reports/acf-summary/acf-summary.counts.ts +37 -0
  132. package/src/reports/acf-summary/acf-summary.scss +162 -0
  133. package/src/reports/datatable-wrapper/datatable-wrapper.component.tsx +63 -48
  134. package/src/reports/moh-240/moh-240-header.component.tsx +62 -0
  135. package/src/reports/moh-240/moh-240.columns.ts +113 -0
  136. package/src/reports/moh-240/moh-240.component.tsx +72 -9
  137. package/src/reports/moh-240/moh-240.scss +40 -0
  138. package/src/reports/moh-240/sub-reports/moh-240-register.component.tsx +31 -37
  139. package/src/reports/moh-240/sub-reports/page-summary.component.tsx +143 -106
  140. package/src/reports/moh-505/moh-505-header.component.tsx +67 -0
  141. package/src/reports/moh-505/moh-505.component.tsx +441 -0
  142. package/src/reports/moh-505/moh-505.scss +117 -0
  143. package/src/reports/moh-705B/moh-204b-register.component.tsx +246 -0
  144. package/src/reports/moh-705B/moh-204b.columns.ts +39 -0
  145. package/src/reports/moh-705B/moh-705b.component.tsx +659 -0
  146. package/src/reports/moh-705B/moh-705b.scss +172 -0
  147. package/src/reports/moh-705a/moh-705a.component.tsx +661 -0
  148. package/src/reports/moh-705a/moh-705a.scss +172 -0
  149. package/src/reports/moh-705a/registers/moh-204a-register.component.tsx +331 -0
  150. package/src/reports/moh-705a/registers/moh-204a.columns.ts +57 -0
  151. package/src/reports/moh-705a/type.ts +6 -0
  152. package/src/reports/moh-706/moh-706-header.component.tsx +66 -0
  153. package/src/reports/moh-706/moh-706.component.tsx +150 -24
  154. package/src/reports/moh-706/moh-706.scss +119 -11
  155. package/src/reports/moh-706/sub-reports/bacteriology/bacteriology.component.tsx +35 -28
  156. package/src/reports/moh-706/sub-reports/blood-chemistry/blood-chemistry.component.tsx +229 -305
  157. package/src/reports/moh-706/sub-reports/haematology/haematology.component.tsx +26 -19
  158. package/src/reports/moh-706/sub-reports/histology-and-cytology/histology-and-cytology.component.tsx +23 -23
  159. package/src/reports/moh-706/sub-reports/parasitology/parasitology.component.tsx +25 -18
  160. package/src/reports/moh-706/sub-reports/serology/serology.component.tsx +21 -14
  161. package/src/reports/moh-706/sub-reports/specimen-referral-to-higher-levels/specimen-referral-to-higher-levels.component.tsx +2 -2
  162. package/src/reports/moh-706/sub-reports/urine-analysis/urine-analysis.component.tsx +27 -20
  163. package/src/reports/moh-710/moh-710.component.tsx +815 -632
  164. package/src/reports/moh-710/moh-710.scss +142 -21
  165. package/src/reports/moh-710/registers/moh-413/moh-413-columns.ts +22 -0
  166. package/src/reports/moh-710/registers/moh-413/moh-413.component.tsx +140 -0
  167. package/src/reports/moh-710/registers/moh-413/moh-413.scss +64 -0
  168. package/src/reports/moh-710/registers/moh-510/moh-510.columns.ts +40 -0
  169. package/src/reports/moh-710/registers/moh-510/moh-510.component.tsx +191 -0
  170. package/src/reports/moh-710/registers/moh-510/moh-510.scss +130 -0
  171. package/src/reports/moh-710/registers/moh-511-710-register.component.tsx +268 -0
  172. package/src/reports/moh-710/registers/moh-511-710.columns.ts +52 -0
  173. package/src/reports/moh-710/registers/type.ts +31 -0
  174. package/src/reports/moh-711/moh-711.component.tsx +153 -1269
  175. package/src/reports/moh-711/moh711.scss +135 -20
  176. package/src/reports/moh-711/registers/moh-333-register.component.tsx +680 -0
  177. package/src/reports/moh-711/registers/moh-333.columns.ts +119 -0
  178. package/src/reports/moh-711/registers/moh-405-register.component.tsx +566 -0
  179. package/src/reports/moh-711/registers/moh-405.columns.ts +120 -0
  180. package/src/reports/moh-711/registers/moh-406-register.component.tsx +581 -0
  181. package/src/reports/moh-711/registers/moh-406.columns.ts +133 -0
  182. package/src/reports/moh-711/registers/moh-407-register.component.tsx +157 -0
  183. package/src/reports/moh-711/registers/moh-407.columns.ts +24 -0
  184. package/src/reports/moh-711/registers/moh-407.scss +64 -0
  185. package/src/reports/moh-711/registers/moh-510-register.component.tsx +55 -0
  186. package/src/reports/moh-711/registers/moh-511-register.component.tsx +270 -0
  187. package/src/reports/moh-711/registers/moh-511.columns.ts +59 -0
  188. package/src/reports/moh-711/registers/moh-512-columns.ts +94 -0
  189. package/src/reports/moh-711/registers/moh-512.component.tsx +191 -0
  190. package/src/reports/moh-711/registers/moh-512.scss +64 -0
  191. package/src/reports/moh-711/registers/tb-register-columns.ts +17 -0
  192. package/src/reports/moh-711/registers/tb-register.component.tsx +96 -0
  193. package/src/reports/moh-711/registers/tb-register.scss +64 -0
  194. package/src/reports/moh-711/registers/type.ts +192 -0
  195. package/src/reports/moh-711/sections/anc.component.tsx +159 -0
  196. package/src/reports/moh-711/sections/cervical-cancer.component.tsx +148 -0
  197. package/src/reports/moh-711/sections/chanis.component.tsx +389 -0
  198. package/src/reports/moh-711/sections/family-planning.component.tsx +299 -0
  199. package/src/reports/moh-711/sections/gbv.component.tsx +115 -0
  200. package/src/reports/moh-711/sections/maternity.component.tsx +321 -0
  201. package/src/reports/moh-711/sections/medical-social-work.component.tsx +73 -0
  202. package/src/reports/moh-711/sections/other.component.tsx +47 -0
  203. package/src/reports/moh-711/sections/physiotherapy.component.tsx +61 -0
  204. package/src/reports/moh-711/sections/pnc.component.tsx +127 -0
  205. package/src/reports/moh-711/sections/post-abortion.component.tsx +42 -0
  206. package/src/reports/moh-711/sections/rehabilitation.component.tsx +57 -0
  207. package/src/reports/moh-711/sections/report-compiled-by.component.tsx +50 -0
  208. package/src/reports/moh-711/sections/tb-screening.component.tsx +94 -0
  209. package/src/reports/moh-717/moh-717.component.tsx +149 -985
  210. package/src/reports/moh-717/moh717.scss +217 -21
  211. package/src/reports/moh-717/registers/nutrition-register.component.tsx +48 -0
  212. package/src/reports/moh-717/sections/finance.component.tsx +42 -0
  213. package/src/reports/moh-717/sections/inpatient.component.tsx +374 -0
  214. package/src/reports/moh-717/sections/maternity.component.tsx +126 -0
  215. package/src/reports/moh-717/sections/medical-records.component.tsx +35 -0
  216. package/src/reports/moh-717/sections/mortuary.component.tsx +43 -0
  217. package/src/reports/moh-717/sections/operations.component.tsx +53 -0
  218. package/src/reports/moh-717/sections/orthopaedic-trauma.component.tsx +98 -0
  219. package/src/reports/moh-717/sections/outpatient.component.tsx +597 -0
  220. package/src/reports/moh-717/sections/pharmacy.component.tsx +43 -0
  221. package/src/reports/moh-717/sections/preparedby.component.tsx +52 -0
  222. package/src/reports/moh-717/sections/special-services.component.tsx +114 -0
  223. package/src/reports/moh-729b/moh-729b-form.component.tsx +385 -0
  224. package/src/reports/moh-729b/moh-729b-linelist.component.tsx +205 -0
  225. package/src/reports/moh-729b/moh-729b-summary.component.tsx +258 -0
  226. package/src/reports/moh-729b/moh-729b-summary.config.ts +229 -0
  227. package/src/reports/moh-729b/moh-729b.component.tsx +157 -0
  228. package/src/reports/moh-729b/moh-729b.config.ts +235 -0
  229. package/src/reports/moh-729b/moh-729b.scss +276 -0
  230. package/src/reports/moh-729b/moh-729b.types.ts +111 -0
  231. package/src/reports/moh-731/linelist/moh-731-indicators.ts +1122 -0
  232. package/src/reports/moh-731/linelist/moh-731-linelist-sections.ts +472 -0
  233. package/src/reports/moh-731/linelist/moh-731-linelist.component.tsx +102 -0
  234. package/src/reports/moh-731/linelist/use-open-moh-731-linelist.ts +50 -0
  235. package/src/reports/moh-731/moh-731.component.tsx +269 -0
  236. package/src/reports/moh-731/moh-731.scss +496 -0
  237. package/src/reports/moh-731/registers/moh-366.columns.ts +38 -0
  238. package/src/reports/moh-731/registers/moh-366.component.tsx +471 -0
  239. package/src/reports/moh-731/registers/prep-register.component.tsx +284 -0
  240. package/src/reports/moh-731/registers/prep.columns.ts +34 -0
  241. package/src/reports/moh-731/sections/hiv-and-tb-treatment.component.tsx +1206 -0
  242. package/src/reports/moh-731/sections/hiv-testing-and-pre-exposure.component.tsx +727 -0
  243. package/src/reports/moh-731/sections/pmtct.component.tsx +572 -0
  244. package/src/reports/moh-731/summary/moh-731-summary.component.tsx +313 -0
  245. package/src/reports/moh-731/summary/moh-731-summary.config.ts +527 -0
  246. package/src/reports/moh-731/summary/moh-731-summary.scss +247 -0
  247. package/src/reports/moh-740/moh-740.component.scss +202 -0
  248. package/src/reports/moh-740/moh-740.component.tsx +1332 -0
  249. package/src/reports/moh-740/moh-740.resource.ts +54 -0
  250. package/src/reports/moh-740/registers/moh-222-daily-register/moh-222-daily-register.tsx +149 -0
  251. package/src/reports/moh-740/registers/moh-270-permanent-register/moh-270-permanent-register.tsx +123 -0
  252. package/src/reports/moh-740/registers/moh-740-patient-list.scss +0 -0
  253. package/src/reports/moh-740/registers/moh-740-patient-list.tsx +60 -0
  254. package/src/reports/moh-740/registers/moh-740.columns.ts +54 -0
  255. package/src/reports/moh-740/shared/data-cell/data-cell.scss +6 -0
  256. package/src/reports/moh-740/shared/data-cell/data-cell.tsx +23 -0
  257. package/src/reports/moh-740/shared/utils/indicator-register-map.ts +159 -0
  258. package/src/reports/moh-740/types/index.ts +223 -0
  259. package/src/reports/moh-745/moh-745.component.tsx +2058 -0
  260. package/src/reports/moh-745/moh-745.scss +142 -0
  261. package/src/reports/moh-745/registers/moh-412-register.component.tsx +249 -0
  262. package/src/reports/moh-745/registers/moh-412.columns.ts +52 -0
  263. package/src/reports/moh-745/registers/type.ts +223 -0
  264. package/src/reports/table-wrapper/table-row-mapper.component.tsx +20 -5
  265. package/src/reports/table-wrapper/table-wrapper.component.tsx +23 -14
  266. package/src/reports/table-wrapper/table-wrapper.scss +1 -16
  267. package/src/resources/moh-267.resource.ts +27 -0
  268. package/src/resources/moh-362.resource.ts +27 -0
  269. package/src/resources/moh-505.resource.ts +69 -0
  270. package/src/resources/moh-705.resource.ts +118 -0
  271. package/src/resources/moh-706.resource.ts +80 -0
  272. package/src/resources/moh-710.resource.ts +60 -9
  273. package/src/resources/moh-711.resource.ts +206 -9
  274. package/src/resources/moh-717.resource.ts +26 -9
  275. package/src/resources/moh-729b.resource.ts +25 -0
  276. package/src/resources/moh-731.resource.ts +90 -0
  277. package/src/resources/moh-745.resource.ts +76 -0
  278. package/src/resources/patient-list-page.ts +66 -0
  279. package/src/resources/presumptive-tb.resource.ts +27 -0
  280. package/src/root.component.tsx +79 -8
  281. package/src/root.scss +36 -0
  282. package/src/routes.json +3 -1
  283. package/src/utils/format-indicator.ts +39 -0
  284. package/src/utils/get-base-url.ts +39 -7
  285. package/src/utils/utils.ts +30 -1
  286. package/tsconfig.json +1 -1
  287. package/webpack.config.js +22 -1
  288. package/dist/138.js +0 -2
  289. package/dist/138.js.LICENSE.txt +0 -20
  290. package/dist/138.js.map +0 -1
  291. package/dist/184.js +0 -2
  292. package/dist/184.js.LICENSE.txt +0 -14
  293. package/dist/184.js.map +0 -1
  294. package/dist/205.js +0 -1
  295. package/dist/205.js.map +0 -1
  296. package/dist/812.js +0 -1
  297. package/dist/812.js.map +0 -1
  298. package/dist/879.js +0 -1
  299. package/dist/879.js.map +0 -1
  300. package/dist/ampath-esm-reports-app.js.buildmanifest.json +0 -539
  301. package/dist/main.js +0 -1
  302. package/dist/main.js.map +0 -1
@@ -0,0 +1,117 @@
1
+ import React, { useRef, useState } from 'react';
2
+ import { useSearchParams } from 'react-router-dom';
3
+ import { Tab, TabList, TabPanel, TabPanels, Tabs } from '@carbon/react';
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 { downloadSheetsPdf } from '../../common/report-page';
9
+ import { registerFileName } from '../register-file-name';
10
+ import { ReportPlaceholder, useGeneratedLocation } from '../../common/report-placeholder';
11
+ import { ReportSkeleton } from '../../common/report-skeleton';
12
+ import { getMoh362 } from '../../resources/moh-362.resource';
13
+ import Moh362LabRegister from './moh-362-lab-register.component';
14
+ import Moh362LinkageRegister from './moh-362-linkage-register.component';
15
+ import { isLinked, type Moh362Row } from './moh-362.types';
16
+ import styles from './moh-362.scss';
17
+
18
+ /**
19
+ * MOH 362 HTS Lab, Referral & Linkage Register, opened from the landing page's
20
+ * Registers tab. It shares the reports' filter bar. Unlike the other registers
21
+ * the form holds two sheets, shown as tabs: the HTS lab register (every test,
22
+ * the same tests as MOH 731 1.1 HIV Tests) and the referral and linkage
23
+ * register (those who tested positive and were linked to care, i.e. have an ART
24
+ * start date).
25
+ */
26
+ const Moh362Register: React.FC = () => {
27
+ const [rows, setRows] = useState<Moh362Row[] | null>(null);
28
+ const [isLoading, setIsLoading] = useState(false);
29
+ const [period, setPeriod] = useState<{ startDate: string; endDate: string }>({ startDate: '', endDate: '' });
30
+
31
+ const session = useSession();
32
+ const locationUuids = session?.sessionLocation?.uuid ?? '';
33
+ /* Only a register for the current location counts */
34
+ const { isForCurrentLocation, hasLocationChanged, markGenerated } = useGeneratedLocation('moh362Location');
35
+ const isGenerated = rows !== null && isForCurrentLocation;
36
+
37
+ const generate = async ({ startDate, endDate }: ReportPeriod) => {
38
+ setIsLoading(true);
39
+ setPeriod({ startDate, endDate });
40
+ try {
41
+ setRows(await getMoh362({ locationUuids, startDate, endDate }));
42
+ markGenerated(locationUuids);
43
+ } catch (error) {
44
+ showReportError('the MOH 362 register', error);
45
+ } finally {
46
+ setIsLoading(false);
47
+ }
48
+ };
49
+
50
+ useRestorePeriod(generate);
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
+
65
+ /* the open sheet is kept in the URL */
66
+ const [searchParams, setSearchParams] = useSearchParams();
67
+ const selectedSheet = searchParams.get('sheet') === 'linkage' ? 1 : 0;
68
+ const selectSheet = ({ selectedIndex }: { selectedIndex: number }) => {
69
+ const next = new URLSearchParams(searchParams);
70
+ if (selectedIndex === 1) {
71
+ next.set('sheet', 'linkage');
72
+ } else {
73
+ next.delete('sheet');
74
+ }
75
+ setSearchParams(next, { replace: true });
76
+ };
77
+
78
+ return (
79
+ <>
80
+ <ReportFiltersComponent
81
+ reportName="MOH 362 HTS Lab, Referral & Linkage Register"
82
+ mode="both"
83
+ onGenerate={generate}
84
+ isLoading={isLoading}
85
+ isReportGenerated={isGenerated}
86
+ generatedPeriod={period}
87
+ defaultTab="custom"
88
+ backLink={{ label: 'All registers', to: '/?tab=registers' }}
89
+ onDownloadPdf={downloadPdf}
90
+ />
91
+ {isLoading && <ReportSkeleton />}
92
+ {!isLoading && !isGenerated && (
93
+ <ReportPlaceholder reportName="MOH 362 register" isLocationChanged={rows !== null && hasLocationChanged} />
94
+ )}
95
+ {!isLoading && isGenerated && (
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>
112
+ )}
113
+ </>
114
+ );
115
+ };
116
+
117
+ export default Moh362Register;
@@ -0,0 +1,11 @@
1
+ @use '@carbon/layout';
2
+
3
+ /* HTS Lab Register / Referral and Linkage Register tabs, under the filters */
4
+ .sheetTabs {
5
+ margin-top: layout.$spacing-05;
6
+ }
7
+
8
+ /* each sheet brings its own frame */
9
+ .sheetPanel {
10
+ padding: 0;
11
+ }
@@ -0,0 +1,87 @@
1
+ import dayjs from 'dayjs';
2
+
3
+ /**
4
+ * One test in the MOH 362 register, as the ETL returns it (/moh-362-register):
5
+ * the same tests as MOH 731 1.1 HIV Tests, with the register's details.
6
+ */
7
+ export interface Moh362Row {
8
+ serial_number?: number | string;
9
+ patient_id?: number;
10
+ nupi?: string | null;
11
+ amrsID?: string | null;
12
+ id_number?: string | null;
13
+ full_name?: string | null;
14
+ first_name?: string | null;
15
+ middle_name?: string | null;
16
+ last_name?: string | null;
17
+ age?: number | string | null;
18
+ sex?: string | null;
19
+ telephone?: string | null;
20
+ date_of_visit?: string | null;
21
+ hts_provider?: string | null;
22
+ /** Married, Widowed, Single, Divorced or Separated */
23
+ marital_status?: string | null;
24
+ /** Gen Pop or KVP */
25
+ population_type?: string | null;
26
+ sub_population?: string | null;
27
+ /** Facility or Community */
28
+ setting?: string | null;
29
+ /** Negative, Positive or Inconclusive */
30
+ hiv_test1?: string | null;
31
+ kit1_name?: string | null;
32
+ lot1?: string | null;
33
+ expiry1?: string | null;
34
+ hiv_test2?: string | null;
35
+ kit2_name?: string | null;
36
+ lot2?: string | null;
37
+ expiry2?: string | null;
38
+ hiv_test3?: string | null;
39
+ kit3_name?: string | null;
40
+ lot3?: string | null;
41
+ expiry3?: string | null;
42
+ /** Positive, Negative or Inconclusive */
43
+ final_result?: string | null;
44
+ /** Yes, No or NA */
45
+ discordant_couple?: string | null;
46
+ /** PrEP, PEP, VMMC, PMTCT, as a list, or NA */
47
+ referral_services?: string | null;
48
+ client_tested_as?: string | null;
49
+ referral_to?: string | null;
50
+ residence?: string | null;
51
+ handed_to?: string | null;
52
+ art_start_date?: string | null;
53
+ ccc_number?: string | null;
54
+ screening_remarks?: string | null;
55
+ initial_remarks?: string | null;
56
+ }
57
+
58
+ /** Blank rather than "null" / "undefined" in a cell */
59
+ export const text = (value: unknown) => (value === null || value === undefined ? '' : String(value));
60
+
61
+ /** DD/MM/YYYY, as the form asks */
62
+ export const formDate = (value: unknown) => (value ? dayjs(String(value)).format('DD/MM/YYYY') : '');
63
+
64
+ export const clientName = (row: Moh362Row) =>
65
+ row.full_name || [row.first_name, row.middle_name, row.last_name].filter(Boolean).join(' ');
66
+
67
+ /** Tested positive and linked to care: started on ART */
68
+ export const isLinked = (row: Moh362Row) => row.final_result === 'Positive' && Boolean(row.art_start_date);
69
+
70
+ export const ageOf = (row: Moh362Row) => {
71
+ const age = Number(row.age);
72
+ return Number.isFinite(age) && row.age !== null && row.age !== '' ? age : null;
73
+ };
74
+
75
+ /** The form's age bands for the Tests (n) and Positive (n) summaries */
76
+ export const AGE_BANDS: Array<{ label: string; min: number; max?: number }> = [
77
+ { label: '1-9 yrs', min: 1, max: 9 },
78
+ { label: '10-14 yrs', min: 10, max: 14 },
79
+ { label: '15-19 yrs', min: 15, max: 19 },
80
+ { label: '20-24 yrs', min: 20, max: 24 },
81
+ { label: '25+ yrs', min: 25 },
82
+ ];
83
+
84
+ export const inBand = (row: Moh362Row, band: { min: number; max?: number }) => {
85
+ const age = ageOf(row);
86
+ return age !== null && age >= band.min && (band.max === undefined || age <= band.max);
87
+ };
@@ -0,0 +1,391 @@
1
+ import React from 'react';
2
+
3
+ import { usePageScale } from '../../common/report-page';
4
+ import {
5
+ formDate,
6
+ isDiagnosed,
7
+ isInitiated,
8
+ isInvestigated,
9
+ text,
10
+ type PresumptiveTbRow,
11
+ } from './presumptive-tb.types';
12
+ import styles from './presumptive-tb-register.scss';
13
+
14
+ interface PresumptiveTbRegisterProps {
15
+ rows: PresumptiveTbRow[];
16
+ }
17
+
18
+ /** Rows ruled on the last page, which also holds the page summary and code tables, as on the form */
19
+ const ROWS_ON_LAST_PAGE = 9;
20
+
21
+ /** Rows on every page before it: the footer's space ruled as further rows */
22
+ const ROWS_PER_FULL_PAGE = 15;
23
+
24
+ /** A3 landscape less its 8mm margins: the page as drawn on screen, inside the report card */
25
+ const PAGE_WIDTH_MM = 420 - 2 * 8;
26
+
27
+ /** The column letters across the top, as printed (R twice: TPT and Remarks) */
28
+ const LETTERS = 'ABCDEFGHIJKLMNOPQRR'.split('');
29
+
30
+ /** Column widths, measured off page 4 of the form, as fractions of the sheet */
31
+ const COLUMN_WIDTHS = [71, 98, 75, 151, 76, 79, 108, 105, 102, 91, 93, 104, 107, 93, 115, 106, 120, 79, 85];
32
+ const COLUMN_TOTAL = COLUMN_WIDTHS.reduce((sum, width) => sum + width, 0);
33
+
34
+ /** The form's code tables, beneath the register on the last page */
35
+ const MICROSCOPY_RESULTS: Array<[string, string]> = [
36
+ ['Microscopy', 'TB LAMP'],
37
+ ['0', 'Negative'],
38
+ ['Actual Number', 'Positive'],
39
+ ['+', 'Indefinite'],
40
+ ['++', 'Invalid'],
41
+ ['+++', ''],
42
+ ];
43
+
44
+ const GENEXPERT_RESULTS: Array<[string, string]> = [
45
+ ['MTB + RR', 'Mycobacterium Tuberculosis positive, Rifampicin Resistant'],
46
+ ['MTB + RS', 'Mycobacterium Tuberculosis positive, Rifampicin Sensitive'],
47
+ ['MTB –Ve', 'Mycobacterium Tuberculosis Negative'],
48
+ ['Trace RR', 'Minimal traces of Mycobacterium Tuberculosis seen Rif Resistant'],
49
+ ['Trace RS', 'Minimal traces of Mycobacterium Tuberculosis seen Rif Sensitive'],
50
+ ['Trace RI', 'Minimal traces of Mycobacterium Tuberculosis seen Rif Indeterminate'],
51
+ ['ND', 'Not done'],
52
+ ['Invalid / No result / Error', ''],
53
+ ['TI', 'MTB detected Rif indeterminate'],
54
+ ];
55
+
56
+ const REFERRED_BY: Array<[string, string]> = [
57
+ ['Private Sector', 'PS'],
58
+ ['Self referral', 'SR'],
59
+ ['Contact Invitation', 'CI'],
60
+ ['Chemist/Pharmacy', 'CP'],
61
+ ['Community Health Volunteer', 'CHV'],
62
+ ['Informal Service Provider', 'ISP'],
63
+ ];
64
+
65
+ const INVESTIGATIONS = ['Microscopy', 'GeneXpert', 'TruNat', 'TB-LAMP', 'LF-LAM', ''];
66
+
67
+ /**
68
+ * Full pages of rows, then a last page of at most 9 rows that carries the
69
+ * summary. A full page never takes every remaining row, so the last page always
70
+ * has at least one case on it.
71
+ */
72
+ const paginate = (rows: PresumptiveTbRow[]) => {
73
+ const pages: PresumptiveTbRow[][] = [];
74
+ let start = 0;
75
+ while (rows.length - start > ROWS_ON_LAST_PAGE) {
76
+ const size = Math.min(ROWS_PER_FULL_PAGE, rows.length - start - 1);
77
+ pages.push(rows.slice(start, start + size));
78
+ start += size;
79
+ }
80
+ pages.push(rows.slice(start));
81
+ return pages;
82
+ };
83
+
84
+ interface CaseCell {
85
+ upper: React.ReactNode;
86
+ lower?: React.ReactNode;
87
+ className?: string;
88
+ split?: boolean;
89
+ }
90
+
91
+ /** One case: two ruled lines, the split columns holding an upper and a lower entry */
92
+ const CaseRows: React.FC<{ row?: PresumptiveTbRow; serial: number }> = ({ row, serial }) => {
93
+ /* each column's entry: a whole cell spanning both lines, or an upper and a
94
+ lower entry for a split column */
95
+ const whole = (content: React.ReactNode, className?: string): CaseCell => ({ upper: content, className });
96
+ const split = (upper: React.ReactNode, lower: React.ReactNode, className?: string): CaseCell => ({
97
+ upper,
98
+ lower,
99
+ className,
100
+ split: true,
101
+ });
102
+ const clamp = (value: unknown) => <span className={styles.clamp}>{text(value)}</span>;
103
+
104
+ const cells = row
105
+ ? [
106
+ whole(text(row.serial_number) || serial, styles.centre),
107
+ whole(text(row.department_number), styles.centre),
108
+ whole(formDate(row.date), styles.centre),
109
+ whole(clamp(row.full_name), styles.left),
110
+ whole(text(row.sex), styles.centre),
111
+ whole(text(row.age), styles.centre),
112
+ split(text(row.national_id), text(row.cr_number), `${styles.centre} ${styles.idCell}`),
113
+ split(text(row.physical_address), text(row.phone_number), styles.left),
114
+ whole(text(row.referred_by), styles.centre),
115
+ split(text(row.hiv_result), formDate(row.hiv_test_date), styles.centre),
116
+ whole(
117
+ <>
118
+ <span className={styles.stacked}>{text(row.sample_type)}</span>
119
+ <span className={styles.stacked}>{formDate(row.sample_date)}</span>
120
+ </>,
121
+ styles.centre,
122
+ ),
123
+ split(text(row.xray_result), formDate(row.xray_date), styles.centre),
124
+ split(text(row.other_radiology_result), formDate(row.other_radiology_date), styles.centre),
125
+ split(text(row.molecular_result), formDate(row.molecular_date), styles.centre),
126
+ split(text(row.microscopy_result), formDate(row.microscopy_date), styles.centre),
127
+ whole(text(row.outcome), styles.centre),
128
+ split(text(row.started_tb_treatment), text(row.tb_registration), styles.centre),
129
+ split(text(row.tpt_started), text(row.tpt_registration), styles.centre),
130
+ whole(clamp(row.remarks), styles.left),
131
+ ]
132
+ : [
133
+ whole(''),
134
+ whole(''),
135
+ whole(''),
136
+ whole(''),
137
+ whole(''),
138
+ whole(''),
139
+ split('', ''),
140
+ split('', ''),
141
+ whole(''),
142
+ split('', ''),
143
+ whole(''),
144
+ split('', ''),
145
+ split('', ''),
146
+ split('', ''),
147
+ split('', ''),
148
+ whole(''),
149
+ split('', ''),
150
+ split('', ''),
151
+ whole(''),
152
+ ];
153
+
154
+ return (
155
+ <>
156
+ <tr className={styles.upperLine}>
157
+ {cells.map((cell, index) =>
158
+ cell.split ? (
159
+ <td key={index} className={cell.className}>
160
+ {cell.upper}
161
+ </td>
162
+ ) : (
163
+ <td key={index} rowSpan={2} className={cell.className}>
164
+ {cell.upper}
165
+ </td>
166
+ ),
167
+ )}
168
+ </tr>
169
+ <tr className={styles.lowerLine}>
170
+ {cells.map((cell, index) =>
171
+ cell.split ? (
172
+ <td key={index} className={cell.className}>
173
+ {cell.lower}
174
+ </td>
175
+ ) : null,
176
+ )}
177
+ </tr>
178
+ </>
179
+ );
180
+ };
181
+
182
+ /** The page summary and the form's code tables, beneath the register on the last page */
183
+ const Footer: React.FC<{ rows: PresumptiveTbRow[] }> = ({ rows }) => {
184
+ const figures: Array<[string, string, number]> = [
185
+ ['of presumptive TB cases', 'identified', rows.length],
186
+ ['of presumptive TB cases', 'investigated for TB', rows.filter(isInvestigated).length],
187
+ ['of presumptive TB cases', 'diagnosed with TB', rows.filter(isDiagnosed).length],
188
+ ['of diagnosed TB cases initiated', 'on treatment', rows.filter(isInitiated).length],
189
+ ];
190
+
191
+ return (
192
+ <div className={styles.footer}>
193
+ <div className={styles.pageSummary}>
194
+ <h5>Page summary</h5>
195
+ {figures.map(([lead, tail, value]) => (
196
+ <p key={tail}>
197
+ <strong>Number</strong> {lead}
198
+ <br />
199
+ {tail} <span className={styles.blank}>{value}</span>
200
+ </p>
201
+ ))}
202
+ </div>
203
+
204
+ <table className={`${styles.codeTable} ${styles.results}`}>
205
+ <thead>
206
+ <tr>
207
+ <th colSpan={2}>Results</th>
208
+ </tr>
209
+ </thead>
210
+ <tbody>
211
+ {MICROSCOPY_RESULTS.map(([microscopy, lamp]) => (
212
+ <tr key={microscopy}>
213
+ <td>{microscopy}</td>
214
+ <td>{lamp}</td>
215
+ </tr>
216
+ ))}
217
+ </tbody>
218
+ </table>
219
+
220
+ <table className={`${styles.codeTable} ${styles.labCodes}`}>
221
+ <thead>
222
+ <tr>
223
+ <th>
224
+ Lab Result
225
+ <br />
226
+ (Code)
227
+ </th>
228
+ <th colSpan={2}>Gene Xpert test results</th>
229
+ </tr>
230
+ </thead>
231
+ <tbody>
232
+ {GENEXPERT_RESULTS.map(([code, meaning]) => (
233
+ <tr key={code}>
234
+ <td />
235
+ <td>{code}</td>
236
+ <td>{meaning}</td>
237
+ </tr>
238
+ ))}
239
+ </tbody>
240
+ </table>
241
+
242
+ <table className={`${styles.codeTable} ${styles.referredBy}`}>
243
+ <thead>
244
+ <tr>
245
+ <th colSpan={2}>Patient referred by</th>
246
+ </tr>
247
+ </thead>
248
+ <tbody>
249
+ {REFERRED_BY.map(([label, code]) => (
250
+ <tr key={code}>
251
+ <td>{label}</td>
252
+ <td>{code}</td>
253
+ </tr>
254
+ ))}
255
+ </tbody>
256
+ </table>
257
+
258
+ <table className={`${styles.codeTable} ${styles.investigations}`}>
259
+ <thead>
260
+ <tr>
261
+ <th>Investigations Done</th>
262
+ </tr>
263
+ </thead>
264
+ <tbody>
265
+ {INVESTIGATIONS.map((name, index) => (
266
+ <tr key={index}>
267
+ <td>{name}</td>
268
+ </tr>
269
+ ))}
270
+ </tbody>
271
+ </table>
272
+ </div>
273
+ );
274
+ };
275
+
276
+ /**
277
+ * One A3 landscape page of the register. Pages before the last are ruled with
278
+ * cases to the foot; the last has 9 rows with the page summary of the whole
279
+ * register and the code tables beneath them.
280
+ */
281
+ const RegisterPage: React.FC<{
282
+ rows: PresumptiveTbRow[];
283
+ firstSerial: number;
284
+ page: number;
285
+ pages: number;
286
+ scale: number;
287
+ /** every case in the register, given only to the last page */
288
+ summaryRows?: PresumptiveTbRow[];
289
+ }> = ({ rows, firstSerial, page, pages, scale, summaryRows }) => (
290
+ <section className={styles.page} data-register-page style={{ '--page-zoom': scale } as React.CSSProperties}>
291
+ <table className={styles.register}>
292
+ <colgroup>
293
+ {COLUMN_WIDTHS.map((width, index) => (
294
+ <col key={index} style={{ width: `${(width / COLUMN_TOTAL) * 100}%` }} />
295
+ ))}
296
+ </colgroup>
297
+ <thead>
298
+ <tr className={styles.letters}>
299
+ {LETTERS.map((letter, index) => (
300
+ <th key={index}>{letter}</th>
301
+ ))}
302
+ </tr>
303
+ <tr className={styles.heading1}>
304
+ <th rowSpan={3}>Serial No</th>
305
+ <th rowSpan={3}>Patient department No. e.g. Patient CCC or OPD No.</th>
306
+ <th rowSpan={3}>Date</th>
307
+ <th rowSpan={3}>Full Names (First/ Middle/Last)</th>
308
+ <th rowSpan={3}>Sex (M/F/ Others)</th>
309
+ <th rowSpan={3}>Age in Yr/ Month</th>
310
+ <th>Nationality and National ID/UPI/ NEMIS No.</th>
311
+ <th rowSpan={3}>Physical Address and Phone number</th>
312
+ <th rowSpan={3}>Referred by (PS /SR /CI / CP / CHV / ISP)</th>
313
+ <th rowSpan={3}>HIV test results (Pos/ Neg/ Not Done) and Date of test</th>
314
+ <th>Date Sputum collected</th>
315
+ <th colSpan={4} className={styles.leftHeading}>
316
+ Write the Investigation Done, Results and Date of results
317
+ </th>
318
+ <th rowSpan={3}>Outcome of Investigation (DSTB, DRTB or No TB)</th>
319
+ <th>Started anti- TB treatment (Yes/No)</th>
320
+ <th>TPT started (Yes/ No)</th>
321
+ <th rowSpan={3}>Remarks</th>
322
+ </tr>
323
+ <tr className={styles.heading2}>
324
+ <th rowSpan={2} />
325
+ <th>Type of Sample (code)</th>
326
+ <th className={styles.leftHeading}>X-ray (N/ SG/AO/ND)</th>
327
+ <th className={styles.leftHeading}>Other Radiological test(s) done</th>
328
+ <th className={styles.leftHeading}>Molecular / Culture test(s)</th>
329
+ <th className={styles.leftHeading}>Microscopy/ TB LAMP</th>
330
+ <th rowSpan={2} className={styles.leftHeading}>
331
+ If Yes, Sub- county TB Registration No. &amp; start date
332
+ </th>
333
+ <th rowSpan={2} className={styles.leftHeading}>
334
+ If Yes, TPT Sub- county register No. &amp; start date
335
+ </th>
336
+ </tr>
337
+ <tr className={styles.heading3}>
338
+ <th>Date Sputum Sample collected</th>
339
+ <th />
340
+ <th />
341
+ <th />
342
+ <th />
343
+ </tr>
344
+ </thead>
345
+ <tbody>
346
+ {Array.from({ length: summaryRows ? ROWS_ON_LAST_PAGE : ROWS_PER_FULL_PAGE }, (_, index) => (
347
+ <CaseRows key={index} row={rows[index]} serial={firstSerial + index} />
348
+ ))}
349
+ </tbody>
350
+ </table>
351
+
352
+ {summaryRows && <Footer rows={summaryRows} />}
353
+
354
+ <div className={styles.pageFoot}>
355
+ <span>
356
+ Page {page} of {pages}
357
+ </span>
358
+ <span>MOH/REG/DNTLDP/02 · February 2023</span>
359
+ </div>
360
+ </section>
361
+ );
362
+
363
+ /**
364
+ * PRESUMPTIVE TB REGISTER (MOH/REG/DNTLDP/02), page 4 of the form, as A3
365
+ * landscape pages of cases, columns A to R. The page summary counts the whole
366
+ * register, so it appears once, at the foot of the last page, with the form's
367
+ * code tables.
368
+ */
369
+ const PresumptiveTbRegister: React.FC<PresumptiveTbRegisterProps> = ({ rows }) => {
370
+ const { ref, scale } = usePageScale<HTMLDivElement>(PAGE_WIDTH_MM);
371
+ const pages = paginate(rows);
372
+ const serials = pages.map((_, index) => pages.slice(0, index).reduce((sum, page) => sum + page.length, 0) + 1);
373
+
374
+ return (
375
+ <div className={styles.viewport} ref={ref}>
376
+ {pages.map((pageRows, index) => (
377
+ <RegisterPage
378
+ key={index}
379
+ rows={pageRows}
380
+ firstSerial={serials[index]}
381
+ page={index + 1}
382
+ pages={pages.length}
383
+ scale={scale}
384
+ summaryRows={index === pages.length - 1 ? rows : undefined}
385
+ />
386
+ ))}
387
+ </div>
388
+ );
389
+ };
390
+
391
+ export default PresumptiveTbRegister;