@ampath/esm-reports-app 1.0.0-next.7 → 1.0.0-next.71

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 (258) hide show
  1. package/dist/197.js +1 -1
  2. package/dist/282.js +1 -1
  3. package/dist/282.js.map +1 -1
  4. package/dist/295.css +1 -0
  5. package/dist/295.css.map +1 -0
  6. package/dist/295.js +1 -0
  7. package/dist/295.js.map +1 -0
  8. package/dist/299.js +2 -0
  9. package/dist/299.js.LICENSE.txt +3 -0
  10. package/dist/299.js.map +1 -0
  11. package/dist/300.js +1 -1
  12. package/dist/311.js +1 -1
  13. package/dist/311.js.map +1 -1
  14. package/dist/335.js +1 -1
  15. package/dist/353.js +1 -1
  16. package/dist/353.js.map +1 -1
  17. package/dist/354.js +2 -0
  18. package/dist/354.js.LICENSE.txt +20 -0
  19. package/dist/354.js.map +1 -0
  20. package/dist/410.js +1 -0
  21. package/dist/410.js.map +1 -0
  22. package/dist/457.js +2 -0
  23. package/dist/457.js.LICENSE.txt +261 -0
  24. package/dist/457.js.map +1 -0
  25. package/dist/478.js +1 -1
  26. package/dist/478.js.map +1 -1
  27. package/dist/480.js +1 -0
  28. package/dist/540.js +1 -1
  29. package/dist/540.js.map +1 -1
  30. package/dist/55.js +1 -1
  31. package/dist/580.js +1 -0
  32. package/dist/580.js.map +1 -0
  33. package/dist/600.js +2 -0
  34. package/dist/600.js.LICENSE.txt +35 -0
  35. package/dist/600.js.map +1 -0
  36. package/dist/652.js +1 -1
  37. package/dist/781.js +1 -0
  38. package/dist/781.js.map +1 -0
  39. package/dist/919.css +1 -0
  40. package/dist/919.css.map +1 -0
  41. package/dist/919.js +1 -0
  42. package/dist/919.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 +255 -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 +59 -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 +27 -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 +173 -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-362/moh-362-lab-register.component.tsx +392 -0
  100. package/src/registers/moh-362/moh-362-linkage-register.component.tsx +120 -0
  101. package/src/registers/moh-362/moh-362-register.scss +222 -0
  102. package/src/registers/moh-362/moh-362.component.tsx +99 -0
  103. package/src/registers/moh-362/moh-362.scss +11 -0
  104. package/src/registers/moh-362/moh-362.types.ts +97 -0
  105. package/src/registers/register-catalog.ts +96 -0
  106. package/src/registers/tb-lab-register/tb-lab-register-table.component.tsx +116 -0
  107. package/src/registers/tb-lab-register/tb-lab-register-table.scss +123 -0
  108. package/src/registers/tb-lab-register/tb-lab-register.columns.ts +164 -0
  109. package/src/registers/tb-lab-register/tb-lab-register.component.test.tsx +232 -0
  110. package/src/registers/tb-lab-register/tb-lab-register.component.tsx +153 -0
  111. package/src/reports/datatable-wrapper/datatable-wrapper.component.tsx +63 -48
  112. package/src/reports/moh-240/moh-240-header.component.tsx +62 -0
  113. package/src/reports/moh-240/moh-240.columns.ts +113 -0
  114. package/src/reports/moh-240/moh-240.component.tsx +72 -9
  115. package/src/reports/moh-240/moh-240.scss +40 -0
  116. package/src/reports/moh-240/sub-reports/moh-240-register.component.tsx +31 -37
  117. package/src/reports/moh-240/sub-reports/page-summary.component.tsx +143 -106
  118. package/src/reports/moh-505/moh-505-header.component.tsx +67 -0
  119. package/src/reports/moh-505/moh-505.component.tsx +441 -0
  120. package/src/reports/moh-505/moh-505.scss +117 -0
  121. package/src/reports/moh-705B/moh-204b-register.component.tsx +244 -0
  122. package/src/reports/moh-705B/moh-204b.columns.ts +39 -0
  123. package/src/reports/moh-705B/moh-705b.component.tsx +659 -0
  124. package/src/reports/moh-705B/moh-705b.scss +172 -0
  125. package/src/reports/moh-705a/moh-705a.component.tsx +661 -0
  126. package/src/reports/moh-705a/moh-705a.scss +172 -0
  127. package/src/reports/moh-705a/registers/moh-204a-register.component.tsx +329 -0
  128. package/src/reports/moh-705a/registers/moh-204a.columns.ts +57 -0
  129. package/src/reports/moh-705a/type.ts +6 -0
  130. package/src/reports/moh-706/moh-706-header.component.tsx +66 -0
  131. package/src/reports/moh-706/moh-706.component.tsx +150 -24
  132. package/src/reports/moh-706/moh-706.scss +119 -11
  133. package/src/reports/moh-706/sub-reports/bacteriology/bacteriology.component.tsx +54 -62
  134. package/src/reports/moh-706/sub-reports/blood-chemistry/blood-chemistry.component.tsx +228 -317
  135. package/src/reports/moh-706/sub-reports/drug-susceptibility-testing/drug-susceptibility-testing.component.tsx +31 -46
  136. package/src/reports/moh-706/sub-reports/haematology/haematology.component.tsx +47 -55
  137. package/src/reports/moh-706/sub-reports/histology-and-cytology/histology-and-cytology.component.tsx +43 -58
  138. package/src/reports/moh-706/sub-reports/parasitology/parasitology.component.tsx +31 -39
  139. package/src/reports/moh-706/sub-reports/serology/serology.component.tsx +26 -34
  140. package/src/reports/moh-706/sub-reports/specimen-referral-to-higher-levels/specimen-referral-to-higher-levels.component.tsx +9 -24
  141. package/src/reports/moh-706/sub-reports/urine-analysis/urine-analysis.component.tsx +43 -51
  142. package/src/reports/moh-710/moh-710.component.tsx +799 -632
  143. package/src/reports/moh-710/moh-710.scss +142 -21
  144. package/src/reports/moh-710/registers/moh-510/moh-510.columns.ts +40 -0
  145. package/src/reports/moh-710/registers/moh-510/moh-510.component.tsx +144 -0
  146. package/src/reports/moh-710/registers/moh-510/moh-510.scss +130 -0
  147. package/src/reports/moh-710/registers/moh-511-710-register.component.tsx +268 -0
  148. package/src/reports/moh-710/registers/moh-511-710.columns.ts +52 -0
  149. package/src/reports/moh-710/registers/type.ts +31 -0
  150. package/src/reports/moh-711/moh-711.component.tsx +138 -1269
  151. package/src/reports/moh-711/moh711.scss +135 -20
  152. package/src/reports/moh-711/registers/moh-333-register.component.tsx +637 -0
  153. package/src/reports/moh-711/registers/moh-333.columns.ts +119 -0
  154. package/src/reports/moh-711/registers/moh-405-register.component.tsx +520 -0
  155. package/src/reports/moh-711/registers/moh-405.columns.ts +120 -0
  156. package/src/reports/moh-711/registers/moh-406-register.component.tsx +530 -0
  157. package/src/reports/moh-711/registers/moh-406.columns.ts +133 -0
  158. package/src/reports/moh-711/registers/moh-510-register.component.tsx +55 -0
  159. package/src/reports/moh-711/registers/moh-511-register.component.tsx +270 -0
  160. package/src/reports/moh-711/registers/moh-511.columns.ts +59 -0
  161. package/src/reports/moh-711/registers/type.ts +192 -0
  162. package/src/reports/moh-711/sections/anc.component.tsx +159 -0
  163. package/src/reports/moh-711/sections/cervical-cancer.component.tsx +148 -0
  164. package/src/reports/moh-711/sections/chanis.component.tsx +366 -0
  165. package/src/reports/moh-711/sections/family-planning.component.tsx +221 -0
  166. package/src/reports/moh-711/sections/gbv.component.tsx +115 -0
  167. package/src/reports/moh-711/sections/maternity.component.tsx +321 -0
  168. package/src/reports/moh-711/sections/medical-social-work.component.tsx +73 -0
  169. package/src/reports/moh-711/sections/other.component.tsx +47 -0
  170. package/src/reports/moh-711/sections/physiotherapy.component.tsx +61 -0
  171. package/src/reports/moh-711/sections/pnc.component.tsx +127 -0
  172. package/src/reports/moh-711/sections/post-abortion.component.tsx +42 -0
  173. package/src/reports/moh-711/sections/rehabilitation.component.tsx +57 -0
  174. package/src/reports/moh-711/sections/report-compiled-by.component.tsx +50 -0
  175. package/src/reports/moh-711/sections/tb-screening.component.tsx +57 -0
  176. package/src/reports/moh-717/moh-717.component.tsx +149 -985
  177. package/src/reports/moh-717/moh717.scss +217 -21
  178. package/src/reports/moh-717/registers/nutrition-register.component.tsx +48 -0
  179. package/src/reports/moh-717/sections/finance.component.tsx +42 -0
  180. package/src/reports/moh-717/sections/inpatient.component.tsx +374 -0
  181. package/src/reports/moh-717/sections/maternity.component.tsx +126 -0
  182. package/src/reports/moh-717/sections/medical-records.component.tsx +35 -0
  183. package/src/reports/moh-717/sections/mortuary.component.tsx +43 -0
  184. package/src/reports/moh-717/sections/operations.component.tsx +53 -0
  185. package/src/reports/moh-717/sections/orthopaedic-trauma.component.tsx +98 -0
  186. package/src/reports/moh-717/sections/outpatient.component.tsx +597 -0
  187. package/src/reports/moh-717/sections/pharmacy.component.tsx +43 -0
  188. package/src/reports/moh-717/sections/preparedby.component.tsx +52 -0
  189. package/src/reports/moh-717/sections/special-services.component.tsx +114 -0
  190. package/src/reports/moh-731/linelist/moh-731-indicators.ts +1122 -0
  191. package/src/reports/moh-731/linelist/moh-731-linelist-sections.ts +472 -0
  192. package/src/reports/moh-731/linelist/moh-731-linelist.component.tsx +102 -0
  193. package/src/reports/moh-731/linelist/use-open-moh-731-linelist.ts +50 -0
  194. package/src/reports/moh-731/moh-731.component.tsx +266 -0
  195. package/src/reports/moh-731/moh-731.scss +499 -0
  196. package/src/reports/moh-731/registers/moh-366.columns.ts +38 -0
  197. package/src/reports/moh-731/registers/moh-366.component.tsx +471 -0
  198. package/src/reports/moh-731/registers/prep-register.component.tsx +284 -0
  199. package/src/reports/moh-731/registers/prep.columns.ts +34 -0
  200. package/src/reports/moh-731/sections/hiv-and-tb-treatment.component.tsx +1206 -0
  201. package/src/reports/moh-731/sections/hiv-testing-and-pre-exposure.component.tsx +727 -0
  202. package/src/reports/moh-731/sections/pmtct.component.tsx +572 -0
  203. package/src/reports/moh-731/summary/moh-731-summary.component.tsx +313 -0
  204. package/src/reports/moh-731/summary/moh-731-summary.config.ts +529 -0
  205. package/src/reports/moh-731/summary/moh-731-summary.scss +242 -0
  206. package/src/reports/moh-740/moh-740.component.scss +202 -0
  207. package/src/reports/moh-740/moh-740.component.tsx +1332 -0
  208. package/src/reports/moh-740/moh-740.resource.ts +54 -0
  209. package/src/reports/moh-740/registers/moh-222-daily-register/moh-222-daily-register.tsx +149 -0
  210. package/src/reports/moh-740/registers/moh-270-permanent-register/moh-270-permanent-register.tsx +123 -0
  211. package/src/reports/moh-740/registers/moh-740-patient-list.scss +0 -0
  212. package/src/reports/moh-740/registers/moh-740-patient-list.tsx +60 -0
  213. package/src/reports/moh-740/registers/moh-740.columns.ts +54 -0
  214. package/src/reports/moh-740/shared/data-cell/data-cell.scss +6 -0
  215. package/src/reports/moh-740/shared/data-cell/data-cell.tsx +23 -0
  216. package/src/reports/moh-740/shared/utils/indicator-register-map.ts +159 -0
  217. package/src/reports/moh-740/types/index.ts +223 -0
  218. package/src/reports/moh-745/moh-745.component.tsx +1021 -0
  219. package/src/reports/moh-745/moh-745.scss +132 -0
  220. package/src/reports/moh-745/registers/moh-412-register.component.tsx +249 -0
  221. package/src/reports/moh-745/registers/moh-412.columns.ts +52 -0
  222. package/src/reports/moh-745/registers/type.ts +181 -0
  223. package/src/reports/table-wrapper/table-row-mapper.component.tsx +57 -0
  224. package/src/reports/table-wrapper/table-wrapper.component.tsx +23 -14
  225. package/src/reports/table-wrapper/table-wrapper.scss +1 -16
  226. package/src/resources/moh-362.resource.ts +27 -0
  227. package/src/resources/moh-505.resource.ts +69 -0
  228. package/src/resources/moh-705.resource.ts +106 -0
  229. package/src/resources/moh-706.resource.ts +80 -0
  230. package/src/resources/moh-710.resource.ts +32 -9
  231. package/src/resources/moh-711.resource.ts +137 -9
  232. package/src/resources/moh-717.resource.ts +26 -9
  233. package/src/resources/moh-731.resource.ts +90 -0
  234. package/src/resources/moh-745.resource.ts +76 -0
  235. package/src/resources/patient-list-page.ts +65 -0
  236. package/src/root.component.tsx +59 -8
  237. package/src/root.scss +36 -0
  238. package/src/routes.json +3 -1
  239. package/src/utils/format-indicator.ts +39 -0
  240. package/src/utils/get-base-url.ts +39 -7
  241. package/src/utils/utils.ts +36 -0
  242. package/tsconfig.json +1 -1
  243. package/webpack.config.js +22 -1
  244. package/dist/138.js +0 -2
  245. package/dist/138.js.LICENSE.txt +0 -20
  246. package/dist/138.js.map +0 -1
  247. package/dist/184.js +0 -2
  248. package/dist/184.js.LICENSE.txt +0 -14
  249. package/dist/184.js.map +0 -1
  250. package/dist/205.js +0 -1
  251. package/dist/205.js.map +0 -1
  252. package/dist/711.js +0 -1
  253. package/dist/711.js.map +0 -1
  254. package/dist/812.js +0 -1
  255. package/dist/812.js.map +0 -1
  256. package/dist/ampath-esm-reports-app.js.buildmanifest.json +0 -539
  257. package/dist/main.js +0 -1
  258. package/dist/main.js.map +0 -1
@@ -1,28 +1,199 @@
1
- import React from 'react';
2
- import { useNavigate } from 'react-router-dom';
3
- import { ClickableTile } from '@carbon/react';
1
+ import React, { useState } from 'react';
2
+ import { useNavigate, useSearchParams } from 'react-router-dom';
3
+ import { Button, ClickableTile, Layer, Search, Tab, TabList, TabPanel, TabPanels, Tabs, Tag } from '@carbon/react';
4
+ import { ArrowRight, DocumentMultiple_01, Report, type CarbonIconType } from '@carbon/react/icons';
5
+ import { formatDate, useSession } from '@openmrs/esm-framework';
4
6
 
7
+ import { registerCategories, totalRegisterCount } from '../registers/register-catalog';
8
+ import {
9
+ filterCategories,
10
+ reportCategories,
11
+ totalReportCount,
12
+ type ReportCategory,
13
+ type ReportDefinition,
14
+ } from './report-catalog';
5
15
  import styles from './reports-dashboard.module.scss';
16
+
6
17
  interface ReportsDashboardProps {}
18
+
7
19
  const ReportsDashboard: React.FC<ReportsDashboardProps> = () => {
8
20
  const navigate = useNavigate();
21
+ const session = useSession();
22
+ /* each tab keeps its own search */
23
+ const [reportSearch, setReportSearch] = useState<string>('');
24
+ const [registerSearch, setRegisterSearch] = useState<string>('');
25
+
26
+ /* Reports first, Registers second; the open tab is kept in the URL */
27
+ const [searchParams, setSearchParams] = useSearchParams();
28
+ const selectedTab = searchParams.get('tab') === 'registers' ? 1 : 0;
29
+ const selectTab = ({ selectedIndex }: { selectedIndex: number }) => {
30
+ const next = new URLSearchParams(searchParams);
31
+ if (selectedIndex === 1) {
32
+ next.set('tab', 'registers');
33
+ } else {
34
+ next.delete('tab');
35
+ }
36
+ setSearchParams(next, { replace: true });
37
+ };
38
+
39
+ const facilityName = session?.sessionLocation?.display;
40
+ const today = formatDate(new Date(), { mode: 'standard', time: false, noToday: true });
41
+
42
+ const renderReportTile = (report: ReportDefinition, tone: string) => {
43
+ const Icon = report.icon;
44
+
45
+ return (
46
+ <ClickableTile
47
+ key={report.path}
48
+ className={styles.tile}
49
+ data-tone={tone}
50
+ onClick={() => navigate(report.path)}
51
+ aria-label={`${report.code} ${report.name}`}
52
+ >
53
+ <div className={styles.tileIcon}>
54
+ <Icon size={20} />
55
+ </div>
56
+ <div className={styles.tileCopy}>
57
+ <p className={styles.tileCode}>{report.code}</p>
58
+ <h4 className={styles.tileName}>{report.name}</h4>
59
+ </div>
60
+ <ArrowRight size={16} className={styles.tileArrow} />
61
+ </ClickableTile>
62
+ );
63
+ };
64
+
65
+ const renderCategory = (category: ReportCategory) => (
66
+ <section key={category.id} className={styles.category}>
67
+ <div className={styles.categoryHeader} data-tone={category.tone}>
68
+ <h5 className={styles.categoryTitle}>{category.title}</h5>
69
+ <Tag size="sm" type="outline">
70
+ {category.reports.length}
71
+ </Tag>
72
+ </div>
73
+ <div className={styles.grid}>{category.reports.map((report) => renderReportTile(report, category.tone))}</div>
74
+ </section>
75
+ );
76
+
77
+ /** A tab's list: search row, then its categories, or a message when nothing matches */
78
+ const renderCatalog = ({
79
+ id,
80
+ noun,
81
+ categories,
82
+ total,
83
+ term,
84
+ setTerm,
85
+ icon: Icon,
86
+ hint,
87
+ }: {
88
+ id: string;
89
+ noun: string;
90
+ categories: ReportCategory[];
91
+ total: number;
92
+ term: string;
93
+ setTerm: (term: string) => void;
94
+ icon: CarbonIconType;
95
+ hint: string;
96
+ }) => {
97
+ const visible = filterCategories(categories, term);
98
+ const matches = visible.reduce((count, category) => count + category.reports.length, 0);
99
+ const isSearching = term.trim().length > 0;
100
+ const label = `Search ${noun}`;
101
+ /* "1 register", "10 reports" */
102
+ const counted = (n: number) => (n === 1 ? noun.replace(/s$/, '') : noun);
103
+
104
+ return (
105
+ <>
106
+ <div className={styles.toolbar}>
107
+ <Layer className={styles.searchLayer}>
108
+ <Search
109
+ id={id}
110
+ labelText={label}
111
+ placeholder={label}
112
+ size="md"
113
+ value={term}
114
+ onChange={(event) => setTerm(event.target.value)}
115
+ onClear={() => setTerm('')}
116
+ />
117
+ </Layer>
118
+ <p className={styles.resultCount}>
119
+ {isSearching ? `${matches} of ${total} ${counted(total)}` : `${total} ${counted(total)} available`}
120
+ </p>
121
+ </div>
122
+
123
+ {visible.length > 0 ? (
124
+ visible.map(renderCategory)
125
+ ) : (
126
+ <Layer className={styles.emptyState}>
127
+ <Icon size={32} className={styles.emptyStateIcon} />
128
+ <h5>
129
+ No {noun} match &ldquo;{term}&rdquo;
130
+ </h5>
131
+ <p>{hint}</p>
132
+ <Button kind="ghost" size="sm" onClick={() => setTerm('')}>
133
+ Clear search
134
+ </Button>
135
+ </Layer>
136
+ )}
137
+ </>
138
+ );
139
+ };
140
+
9
141
  return (
10
- <>
11
- <div className={styles.container}>
12
- <ClickableTile onClick={() => navigate('/moh-710')} className={styles.tile}>
13
- MOH-710 REPORT
14
- </ClickableTile>
15
- <ClickableTile onClick={() => navigate('/moh-711')} className={styles.tile}>
16
- MOH-711 REPORT
17
- </ClickableTile>
18
- <ClickableTile onClick={() => navigate('/moh-717')} className={styles.tile}>
19
- MOH-717 REPORT
20
- </ClickableTile>
21
- <ClickableTile onClick={() => navigate('/moh-706')} className={styles.tile}>
22
- MOH-706 REPORT
23
- </ClickableTile>
142
+ <div className={styles.dashboard}>
143
+ {/* The facility and date sit at the right end of the tab row */}
144
+ <div className={styles.tabsArea}>
145
+ <p className={styles.headerMeta}>{[facilityName, today].filter(Boolean).join(' · ')}</p>
146
+ <Tabs selectedIndex={selectedTab} onChange={selectTab}>
147
+ <TabList aria-label="Reports and registers" className={styles.tabList}>
148
+ <Tab>
149
+ <span className={styles.tabLabel}>
150
+ <Report size={16} />
151
+ Reports
152
+ <Tag size="sm" type="gray" className={styles.tabCount}>
153
+ {totalReportCount}
154
+ </Tag>
155
+ </span>
156
+ </Tab>
157
+ <Tab>
158
+ <span className={styles.tabLabel}>
159
+ <DocumentMultiple_01 size={16} />
160
+ Registers
161
+ <Tag size="sm" type="gray" className={styles.tabCount}>
162
+ {totalRegisterCount}
163
+ </Tag>
164
+ </span>
165
+ </Tab>
166
+ </TabList>
167
+ <TabPanels>
168
+ <TabPanel className={styles.tabPanel}>
169
+ {renderCatalog({
170
+ id: 'reports-search',
171
+ noun: 'reports',
172
+ categories: reportCategories,
173
+ total: totalReportCount,
174
+ term: reportSearch,
175
+ setTerm: setReportSearch,
176
+ icon: Report,
177
+ hint: 'Try a form number such as 731, or a service such as immunization.',
178
+ })}
179
+ </TabPanel>
180
+ <TabPanel className={styles.tabPanel}>
181
+ {/* Registers, from src/registers, listed and searched as the reports are */}
182
+ {renderCatalog({
183
+ id: 'registers-search',
184
+ noun: 'registers',
185
+ categories: registerCategories,
186
+ total: totalRegisterCount,
187
+ term: registerSearch,
188
+ setTerm: setRegisterSearch,
189
+ icon: DocumentMultiple_01,
190
+ hint: 'Try a register number such as 362, or a service such as HTS.',
191
+ })}
192
+ </TabPanel>
193
+ </TabPanels>
194
+ </Tabs>
24
195
  </div>
25
- </>
196
+ </div>
26
197
  );
27
198
  };
28
199
 
@@ -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
+ }