@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
@@ -1,14 +1,357 @@
1
- .container {
2
- display: flex;
3
- flex-wrap: wrap;
4
- gap: 20px;
5
- margin: 2rem;
6
- }
7
-
8
- .tile {
9
- flex: 1 1 calc(33.333% - 40px);
10
- min-width: 5rem;
11
- max-width: 15rem;
12
- padding: 1rem;
13
- text-align: center;
14
- }
1
+ @use 'sass:list';
2
+ @use '@carbon/colors';
3
+ @use '@carbon/layout';
4
+ @use '@carbon/motion';
5
+ @use '@carbon/type';
6
+ @use '@openmrs/esm-styleguide/src/vars' as *;
7
+
8
+ $tones: (
9
+ 'violet': (
10
+ colors.$purple-60,
11
+ colors.$purple-10,
12
+ ),
13
+ 'magenta': (
14
+ colors.$magenta-60,
15
+ colors.$magenta-10,
16
+ ),
17
+ 'teal': (
18
+ colors.$teal-60,
19
+ colors.$teal-10,
20
+ ),
21
+ 'blue': (
22
+ colors.$blue-60,
23
+ colors.$blue-10,
24
+ ),
25
+ );
26
+
27
+ .dashboard {
28
+ /* `.page` in root.scss gives every route this side gutter. The dashboard
29
+ reaches over it so its grey runs edge to edge, then pads it back. */
30
+ --page-gutter: #{layout.$spacing-06};
31
+
32
+ box-sizing: border-box;
33
+ margin: 0 calc(-1 * var(--page-gutter));
34
+ padding: 0 var(--page-gutter) layout.$spacing-07;
35
+ /* the original grey behind the reports; the tab row above it is white */
36
+ background-color: $openmrs-background-grey;
37
+ /* grey down to the foot of the window, below the 3rem top bar, however few
38
+ reports are listed */
39
+ min-height: calc(100vh - 3rem);
40
+ }
41
+
42
+ /* Header */
43
+
44
+ /* Tabs, with the facility and date at the right end of the tab row */
45
+ .tabsArea {
46
+ position: relative;
47
+ padding-top: layout.$spacing-05;
48
+
49
+ /* a white band behind the tab row, edge to edge over the page gutter, with
50
+ the tabs' 2px rule along its foot; the reports below sit on grey */
51
+ &::before {
52
+ content: '';
53
+ position: absolute;
54
+ top: 0;
55
+ left: calc(-1 * var(--page-gutter));
56
+ right: calc(-1 * var(--page-gutter));
57
+ height: calc(#{layout.$spacing-05} + #{layout.$spacing-08});
58
+ background-color: $ui-02;
59
+ box-shadow: inset 0 -2px 0 $ui-03;
60
+ }
61
+ }
62
+
63
+ .headerMeta {
64
+ @include type.type-style('body-compact-01');
65
+ position: absolute;
66
+ /* level with the tab row, below the white band's top padding, and drawn
67
+ over the band */
68
+ top: layout.$spacing-05;
69
+ right: 0;
70
+ z-index: 1;
71
+ display: flex;
72
+ align-items: center;
73
+ /* the height of the tab row, so the line sits level with the tab labels */
74
+ height: layout.$spacing-08;
75
+ color: $text-02;
76
+ white-space: nowrap;
77
+ }
78
+
79
+ /* Toolbar */
80
+
81
+ .toolbar {
82
+ /* the tiles' columns, so the search box is exactly one tile wide and the
83
+ count ends at the grid's right edge */
84
+ display: grid;
85
+ grid-template-columns: repeat(3, 1fr);
86
+ align-items: center;
87
+ gap: layout.$spacing-04;
88
+ margin-bottom: layout.$spacing-05;
89
+ }
90
+
91
+ .searchLayer {
92
+ /* two tiles wide, edges lined up with the first two tiles */
93
+ grid-column: 1 / span 2;
94
+
95
+ /* a white field with a thin outline, and the brand colour on focus */
96
+ :global(.cds--search-input) {
97
+ background-color: $ui-02;
98
+ border: 1px solid $ui-03;
99
+
100
+ &:hover {
101
+ border-color: $ui-04;
102
+ }
103
+
104
+ &:focus {
105
+ outline: 2px solid var(--brand-01);
106
+ outline-offset: -2px;
107
+ }
108
+ }
109
+ }
110
+
111
+ .resultCount {
112
+ @include type.type-style('label-01');
113
+ grid-column: -2 / -1;
114
+ justify-self: end;
115
+ color: $text-02;
116
+ white-space: nowrap;
117
+ }
118
+
119
+ /* Categories */
120
+
121
+ .category {
122
+ margin-bottom: layout.$spacing-06;
123
+ }
124
+
125
+ .categoryHeader {
126
+ display: flex;
127
+ align-items: center;
128
+ gap: layout.$spacing-03;
129
+ padding-left: layout.$spacing-03;
130
+ margin-bottom: layout.$spacing-04;
131
+ border-left: layout.$spacing-02 solid $ui-04;
132
+ }
133
+
134
+ .categoryTitle {
135
+ @include type.type-style('productive-heading-01');
136
+ color: $color-gray-100;
137
+ }
138
+
139
+ .grid {
140
+ display: grid;
141
+ grid-template-columns: repeat(3, 1fr);
142
+ gap: layout.$spacing-04;
143
+ }
144
+
145
+ /* Report tiles. Selectors are nested under `.grid` so they win over the
146
+ Carbon tile styles the app shell loads globally. */
147
+
148
+ .grid .tile {
149
+ position: relative;
150
+ display: flex;
151
+ align-items: center;
152
+ gap: layout.$spacing-04;
153
+ min-height: 0;
154
+ padding: layout.$spacing-04;
155
+ background-color: $ui-02;
156
+ border: 1px solid $ui-03;
157
+ border-left: layout.$spacing-02 solid $ui-04;
158
+ text-decoration: none;
159
+ transition: all motion.$duration-fast-02 motion.motion(standard, productive);
160
+
161
+ &:hover {
162
+ background-color: $ui-02;
163
+ border-color: $ui-04;
164
+ box-shadow: 0 layout.$spacing-02 layout.$spacing-05 rgba(0, 0, 0, 0.1);
165
+
166
+ .tileArrow {
167
+ transform: translateX(layout.$spacing-02);
168
+ }
169
+ }
170
+ }
171
+
172
+ .tileIcon {
173
+ display: flex;
174
+ align-items: center;
175
+ justify-content: center;
176
+ flex-shrink: 0;
177
+ width: layout.$spacing-07;
178
+ height: layout.$spacing-07;
179
+ background-color: $ui-01;
180
+ }
181
+
182
+ .tileCopy {
183
+ min-width: 0;
184
+ }
185
+
186
+ .tileCode {
187
+ @include type.type-style('label-01');
188
+ color: $text-02;
189
+ letter-spacing: 0.04em;
190
+ }
191
+
192
+ .tileName {
193
+ @include type.type-style('productive-heading-01');
194
+ color: $color-gray-100;
195
+ }
196
+
197
+ .tileArrow {
198
+ flex-shrink: 0;
199
+ margin-left: auto;
200
+ fill: $text-02;
201
+ transition: transform motion.$duration-fast-02 motion.motion(standard, productive);
202
+ }
203
+
204
+ /* Tone accents, applied per category */
205
+
206
+ @each $name, $tone in $tones {
207
+ $accent: list.nth($tone, 1);
208
+ $tint: list.nth($tone, 2);
209
+
210
+ .categoryHeader[data-tone='#{$name}'] {
211
+ border-left-color: $accent;
212
+ }
213
+
214
+ .grid .tile[data-tone='#{$name}'] {
215
+ border-left-color: $accent;
216
+
217
+ .tileIcon {
218
+ color: $accent;
219
+ background-color: $tint;
220
+ }
221
+
222
+ &:hover {
223
+ border-color: $accent;
224
+
225
+ .tileArrow {
226
+ fill: $accent;
227
+ }
228
+ }
229
+ }
230
+ }
231
+
232
+ /* Empty state */
233
+
234
+ /* Reports / Registers tabs, between the header and the panels */
235
+
236
+ .tabList {
237
+ /* above the white band drawn behind it (.tabsArea::before) */
238
+ position: relative;
239
+ z-index: 1;
240
+ margin-bottom: layout.$spacing-05;
241
+
242
+ /* the first tab's label lines up with the content's left edge */
243
+ :global(.cds--tabs__nav-item:first-child) {
244
+ padding-left: 0;
245
+ }
246
+
247
+ /* keyboard / click focus in the brand colour too */
248
+ :global(.cds--tabs__nav-item:focus) {
249
+ outline-color: var(--brand-01);
250
+ }
251
+
252
+ /* the open tab in the AMPATH brand colour rather than Carbon blue */
253
+ :global(.cds--tabs__nav-item--selected) {
254
+ border-bottom-color: var(--brand-01);
255
+
256
+ .tabLabel svg {
257
+ fill: var(--brand-01);
258
+ }
259
+ }
260
+ }
261
+
262
+ /* Icon, name and count inside a tab */
263
+ .tabLabel {
264
+ display: inline-flex;
265
+ align-items: center;
266
+ gap: layout.$spacing-03;
267
+
268
+ svg {
269
+ fill: $text-02;
270
+ }
271
+ }
272
+
273
+ .tabCount {
274
+ margin: 0;
275
+ }
276
+
277
+ /* Each panel keeps the spacing its content already brings */
278
+ .tabPanel {
279
+ padding: 0;
280
+ }
281
+
282
+ .emptyState {
283
+ display: flex;
284
+ flex-direction: column;
285
+ align-items: center;
286
+ gap: layout.$spacing-03;
287
+ padding: layout.$spacing-07 layout.$spacing-05;
288
+ text-align: center;
289
+ background-color: $ui-02;
290
+ border: 1px solid $ui-03;
291
+
292
+ h5 {
293
+ @include type.type-style('productive-heading-02');
294
+ color: $color-gray-100;
295
+ }
296
+
297
+ p {
298
+ @include type.type-style('body-long-01');
299
+ color: $text-02;
300
+ }
301
+ }
302
+
303
+ .emptyStateIcon {
304
+ fill: $ui-04;
305
+ }
306
+
307
+ @media (max-width: $breakpoint-tablet-max) {
308
+ /* the gutter `.page` uses at this size */
309
+ .dashboard {
310
+ --page-gutter: #{layout.$spacing-05};
311
+
312
+ padding-bottom: layout.$spacing-05;
313
+ }
314
+
315
+ .toolbar,
316
+ .grid {
317
+ grid-template-columns: repeat(2, 1fr);
318
+ }
319
+
320
+ /* two columns: one and a half tiles, leaving the count room beside it */
321
+ .searchLayer {
322
+ grid-column: 1;
323
+ width: 150%;
324
+ }
325
+ }
326
+
327
+ @media (max-width: $breakpoint-phone-max) {
328
+ /* no room beside the tabs: the facility and date sit above them, and the
329
+ white band grows to hold both */
330
+ .headerMeta {
331
+ /* in the flow, but still painted over the band */
332
+ position: relative;
333
+ top: auto;
334
+ right: auto;
335
+ height: 1.125rem;
336
+ margin-bottom: layout.$spacing-03;
337
+ }
338
+
339
+ .tabsArea::before {
340
+ height: calc(#{layout.$spacing-05} + 1.125rem + #{layout.$spacing-03} + #{layout.$spacing-08});
341
+ }
342
+
343
+ .toolbar,
344
+ .grid {
345
+ grid-template-columns: 1fr;
346
+ }
347
+
348
+ /* one column: full width, with the count under the search box */
349
+ .searchLayer {
350
+ grid-column: 1;
351
+ width: 100%;
352
+ }
353
+
354
+ .resultCount {
355
+ justify-self: start;
356
+ }
357
+ }
package/src/index.ts CHANGED
@@ -5,6 +5,18 @@ import { reportsDashboardMeta } from './dashboard-meta/reports-dashboard.meta';
5
5
 
6
6
  export const moduleName = '@ampath/esm-reports-app';
7
7
 
8
+ /**
9
+ * The section this app's configuration is declared under.
10
+ *
11
+ * Not the package name: the AMPATH deployment's `o3-config.json` declares
12
+ * `@ampath/esm-reporting`, and there is no `@ampath/esm-reports-app` section in
13
+ * it. Registering the schema under the package name left `etlBaseUrl` on its
14
+ * empty default no matter what the deployment set, so every report failed with
15
+ * "the ETL base URL is not configured". Keep this in step with the config, not
16
+ * with `package.json`.
17
+ */
18
+ export const configSectionName = '@ampath/esm-reporting';
19
+
8
20
  const options = {
9
21
  featureName: 'ampath-esm-reports',
10
22
  moduleName,
@@ -13,7 +25,7 @@ const options = {
13
25
  export const importTranslation = require.context('../translations', false, /.json$/, 'lazy');
14
26
 
15
27
  export function startupApp() {
16
- defineConfigSchema(moduleName, configSchema);
28
+ defineConfigSchema(configSectionName, configSchema);
17
29
  }
18
30
  export const root = getAsyncLifecycle(() => import('./root.component'), options);
19
31
 
@@ -0,0 +1,41 @@
1
+ @use '@carbon/layout';
2
+ @use '@openmrs/esm-styleguide/src/vars' as *;
3
+
4
+ /* Page furniture for the standalone Laboratory (LAB) Register MOH 240. The
5
+ printed sheet itself -- masthead, the A-AA table and the page summary -- is
6
+ the drill-down register's own (reports/moh-240), reused so the register is
7
+ identical wherever it opens from. What is styled here is only what the
8
+ standalone page adds around it: the export toolbar, the pager and the
9
+ empty state. */
10
+
11
+ .toolbar {
12
+ display: flex;
13
+ align-items: center;
14
+ justify-content: flex-end;
15
+ gap: layout.$spacing-05;
16
+ margin-top: layout.$spacing-05;
17
+ }
18
+
19
+ .total {
20
+ margin-right: auto;
21
+ font-size: 0.8125rem;
22
+ color: $text-02;
23
+ }
24
+
25
+ .pager {
26
+ margin-top: layout.$spacing-04;
27
+ }
28
+
29
+ .empty {
30
+ display: flex;
31
+ flex-direction: column;
32
+ align-items: center;
33
+ gap: layout.$spacing-03;
34
+ margin-top: layout.$spacing-06;
35
+ padding: layout.$spacing-08 layout.$spacing-05;
36
+ text-align: center;
37
+ }
38
+
39
+ .emptyIcon {
40
+ fill: $text-03;
41
+ }
@@ -0,0 +1,166 @@
1
+ import React from 'react';
2
+ import { render, screen, waitFor, within } from '@testing-library/react';
3
+ import userEvent from '@testing-library/user-event';
4
+ import { MemoryRouter } from 'react-router-dom';
5
+
6
+ import Moh240LabRegister from './moh-240.component';
7
+ import { MOH_240_HEADERS, MOH_240_SUB_HEADERS } from '../../reports/moh-240/moh-240.columns';
8
+ import { downloadCsv } from '../../common/report-register';
9
+ import { getMoh706PatientList } from '../../resources/moh-706.resource';
10
+
11
+ jest.mock('../../common/report-register', () => ({
12
+ ...jest.requireActual('../../common/report-register'),
13
+ downloadCsv: jest.fn(),
14
+ }));
15
+
16
+ jest.mock('../../resources/moh-706.resource', () => ({
17
+ getMoh706PatientList: jest.fn(),
18
+ }));
19
+
20
+ jest.mock('@openmrs/esm-framework', () => ({
21
+ useSession: () => ({ sessionLocation: { uuid: 'loc-uuid', display: 'Test Facility' } }),
22
+ openmrsFetch: jest.fn().mockResolvedValue({ ok: false }),
23
+ showSnackbar: jest.fn(),
24
+ }));
25
+
26
+ /* The filter bar is Carbon-heavy and its period picking is not what this page
27
+ is about, so it is stood in by a button that generates a fixed period. The
28
+ restore-from-URL hook stays real, as it is part of the page's behaviour. */
29
+ jest.mock('../../common/report-filters', () => {
30
+ const actual = jest.requireActual('../../common/report-filters');
31
+ return {
32
+ ...actual,
33
+ ReportFiltersComponent: ({
34
+ onGenerate,
35
+ }: {
36
+ onGenerate: (period: { startDate: string; endDate: string; label?: string }) => void;
37
+ }) => (
38
+ <button onClick={() => onGenerate({ startDate: '2026-10-01', endDate: '2026-10-02', label: 'period' })}>
39
+ generate
40
+ </button>
41
+ ),
42
+ };
43
+ });
44
+
45
+ /** A row carrying every register field, so a missing column shows as a blank cell */
46
+ const aRow = (overrides: Record<string, unknown> = {}) => {
47
+ const row: Record<string, any> = {};
48
+ MOH_240_HEADERS.forEach(({ key }, index) => {
49
+ row[key] = `${key}-${index}`;
50
+ });
51
+ return { ...row, ...overrides };
52
+ };
53
+
54
+ const renderRegister = () =>
55
+ render(
56
+ <MemoryRouter>
57
+ <Moh240LabRegister />
58
+ </MemoryRouter>,
59
+ );
60
+
61
+ /** The register table itself: the page also holds the masthead and page
62
+ summary tables, so the queries are scoped to it rather than to the page */
63
+ const registerTable = (container: HTMLElement) => {
64
+ const table = container.querySelector('table.table');
65
+ expect(table).not.toBeNull();
66
+ return table as HTMLTableElement;
67
+ };
68
+
69
+ const page = (results: Array<Record<string, unknown>>, total = results.length) => ({
70
+ results,
71
+ total,
72
+ isTotalExact: true,
73
+ });
74
+
75
+ describe('Moh240LabRegister', () => {
76
+ beforeEach(() => {
77
+ jest.mocked(downloadCsv).mockClear();
78
+ jest.mocked(getMoh706PatientList).mockReset();
79
+ });
80
+
81
+ it('asks for the register to be generated before showing anything', () => {
82
+ renderRegister();
83
+
84
+ expect(screen.getByText('No report generated yet')).toBeInTheDocument();
85
+ expect(getMoh706PatientList).not.toHaveBeenCalled();
86
+ });
87
+
88
+ it('renders the printed register under its own columns (A-AA) once generated', async () => {
89
+ jest
90
+ .mocked(getMoh706PatientList)
91
+ .mockResolvedValue(page([aRow({ date: '01/10/2026', full_name: 'Jane Akinyi' }), aRow()]));
92
+
93
+ const { container } = renderRegister();
94
+ await userEvent.click(screen.getByRole('button', { name: 'generate' }));
95
+
96
+ await waitFor(() => expect(screen.getByText('2 records')).toBeInTheDocument());
97
+
98
+ /* the whole register asked for, not a drill-down's indicator subset */
99
+ expect(getMoh706PatientList).toHaveBeenCalledWith(
100
+ expect.objectContaining({ locationUuids: 'loc-uuid', startDate: '2026-10-01', endDate: '2026-10-02' }),
101
+ );
102
+
103
+ const table = within(registerTable(container));
104
+ const headerRows = within(table.getAllByRole('rowgroup')[0]).getAllByRole('row');
105
+ expect(
106
+ headerRows.map((row) =>
107
+ within(row)
108
+ .getAllByRole('columnheader')
109
+ .map((cell) => cell.textContent),
110
+ ),
111
+ ).toEqual([MOH_240_HEADERS.map(({ header }) => header), [...MOH_240_SUB_HEADERS]]);
112
+
113
+ const firstRow = within(table.getAllByRole('rowgroup')[1]).getAllByRole('row')[0];
114
+ const cells = within(firstRow)
115
+ .getAllByRole('cell')
116
+ .map((cell) => cell.textContent);
117
+ expect(cells[0]).toBe('01/10/2026');
118
+ expect(cells[4]).toBe('Jane Akinyi');
119
+ });
120
+
121
+ it('exports every page as the form\u2019s columns with the letter row beneath', async () => {
122
+ jest
123
+ .mocked(getMoh706PatientList)
124
+ .mockImplementation(({ startIndex }: any) =>
125
+ Promise.resolve(page([aRow({ lab_no: '001234' })], startIndex === 0 ? 2 : 2)),
126
+ );
127
+
128
+ renderRegister();
129
+ await userEvent.click(screen.getByRole('button', { name: 'generate' }));
130
+ await waitFor(() => expect(screen.getByRole('button', { name: /Export CSV/ })).toBeInTheDocument());
131
+
132
+ await userEvent.click(screen.getByRole('button', { name: /Export CSV/ }));
133
+
134
+ await waitFor(() => expect(jest.mocked(downloadCsv)).toHaveBeenCalledTimes(1));
135
+ const [csv, filename] = jest.mocked(downloadCsv).mock.calls[0];
136
+ const lines = (csv as string).split('\r\n');
137
+ expect(lines[0]).toBe(MOH_240_HEADERS.map(({ header }) => header).join(','));
138
+ expect(lines[1]).toBe(MOH_240_SUB_HEADERS.join(','));
139
+ /* identifier columns survive Excel as text */
140
+ expect(lines[2]).toContain('="001234"');
141
+ expect(filename).toBe('laboratory-lab-register-moh-240-2026-10-01-2026-10-02.csv');
142
+ });
143
+
144
+ it('pagers past the first page when the period holds more rows than one page', async () => {
145
+ jest.mocked(getMoh706PatientList).mockResolvedValue(
146
+ page(
147
+ Array.from({ length: 50 }, () => aRow()),
148
+ 120,
149
+ ),
150
+ );
151
+
152
+ renderRegister();
153
+ await userEvent.click(screen.getByRole('button', { name: 'generate' }));
154
+
155
+ await waitFor(() => expect(screen.getByText(/120 items/)).toBeInTheDocument());
156
+ });
157
+
158
+ it('falls back to the placeholder when the register cannot be fetched', async () => {
159
+ jest.mocked(getMoh706PatientList).mockRejectedValue(new Error('etl down'));
160
+
161
+ renderRegister();
162
+ await userEvent.click(screen.getByRole('button', { name: 'generate' }));
163
+
164
+ await waitFor(() => expect(screen.getByText('No report generated yet')).toBeInTheDocument());
165
+ });
166
+ });