@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,426 @@
1
+ import type { IndicatorDto } from '../../../resources/indicator/indicators.api';
2
+ import { resolveIndicatorSql, isSqlResolutionError, resolvePatientIdColumn, type SqlSource } from './indicator-sql-resolution.utils';
3
+
4
+ export type CountSqlSource = SqlSource;
5
+
6
+ export function idFieldForUnit(unit: 'Patients' | 'Encounters', overrideColumn?: string) {
7
+ if (overrideColumn) return overrideColumn;
8
+ return unit === 'Encounters' ? 'encounter_id' : 'client_id';
9
+ }
10
+
11
+ /**
12
+ * Extract patientIdColumn from stored authoring (if present),
13
+ * otherwise fallback to "client_id".
14
+ *
15
+ * @deprecated Use resolvePatientIdColumn from indicator-sql-resolution.utils.ts instead.
16
+ * This function is maintained for backward compatibility but delegates to the centralized implementation.
17
+ */
18
+ export function tryGetPatientIdColumnFromConfig(ind: IndicatorDto): string {
19
+ return resolvePatientIdColumn(ind);
20
+ }
21
+
22
+ /**
23
+ * Check if an indicator is complex (has built-in disaggregation logic).
24
+ * Complex indicators should not be processed through COUNT-to-population conversion
25
+ * because they already have their own disaggregation structure.
26
+ * Note: Similar function exists in section-disaggregation.utils.ts which is actively used.
27
+ */
28
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
29
+ function isComplexIndicator(sql: string): boolean {
30
+ if (!sql) return false;
31
+
32
+ // Signs of complex indicators with built-in disaggregation:
33
+ // 1. Multiple GROUP BY clauses
34
+ const groupByCount = (sql.match(/GROUP BY/gi) || []).length;
35
+ if (groupByCount > 1) return true;
36
+
37
+ // 2. Has age_group/gender columns in SELECT with aggregation
38
+ if (/age_group.*AS.*aggregat|gender.*AS.*sex|disaggregat/i.test(sql)) return true;
39
+
40
+ // 3. Has complex WITH clauses (multiple CTEs with aggregation)
41
+ const withCount = (sql.match(/WITH.*AS\s*\(/gi) || []).length;
42
+ if (withCount > 1 && /GROUP BY/i.test(sql)) return true;
43
+
44
+ return false;
45
+ }
46
+
47
+ /**
48
+ * ✅ Key fix:
49
+ * Many times the backend returns sqlTemplate empty, but configJson contains sqlPreview.
50
+ * We support all known config shapes.
51
+ *
52
+ * @deprecated Use resolveIndicatorSql from indicator-sql-resolution.utils.ts instead.
53
+ * This function is maintained for backward compatibility but delegates to the centralized implementation.
54
+ */
55
+ export function tryGetCountSqlFromIndicator(ind: IndicatorDto): { sql: string; source: CountSqlSource } {
56
+ const result = resolveIndicatorSql(ind);
57
+
58
+ if (isSqlResolutionError(result)) {
59
+ return { sql: '', source: result.success === false ? 'none(parse-error)' : 'none' };
60
+ }
61
+
62
+ return { sql: result.sql, source: result.source };
63
+ }
64
+
65
+ /**
66
+ * Extract the primary table alias from a SQL query.
67
+ * Looks for patterns like "FROM table_name alias" or "FROM table_name AS alias"
68
+ * Returns the alias or null if not found.
69
+ */
70
+ function extractTableAlias(sql: string): string | null {
71
+ // Match: FROM table_name alias or FROM table_name AS alias
72
+ // Skip if inside parentheses (subqueries) - we only care about the first FROM
73
+ const fromPattern = /FROM\s+([a-zA-Z_][a-zA-Z0-9_]*)\s+(?:AS\s+)?([a-zA-Z_][a-zA-Z0-9_]*)\b/i;
74
+ const match = sql.match(fromPattern);
75
+ return match ? match[2] : null;
76
+ }
77
+
78
+ /**
79
+ * Convert a base indicator COUNT sqlTemplate into a population query.
80
+ *
81
+ * Supports BOTH:
82
+ * A) Base-style COUNT SQL:
83
+ * SELECT COUNT(*) AS total
84
+ * FROM <table> a
85
+ * ...
86
+ *
87
+ * B) Composite-style COUNT SQL:
88
+ * WITH A AS (...), B AS (...)
89
+ * SELECT COUNT(*) AS total
90
+ * FROM ( SELECT A.client_id ... ) X;
91
+ *
92
+ * For composite-style, we MUST preserve the WITH ... prefix,
93
+ * otherwise the resulting query references A/B without defining them.
94
+ *
95
+ * Returns population SQL that always exposes the patient ID column.
96
+ */
97
+ export function countSqlToPopulationSql(sql: string, idColumn: string, unit: 'Patients' | 'Encounters') {
98
+ // Use idColumn (from theme config) as the output field name for consistency
99
+ // This ensures base_pop uses the same column name as the source table
100
+ const idField = idColumn || idFieldForUnit(unit);
101
+
102
+ let raw = (sql ?? '').trim();
103
+ if (!raw) return '';
104
+
105
+ // Handle escaped newlines BEFORE pattern matching
106
+ // This ensures multi-line SQL with GROUP BY, HAVING, etc. on separate lines is preserved
107
+ raw = normalizeEscapedNewlines(raw);
108
+
109
+ // normalize trailing semicolons
110
+ const noSemi = raw.replace(/;+\s*$/, '');
111
+
112
+ // ✅ Case 0a: Base COUNT DISTINCT SQL without AS clause
113
+ // Handle "SELECT COUNT(DISTINCT column) FROM ... WHERE ..." (no AS alias)
114
+ // by converting to "SELECT DISTINCT column AS {idField} FROM ... WHERE ..."
115
+ const distinctPatternNoAlias = /SELECT\s+COUNT\s*\(\s*DISTINCT\s+([a-zA-Z_][a-zA-Z0-9_]*(?:\.[a-zA-Z_][a-zA-Z0-9_]*)?)\s*\)\s+FROM/i;
116
+ const distinctMatchNoAlias = noSemi.match(distinctPatternNoAlias);
117
+ if (distinctMatchNoAlias) {
118
+ const columnRef = distinctMatchNoAlias[1]; // e.g., "a.client_id" or "client_id"
119
+ // Get everything after "SELECT COUNT(DISTINCT ...) FROM " to preserve FROM, WHERE, JOINs, etc.
120
+ const fromIdx = distinctMatchNoAlias[0].length - 5; // -5 to account for " FROM" that we'll replace
121
+ const afterSelect = noSemi.substring(fromIdx).trim();
122
+
123
+ // If column is already qualified (e.g., "a.client_id"), use it as-is
124
+ if (columnRef.includes('.')) {
125
+ const result = `SELECT DISTINCT ${columnRef} AS ${idField} ${afterSelect}`.trim();
126
+ return validatePopulationSqlOutput(result, idField);
127
+ }
128
+
129
+ // Extract the actual table alias from FROM clause instead of assuming "a"
130
+ const tableAlias = extractTableAlias(afterSelect) || 'a';
131
+ const result = `SELECT DISTINCT ${tableAlias}.${columnRef} AS ${idField} ${afterSelect}`.trim();
132
+ return validatePopulationSqlOutput(result, idField);
133
+ }
134
+
135
+ // ✅ Case 0b: Base COUNT DISTINCT SQL with AS clause
136
+ // Handle "SELECT COUNT(DISTINCT column) AS alias FROM ... WHERE ..."
137
+ // by converting to "SELECT DISTINCT column AS {idField} FROM ... WHERE ..."
138
+ // Preserves FROM, WHERE, JOINs, and all other clauses
139
+ // Enhanced: supports any result alias name (not just "total") and extracts actual table alias
140
+ const distinctPattern = /SELECT\s+COUNT\s*\(\s*DISTINCT\s+([a-zA-Z_][a-zA-Z0-9_]*(?:\.[a-zA-Z_][a-zA-Z0-9_]*)?)\s*\)\s+AS\s+(\w+)\s*/i;
141
+ const distinctMatch = noSemi.match(distinctPattern);
142
+ if (distinctMatch) {
143
+ const columnRef = distinctMatch[1]; // e.g., "a.client_id" or "client_id"
144
+ // Get everything after "SELECT COUNT(DISTINCT ...) AS alias "
145
+ // This preserves FROM, WHERE, JOINs, etc.
146
+ const afterSelect = noSemi.substring(distinctMatch[0].length);
147
+
148
+ // If column is already qualified (e.g., "a.client_id"), use it as-is
149
+ // Always add the AS alias for consistency
150
+ if (columnRef.includes('.')) {
151
+ const result = `SELECT DISTINCT ${columnRef} AS ${idField} ${afterSelect}`.trim();
152
+ return validatePopulationSqlOutput(result, idField);
153
+ }
154
+
155
+ // Extract the actual table alias from FROM clause instead of assuming "a"
156
+ const tableAlias = extractTableAlias(afterSelect) || 'a';
157
+ const result = `SELECT DISTINCT ${tableAlias}.${columnRef} AS ${idField} ${afterSelect}`.trim();
158
+ return validatePopulationSqlOutput(result, idField);
159
+ }
160
+
161
+ // ✅ Case 1: Composite COUNT SQL
162
+ // Preserve any WITH CTE prefix (e.g. WITH A AS (...), B AS (...))
163
+ // Replace "SELECT COUNT(*) AS total FROM ( <inner select> ) X" with a population select.
164
+ const countIdx = noSemi.search(/SELECT\s+COUNT\s*\(\s*\*\s*\)\s+AS\s+total/i);
165
+ const innerMatch = noSemi.match(/FROM\s*\(\s*(SELECT[\s\S]*?)\)\s*X\b/i);
166
+
167
+ if (countIdx >= 0 && innerMatch?.[1]) {
168
+ const prefix = noSemi.slice(0, countIdx).trim(); // keeps WITH A,B if present
169
+ const inner = innerMatch[1].trim().replace(/;+\s*$/, '');
170
+
171
+ const result = `
172
+ ${prefix}
173
+ SELECT DISTINCT pop.${idField}
174
+ FROM (
175
+ ${inner}
176
+ ) pop
177
+ `.trim();
178
+ return validatePopulationSqlOutput(result, idField);
179
+ }
180
+
181
+ // ✅ Case 2: Base COUNT SQL
182
+ // Replace the COUNT select with a DISTINCT id select.
183
+ // Extract the actual table alias from FROM clause instead of assuming "a"
184
+ const tableAlias = extractTableAlias(noSemi) || 'a';
185
+ const replaced = noSemi.replace(
186
+ /SELECT\s+([\s\S]*?)COUNT\s*\(\s*\*\s*\)\s+AS\s+total\s*/i,
187
+ `SELECT DISTINCT ${tableAlias}.${idColumn} AS ${idField}\n`,
188
+ );
189
+
190
+ // If it didn't replace, fallback: try simpler replace
191
+ let result = replaced;
192
+ if (result === noSemi) {
193
+ result = noSemi.replace(/COUNT\s*\(\s*\*\s*\)\s+AS\s+total/gi, `DISTINCT ${tableAlias}.${idColumn} AS ${idField}`);
194
+ }
195
+
196
+ return validatePopulationSqlOutput(result, idField);
197
+ }
198
+
199
+ /**
200
+ * Validates that the output of countSqlToPopulationSql is proper population SQL.
201
+ * Logs a warning if the output doesn't start with SELECT DISTINCT.
202
+ */
203
+ function validatePopulationSqlOutput(sql: string, idField: string): string {
204
+ const trimmed = sql.trim();
205
+
206
+ // Check if output is valid population SQL
207
+ if (!trimmed.startsWith('SELECT DISTINCT')) {
208
+ console.warn(`[countSqlToPopulationSql] Output does not start with SELECT DISTINCT. Output:`, trimmed.substring(0, 100));
209
+ }
210
+
211
+ // Check if the output exposes the expected ID field
212
+ const idPattern = new RegExp(`(?:SELECT\\s+DISTINCT\\s+(?:[\\w]+\\.)?${idField}\\b|(?:AS\\s+${idField}\\b))`, 'i');
213
+ if (!idPattern.test(trimmed)) {
214
+ console.warn(`[countSqlToPopulationSql] Output may not expose '${idField}' column. Output:`, trimmed.substring(0, 100));
215
+ }
216
+
217
+ return trimmed;
218
+ }
219
+
220
+ /**
221
+ * Extracts CTEs from a SQL query and renames them to avoid conflicts.
222
+ * Handles nested WITH clauses by recursively extracting inner CTEs to the top level.
223
+ *
224
+ * @param sql - The SQL query that may start WITH CTEs
225
+ * @param prefix - Prefix to add to CTE names (e.g., 'base_' or 'filter_')
226
+ * @returns Object with ctes string and the main query with updated references
227
+ */
228
+ function extractAndRenameCtes(sql: string, prefix: string): { ctes: string; mainQuery: string } {
229
+ const trimmed = sql.trim();
230
+
231
+ // Check if SQL starts WITH
232
+ const withMatch = trimmed.match(/^\s*WITH\s+/i);
233
+ if (!withMatch) {
234
+ // No CTEs, return as-is
235
+ return { ctes: '', mainQuery: trimmed };
236
+ }
237
+
238
+ // Find the SELECT that starts the main query (after all CTEs)
239
+ let parenCount = 0;
240
+ let mainSelectIndex = -1;
241
+ let foundWith = false;
242
+
243
+ for (let i = 0; i < trimmed.length; i++) {
244
+ const char = trimmed[i];
245
+
246
+ // Track if we've passed the WITH keyword
247
+ if (!foundWith && i >= withMatch[0].length) {
248
+ foundWith = true;
249
+ }
250
+
251
+ if (char === '(') {
252
+ parenCount++;
253
+ } else if (char === ')') {
254
+ parenCount--;
255
+ }
256
+
257
+ // Look for SELECT when we're not in parentheses
258
+ if (foundWith && parenCount === 0) {
259
+ if (trimmed.substring(i, i + 6).toUpperCase() === 'SELECT') {
260
+ mainSelectIndex = i;
261
+ break;
262
+ }
263
+ }
264
+ }
265
+
266
+ if (mainSelectIndex === -1) {
267
+ // Couldn't find main SELECT, return as-is
268
+ return { ctes: '', mainQuery: trimmed };
269
+ }
270
+
271
+ // Split into CTEs section and main query
272
+ const ctesSection = trimmed.substring(withMatch[0].length, mainSelectIndex);
273
+ const mainQuery = trimmed.substring(mainSelectIndex);
274
+
275
+ // Process CTEs - handle nested WITH by recursive extraction
276
+ const allCtes: string[] = [];
277
+ let current = '';
278
+ let level = 0;
279
+
280
+ for (let i = 0; i < ctesSection.length; i++) {
281
+ const char = ctesSection[i];
282
+
283
+ if (char === '(') level++;
284
+ else if (char === ')') level--;
285
+
286
+ current += char;
287
+
288
+ // When level returns to 0, check for comma or end
289
+ if (level === 0 && current.trim()) {
290
+ // Look ahead for comma
291
+ let j = i + 1;
292
+ while (j < ctesSection.length && /[\s]/.test(ctesSection[j])) j++;
293
+
294
+ if (j >= ctesSection.length || ctesSection[j] === ',') {
295
+ // Complete CTE
296
+ const cteDef = current.trim().replace(/,$/, '');
297
+ if (cteDef) {
298
+ // Match: NAME AS (body)
299
+ const nameMatch = cteDef.match(/^([A-Za-z_][A-Za-z0-9_]*)\s+AS\s*\((.*)\)\s*$/);
300
+ if (nameMatch) {
301
+ const oldName = nameMatch[1];
302
+ let body = nameMatch[2].trim();
303
+ const newName = prefix + oldName;
304
+
305
+ // Check if body contains nested WITH clause
306
+ const nestedWithMatch = body.match(/^\s*WITH\s+/i);
307
+ if (nestedWithMatch) {
308
+ // Recursively extract nested CTEs
309
+ const nestedResult = extractAndRenameCtes(body, prefix);
310
+ // Add the nested CTEs first (they're referenced by the main query)
311
+ if (nestedResult.ctes) {
312
+ allCtes.push(nestedResult.ctes);
313
+ }
314
+ // Update body to use the extracted main query
315
+ body = nestedResult.mainQuery;
316
+ }
317
+
318
+ // Update references within body:
319
+ // 1. Column references like A.xxx -> base_A.xxx
320
+ let updatedBody = body.replace(/\b([A-D])\.(\w+)/g, `${prefix}$1.$2`);
321
+ // 2. Table references like FROM A, JOIN A -> FROM base_A, JOIN base_A
322
+ // Use negative lookbehind to avoid matching A in base_A
323
+ updatedBody = updatedBody.replace(/(?<![_A-Za-z0-9])([A-D])\b(?!\.)/g, `${prefix}$1`);
324
+
325
+ allCtes.push(`${newName} AS (\n ${updatedBody}\n)`);
326
+ }
327
+ }
328
+ current = '';
329
+ // Skip past comma
330
+ if (j < ctesSection.length && ctesSection[j] === ',') {
331
+ i = j;
332
+ }
333
+ }
334
+ }
335
+ }
336
+
337
+ // Update references in main query
338
+ let updatedMainQuery = mainQuery.replace(/\b([A-D])\.(\w+)/g, `${prefix}$1.$2`);
339
+ // Also update table references like FROM A, JOIN A
340
+ updatedMainQuery = updatedMainQuery.replace(/(?<![_A-Za-z0-9])([A-D])\b(?!\.)/g, `${prefix}$1`);
341
+
342
+ return {
343
+ ctes: allCtes.join(',\n'),
344
+ mainQuery: updatedMainQuery
345
+ };
346
+ }
347
+
348
+ /**
349
+ * Build composite COUNT SQL from two population queries.
350
+ * Population queries MUST return a column named client_id or encounter_id.
351
+ *
352
+ * Handles the case where population queries themselves contain WITH clauses
353
+ * (composite indicators) by extracting and renaming inner CTEs to avoid conflicts.
354
+ */
355
+ export function buildCompositeCountSql(args: {
356
+ unit: 'Patients' | 'Encounters';
357
+ operator: 'AND' | 'OR' | 'A_AND_NOT_B';
358
+ populationSqlA: string;
359
+ populationSqlB: string;
360
+ }) {
361
+ const idField = idFieldForUnit(args.unit);
362
+
363
+ const rawA = args.populationSqlA.trim().replace(/;+\s*$/, '');
364
+ const rawB = args.populationSqlB.trim().replace(/;+\s*$/, '');
365
+
366
+ if (!rawA || !rawB) return '';
367
+
368
+ // Extract and rename CTEs from population queries (if they are composite indicators)
369
+ const resultA = extractAndRenameCtes(rawA, 'base_');
370
+ const resultB = extractAndRenameCtes(rawB, 'filter_');
371
+
372
+ // Build the flattened WITH clause with all CTEs
373
+ // IMPORTANT: CTEs must be ordered such that inner CTEs are defined before outer CTEs
374
+ // that reference them. The order should be:
375
+ // 1. Inner CTEs from A (base_A, base_B, ...)
376
+ // 2. Inner CTEs from B (filter_A, filter_B, ...)
377
+ // 3. Outer CTE A (which references base_*)
378
+ // 4. Outer CTE B (which references filter_*)
379
+ const withCtes: string[] = [];
380
+
381
+ // Add inner CTEs from A (renamed with base_ prefix) - these come first
382
+ if (resultA.ctes) {
383
+ withCtes.push(resultA.ctes);
384
+ }
385
+
386
+ // Add inner CTEs from B (renamed with filter_ prefix) - these come second
387
+ if (resultB.ctes) {
388
+ withCtes.push(resultB.ctes);
389
+ }
390
+
391
+ // Add outer CTE declarations for A and B - these come last and reference the inner CTEs
392
+ withCtes.push(`A AS (\n ${resultA.mainQuery}\n)`);
393
+ withCtes.push(`B AS (\n ${resultB.mainQuery}\n)`);
394
+
395
+ // Build the final query based on operator
396
+ let innerSelect = '';
397
+
398
+ if (args.operator === 'AND') {
399
+ innerSelect = `SELECT A.${idField}\n FROM A\n INNER JOIN B ON B.${idField} = A.${idField}`;
400
+ } else if (args.operator === 'OR') {
401
+ innerSelect = `SELECT ${idField} FROM A\n UNION\n SELECT ${idField} FROM B`;
402
+ } else {
403
+ // A_AND_NOT_B
404
+ innerSelect = `SELECT A.${idField}\n FROM A\n LEFT JOIN B ON B.${idField} = A.${idField}\n WHERE B.${idField} IS NULL`;
405
+ }
406
+
407
+ return `
408
+ WITH
409
+ ${withCtes.join(',\n')}
410
+ SELECT COUNT(*) AS total
411
+ FROM (
412
+ ${innerSelect}
413
+ ) X;
414
+ `.trim();
415
+ }
416
+
417
+ /**
418
+ * Normalize SQL by converting escaped newlines to actual newlines.
419
+ * This handles cases where SQL is serialized through JSON in the backend,
420
+ * which can escape newlines as literal \n instead of actual newline characters.
421
+ */
422
+ function normalizeEscapedNewlines(sql: string): string {
423
+ if (!sql) return sql;
424
+ // Convert literal \n to actual newlines
425
+ return sql.replace(/\\n/g, '\n');
426
+ }
@@ -0,0 +1,21 @@
1
+ import { omrsGet } from '../../../resources/openmrs-api';
2
+ import type { ConceptSummary } from '../../../resources/concepts/concept-types';
3
+ import type { SelectedConcept } from '../handler/concept-search-multiselect.component';
4
+
5
+ export async function searchConcepts(q: string, signal?: AbortSignal): Promise<ConceptSummary[]> {
6
+ const trimmed = q.trim();
7
+ if (!trimmed) return [];
8
+ const data = await omrsGet<any>(`/concept?q=${encodeURIComponent(trimmed)}&v=default`, signal);
9
+ const results = Array.isArray(data?.results) ? data.results : Array.isArray(data) ? data : [];
10
+ return results as ConceptSummary[];
11
+ }
12
+
13
+ export function toSelectedConcept(c: any): SelectedConcept {
14
+ return {
15
+ id: Number(c.id) || 0,
16
+ uuid: c.uuid,
17
+ display: c.display,
18
+ conceptClass: c.conceptClass?.name,
19
+ datatype: c.datatype?.name,
20
+ };
21
+ }