@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,164 @@
1
+ import React, { useState } from 'react';
2
+ import { Button, Layer, Pagination } from '@carbon/react';
3
+ import { Document, Download } from '@carbon/react/icons';
4
+ import { useSession } from '@openmrs/esm-framework';
5
+
6
+ import { ReportFiltersComponent, type ReportPeriod, useRestorePeriod } from '../../common/report-filters';
7
+ import { showReportError } from '../../common/report-error';
8
+ import { ReportPlaceholder, useGeneratedLocation } from '../../common/report-placeholder';
9
+ import { ReportSkeleton } from '../../common/report-skeleton';
10
+ import { columnsToCsv, downloadCsv, usePatientList } from '../../common/report-register';
11
+ import { getMoh706PatientList } from '../../resources/moh-706.resource';
12
+ import { MOH_240_SUB_HEADERS, moh240Columns } from '../../reports/moh-240/moh-240.columns';
13
+ import MOH240Header from '../../reports/moh-240/moh-240-header.component';
14
+ import sheetStyles from '../../reports/moh-240/moh-240.scss';
15
+ import Moh240PageSummary from '../../reports/moh-240/sub-reports/page-summary.component';
16
+ import Moh240Register from '../../reports/moh-240/sub-reports/moh-240-register.component';
17
+ import styles from './moh-240-register.scss';
18
+
19
+ /** The register's own columns (A-AA). No drill-down indicator runs here, so the
20
+ results column reads the row's translated `results` field. */
21
+ const columns = moh240Columns('');
22
+
23
+ /**
24
+ * Laboratory (LAB) Register MOH 240, opened from the landing page's Registers
25
+ * tab. It shares the reports' filter bar and serves the same printed register
26
+ * -- masthead, columns A-AA and the page summary -- that the MOH 505 and
27
+ * MOH 706 drill-downs open, but over the laboratory's whole workload for the
28
+ * period rather than one indicator's rows.
29
+ */
30
+ const Moh240LabRegister: React.FC = () => {
31
+ const [period, setPeriod] = useState<{ startDate: string; endDate: string }>({ startDate: '', endDate: '' });
32
+ const [isExporting, setIsExporting] = useState(false);
33
+ /* A fetch that failed leaves the page on the placeholder rather than showing
34
+ an empty register, so the two are told apart here. */
35
+ const [hasError, setHasError] = useState(false);
36
+
37
+ const session = useSession();
38
+ const locationUuids = session?.sessionLocation?.uuid ?? '';
39
+ /* Only a register for the current location counts */
40
+ const { isForCurrentLocation, hasLocationChanged, markGenerated } = useGeneratedLocation('moh240RegisterLocation');
41
+ const isGenerated = Boolean(period.startDate && period.endDate) && isForCurrentLocation && !hasError;
42
+
43
+ const { rows, total, isTotalExact, isLoading, page, pageSize, onPageChange, fetchAll } = usePatientList(
44
+ ({ startIndex, limit }) => {
45
+ /* Nothing is fetched before a period is chosen: the mount-time load and
46
+ an unset session land here rather than querying the endpoint's defaults. */
47
+ if (!period.startDate || !period.endDate) {
48
+ return Promise.resolve({ results: [], total: 0, isTotalExact: true });
49
+ }
50
+
51
+ return getMoh706PatientList({
52
+ locationUuids,
53
+ startDate: period.startDate,
54
+ endDate: period.endDate,
55
+ startIndex,
56
+ limit,
57
+ }).then(
58
+ (result) => {
59
+ setHasError(false);
60
+ return result;
61
+ },
62
+ (error) => {
63
+ setHasError(true);
64
+ throw error;
65
+ },
66
+ );
67
+ },
68
+ [locationUuids, period.startDate, period.endDate],
69
+ );
70
+
71
+ const generate = ({ startDate, endDate }: ReportPeriod) => {
72
+ setHasError(false);
73
+ setPeriod({ startDate, endDate });
74
+ markGenerated(locationUuids);
75
+ };
76
+
77
+ useRestorePeriod(generate);
78
+
79
+ /* Exports every page, not the one on screen: with pagination the DOM only
80
+ holds one page, and the file must match the printed form column for column. */
81
+ const exportCsv = async () => {
82
+ setIsExporting(true);
83
+
84
+ try {
85
+ const all = await fetchAll();
86
+ const slug = ['laboratory-lab-register-moh-240', period.startDate, period.endDate]
87
+ .filter(Boolean)
88
+ .join('-')
89
+ .toLowerCase();
90
+ downloadCsv(columnsToCsv(all, columns, { subHeaderRow: MOH_240_SUB_HEADERS }), `${slug}.csv`);
91
+ } catch (error) {
92
+ showReportError('the register export', error);
93
+ } finally {
94
+ setIsExporting(false);
95
+ }
96
+ };
97
+
98
+ return (
99
+ <>
100
+ <ReportFiltersComponent
101
+ reportName="Laboratory (LAB) Register MOH 240"
102
+ mode="both"
103
+ onGenerate={generate}
104
+ isLoading={isLoading}
105
+ isReportGenerated={isGenerated}
106
+ generatedPeriod={period}
107
+ defaultTab="custom"
108
+ backLink={{ label: 'All registers', to: '/?tab=registers' }}
109
+ />
110
+ {isGenerated && isLoading && rows.length === 0 && <ReportSkeleton />}
111
+ {isGenerated && !isLoading && (
112
+ <>
113
+ <div className={styles.toolbar}>
114
+ <span className={styles.total}>
115
+ {total.toLocaleString()}
116
+ {isTotalExact ? '' : '+'} records
117
+ </span>
118
+ <Button kind="tertiary" size="sm" renderIcon={Download} onClick={exportCsv} disabled={isExporting}>
119
+ {isExporting ? 'Preparing…' : 'Export CSV'}
120
+ </Button>
121
+ </div>
122
+ {rows.length > 0 ? (
123
+ <>
124
+ <div className={sheetStyles.sheet}>
125
+ <MOH240Header
126
+ facility={session?.sessionLocation?.display ?? ''}
127
+ startDate={period.startDate}
128
+ endDate={period.endDate}
129
+ />
130
+ <Moh240Register patientList={rows} />
131
+ <Moh240PageSummary rows={rows} />
132
+ </div>
133
+ {typeof total === 'number' && total > pageSize && (
134
+ <Pagination
135
+ className={styles.pager}
136
+ page={page}
137
+ pageSize={pageSize}
138
+ pageSizes={[25, 50, 100, 200]}
139
+ totalItems={total}
140
+ size="sm"
141
+ onChange={onPageChange}
142
+ />
143
+ )}
144
+ </>
145
+ ) : (
146
+ <Layer className={styles.empty}>
147
+ <Document size={32} className={styles.emptyIcon} />
148
+ <h4>No records to list</h4>
149
+ <p>No laboratory entries matched the selected period and facility.</p>
150
+ </Layer>
151
+ )}
152
+ </>
153
+ )}
154
+ {!isGenerated && (
155
+ <ReportPlaceholder
156
+ reportName="MOH 240 laboratory register"
157
+ isLocationChanged={Boolean(period.startDate) && hasLocationChanged}
158
+ />
159
+ )}
160
+ </>
161
+ );
162
+ };
163
+
164
+ export default Moh240LabRegister;
@@ -0,0 +1,407 @@
1
+ import React from 'react';
2
+
3
+ import { usePageScale } from '../../common/report-page';
4
+ import { AGE_BANDS, METHODS, moh267Summary, type AgeSexCounts, type Population } from './moh-267-summary';
5
+ import { formDate, text, type Moh267Row } from './moh-267.types';
6
+ import styles from './moh-267-register.scss';
7
+
8
+ interface Moh267RegisterProps {
9
+ rows: Moh267Row[];
10
+ /** the period's last day, for the clients on PrEP at least 3 months */
11
+ endDate: string;
12
+ /** the visits in the calendar month of the period's end, for TOTAL this Month */
13
+ monthVisits: number;
14
+ }
15
+
16
+ /** Rows ruled on the last page, which also holds the summary tables, as on the form */
17
+ const ROWS_ON_LAST_PAGE = 15;
18
+
19
+ /** Rows on every page before it: the summary's space ruled as further rows */
20
+ const ROWS_PER_FULL_PAGE = 22;
21
+
22
+ /** A4 landscape less its 8mm margins: the page as drawn on screen, inside the report card */
23
+ const PAGE_WIDTH_MM = 297 - 2 * 8;
24
+
25
+ const COLUMN_IDS = 'abcdefghijk'.split('');
26
+
27
+ /** Column widths, measured off page 3 of the form, as fractions of the sheet */
28
+ const COLUMN_WIDTHS = [147, 166, 199, 104, 115, 203, 150, 219, 165, 137, 244];
29
+ const COLUMN_TOTAL = COLUMN_WIDTHS.reduce((sum, width) => sum + width, 0);
30
+
31
+ /**
32
+ * The summary tables' rows, as each table words them, and the register's
33
+ * population each counts. MSM/MSW are male and FSW female, so the other sex's
34
+ * cells are shaded as on the form.
35
+ */
36
+ type SummaryRow = { label: string; population: Population | 'TOTAL'; only?: 'M' | 'F' };
37
+ const summaryRows = (pwid: string, vulnerable: string): SummaryRow[] => [
38
+ { label: 'General popn', population: 'General Population' },
39
+ { label: 'MSM/MSW', population: 'MSM/MSW', only: 'M' },
40
+ { label: 'FSW', population: 'FSW', only: 'F' },
41
+ { label: pwid, population: 'PWID/PWUD' },
42
+ { label: 'Discordant Couple', population: 'Discordant Couple' },
43
+ { label: vulnerable, population: 'VP' },
44
+ { label: 'TOTAL', population: 'TOTAL' },
45
+ ];
46
+
47
+ /**
48
+ * Full pages of rows, then a last page of at most 15 rows that carries the
49
+ * summary. A full page never takes every remaining row, so the last page always
50
+ * has at least one visit on it.
51
+ */
52
+ const paginate = (rows: Moh267Row[]) => {
53
+ const pages: Moh267Row[][] = [];
54
+ let start = 0;
55
+ while (rows.length - start > ROWS_ON_LAST_PAGE) {
56
+ const size = Math.min(ROWS_PER_FULL_PAGE, rows.length - start - 1);
57
+ pages.push(rows.slice(start, start + size));
58
+ start += size;
59
+ }
60
+ pages.push(rows.slice(start));
61
+ return pages;
62
+ };
63
+
64
+ /** One visit: two ruled lines, column (c) split into national ID over NUPI */
65
+ const VisitRows: React.FC<{ row?: Moh267Row }> = ({ row }) => (
66
+ <>
67
+ <tr className={styles.upperLine}>
68
+ <td rowSpan={2}>{text(row?.prep_number)}</td>
69
+ <td rowSpan={2}>{formDate(row?.date_of_visit)}</td>
70
+ <td className={styles.upperCell}>{text(row?.id_number)}</td>
71
+ <td rowSpan={2}>{text(row?.age)}</td>
72
+ <td rowSpan={2}>{text(row?.sex)}</td>
73
+ <td rowSpan={2}>{text(row?.population_type)}</td>
74
+ <td rowSpan={2}>{text(row?.prep_status)}</td>
75
+ <td rowSpan={2}>{text(row?.prep_method)}</td>
76
+ <td rowSpan={2}>{text(row?.hiv_result)}</td>
77
+ <td rowSpan={2}>{text(row?.sti_diagnosed)}</td>
78
+ <td rowSpan={2} className={styles.left}>
79
+ <span className={styles.clamp}>{text(row?.remarks)}</span>
80
+ </td>
81
+ </tr>
82
+ <tr className={styles.lowerLine}>
83
+ <td className={styles.lowerCell}>{text(row?.nupi)}</td>
84
+ </tr>
85
+ </>
86
+ );
87
+
88
+ /**
89
+ * A total row's figure: the number of visits -- the page's, or the calendar
90
+ * month's -- in the first column after the label, the rest of the row left
91
+ * ruled and blank
92
+ */
93
+ const VisitTotals: React.FC<{ visits: number }> = ({ visits }) => (
94
+ <>
95
+ <td>{visits}</td>
96
+ {COLUMN_IDS.slice(4).map((id) => (
97
+ <td key={id} />
98
+ ))}
99
+ </>
100
+ );
101
+
102
+ /** An age-band-by-sex summary table: initiated on PrEP, current on PrEP, diagnosed with STI */
103
+ const AgeSexTable: React.FC<{
104
+ title: string;
105
+ corner?: React.ReactNode;
106
+ rows: SummaryRow[];
107
+ counts: AgeSexCounts;
108
+ }> = ({ title, corner, rows, counts }) => (
109
+ <table className={`${styles.summaryTable} ${styles.ageTable}`}>
110
+ <thead>
111
+ <tr>
112
+ <th rowSpan={3} className={styles.rowLabel}>
113
+ {corner}
114
+ </th>
115
+ <th colSpan={9}>{title}</th>
116
+ <th />
117
+ </tr>
118
+ <tr>
119
+ {AGE_BANDS.map((band) => (
120
+ <th key={band.label} colSpan={2}>
121
+ {band.label}
122
+ </th>
123
+ ))}
124
+ </tr>
125
+ <tr>
126
+ {AGE_BANDS.map((band) => (
127
+ <React.Fragment key={band.label}>
128
+ <th>M</th>
129
+ <th>F</th>
130
+ </React.Fragment>
131
+ ))}
132
+ </tr>
133
+ </thead>
134
+ <tbody>
135
+ {rows.map((population) => (
136
+ <tr key={population.label}>
137
+ <th className={styles.rowLabel}>{population.label}</th>
138
+ {AGE_BANDS.map((band, bandIndex) =>
139
+ (['M', 'F'] as const).map((sex) =>
140
+ population.only && population.only !== sex ? (
141
+ <td key={`${band.label}-${sex}`} className={styles.notApplicable} />
142
+ ) : (
143
+ <td key={`${band.label}-${sex}`}>{counts(population.population, bandIndex, sex)}</td>
144
+ ),
145
+ ),
146
+ )}
147
+ </tr>
148
+ ))}
149
+ </tbody>
150
+ </table>
151
+ );
152
+
153
+ /** The summary tables at the foot of the last page, counting every visit in the register */
154
+ const Summary: React.FC<{ rows: Moh267Row[]; endDate: string }> = ({ rows, endDate }) => {
155
+ const summary = moh267Summary(rows, endDate);
156
+ return (
157
+ <div className={styles.summary}>
158
+ <AgeSexTable
159
+ title="Number initiated (New) on PrEP (g)"
160
+ rows={summaryRows('PWID/PWUD', 'Vulnerable Pop.')}
161
+ counts={summary.initiated}
162
+ />
163
+ <AgeSexTable
164
+ title="Number Current on PrEP"
165
+ corner={
166
+ <>
167
+ New +<br />
168
+ Refill +<br />
169
+ Restart
170
+ </>
171
+ }
172
+ rows={summaryRows('PWID', 'VP')}
173
+ counts={summary.current}
174
+ />
175
+ <AgeSexTable
176
+ title="Number diagnosed with STI (j)"
177
+ rows={summaryRows('PWID', 'Vulnerable Pop.')}
178
+ counts={summary.sti}
179
+ />
180
+
181
+ <div className={styles.methodColumn}>
182
+ <table className={`${styles.summaryTable} ${styles.methodTable}`}>
183
+ <thead>
184
+ <tr>
185
+ <th rowSpan={2} className={styles.rowLabel}>
186
+ Indicator/
187
+ <br />
188
+ PrEP method
189
+ </th>
190
+ <th colSpan={5}>Number of Clients on each PrEP method (h)</th>
191
+ </tr>
192
+ <tr>
193
+ <th>
194
+ Event
195
+ <br />
196
+ Driven
197
+ </th>
198
+ <th>
199
+ Daily
200
+ <br />
201
+ Oral
202
+ </th>
203
+ <th>
204
+ Dapivirine
205
+ <br />
206
+ Vaginal
207
+ </th>
208
+ <th>
209
+ Cabotegravir
210
+ <br />
211
+ injectable
212
+ </th>
213
+ <th>Total</th>
214
+ </tr>
215
+ </thead>
216
+ <tbody>
217
+ {summary.methods.map((indicator) => (
218
+ <tr key={indicator.label}>
219
+ <th className={styles.rowLabel}>{indicator.label}</th>
220
+ {METHODS.map((method) => (
221
+ <td key={method}>{indicator.count(method)}</td>
222
+ ))}
223
+ {/* every method, Lenacapavir included, which has no column of its own */}
224
+ <td>{indicator.count('Total')}</td>
225
+ </tr>
226
+ ))}
227
+ </tbody>
228
+ </table>
229
+
230
+ <table className={`${styles.summaryTable} ${styles.seroconverting}`}>
231
+ <tbody>
232
+ <tr>
233
+ <th>Number Seroconverting while on PrEP (i)</th>
234
+ <td>{summary.seroconverted}</td>
235
+ </tr>
236
+ </tbody>
237
+ </table>
238
+ </div>
239
+ </div>
240
+ );
241
+ };
242
+
243
+ /**
244
+ * One A4 landscape page of the register. Pages before the last are ruled with
245
+ * visits and their page total; the last has 15 rows, the page and month
246
+ * totals, and the summary tables beneath, in one frame.
247
+ */
248
+ const RegisterPage: React.FC<{
249
+ rows: Moh267Row[];
250
+ page: number;
251
+ pages: number;
252
+ scale: number;
253
+ /** every visit in the register and the period's end, given only to the last page */
254
+ summary?: { rows: Moh267Row[]; endDate: string; monthVisits: number };
255
+ }> = ({ rows, page, pages, scale, summary }) => {
256
+ const isLast = summary !== undefined;
257
+ return (
258
+ <section className={styles.page} data-register-page style={{ '--page-zoom': scale } as React.CSSProperties}>
259
+ <div className={styles.runningHead}>
260
+ <span>Ministry Of Health</span>
261
+ <span>MOH 267 PrEP Daily Activity Register</span>
262
+ <span>Ver. July 2023</span>
263
+ </div>
264
+
265
+ <div className={isLast ? `${styles.frame} ${styles.lastFrame}` : styles.frame}>
266
+ <table className={styles.register}>
267
+ <colgroup>
268
+ {COLUMN_WIDTHS.map((width, index) => (
269
+ <col key={index} style={{ width: `${(width / COLUMN_TOTAL) * 100}%` }} />
270
+ ))}
271
+ </colgroup>
272
+ <thead>
273
+ <tr className={styles.headings}>
274
+ <th>PrEP No</th>
275
+ <th>Date of Visit</th>
276
+ <th className={styles.splitHeader}>
277
+ <div className={styles.splitStack}>
278
+ <span>National Identification Number</span>
279
+ <span>National Unique Patient Identifier (NUPI)</span>
280
+ </div>
281
+ </th>
282
+ <th>Age</th>
283
+ <th>
284
+ Sex
285
+ <br />
286
+ (M/F)
287
+ </th>
288
+ <th className={styles.codeHeading}>
289
+ <strong>Population type</strong>
290
+ <br />
291
+ (Use the codes):
292
+ <br />
293
+ 1. General Population
294
+ <br />
295
+ 2. Discordant Couple
296
+ <br />
297
+ 3. MSM/MSW
298
+ <br />
299
+ 4. FSW
300
+ <br />
301
+ 5. PWID/PWUD
302
+ <br />
303
+ 6. VP
304
+ </th>
305
+ <th className={styles.codeHeading}>
306
+ <strong>Client PrEP Status:</strong>
307
+ <br />
308
+ <strong>N</strong>- New
309
+ <br />
310
+ <strong>C</strong>- Continuing
311
+ <br />
312
+ <strong>D</strong>-Discontinued
313
+ <br />
314
+ <strong>R</strong>-Restart
315
+ </th>
316
+ <th className={styles.codeHeading}>
317
+ <strong>PrEP Method:</strong>
318
+ <br />
319
+ <strong>1.</strong> Daily oral PrEP
320
+ <br />
321
+ <strong>2.</strong> Event driven oral PrEP
322
+ <br />
323
+ <strong>3.</strong> Dapivirine Vaginal ring
324
+ <br />
325
+ <strong>4.</strong> Cabotegravir Injectable
326
+ </th>
327
+ <th>
328
+ HIV Results
329
+ <br />
330
+ <span className={styles.codes}>(N/P/Ic/NA)</span>
331
+ </th>
332
+ <th>
333
+ Diagnosed with
334
+ <br />
335
+ STI
336
+ <br />
337
+ <br />
338
+ <span className={styles.codes}>(N/Y)</span>
339
+ </th>
340
+ <th>Remarks</th>
341
+ </tr>
342
+ <tr className={styles.columnIds}>
343
+ {COLUMN_IDS.map((id) => (
344
+ <th key={id}>({id})</th>
345
+ ))}
346
+ </tr>
347
+ </thead>
348
+ <tbody>
349
+ {Array.from({ length: isLast ? ROWS_ON_LAST_PAGE : ROWS_PER_FULL_PAGE }, (_, index) => (
350
+ <VisitRows key={index} row={rows[index]} />
351
+ ))}
352
+ <tr className={styles.totalRow}>
353
+ <th colSpan={3} className={styles.left}>
354
+ TOTAL this Page
355
+ </th>
356
+ <VisitTotals visits={rows.length} />
357
+ </tr>
358
+ {isLast && (
359
+ <tr className={styles.totalRow}>
360
+ <th colSpan={3} className={styles.left}>
361
+ TOTAL this Month
362
+ </th>
363
+ <VisitTotals visits={summary.monthVisits} />
364
+ </tr>
365
+ )}
366
+ </tbody>
367
+ </table>
368
+
369
+ {summary && <Summary rows={summary.rows} endDate={summary.endDate} />}
370
+ </div>
371
+
372
+ <div className={styles.pageFoot}>
373
+ <span>
374
+ Page {page} of {pages}
375
+ </span>
376
+ <span>Ver. July 2023</span>
377
+ </div>
378
+ </section>
379
+ );
380
+ };
381
+
382
+ /**
383
+ * MOH 267 PrEP DAILY ACTIVITY REGISTER (Ver. July 2023), page 3 of the form,
384
+ * as A4 landscape pages of visits, columns (a) to (k). The summary tables sum
385
+ * the whole register, so they appear once, at the foot of the last page.
386
+ */
387
+ const Moh267Register: React.FC<Moh267RegisterProps> = ({ rows, endDate, monthVisits }) => {
388
+ const { ref, scale } = usePageScale<HTMLDivElement>(PAGE_WIDTH_MM);
389
+ const pages = paginate(rows);
390
+
391
+ return (
392
+ <div className={styles.viewport} ref={ref}>
393
+ {pages.map((pageRows, index) => (
394
+ <RegisterPage
395
+ key={index}
396
+ rows={pageRows}
397
+ page={index + 1}
398
+ pages={pages.length}
399
+ scale={scale}
400
+ summary={index === pages.length - 1 ? { rows, endDate, monthVisits } : undefined}
401
+ />
402
+ ))}
403
+ </div>
404
+ );
405
+ };
406
+
407
+ export default Moh267Register;