@openmrs/esm-report-builder 0.1.1-pre.87

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 (551) hide show
  1. package/.eslintignore +5 -0
  2. package/.eslintrc.js +19 -0
  3. package/.prettierrc +8 -0
  4. package/.tx/config +12 -0
  5. package/LICENSE +401 -0
  6. package/README.md +336 -0
  7. package/__mocks__/react-i18next.js +12 -0
  8. package/dist/162.js +1 -0
  9. package/dist/162.js.map +1 -0
  10. package/dist/184.js +2 -0
  11. package/dist/184.js.LICENSE.txt +14 -0
  12. package/dist/184.js.map +1 -0
  13. package/dist/242.js +2 -0
  14. package/dist/242.js.LICENSE.txt +21 -0
  15. package/dist/242.js.map +1 -0
  16. package/dist/253.js +1 -0
  17. package/dist/253.js.map +1 -0
  18. package/dist/300.js +1 -0
  19. package/dist/311.js +1 -0
  20. package/dist/311.js.map +1 -0
  21. package/dist/469.js +2 -0
  22. package/dist/469.js.LICENSE.txt +45 -0
  23. package/dist/469.js.map +1 -0
  24. package/dist/478.js +2 -0
  25. package/dist/478.js.LICENSE.txt +9 -0
  26. package/dist/478.js.map +1 -0
  27. package/dist/539.js +1 -0
  28. package/dist/539.js.map +1 -0
  29. package/dist/540.js +2 -0
  30. package/dist/540.js.LICENSE.txt +9 -0
  31. package/dist/540.js.map +1 -0
  32. package/dist/669.js +1 -0
  33. package/dist/669.js.map +1 -0
  34. package/dist/710.js +1 -0
  35. package/dist/710.js.map +1 -0
  36. package/dist/729.js +1 -0
  37. package/dist/729.js.map +1 -0
  38. package/dist/838.js +2 -0
  39. package/dist/838.js.LICENSE.txt +9 -0
  40. package/dist/838.js.map +1 -0
  41. package/dist/917.js +1 -0
  42. package/dist/917.js.map +1 -0
  43. package/dist/961.js +2 -0
  44. package/dist/961.js.LICENSE.txt +19 -0
  45. package/dist/961.js.map +1 -0
  46. package/dist/main.js +1 -0
  47. package/dist/main.js.map +1 -0
  48. package/dist/openmrs-esm-report-builder.js +1 -0
  49. package/dist/openmrs-esm-report-builder.js.buildmanifest.json +530 -0
  50. package/dist/openmrs-esm-report-builder.js.map +1 -0
  51. package/dist/routes.json +1 -0
  52. package/e2e/core/env.ts +5 -0
  53. package/e2e/core/global-setup.ts +34 -0
  54. package/e2e/core/test.ts +21 -0
  55. package/e2e/pages/app-shell.page.ts +34 -0
  56. package/e2e/pages/etl-sources.page.ts +35 -0
  57. package/e2e/pages/report-sections.page.ts +20 -0
  58. package/e2e/pages/section-preview.modal.ts +29 -0
  59. package/e2e/specs/etl-sources.spec.ts +51 -0
  60. package/e2e/specs/navigation.spec.ts +13 -0
  61. package/e2e/specs/section-preview-validation.spec.ts +38 -0
  62. package/e2e/specs/section-preview.spec.ts +94 -0
  63. package/e2e/support/github/Dockerfile +22 -0
  64. package/e2e/support/github/Dockerfile.dockerignore +13 -0
  65. package/e2e/support/github/docker-compose.yml +31 -0
  66. package/e2e/support/github/run-e2e-docker-env.sh +131 -0
  67. package/e2e/support/github/spa-assemble-config.json +9 -0
  68. package/e2e/tsconfig.json +11 -0
  69. package/example.env +5 -0
  70. package/jest.config.js +28 -0
  71. package/linelist_report_builder_design_spec.md +816 -0
  72. package/package.json +111 -0
  73. package/playwright.config.ts +28 -0
  74. package/routes.json +22 -0
  75. package/src/__debug__/all-designs.test.tsx +30 -0
  76. package/src/admin-card-link.extension.module.scss +55 -0
  77. package/src/admin-card-link.extension.tsx +24 -0
  78. package/src/components/admin/admin-page.component.tsx +75 -0
  79. package/src/components/admin/age-categories-page.component.tsx +109 -0
  80. package/src/components/admin/age-groups-page.component.tsx +146 -0
  81. package/src/components/admin/etl-browser/columns-tab.component.tsx +80 -0
  82. package/src/components/admin/etl-browser/etl-browser-page.component.tsx +309 -0
  83. package/src/components/admin/etl-browser/etl-browser.component.scss +424 -0
  84. package/src/components/admin/etl-browser/metadata-tab.component.tsx +65 -0
  85. package/src/components/admin/etl-browser/preview-grid.component.tsx +189 -0
  86. package/src/components/admin/etl-browser/relationships-tab.component.tsx +21 -0
  87. package/src/components/admin/etl-browser/sources-tree.component.tsx +118 -0
  88. package/src/components/admin/etl-browser/table-detail.component.tsx +124 -0
  89. package/src/components/admin/etl-browser/table-info-strip.component.tsx +51 -0
  90. package/src/components/admin/etl-browser/table-list.component.tsx +136 -0
  91. package/src/components/admin/etl-browser/use-table-preview.ts +47 -0
  92. package/src/components/admin/etl-sources-page.component.tsx +355 -0
  93. package/src/components/admin/etl-tasks-page.component.scss +370 -0
  94. package/src/components/admin/etl-tasks-page.component.tsx +768 -0
  95. package/src/components/admin/report-categories-page.component.tsx +267 -0
  96. package/src/components/admin/report-library-page.component.tsx +551 -0
  97. package/src/components/ai-support/ai-assist-button.component.tsx +30 -0
  98. package/src/components/ai-support/ai-assist-modal.component.tsx +81 -0
  99. package/src/components/ai-support/ai.service.ts +21 -0
  100. package/src/components/ai-support/ai.types.ts +12 -0
  101. package/src/components/app-shell/report-builder-shell.component.tsx +242 -0
  102. package/src/components/app-shell/report-builder-shell.scss +18 -0
  103. package/src/components/dashboards/dashboard-config.schema.ts +97 -0
  104. package/src/components/dashboards/dashboard-form-modal.component.tsx +150 -0
  105. package/src/components/dashboards/dashboards-admin.scss +62 -0
  106. package/src/components/dashboards/dashboards-page.component.tsx +153 -0
  107. package/src/components/dashboards/dashboards-table.component.tsx +130 -0
  108. package/src/components/dashboards/sections/dashboard-autoinclude.section.tsx +75 -0
  109. package/src/components/dashboards/sections/dashboard-basics.section.tsx +80 -0
  110. package/src/components/dashboards/sections/dashboard-json.section.tsx +79 -0
  111. package/src/components/dashboards/sections/dashboard-sections-editor.section.tsx +130 -0
  112. package/src/components/dashboards/sections/dashboard-widgets-editor.section.tsx +184 -0
  113. package/src/components/data-themes/data-theme-form.component.tsx +59 -0
  114. package/src/components/data-themes/data-theme-modal.component.tsx +349 -0
  115. package/src/components/data-themes/data-themes-page.component.tsx +139 -0
  116. package/src/components/data-themes/data-themes-table.component.tsx +98 -0
  117. package/src/components/data-themes/icon-registry.ts +35 -0
  118. package/src/components/data-themes/sections/data-theme-basics.section.tsx +63 -0
  119. package/src/components/data-themes/sections/data-theme-condition-columns.section.tsx +397 -0
  120. package/src/components/data-themes/sections/data-theme-fields-editor.section.tsx +125 -0
  121. package/src/components/data-themes/sections/data-theme-metadata.section.tsx +245 -0
  122. package/src/components/data-themes/sections/data-theme-preview.section.tsx +115 -0
  123. package/src/components/data-themes/sections/data-theme-source.section.tsx +184 -0
  124. package/src/components/data-visualizer/components/cqi-components/cqi-data-table.component.tsx +214 -0
  125. package/src/components/data-visualizer/components/cqi-components/cqi-elements.ts +203 -0
  126. package/src/components/data-visualizer/components/cqi-components/detail.component.tsx +110 -0
  127. package/src/components/data-visualizer/components/cqi-components/detail.scss +22 -0
  128. package/src/components/data-visualizer/components/data-table/data-table.component.tsx +208 -0
  129. package/src/components/data-visualizer/components/data-table/data-tables.scss +206 -0
  130. package/src/components/data-visualizer/components/empty-state/empty-state-illustration.component.tsx +53 -0
  131. package/src/components/data-visualizer/components/header/header.component.tsx +43 -0
  132. package/src/components/data-visualizer/components/header/header.scss +71 -0
  133. package/src/components/data-visualizer/components/model/confirm.component.tsx +40 -0
  134. package/src/components/data-visualizer/components/panel/panel.component.tsx +20 -0
  135. package/src/components/data-visualizer/components/panel/panel.scss +25 -0
  136. package/src/components/data-visualizer/components/popover/modifier-panel.tsx +53 -0
  137. package/src/components/data-visualizer/constants.ts +445 -0
  138. package/src/components/data-visualizer/data-visualizer-illustration.component.tsx +31 -0
  139. package/src/components/data-visualizer/data-visualizer.component.tsx +1935 -0
  140. package/src/components/data-visualizer/data-visualizer.resource.ts +462 -0
  141. package/src/components/data-visualizer/data-visualizer.scss +449 -0
  142. package/src/components/data-visualizer/declarations.d.ts +131 -0
  143. package/src/components/data-visualizer/parameter-inputs/boolean-parameter-input.component.tsx +34 -0
  144. package/src/components/data-visualizer/parameter-inputs/concept-parameter-input.component.tsx +81 -0
  145. package/src/components/data-visualizer/parameter-inputs/date-parameter-input.component.tsx +110 -0
  146. package/src/components/data-visualizer/parameter-inputs/date-range-input.component.scss +47 -0
  147. package/src/components/data-visualizer/parameter-inputs/date-range-input.component.tsx +228 -0
  148. package/src/components/data-visualizer/parameter-inputs/identifier-type-parameter-input.component.tsx +69 -0
  149. package/src/components/data-visualizer/parameter-inputs/list-parameter-input.component.tsx +73 -0
  150. package/src/components/data-visualizer/parameter-inputs/location-parameter-input.component.tsx +81 -0
  151. package/src/components/data-visualizer/parameter-inputs/number-parameter-input.component.tsx +40 -0
  152. package/src/components/data-visualizer/parameter-inputs/person-attribute-parameter-input.component.tsx +71 -0
  153. package/src/components/data-visualizer/parameter-inputs/text-parameter-input.component.tsx +52 -0
  154. package/src/components/data-visualizer/report-parameter-modal.component.tsx +383 -0
  155. package/src/components/data-visualizer/types.ts +206 -0
  156. package/src/components/etl-monitor/builder/builder-state-machine.ts +381 -0
  157. package/src/components/etl-monitor/builder/design-registry.tsx +139 -0
  158. package/src/components/etl-monitor/builder/etl-monitor-builder-context.tsx +702 -0
  159. package/src/components/etl-monitor/builder/etl-monitor-builder-modal.component.tsx +461 -0
  160. package/src/components/etl-monitor/builder/etl-monitor-builder-modal.scss +256 -0
  161. package/src/components/etl-monitor/builder/etl-monitor-builder-page.component.tsx +387 -0
  162. package/src/components/etl-monitor/builder/etl-monitor-builder-page.scss +976 -0
  163. package/src/components/etl-monitor/builder/generated-config-panel.component.tsx +168 -0
  164. package/src/components/etl-monitor/builder/generated-config-panel.scss +198 -0
  165. package/src/components/etl-monitor/builder/index.ts +5 -0
  166. package/src/components/etl-monitor/builder/monitor-preview.tsx +124 -0
  167. package/src/components/etl-monitor/builder/steps/data-source-step.component.tsx +667 -0
  168. package/src/components/etl-monitor/builder/steps/data-source-step.scss +284 -0
  169. package/src/components/etl-monitor/builder/steps/design-samples.ts +278 -0
  170. package/src/components/etl-monitor/builder/steps/design-step.component.tsx +299 -0
  171. package/src/components/etl-monitor/builder/steps/design-step.scss +479 -0
  172. package/src/components/etl-monitor/builder/steps/fields-step.component.tsx +859 -0
  173. package/src/components/etl-monitor/builder/steps/fields-step.scss +459 -0
  174. package/src/components/etl-monitor/builder/steps/general-step.component.tsx +288 -0
  175. package/src/components/etl-monitor/builder/steps/general-step.scss +103 -0
  176. package/src/components/etl-monitor/builder/steps/index.ts +5 -0
  177. package/src/components/etl-monitor/builder/steps/preview-step.component.tsx +245 -0
  178. package/src/components/etl-monitor/builder/steps/preview-step.scss +236 -0
  179. package/src/components/etl-monitor/etl-monitor-card-shell.component.tsx +154 -0
  180. package/src/components/etl-monitor/etl-monitor-card-shell.scss +103 -0
  181. package/src/components/etl-monitor/etl-monitor-dashboard.component.tsx +227 -0
  182. package/src/components/etl-monitor/etl-monitor-dashboard.scss +148 -0
  183. package/src/components/etl-monitor/etl-monitor-status-card.component.tsx +192 -0
  184. package/src/components/etl-monitor/etl-monitors-page.component.tsx +621 -0
  185. package/src/components/etl-monitor/renderers/DetailsRenderer.tsx +2 -0
  186. package/src/components/etl-monitor/renderers/ErrorLogRenderer.tsx +2 -0
  187. package/src/components/etl-monitor/renderers/EtlMonitorWidget.tsx +97 -0
  188. package/src/components/etl-monitor/renderers/MetricsGridRenderer.tsx +2 -0
  189. package/src/components/etl-monitor/renderers/MonitorRenderer.tsx +175 -0
  190. package/src/components/etl-monitor/renderers/ProgressRenderer.tsx +2 -0
  191. package/src/components/etl-monitor/renderers/StatusCardRenderer.tsx +2 -0
  192. package/src/components/etl-monitor/renderers/SummaryCardRenderer.tsx +2 -0
  193. package/src/components/etl-monitor/renderers/TableRenderer.tsx +2 -0
  194. package/src/components/etl-monitor/renderers/component-icons.tsx +33 -0
  195. package/src/components/etl-monitor/renderers/components/details-renderer.tsx +195 -0
  196. package/src/components/etl-monitor/renderers/components/error-log-renderer.tsx +104 -0
  197. package/src/components/etl-monitor/renderers/components/index.ts +6 -0
  198. package/src/components/etl-monitor/renderers/components/log-renderer.tsx +86 -0
  199. package/src/components/etl-monitor/renderers/components/metrics-grid-renderer.tsx +133 -0
  200. package/src/components/etl-monitor/renderers/components/progress-renderer.tsx +188 -0
  201. package/src/components/etl-monitor/renderers/components/status-card-renderer.tsx +247 -0
  202. package/src/components/etl-monitor/renderers/components/summary-card-renderer.tsx +160 -0
  203. package/src/components/etl-monitor/renderers/components/table-renderer.tsx +175 -0
  204. package/src/components/etl-monitor/renderers/components/time-series-renderer.tsx +310 -0
  205. package/src/components/etl-monitor/renderers/data-transformer.ts +361 -0
  206. package/src/components/etl-monitor/renderers/index.ts +11 -0
  207. package/src/components/etl-monitor/renderers/legacy-monitor-renderer.tsx +69 -0
  208. package/src/components/etl-monitor/renderers/monitor-renderer.tsx +6 -0
  209. package/src/components/etl-monitor/renderers/monitor-renderers.scss +1621 -0
  210. package/src/components/etl-monitor/renderers/monitor-widget-card.component.tsx +54 -0
  211. package/src/components/etl-monitor/renderers/monitor-widget-card.scss +16 -0
  212. package/src/components/etl-monitor/renderers/v2-monitor-renderer.tsx +227 -0
  213. package/src/components/import-export/artifact-groups.ts +153 -0
  214. package/src/components/import-export/export-report-modal.component.tsx +265 -0
  215. package/src/components/import-export/import-export-page.component.tsx +610 -0
  216. package/src/components/import-export/import-export.styles.scss +825 -0
  217. package/src/components/import-export/import-package-modal.component.tsx +401 -0
  218. package/src/components/import-export/index.ts +3 -0
  219. package/src/components/indicators/README.md +24 -0
  220. package/src/components/indicators/base-indicator-basic-fields.component.tsx +18 -0
  221. package/src/components/indicators/base-indicator-basics.section.tsx +54 -0
  222. package/src/components/indicators/base-indicator-countby.section.tsx +25 -0
  223. package/src/components/indicators/base-indicator-criteria.section.tsx +83 -0
  224. package/src/components/indicators/base-indicator-details-form.component.tsx +80 -0
  225. package/src/components/indicators/base-indicator-theme-unit-fields.component.tsx +39 -0
  226. package/src/components/indicators/create-base-indicator-modal.component.tsx +623 -0
  227. package/src/components/indicators/create-composite-base-indicator-modal.component.tsx +295 -0
  228. package/src/components/indicators/create-custom-indicator-modal.component.tsx +548 -0
  229. package/src/components/indicators/create-final-indicator-modal.component.tsx +285 -0
  230. package/src/components/indicators/custom-condition-input.component.tsx +310 -0
  231. package/src/components/indicators/diagnosis-filters-form.component.tsx +101 -0
  232. package/src/components/indicators/disaggregation-panel.component.tsx +93 -0
  233. package/src/components/indicators/handler/concept-search-multiselect.component.tsx +153 -0
  234. package/src/components/indicators/handler/question-answer-concept-search.component.tsx +336 -0
  235. package/src/components/indicators/indicator-run-preview-modal.component.tsx +391 -0
  236. package/src/components/indicators/indicator-search-select.component.tsx +297 -0
  237. package/src/components/indicators/indicator-status-tag.component.tsx +9 -0
  238. package/src/components/indicators/indicators-page.component.tsx +673 -0
  239. package/src/components/indicators/indicators-page.scss +121 -0
  240. package/src/components/indicators/indicators-table.component.tsx +188 -0
  241. package/src/components/indicators/preview-table.component.tsx +31 -0
  242. package/src/components/indicators/sections/composite-indicator-basics.section.tsx +36 -0
  243. package/src/components/indicators/sections/composite-indicator-picker.section.tsx +116 -0
  244. package/src/components/indicators/sections/composite-indicator-sql-preview.section.tsx +31 -0
  245. package/src/components/indicators/sections/custom-indicator-basics.section.tsx +65 -0
  246. package/src/components/indicators/sections/custom-indicator-disaggregation.section.tsx +93 -0
  247. package/src/components/indicators/sections/custom-indicator-filters.section.tsx +203 -0
  248. package/src/components/indicators/sections/custom-indicator-population.section.tsx +107 -0
  249. package/src/components/indicators/sections/custom-indicator-sql-preview.section.tsx +108 -0
  250. package/src/components/indicators/sections/final-indicator-basics.section.tsx +46 -0
  251. package/src/components/indicators/sections/final-indicator-disaggregation.section.tsx +47 -0
  252. package/src/components/indicators/sections/final-indicator-picker.section.tsx +60 -0
  253. package/src/components/indicators/sections/final-indicator-preview.section.tsx +20 -0
  254. package/src/components/indicators/sections/final-indicator-results-preview.section.tsx +65 -0
  255. package/src/components/indicators/sections/indicator-basics.section.tsx +40 -0
  256. package/src/components/indicators/sections/indicator-conditions.section.tsx +279 -0
  257. package/src/components/indicators/sections/indicator-sql-preview.section.tsx +63 -0
  258. package/src/components/indicators/sections/indicator-theme.section.tsx +48 -0
  259. package/src/components/indicators/sql-preview.component.tsx +19 -0
  260. package/src/components/indicators/tag-input.component.tsx +177 -0
  261. package/src/components/indicators/types/base-indicator-form.types.ts +17 -0
  262. package/src/components/indicators/types/base-indicator-theme.constants.ts +8 -0
  263. package/src/components/indicators/types/composite-indicator.types.ts +27 -0
  264. package/src/components/indicators/types/condition-ui.types.ts +11 -0
  265. package/src/components/indicators/types/custom-indicator.types.ts +161 -0
  266. package/src/components/indicators/types/data-theme-config.types.ts +88 -0
  267. package/src/components/indicators/types/indicator-types.ts +57 -0
  268. package/src/components/indicators/types/sql-builders.ts +43 -0
  269. package/src/components/indicators/utils/composite-indicator-sql.utils.test.ts +143 -0
  270. package/src/components/indicators/utils/composite-indicator-sql.utils.ts +426 -0
  271. package/src/components/indicators/utils/concept-preload.utils.ts +21 -0
  272. package/src/components/indicators/utils/custom-indicator-interpreter.test.ts +493 -0
  273. package/src/components/indicators/utils/custom-indicator-interpreter.ts +702 -0
  274. package/src/components/indicators/utils/final-indicator-sql.utils.ts +190 -0
  275. package/src/components/indicators/utils/indicator-conditions-hydration.utils.ts +254 -0
  276. package/src/components/indicators/utils/indicator-sql-resolution.utils.ts +204 -0
  277. package/src/components/indicators/utils/indicator-sql.utils.ts +721 -0
  278. package/src/components/indicators/utils/population-sql.compiler.test.ts +999 -0
  279. package/src/components/indicators/utils/population-sql.compiler.ts +858 -0
  280. package/src/components/landing/landing-action-item.component.tsx +50 -0
  281. package/src/components/landing/landing-action-list.component.tsx +12 -0
  282. package/src/components/landing/landing-activity-list.component.tsx +47 -0
  283. package/src/components/landing/landing-footer-grid.component.tsx +45 -0
  284. package/src/components/landing/landing-hero.component.tsx +18 -0
  285. package/src/components/landing/landing-section-card.component.tsx +33 -0
  286. package/src/components/landing/report-builder-landing-page.component.tsx +186 -0
  287. package/src/components/landing/report-builder-landing-page.scss +131 -0
  288. package/src/components/legacy-reports/legacy-report-detail-page.component.tsx +300 -0
  289. package/src/components/legacy-reports/legacy-report-editor-page.component.tsx +324 -0
  290. package/src/components/legacy-reports/legacy-report-upload-modal.component.tsx +307 -0
  291. package/src/components/legacy-reports/legacy-reports-page.component.tsx +206 -0
  292. package/src/components/legacy-reports/legacy-reports-table.component.tsx +154 -0
  293. package/src/components/legacy-reports/tabs/legacy-report-datasets-tab.component.tsx +344 -0
  294. package/src/components/legacy-reports/tabs/legacy-report-dimensions-tab.component.tsx +590 -0
  295. package/src/components/legacy-reports/tabs/legacy-report-indicators-tab.component.tsx +490 -0
  296. package/src/components/legacy-reports/tabs/legacy-report-json-preview-tab.component.tsx +272 -0
  297. package/src/components/legacy-reports/tabs/legacy-report-overview-tab.component.tsx +218 -0
  298. package/src/components/legacy-reports/tabs/legacy-report-parameters-tab.component.tsx +279 -0
  299. package/src/components/linelist/definition/indicator-to-sql.utils.ts +281 -0
  300. package/src/components/linelist/definition/linelist-definition-editor.component.tsx +1676 -0
  301. package/src/components/linelist/definition/linelist-definition-editor.scss +461 -0
  302. package/src/components/linelist/definition/linelist-definition.types.ts +108 -0
  303. package/src/components/linelist/definition/panels/column-properties-panel.component.tsx +328 -0
  304. package/src/components/linelist/definition/panels/column-selector-panel.component.tsx +1082 -0
  305. package/src/components/linelist/definition/panels/column-selector-panel.scss +100 -0
  306. package/src/components/linelist/definition/panels/custom-sql-column-modal.component.tsx +248 -0
  307. package/src/components/linelist/definition/panels/encounter-diagnosis-column-modal.component.tsx +300 -0
  308. package/src/components/linelist/definition/panels/encounter-diagnosis-column-modal.scss +21 -0
  309. package/src/components/linelist/definition/panels/multi-datasource-selector.component.tsx +332 -0
  310. package/src/components/linelist/definition/panels/multi-datasource-selector.scss +161 -0
  311. package/src/components/linelist/definition/panels/observation-column-modal.component.tsx +450 -0
  312. package/src/components/linelist/definition/panels/observation-column-modal.scss +29 -0
  313. package/src/components/linelist/definition/panels/parameter-panel.component.tsx +659 -0
  314. package/src/components/linelist/definition/panels/parameter-panel.scss +64 -0
  315. package/src/components/linelist/definition/panels/population-source-selector.component.tsx +407 -0
  316. package/src/components/linelist/definition/panels/population-source-selector.scss +187 -0
  317. package/src/components/linelist/design/panels/column-category-selector.component.tsx +433 -0
  318. package/src/components/linelist/design/panels/column-category-selector.scss +329 -0
  319. package/src/components/linelist/design/panels/column-debug-panel.component.tsx +197 -0
  320. package/src/components/linelist/design/panels/column-debug-panel.scss +141 -0
  321. package/src/components/linelist/design/panels/data-catalogue.component.tsx +1062 -0
  322. package/src/components/linelist/design/panels/data-catalogue.scss +263 -0
  323. package/src/components/linelist/design/panels/display-settings-panel.component.tsx +405 -0
  324. package/src/components/linelist/design/panels/display-settings-panel.scss +78 -0
  325. package/src/components/linelist/design/panels/filtermap-editor.component.tsx +434 -0
  326. package/src/components/linelist/design/panels/filtermap-editor.scss +176 -0
  327. package/src/components/linelist/design/panels/json-preview.component.tsx +114 -0
  328. package/src/components/linelist/design/panels/json-preview.scss +85 -0
  329. package/src/components/linelist/design/panels/query-config-panel.component.tsx +1137 -0
  330. package/src/components/linelist/design/panels/query-config-panel.scss +342 -0
  331. package/src/components/linelist/design/panels/sort-config-panel.component.tsx +330 -0
  332. package/src/components/linelist/design/panels/sort-config-panel.scss +77 -0
  333. package/src/components/linelist/design/panels/sql-builder-panel.component.tsx +475 -0
  334. package/src/components/linelist/design/panels/sql-builder-panel.scss +226 -0
  335. package/src/components/linelist/design/panels/transformation-panel.component.tsx +687 -0
  336. package/src/components/linelist/design/panels/transformation-panel.scss +81 -0
  337. package/src/components/linelist/design/panels/visual-filter-panel.component.tsx +595 -0
  338. package/src/components/linelist/design/panels/visual-filter-panel.scss +76 -0
  339. package/src/components/linelist/linelist-dashboard.page.component.tsx +756 -0
  340. package/src/components/linelist/linelist-dashboard.page.scss +240 -0
  341. package/src/components/linelist/linelist-editor.page.component.tsx +134 -0
  342. package/src/components/linelist/linelist-editor.page.scss +28 -0
  343. package/src/components/linelist/linelist-run-report.page.component.tsx +715 -0
  344. package/src/components/linelist/linelist-run-report.page.scss +136 -0
  345. package/src/components/linelist/modals/linelist-report-builder-modal.component.tsx +1454 -0
  346. package/src/components/linelist/modals/linelist-report-builder-modal.scss +164 -0
  347. package/src/components/linelist/modals/preview-parameter-modal.component.tsx +361 -0
  348. package/src/components/report/dashboard/reports-table.component.tsx +186 -0
  349. package/src/components/report/definition/panels/properties-panel.component.tsx +38 -0
  350. package/src/components/report/definition/panels/report-structure-panel.component.tsx +61 -0
  351. package/src/components/report/definition/panels/selection-panel.component.tsx +58 -0
  352. package/src/components/report/definition/report-definition-editor.component.tsx +99 -0
  353. package/src/components/report/definition/report-definition.types.ts +66 -0
  354. package/src/components/report/design/panels/IndicatorPropertiesPanel.scss +33 -0
  355. package/src/components/report/design/panels/IndicatorPropertiesPanel.tsx +389 -0
  356. package/src/components/report/design/panels/JsonPreviewPanel.scss +32 -0
  357. package/src/components/report/design/panels/JsonPreviewPanel.tsx +104 -0
  358. package/src/components/report/design/panels/mapping-preview-panel.component.tsx +82 -0
  359. package/src/components/report/design/panels/mapping-preview-panel.scss +55 -0
  360. package/src/components/report/design/panels/template-structure-panel.component.tsx +296 -0
  361. package/src/components/report/design/panels/template-structure-panel.scss +81 -0
  362. package/src/components/report/design/report-design-editor.component.tsx +1170 -0
  363. package/src/components/report/design/report-design.types.ts +48 -0
  364. package/src/components/report/design/report-design.utils.ts +86 -0
  365. package/src/components/report/panels/report-detail.component.tsx +71 -0
  366. package/src/components/report/panels/report-panel-shell.component.tsx +60 -0
  367. package/src/components/report/report-dashboard.page.component.tsx +554 -0
  368. package/src/components/report/report-editor.page.component.tsx +590 -0
  369. package/src/components/report/report.routes.tsx +32 -0
  370. package/src/components/report-sections/create-report-section-modal.component.tsx +388 -0
  371. package/src/components/report-sections/hooks/useAgeCategories.ts +25 -0
  372. package/src/components/report-sections/hooks/useSectionEditorState.ts +449 -0
  373. package/src/components/report-sections/panels/Dhis2MappingPanel.tsx +157 -0
  374. package/src/components/report-sections/panels/DisaggregationPanel.tsx +97 -0
  375. package/src/components/report-sections/panels/IndicatorPickerPanel.tsx +201 -0
  376. package/src/components/report-sections/panels/SectionBasicsPanel.tsx +27 -0
  377. package/src/components/report-sections/report-section-preview-modal.component.tsx +548 -0
  378. package/src/components/report-sections/report-sections-page.component.tsx +325 -0
  379. package/src/components/report-sections/section-disaggregation.utils.ts +637 -0
  380. package/src/components/report-sections/section-types.ts +55 -0
  381. package/src/components/report-sections/section-utils.ts +50 -0
  382. package/src/components/report-sections/sections-page.scss +79 -0
  383. package/src/components/run-reports/run-reports-page.component.tsx +8 -0
  384. package/src/components/security/access-denied-page.component.tsx +69 -0
  385. package/src/components/security/access-denied-page.scss +39 -0
  386. package/src/components/security/privilege-route.component.test.tsx +79 -0
  387. package/src/components/security/privilege-route.component.tsx +39 -0
  388. package/src/components/shared/compile-setup-modal.component.tsx +182 -0
  389. package/src/components/shared/concept-selector.component.tsx +245 -0
  390. package/src/components/shared/concept-selector.scss +121 -0
  391. package/src/components/shared/header/builder-header-actions.component.tsx +38 -0
  392. package/src/components/shared/header/header.component.tsx +85 -0
  393. package/src/components/shared/header/header.scss +150 -0
  394. package/src/components/shared/header/illustration.component.tsx +34 -0
  395. package/src/components/shared/indicator-properties-panel/IndicatorPropertiesPanel.scss +33 -0
  396. package/src/components/shared/indicator-properties-panel/IndicatorPropertiesPanel.tsx +389 -0
  397. package/src/components/shared/json-preview-panel/JsonPreviewPanel.scss +32 -0
  398. package/src/components/shared/json-preview-panel/JsonPreviewPanel.tsx +104 -0
  399. package/src/components/shared/mapping-preview-panel/mapping-preview-panel.component.tsx +82 -0
  400. package/src/components/shared/mapping-preview-panel/mapping-preview-panel.scss +55 -0
  401. package/src/components/shared/preview/query-results-preview.component.tsx +204 -0
  402. package/src/components/shared/template-structure-panel/template-structure-panel.component.tsx +296 -0
  403. package/src/components/shared/template-structure-panel/template-structure-panel.scss +81 -0
  404. package/src/config-schema.ts +43 -0
  405. package/src/constants/privileges.test.ts +53 -0
  406. package/src/constants/privileges.ts +118 -0
  407. package/src/features/dashboards/DashboardRendererPage.tsx +174 -0
  408. package/src/features/dashboards/DashboardsPage.tsx +164 -0
  409. package/src/features/dashboards/EtlDashboardPage.tsx +32 -0
  410. package/src/features/dashboards/components/DashboardEmptyState.tsx +41 -0
  411. package/src/features/dashboards/components/DashboardGrid.tsx +48 -0
  412. package/src/features/dashboards/components/DashboardHeader.tsx +58 -0
  413. package/src/features/dashboards/components/DashboardSection.tsx +46 -0
  414. package/src/features/dashboards/components/ReportLaunchCard.tsx +51 -0
  415. package/src/features/dashboards/components/WidgetSlotDispatcher.tsx +61 -0
  416. package/src/features/dashboards/components/WidgetUnavailableCard.tsx +36 -0
  417. package/src/features/dashboards/dashboards.scss +196 -0
  418. package/src/features/dashboards/hooks/useDashboard.ts +61 -0
  419. package/src/features/dashboards/hooks/useDashboardWidgets.ts +74 -0
  420. package/src/features/dashboards/hooks/useDashboards.ts +56 -0
  421. package/src/features/dashboards/hooks/useMonitorDataMap.ts +84 -0
  422. package/src/features/dashboards/index.ts +11 -0
  423. package/src/features/dashboards/utils/dashboard-config.util.test.ts +255 -0
  424. package/src/features/dashboards/utils/dashboard-config.util.ts +382 -0
  425. package/src/features/report-visualizer/ReportVisualizerPage.tsx +448 -0
  426. package/src/features/report-visualizer/components/Export/ExportMenu.tsx +141 -0
  427. package/src/features/report-visualizer/components/Export/index.ts +2 -0
  428. package/src/features/report-visualizer/components/ReportExplorer/QuickFilters.tsx +152 -0
  429. package/src/features/report-visualizer/components/ReportExplorer/ReportCategoryFilter.tsx +54 -0
  430. package/src/features/report-visualizer/components/ReportExplorer/ReportExplorerPanel.tsx +201 -0
  431. package/src/features/report-visualizer/components/ReportExplorer/ReportExplorerRail.tsx +202 -0
  432. package/src/features/report-visualizer/components/ReportExplorer/ReportList.tsx +287 -0
  433. package/src/features/report-visualizer/components/ReportExplorer/ReportSearch.tsx +151 -0
  434. package/src/features/report-visualizer/components/ReportExplorer/ReportTagFilter.tsx +100 -0
  435. package/src/features/report-visualizer/components/ReportExplorer/RunReportButton.tsx +42 -0
  436. package/src/features/report-visualizer/components/ReportExplorer/RuntimeParameterForm.tsx +259 -0
  437. package/src/features/report-visualizer/components/ReportExplorer/index.ts +8 -0
  438. package/src/features/report-visualizer/components/ReportWorkspace/ReportLayer.tsx +33 -0
  439. package/src/features/report-visualizer/components/ReportWorkspace/ReportSummary.tsx +88 -0
  440. package/src/features/report-visualizer/components/ReportWorkspace/ReportViewTabs.tsx +72 -0
  441. package/src/features/report-visualizer/components/ReportWorkspace/ReportWorkspaceHeader.tsx +92 -0
  442. package/src/features/report-visualizer/components/ReportWorkspace/ReportWorkspacePanel.tsx +242 -0
  443. package/src/features/report-visualizer/components/ReportWorkspace/index.ts +5 -0
  444. package/src/features/report-visualizer/components/TableView/ColumnVisibilityMenu.tsx +206 -0
  445. package/src/features/report-visualizer/components/TableView/ReportTable.tsx +253 -0
  446. package/src/features/report-visualizer/components/TableView/index.ts +2 -0
  447. package/src/features/report-visualizer/hooks/index.ts +4 -0
  448. package/src/features/report-visualizer/hooks/useExplorerPreferences.ts +103 -0
  449. package/src/features/report-visualizer/hooks/useReportCapabilities.ts +112 -0
  450. package/src/features/report-visualizer/hooks/useReportParameters.ts +152 -0
  451. package/src/features/report-visualizer/hooks/useReports.ts +135 -0
  452. package/src/features/report-visualizer/index.ts +6 -0
  453. package/src/features/report-visualizer/report-visualizer.scss +593 -0
  454. package/src/features/report-visualizer/types/index.ts +1 -0
  455. package/src/features/report-visualizer/types/report-visualizer.types.ts +104 -0
  456. package/src/features/report-visualizer/utils/status-colors.ts +117 -0
  457. package/src/globals.d.ts +14 -0
  458. package/src/hooks/address-template.ts +86 -0
  459. package/src/hooks/concept.ts +112 -0
  460. package/src/hooks/indicator/index.ts +1 -0
  461. package/src/hooks/indicator/useIndicatorsByTheme.ts +41 -0
  462. package/src/hooks/openmrs-reference-data.ts +432 -0
  463. package/src/hooks/theme/index.ts +4 -0
  464. package/src/hooks/theme/useDataTheme.ts +61 -0
  465. package/src/hooks/theme/useDataThemes.ts +28 -0
  466. package/src/hooks/theme/useMambaTableMeta.ts +34 -0
  467. package/src/hooks/theme/useMambaTables.ts +95 -0
  468. package/src/hooks/use-report-builder-privileges.test.tsx +98 -0
  469. package/src/hooks/use-report-builder-privileges.ts +58 -0
  470. package/src/index.ts +29 -0
  471. package/src/resources/address-template/address-template.api.ts +180 -0
  472. package/src/resources/agegroup/agegroups.api.ts +58 -0
  473. package/src/resources/concept/concept.api.ts +116 -0
  474. package/src/resources/concepts/concept-mapper.ts +21 -0
  475. package/src/resources/concepts/concept-types.ts +31 -0
  476. package/src/resources/concepts/concepts.resource.ts +44 -0
  477. package/src/resources/concepts/useConceptSearch.ts +41 -0
  478. package/src/resources/dashboard/dashboard.api.ts +107 -0
  479. package/src/resources/etl-monitor/etl-monitor-test.api.ts +409 -0
  480. package/src/resources/etl-monitor/etl-monitor.api.ts +381 -0
  481. package/src/resources/etl-source/etl-source.api.ts +65 -0
  482. package/src/resources/etl-task/etl-task.api.ts +96 -0
  483. package/src/resources/etl-task/etl-task.types.ts +31 -0
  484. package/src/resources/indicator/indicators.api.ts +125 -0
  485. package/src/resources/legacy-reports/legacy-reports.api.ts +144 -0
  486. package/src/resources/linelist/linelist-reports.api.ts +616 -0
  487. package/src/resources/location/location.api.ts +135 -0
  488. package/src/resources/openmrs-api.ts +40 -0
  489. package/src/resources/patient-identifier-type/patient-identifier-type.api.ts +65 -0
  490. package/src/resources/person-attribute-type/person-attribute-type.api.ts +71 -0
  491. package/src/resources/preview/sql-preview.api.ts +54 -0
  492. package/src/resources/report/reports.api.ts +140 -0
  493. package/src/resources/report-category/report-category.api.ts +41 -0
  494. package/src/resources/report-import-export/import-export-api.ts +164 -0
  495. package/src/resources/report-library/report-library.api.ts +63 -0
  496. package/src/resources/report-section/report-sections.api.ts +73 -0
  497. package/src/resources/report-section/section-preview.api.ts +25 -0
  498. package/src/resources/theme/data-theme.api.ts +88 -0
  499. package/src/resources/theme/etl-schema.api.ts +101 -0
  500. package/src/resources/theme/etl-table-meta.api.ts +45 -0
  501. package/src/root.component.tsx +20 -0
  502. package/src/routes/report-builder.routes.tsx +160 -0
  503. package/src/routes/report-builder.scss +56 -0
  504. package/src/routes.json +28 -0
  505. package/src/sample-template.ts +121 -0
  506. package/src/template-utils.ts +110 -0
  507. package/src/types/carbon-react-inert.d.ts +13 -0
  508. package/src/types/condition-operators.ts +63 -0
  509. package/src/types/dashboard/dashboard.types.ts +137 -0
  510. package/src/types/dashboard/index.ts +4 -0
  511. package/src/types/etl/etl-types.ts +238 -0
  512. package/src/types/etl-monitor/etl-monitor-builder.types.ts +555 -0
  513. package/src/types/etl-monitor/etl-monitor-v2.types.ts +397 -0
  514. package/src/types/etl-monitor/etl-monitor.types.ts +188 -0
  515. package/src/types/etl-monitor/index.ts +6 -0
  516. package/src/types/linelist/compile-config.ts +455 -0
  517. package/src/types/linelist-types.ts +2059 -0
  518. package/src/types/theme/data-theme.types.ts +88 -0
  519. package/src/utils/api-error.utils.test.ts +151 -0
  520. package/src/utils/api-error.utils.ts +131 -0
  521. package/src/utils/config-enhancer.ts +62 -0
  522. package/src/utils/etl-browser/etl-browser.util.test.ts +145 -0
  523. package/src/utils/etl-browser/export-csv.util.ts +43 -0
  524. package/src/utils/etl-browser/group-tables.util.ts +84 -0
  525. package/src/utils/etl-browser/index.ts +4 -0
  526. package/src/utils/etl-browser/summarize-columns.util.ts +27 -0
  527. package/src/utils/etl-browser/table-sql.util.ts +20 -0
  528. package/src/utils/etl-monitor/config-v2-adapter.util.ts +460 -0
  529. package/src/utils/etl-monitor/endpoint-config-normalizer.util.ts +383 -0
  530. package/src/utils/etl-monitor/index.ts +10 -0
  531. package/src/utils/etl-monitor/jsonpath-resolver.util.ts +118 -0
  532. package/src/utils/etl-monitor/normalization-examples.ts +0 -0
  533. package/src/utils/etl-monitor/schema-inspector.util.ts +76 -0
  534. package/src/utils/etl-monitor/semantic-type-detector.util.ts +76 -0
  535. package/src/utils/etl-monitor/status-mapper.util.ts +105 -0
  536. package/src/utils/etl-monitor/value-formatters.util.ts +167 -0
  537. package/src/utils/etl-task/etl-task-card.util.test.ts +239 -0
  538. package/src/utils/etl-task/etl-task-card.util.ts +276 -0
  539. package/src/utils/etl-task/index.ts +1 -0
  540. package/src/utils/html-entities.utils.ts +34 -0
  541. package/src/utils/parameter-resolution.ts +218 -0
  542. package/src/views/etl-monitor-builder/add-monitor-modal.component.scss +0 -0
  543. package/src/views/etl-monitor-builder/add-monitor-modal.component.tsx +210 -0
  544. package/src/views/etl-monitor-builder/etl-monitor-builder.component.scss +157 -0
  545. package/src/views/etl-monitor-builder/etl-monitor-builder.component.tsx +252 -0
  546. package/src/views/etl-monitor-builder/etl-monitor-builder.store.ts +126 -0
  547. package/tools/scss-stub.js +12 -0
  548. package/tools/setup-tests.ts +1 -0
  549. package/translations/en.json +65 -0
  550. package/tsconfig.json +43 -0
  551. package/webpack.config.js +30 -0
@@ -0,0 +1,1935 @@
1
+ import React, { useCallback, useEffect, useMemo, useState } from "react";
2
+ import PivotTableUI from "react-pivottable/PivotTableUI";
3
+ import TableRenderers from "react-pivottable/TableRenderers";
4
+ import Plot from "react-plotly.js";
5
+ import createPlotlyRenderers from "react-pivottable/PlotlyRenderers";
6
+ import Illustration from "./data-visualizer-illustration.component";
7
+ import type { Indicator, Item, ReportType, ReportCategory, RenderType } from "./types";
8
+ import {
9
+ ArrowLeft,
10
+ ArrowRight,
11
+ Catalog,
12
+ ChevronDown,
13
+ ChevronUp,
14
+ CrossTab,
15
+ Intersect,
16
+ ImageService,
17
+ SendAlt,
18
+ DocumentDownload,
19
+ Save,
20
+ List as ListIcon,
21
+ } from "@carbon/react/icons";
22
+ import {
23
+ RadioButton,
24
+ RadioButtonGroup,
25
+ InlineLoading,
26
+ Select,
27
+ SelectItem,
28
+ Accordion,
29
+ AccordionItem,
30
+ Button,
31
+ ButtonSet,
32
+ ComboBox,
33
+ ContentSwitcher,
34
+ DataTableSkeleton,
35
+ DatePicker,
36
+ DatePickerInput,
37
+ Form,
38
+ FormGroup,
39
+ FormLabel,
40
+ Layer,
41
+ Modal,
42
+ Stack,
43
+ Switch,
44
+ TextInput,
45
+ TextArea,
46
+ Tile,
47
+ } from "@carbon/react";
48
+ import ReportingHomeHeader from "./components/header/header.component";
49
+ import {
50
+ CQIReportHeaders,
51
+ reportIndicators,
52
+ dynamicReportOptions,
53
+ personNames,
54
+ Address,
55
+ Demographics,
56
+ AppointmentIndicators,
57
+ } from "./constants";
58
+ import DataList from "./components/data-table/data-table.component";
59
+ import CQIDataList from "./components/cqi-components/cqi-data-table.component";
60
+ import EmptyStateIllustration from "./components/empty-state/empty-state-illustration.component";
61
+ import Panel from "./components/panel/panel.component";
62
+ import pivotTableStyles from "!!raw-loader!react-pivottable/pivottable.css";
63
+ import styles from "./data-visualizer.scss";
64
+ import {
65
+ createColumns,
66
+ downloadReport,
67
+ extractDate,
68
+ formatDate,
69
+ getCategoryIndicator,
70
+ getCohortCategory,
71
+ getDateRange,
72
+ getReport,
73
+ mapDataElements,
74
+ mapOrderDataElements,
75
+ saveReport,
76
+ sendReportToDHIS2,
77
+ useGetEncounterType,
78
+ useGetOrderTypes,
79
+ useGetReportCategories,
80
+ useGetReportLibrary,
81
+ } from "./data-visualizer.resource";
82
+ import dayjs from "dayjs";
83
+ import { showModal, showNotification, showToast } from "@openmrs/esm-framework";
84
+ import ModifierComponent from "./components/popover/modifier-panel";
85
+ import ReportParameterModal from "./report-parameter-modal.component";
86
+ import type { LinelistParameter } from "../../types/linelist-types";
87
+ import {
88
+ RELATIVE_PERIOD_OPTIONS,
89
+ type RelativePeriod,
90
+ resolveRelativePeriod,
91
+ } from "../../utils/parameter-resolution";
92
+
93
+ // Import parameter input components
94
+ import DateParameterInput from "./parameter-inputs/date-parameter-input.component";
95
+ import NumberParameterInput from "./parameter-inputs/number-parameter-input.component";
96
+ import BooleanParameterInput from "./parameter-inputs/boolean-parameter-input.component";
97
+ import ListParameterInput from "./parameter-inputs/list-parameter-input.component";
98
+ import LocationParameterInput from "./parameter-inputs/location-parameter-input.component";
99
+ import ConceptParameterInput from "./parameter-inputs/concept-parameter-input.component";
100
+ import IdentifierTypeParameterInput from "./parameter-inputs/identifier-type-parameter-input.component";
101
+ import PersonAttributeParameterInput from "./parameter-inputs/person-attribute-parameter-input.component";
102
+ import TextParameterInput from "./parameter-inputs/text-parameter-input.component";
103
+
104
+ type ChartType = "list" | "pivot" | "aggregate" | "linelist";
105
+ type ReportingDuration = "fixed" | "relative";
106
+ export type CQIReportingCohort =
107
+ | "Patients with encounters"
108
+ | "Patients on appointment";
109
+
110
+ type ReportLibraryItem = {
111
+ uuid: string;
112
+ name: string;
113
+ description?: string;
114
+ code?: string;
115
+ sourceType?: string;
116
+ reportDefinitionUuid?: string;
117
+ reportBuilderReportUuid?: string;
118
+ reportType?: string;
119
+ migrated?: boolean;
120
+ retired?: boolean;
121
+ category?: {
122
+ uuid?: string;
123
+ name?: string;
124
+ display?: string;
125
+ description?: string;
126
+ };
127
+ metaJson?: string;
128
+ };
129
+
130
+ const DataVisualizer: React.FC = () => {
131
+ const PlotlyRenderers = createPlotlyRenderers(Plot);
132
+
133
+ const [tableHeaders, setTableHeaders] = useState([]);
134
+ const [data, setData] = useState([]);
135
+ const [pivotTableData, setPivotTableData] = useState<any[]>([]);
136
+ const [chartType, setChartType] = useState<ChartType>("list");
137
+ const [reportType, setReportType] = useState<ReportType>("fixed");
138
+ const [reportCategory, setReportCategory] = useState<{
139
+ category?: ReportCategory;
140
+ renderType?: RenderType;
141
+ categoryUuid?: string;
142
+ categoryName?: string;
143
+ }>({
144
+ category: undefined,
145
+ renderType: undefined,
146
+ categoryUuid: undefined,
147
+ categoryName: undefined,
148
+ });
149
+ const [reportingDuration, setReportingDuration] =
150
+ useState<ReportingDuration>("fixed");
151
+
152
+ const { reportLibrary } = useGetReportLibrary();
153
+ const { reportCategories } = useGetReportCategories();
154
+
155
+ const reportTypes = useMemo(
156
+ () =>
157
+ (reportCategories ?? []).map((category) => ({
158
+ id: category?.uuid,
159
+ key: category?.uuid,
160
+ label: category?.name,
161
+ name: category?.name,
162
+ uuid: category?.uuid,
163
+ })),
164
+ [reportCategories]
165
+ );
166
+
167
+ const reportPeriod = [
168
+ { id: "today", label: "Today" },
169
+ { id: "week", label: "This Week" },
170
+ { id: "month", label: "This Month" },
171
+ { id: "quarter", label: "This Quarter" },
172
+ { id: "lastQuarter", label: "Last Quarter" },
173
+ ];
174
+
175
+ const toReportOption = (report: ReportLibraryItem) => ({
176
+ id:
177
+ report?.reportDefinitionUuid ||
178
+ report?.reportBuilderReportUuid ||
179
+ report?.uuid,
180
+ uuid: report?.uuid,
181
+ label: report?.name,
182
+ name: report?.name,
183
+ sourceType: report?.sourceType,
184
+ reportType: report?.reportType,
185
+ code: report?.code,
186
+ category: report?.category?.name,
187
+ metaJson: report?.metaJson,
188
+ });
189
+
190
+ const getReportsByCategoryName = useCallback(
191
+ (categoryName: string) => {
192
+ return (reportLibrary ?? [])
193
+ .filter(
194
+ (report: ReportLibraryItem) =>
195
+ report?.category?.name?.toLowerCase() === categoryName?.toLowerCase()
196
+ )
197
+ .map(toReportOption);
198
+ },
199
+ [reportLibrary]
200
+ );
201
+
202
+ const facilityReports = useMemo(
203
+ () => getReportsByCategoryName("FACILITY REPORTS"),
204
+ [getReportsByCategoryName]
205
+ );
206
+
207
+ const donorReports = useMemo(
208
+ () => getReportsByCategoryName("MER INDICATOR REPORTS"),
209
+ [getReportsByCategoryName]
210
+ );
211
+
212
+ const nationalReports = useMemo(
213
+ () => getReportsByCategoryName("NATIONAL REPORTS"),
214
+ [getReportsByCategoryName]
215
+ );
216
+
217
+ const cqiReports = useMemo(
218
+ () => getReportsByCategoryName("CQI REPORTS"),
219
+ [getReportsByCategoryName]
220
+ );
221
+
222
+ const integrationDataExports = useMemo(
223
+ () => getReportsByCategoryName("Integration Data Exports"),
224
+ [getReportsByCategoryName]
225
+ );
226
+
227
+ const [reportingPeriod, setReportingPeriod] = useState<Item | null>(null);
228
+ const [selectedIndicators, setSelectedIndicators] =
229
+ useState<Indicator | null>(null);
230
+ const [selectedReport, setSelectedReport] = useState<Item | null>(null);
231
+ const [cqiReportingCohort, setCQIReportingCohort] =
232
+ useState<CQIReportingCohort>("Patients with encounters");
233
+
234
+ const [startDate, setStartDate] = useState(new Date());
235
+ const [endDate, setEndDate] = useState(new Date());
236
+ const [loading, setLoading] = useState(true);
237
+ const [showLineList, setShowLineList] = useState(false);
238
+ const [availableParameters, setAvailableParameters] = useState([]);
239
+ const [selectedParameters, setSelectedParameters] = useState<
240
+ Array<Indicator>
241
+ >([]);
242
+ const [showFilters, setShowFilters] = useState(true);
243
+ const [reportName, setReportName] = useState("Patient List");
244
+ const { encounterTypes } = useGetEncounterType();
245
+ const { orderTypes } = useGetOrderTypes();
246
+ const [saveReportModal, setSaveReportModal] = useState(false);
247
+ const [reportTitle, setReportTitle] = useState("");
248
+ const [reportDescription, setReportDescription] = useState("");
249
+ const [htmlContent, setHTML] = useState("");
250
+ const [isDownloading, setIsDownloading] = useState(false);
251
+ const [isSendingReport, setIsSendingReport] = useState(false);
252
+ const [dhisJson, setDhisJson] = useState({});
253
+ const [parameterModalOpen, setParameterModalOpen] = useState(false);
254
+ const [reportParameters, setReportParameters] = useState<LinelistParameter[]>([]);
255
+ const [parameterValues, setParameterValues] = useState<Record<string, any>>({});
256
+
257
+ // Parameter state for inline rendering
258
+ const [paramErrors, setParamErrors] = useState<Record<string, string>>({});
259
+ const [searchQueries, setSearchQueries] = useState<Record<string, string>>({});
260
+ const [dateMode, setDateMode] = useState<'FIXED' | 'RELATIVE'>('FIXED');
261
+ const [relativePeriod, setRelativePeriod] = useState<RelativePeriod | ''>('');
262
+
263
+ const [selectedDynamicReportType, setSelectedDynamicReportType] =
264
+ useState<Item | null>(null);
265
+ const [dynamicReportTypes, setDynamicReportTypes] = useState([]);
266
+
267
+ const hasFixedCategory = !!reportCategory.categoryUuid;
268
+ const hasDynamicCategory = !!selectedDynamicReportType;
269
+
270
+ useEffect(() => {
271
+ setSelectedReport(null);
272
+ setData([]);
273
+ setPivotTableData([]);
274
+ setHTML("");
275
+ setShowLineList(false);
276
+ }, [reportCategory, reportType]);
277
+
278
+ const handleSelectedReport = ({ selectedItem }) => {
279
+ console.log('=== handleSelectedReport called ===');
280
+ console.log('selectedItem:', selectedItem);
281
+
282
+ setSelectedReport(selectedItem ?? null);
283
+
284
+ // Parse parameters from metaJson and initialize default values
285
+ if (selectedItem?.metaJson) {
286
+ try {
287
+ const meta = JSON.parse(selectedItem.metaJson);
288
+ console.log('Parsed metaJson:', meta);
289
+
290
+ if (meta?.parameters && Array.isArray(meta.parameters)) {
291
+ console.log('Found parameters in metaJson:', meta.parameters);
292
+ console.log('Number of parameters:', meta.parameters.length);
293
+ meta.parameters.forEach((param, idx) => {
294
+ console.log(` Param ${idx}:`, param);
295
+ });
296
+ setReportParameters(meta.parameters);
297
+
298
+ // Initialize parameter values with defaults
299
+ const defaults: Record<string, any> = {};
300
+ meta.parameters.forEach((param) => {
301
+ if (param.defaultValue) {
302
+ defaults[param.name] = param.defaultValue;
303
+ } else if (param.type === 'DATE' || param.type === 'DATETIME') {
304
+ // Set default dates for date parameters without defaults
305
+ if (param.name === 'startDate') {
306
+ const startDate = new Date();
307
+ startDate.setMonth(startDate.getMonth() - 6);
308
+ defaults[param.name] = startDate.toISOString().split('T')[0];
309
+ } else if (param.name === 'endDate') {
310
+ defaults[param.name] = new Date().toISOString().split('T')[0];
311
+ }
312
+ }
313
+ });
314
+ setParameterValues(defaults);
315
+ } else {
316
+ console.log('No parameters found in metaJson, setting to empty array');
317
+ setReportParameters([]);
318
+ setParameterValues({});
319
+ }
320
+ } catch (error) {
321
+ console.error('Failed to parse metaJson:', error);
322
+ setReportParameters([]);
323
+ setParameterValues({});
324
+ }
325
+ } else {
326
+ console.log('No metaJson on selected report, setting parameters to empty array');
327
+ setReportParameters([]);
328
+ setParameterValues({});
329
+ }
330
+
331
+ // Reset date mode and errors for new selection
332
+ setDateMode('FIXED');
333
+ setRelativePeriod('');
334
+ setParamErrors({});
335
+ setSearchQueries({});
336
+
337
+ // Detect linelist reports by reportType
338
+ if (selectedItem?.reportType === 'LINE_LIST' || selectedItem?.reportType === 'linelist') {
339
+ setChartType('linelist');
340
+ } else if (chartType === 'linelist') {
341
+ // Reset chart type if not a linelist report
342
+ setChartType('list');
343
+ }
344
+ };
345
+
346
+ const handleChartTypeChange = ({ name }) => {
347
+ setChartType(name);
348
+ };
349
+
350
+ const handleReportTypeChange = ({ name }) => {
351
+ setReportType(name);
352
+ setSelectedReport(null);
353
+ setSelectedIndicators(null);
354
+ setAvailableParameters([]);
355
+ setSelectedParameters([]);
356
+ setData([]);
357
+ setPivotTableData([]);
358
+ setHTML("");
359
+ setShowLineList(false);
360
+ setReportCategory({
361
+ category: undefined,
362
+ renderType: undefined,
363
+ categoryUuid: undefined,
364
+ categoryName: undefined,
365
+ });
366
+ setSelectedDynamicReportType(null);
367
+ setDynamicReportTypes([]);
368
+ setChartType("list");
369
+ };
370
+
371
+ const handleReportingDurationChange = (period) => {
372
+ setReportingDuration(period);
373
+ };
374
+
375
+ const handleCohortChange = (cohort) => {
376
+ setCQIReportingCohort(cohort);
377
+ };
378
+
379
+ const showSaveReportModal = () => {
380
+ setSaveReportModal(true);
381
+ };
382
+
383
+ const closeReportModal = () => {
384
+ setSaveReportModal(false);
385
+ };
386
+
387
+ const confirmSendReport = () => {
388
+ if (!selectedReport) return;
389
+
390
+ const dispose = showModal("confirm-modal", {
391
+ close: () => dispose(),
392
+ submit: () => {
393
+ handleSendToDHIS2();
394
+ dispose();
395
+ },
396
+ report: selectedReport.label,
397
+ });
398
+ };
399
+
400
+ const handleSendToDHIS2 = useCallback(() => {
401
+ if (!selectedReport) return;
402
+
403
+ setIsSendingReport(true);
404
+
405
+ sendReportToDHIS2(selectedReport.id, dhisJson).then(
406
+ (response) => {
407
+ if (response.status === 200) {
408
+ showToast({
409
+ critical: true,
410
+ title: "Sending Report To DHIS2",
411
+ kind: "success",
412
+ description: `Report ${selectedReport.label} sent Successfully`,
413
+ });
414
+ } else {
415
+ showNotification({
416
+ title: "Error sending report to DHIS2",
417
+ kind: "error",
418
+ critical: true,
419
+ description: `Failed with error code ${response.status}, Contact System Administrator`,
420
+ });
421
+ }
422
+
423
+ setIsSendingReport(false);
424
+ },
425
+ (error) => {
426
+ showNotification({
427
+ title: "Error sending report to DHIS2",
428
+ kind: "error",
429
+ critical: true,
430
+ description: error?.message,
431
+ });
432
+ setIsSendingReport(false);
433
+ }
434
+ );
435
+ }, [selectedReport, dhisJson]);
436
+
437
+ const handleDownloadReport = useCallback(() => {
438
+ if (!selectedReport) return;
439
+
440
+ setIsDownloading(true);
441
+
442
+ downloadReport({
443
+ uuid: selectedReport.id,
444
+ startDate: formatDate(startDate),
445
+ endDate: formatDate(endDate),
446
+ reportCategory: reportCategory.category,
447
+ reportingCohort: cqiReportingCohort,
448
+ }).then(
449
+ async (response) => {
450
+ try {
451
+ const blob = await response.blob();
452
+ const url = window.URL.createObjectURL(blob);
453
+ const filename = response?.headers
454
+ ?.get("Content-Disposition")
455
+ ?.match(/filename=(.+)/)?.[1];
456
+
457
+ const a = document.createElement("a");
458
+ a.href = url;
459
+ a.download = filename ?? `${selectedReport.label}.csv`;
460
+ a.click();
461
+ window.URL.revokeObjectURL(url);
462
+ } catch (error) {
463
+ showNotification({
464
+ title: "Error downloading Report",
465
+ kind: "error",
466
+ critical: true,
467
+ description: `Error downloading file: ${error?.message}`,
468
+ });
469
+ }
470
+ setIsDownloading(false);
471
+ },
472
+ (error) => {
473
+ showNotification({
474
+ title: "Error downloading Report",
475
+ kind: "error",
476
+ critical: true,
477
+ description: error?.message,
478
+ });
479
+ setIsDownloading(false);
480
+ }
481
+ );
482
+ }, [startDate, endDate, selectedReport, reportCategory, cqiReportingCohort]);
483
+
484
+ const handleSaveReport = useCallback(() => {
485
+ saveReport({
486
+ reportName: reportTitle,
487
+ reportDescription: reportDescription,
488
+ reportType: pivotTableData?.["rendererName"],
489
+ columns: "",
490
+ rows: "",
491
+ report_request_object: JSON.stringify(pivotTableData),
492
+ }).then(
493
+ (response) => {
494
+ if (response.status === 201) {
495
+ showToast({
496
+ critical: true,
497
+ title: "Saving Report",
498
+ kind: "success",
499
+ description: `Report ${reportTitle} saved Successfully`,
500
+ });
501
+ setSaveReportModal(false);
502
+ }
503
+ },
504
+ (error) => {
505
+ showNotification({
506
+ title: "Error saving report",
507
+ kind: "error",
508
+ critical: true,
509
+ description: error?.message,
510
+ });
511
+ }
512
+ );
513
+ }, [reportDescription, pivotTableData, reportTitle]);
514
+
515
+ const handleReportTitleChange = (event) => {
516
+ setReportTitle(event.target.value);
517
+ };
518
+
519
+ const handleReportDescChange = (event) => {
520
+ setReportDescription(event.target.value);
521
+ };
522
+
523
+ const moveAllFromLeftToRight = (selectedParameter) => {
524
+ const updatedAvailableParameters = availableParameters.filter(
525
+ (parameter) => parameter !== selectedParameter
526
+ );
527
+ setAvailableParameters(updatedAvailableParameters);
528
+ setSelectedParameters([...selectedParameters, selectedParameter]);
529
+ };
530
+
531
+ const moveAllFromRightToLeft = (selectedParameter) => {
532
+ const updatedSelectedParameters = selectedParameters.filter(
533
+ (parameter) => parameter !== selectedParameter
534
+ );
535
+ setSelectedParameters(updatedSelectedParameters);
536
+
537
+ let updatedAvailableParameters = [...availableParameters];
538
+
539
+ selectedIndicators?.attributes?.filter((parameter) => {
540
+ if (parameter === selectedParameter) {
541
+ updatedAvailableParameters = [
542
+ ...updatedAvailableParameters,
543
+ selectedParameter,
544
+ ];
545
+ }
546
+ });
547
+
548
+ setAvailableParameters(updatedAvailableParameters);
549
+ };
550
+
551
+ const moveAllParametersLeft = () => {
552
+ setAvailableParameters(selectedIndicators?.attributes ?? []);
553
+ setSelectedParameters([]);
554
+ };
555
+
556
+ const moveAllParametersRight = () => {
557
+ setSelectedParameters([...availableParameters, ...selectedParameters]);
558
+ setAvailableParameters([]);
559
+ };
560
+
561
+ const handleIndicatorChange = useCallback(
562
+ ({ selectedItem }) => {
563
+ const indicator = selectedItem;
564
+
565
+ if (!selectedItem) {
566
+ setSelectedIndicators(null);
567
+ setAvailableParameters([]);
568
+ return;
569
+ }
570
+
571
+ getCategoryIndicator(selectedItem?.id, selectedItem?.type).then(
572
+ (response) => {
573
+ let results;
574
+
575
+ switch (selectedItem.type) {
576
+ case "PersonName":
577
+ results = personNames;
578
+ break;
579
+ case "Demographics":
580
+ results = Demographics;
581
+ break;
582
+ case "Address":
583
+ results = Address;
584
+ break;
585
+ case "Appointment":
586
+ results = AppointmentIndicators;
587
+ break;
588
+ case "Condition":
589
+ results = mapDataElements(response, null, "concepts");
590
+ break;
591
+ case "Orders":
592
+ results = mapOrderDataElements(
593
+ response,
594
+ "Orders",
595
+ selectedItem.id
596
+ );
597
+ break;
598
+ case "":
599
+ results = mapDataElements(response, null, "concepts");
600
+ break;
601
+ default:
602
+ results = mapDataElements(response?.results, selectedItem.type);
603
+ break;
604
+ }
605
+
606
+ setSelectedIndicators(indicator);
607
+
608
+ const filteredArray = results?.filter(
609
+ (resultParameter) =>
610
+ !selectedParameters?.some(
611
+ (parameter) => parameter.id === resultParameter.id
612
+ )
613
+ );
614
+
615
+ indicator.attributes = filteredArray;
616
+ setAvailableParameters(indicator.attributes ?? []);
617
+ },
618
+ (error) => {
619
+ showNotification({
620
+ title: "Error fetching Indicators",
621
+ kind: "error",
622
+ critical: true,
623
+ description: error?.message,
624
+ });
625
+ }
626
+ );
627
+ },
628
+ [selectedParameters]
629
+ );
630
+
631
+ const handleSelectedReportDefinition = ({ selectedItem }) => {
632
+ setSelectedReport(selectedItem ?? null);
633
+ };
634
+
635
+ const handleSelectedDynamicReportType = ({ selectedItem }) => {
636
+ setSelectedDynamicReportType(selectedItem ?? null);
637
+ setSelectedReport(null);
638
+ setDynamicReportTypes([]);
639
+
640
+ if (!selectedItem) {
641
+ return;
642
+ }
643
+
644
+ if (selectedItem.id === "reportDefinition") {
645
+ setDynamicReportTypes(facilityReports ?? []);
646
+ } else {
647
+ getCohortCategory(selectedItem.id).then((response) => {
648
+ const responseResults =
649
+ selectedItem.id === "patientSearch" ? response : response?.results;
650
+
651
+ const reports =
652
+ responseResults?.map((responseItem) => ({
653
+ id: responseItem?.uuid,
654
+ label: responseItem?.name,
655
+ })) ?? [];
656
+
657
+ setDynamicReportTypes(reports);
658
+ });
659
+ }
660
+ };
661
+
662
+ const handleFiltersToggle = () => {
663
+ setShowFilters((prev) => !prev);
664
+ };
665
+
666
+ const handleStartDateChange = (selectedDate) => {
667
+ setStartDate(selectedDate[0]);
668
+ };
669
+
670
+ const handleEndDateChange = (selectedDate) => {
671
+ setEndDate(selectedDate[0]);
672
+ };
673
+
674
+ const handleReportCategoryChange = ({ selectedItem }) => {
675
+ if (!selectedItem) {
676
+ setReportCategory({
677
+ category: undefined,
678
+ renderType: undefined,
679
+ categoryUuid: undefined,
680
+ categoryName: undefined,
681
+ });
682
+ setSelectedReport(null);
683
+ setChartType("list");
684
+ return;
685
+ }
686
+
687
+ const categoryName = selectedItem?.name || selectedItem?.label || "";
688
+ const categoryUuid = selectedItem?.uuid || selectedItem?.id;
689
+
690
+ setSelectedReport(null);
691
+
692
+ if (categoryName === "NATIONAL REPORTS") {
693
+ setReportCategory({
694
+ category: "national",
695
+ renderType: "html",
696
+ categoryUuid,
697
+ categoryName,
698
+ });
699
+ setChartType("aggregate");
700
+ } else if (categoryName === "CQI REPORTS") {
701
+ setReportCategory({
702
+ category: "cqi",
703
+ renderType: undefined,
704
+ categoryUuid,
705
+ categoryName,
706
+ });
707
+ setChartType("list");
708
+ } else if (categoryName === "MER INDICATOR REPORTS") {
709
+ setReportCategory({
710
+ category: "donor",
711
+ renderType: "html",
712
+ categoryUuid,
713
+ categoryName,
714
+ });
715
+ setChartType("aggregate");
716
+ } else if (categoryName === "Integration Data Exports") {
717
+ setReportCategory({
718
+ category: "integration",
719
+ renderType: undefined,
720
+ categoryUuid,
721
+ categoryName,
722
+ });
723
+ setChartType("list");
724
+ } else {
725
+ setReportCategory({
726
+ category: "facility",
727
+ renderType: "list",
728
+ categoryUuid,
729
+ categoryName,
730
+ });
731
+ setChartType("list");
732
+ }
733
+ };
734
+
735
+ const handleReportingPeriod = (selectedPeriod) => {
736
+ setReportingPeriod(selectedPeriod?.selectedItem ?? null);
737
+
738
+ if (!selectedPeriod?.selectedItem?.id) {
739
+ return;
740
+ }
741
+
742
+ const dateRange = getDateRange(selectedPeriod?.selectedItem?.id);
743
+ setStartDate(dateRange.start);
744
+ setEndDate(dateRange.end);
745
+ };
746
+
747
+ const changeModifier = (selectedParameter, type) => {
748
+ setSelectedParameters((selectedParameters) =>
749
+ selectedParameters.map((parameter) =>
750
+ parameter.id === selectedParameter.id
751
+ ? {
752
+ ...parameter,
753
+ modifier: addORSubtract(selectedParameter?.modifier, type),
754
+ }
755
+ : parameter
756
+ )
757
+ );
758
+ };
759
+
760
+ const addORSubtract = (value, type) => {
761
+ if (type === "add") {
762
+ return value + 1;
763
+ } else if (type === "subtract" && value > 1) {
764
+ return value - 1;
765
+ } else {
766
+ return value;
767
+ }
768
+ };
769
+
770
+ const showModifierPanel = (selectedParameter: Indicator) => {
771
+ setSelectedParameters((selectedParameters) =>
772
+ selectedParameters.map((parameter) =>
773
+ parameter.id === selectedParameter.id
774
+ ? {
775
+ ...parameter,
776
+ showModifierPanel: !selectedParameter?.showModifierPanel,
777
+ }
778
+ : parameter
779
+ )
780
+ );
781
+ };
782
+
783
+ // eslint-disable-next-line react-hooks/exhaustive-deps
784
+ const handleUpdateReport = useCallback(() => {
785
+ if (!selectedReport) return;
786
+
787
+ // If report has parameters, validate and run with inline values
788
+ if (reportParameters.length > 0) {
789
+ // Validate required parameters
790
+ const newErrors: Record<string, string> = {};
791
+ for (const param of reportParameters) {
792
+ const value = parameterValues[param.name];
793
+
794
+ // Skip validation for date parameters in RELATIVE mode (they'll be resolved)
795
+ if ((param.type === 'DATE' || param.type === 'DATETIME') && dateMode === 'RELATIVE') {
796
+ const isStartDate = param.name.toLowerCase() === 'startdate' || param.name.toLowerCase() === 'start_date';
797
+ const isEndDate = param.name.toLowerCase() === 'enddate' || param.name.toLowerCase() === 'end_date';
798
+ if (isStartDate || isEndDate) continue;
799
+ }
800
+
801
+ if (param.required && (!value || value.toString().trim() === '')) {
802
+ newErrors[param.name] = `${param.label} is required`;
803
+ }
804
+ }
805
+
806
+ // Check relative period is selected in RELATIVE mode
807
+ if (dateMode === 'RELATIVE' && !relativePeriod) {
808
+ newErrors.relativePeriod = 'Please select a reporting period';
809
+ }
810
+
811
+ if (Object.keys(newErrors).length > 0) {
812
+ setParamErrors(newErrors);
813
+ showNotification({
814
+ title: 'Validation Error',
815
+ kind: 'error',
816
+ critical: false,
817
+ description: 'Please fill in all required parameters',
818
+ });
819
+ return;
820
+ }
821
+
822
+ // Clear errors and run report
823
+ setParamErrors({});
824
+
825
+ // Build parameter values (resolve relative dates if needed)
826
+ const finalParams = { ...parameterValues };
827
+ if (dateMode === 'RELATIVE' && relativePeriod) {
828
+ const { start, end } = resolveRelativePeriod(relativePeriod);
829
+ const dateParams = reportParameters.filter(p => p.type === 'DATE' || p.type === 'DATETIME');
830
+ dateParams.forEach((param, index) => {
831
+ if (index === 0) {
832
+ finalParams[param.name] = formatDate(start);
833
+ } else if (index === 1) {
834
+ finalParams[param.name] = formatDate(end);
835
+ }
836
+ });
837
+ }
838
+
839
+ // Run report with parameters
840
+ handleRunReportWithParameters(finalParams);
841
+ return;
842
+ }
843
+
844
+ // Original logic for reports without parameters
845
+ setHTML("");
846
+ setShowLineList(true);
847
+ setLoading(true);
848
+ setShowFilters(false);
849
+
850
+ getReport({
851
+ uuid: selectedReport.id,
852
+ startDate: formatDate(startDate),
853
+ endDate: formatDate(endDate),
854
+ reportCategory: reportCategory as {
855
+ category: ReportCategory;
856
+ renderType?: RenderType;
857
+ },
858
+ reportingCohort: cqiReportingCohort,
859
+ }).then(
860
+ (response) => {
861
+ if (response.status === 200) {
862
+ let headers = [];
863
+ let dataForReport: any = [];
864
+ const reportData = response?.data;
865
+
866
+ if (reportType === "fixed") {
867
+ if (reportCategory.category === "cqi") {
868
+ dataForReport = response?.data?.A;
869
+ headers = CQIReportHeaders;
870
+ } else if (reportCategory.renderType === "html") {
871
+ setHTML(reportData?.html ?? "");
872
+ setDhisJson(reportData?.json ?? {});
873
+ } else if (chartType === "linelist" && reportData?._html) {
874
+ // Handle linelist HTML from backend _html key
875
+ setHTML(reportData?._html?.[0]?.html ?? "");
876
+ setDhisJson(reportData?.json ?? {});
877
+ } else {
878
+ const responseReportName = Object.keys(reportData)[0];
879
+
880
+ if (
881
+ reportData[responseReportName] &&
882
+ reportData[responseReportName][0]
883
+ ) {
884
+ let columnNames = Object.keys(reportData[responseReportName][0]);
885
+
886
+ if (
887
+ selectedReport.id === "bf79f017-8591-4eaf-88c9-1cde33226517"
888
+ ) {
889
+ columnNames = columnNames
890
+ .reverse()
891
+ .filter((column) => column !== "EDD" && column !== "Names");
892
+
893
+ headers = createColumns(columnNames);
894
+ dataForReport = reportData[responseReportName]
895
+ .filter((row) => row.PhoneNumber)
896
+ .map((row) => {
897
+ const formattedDate = extractDate(row.LastVisitDate);
898
+
899
+ if (row.PhoneNumber && row.PhoneNumber.startsWith("0")) {
900
+ return {
901
+ ...row,
902
+ PhoneNumber: "256" + row.PhoneNumber.substring(1),
903
+ LastVisitDate: formattedDate,
904
+ };
905
+ }
906
+
907
+ return row;
908
+ });
909
+ } else {
910
+ headers = createColumns(columnNames);
911
+ dataForReport = reportData[responseReportName];
912
+ }
913
+ } else {
914
+ setShowLineList(false);
915
+ }
916
+ }
917
+ } else {
918
+ if (chartType === "linelist" && reportData?._html) {
919
+ // Handle linelist HTML from backend _html key
920
+ setHTML(reportData?._html?.[0]?.html ?? "");
921
+ setDhisJson(reportData?.json ?? {});
922
+ } else if (reportData[0]) {
923
+ const columnNames = Object.keys(reportData[0]);
924
+ headers = createColumns(columnNames);
925
+ dataForReport = reportData;
926
+ } else {
927
+ setShowLineList(false);
928
+ }
929
+ }
930
+
931
+ setLoading(false);
932
+ setShowFilters(false);
933
+ setTableHeaders(headers);
934
+ setData(dataForReport);
935
+ setPivotTableData(dataForReport);
936
+ setReportName(selectedReport?.label);
937
+ }
938
+ },
939
+ (error) => {
940
+ setLoading(false);
941
+ setShowFilters(false);
942
+ showNotification({
943
+ title: "Error fetching report",
944
+ kind: "error",
945
+ critical: true,
946
+ description: error?.message,
947
+ });
948
+ }
949
+ );
950
+ // eslint-disable-next-line react-hooks/exhaustive-deps
951
+ }, [
952
+ cqiReportingCohort,
953
+ chartType,
954
+ dateMode,
955
+ endDate,
956
+ parameterValues,
957
+ relativePeriod,
958
+ reportCategory,
959
+ reportParameters,
960
+ reportType,
961
+ selectedParameters,
962
+ selectedReport,
963
+ startDate,
964
+ selectedDynamicReportType?.label,
965
+ // Note: handleRunReportWithParameters omitted to avoid circular dependency (defined below)
966
+ ]);
967
+
968
+ const handleRunReportWithParameters = useCallback((params: Record<string, any>) => {
969
+ console.log('=== handleRunReportWithParameters called ===');
970
+ console.log('chartType:', chartType);
971
+ console.log('Params from modal:', params);
972
+ console.log('selectedReport:', selectedReport);
973
+
974
+ setParameterValues(params);
975
+ setParameterModalOpen(false);
976
+ setHTML("");
977
+ setShowLineList(true);
978
+ setLoading(true);
979
+ setShowFilters(false);
980
+
981
+ // For linelist reports, only send the parameters defined in the report (from params)
982
+ // For legacy reports, send startDate/endDate as before
983
+ const requestOptions: any = {
984
+ uuid: selectedReport.id,
985
+ reportCategory: reportCategory as {
986
+ category: ReportCategory;
987
+ renderType?: RenderType;
988
+ },
989
+ reportingCohort: cqiReportingCohort,
990
+ parameters: params,
991
+ };
992
+
993
+ console.log('requestOptions before date check:', requestOptions);
994
+
995
+ // Only add startDate/endDate for non-linelist reports
996
+ if (chartType !== 'linelist') {
997
+ requestOptions.startDate = formatDate(startDate);
998
+ requestOptions.endDate = formatDate(endDate);
999
+ console.log('Added startDate/endDate (non-linelist report)');
1000
+ } else {
1001
+ console.log('NOT adding startDate/endDate (linelist report)');
1002
+ }
1003
+
1004
+ console.log('Final requestOptions:', requestOptions);
1005
+
1006
+ getReport(requestOptions).then(
1007
+ (response) => {
1008
+ if (response.status === 200) {
1009
+ let headers = [];
1010
+ let dataForReport: any = [];
1011
+ const reportData = response?.data;
1012
+
1013
+ if (reportType === "fixed") {
1014
+ if (reportCategory.category === "cqi") {
1015
+ dataForReport = response?.data?.A;
1016
+ headers = CQIReportHeaders;
1017
+ } else if (reportCategory.renderType === "html") {
1018
+ setHTML(reportData?.html ?? "");
1019
+ setDhisJson(reportData?.json ?? {});
1020
+ } else if (chartType === "linelist" && reportData?._html) {
1021
+ // Handle linelist HTML from backend _html key
1022
+ setHTML(reportData?._html?.[0]?.html ?? "");
1023
+ setDhisJson(reportData?.json ?? {});
1024
+ } else {
1025
+ const responseReportName = Object.keys(reportData)[0];
1026
+
1027
+ if (
1028
+ reportData[responseReportName] &&
1029
+ reportData[responseReportName][0]
1030
+ ) {
1031
+ let columnNames = Object.keys(reportData[responseReportName][0]);
1032
+
1033
+ if (
1034
+ selectedReport.id === "bf79f017-8591-4eaf-88c9-1cde33226517"
1035
+ ) {
1036
+ columnNames = columnNames
1037
+ .reverse()
1038
+ .filter((column) => column !== "EDD" && column !== "Names");
1039
+
1040
+ headers = createColumns(columnNames);
1041
+ dataForReport = reportData[responseReportName]
1042
+ .filter((row) => row.PhoneNumber)
1043
+ .map((row) => {
1044
+ const formattedDate = extractDate(row.LastVisitDate);
1045
+
1046
+ if (row.PhoneNumber && row.PhoneNumber.startsWith("0")) {
1047
+ return {
1048
+ ...row,
1049
+ PhoneNumber: "256" + row.PhoneNumber.substring(1),
1050
+ LastVisitDate: formattedDate,
1051
+ };
1052
+ }
1053
+
1054
+ return row;
1055
+ });
1056
+ } else {
1057
+ headers = createColumns(columnNames);
1058
+ dataForReport = reportData[responseReportName];
1059
+ }
1060
+ } else {
1061
+ setShowLineList(false);
1062
+ }
1063
+ }
1064
+ } else {
1065
+ if (chartType === "linelist" && reportData?._html) {
1066
+ // Handle linelist HTML from backend _html key
1067
+ setHTML(reportData?._html?.[0]?.html ?? "");
1068
+ setDhisJson(reportData?.json ?? {});
1069
+ } else if (reportData[0]) {
1070
+ const columnNames = Object.keys(reportData[0]);
1071
+ headers = createColumns(columnNames);
1072
+ dataForReport = reportData;
1073
+ } else {
1074
+ setShowLineList(false);
1075
+ }
1076
+ }
1077
+
1078
+ setLoading(false);
1079
+ setShowFilters(false);
1080
+ setTableHeaders(headers);
1081
+ setData(dataForReport);
1082
+ setPivotTableData(dataForReport);
1083
+ setReportName(selectedReport?.label);
1084
+ }
1085
+ },
1086
+ (error) => {
1087
+ setLoading(false);
1088
+ setShowFilters(false);
1089
+ showNotification({
1090
+ title: "Error fetching report",
1091
+ kind: "error",
1092
+ critical: true,
1093
+ description: error?.message,
1094
+ });
1095
+ }
1096
+ );
1097
+ }, [
1098
+ cqiReportingCohort,
1099
+ chartType,
1100
+ endDate,
1101
+ reportCategory,
1102
+ reportType,
1103
+ selectedReport,
1104
+ startDate,
1105
+ ]);
1106
+
1107
+ useEffect(() => {
1108
+ const styleElement = document.createElement("style");
1109
+ styleElement.textContent = `${pivotTableStyles}`;
1110
+ document.head.appendChild(styleElement);
1111
+
1112
+ return () => {
1113
+ document.head.removeChild(styleElement);
1114
+ };
1115
+ }, []);
1116
+
1117
+ const selectedReportTypeItem =
1118
+ reportTypes?.find((item) => item.uuid === reportCategory.categoryUuid) ?? null;
1119
+
1120
+ return (
1121
+ <>
1122
+ <ReportingHomeHeader illustrationComponent={<Illustration />} />
1123
+
1124
+ <div className={styles.container}>
1125
+ <Accordion className={styles.accordion}>
1126
+ <AccordionItem
1127
+ className={styles.heading}
1128
+ title="Report filters"
1129
+ open={showFilters}
1130
+ onHeadingClick={handleFiltersToggle}
1131
+ >
1132
+ <div className={styles.formContainer}>
1133
+ <div className={`${styles.form} ${styles.formFirst}`}>
1134
+ <Form>
1135
+ <Stack gap={2}>
1136
+ <FormGroup legendText={``}>
1137
+ <FormLabel className={styles.label}>
1138
+ Type of report
1139
+ </FormLabel>
1140
+ <ContentSwitcher
1141
+ size="sm"
1142
+ selectedIndex={reportType === "fixed" ? 0 : 1}
1143
+ onChange={handleReportTypeChange}
1144
+ >
1145
+ <Switch name="fixed" text="Fixed" />
1146
+ <Switch name="dynamic" text="Dynamic" />
1147
+ </ContentSwitcher>
1148
+ </FormGroup>
1149
+
1150
+ {reportType === "fixed" && (
1151
+ <>
1152
+ <FormGroup legendText={``}>
1153
+ <FormLabel className={styles.label}>
1154
+ Which kind of report do you want to show?
1155
+ </FormLabel>
1156
+ <ComboBox
1157
+ aria-label="Select Report Type"
1158
+ id="ReportTypeCombobox"
1159
+ items={reportTypes}
1160
+ onChange={handleReportCategoryChange}
1161
+ selectedItem={selectedReportTypeItem}
1162
+ itemToString={(item) => item?.label ?? ""}
1163
+ placeholder="Select report type"
1164
+ />
1165
+ </FormGroup>
1166
+
1167
+ {reportCategory.category === "facility" && (
1168
+ <FormGroup legendText={``}>
1169
+ <FormLabel className={styles.label}>
1170
+ Facility Reports
1171
+ </FormLabel>
1172
+ <ComboBox
1173
+ aria-label="Select facility report"
1174
+ id="facilityReportsCombobox"
1175
+ items={facilityReports ?? []}
1176
+ onChange={handleSelectedReport}
1177
+ selectedItem={selectedReport}
1178
+ itemToString={(item) => item?.label ?? ""}
1179
+ placeholder="Select facility report"
1180
+ disabled={!hasFixedCategory}
1181
+ />
1182
+ </FormGroup>
1183
+ )}
1184
+
1185
+ {reportCategory.category === "national" && (
1186
+ <FormGroup legendText={``}>
1187
+ <FormLabel className={styles.label}>
1188
+ National Reports
1189
+ </FormLabel>
1190
+ <ComboBox
1191
+ aria-label="Select national report"
1192
+ id="nationalReportsCombobox"
1193
+ items={nationalReports ?? []}
1194
+ onChange={handleSelectedReport}
1195
+ selectedItem={selectedReport}
1196
+ itemToString={(item) => item?.label ?? ""}
1197
+ placeholder="Select national report"
1198
+ disabled={!hasFixedCategory}
1199
+ />
1200
+ </FormGroup>
1201
+ )}
1202
+
1203
+ {reportCategory.category === "donor" && (
1204
+ <FormGroup legendText={``}>
1205
+ <FormLabel className={styles.label}>
1206
+ Donor Reports
1207
+ </FormLabel>
1208
+ <ComboBox
1209
+ aria-label="Select donor report"
1210
+ id="donorReportsCombobox"
1211
+ items={donorReports ?? []}
1212
+ onChange={handleSelectedReport}
1213
+ selectedItem={selectedReport}
1214
+ itemToString={(item) => item?.label ?? ""}
1215
+ placeholder="Select donor report"
1216
+ disabled={!hasFixedCategory}
1217
+ />
1218
+ </FormGroup>
1219
+ )}
1220
+
1221
+ {reportCategory.category === "cqi" && (
1222
+ <FormGroup legendText={``}>
1223
+ <FormLabel className={styles.label}>
1224
+ CQI Reports
1225
+ </FormLabel>
1226
+ <ComboBox
1227
+ aria-label="Select CQI report"
1228
+ id="CQIReportsCombobox"
1229
+ items={cqiReports ?? []}
1230
+ onChange={handleSelectedReport}
1231
+ selectedItem={selectedReport}
1232
+ itemToString={(item) => item?.label ?? ""}
1233
+ placeholder="Select CQI report"
1234
+ disabled={!hasFixedCategory}
1235
+ />
1236
+ </FormGroup>
1237
+ )}
1238
+
1239
+ {reportCategory.category === "integration" && (
1240
+ <FormGroup legendText={``}>
1241
+ <FormLabel className={styles.label}>
1242
+ Integration Data Exports
1243
+ </FormLabel>
1244
+ <ComboBox
1245
+ aria-label="Select Integration Data Exports"
1246
+ id="integrationDataExportCombobox"
1247
+ items={integrationDataExports ?? []}
1248
+ onChange={handleSelectedReport}
1249
+ selectedItem={selectedReport}
1250
+ itemToString={(item) => item?.label ?? ""}
1251
+ placeholder="Select integration export"
1252
+ disabled={!hasFixedCategory}
1253
+ />
1254
+ </FormGroup>
1255
+ )}
1256
+
1257
+ {reportCategory.category === "cqi" && (
1258
+ <FormGroup legendText={``}>
1259
+ <FormLabel className={styles.label}>
1260
+ Select your cohort of interest
1261
+ </FormLabel>
1262
+ <RadioButtonGroup
1263
+ legendText=""
1264
+ name="patientCohort"
1265
+ onChange={handleCohortChange}
1266
+ defaultSelected="Patients with encounters"
1267
+ >
1268
+ <RadioButton
1269
+ id="patient_with_encounters"
1270
+ labelText="Patient with encounters"
1271
+ value="Patients with encounters"
1272
+ />
1273
+ <RadioButton
1274
+ id="patients_on_appointment"
1275
+ labelText="Patients on appointment"
1276
+ value="Patients on appointment"
1277
+ />
1278
+ </RadioButtonGroup>
1279
+ </FormGroup>
1280
+ )}
1281
+ </>
1282
+ )}
1283
+
1284
+ {reportType === "dynamic" && (
1285
+ <Stack gap={2}>
1286
+ <FormGroup legendText={``}>
1287
+ <FormLabel className={styles.label}>
1288
+ Which kind of dynamic report type do you want to
1289
+ base on?
1290
+ </FormLabel>
1291
+ <ComboBox
1292
+ aria-label="Select dynamic report type"
1293
+ id="dynamicReportOptions"
1294
+ items={dynamicReportOptions}
1295
+ onChange={handleSelectedDynamicReportType}
1296
+ selectedItem={selectedDynamicReportType}
1297
+ itemToString={(item) => item?.label ?? ""}
1298
+ placeholder="Select dynamic report type"
1299
+ />
1300
+ </FormGroup>
1301
+
1302
+ {selectedDynamicReportType && (
1303
+ <FormGroup legendText={``}>
1304
+ <FormLabel className={styles.label}>
1305
+ {selectedDynamicReportType?.label ?? "Report"}
1306
+ </FormLabel>
1307
+
1308
+ <ComboBox
1309
+ aria-label="Select report type"
1310
+ id="reportTypeCombobox"
1311
+ items={dynamicReportTypes ?? []}
1312
+ onChange={handleSelectedReportDefinition}
1313
+ selectedItem={selectedReport}
1314
+ itemToString={(item) => item?.label ?? ""}
1315
+ placeholder="Select report"
1316
+ disabled={!hasDynamicCategory}
1317
+ />
1318
+ </FormGroup>
1319
+ )}
1320
+ </Stack>
1321
+ )}
1322
+ </Stack>
1323
+ </Form>
1324
+ </div>
1325
+
1326
+ {/* Only show hardcoded parameters when a report is selected but has no parameters */}
1327
+ {selectedReport && reportParameters.length === 0 && (
1328
+ <div className={`${styles.form} ${styles.formRight}`}>
1329
+ <Form>
1330
+ <Stack gap={3}>
1331
+ <FormGroup legendText={``}>
1332
+ <FormLabel className={styles.label}>
1333
+ Do you want your report to cover a fixed reporting
1334
+ period or a relative one?
1335
+ </FormLabel>
1336
+ <RadioButtonGroup
1337
+ legendText=""
1338
+ name="reportingDuration"
1339
+ onChange={handleReportingDurationChange}
1340
+ defaultSelected="fixed"
1341
+ >
1342
+ <RadioButton
1343
+ id="fixedPeriod"
1344
+ labelText="Fixed period"
1345
+ value="fixed"
1346
+ />
1347
+ <RadioButton
1348
+ id="relativePeriod"
1349
+ labelText="Relative period"
1350
+ value="relative"
1351
+ />
1352
+ </RadioButtonGroup>
1353
+ </FormGroup>
1354
+
1355
+ {reportingDuration === "fixed" && (
1356
+ <FormGroup legendText={``} className={styles.dateForm}>
1357
+ <DatePicker
1358
+ datePickerType="single"
1359
+ onChange={handleStartDateChange}
1360
+ dateFormat={"d/m/Y"}
1361
+ value={startDate}
1362
+ >
1363
+ <DatePickerInput
1364
+ id="date-picker-input-id-start"
1365
+ placeholder="dd/mm/yyyy"
1366
+ labelText="Start Date"
1367
+ />
1368
+ </DatePicker>
1369
+ <br />
1370
+ <DatePicker
1371
+ datePickerType="single"
1372
+ onChange={handleEndDateChange}
1373
+ dateFormat={"d/m/Y"}
1374
+ value={endDate}
1375
+ >
1376
+ <DatePickerInput
1377
+ id="date-picker-input-id-end"
1378
+ placeholder="dd/mm/yyyy"
1379
+ labelText="End Date"
1380
+ />
1381
+ </DatePicker>
1382
+ </FormGroup>
1383
+ )}
1384
+
1385
+ {reportingDuration === "relative" && (
1386
+ <FormGroup legendText={``}>
1387
+ <FormLabel className={styles.label}>
1388
+ Select your desired reporting period
1389
+ </FormLabel>
1390
+
1391
+ <ComboBox
1392
+ ariaLabel="Select reporting period"
1393
+ id="reportingPeriodCombobox"
1394
+ items={reportPeriod}
1395
+ placeholder="Choose the reporting period"
1396
+ onChange={handleReportingPeriod}
1397
+ selectedItem={reportingPeriod}
1398
+ itemToString={(item) => item?.label ?? ""}
1399
+ />
1400
+ </FormGroup>
1401
+ )}
1402
+ </Stack>
1403
+ </Form>
1404
+ </div>
1405
+ )}
1406
+
1407
+ {/* Show inline parameters when report has parameters */}
1408
+ {selectedReport && reportParameters.length > 0 && (
1409
+ <div className={`${styles.form} ${styles.formRight}`}>
1410
+ <Stack gap={5}>
1411
+ {/* Date Mode Toggle - shown if report has date parameters */}
1412
+ {reportParameters.some(p => p.type === 'DATE' || p.type === 'DATETIME') && (
1413
+ <div style={{ marginBottom: '1rem' }}>
1414
+ <label className="cds--label">Date Selection Mode</label>
1415
+ <RadioButtonGroup
1416
+ legendText=""
1417
+ name="date-mode"
1418
+ valueSelected={dateMode}
1419
+ onChange={(mode) => {
1420
+ setDateMode(mode as 'FIXED' | 'RELATIVE');
1421
+ if (mode === 'FIXED') {
1422
+ setRelativePeriod('');
1423
+ }
1424
+ }}
1425
+ >
1426
+ <RadioButton
1427
+ id="date-mode-fixed"
1428
+ labelText="Specific dates"
1429
+ value="FIXED"
1430
+ />
1431
+ <RadioButton
1432
+ id="date-mode-relative"
1433
+ labelText="Relative period"
1434
+ value="RELATIVE"
1435
+ />
1436
+ </RadioButtonGroup>
1437
+ </div>
1438
+ )}
1439
+
1440
+ {/* Relative Period Selector - shown in RELATIVE mode */}
1441
+ {dateMode === 'RELATIVE' && reportParameters.some(p => p.type === 'DATE' || p.type === 'DATETIME') && (
1442
+ <div style={{ marginBottom: '1rem', maxWidth: '300px' }}>
1443
+ <Select
1444
+ id="relative-period-select"
1445
+ labelText="Select reporting period"
1446
+ value={relativePeriod}
1447
+ onChange={(e) => {
1448
+ setRelativePeriod(e.target.value as RelativePeriod);
1449
+ }}
1450
+ size="sm"
1451
+ >
1452
+ <SelectItem value="" text="Select period" />
1453
+ {RELATIVE_PERIOD_OPTIONS.map((option) => (
1454
+ <SelectItem
1455
+ key={option.value}
1456
+ value={option.value}
1457
+ text={option.label}
1458
+ />
1459
+ ))}
1460
+ </Select>
1461
+ {paramErrors.relativePeriod && (
1462
+ <div style={{ color: '#da1e28', fontSize: '0.875rem', marginTop: '0.5rem' }}>
1463
+ {paramErrors.relativePeriod}
1464
+ </div>
1465
+ )}
1466
+ </div>
1467
+ )}
1468
+
1469
+ {/* Render parameter inputs inline */}
1470
+ {reportParameters.map((param) => {
1471
+ const value = parameterValues[param.name] || '';
1472
+ const error = paramErrors[param.name];
1473
+
1474
+ // Skip start/end date parameters in RELATIVE mode
1475
+ if (dateMode === 'RELATIVE') {
1476
+ const isStartDate = param.name.toLowerCase() === 'startdate' || param.name.toLowerCase() === 'start_date';
1477
+ const isEndDate = param.name.toLowerCase() === 'enddate' || param.name.toLowerCase() === 'end_date';
1478
+ if ((param.type === 'DATE' || param.type === 'DATETIME') && (isStartDate || isEndDate)) {
1479
+ return null;
1480
+ }
1481
+ }
1482
+
1483
+ const commonProps = {
1484
+ key: param.name,
1485
+ parameter: param,
1486
+ value,
1487
+ error,
1488
+ onChange: (newValue: any) => {
1489
+ setParameterValues((prev) => ({ ...prev, [param.name]: newValue }));
1490
+ if (paramErrors[param.name]) {
1491
+ setParamErrors((prev) => {
1492
+ const newErrors = { ...prev };
1493
+ delete newErrors[param.name];
1494
+ return newErrors;
1495
+ });
1496
+ }
1497
+ },
1498
+ };
1499
+
1500
+ switch (param.type) {
1501
+ case 'DATE':
1502
+ case 'DATETIME':
1503
+ return <DateParameterInput {...commonProps} />;
1504
+ case 'NUMBER':
1505
+ return <NumberParameterInput {...commonProps} />;
1506
+ case 'BOOLEAN':
1507
+ return <BooleanParameterInput {...commonProps} />;
1508
+ case 'LIST':
1509
+ return <ListParameterInput {...commonProps} />;
1510
+ case 'LOCATION':
1511
+ return (
1512
+ <LocationParameterInput
1513
+ {...commonProps}
1514
+ onSearchQueryChange={(query) => setSearchQueries((prev) => ({ ...prev, [param.name]: query }))}
1515
+ />
1516
+ );
1517
+ case 'CONCEPT':
1518
+ return (
1519
+ <ConceptParameterInput
1520
+ {...commonProps}
1521
+ searchQuery={searchQueries[param.name] || ''}
1522
+ onSearchQueryChange={(query) => setSearchQueries((prev) => ({ ...prev, [param.name]: query }))}
1523
+ />
1524
+ );
1525
+ case 'IDENTIFIER_TYPE':
1526
+ return <IdentifierTypeParameterInput {...commonProps} />;
1527
+ case 'PERSON_ATTRIBUTE':
1528
+ return <PersonAttributeParameterInput {...commonProps} />;
1529
+ case 'TEXT':
1530
+ default:
1531
+ return (
1532
+ <TextParameterInput
1533
+ {...commonProps}
1534
+ onError={(error) => setParamErrors((prev) => ({ ...prev, [param.name]: error }))}
1535
+ />
1536
+ );
1537
+ }
1538
+ })}
1539
+ </Stack>
1540
+ </div>
1541
+ )}
1542
+
1543
+ {/* Show empty state when no report is selected */}
1544
+ {!selectedReport && (
1545
+ <div className={`${styles.form} ${styles.formRight}`}>
1546
+ <div style={{ padding: '1rem', color: '#525252', fontStyle: 'italic', textAlign: 'center', marginTop: '2rem' }}>
1547
+ Select a report above to configure and run
1548
+ </div>
1549
+ </div>
1550
+ )}
1551
+ </div>
1552
+
1553
+ <div>
1554
+ <Form>
1555
+ {reportType === "dynamic" && (
1556
+ <Stack gap={2}>
1557
+ <FormGroup legendText={``}>
1558
+ <FormLabel className={styles.label}>Indicators</FormLabel>
1559
+
1560
+ <ComboBox
1561
+ ariaLabel="Select indicators"
1562
+ id="indicatorCombobox"
1563
+ items={[
1564
+ ...reportIndicators,
1565
+ ...encounterTypes,
1566
+ ...orderTypes,
1567
+ ]}
1568
+ placeholder="Choose the indicators"
1569
+ onChange={handleIndicatorChange}
1570
+ selectedItem={selectedIndicators}
1571
+ itemToString={(item) => item?.label ?? ""}
1572
+ />
1573
+ </FormGroup>
1574
+
1575
+ <div className={styles.panelContainer}>
1576
+ <Panel heading="Available parameters">
1577
+ <ul className={styles.list}>
1578
+ {availableParameters.map((parameter) => (
1579
+ <li
1580
+ role="menuitem"
1581
+ className={styles.leftListItem}
1582
+ key={parameter.label}
1583
+ onClick={() => moveAllFromLeftToRight(parameter)}
1584
+ >
1585
+ {parameter.label}
1586
+ </li>
1587
+ ))}
1588
+ </ul>
1589
+ </Panel>
1590
+
1591
+ <div className={styles.paramsControlContainer}>
1592
+ <Button
1593
+ iconDescription="Move all parameters to the right"
1594
+ kind="tertiary"
1595
+ hasIconOnly
1596
+ renderIcon={ArrowRight}
1597
+ onClick={moveAllParametersRight}
1598
+ role="button"
1599
+ size="md"
1600
+ disabled={availableParameters.length < 1}
1601
+ />
1602
+ <Button
1603
+ iconDescription="Move all parameters to the left"
1604
+ kind="tertiary"
1605
+ hasIconOnly
1606
+ renderIcon={ArrowLeft}
1607
+ onClick={moveAllParametersLeft}
1608
+ role="button"
1609
+ size="md"
1610
+ disabled={selectedParameters.length < 1}
1611
+ />
1612
+ </div>
1613
+
1614
+ <Panel heading="Selected parameters">
1615
+ <ul className={styles.list}>
1616
+ {selectedParameters.map((parameter) => (
1617
+ <React.Fragment key={parameter.label}>
1618
+ <li
1619
+ className={`${styles.rightListItem} ${
1620
+ parameter?.showModifierPanel
1621
+ ? styles.openRightListItem
1622
+ : ""
1623
+ } `}
1624
+ role="menuitem"
1625
+ >
1626
+ <div className={styles.selectedListItem}>
1627
+ <div>
1628
+ <ArrowLeft
1629
+ className={styles.itemChevronUpDown}
1630
+ onClick={() =>
1631
+ moveAllFromRightToLeft(parameter)
1632
+ }
1633
+ />
1634
+ </div>
1635
+ {parameter.label}
1636
+ {![
1637
+ "PatientIdentifier",
1638
+ "PersonAttribute",
1639
+ "PersonName",
1640
+ "Demographics",
1641
+ "Address",
1642
+ "Condition",
1643
+ "Appointment",
1644
+ "Orders",
1645
+ ].includes(parameter?.type) ? (
1646
+ <div className={styles.modifierContainer}>
1647
+ <div>
1648
+ {parameter?.showModifierPanel ? (
1649
+ <ChevronUp
1650
+ className={styles.itemChevronUpDown}
1651
+ onClick={() =>
1652
+ showModifierPanel(parameter)
1653
+ }
1654
+ />
1655
+ ) : (
1656
+ <ChevronDown
1657
+ className={styles.itemChevronUpDown}
1658
+ onClick={() =>
1659
+ showModifierPanel(parameter)
1660
+ }
1661
+ />
1662
+ )}
1663
+ </div>
1664
+ </div>
1665
+ ) : null}
1666
+ </div>
1667
+ </li>
1668
+
1669
+ <div
1670
+ className={`${styles.fadeModifierContainer} ${
1671
+ parameter?.showModifierPanel
1672
+ ? styles.show
1673
+ : styles.hide
1674
+ }`}
1675
+ >
1676
+ <ModifierComponent
1677
+ listItem={parameter}
1678
+ onChangeMostRecent={changeModifier}
1679
+ />
1680
+ </div>
1681
+ </React.Fragment>
1682
+ ))}
1683
+ </ul>
1684
+ </Panel>
1685
+ </div>
1686
+ </Stack>
1687
+ )}
1688
+ </Form>
1689
+ </div>
1690
+ </AccordionItem>
1691
+ </Accordion>
1692
+ </div>
1693
+
1694
+ <section className={styles.section}>
1695
+ <div className={styles.contentSwitchContainer}>
1696
+ <ContentSwitcher
1697
+ size={`md`}
1698
+ selectedIndex={
1699
+ chartType === "linelist" ? 3 : chartType === "pivot" ? 1 : chartType === "aggregate" ? 2 : 0
1700
+ }
1701
+ onChange={handleChartTypeChange}
1702
+ >
1703
+ <Switch name="list" disabled={chartType === "aggregate" || chartType === "linelist"}>
1704
+ <div className={styles.switch}>
1705
+ <Catalog />
1706
+ <span>Patient list</span>
1707
+ </div>
1708
+ </Switch>
1709
+ <Switch name="pivot" disabled={chartType === "aggregate" || chartType === "linelist"}>
1710
+ <div className={styles.switch}>
1711
+ <CrossTab />
1712
+ <span>Pivot table</span>
1713
+ </div>
1714
+ </Switch>
1715
+ <Switch name="aggregate" disabled={chartType !== "aggregate"}>
1716
+ <div className={styles.switch}>
1717
+ <ImageService />
1718
+ <span>Aggregate Report</span>
1719
+ </div>
1720
+ </Switch>
1721
+ <Switch name="linelist" disabled={chartType !== "linelist"}>
1722
+ <div className={styles.switch}>
1723
+ <ListIcon />
1724
+ <span>Linelist</span>
1725
+ </div>
1726
+ </Switch>
1727
+ </ContentSwitcher>
1728
+ </div>
1729
+
1730
+ <div className={styles.actionButtonContainer}>
1731
+ <ButtonSet>
1732
+ <Button
1733
+ size="md"
1734
+ kind="primary"
1735
+ onClick={handleUpdateReport}
1736
+ className={styles.actionButton}
1737
+ disabled={
1738
+ !selectedReport ||
1739
+ (reportType === "fixed" && !reportCategory.category) ||
1740
+ (reportType === "dynamic" && !selectedDynamicReportType)
1741
+ }
1742
+ >
1743
+ <Intersect />
1744
+ <span>View Report</span>
1745
+ </Button>
1746
+
1747
+ {data.length > 0 || htmlContent !== "" ? (
1748
+ <>
1749
+ {chartType === "pivot" ? (
1750
+ <Button
1751
+ size="md"
1752
+ kind="secondary"
1753
+ iconDescription="Save Report"
1754
+ onClick={showSaveReportModal}
1755
+ className={styles.dsReportBtn}
1756
+ renderIcon={Save}
1757
+ hasIconOnly
1758
+ />
1759
+ ) : null}
1760
+
1761
+ {reportType === "fixed" ? (
1762
+ isDownloading ? (
1763
+ <InlineLoading />
1764
+ ) : (
1765
+ <Button
1766
+ size="md"
1767
+ kind="tertiary"
1768
+ iconDescription="Download Report"
1769
+ tooltipAlignment="end"
1770
+ onClick={handleDownloadReport}
1771
+ className={styles.dsReportBtn}
1772
+ renderIcon={DocumentDownload}
1773
+ hasIconOnly
1774
+ />
1775
+ )
1776
+ ) : null}
1777
+
1778
+ {chartType === "aggregate" ? (
1779
+ isSendingReport ? (
1780
+ <InlineLoading />
1781
+ ) : (
1782
+ <Button
1783
+ size="md"
1784
+ kind="secondary"
1785
+ iconDescription="Send Report to DHIS2"
1786
+ tooltipAlignment="end"
1787
+ onClick={confirmSendReport}
1788
+ className={styles.dsReportBtn}
1789
+ renderIcon={SendAlt}
1790
+ hasIconOnly
1791
+ />
1792
+ )
1793
+ ) : null}
1794
+ </>
1795
+ ) : null}
1796
+ </ButtonSet>
1797
+ </div>
1798
+ </section>
1799
+
1800
+ {showLineList ? (
1801
+ <>
1802
+ {loading && <DataTableSkeleton role="progressbar" />}
1803
+
1804
+ {chartType === "list" && !loading && (
1805
+ <div className={styles.reportContainer}>
1806
+ <h3 className={styles.listHeading}>
1807
+ {reportName} ({dayjs(startDate).format("DD/MM/YYYY")} -{" "}
1808
+ {dayjs(endDate).format("DD/MM/YYYY")})
1809
+ </h3>
1810
+ <div className={styles.reportDataTable}>
1811
+ {reportCategory.category === "cqi" ? (
1812
+ <CQIDataList columns={tableHeaders} data={data} />
1813
+ ) : (
1814
+ <DataList
1815
+ columns={tableHeaders}
1816
+ data={data}
1817
+ report={{
1818
+ type: reportType,
1819
+ name: selectedReport?.label ?? "",
1820
+ }}
1821
+ />
1822
+ )}
1823
+ </div>
1824
+ </div>
1825
+ )}
1826
+
1827
+ {chartType === "list" &&
1828
+ !loading &&
1829
+ selectedReport?.id === "bf79f017-8591-4eaf-88c9-1cde33226517" && (
1830
+ <div className={styles.sendReportBtn}>
1831
+ <Button
1832
+ size="md"
1833
+ kind="primary"
1834
+ className={styles.actionButton}
1835
+ >
1836
+ <SendAlt />
1837
+ <span>Send Report to Family Connect</span>
1838
+ </Button>
1839
+ </div>
1840
+ )}
1841
+
1842
+ {chartType === "pivot" && (
1843
+ <div className={styles.reportContainer}>
1844
+ <h3>Pivot Table</h3>
1845
+ <PivotTableUI
1846
+ data={pivotTableData}
1847
+ onChange={(s) => setPivotTableData(s)}
1848
+ renderers={{ ...TableRenderers, ...PlotlyRenderers }}
1849
+ {...pivotTableData}
1850
+ />
1851
+ </div>
1852
+ )}
1853
+
1854
+ {chartType === "aggregate" && !loading && (
1855
+ <div className={styles.reportTableContainer}>
1856
+ <section className={styles.reportOptions}>
1857
+ <h3 className={styles.listHeading}>
1858
+ {reportName} ({dayjs(startDate).format("DD/MM/YYYY")} -{" "}
1859
+ {dayjs(endDate).format("DD/MM/YYYY")})
1860
+ </h3>
1861
+ </section>
1862
+ <div dangerouslySetInnerHTML={{ __html: htmlContent }} />
1863
+ </div>
1864
+ )}
1865
+
1866
+ {chartType === "linelist" && !loading && (
1867
+ <div className={styles.reportTableContainer}>
1868
+ <section className={styles.reportOptions}>
1869
+ <h3 className={styles.listHeading}>
1870
+ {reportName} ({dayjs(startDate).format("DD/MM/YYYY")} -{" "}
1871
+ {dayjs(endDate).format("DD/MM/YYYY")})
1872
+ </h3>
1873
+ </section>
1874
+ <div dangerouslySetInnerHTML={{ __html: htmlContent }} />
1875
+ </div>
1876
+ )}
1877
+
1878
+ {saveReportModal && (
1879
+ <Modal
1880
+ open
1881
+ size="sm"
1882
+ preventCloseOnClickOutside={true}
1883
+ hasScrollingContent={true}
1884
+ modalHeading="ENTER REPORT DETAILS"
1885
+ secondaryButtonText="Cancel"
1886
+ primaryButtonText="Save Report"
1887
+ onRequestClose={closeReportModal}
1888
+ onRequestSubmit={handleSaveReport}
1889
+ >
1890
+ <div>
1891
+ <TextInput
1892
+ id="title"
1893
+ labelText={`Report Title`}
1894
+ onChange={handleReportTitleChange}
1895
+ maxCount={50}
1896
+ placeholder="Enter report title"
1897
+ />
1898
+ <TextArea
1899
+ id="description"
1900
+ className={styles.reportDescription}
1901
+ labelText={`Report Description`}
1902
+ onChange={handleReportDescChange}
1903
+ rows={2}
1904
+ placeholder="Enter report description"
1905
+ />
1906
+ </div>
1907
+ </Modal>
1908
+ )}
1909
+
1910
+ {/* Parameter Modal */}
1911
+ <ReportParameterModal
1912
+ open={parameterModalOpen}
1913
+ onClose={() => setParameterModalOpen(false)}
1914
+ onRun={handleRunReportWithParameters}
1915
+ parameters={reportParameters}
1916
+ initialValues={parameterValues}
1917
+ loading={loading}
1918
+ />
1919
+ </>
1920
+ ) : (
1921
+ <Layer className={styles.layer}>
1922
+ <Tile className={styles.tile}>
1923
+ <EmptyStateIllustration />
1924
+ <p className={styles.content}>No data to display</p>
1925
+ <p className={styles.explainer}>
1926
+ Use the report filters above to build your reports
1927
+ </p>
1928
+ </Tile>
1929
+ </Layer>
1930
+ )}
1931
+ </>
1932
+ );
1933
+ };
1934
+
1935
+ export default DataVisualizer;