@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,190 @@
1
+ import type { IndicatorDto } from '../../../resources/indicator/indicators.api';
2
+
3
+ import {
4
+ countSqlToPopulationSql,
5
+ tryGetPatientIdColumnFromConfig,
6
+ tryGetCountSqlFromIndicator,
7
+ } from './composite-indicator-sql.utils';
8
+ import {
9
+ compilePopulationSql,
10
+ generateAgeSexDisaggregationSql,
11
+ clearCompilationCache,
12
+ type CompilerOptions
13
+ } from './population-sql.compiler';
14
+
15
+ export type FinalIndicatorAuthoringV1 = {
16
+ version: 1;
17
+ baseIndicatorId: string;
18
+
19
+ // Backward/forward compatibility
20
+ ageGroupSetCode?: string;
21
+ ageCategoryCode?: string;
22
+
23
+ genders: Array<'F' | 'M'>;
24
+ sqlPreview: string;
25
+ };
26
+
27
+ type BuildFinalSqlArgs = {
28
+ baseIndicator: IndicatorDto;
29
+ ageCategoryCode: string;
30
+ genders: Array<'F' | 'M'>;
31
+ };
32
+
33
+ type BuildFinalSqlAsyncArgs = BuildFinalSqlArgs & {
34
+ getIndicator: (uuid: string) => Promise<IndicatorDto | null>;
35
+ compilerOptions?: CompilerOptions;
36
+ };
37
+
38
+ /**
39
+ * Builds a Final Indicator query that ALWAYS returns all age groups (even if value = 0),
40
+ * and for each selected gender.
41
+ *
42
+ * Strategy:
43
+ * - base_pop: patient population set from base indicator
44
+ * - ag: all age groups for the selected category (active only)
45
+ * - genders: the selected genders as rows
46
+ * - cnt: computed counts per (age_group_id, gender)
47
+ * - final: ag CROSS JOIN genders LEFT JOIN cnt, COALESCE(value,0)
48
+ */
49
+ export function buildFinalIndicatorSql({ baseIndicator, ageCategoryCode, genders }: BuildFinalSqlArgs): string {
50
+ const { sql: countSql } = tryGetCountSqlFromIndicator(baseIndicator);
51
+ const pidCol = tryGetPatientIdColumnFromConfig(baseIndicator);
52
+
53
+ const populationSql = countSql ? countSqlToPopulationSql(countSql, pidCol, 'Patients') : '';
54
+
55
+ if (!populationSql.trim()) {
56
+ return `-- Unable to build final indicator SQL: base indicator does not contain usable count SQL.\n-- Ensure the base indicator has a valid sqlTemplate or configJson.sqlPreview.`;
57
+ }
58
+
59
+ // Default to both genders if none selected (for backward compatibility)
60
+ // TODO: Remove this default in a future version and require explicit gender selection
61
+ const selectedGenders = (genders ?? []).length ? genders : (['F', 'M'] as Array<'F' | 'M'>);
62
+
63
+ // Log deprecation warning when defaulting
64
+ if (!genders || genders.length === 0) {
65
+ console.warn('[buildFinalIndicatorSql] No genders specified, defaulting to both F and M. This default will be removed in a future version.');
66
+ }
67
+
68
+ const gendersCte = buildGenderCte(selectedGenders);
69
+
70
+ // NOTE: adjust these table names if your DB uses different names.
71
+ // If you adopted the new naming, use report_builder_dim_age_category/group.
72
+ const AGE_CATEGORY_TABLE = 'report_builder_dim_age_category';
73
+ const AGE_GROUP_TABLE = 'report_builder_dim_age_group';
74
+
75
+ return `
76
+ WITH base_pop AS (
77
+ ${indent(populationSql.trim(), 2)}
78
+ ),
79
+ ag AS (
80
+ SELECT
81
+ ag.age_group_id,
82
+ ag.label,
83
+ ag.min_age_days,
84
+ ag.max_age_days,
85
+ ag.sort_order
86
+ FROM ${AGE_GROUP_TABLE} ag
87
+ JOIN ${AGE_CATEGORY_TABLE} ac
88
+ ON ac.age_category_id = ag.age_category_id
89
+ WHERE ac.code = '${escapeSqlLiteral(ageCategoryCode)}'
90
+ AND ag.is_active = 1
91
+ ),
92
+ genders AS (
93
+ ${indent(gendersCte, 2)}
94
+ ),
95
+ cnt AS (
96
+ SELECT
97
+ ag.age_group_id AS age_group_id,
98
+ mdp.gender AS gender,
99
+ COUNT(DISTINCT base_pop.${pidCol}) AS value
100
+ FROM base_pop
101
+ JOIN mamba_fact_patients_latest_patient_demographics mdp
102
+ ON mdp.patient_id = base_pop.${pidCol}
103
+ JOIN ag
104
+ ON TIMESTAMPDIFF(DAY, mdp.birthdate, :endDate)
105
+ BETWEEN ag.min_age_days AND ag.max_age_days
106
+ WHERE mdp.birthdate IS NOT NULL
107
+ AND mdp.gender IS NOT NULL
108
+ AND mdp.gender IN (${selectedGenders.map((g) => `'${g}'`).join(',')})
109
+ GROUP BY ag.age_group_id, mdp.gender
110
+ )
111
+ SELECT
112
+ ag.label AS age_group,
113
+ g.gender AS gender,
114
+ COALESCE(cnt.value, 0) AS value
115
+ FROM ag
116
+ CROSS JOIN genders g
117
+ LEFT JOIN cnt
118
+ ON cnt.age_group_id = ag.age_group_id
119
+ AND cnt.gender = g.gender
120
+ ORDER BY ag.sort_order, g.gender;
121
+ `.trim();
122
+ }
123
+
124
+ function buildGenderCte(genders: Array<'F' | 'M'>): string {
125
+ // Produce rows:
126
+ // SELECT 'F' AS gender UNION ALL SELECT 'M' AS gender
127
+ const parts = genders.map((g, i) => (i === 0 ? `SELECT '${g}' AS gender` : `UNION ALL SELECT '${g}' AS gender`));
128
+ return parts.join('\n');
129
+ }
130
+
131
+ function indent(s: string, spaces: number) {
132
+ const pad = ' '.repeat(spaces);
133
+ return s
134
+ .split('\n')
135
+ .map((l) => (l.trim().length ? pad + l : l))
136
+ .join('\n');
137
+ }
138
+
139
+ function escapeSqlLiteral(s: string) {
140
+ return String(s).replace(/'/g, "''");
141
+ }
142
+
143
+ /**
144
+ * Async version of final indicator SQL builder.
145
+ *
146
+ * This function uses the recursive population SQL compiler to properly handle
147
+ * composite base indicators. For simple base indicators, it falls back to the
148
+ * synchronous logic.
149
+ *
150
+ * Use this version when the base indicator might be a composite indicator.
151
+ */
152
+ export async function buildFinalIndicatorSqlAsync({
153
+ baseIndicator,
154
+ ageCategoryCode,
155
+ genders,
156
+ getIndicator,
157
+ compilerOptions = {}
158
+ }: BuildFinalSqlAsyncArgs): Promise<string> {
159
+ try {
160
+ // Clear cache to ensure fresh compilation
161
+ clearCompilationCache();
162
+
163
+ // If base indicator is composite, use the recursive compiler
164
+ if (baseIndicator.kind === 'COMPOSITE') {
165
+ const result = await compilePopulationSql(baseIndicator, getIndicator, new Set(), compilerOptions);
166
+
167
+ // Check for compilation errors
168
+ if (result.error) {
169
+ return `-- Error: ${result.error.message}\n-- Base indicator: ${baseIndicator.name} (${baseIndicator.code || baseIndicator.uuid})\n-- Error Code: ${result.error.code}`;
170
+ }
171
+
172
+ const populationSql = result.sql;
173
+
174
+ // Generate the disaggregation SQL using the compiled population SQL
175
+ // Note: population SQL is normalized to patient_id, so no need to specify patientIdColumn
176
+ return generateAgeSexDisaggregationSql({
177
+ populationSql,
178
+ ageCategoryCode,
179
+ genders
180
+ });
181
+ }
182
+
183
+ // For BASE indicators, use the synchronous logic
184
+ return buildFinalIndicatorSql({ baseIndicator, ageCategoryCode, genders });
185
+ } catch (error) {
186
+ // Return an error comment in the SQL
187
+ const errorMsg = error instanceof Error ? error.message : String(error);
188
+ return `-- Error building final indicator SQL: ${errorMsg}\n-- Base indicator: ${baseIndicator.name} (${baseIndicator.code || baseIndicator.uuid})`;
189
+ }
190
+ }
@@ -0,0 +1,254 @@
1
+ // src/report-builder/components/indicators/utils/indicator-conditions-hydration.utils.ts
2
+
3
+ import type { ThemeCondition } from '../types/data-theme-config.types';
4
+ import type { IndicatorCondition } from '../types/indicator-types';
5
+
6
+ import { getConceptByUuid } from '../../../resources/concepts/concepts.resource';
7
+ import type { ConceptSummary } from '../../../resources/concepts/concept-types';
8
+
9
+ import { toSelectedConcept, type SelectedConcept } from '../handler/concept-search-multiselect.component';
10
+ import type { ConceptUiMap, QaUiMap } from '../types/condition-ui.types';
11
+
12
+ export type HydrationOptions = {
13
+ force?: boolean;
14
+ dedupe?: boolean;
15
+ preserveUnknownKeys?: boolean;
16
+ };
17
+
18
+ export type HydrationResult = {
19
+ conceptUi: ConceptUiMap;
20
+ qaUi: QaUiMap;
21
+ stats: {
22
+ themeConditionsCount: number;
23
+ pickedConditionsCount: number;
24
+ conceptConditionsHydrated: number;
25
+ qaConditionsHydrated: number;
26
+ tokensHydrated: number;
27
+ tokensMissed: number;
28
+ };
29
+ };
30
+
31
+ // Stored value shape in pickedConditions for QA handler
32
+ // (support both legacy single-question and current multi-question formats)
33
+ type QAValue = {
34
+ question?: string | number | null;
35
+ questions?: Array<string | number>;
36
+ answers?: Array<string | number>;
37
+ };
38
+
39
+ function uniq(tokens: string[]) {
40
+ const out: string[] = [];
41
+ const seen = new Set<string>();
42
+ for (const t of tokens) {
43
+ const k = String(t ?? '').trim();
44
+ if (!k) continue;
45
+ if (seen.has(k)) continue;
46
+ seen.add(k);
47
+ out.push(k);
48
+ }
49
+ return out;
50
+ }
51
+
52
+ /**
53
+ * Hydrate ONE token into ConceptSummary (internal).
54
+ * We now persist UUIDs, so we call direct endpoint by UUID.
55
+ */
56
+ async function hydrateConceptSummaryToken(token: string, signal?: AbortSignal): Promise<ConceptSummary | null> {
57
+ const tok = String(token ?? '').trim();
58
+ if (!tok) return null;
59
+
60
+ try {
61
+ const concept = await getConceptByUuid(tok, signal);
62
+ if (!concept?.uuid) return null;
63
+ return concept;
64
+ } catch {
65
+ return null;
66
+ }
67
+ }
68
+
69
+ /**
70
+ * Hydrate ONE token into SelectedConcept (UI shape).
71
+ * ✅ Uses toSelectedConcept so ICD mappings are preserved.
72
+ */
73
+ async function hydrateConceptToken(token: string, signal?: AbortSignal): Promise<SelectedConcept | null> {
74
+ const cs = await hydrateConceptSummaryToken(token, signal);
75
+ return cs ? toSelectedConcept(cs) : null;
76
+ }
77
+
78
+ /**
79
+ * Hydrate MANY tokens into SelectedConcept[].
80
+ * ✅ for-loop; preserves order; returns only valid concepts.
81
+ */
82
+ async function hydrateConceptTokens(tokens: string[], signal?: AbortSignal, dedupe = true): Promise<SelectedConcept[]> {
83
+ const clean = dedupe ? uniq(tokens) : tokens.map((t) => String(t ?? '').trim()).filter(Boolean);
84
+ if (!clean.length) return [];
85
+
86
+ const results: SelectedConcept[] = [];
87
+
88
+ for (const tok of clean) {
89
+ if (signal?.aborted) break;
90
+
91
+ try {
92
+ const concept = await hydrateConceptToken(tok, signal);
93
+ if (concept) results.push(concept);
94
+ } catch {
95
+ // ignore individual token failures
96
+ }
97
+ }
98
+
99
+ return results;
100
+ }
101
+
102
+ /**
103
+ * Hydrates UI state for concept-based conditions (edit mode):
104
+ * - iterate picked first (source of truth)
105
+ * - match to themeByKey for handler
106
+ * - ensure defaults for theme conditions not saved
107
+ */
108
+ export async function hydrateConditionUiState(
109
+ themeConditions: ThemeCondition[] | undefined,
110
+ pickedConditions: IndicatorCondition[] | undefined,
111
+ existingConceptUi: ConceptUiMap | undefined,
112
+ existingQaUi: QaUiMap | undefined,
113
+ signal?: AbortSignal,
114
+ options?: HydrationOptions,
115
+ ): Promise<HydrationResult> {
116
+ const opt: Required<HydrationOptions> = {
117
+ force: options?.force ?? true,
118
+ dedupe: options?.dedupe ?? true,
119
+ preserveUnknownKeys: options?.preserveUnknownKeys ?? true,
120
+ };
121
+
122
+ const themeList = themeConditions ?? [];
123
+ const pickedList = pickedConditions ?? [];
124
+
125
+ const themeByKey = new Map<string, ThemeCondition>();
126
+ for (const tc of themeList) {
127
+ if (tc?.key) themeByKey.set(tc.key, tc);
128
+ }
129
+
130
+ const nextConceptUi: ConceptUiMap = { ...(existingConceptUi ?? {}) };
131
+ const nextQaUi: QaUiMap = { ...(existingQaUi ?? {}) };
132
+
133
+ let conceptConditionsHydrated = 0;
134
+ let qaConditionsHydrated = 0;
135
+ let tokensHydrated = 0;
136
+ let tokensMissed = 0;
137
+
138
+ const seenKeys = new Set<string>();
139
+
140
+ // 1) hydrate from picked conditions (source of truth)
141
+ for (const pc of pickedList) {
142
+ if (!pc?.key) continue;
143
+ seenKeys.add(pc.key);
144
+
145
+ const tc = themeByKey.get(pc.key);
146
+ if (!tc) {
147
+ if (!opt.preserveUnknownKeys) {
148
+ delete nextConceptUi[pc.key];
149
+ delete nextQaUi[pc.key];
150
+ }
151
+ continue;
152
+ }
153
+
154
+ // -----------------------------
155
+ // CONCEPT_SEARCH
156
+ // -----------------------------
157
+ if (tc.handler === 'CONCEPT_SEARCH') {
158
+ const shouldHydrate =
159
+ opt.force || !Array.isArray(nextConceptUi[tc.key]) || nextConceptUi[tc.key].length === 0;
160
+
161
+ if (!shouldHydrate) continue;
162
+
163
+ const raw = pc.value;
164
+
165
+ if (Array.isArray(raw) && raw.length) {
166
+ const tokens = raw.map((x) => String(x)).filter(Boolean);
167
+ const selected = await hydrateConceptTokens(tokens, signal, opt.dedupe);
168
+
169
+ tokensHydrated += selected.length;
170
+ tokensMissed += Math.max(0, tokens.length - selected.length);
171
+
172
+ nextConceptUi[tc.key] = selected;
173
+ } else {
174
+ nextConceptUi[tc.key] = [];
175
+ }
176
+
177
+ conceptConditionsHydrated += 1;
178
+ continue;
179
+ }
180
+
181
+ // -----------------------------
182
+ // QUESTION_ANSWER_CONCEPT_SEARCH
183
+ // -----------------------------
184
+ if (tc.handler === 'QUESTION_ANSWER_CONCEPT_SEARCH') {
185
+ const existing = nextQaUi[tc.key];
186
+
187
+ const shouldHydrate =
188
+ opt.force ||
189
+ !existing ||
190
+ ((existing.questions?.length ?? 0) === 0 && (existing.answers?.length ?? 0) === 0 && !existing.error);
191
+
192
+ if (!shouldHydrate) continue;
193
+
194
+ const raw: any = pc.value;
195
+
196
+ const qa: QAValue | null = raw && typeof raw === 'object' ? (raw as QAValue) : null;
197
+
198
+ // ✅ support both shapes: { question } OR { questions: [] }
199
+ const qTokens: string[] = Array.isArray(qa?.questions)
200
+ ? (qa?.questions ?? []).map((x) => String(x).trim()).filter(Boolean)
201
+ : qa?.question !== null && qa?.question !== undefined
202
+ ? [String(qa.question).trim()].filter(Boolean)
203
+ : [];
204
+
205
+ const aTokens: string[] = Array.isArray(qa?.answers)
206
+ ? (qa?.answers ?? []).map((x) => String(x).trim()).filter(Boolean)
207
+ : [];
208
+
209
+ const questions: SelectedConcept[] = qTokens.length
210
+ ? await hydrateConceptTokens(qTokens, signal, opt.dedupe)
211
+ : [];
212
+
213
+ const answers: SelectedConcept[] = aTokens.length
214
+ ? await hydrateConceptTokens(aTokens, signal, opt.dedupe)
215
+ : [];
216
+
217
+ tokensHydrated += questions.length + answers.length;
218
+ tokensMissed += Math.max(0, qTokens.length - questions.length) + Math.max(0, aTokens.length - answers.length);
219
+
220
+ nextQaUi[tc.key] = { questions, answers };
221
+ qaConditionsHydrated += 1;
222
+ continue;
223
+ }
224
+
225
+ // other handlers ignored
226
+ }
227
+
228
+ // 2) ensure all theme conditions have UI defaults
229
+ for (const tc of themeList) {
230
+ if (!tc?.key) continue;
231
+
232
+ if (!seenKeys.has(tc.key)) {
233
+ if (tc.handler === 'CONCEPT_SEARCH') {
234
+ if (!Array.isArray(nextConceptUi[tc.key])) nextConceptUi[tc.key] = [];
235
+ }
236
+ if (tc.handler === 'QUESTION_ANSWER_CONCEPT_SEARCH') {
237
+ if (!nextQaUi[tc.key]) nextQaUi[tc.key] = { questions: [], answers: [] };
238
+ }
239
+ }
240
+ }
241
+
242
+ return {
243
+ conceptUi: nextConceptUi,
244
+ qaUi: nextQaUi,
245
+ stats: {
246
+ themeConditionsCount: themeList.length,
247
+ pickedConditionsCount: pickedList.length,
248
+ conceptConditionsHydrated,
249
+ qaConditionsHydrated,
250
+ tokensHydrated,
251
+ tokensMissed,
252
+ },
253
+ };
254
+ }
@@ -0,0 +1,204 @@
1
+ /**
2
+ * Centralized Indicator SQL Resolution
3
+ *
4
+ * This module provides a single canonical way to resolve SQL from indicators
5
+ * across all compilation paths (Indicator Preview, Section Preview, etc.).
6
+ *
7
+ * Instead of duplicating the fallback chain in multiple components,
8
+ * use these functions to reliably locate and extract SQL from indicator configurations.
9
+ */
10
+
11
+ import type { IndicatorDto } from '../../../resources/indicator/indicators.api';
12
+
13
+ /**
14
+ * SQL extraction source for debugging and error reporting
15
+ */
16
+ export type SqlSource =
17
+ | 'indicator.sqlTemplate'
18
+ | 'configJson.sqlPreview'
19
+ | 'configJson.sqlTemplate'
20
+ | 'configJson.base.sqlPreview'
21
+ | 'configJson.base.sqlTemplate'
22
+ | 'configJson.authoring.base.sqlPreview'
23
+ | 'configJson.authoring.base.sqlTemplate'
24
+ | 'configJson.baseIndicator.sqlPreview'
25
+ | 'configJson.baseIndicator.sqlTemplate'
26
+ | 'none'
27
+ | 'none(parse-error)'
28
+ | 'none(empty-config)';
29
+
30
+ /**
31
+ * Result of resolving indicator SQL with source tracking for debugging
32
+ */
33
+ export type ResolvedIndicatorSql = {
34
+ /** The extracted SQL */
35
+ sql: string;
36
+ /** Where the SQL was found (for debugging) */
37
+ source: SqlSource;
38
+ /** The config JSON that was parsed (for reference) */
39
+ parsedConfig?: any;
40
+ };
41
+
42
+ /**
43
+ * Structured error when SQL cannot be resolved
44
+ */
45
+ export type SqlResolutionError = {
46
+ success: false;
47
+ error: string;
48
+ indicator: {
49
+ uuid: string;
50
+ code?: string;
51
+ name?: string;
52
+ kind?: string;
53
+ };
54
+ attemptedSources: SqlSource[];
55
+ };
56
+
57
+ /**
58
+ * Resolve SQL from an indicator with comprehensive fallback chain.
59
+ *
60
+ * This is the canonical way to extract SQL from indicators across all compilation paths.
61
+ *
62
+ * Priority order (as per the current config structure):
63
+ * 1. indicator.sqlTemplate (direct field)
64
+ * 2. configJson.sqlPreview (flat config)
65
+ * 3. configJson.sqlTemplate (flat config)
66
+ * 4. configJson.base.sqlPreview (nested base config)
67
+ * 5. configJson.base.sqlTemplate (nested base config)
68
+ * 6. configJson.authoring.base.sqlPreview (authoring structure)
69
+ * 7. configJson.authoring.base.sqlTemplate (authoring structure)
70
+ * 8. configJson.baseIndicator.sqlPreview (wrapped indicator config)
71
+ * 9. configJson.baseIndicator.sqlTemplate (wrapped indicator config)
72
+ *
73
+ * @param indicator - The indicator to extract SQL from
74
+ * @returns Resolved SQL with source tracking, or structured error
75
+ */
76
+ export function resolveIndicatorSql(indicator: IndicatorDto): ResolvedIndicatorSql | SqlResolutionError {
77
+ const attemptedSources: SqlSource[] = [];
78
+ const direct = (indicator?.sqlTemplate ?? '').trim();
79
+
80
+ if (direct) {
81
+ return {
82
+ sql: normalizeEscapedNewlines(direct),
83
+ source: 'indicator.sqlTemplate'
84
+ };
85
+ }
86
+ attemptedSources.push('indicator.sqlTemplate');
87
+
88
+ // Try to parse config JSON
89
+ let parsed: any = null;
90
+ try {
91
+ parsed = indicator?.configJson ? JSON.parse(indicator.configJson) : null;
92
+ } catch (e) {
93
+ // Invalid JSON - will return error at end
94
+ parsed = null;
95
+ }
96
+
97
+ if (!parsed) {
98
+ return createSqlResolutionError(indicator, attemptedSources, 'configJson is null or invalid JSON');
99
+ }
100
+
101
+ // Helper to try a path and return early if found
102
+ const tryPath = (source: SqlSource, getValue: (p: any) => string): ResolvedIndicatorSql | null => {
103
+ attemptedSources.push(source);
104
+ const value = (getValue(parsed) ?? '').trim();
105
+ if (value) {
106
+ return {
107
+ sql: normalizeEscapedNewlines(value),
108
+ source,
109
+ parsedConfig: parsed
110
+ };
111
+ }
112
+ return null;
113
+ };
114
+
115
+ // Try all known paths in priority order
116
+ const result =
117
+ tryPath('configJson.sqlPreview', (p) => p.sqlPreview) ||
118
+ tryPath('configJson.sqlTemplate', (p) => p.sqlTemplate) ||
119
+ tryPath('configJson.base.sqlPreview', (p) => p.base?.sqlPreview) ||
120
+ tryPath('configJson.base.sqlTemplate', (p) => p.base?.sqlTemplate) ||
121
+ tryPath('configJson.authoring.base.sqlPreview', (p) => p.authoring?.base?.sqlPreview) ||
122
+ tryPath('configJson.authoring.base.sqlTemplate', (p) => p.authoring?.base?.sqlTemplate) ||
123
+ tryPath('configJson.baseIndicator.sqlPreview', (p) => p.baseIndicator?.sqlPreview) ||
124
+ tryPath('configJson.baseIndicator.sqlTemplate', (p) => p.baseIndicator?.sqlTemplate);
125
+
126
+ if (result) {
127
+ return result;
128
+ }
129
+
130
+ attemptedSources.push('none');
131
+ return createSqlResolutionError(indicator, attemptedSources, 'No SQL found in any known config location');
132
+ }
133
+
134
+ /**
135
+ * Create a structured SQL resolution error
136
+ */
137
+ function createSqlResolutionError(
138
+ indicator: IndicatorDto,
139
+ attemptedSources: SqlSource[],
140
+ reason: string
141
+ ): SqlResolutionError {
142
+ return {
143
+ success: false,
144
+ error: `SQL_RESOLUTION_FAILED: ${reason}`,
145
+ indicator: {
146
+ uuid: indicator.uuid,
147
+ code: indicator.code,
148
+ name: indicator.name,
149
+ kind: indicator.kind
150
+ },
151
+ attemptedSources
152
+ };
153
+ }
154
+
155
+ /**
156
+ * Check if a resolved SQL result is an error
157
+ */
158
+ export function isSqlResolutionError(result: ResolvedIndicatorSql | SqlResolutionError): result is SqlResolutionError {
159
+ return (result as any).success === false;
160
+ }
161
+
162
+ /**
163
+ * Resolve patient ID column from indicator config.
164
+ *
165
+ * Centralized way to extract the patient ID column across all compilation paths.
166
+ *
167
+ * Priority order:
168
+ * 1. parsed.themeConfig.patientIdColumn
169
+ * 2. parsed.base.themeConfig.patientIdColumn
170
+ * 3. parsed.authoring.base.themeConfig.patientIdColumn
171
+ * 4. parsed.baseIndicator.themeConfig.patientIdColumn
172
+ * 5. Default: 'client_id'
173
+ *
174
+ * @param indicator - The indicator to extract patient ID column from
175
+ * @returns The patient ID column name (defaults to 'client_id')
176
+ */
177
+ export function resolvePatientIdColumn(indicator: IndicatorDto): string {
178
+ try {
179
+ const parsed: any = indicator?.configJson ? JSON.parse(indicator.configJson) : null;
180
+
181
+ const cfg =
182
+ parsed?.themeConfig ? { ...parsed.themeConfig, _source: 'themeConfig' } :
183
+ parsed?.base?.themeConfig ? { ...parsed.base.themeConfig, _source: 'base.themeConfig' } :
184
+ parsed?.authoring?.base?.themeConfig ? { ...parsed.authoring.base.themeConfig, _source: 'authoring.base.themeConfig' } :
185
+ parsed?.baseIndicator?.themeConfig ? { ...parsed.baseIndicator.themeConfig, _source: 'baseIndicator.themeConfig' } :
186
+ null;
187
+
188
+ const pid = cfg?.patientIdColumn;
189
+ return pid ? String(pid) : 'client_id';
190
+ } catch {
191
+ return 'client_id';
192
+ }
193
+ }
194
+
195
+ /**
196
+ * Normalize escaped newlines in SQL.
197
+ *
198
+ * SQL stored in JSON may have literal \n instead of actual newlines.
199
+ * This converts them back.
200
+ */
201
+ export function normalizeEscapedNewlines(sql: string): string {
202
+ if (!sql) return sql;
203
+ return sql.replace(/\\n/g, '\n');
204
+ }