@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,153 @@
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 TbLabRegisterTable from './tb-lab-register-table.component';
13
+ import { tbLabCsvColumns, type TbLabRow } from './tb-lab-register.columns';
14
+ import styles from './tb-lab-register-table.scss';
15
+
16
+ /**
17
+ * TB Laboratory Register, opened from the landing page's Registers tab. It
18
+ * shares the reports' filter bar and renders the printed template -- the
19
+ * two-tier header over the appearance and microscopy columns -- from the
20
+ * existing lab-706 dataset: rows come back filtered by the `total_tb_smear`
21
+ * indicator (the MOH 706 5.29 filter) until the ETL layer carries the rest of
22
+ * the TB workload, and the columns the dataset does not carry yet read blank.
23
+ */
24
+ const TbLabRegister: React.FC = () => {
25
+ const [period, setPeriod] = useState<{ startDate: string; endDate: string }>({ startDate: '', endDate: '' });
26
+ const [isExporting, setIsExporting] = useState(false);
27
+ /* A fetch that failed leaves the page on the placeholder rather than showing
28
+ an empty register, so the two are told apart here. */
29
+ const [hasError, setHasError] = useState(false);
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('tbLabRegisterLocation');
35
+ const isGenerated = Boolean(period.startDate && period.endDate) && isForCurrentLocation && !hasError;
36
+
37
+ const { rows, total, isTotalExact, isLoading, page, pageSize, onPageChange, fetchAll } = usePatientList(
38
+ ({ startIndex, limit }) => {
39
+ /* Nothing is fetched before a period is chosen: the mount-time load and
40
+ an unset session land here rather than querying the endpoint's defaults. */
41
+ if (!period.startDate || !period.endDate) {
42
+ return Promise.resolve({ results: [], total: 0, isTotalExact: true });
43
+ }
44
+
45
+ return getMoh706PatientList({
46
+ locationUuids,
47
+ startDate: period.startDate,
48
+ endDate: period.endDate,
49
+ indicators: 'total_tb_smear',
50
+ startIndex,
51
+ limit,
52
+ }).then(
53
+ (result) => {
54
+ setHasError(false);
55
+ return result;
56
+ },
57
+ (error) => {
58
+ setHasError(true);
59
+ throw error;
60
+ },
61
+ );
62
+ },
63
+ [locationUuids, period.startDate, period.endDate],
64
+ );
65
+
66
+ const generate = ({ startDate, endDate }: ReportPeriod) => {
67
+ setHasError(false);
68
+ setPeriod({ startDate, endDate });
69
+ markGenerated(locationUuids);
70
+ };
71
+
72
+ useRestorePeriod(generate);
73
+
74
+ /* Exports every page, not the one on screen: with pagination the DOM only
75
+ holds one page, and the file must match the printed register. */
76
+ const exportCsv = async () => {
77
+ setIsExporting(true);
78
+
79
+ try {
80
+ const all: TbLabRow[] = await fetchAll();
81
+ const slug = ['tb-laboratory-register', period.startDate, period.endDate].filter(Boolean).join('-').toLowerCase();
82
+ downloadCsv(columnsToCsv(all, tbLabCsvColumns()), `${slug}.csv`);
83
+ } catch (error) {
84
+ showReportError('the register export', error);
85
+ } finally {
86
+ setIsExporting(false);
87
+ }
88
+ };
89
+
90
+ return (
91
+ <>
92
+ <ReportFiltersComponent
93
+ reportName="TB Laboratory Register"
94
+ mode="both"
95
+ onGenerate={generate}
96
+ isLoading={isLoading}
97
+ isReportGenerated={isGenerated}
98
+ generatedPeriod={period}
99
+ defaultTab="custom"
100
+ backLink={{ label: 'All registers', to: '/?tab=registers' }}
101
+ />
102
+ {isGenerated && isLoading && rows.length === 0 && <ReportSkeleton />}
103
+ {isGenerated && !isLoading && (
104
+ <>
105
+ <div className={styles.toolbar}>
106
+ <span className={styles.total}>
107
+ {total.toLocaleString()}
108
+ {isTotalExact ? '' : '+'} records
109
+ </span>
110
+ <Button kind="tertiary" size="sm" renderIcon={Download} onClick={exportCsv} disabled={isExporting}>
111
+ {isExporting ? 'Preparing…' : 'Export CSV'}
112
+ </Button>
113
+ </div>
114
+ {rows.length > 0 ? (
115
+ <>
116
+ <TbLabRegisterTable
117
+ rows={rows}
118
+ facility={session?.sessionLocation?.display ?? ''}
119
+ startDate={period.startDate}
120
+ endDate={period.endDate}
121
+ />
122
+ {typeof total === 'number' && total > pageSize && (
123
+ <Pagination
124
+ className={styles.pager}
125
+ page={page}
126
+ pageSize={pageSize}
127
+ pageSizes={[25, 50, 100, 200]}
128
+ totalItems={total}
129
+ size="sm"
130
+ onChange={onPageChange}
131
+ />
132
+ )}
133
+ </>
134
+ ) : (
135
+ <Layer className={styles.empty}>
136
+ <Document size={32} className={styles.emptyIcon} />
137
+ <h4>No records to list</h4>
138
+ <p>No TB laboratory entries matched the selected period and facility.</p>
139
+ </Layer>
140
+ )}
141
+ </>
142
+ )}
143
+ {!isGenerated && (
144
+ <ReportPlaceholder
145
+ reportName="TB laboratory register"
146
+ isLocationChanged={Boolean(period.startDate) && hasLocationChanged}
147
+ />
148
+ )}
149
+ </>
150
+ );
151
+ };
152
+
153
+ export default TbLabRegister;
@@ -0,0 +1,164 @@
1
+ import React from 'react';
2
+
3
+ import { type Tb4Nutrition, type Tb4Row, text } from './tb4.types';
4
+ import styles from './tb4-register.scss';
5
+
6
+ /** Patients ruled on each page, on both facing pages, as on the form */
7
+ export const ROWS_PER_PAGE = 5;
8
+
9
+ /** A3 landscape less its 8mm margins: the page as drawn on screen, inside the report card */
10
+ export const PAGE_WIDTH_MM = 420 - 2 * 8;
11
+
12
+ export const paginate = (rows: Tb4Row[]) => {
13
+ const pages: Tb4Row[][] = [];
14
+ for (let start = 0; start < rows.length; start += ROWS_PER_PAGE) {
15
+ pages.push(rows.slice(start, start + ROWS_PER_PAGE));
16
+ }
17
+ return pages.length ? pages : [[]];
18
+ };
19
+
20
+ /** A box in a divided cell: a printed label filling it, a label in its corner, or an entry */
21
+ export interface Box {
22
+ heading?: string;
23
+ tag?: string;
24
+ value?: React.ReactNode;
25
+ /** relative width beside the other boxes of its part (default 1) */
26
+ grow?: number;
27
+ }
28
+
29
+ /** A part of a divided cell: its share of the row's height and the boxes across it */
30
+ export interface Part {
31
+ share: number;
32
+ boxes?: Box[];
33
+ }
34
+
35
+ /** A divided cell: parts stacked down the row, as printed */
36
+ export const Stack: React.FC<{ parts: Part[] }> = ({ parts }) => (
37
+ <div className={styles.stack} style={{ gridTemplateRows: parts.map((part) => `${part.share}fr`).join(' ') }}>
38
+ {parts.map((part, index) => (
39
+ <div key={index} className={styles.part}>
40
+ {(part.boxes ?? [{}]).map((box, boxIndex) => (
41
+ <div key={boxIndex} className={styles.box} style={box.grow ? { flexGrow: box.grow } : undefined}>
42
+ {box.heading && <span className={styles.heading}>{box.heading}</span>}
43
+ {box.tag && <span className={styles.tag}>{box.tag}</span>}
44
+ {box.value !== undefined && <span className={styles.boxValue}>{box.value}</span>}
45
+ </div>
46
+ ))}
47
+ </div>
48
+ ))}
49
+ </div>
50
+ );
51
+
52
+ /** A whole cell's entry */
53
+ export const Value: React.FC<{ children: React.ReactNode; left?: boolean; wrap?: boolean }> = ({
54
+ children,
55
+ left,
56
+ wrap,
57
+ }) => (
58
+ <span className={[styles.value, left && styles.valueLeft, wrap && styles.wrap].filter(Boolean).join(' ')}>
59
+ {children}
60
+ </span>
61
+ );
62
+
63
+ /**
64
+ * Nutritional status, as printed in (K) and (AD): weight beside height, then
65
+ * BMI/Z score, then MUAC
66
+ */
67
+ export const NutritionStack: React.FC<{ nutrition?: Tb4Nutrition | null }> = ({ nutrition }) => (
68
+ <Stack
69
+ parts={[
70
+ { share: 0.146, boxes: [{ tag: 'Weight' }, { tag: 'Height' }] },
71
+ { share: 0.295, boxes: [{ value: text(nutrition?.weight) }, { value: text(nutrition?.height) }] },
72
+ { share: 0.146, boxes: [{ tag: 'BMI/Z score', value: text(nutrition?.bmi) }] },
73
+ { share: 0.413, boxes: [{ tag: 'MUAC', value: text(nutrition?.muac) }] },
74
+ ]}
75
+ />
76
+ );
77
+
78
+ /**
79
+ * A key beneath the register: its heading across the top, then its rows. A row
80
+ * with fewer cells than the widest spans its last cell across the rest, and
81
+ * the rows listed in `subheadings` are set bold ("CT - Scan", "Comorbidities").
82
+ */
83
+ export const KeyTable: React.FC<{
84
+ title: string;
85
+ rows: Array<Array<React.ReactNode>>;
86
+ widthMm: number;
87
+ subheadings?: number[];
88
+ leftTitle?: boolean;
89
+ }> = ({ title, rows, widthMm, subheadings = [], leftTitle }) => {
90
+ const columns = Math.max(...rows.map((cells) => cells.length));
91
+ return (
92
+ <table
93
+ className={[styles.keyTable, leftTitle && styles.leftHeadingKey].filter(Boolean).join(' ')}
94
+ style={{ width: `${widthMm}mm` }}
95
+ >
96
+ <thead>
97
+ <tr>
98
+ <th colSpan={columns}>{title}</th>
99
+ </tr>
100
+ </thead>
101
+ <tbody>
102
+ {rows.map((cells, index) => (
103
+ <tr key={index} className={subheadings.includes(index) ? styles.keySubheading : undefined}>
104
+ {cells.map((cell, cellIndex) => (
105
+ <td
106
+ key={cellIndex}
107
+ colSpan={cells.length < columns && cellIndex === cells.length - 1 ? columns - cellIndex : 1}
108
+ >
109
+ {cell}
110
+ </td>
111
+ ))}
112
+ </tr>
113
+ ))}
114
+ </tbody>
115
+ </table>
116
+ );
117
+ };
118
+
119
+ /** Widths, measured off the form, as `<col>` percentages */
120
+ export const Columns: React.FC<{ widths: number[] }> = ({ widths }) => {
121
+ const total = widths.reduce((sum, width) => sum + width, 0);
122
+ return (
123
+ <colgroup>
124
+ {widths.map((width, index) => (
125
+ <col key={index} style={{ width: `${(width / total) * 100}%` }} />
126
+ ))}
127
+ </colgroup>
128
+ );
129
+ };
130
+
131
+ /** A column heading: its letter in bold over the printed heading */
132
+ export const Heading: React.FC<{
133
+ letter: string;
134
+ children?: React.ReactNode;
135
+ centre?: boolean;
136
+ rowSpan?: number;
137
+ colSpan?: number;
138
+ }> = ({ letter, children, centre, rowSpan, colSpan }) => (
139
+ <th rowSpan={rowSpan} colSpan={colSpan} className={centre ? styles.centreHeading : undefined}>
140
+ <span className={styles.letter}>({letter})</span>
141
+ {children}
142
+ </th>
143
+ );
144
+
145
+ /** One A3 landscape page of the register: the ruled table, the keys beneath, and the page line */
146
+ export const Tb4Page: React.FC<{
147
+ page: number;
148
+ pages: number;
149
+ side: string;
150
+ scale: number;
151
+ table: React.ReactNode;
152
+ keys: React.ReactNode;
153
+ }> = ({ page, pages, side, scale, table, keys }) => (
154
+ <section className={styles.page} data-register-page style={{ '--page-zoom': scale } as React.CSSProperties}>
155
+ {table}
156
+ {keys}
157
+ <div className={styles.pageFoot}>
158
+ <span>
159
+ Page {page} of {pages} · {side}
160
+ </span>
161
+ <span>MOH/REG/DLTLDP/01 · February 2025</span>
162
+ </div>
163
+ </section>
164
+ );