@epcare/esm-report-builder 0.1.1-pre.2 → 0.1.1-pre.25

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 (328) hide show
  1. package/.eslintignore +5 -0
  2. package/.eslintrc.js +19 -0
  3. package/.prettierrc +8 -0
  4. package/README.md +19 -0
  5. package/dist/162.js +1 -0
  6. package/dist/162.js.map +1 -0
  7. package/dist/184.js +2 -0
  8. package/dist/184.js.map +1 -0
  9. package/dist/242.js +2 -0
  10. package/dist/{d1aa59bd9c702dc4.js.LICENSE.txt → 242.js.LICENSE.txt} +0 -11
  11. package/dist/242.js.map +1 -0
  12. package/dist/271.js +2 -0
  13. package/dist/{377971e519bf3f4a.js.LICENSE.txt → 271.js.LICENSE.txt} +6 -0
  14. package/dist/271.js.map +1 -0
  15. package/dist/300.js +1 -0
  16. package/dist/311.js +1 -0
  17. package/dist/311.js.map +1 -0
  18. package/dist/382.js +2 -0
  19. package/dist/382.js.LICENSE.txt +77 -0
  20. package/dist/382.js.map +1 -0
  21. package/dist/478.js +2 -0
  22. package/dist/478.js.map +1 -0
  23. package/dist/540.js +2 -0
  24. package/dist/540.js.map +1 -0
  25. package/dist/710.js +1 -0
  26. package/dist/710.js.map +1 -0
  27. package/dist/729.js +1 -0
  28. package/dist/729.js.map +1 -0
  29. package/dist/73.js +1 -0
  30. package/dist/73.js.map +1 -0
  31. package/dist/764.js +1 -0
  32. package/dist/764.js.map +1 -0
  33. package/dist/917.js +1 -0
  34. package/dist/917.js.map +1 -0
  35. package/dist/961.js +2 -0
  36. package/dist/961.js.map +1 -0
  37. package/dist/main.js +1 -0
  38. package/dist/main.js.map +1 -0
  39. package/dist/openmrs-esm-report-builder.js +1 -0
  40. package/dist/openmrs-esm-report-builder.js.buildmanifest.json +506 -0
  41. package/dist/openmrs-esm-report-builder.js.map +1 -0
  42. package/dist/routes.json +1 -0
  43. package/e2e/core/env.ts +5 -0
  44. package/e2e/core/global-setup.ts +34 -0
  45. package/e2e/core/test.ts +21 -0
  46. package/e2e/pages/app-shell.page.ts +34 -0
  47. package/e2e/pages/etl-sources.page.ts +35 -0
  48. package/e2e/pages/report-sections.page.ts +20 -0
  49. package/e2e/pages/section-preview.modal.ts +29 -0
  50. package/e2e/specs/etl-sources.spec.ts +51 -0
  51. package/e2e/specs/navigation.spec.ts +13 -0
  52. package/e2e/specs/section-preview-validation.spec.ts +38 -0
  53. package/e2e/specs/section-preview.spec.ts +94 -0
  54. package/e2e/tsconfig.json +11 -0
  55. package/example.env +5 -0
  56. package/jest.config.js +28 -0
  57. package/package.json +36 -17
  58. package/playwright.config.ts +28 -0
  59. package/src/components/admin/admin-page.component.tsx +5 -0
  60. package/src/components/app-shell/report-builder-shell.component.tsx +3 -0
  61. package/src/components/data-themes/data-theme-modal.component.tsx +3 -3
  62. package/src/components/data-themes/sections/data-theme-condition-columns.section.tsx +1 -1
  63. package/src/components/data-themes/sections/data-theme-source.section.tsx +3 -3
  64. package/src/components/indicators/create-base-indicator-modal.component.tsx +3 -1
  65. package/src/components/indicators/create-composite-base-indicator-modal.component.tsx +8 -10
  66. package/src/components/indicators/create-final-indicator-modal.component.tsx +37 -12
  67. package/src/components/indicators/handler/question-answer-concept-search.component.tsx +2 -2
  68. package/src/components/indicators/indicator-search-select.component.tsx +309 -0
  69. package/src/components/indicators/indicators-page.component.tsx +0 -15
  70. package/src/components/indicators/sections/composite-indicator-picker.section.tsx +30 -75
  71. package/src/components/indicators/sections/final-indicator-picker.section.tsx +27 -59
  72. package/src/components/indicators/sections/indicator-conditions.section.tsx +39 -1
  73. package/src/components/indicators/types/composite-indicator.types.ts +7 -1
  74. package/src/components/indicators/utils/composite-indicator-sql.utils.ts +173 -36
  75. package/src/components/indicators/utils/final-indicator-sql.utils.ts +54 -1
  76. package/src/components/indicators/utils/indicator-sql.utils.ts +33 -0
  77. package/src/components/indicators/utils/population-sql.compiler.test.ts +747 -0
  78. package/src/components/indicators/utils/population-sql.compiler.ts +616 -0
  79. package/src/components/landing/report-builder-landing-page.component.tsx +26 -1
  80. package/src/components/landing/report-builder-landing-page.scss +1 -1
  81. package/src/components/legacy-reports/legacy-report-detail-page.component.tsx +300 -0
  82. package/src/components/legacy-reports/legacy-report-editor-page.component.tsx +324 -0
  83. package/src/components/legacy-reports/legacy-report-upload-modal.component.tsx +307 -0
  84. package/src/components/legacy-reports/legacy-reports-page.component.tsx +206 -0
  85. package/src/components/legacy-reports/legacy-reports-table.component.tsx +154 -0
  86. package/src/components/legacy-reports/tabs/legacy-report-datasets-tab.component.tsx +344 -0
  87. package/src/components/legacy-reports/tabs/legacy-report-dimensions-tab.component.tsx +590 -0
  88. package/src/components/legacy-reports/tabs/legacy-report-indicators-tab.component.tsx +490 -0
  89. package/src/components/legacy-reports/tabs/legacy-report-json-preview-tab.component.tsx +272 -0
  90. package/src/components/legacy-reports/tabs/legacy-report-overview-tab.component.tsx +218 -0
  91. package/src/components/legacy-reports/tabs/legacy-report-parameters-tab.component.tsx +279 -0
  92. package/src/components/report/design/report-design-editor.component.tsx +1 -1
  93. package/src/components/report-sections/create-report-section-modal.component.tsx +92 -56
  94. package/src/components/report-sections/section-disaggregation.utils.ts +250 -0
  95. package/src/components/run-reports/run-reports-page.component.tsx +2 -17
  96. package/src/components/shared/preview/query-results-preview.component.tsx +2 -2
  97. package/src/data-visualizer/components/cqi-components/cqi-data-table.component.tsx +214 -0
  98. package/src/data-visualizer/components/cqi-components/cqi-elements.ts +203 -0
  99. package/src/data-visualizer/components/cqi-components/detail.component.tsx +110 -0
  100. package/src/data-visualizer/components/cqi-components/detail.scss +22 -0
  101. package/src/data-visualizer/components/data-table/data-table.component.tsx +208 -0
  102. package/src/data-visualizer/components/data-table/data-tables.scss +206 -0
  103. package/src/data-visualizer/components/empty-state/empty-state-illustration.component.tsx +53 -0
  104. package/src/data-visualizer/components/header/header.component.tsx +43 -0
  105. package/src/data-visualizer/components/header/header.scss +71 -0
  106. package/src/data-visualizer/components/model/confirm.component.tsx +40 -0
  107. package/src/data-visualizer/components/panel/panel.component.tsx +20 -0
  108. package/src/data-visualizer/components/panel/panel.scss +25 -0
  109. package/src/data-visualizer/components/popover/modifier-panel.tsx +53 -0
  110. package/src/data-visualizer/constants.ts +445 -0
  111. package/src/data-visualizer/data-visualizer-illustration.component.tsx +31 -0
  112. package/src/data-visualizer/data-visualizer.component.tsx +1453 -0
  113. package/src/data-visualizer/data-visualizer.resource.ts +467 -0
  114. package/src/data-visualizer/data-visualizer.scss +363 -0
  115. package/src/data-visualizer/declarations.d.ts +131 -0
  116. package/src/data-visualizer/types.ts +206 -0
  117. package/src/hooks/theme/useMambaTableMeta.ts +4 -4
  118. package/src/hooks/theme/useMambaTables.ts +2 -2
  119. package/src/index.ts +6 -0
  120. package/src/resources/indicator/indicators.api.ts +1 -1
  121. package/src/resources/legacy-reports/legacy-reports.api.ts +156 -0
  122. package/src/resources/theme/{mamba-table-meta.api.ts → etl-table-meta.api.ts} +2 -2
  123. package/src/routes/report-builder.routes.tsx +10 -1
  124. package/src/routes.json +6 -0
  125. package/tools/setup-tests.ts +1 -0
  126. package/translations/en.json +65 -0
  127. package/tsconfig.json +43 -0
  128. package/webpack.config.js +14 -0
  129. package/dist/0081d5d3d536cb23.js +0 -1
  130. package/dist/02acc4469a2fe2e4.woff2 +0 -0
  131. package/dist/081587ca0db8e63f.woff2 +0 -0
  132. package/dist/08cf53c4e5e71cf1.woff2 +0 -0
  133. package/dist/090b2bc014d24ea4.woff2 +0 -0
  134. package/dist/09de5db63ef7b579.js +0 -1
  135. package/dist/0ae92bcea615e948.woff2 +0 -0
  136. package/dist/0be8520d8945b3b7.woff2 +0 -0
  137. package/dist/0e7dd073cd8bb0ad.woff2 +0 -0
  138. package/dist/0edb77cd19e43550.js +0 -1
  139. package/dist/14869ac5d1830304.woff2 +0 -0
  140. package/dist/14aaceff85fc8b28.woff2 +0 -0
  141. package/dist/14c3abea79fc1389.js +0 -1
  142. package/dist/155a4ac079785bfd.js +0 -1
  143. package/dist/155a4ac079785bfd.js.map +0 -1
  144. package/dist/16f443eed01776ca.woff2 +0 -0
  145. package/dist/183bae0e53a22182.woff2 +0 -0
  146. package/dist/19c8f94ccd6289f5.woff2 +0 -0
  147. package/dist/1ae86565eab7451c.woff2 +0 -0
  148. package/dist/1c7bf8797e0a9be6.woff2 +0 -0
  149. package/dist/1f266d3619b30f16.woff2 +0 -0
  150. package/dist/1f8c5a4087a524fd.woff2 +0 -0
  151. package/dist/20ba2b23c5c0cd0b.woff2 +0 -0
  152. package/dist/22a32bd51591301b.js +0 -1
  153. package/dist/273c72d90e432a3d.js +0 -1
  154. package/dist/295dd01a1fbfee6d.woff2 +0 -0
  155. package/dist/29cbe6827c3f03ef.js +0 -1
  156. package/dist/2f686985e26e7107.woff2 +0 -0
  157. package/dist/315eff729e4ccbd9.woff2 +0 -0
  158. package/dist/368d16e3259bf207.woff2 +0 -0
  159. package/dist/36c49aa341a7079a.woff2 +0 -0
  160. package/dist/36e1e0430475edee.woff2 +0 -0
  161. package/dist/374c3ee36d9e040a.js +0 -1
  162. package/dist/377971e519bf3f4a.js +0 -2
  163. package/dist/377971e519bf3f4a.js.map +0 -1
  164. package/dist/3b0ea8395009dcf3.js +0 -2
  165. package/dist/3b0ea8395009dcf3.js.map +0 -1
  166. package/dist/3bc19662b8630ee9.woff2 +0 -0
  167. package/dist/3f645f473b3f8fba.woff2 +0 -0
  168. package/dist/41320d8930d23b36.js +0 -1
  169. package/dist/4218ac9901b9d36d.woff2 +0 -0
  170. package/dist/4659f3ca46d928b5.js +0 -1
  171. package/dist/4659f3ca46d928b5.js.map +0 -1
  172. package/dist/4680ba68412e4da9.woff2 +0 -0
  173. package/dist/48e37bd7ebf85a1f.woff2 +0 -0
  174. package/dist/4aabceefe5a2494a.js +0 -1
  175. package/dist/4c188cbaefba58cd.js +0 -1
  176. package/dist/4e11b8a7c445255f.woff2 +0 -0
  177. package/dist/537ddd61c5c1fce2.woff2 +0 -0
  178. package/dist/54f5eee3a7989556.js +0 -1
  179. package/dist/55c9107b50dd747c.js +0 -1
  180. package/dist/56c4118c0907f151.woff2 +0 -0
  181. package/dist/5938bc2ae7ec60c0.woff2 +0 -0
  182. package/dist/5c6586155f0d278a.woff2 +0 -0
  183. package/dist/5e50c92017029ca3.js +0 -1
  184. package/dist/5e50c92017029ca3.js.map +0 -1
  185. package/dist/600aca7295d8bcb7.js +0 -2
  186. package/dist/600aca7295d8bcb7.js.map +0 -1
  187. package/dist/60ec587649ba812a.woff2 +0 -0
  188. package/dist/64cf08a5ac3266d8.js +0 -1
  189. package/dist/652f476a8a1603d3.js +0 -2
  190. package/dist/652f476a8a1603d3.js.LICENSE.txt +0 -9
  191. package/dist/652f476a8a1603d3.js.map +0 -1
  192. package/dist/65e6511e035870e1.woff2 +0 -0
  193. package/dist/661425f6798cd16a.woff2 +0 -0
  194. package/dist/66b864fe213caf46.woff2 +0 -0
  195. package/dist/680eb791e6266590.js +0 -2
  196. package/dist/680eb791e6266590.js.map +0 -1
  197. package/dist/69d087d970ee9455.woff2 +0 -0
  198. package/dist/69e3aa4276b4cb4b.js +0 -1
  199. package/dist/6a88f03fc4e7c3da.js +0 -1
  200. package/dist/6c6c45e66ff54281.woff2 +0 -0
  201. package/dist/6d9009857c6df878.js +0 -1
  202. package/dist/6ebe7cea59ed2853.js +0 -1
  203. package/dist/6f3181849e740935.js +0 -1
  204. package/dist/6f3181849e740935.js.map +0 -1
  205. package/dist/7073a7323e92bd11.woff2 +0 -0
  206. package/dist/70ac433ae2a89a89.js +0 -1
  207. package/dist/70ee4ee625ac0ec1.js +0 -1
  208. package/dist/718c6cc31274ab1a.woff2 +0 -0
  209. package/dist/727696c3934d0966.js +0 -1
  210. package/dist/727763c0f0df59dc.js +0 -1
  211. package/dist/727763c0f0df59dc.js.map +0 -1
  212. package/dist/72cdbedfe04be9f9.woff2 +0 -0
  213. package/dist/7509c9f56fe9e532.woff2 +0 -0
  214. package/dist/75dc5c5b04adc8bf.woff2 +0 -0
  215. package/dist/77b48bc05299ecca.woff2 +0 -0
  216. package/dist/7a0a0215c248c73d.woff2 +0 -0
  217. package/dist/7c0d0c5180de8729.js +0 -1
  218. package/dist/7f00bebb24e40167.woff2 +0 -0
  219. package/dist/802c6176541eeed7.woff2 +0 -0
  220. package/dist/8080081b3379c637.woff2 +0 -0
  221. package/dist/81cfdaeaa0bbca1f.js +0 -1
  222. package/dist/8605507b7a8487c4.woff2 +0 -0
  223. package/dist/893a160d06428365.js +0 -1
  224. package/dist/893a160d06428365.js.map +0 -1
  225. package/dist/8a83d654702f6534.woff2 +0 -0
  226. package/dist/8fe71173a13fe571.woff2 +0 -0
  227. package/dist/94aa912dcac90114.js +0 -1
  228. package/dist/9624cec0f9bdd81e.woff2 +0 -0
  229. package/dist/98a3af55a1698171.woff2 +0 -0
  230. package/dist/99823aaebba56f46.js +0 -1
  231. package/dist/9ba85996af74c5ce.woff2 +0 -0
  232. package/dist/9ddb22faa35006e7.woff2 +0 -0
  233. package/dist/a167fb2799dd741b.woff2 +0 -0
  234. package/dist/a2045a5cc4fdf94f.woff2 +0 -0
  235. package/dist/a246c73905a31c93.woff2 +0 -0
  236. package/dist/a325099b0a580ce2.js +0 -2
  237. package/dist/a325099b0a580ce2.js.map +0 -1
  238. package/dist/a401aa7939cb6e6f.woff2 +0 -0
  239. package/dist/a40fef67997d3b2c.js +0 -1
  240. package/dist/a62897b000a24b86.js +0 -1
  241. package/dist/a6ed036f17a1ea29.woff2 +0 -0
  242. package/dist/a77bac4877483a0a.js +0 -1
  243. package/dist/a9f25ca430709a1a.woff2 +0 -0
  244. package/dist/aa0dfbdd569b1c24.js +0 -2
  245. package/dist/aa0dfbdd569b1c24.js.LICENSE.txt +0 -5
  246. package/dist/aa0dfbdd569b1c24.js.map +0 -1
  247. package/dist/ab0f637d6cff12e4.js +0 -1
  248. package/dist/afa9121a49e95351.woff2 +0 -0
  249. package/dist/afcda12d9f805543.woff2 +0 -0
  250. package/dist/b09ded85a7da77f1.woff2 +0 -0
  251. package/dist/b10dbe5318a91a12.js +0 -1
  252. package/dist/b2f4e9d1e821b71c.woff2 +0 -0
  253. package/dist/b4a074bf828bb757.woff2 +0 -0
  254. package/dist/b7f447da43f6e596.woff2 +0 -0
  255. package/dist/bcca84a524572d10.woff2 +0 -0
  256. package/dist/be093e052a6d50eb.js +0 -1
  257. package/dist/be4d1f97b11d087c.woff2 +0 -0
  258. package/dist/bec14780d81164de.woff2 +0 -0
  259. package/dist/c31657fe213c1b5e.js +0 -1
  260. package/dist/c49ef726d3f0420b.js +0 -1
  261. package/dist/c553f7e0199c5084.woff2 +0 -0
  262. package/dist/c94ac2fdc1ff1418.js +0 -1
  263. package/dist/c9d9ce9f465df4fe.woff2 +0 -0
  264. package/dist/cbbb57b397433964.js +0 -2
  265. package/dist/cbbb57b397433964.js.LICENSE.txt +0 -14
  266. package/dist/cbbb57b397433964.js.map +0 -1
  267. package/dist/d188f7f036ac5779.woff2 +0 -0
  268. package/dist/d1aa59bd9c702dc4.js +0 -2
  269. package/dist/d1aa59bd9c702dc4.js.map +0 -1
  270. package/dist/d265d81c58525ae4.woff2 +0 -0
  271. package/dist/d5942cd81663e136.woff2 +0 -0
  272. package/dist/d66b8ff73d95cabb.woff2 +0 -0
  273. package/dist/d84bac6d07672dd2.js +0 -1
  274. package/dist/d84bac6d07672dd2.js.map +0 -1
  275. package/dist/d88899ef0fd16719.woff2 +0 -0
  276. package/dist/d92b5ecf31fc187b.js +0 -1
  277. package/dist/d9a5d958e69e058d.woff2 +0 -0
  278. package/dist/dac90d495be353a0.woff2 +0 -0
  279. package/dist/db0550511978dc47.js +0 -1
  280. package/dist/dd8ce598529c3fab.woff2 +0 -0
  281. package/dist/de013f1be25103fa.woff2 +0 -0
  282. package/dist/df222fceeffca849.woff2 +0 -0
  283. package/dist/df22bda08ec7964c.js +0 -1
  284. package/dist/e05b7f902b02a251.js +0 -1
  285. package/dist/e05b7f902b02a251.js.map +0 -1
  286. package/dist/e465f1481f0d2e1c.js +0 -1
  287. package/dist/e62b00b9199c1ac9.woff2 +0 -0
  288. package/dist/e696cef1407b2610.woff2 +0 -0
  289. package/dist/eb42de5422d9af00.js +0 -1
  290. package/dist/eed48feba743e21c.woff2 +0 -0
  291. package/dist/f135ef54213f8975.js +0 -1
  292. package/dist/f260e50923d4283b.js +0 -1
  293. package/dist/f543c88feb236d11.woff2 +0 -0
  294. package/dist/f591b0079faa93fe.woff2 +0 -0
  295. package/dist/f6ed27923769ad1d.woff2 +0 -0
  296. package/dist/f8d066e92ea157c6.js +0 -1
  297. package/dist/fae042506530c1e6.js +0 -1
  298. package/dist/favicon.ico +0 -0
  299. package/dist/fbebeb4336e67012.woff2 +0 -0
  300. package/dist/fd574df77d31a5eb.js +0 -1
  301. package/dist/icon_128x128.3dde25c6e69b1c010a6a5d6b27b3f436.png +0 -0
  302. package/dist/icon_144x144.e263e02b838d229b02ff9133e6e0f2ba.png +0 -0
  303. package/dist/icon_192x192.d3d318638bbe8a10a3c687d3d302d01a.png +0 -0
  304. package/dist/icon_256x256.c729f6e7e914b58fcea5ce91d7aa9bad.png +0 -0
  305. package/dist/icon_384x384.c8923df6b6bd08eba5c79b78d7fcecc0.png +0 -0
  306. package/dist/icon_512x512.7163efb26df832de513bc58d925ea22e.png +0 -0
  307. package/dist/icon_96x96.682ee334ae19d49894217370b8596f61.png +0 -0
  308. package/dist/index.html +0 -9
  309. package/dist/logo-512.png +0 -0
  310. package/dist/logo.svg +0 -30
  311. package/dist/manifest.266df36f2271d27ae9bcaa196a2976ae.json +0 -47
  312. package/dist/openmrs.39723c21cec4b1fc.css +0 -1
  313. package/dist/openmrs.39723c21cec4b1fc.css.map +0 -1
  314. package/dist/openmrs.44bac4c8520ee42f.css +0 -2
  315. package/dist/openmrs.44bac4c8520ee42f.css.map +0 -1
  316. package/dist/openmrs.936e8cf6a4b1599d.css +0 -2
  317. package/dist/openmrs.936e8cf6a4b1599d.css.map +0 -1
  318. package/dist/openmrs.c3adea28ef850516.js +0 -1
  319. package/dist/openmrs.c3adea28ef850516.js.map +0 -1
  320. package/dist/openmrs.e4daba1e3b840e70.css +0 -25
  321. package/dist/openmrs.e4daba1e3b840e70.css.map +0 -1
  322. package/dist/service-worker.js +0 -1
  323. package/dist/service-worker.js.map +0 -1
  324. /package/dist/{a325099b0a580ce2.js.LICENSE.txt → 184.js.LICENSE.txt} +0 -0
  325. /package/dist/{600aca7295d8bcb7.js.LICENSE.txt → 478.js.LICENSE.txt} +0 -0
  326. /package/dist/{3b0ea8395009dcf3.js.LICENSE.txt → 540.js.LICENSE.txt} +0 -0
  327. /package/dist/{680eb791e6266590.js.LICENSE.txt → 961.js.LICENSE.txt} +0 -0
  328. /package/src/resources/theme/{mamba-schema.api.ts → etl-schema.api.ts} +0 -0
@@ -0,0 +1,110 @@
1
+ import React, { useState } from "react";
2
+ import styles from "./detail.scss";
3
+ import { ContentSwitcher, Switch } from "@carbon/react";
4
+ import {
5
+ ClientStatusElements,
6
+ DemographicsElements,
7
+ OtherElements,
8
+ PartnerTestingElements,
9
+ RegimenDetailsElements,
10
+ SiblingsTestingElements,
11
+ VlCdElements,
12
+ } from "./cqi-elements";
13
+ interface RowDetailProps {
14
+ reportItem: any;
15
+ }
16
+
17
+ type TabType =
18
+ | "client_demographics"
19
+ | "siblings_testing"
20
+ | "partner_testing"
21
+ | "client_status"
22
+ | "regimen_details"
23
+ | "vl_cd4"
24
+ | "other_details";
25
+
26
+ const RowDetail: React.FC<RowDetailProps> = ({ reportItem }) => {
27
+ const [tabType, setTabType] = useState<TabType>("client_demographics");
28
+
29
+ const handleTabTypeChange = ({ name }) => {
30
+ setTabType(name);
31
+ };
32
+
33
+ const createReportDetail = (item) => {
34
+ return (
35
+ <div className={styles.detailItem}>
36
+ <span className={styles.detailLabel}>{item.label}:</span>
37
+ <span className={styles.detailValue}>
38
+ {reportItem?.[`${item.key}`]}
39
+ </span>
40
+ </div>
41
+ );
42
+ };
43
+
44
+ return (
45
+ <>
46
+ <div className={styles.detailContentSwitchContainer}>
47
+ <ContentSwitcher
48
+ size={`lg`}
49
+ selectedIndex={0}
50
+ onChange={handleTabTypeChange}
51
+ >
52
+ <Switch name="client_demographics">
53
+ <div className={styles.switch}>
54
+ <span>Client Demographics</span>
55
+ </div>
56
+ </Switch>
57
+ <Switch name="siblings_testing">
58
+ <div className={styles.switch}>
59
+ <span>Index Testing of Siblings</span>
60
+ </div>
61
+ </Switch>
62
+ <Switch name="partner_testing">
63
+ <div className={styles.switch}>
64
+ <span>Index Testing of Sexual Partners</span>
65
+ </div>
66
+ </Switch>
67
+ <Switch name="client_status">
68
+ <div className={styles.switch}>
69
+ <span>Client Status at Facility</span>
70
+ </div>
71
+ </Switch>
72
+ <Switch name="regimen_details">
73
+ <div className={styles.switch}>
74
+ <span>Current Regimen details</span>
75
+ </div>
76
+ </Switch>
77
+ <Switch name="vl_cd4">
78
+ <div className={styles.switch}>
79
+ <span>Viral load & CD4</span>
80
+ </div>
81
+ </Switch>
82
+ <Switch name="other_details">
83
+ <div className={styles.switch}>
84
+ <span>Other details</span>
85
+ </div>
86
+ </Switch>
87
+ </ContentSwitcher>
88
+ </div>
89
+
90
+ <div className={styles.detailContainer}>
91
+ {tabType === "client_demographics" &&
92
+ DemographicsElements.map((item) => createReportDetail(item))}
93
+ {tabType === "siblings_testing" &&
94
+ SiblingsTestingElements.map((item) => createReportDetail(item))}
95
+ {tabType === "partner_testing" &&
96
+ PartnerTestingElements.map((item) => createReportDetail(item))}
97
+ {tabType === "client_status" &&
98
+ ClientStatusElements.map((item) => createReportDetail(item))}
99
+ {tabType === "regimen_details" &&
100
+ RegimenDetailsElements.map((item) => createReportDetail(item))}
101
+ {tabType === "vl_cd4" &&
102
+ VlCdElements.map((item) => createReportDetail(item))}
103
+ {tabType === "other_details" &&
104
+ OtherElements.map((item) => createReportDetail(item))}
105
+ </div>
106
+ </>
107
+ );
108
+ };
109
+
110
+ export default RowDetail;
@@ -0,0 +1,22 @@
1
+ @use '@carbon/styles/scss/colors';
2
+ @use '@carbon/styles/scss/type';
3
+
4
+ .detailContainer {
5
+ width: 100%;
6
+ padding: 1rem;
7
+ }
8
+
9
+ .detailItem {
10
+ width: 25%;
11
+ display: inline-block;
12
+ padding-right: 1rem;
13
+ }
14
+
15
+ .detailLabel {
16
+ @include type.type-style('heading-01');
17
+ }
18
+
19
+ .detailValue {
20
+ margin-left: 0.5rem;
21
+ }
22
+
@@ -0,0 +1,208 @@
1
+ import {
2
+ Button,
3
+ DataTable,
4
+ Pagination,
5
+ Table,
6
+ TableBody,
7
+ TableCell,
8
+ TableContainer,
9
+ TableHead,
10
+ TableHeader,
11
+ TableRow,
12
+ TableToolbar,
13
+ TableToolbarContent,
14
+ TableToolbarSearch,
15
+ Tile,
16
+ } from "@carbon/react";
17
+ import {
18
+ isDesktop,
19
+ useLayoutType,
20
+ usePagination,
21
+ } from "@openmrs/esm-framework";
22
+ import { DocumentDownload } from "@carbon/react/icons";
23
+ import React, { useEffect, useState } from "react";
24
+ import { useTranslation } from "react-i18next";
25
+ import styles from "./data-tables.scss";
26
+ import { saveAs } from "file-saver";
27
+ import type { ReportType } from "../../types";
28
+
29
+ type FilterProps = {
30
+ rowIds: Array<string>;
31
+ headers: any;
32
+ cellsById: any;
33
+ inputValue: string;
34
+ getCellId: (row, key) => string;
35
+ };
36
+
37
+ interface ListProps {
38
+ columns: any;
39
+ data: any;
40
+ report: {
41
+ type: ReportType;
42
+ name: string;
43
+ };
44
+ }
45
+
46
+ const DataList: React.FC<ListProps> = ({ columns, data, report }) => {
47
+ const { t } = useTranslation();
48
+ const layout = useLayoutType();
49
+ const isTablet = useLayoutType() === "tablet";
50
+ const responsiveSize = isTablet ? "lg" : "sm";
51
+ const [allRows, setAllRows] = useState([]);
52
+ const [list] = useState(data);
53
+ const pageSizes = [10, 20, 30, 40, 50];
54
+ const [currentPageSize, setPageSize] = useState(10);
55
+ const {
56
+ goTo,
57
+ results: paginatedList,
58
+ currentPage,
59
+ } = usePagination(data, currentPageSize);
60
+
61
+ const handleFilter = ({
62
+ rowIds,
63
+ headers,
64
+ cellsById,
65
+ inputValue,
66
+ getCellId,
67
+ }: FilterProps): Array<string> => {
68
+ return rowIds.filter((rowId) =>
69
+ headers.some(({ key }) => {
70
+ const cellId = getCellId(rowId, key);
71
+ const filterableValue = cellsById[cellId].value;
72
+ const filterTerm = inputValue.toLowerCase();
73
+
74
+ if (typeof filterableValue === "boolean") {
75
+ return false;
76
+ }
77
+
78
+ return ("" + filterableValue).toLowerCase().includes(filterTerm);
79
+ })
80
+ );
81
+ };
82
+
83
+ useEffect(() => {
84
+ const rows: Array<Record<string, string>> = [];
85
+
86
+ paginatedList.map((item: any, index) => {
87
+ return rows.push({ ...item, id: index++ });
88
+ });
89
+ setAllRows(rows);
90
+ }, [paginatedList, allRows]);
91
+
92
+ const handleReportDownload = (documentType: string) => {
93
+ const currentDate = new Date();
94
+ const filename = `${report.name}_${currentDate
95
+ .toISOString()
96
+ .replace(/:/g, "-")}`;
97
+ const csvString = convertToCSV(list, columns);
98
+
99
+ if (documentType === "csv") {
100
+ const blob = new Blob([csvString], { type: "text/csv;charset=utf-8" });
101
+ saveAs(blob, `${filename}.csv`);
102
+ } else if (documentType === "json") {
103
+ const jsonBlob = new Blob([csvString], { type: "application/json" });
104
+ saveAs(jsonBlob, `${filename}.json`);
105
+ }
106
+ };
107
+
108
+ const convertToCSV = (data, columns) => {
109
+ const header = columns.map((col) => col.header).join(",");
110
+ const rows = data.map((row) =>
111
+ columns.map((col) => JSON.stringify(row[col.key])).join(",")
112
+ );
113
+ return [header, ...rows].join("\n");
114
+ };
115
+
116
+ return (
117
+ <DataTable
118
+ data-floating-menu-container
119
+ rows={allRows}
120
+ headers={columns}
121
+ filterRows={handleFilter}
122
+ overflowMenuOnHover={isDesktop(layout)}
123
+ size={isTablet ? "lg" : "sm"}
124
+ useZebraStyles
125
+ >
126
+ {({ rows, headers, getHeaderProps, getTableProps, onInputChange }) => (
127
+ <div>
128
+ <TableContainer className={styles.tableContainer}>
129
+ <div className={styles.toolbarWrapper}>
130
+ <TableToolbar size={responsiveSize}>
131
+ <TableToolbarContent className={styles.toolbarContent}>
132
+ <TableToolbarSearch
133
+ className={styles.searchbox}
134
+ expanded
135
+ onChange={onInputChange}
136
+ placeholder={t("searchThisList", "Search this list")}
137
+ size={responsiveSize}
138
+ />
139
+ {report.type === "dynamic" ? (
140
+ <Button
141
+ size="sm"
142
+ kind="tertiary"
143
+ onClick={() => handleReportDownload("csv")}
144
+ className={styles.actionButton}
145
+ >
146
+ <DocumentDownload />
147
+ <span>Download Report</span>
148
+ </Button>
149
+ ) : null}
150
+ </TableToolbarContent>
151
+ </TableToolbar>
152
+ </div>
153
+ <Table {...getTableProps()}>
154
+ <TableHead>
155
+ <TableRow>
156
+ {headers.map((header) => (
157
+ <TableHeader {...getHeaderProps({ header })}>
158
+ {header.header}
159
+ </TableHeader>
160
+ ))}
161
+ </TableRow>
162
+ </TableHead>
163
+ <TableBody>
164
+ {rows.map((row) => (
165
+ <TableRow key={row.id}>
166
+ {row.cells.map((cell) => (
167
+ <TableCell key={cell.id}>{cell.value}</TableCell>
168
+ ))}
169
+ </TableRow>
170
+ ))}
171
+ </TableBody>
172
+ </Table>
173
+ {rows.length === 0 ? (
174
+ <div className={styles.tileContainer}>
175
+ <Tile className={styles.tile}>
176
+ <div className={styles.tileContent}>
177
+ <p className={styles.content}>
178
+ {t("No data", "No data to display")}
179
+ </p>
180
+ </div>
181
+ </Tile>
182
+ </div>
183
+ ) : null}
184
+ <Pagination
185
+ forwardText="Next page"
186
+ backwardText="Previous page"
187
+ page={currentPage}
188
+ pageSize={currentPageSize}
189
+ pageSizes={pageSizes}
190
+ totalItems={data?.length}
191
+ className={styles.pagination}
192
+ onChange={({ pageSize, page }) => {
193
+ if (pageSize !== currentPageSize) {
194
+ setPageSize(pageSize);
195
+ }
196
+ if (page !== currentPage) {
197
+ goTo(page);
198
+ }
199
+ }}
200
+ />
201
+ </TableContainer>
202
+ </div>
203
+ )}
204
+ </DataTable>
205
+ );
206
+ };
207
+
208
+ export default DataList;
@@ -0,0 +1,206 @@
1
+ @use '@carbon/styles/scss/colors';
2
+ @use '@carbon/styles/scss/spacing';
3
+ @use '@carbon/styles/scss/type';
4
+ .widgetContainer {
5
+ background-color: colors.$white-0;
6
+ }
7
+
8
+ .widgetHeaderContainer {
9
+ display: flex;
10
+ justify-content: space-between;
11
+ align-items: center;
12
+ padding: spacing.$spacing-04 0 spacing.$spacing-04 spacing.$spacing-05;
13
+ h4 {
14
+ &::after {
15
+ content: '';
16
+ display: block;
17
+ width: 2rem;
18
+ padding-top: 0.188rem;
19
+ border-bottom: 0.375rem solid var(--brand-01);
20
+ }
21
+ }
22
+ }
23
+
24
+ .toggleButtons {
25
+ width: fit-content;
26
+ margin: 0 spacing.$spacing-03;
27
+ }
28
+
29
+ .searchField {
30
+ width: 100%;
31
+ max-width: 250px;
32
+ border: 0 !important;
33
+ }
34
+
35
+ .container {
36
+ margin-top: 16px;
37
+ margin-left: inherit;
38
+ width: 100%;
39
+ overflow-y: hidden;
40
+ }
41
+
42
+ .tabContainer {
43
+ margin-top: 16px;
44
+ padding-left: 1rem;
45
+ background-color: colors.$white-0;
46
+ }
47
+
48
+ .searchbox {
49
+ max-width: 16rem;
50
+ input:focus {
51
+ outline: 2px solid colors.$orange-40 !important;
52
+ }
53
+ }
54
+
55
+ .tabContainer li button {
56
+ width: 100% !important;
57
+ }
58
+
59
+ .tileContainer {
60
+ background-color: colors.$white-0;
61
+ border-top: 1px solid colors.$gray-20;
62
+ padding: 5rem 0;
63
+ }
64
+
65
+ .tile {
66
+ margin: auto;
67
+ width: fit-content;
68
+ }
69
+
70
+ .tileContent {
71
+ display: flex;
72
+ flex-direction: column;
73
+ align-items: center;
74
+ }
75
+
76
+ .menuItem {
77
+ max-width: none;
78
+ }
79
+
80
+ .radioButtonGroup {
81
+ display: flex;
82
+ flex-direction: column;
83
+ align-items: flex-start;
84
+ margin-top: spacing.$spacing-03;
85
+ min-height: spacing.$spacing-10;
86
+ width: 100%;
87
+ @include type.type-style('body-compact-01');
88
+ }
89
+
90
+ .radioButton {
91
+ padding: spacing.$spacing-02 spacing.$spacing-02;
92
+ margin: spacing.$spacing-03 0;
93
+ }
94
+
95
+ .sectionTitle {
96
+ @include type.type-style('heading-compact-02');
97
+ color: colors.$gray-70;
98
+ margin-bottom: spacing.$spacing-04;
99
+ }
100
+
101
+ .modalBody {
102
+ padding-bottom: spacing.$spacing-05;
103
+ }
104
+
105
+ .tabContainer div[role='tabpanel'] {
106
+ padding: 0 !important;
107
+ }
108
+
109
+ .tabContainer li button {
110
+ width: 100% !important;
111
+ }
112
+
113
+ .tableContainer {
114
+ margin: 0;
115
+ padding: 0;
116
+ }
117
+
118
+ .search {
119
+ max-width: 16rem;
120
+ input {
121
+ background-color: colors.$white-0 !important;
122
+ }
123
+ }
124
+
125
+ .toolbarWrapper {
126
+ position: relative;
127
+ display: flex;
128
+ justify-content: flex-end;
129
+ }
130
+
131
+ .tableContainer {
132
+ padding: 0;
133
+ :global(.cds--data-table-content) {
134
+ border: 1px solid colors.$gray-20;
135
+ border-bottom: none;
136
+ overflow-x: auto;
137
+ display: block;
138
+ }
139
+ :global(.cds--table-toolbar) {
140
+ position: relative;
141
+ height: 2rem;
142
+ min-height: 0;
143
+ overflow: visible;
144
+ top: 0;
145
+ }
146
+ &:global(.cds--data-table-container) {
147
+ margin: 0 1rem;
148
+ padding: 0;
149
+ }
150
+ a {
151
+ text-decoration: none;
152
+ }
153
+ tr {
154
+ width: spacing.$spacing-01;
155
+ }
156
+ .cds--table-sort__description {
157
+ display: none;
158
+ }
159
+ }
160
+
161
+ .toolbarAction {
162
+ max-width: none;
163
+ }
164
+
165
+ .patientListDownload {
166
+ margin-top: 10px;
167
+ }
168
+
169
+ .redCell {
170
+ background-color: colors.$red-40 !important;
171
+ }
172
+
173
+ .grayCell {
174
+ background-color: colors.$gray-30 !important;
175
+ }
176
+
177
+ .greenCell {
178
+ background-color: colors.$green-30 !important;
179
+ }
180
+
181
+ .yellowCell {
182
+ background-color: colors.$yellow-20 !important;
183
+ }
184
+
185
+ .cellStyling {
186
+ border-right: 1px solid colors.$gray-20;
187
+ border-left: 1px solid colors.$gray-20;
188
+ }
189
+
190
+ .tableHeaderStyle {
191
+ font-weight: 600;
192
+ font-size: 12px;
193
+ line-height: 1px;
194
+ }
195
+
196
+ .tableHeaderFlag {
197
+ writing-mode: vertical-lr;
198
+ transform: rotate(180deg);
199
+ }
200
+
201
+ .actionButton {
202
+ margin-left: 1rem;
203
+ svg {
204
+ margin-right: 0.5rem;
205
+ }
206
+ }
@@ -0,0 +1,53 @@
1
+ import React from "react";
2
+
3
+ const EmptyStateIllustration = () => {
4
+ return (
5
+ <svg width={64} height={64} viewBox="0 0 64 64">
6
+ <title>Empty data illustration</title>
7
+ <g fill="none" fillRule="evenodd">
8
+ <path
9
+ d="M38.133 13.186H21.947c-.768.001-1.39.623-1.39 1.391V50.55l-.186.057-3.97 1.216a.743.743 0 01-.927-.493L3.664 12.751a.742.742 0 01.492-.926l6.118-1.874 17.738-5.43 6.119-1.873a.741.741 0 01.926.492L38.076 13l.057.186z"
10
+ fill="#F4F4F4"
11
+ />
12
+ <path
13
+ d="M41.664 13L38.026 1.117A1.576 1.576 0 0036.056.07l-8.601 2.633-17.737 5.43-8.603 2.634a1.578 1.578 0 00-1.046 1.97l12.436 40.616a1.58 1.58 0 001.969 1.046l5.897-1.805.185-.057v-.194l-.185.057-5.952 1.822a1.393 1.393 0 01-1.737-.923L.247 12.682a1.39 1.39 0 01.923-1.738L9.772 8.31 27.51 2.881 36.112.247a1.393 1.393 0 011.737.923L41.47 13l.057.186h.193l-.057-.185z"
14
+ fill="#8D8D8D"
15
+ />
16
+ <path
17
+ d="M11.378 11.855a.836.836 0 01-.798-.59L9.385 7.361a.835.835 0 01.554-1.042l16.318-4.996a.836.836 0 011.042.554l1.195 3.902a.836.836 0 01-.554 1.043l-16.318 4.995a.831.831 0 01-.244.037z"
18
+ fill="#C6C6C6"
19
+ />
20
+ <circle fill="#C6C6C6" cx={17.636} cy={2.314} r={1.855} />
21
+ <circle
22
+ fill="#FFF"
23
+ fillRule="nonzero"
24
+ cx={17.636}
25
+ cy={2.314}
26
+ r={1.175}
27
+ />
28
+ <path
29
+ d="M55.893 53.995H24.544a.79.79 0 01-.788-.789V15.644a.79.79 0 01.788-.788h31.349a.79.79 0 01.788.788v37.562a.79.79 0 01-.788.789z"
30
+ fill="#F4F4F4"
31
+ />
32
+ <path
33
+ d="M41.47 13H21.948a1.579 1.579 0 00-1.576 1.577V52.4l.185-.057V14.577c.001-.768.623-1.39 1.391-1.39h19.581L41.471 13zm17.02 0H21.947a1.579 1.579 0 00-1.576 1.577v42.478c0 .87.706 1.576 1.576 1.577H58.49a1.579 1.579 0 001.576-1.577V14.577a1.579 1.579 0 00-1.576-1.576zm1.39 44.055c0 .768-.622 1.39-1.39 1.392H21.947c-.768-.001-1.39-.624-1.39-1.392V14.577c0-.768.622-1.39 1.39-1.39H58.49c.768 0 1.39.622 1.39 1.39v42.478z"
34
+ fill="#8D8D8D"
35
+ />
36
+ <path
37
+ d="M48.751 17.082H31.686a.836.836 0 01-.835-.835v-4.081c0-.46.374-.834.835-.835H48.75c.461 0 .834.374.835.835v4.08c0 .462-.374.835-.835.836z"
38
+ fill="#C6C6C6"
39
+ />
40
+ <circle fill="#C6C6C6" cx={40.218} cy={9.755} r={1.855} />
41
+ <circle
42
+ fill="#FFF"
43
+ fillRule="nonzero"
44
+ cx={40.218}
45
+ cy={9.755}
46
+ r={1.13}
47
+ />
48
+ </g>
49
+ </svg>
50
+ );
51
+ };
52
+
53
+ export default EmptyStateIllustration;
@@ -0,0 +1,43 @@
1
+ import React, { JSX } from "react";
2
+ import { useTranslation } from "react-i18next";
3
+ import { Calendar, Location } from "@carbon/react/icons";
4
+ import { formatDate, useSession } from "@openmrs/esm-framework";
5
+ import styles from "./header.scss";
6
+
7
+ const Header: React.FC<{
8
+ title?: string;
9
+ illustrationComponent: JSX.Element;
10
+ }> = ({ title, illustrationComponent }) => {
11
+ const { t } = useTranslation();
12
+ const userSession = useSession();
13
+ const userLocation = userSession?.sessionLocation?.display;
14
+
15
+ return (
16
+ <>
17
+ <div className={styles.header}>
18
+ <div className={styles["left-justified-items"]}>
19
+ {illustrationComponent}
20
+ <div className={styles["page-labels"]}>
21
+ <p>{t("home", "Home")}</p>
22
+ <p className={styles["page-name"]}>
23
+ {title ?? t("dataVisualizer", "Data visualizer")}
24
+ </p>
25
+ </div>
26
+ </div>
27
+ <div className={styles["right-justified-items"]}>
28
+ <div className={styles["date-and-location"]}>
29
+ <Location size={16} />
30
+ <span className={styles.value}>{userLocation}</span>
31
+ <span className={styles.middot}>&middot;</span>
32
+ <Calendar size={16} />
33
+ <span className={styles.value}>
34
+ {formatDate(new Date(), { mode: "standard" })}
35
+ </span>
36
+ </div>
37
+ </div>
38
+ </div>
39
+ </>
40
+ );
41
+ };
42
+
43
+ export default Header;