@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.
- package/.eslintignore +5 -0
- package/.eslintrc.js +19 -0
- package/.prettierrc +8 -0
- package/.tx/config +12 -0
- package/LICENSE +401 -0
- package/README.md +336 -0
- package/__mocks__/react-i18next.js +12 -0
- package/dist/162.js +1 -0
- package/dist/162.js.map +1 -0
- package/dist/184.js +2 -0
- package/dist/184.js.LICENSE.txt +14 -0
- package/dist/184.js.map +1 -0
- package/dist/242.js +2 -0
- package/dist/242.js.LICENSE.txt +21 -0
- package/dist/242.js.map +1 -0
- package/dist/253.js +1 -0
- package/dist/253.js.map +1 -0
- package/dist/300.js +1 -0
- package/dist/311.js +1 -0
- package/dist/311.js.map +1 -0
- package/dist/469.js +2 -0
- package/dist/469.js.LICENSE.txt +45 -0
- package/dist/469.js.map +1 -0
- package/dist/478.js +2 -0
- package/dist/478.js.LICENSE.txt +9 -0
- package/dist/478.js.map +1 -0
- package/dist/539.js +1 -0
- package/dist/539.js.map +1 -0
- package/dist/540.js +2 -0
- package/dist/540.js.LICENSE.txt +9 -0
- package/dist/540.js.map +1 -0
- package/dist/669.js +1 -0
- package/dist/669.js.map +1 -0
- package/dist/710.js +1 -0
- package/dist/710.js.map +1 -0
- package/dist/729.js +1 -0
- package/dist/729.js.map +1 -0
- package/dist/838.js +2 -0
- package/dist/838.js.LICENSE.txt +9 -0
- package/dist/838.js.map +1 -0
- package/dist/917.js +1 -0
- package/dist/917.js.map +1 -0
- package/dist/961.js +2 -0
- package/dist/961.js.LICENSE.txt +19 -0
- package/dist/961.js.map +1 -0
- package/dist/main.js +1 -0
- package/dist/main.js.map +1 -0
- package/dist/openmrs-esm-report-builder.js +1 -0
- package/dist/openmrs-esm-report-builder.js.buildmanifest.json +530 -0
- package/dist/openmrs-esm-report-builder.js.map +1 -0
- package/dist/routes.json +1 -0
- package/e2e/core/env.ts +5 -0
- package/e2e/core/global-setup.ts +34 -0
- package/e2e/core/test.ts +21 -0
- package/e2e/pages/app-shell.page.ts +34 -0
- package/e2e/pages/etl-sources.page.ts +35 -0
- package/e2e/pages/report-sections.page.ts +20 -0
- package/e2e/pages/section-preview.modal.ts +29 -0
- package/e2e/specs/etl-sources.spec.ts +51 -0
- package/e2e/specs/navigation.spec.ts +13 -0
- package/e2e/specs/section-preview-validation.spec.ts +38 -0
- package/e2e/specs/section-preview.spec.ts +94 -0
- package/e2e/support/github/Dockerfile +22 -0
- package/e2e/support/github/Dockerfile.dockerignore +13 -0
- package/e2e/support/github/docker-compose.yml +31 -0
- package/e2e/support/github/run-e2e-docker-env.sh +131 -0
- package/e2e/support/github/spa-assemble-config.json +9 -0
- package/e2e/tsconfig.json +11 -0
- package/example.env +5 -0
- package/jest.config.js +28 -0
- package/linelist_report_builder_design_spec.md +816 -0
- package/package.json +111 -0
- package/playwright.config.ts +28 -0
- package/routes.json +22 -0
- package/src/__debug__/all-designs.test.tsx +30 -0
- package/src/admin-card-link.extension.module.scss +55 -0
- package/src/admin-card-link.extension.tsx +24 -0
- package/src/components/admin/admin-page.component.tsx +75 -0
- package/src/components/admin/age-categories-page.component.tsx +109 -0
- package/src/components/admin/age-groups-page.component.tsx +146 -0
- package/src/components/admin/etl-browser/columns-tab.component.tsx +80 -0
- package/src/components/admin/etl-browser/etl-browser-page.component.tsx +309 -0
- package/src/components/admin/etl-browser/etl-browser.component.scss +424 -0
- package/src/components/admin/etl-browser/metadata-tab.component.tsx +65 -0
- package/src/components/admin/etl-browser/preview-grid.component.tsx +189 -0
- package/src/components/admin/etl-browser/relationships-tab.component.tsx +21 -0
- package/src/components/admin/etl-browser/sources-tree.component.tsx +118 -0
- package/src/components/admin/etl-browser/table-detail.component.tsx +124 -0
- package/src/components/admin/etl-browser/table-info-strip.component.tsx +51 -0
- package/src/components/admin/etl-browser/table-list.component.tsx +136 -0
- package/src/components/admin/etl-browser/use-table-preview.ts +47 -0
- package/src/components/admin/etl-sources-page.component.tsx +355 -0
- package/src/components/admin/etl-tasks-page.component.scss +370 -0
- package/src/components/admin/etl-tasks-page.component.tsx +768 -0
- package/src/components/admin/report-categories-page.component.tsx +267 -0
- package/src/components/admin/report-library-page.component.tsx +551 -0
- package/src/components/ai-support/ai-assist-button.component.tsx +30 -0
- package/src/components/ai-support/ai-assist-modal.component.tsx +81 -0
- package/src/components/ai-support/ai.service.ts +21 -0
- package/src/components/ai-support/ai.types.ts +12 -0
- package/src/components/app-shell/report-builder-shell.component.tsx +242 -0
- package/src/components/app-shell/report-builder-shell.scss +18 -0
- package/src/components/dashboards/dashboard-config.schema.ts +97 -0
- package/src/components/dashboards/dashboard-form-modal.component.tsx +150 -0
- package/src/components/dashboards/dashboards-admin.scss +62 -0
- package/src/components/dashboards/dashboards-page.component.tsx +153 -0
- package/src/components/dashboards/dashboards-table.component.tsx +130 -0
- package/src/components/dashboards/sections/dashboard-autoinclude.section.tsx +75 -0
- package/src/components/dashboards/sections/dashboard-basics.section.tsx +80 -0
- package/src/components/dashboards/sections/dashboard-json.section.tsx +79 -0
- package/src/components/dashboards/sections/dashboard-sections-editor.section.tsx +130 -0
- package/src/components/dashboards/sections/dashboard-widgets-editor.section.tsx +184 -0
- package/src/components/data-themes/data-theme-form.component.tsx +59 -0
- package/src/components/data-themes/data-theme-modal.component.tsx +349 -0
- package/src/components/data-themes/data-themes-page.component.tsx +139 -0
- package/src/components/data-themes/data-themes-table.component.tsx +98 -0
- package/src/components/data-themes/icon-registry.ts +35 -0
- package/src/components/data-themes/sections/data-theme-basics.section.tsx +63 -0
- package/src/components/data-themes/sections/data-theme-condition-columns.section.tsx +397 -0
- package/src/components/data-themes/sections/data-theme-fields-editor.section.tsx +125 -0
- package/src/components/data-themes/sections/data-theme-metadata.section.tsx +245 -0
- package/src/components/data-themes/sections/data-theme-preview.section.tsx +115 -0
- package/src/components/data-themes/sections/data-theme-source.section.tsx +184 -0
- package/src/components/data-visualizer/components/cqi-components/cqi-data-table.component.tsx +214 -0
- package/src/components/data-visualizer/components/cqi-components/cqi-elements.ts +203 -0
- package/src/components/data-visualizer/components/cqi-components/detail.component.tsx +110 -0
- package/src/components/data-visualizer/components/cqi-components/detail.scss +22 -0
- package/src/components/data-visualizer/components/data-table/data-table.component.tsx +208 -0
- package/src/components/data-visualizer/components/data-table/data-tables.scss +206 -0
- package/src/components/data-visualizer/components/empty-state/empty-state-illustration.component.tsx +53 -0
- package/src/components/data-visualizer/components/header/header.component.tsx +43 -0
- package/src/components/data-visualizer/components/header/header.scss +71 -0
- package/src/components/data-visualizer/components/model/confirm.component.tsx +40 -0
- package/src/components/data-visualizer/components/panel/panel.component.tsx +20 -0
- package/src/components/data-visualizer/components/panel/panel.scss +25 -0
- package/src/components/data-visualizer/components/popover/modifier-panel.tsx +53 -0
- package/src/components/data-visualizer/constants.ts +445 -0
- package/src/components/data-visualizer/data-visualizer-illustration.component.tsx +31 -0
- package/src/components/data-visualizer/data-visualizer.component.tsx +1935 -0
- package/src/components/data-visualizer/data-visualizer.resource.ts +462 -0
- package/src/components/data-visualizer/data-visualizer.scss +449 -0
- package/src/components/data-visualizer/declarations.d.ts +131 -0
- package/src/components/data-visualizer/parameter-inputs/boolean-parameter-input.component.tsx +34 -0
- package/src/components/data-visualizer/parameter-inputs/concept-parameter-input.component.tsx +81 -0
- package/src/components/data-visualizer/parameter-inputs/date-parameter-input.component.tsx +110 -0
- package/src/components/data-visualizer/parameter-inputs/date-range-input.component.scss +47 -0
- package/src/components/data-visualizer/parameter-inputs/date-range-input.component.tsx +228 -0
- package/src/components/data-visualizer/parameter-inputs/identifier-type-parameter-input.component.tsx +69 -0
- package/src/components/data-visualizer/parameter-inputs/list-parameter-input.component.tsx +73 -0
- package/src/components/data-visualizer/parameter-inputs/location-parameter-input.component.tsx +81 -0
- package/src/components/data-visualizer/parameter-inputs/number-parameter-input.component.tsx +40 -0
- package/src/components/data-visualizer/parameter-inputs/person-attribute-parameter-input.component.tsx +71 -0
- package/src/components/data-visualizer/parameter-inputs/text-parameter-input.component.tsx +52 -0
- package/src/components/data-visualizer/report-parameter-modal.component.tsx +383 -0
- package/src/components/data-visualizer/types.ts +206 -0
- package/src/components/etl-monitor/builder/builder-state-machine.ts +381 -0
- package/src/components/etl-monitor/builder/design-registry.tsx +139 -0
- package/src/components/etl-monitor/builder/etl-monitor-builder-context.tsx +702 -0
- package/src/components/etl-monitor/builder/etl-monitor-builder-modal.component.tsx +461 -0
- package/src/components/etl-monitor/builder/etl-monitor-builder-modal.scss +256 -0
- package/src/components/etl-monitor/builder/etl-monitor-builder-page.component.tsx +387 -0
- package/src/components/etl-monitor/builder/etl-monitor-builder-page.scss +976 -0
- package/src/components/etl-monitor/builder/generated-config-panel.component.tsx +168 -0
- package/src/components/etl-monitor/builder/generated-config-panel.scss +198 -0
- package/src/components/etl-monitor/builder/index.ts +5 -0
- package/src/components/etl-monitor/builder/monitor-preview.tsx +124 -0
- package/src/components/etl-monitor/builder/steps/data-source-step.component.tsx +667 -0
- package/src/components/etl-monitor/builder/steps/data-source-step.scss +284 -0
- package/src/components/etl-monitor/builder/steps/design-samples.ts +278 -0
- package/src/components/etl-monitor/builder/steps/design-step.component.tsx +299 -0
- package/src/components/etl-monitor/builder/steps/design-step.scss +479 -0
- package/src/components/etl-monitor/builder/steps/fields-step.component.tsx +859 -0
- package/src/components/etl-monitor/builder/steps/fields-step.scss +459 -0
- package/src/components/etl-monitor/builder/steps/general-step.component.tsx +288 -0
- package/src/components/etl-monitor/builder/steps/general-step.scss +103 -0
- package/src/components/etl-monitor/builder/steps/index.ts +5 -0
- package/src/components/etl-monitor/builder/steps/preview-step.component.tsx +245 -0
- package/src/components/etl-monitor/builder/steps/preview-step.scss +236 -0
- package/src/components/etl-monitor/etl-monitor-card-shell.component.tsx +154 -0
- package/src/components/etl-monitor/etl-monitor-card-shell.scss +103 -0
- package/src/components/etl-monitor/etl-monitor-dashboard.component.tsx +227 -0
- package/src/components/etl-monitor/etl-monitor-dashboard.scss +148 -0
- package/src/components/etl-monitor/etl-monitor-status-card.component.tsx +192 -0
- package/src/components/etl-monitor/etl-monitors-page.component.tsx +621 -0
- package/src/components/etl-monitor/renderers/DetailsRenderer.tsx +2 -0
- package/src/components/etl-monitor/renderers/ErrorLogRenderer.tsx +2 -0
- package/src/components/etl-monitor/renderers/EtlMonitorWidget.tsx +97 -0
- package/src/components/etl-monitor/renderers/MetricsGridRenderer.tsx +2 -0
- package/src/components/etl-monitor/renderers/MonitorRenderer.tsx +175 -0
- package/src/components/etl-monitor/renderers/ProgressRenderer.tsx +2 -0
- package/src/components/etl-monitor/renderers/StatusCardRenderer.tsx +2 -0
- package/src/components/etl-monitor/renderers/SummaryCardRenderer.tsx +2 -0
- package/src/components/etl-monitor/renderers/TableRenderer.tsx +2 -0
- package/src/components/etl-monitor/renderers/component-icons.tsx +33 -0
- package/src/components/etl-monitor/renderers/components/details-renderer.tsx +195 -0
- package/src/components/etl-monitor/renderers/components/error-log-renderer.tsx +104 -0
- package/src/components/etl-monitor/renderers/components/index.ts +6 -0
- package/src/components/etl-monitor/renderers/components/log-renderer.tsx +86 -0
- package/src/components/etl-monitor/renderers/components/metrics-grid-renderer.tsx +133 -0
- package/src/components/etl-monitor/renderers/components/progress-renderer.tsx +188 -0
- package/src/components/etl-monitor/renderers/components/status-card-renderer.tsx +247 -0
- package/src/components/etl-monitor/renderers/components/summary-card-renderer.tsx +160 -0
- package/src/components/etl-monitor/renderers/components/table-renderer.tsx +175 -0
- package/src/components/etl-monitor/renderers/components/time-series-renderer.tsx +310 -0
- package/src/components/etl-monitor/renderers/data-transformer.ts +361 -0
- package/src/components/etl-monitor/renderers/index.ts +11 -0
- package/src/components/etl-monitor/renderers/legacy-monitor-renderer.tsx +69 -0
- package/src/components/etl-monitor/renderers/monitor-renderer.tsx +6 -0
- package/src/components/etl-monitor/renderers/monitor-renderers.scss +1621 -0
- package/src/components/etl-monitor/renderers/monitor-widget-card.component.tsx +54 -0
- package/src/components/etl-monitor/renderers/monitor-widget-card.scss +16 -0
- package/src/components/etl-monitor/renderers/v2-monitor-renderer.tsx +227 -0
- package/src/components/import-export/artifact-groups.ts +153 -0
- package/src/components/import-export/export-report-modal.component.tsx +265 -0
- package/src/components/import-export/import-export-page.component.tsx +610 -0
- package/src/components/import-export/import-export.styles.scss +825 -0
- package/src/components/import-export/import-package-modal.component.tsx +401 -0
- package/src/components/import-export/index.ts +3 -0
- package/src/components/indicators/README.md +24 -0
- package/src/components/indicators/base-indicator-basic-fields.component.tsx +18 -0
- package/src/components/indicators/base-indicator-basics.section.tsx +54 -0
- package/src/components/indicators/base-indicator-countby.section.tsx +25 -0
- package/src/components/indicators/base-indicator-criteria.section.tsx +83 -0
- package/src/components/indicators/base-indicator-details-form.component.tsx +80 -0
- package/src/components/indicators/base-indicator-theme-unit-fields.component.tsx +39 -0
- package/src/components/indicators/create-base-indicator-modal.component.tsx +623 -0
- package/src/components/indicators/create-composite-base-indicator-modal.component.tsx +295 -0
- package/src/components/indicators/create-custom-indicator-modal.component.tsx +548 -0
- package/src/components/indicators/create-final-indicator-modal.component.tsx +285 -0
- package/src/components/indicators/custom-condition-input.component.tsx +310 -0
- package/src/components/indicators/diagnosis-filters-form.component.tsx +101 -0
- package/src/components/indicators/disaggregation-panel.component.tsx +93 -0
- package/src/components/indicators/handler/concept-search-multiselect.component.tsx +153 -0
- package/src/components/indicators/handler/question-answer-concept-search.component.tsx +336 -0
- package/src/components/indicators/indicator-run-preview-modal.component.tsx +391 -0
- package/src/components/indicators/indicator-search-select.component.tsx +297 -0
- package/src/components/indicators/indicator-status-tag.component.tsx +9 -0
- package/src/components/indicators/indicators-page.component.tsx +673 -0
- package/src/components/indicators/indicators-page.scss +121 -0
- package/src/components/indicators/indicators-table.component.tsx +188 -0
- package/src/components/indicators/preview-table.component.tsx +31 -0
- package/src/components/indicators/sections/composite-indicator-basics.section.tsx +36 -0
- package/src/components/indicators/sections/composite-indicator-picker.section.tsx +116 -0
- package/src/components/indicators/sections/composite-indicator-sql-preview.section.tsx +31 -0
- package/src/components/indicators/sections/custom-indicator-basics.section.tsx +65 -0
- package/src/components/indicators/sections/custom-indicator-disaggregation.section.tsx +93 -0
- package/src/components/indicators/sections/custom-indicator-filters.section.tsx +203 -0
- package/src/components/indicators/sections/custom-indicator-population.section.tsx +107 -0
- package/src/components/indicators/sections/custom-indicator-sql-preview.section.tsx +108 -0
- package/src/components/indicators/sections/final-indicator-basics.section.tsx +46 -0
- package/src/components/indicators/sections/final-indicator-disaggregation.section.tsx +47 -0
- package/src/components/indicators/sections/final-indicator-picker.section.tsx +60 -0
- package/src/components/indicators/sections/final-indicator-preview.section.tsx +20 -0
- package/src/components/indicators/sections/final-indicator-results-preview.section.tsx +65 -0
- package/src/components/indicators/sections/indicator-basics.section.tsx +40 -0
- package/src/components/indicators/sections/indicator-conditions.section.tsx +279 -0
- package/src/components/indicators/sections/indicator-sql-preview.section.tsx +63 -0
- package/src/components/indicators/sections/indicator-theme.section.tsx +48 -0
- package/src/components/indicators/sql-preview.component.tsx +19 -0
- package/src/components/indicators/tag-input.component.tsx +177 -0
- package/src/components/indicators/types/base-indicator-form.types.ts +17 -0
- package/src/components/indicators/types/base-indicator-theme.constants.ts +8 -0
- package/src/components/indicators/types/composite-indicator.types.ts +27 -0
- package/src/components/indicators/types/condition-ui.types.ts +11 -0
- package/src/components/indicators/types/custom-indicator.types.ts +161 -0
- package/src/components/indicators/types/data-theme-config.types.ts +88 -0
- package/src/components/indicators/types/indicator-types.ts +57 -0
- package/src/components/indicators/types/sql-builders.ts +43 -0
- package/src/components/indicators/utils/composite-indicator-sql.utils.test.ts +143 -0
- package/src/components/indicators/utils/composite-indicator-sql.utils.ts +426 -0
- package/src/components/indicators/utils/concept-preload.utils.ts +21 -0
- package/src/components/indicators/utils/custom-indicator-interpreter.test.ts +493 -0
- package/src/components/indicators/utils/custom-indicator-interpreter.ts +702 -0
- package/src/components/indicators/utils/final-indicator-sql.utils.ts +190 -0
- package/src/components/indicators/utils/indicator-conditions-hydration.utils.ts +254 -0
- package/src/components/indicators/utils/indicator-sql-resolution.utils.ts +204 -0
- package/src/components/indicators/utils/indicator-sql.utils.ts +721 -0
- package/src/components/indicators/utils/population-sql.compiler.test.ts +999 -0
- package/src/components/indicators/utils/population-sql.compiler.ts +858 -0
- package/src/components/landing/landing-action-item.component.tsx +50 -0
- package/src/components/landing/landing-action-list.component.tsx +12 -0
- package/src/components/landing/landing-activity-list.component.tsx +47 -0
- package/src/components/landing/landing-footer-grid.component.tsx +45 -0
- package/src/components/landing/landing-hero.component.tsx +18 -0
- package/src/components/landing/landing-section-card.component.tsx +33 -0
- package/src/components/landing/report-builder-landing-page.component.tsx +186 -0
- package/src/components/landing/report-builder-landing-page.scss +131 -0
- package/src/components/legacy-reports/legacy-report-detail-page.component.tsx +300 -0
- package/src/components/legacy-reports/legacy-report-editor-page.component.tsx +324 -0
- package/src/components/legacy-reports/legacy-report-upload-modal.component.tsx +307 -0
- package/src/components/legacy-reports/legacy-reports-page.component.tsx +206 -0
- package/src/components/legacy-reports/legacy-reports-table.component.tsx +154 -0
- package/src/components/legacy-reports/tabs/legacy-report-datasets-tab.component.tsx +344 -0
- package/src/components/legacy-reports/tabs/legacy-report-dimensions-tab.component.tsx +590 -0
- package/src/components/legacy-reports/tabs/legacy-report-indicators-tab.component.tsx +490 -0
- package/src/components/legacy-reports/tabs/legacy-report-json-preview-tab.component.tsx +272 -0
- package/src/components/legacy-reports/tabs/legacy-report-overview-tab.component.tsx +218 -0
- package/src/components/legacy-reports/tabs/legacy-report-parameters-tab.component.tsx +279 -0
- package/src/components/linelist/definition/indicator-to-sql.utils.ts +281 -0
- package/src/components/linelist/definition/linelist-definition-editor.component.tsx +1676 -0
- package/src/components/linelist/definition/linelist-definition-editor.scss +461 -0
- package/src/components/linelist/definition/linelist-definition.types.ts +108 -0
- package/src/components/linelist/definition/panels/column-properties-panel.component.tsx +328 -0
- package/src/components/linelist/definition/panels/column-selector-panel.component.tsx +1082 -0
- package/src/components/linelist/definition/panels/column-selector-panel.scss +100 -0
- package/src/components/linelist/definition/panels/custom-sql-column-modal.component.tsx +248 -0
- package/src/components/linelist/definition/panels/encounter-diagnosis-column-modal.component.tsx +300 -0
- package/src/components/linelist/definition/panels/encounter-diagnosis-column-modal.scss +21 -0
- package/src/components/linelist/definition/panels/multi-datasource-selector.component.tsx +332 -0
- package/src/components/linelist/definition/panels/multi-datasource-selector.scss +161 -0
- package/src/components/linelist/definition/panels/observation-column-modal.component.tsx +450 -0
- package/src/components/linelist/definition/panels/observation-column-modal.scss +29 -0
- package/src/components/linelist/definition/panels/parameter-panel.component.tsx +659 -0
- package/src/components/linelist/definition/panels/parameter-panel.scss +64 -0
- package/src/components/linelist/definition/panels/population-source-selector.component.tsx +407 -0
- package/src/components/linelist/definition/panels/population-source-selector.scss +187 -0
- package/src/components/linelist/design/panels/column-category-selector.component.tsx +433 -0
- package/src/components/linelist/design/panels/column-category-selector.scss +329 -0
- package/src/components/linelist/design/panels/column-debug-panel.component.tsx +197 -0
- package/src/components/linelist/design/panels/column-debug-panel.scss +141 -0
- package/src/components/linelist/design/panels/data-catalogue.component.tsx +1062 -0
- package/src/components/linelist/design/panels/data-catalogue.scss +263 -0
- package/src/components/linelist/design/panels/display-settings-panel.component.tsx +405 -0
- package/src/components/linelist/design/panels/display-settings-panel.scss +78 -0
- package/src/components/linelist/design/panels/filtermap-editor.component.tsx +434 -0
- package/src/components/linelist/design/panels/filtermap-editor.scss +176 -0
- package/src/components/linelist/design/panels/json-preview.component.tsx +114 -0
- package/src/components/linelist/design/panels/json-preview.scss +85 -0
- package/src/components/linelist/design/panels/query-config-panel.component.tsx +1137 -0
- package/src/components/linelist/design/panels/query-config-panel.scss +342 -0
- package/src/components/linelist/design/panels/sort-config-panel.component.tsx +330 -0
- package/src/components/linelist/design/panels/sort-config-panel.scss +77 -0
- package/src/components/linelist/design/panels/sql-builder-panel.component.tsx +475 -0
- package/src/components/linelist/design/panels/sql-builder-panel.scss +226 -0
- package/src/components/linelist/design/panels/transformation-panel.component.tsx +687 -0
- package/src/components/linelist/design/panels/transformation-panel.scss +81 -0
- package/src/components/linelist/design/panels/visual-filter-panel.component.tsx +595 -0
- package/src/components/linelist/design/panels/visual-filter-panel.scss +76 -0
- package/src/components/linelist/linelist-dashboard.page.component.tsx +756 -0
- package/src/components/linelist/linelist-dashboard.page.scss +240 -0
- package/src/components/linelist/linelist-editor.page.component.tsx +134 -0
- package/src/components/linelist/linelist-editor.page.scss +28 -0
- package/src/components/linelist/linelist-run-report.page.component.tsx +715 -0
- package/src/components/linelist/linelist-run-report.page.scss +136 -0
- package/src/components/linelist/modals/linelist-report-builder-modal.component.tsx +1454 -0
- package/src/components/linelist/modals/linelist-report-builder-modal.scss +164 -0
- package/src/components/linelist/modals/preview-parameter-modal.component.tsx +361 -0
- package/src/components/report/dashboard/reports-table.component.tsx +186 -0
- package/src/components/report/definition/panels/properties-panel.component.tsx +38 -0
- package/src/components/report/definition/panels/report-structure-panel.component.tsx +61 -0
- package/src/components/report/definition/panels/selection-panel.component.tsx +58 -0
- package/src/components/report/definition/report-definition-editor.component.tsx +99 -0
- package/src/components/report/definition/report-definition.types.ts +66 -0
- package/src/components/report/design/panels/IndicatorPropertiesPanel.scss +33 -0
- package/src/components/report/design/panels/IndicatorPropertiesPanel.tsx +389 -0
- package/src/components/report/design/panels/JsonPreviewPanel.scss +32 -0
- package/src/components/report/design/panels/JsonPreviewPanel.tsx +104 -0
- package/src/components/report/design/panels/mapping-preview-panel.component.tsx +82 -0
- package/src/components/report/design/panels/mapping-preview-panel.scss +55 -0
- package/src/components/report/design/panels/template-structure-panel.component.tsx +296 -0
- package/src/components/report/design/panels/template-structure-panel.scss +81 -0
- package/src/components/report/design/report-design-editor.component.tsx +1170 -0
- package/src/components/report/design/report-design.types.ts +48 -0
- package/src/components/report/design/report-design.utils.ts +86 -0
- package/src/components/report/panels/report-detail.component.tsx +71 -0
- package/src/components/report/panels/report-panel-shell.component.tsx +60 -0
- package/src/components/report/report-dashboard.page.component.tsx +554 -0
- package/src/components/report/report-editor.page.component.tsx +590 -0
- package/src/components/report/report.routes.tsx +32 -0
- package/src/components/report-sections/create-report-section-modal.component.tsx +388 -0
- package/src/components/report-sections/hooks/useAgeCategories.ts +25 -0
- package/src/components/report-sections/hooks/useSectionEditorState.ts +449 -0
- package/src/components/report-sections/panels/Dhis2MappingPanel.tsx +157 -0
- package/src/components/report-sections/panels/DisaggregationPanel.tsx +97 -0
- package/src/components/report-sections/panels/IndicatorPickerPanel.tsx +201 -0
- package/src/components/report-sections/panels/SectionBasicsPanel.tsx +27 -0
- package/src/components/report-sections/report-section-preview-modal.component.tsx +548 -0
- package/src/components/report-sections/report-sections-page.component.tsx +325 -0
- package/src/components/report-sections/section-disaggregation.utils.ts +637 -0
- package/src/components/report-sections/section-types.ts +55 -0
- package/src/components/report-sections/section-utils.ts +50 -0
- package/src/components/report-sections/sections-page.scss +79 -0
- package/src/components/run-reports/run-reports-page.component.tsx +8 -0
- package/src/components/security/access-denied-page.component.tsx +69 -0
- package/src/components/security/access-denied-page.scss +39 -0
- package/src/components/security/privilege-route.component.test.tsx +79 -0
- package/src/components/security/privilege-route.component.tsx +39 -0
- package/src/components/shared/compile-setup-modal.component.tsx +182 -0
- package/src/components/shared/concept-selector.component.tsx +245 -0
- package/src/components/shared/concept-selector.scss +121 -0
- package/src/components/shared/header/builder-header-actions.component.tsx +38 -0
- package/src/components/shared/header/header.component.tsx +85 -0
- package/src/components/shared/header/header.scss +150 -0
- package/src/components/shared/header/illustration.component.tsx +34 -0
- package/src/components/shared/indicator-properties-panel/IndicatorPropertiesPanel.scss +33 -0
- package/src/components/shared/indicator-properties-panel/IndicatorPropertiesPanel.tsx +389 -0
- package/src/components/shared/json-preview-panel/JsonPreviewPanel.scss +32 -0
- package/src/components/shared/json-preview-panel/JsonPreviewPanel.tsx +104 -0
- package/src/components/shared/mapping-preview-panel/mapping-preview-panel.component.tsx +82 -0
- package/src/components/shared/mapping-preview-panel/mapping-preview-panel.scss +55 -0
- package/src/components/shared/preview/query-results-preview.component.tsx +204 -0
- package/src/components/shared/template-structure-panel/template-structure-panel.component.tsx +296 -0
- package/src/components/shared/template-structure-panel/template-structure-panel.scss +81 -0
- package/src/config-schema.ts +43 -0
- package/src/constants/privileges.test.ts +53 -0
- package/src/constants/privileges.ts +118 -0
- package/src/features/dashboards/DashboardRendererPage.tsx +174 -0
- package/src/features/dashboards/DashboardsPage.tsx +164 -0
- package/src/features/dashboards/EtlDashboardPage.tsx +32 -0
- package/src/features/dashboards/components/DashboardEmptyState.tsx +41 -0
- package/src/features/dashboards/components/DashboardGrid.tsx +48 -0
- package/src/features/dashboards/components/DashboardHeader.tsx +58 -0
- package/src/features/dashboards/components/DashboardSection.tsx +46 -0
- package/src/features/dashboards/components/ReportLaunchCard.tsx +51 -0
- package/src/features/dashboards/components/WidgetSlotDispatcher.tsx +61 -0
- package/src/features/dashboards/components/WidgetUnavailableCard.tsx +36 -0
- package/src/features/dashboards/dashboards.scss +196 -0
- package/src/features/dashboards/hooks/useDashboard.ts +61 -0
- package/src/features/dashboards/hooks/useDashboardWidgets.ts +74 -0
- package/src/features/dashboards/hooks/useDashboards.ts +56 -0
- package/src/features/dashboards/hooks/useMonitorDataMap.ts +84 -0
- package/src/features/dashboards/index.ts +11 -0
- package/src/features/dashboards/utils/dashboard-config.util.test.ts +255 -0
- package/src/features/dashboards/utils/dashboard-config.util.ts +382 -0
- package/src/features/report-visualizer/ReportVisualizerPage.tsx +448 -0
- package/src/features/report-visualizer/components/Export/ExportMenu.tsx +141 -0
- package/src/features/report-visualizer/components/Export/index.ts +2 -0
- package/src/features/report-visualizer/components/ReportExplorer/QuickFilters.tsx +152 -0
- package/src/features/report-visualizer/components/ReportExplorer/ReportCategoryFilter.tsx +54 -0
- package/src/features/report-visualizer/components/ReportExplorer/ReportExplorerPanel.tsx +201 -0
- package/src/features/report-visualizer/components/ReportExplorer/ReportExplorerRail.tsx +202 -0
- package/src/features/report-visualizer/components/ReportExplorer/ReportList.tsx +287 -0
- package/src/features/report-visualizer/components/ReportExplorer/ReportSearch.tsx +151 -0
- package/src/features/report-visualizer/components/ReportExplorer/ReportTagFilter.tsx +100 -0
- package/src/features/report-visualizer/components/ReportExplorer/RunReportButton.tsx +42 -0
- package/src/features/report-visualizer/components/ReportExplorer/RuntimeParameterForm.tsx +259 -0
- package/src/features/report-visualizer/components/ReportExplorer/index.ts +8 -0
- package/src/features/report-visualizer/components/ReportWorkspace/ReportLayer.tsx +33 -0
- package/src/features/report-visualizer/components/ReportWorkspace/ReportSummary.tsx +88 -0
- package/src/features/report-visualizer/components/ReportWorkspace/ReportViewTabs.tsx +72 -0
- package/src/features/report-visualizer/components/ReportWorkspace/ReportWorkspaceHeader.tsx +92 -0
- package/src/features/report-visualizer/components/ReportWorkspace/ReportWorkspacePanel.tsx +242 -0
- package/src/features/report-visualizer/components/ReportWorkspace/index.ts +5 -0
- package/src/features/report-visualizer/components/TableView/ColumnVisibilityMenu.tsx +206 -0
- package/src/features/report-visualizer/components/TableView/ReportTable.tsx +253 -0
- package/src/features/report-visualizer/components/TableView/index.ts +2 -0
- package/src/features/report-visualizer/hooks/index.ts +4 -0
- package/src/features/report-visualizer/hooks/useExplorerPreferences.ts +103 -0
- package/src/features/report-visualizer/hooks/useReportCapabilities.ts +112 -0
- package/src/features/report-visualizer/hooks/useReportParameters.ts +152 -0
- package/src/features/report-visualizer/hooks/useReports.ts +135 -0
- package/src/features/report-visualizer/index.ts +6 -0
- package/src/features/report-visualizer/report-visualizer.scss +593 -0
- package/src/features/report-visualizer/types/index.ts +1 -0
- package/src/features/report-visualizer/types/report-visualizer.types.ts +104 -0
- package/src/features/report-visualizer/utils/status-colors.ts +117 -0
- package/src/globals.d.ts +14 -0
- package/src/hooks/address-template.ts +86 -0
- package/src/hooks/concept.ts +112 -0
- package/src/hooks/indicator/index.ts +1 -0
- package/src/hooks/indicator/useIndicatorsByTheme.ts +41 -0
- package/src/hooks/openmrs-reference-data.ts +432 -0
- package/src/hooks/theme/index.ts +4 -0
- package/src/hooks/theme/useDataTheme.ts +61 -0
- package/src/hooks/theme/useDataThemes.ts +28 -0
- package/src/hooks/theme/useMambaTableMeta.ts +34 -0
- package/src/hooks/theme/useMambaTables.ts +95 -0
- package/src/hooks/use-report-builder-privileges.test.tsx +98 -0
- package/src/hooks/use-report-builder-privileges.ts +58 -0
- package/src/index.ts +29 -0
- package/src/resources/address-template/address-template.api.ts +180 -0
- package/src/resources/agegroup/agegroups.api.ts +58 -0
- package/src/resources/concept/concept.api.ts +116 -0
- package/src/resources/concepts/concept-mapper.ts +21 -0
- package/src/resources/concepts/concept-types.ts +31 -0
- package/src/resources/concepts/concepts.resource.ts +44 -0
- package/src/resources/concepts/useConceptSearch.ts +41 -0
- package/src/resources/dashboard/dashboard.api.ts +107 -0
- package/src/resources/etl-monitor/etl-monitor-test.api.ts +409 -0
- package/src/resources/etl-monitor/etl-monitor.api.ts +381 -0
- package/src/resources/etl-source/etl-source.api.ts +65 -0
- package/src/resources/etl-task/etl-task.api.ts +96 -0
- package/src/resources/etl-task/etl-task.types.ts +31 -0
- package/src/resources/indicator/indicators.api.ts +125 -0
- package/src/resources/legacy-reports/legacy-reports.api.ts +144 -0
- package/src/resources/linelist/linelist-reports.api.ts +616 -0
- package/src/resources/location/location.api.ts +135 -0
- package/src/resources/openmrs-api.ts +40 -0
- package/src/resources/patient-identifier-type/patient-identifier-type.api.ts +65 -0
- package/src/resources/person-attribute-type/person-attribute-type.api.ts +71 -0
- package/src/resources/preview/sql-preview.api.ts +54 -0
- package/src/resources/report/reports.api.ts +140 -0
- package/src/resources/report-category/report-category.api.ts +41 -0
- package/src/resources/report-import-export/import-export-api.ts +164 -0
- package/src/resources/report-library/report-library.api.ts +63 -0
- package/src/resources/report-section/report-sections.api.ts +73 -0
- package/src/resources/report-section/section-preview.api.ts +25 -0
- package/src/resources/theme/data-theme.api.ts +88 -0
- package/src/resources/theme/etl-schema.api.ts +101 -0
- package/src/resources/theme/etl-table-meta.api.ts +45 -0
- package/src/root.component.tsx +20 -0
- package/src/routes/report-builder.routes.tsx +160 -0
- package/src/routes/report-builder.scss +56 -0
- package/src/routes.json +28 -0
- package/src/sample-template.ts +121 -0
- package/src/template-utils.ts +110 -0
- package/src/types/carbon-react-inert.d.ts +13 -0
- package/src/types/condition-operators.ts +63 -0
- package/src/types/dashboard/dashboard.types.ts +137 -0
- package/src/types/dashboard/index.ts +4 -0
- package/src/types/etl/etl-types.ts +238 -0
- package/src/types/etl-monitor/etl-monitor-builder.types.ts +555 -0
- package/src/types/etl-monitor/etl-monitor-v2.types.ts +397 -0
- package/src/types/etl-monitor/etl-monitor.types.ts +188 -0
- package/src/types/etl-monitor/index.ts +6 -0
- package/src/types/linelist/compile-config.ts +455 -0
- package/src/types/linelist-types.ts +2059 -0
- package/src/types/theme/data-theme.types.ts +88 -0
- package/src/utils/api-error.utils.test.ts +151 -0
- package/src/utils/api-error.utils.ts +131 -0
- package/src/utils/config-enhancer.ts +62 -0
- package/src/utils/etl-browser/etl-browser.util.test.ts +145 -0
- package/src/utils/etl-browser/export-csv.util.ts +43 -0
- package/src/utils/etl-browser/group-tables.util.ts +84 -0
- package/src/utils/etl-browser/index.ts +4 -0
- package/src/utils/etl-browser/summarize-columns.util.ts +27 -0
- package/src/utils/etl-browser/table-sql.util.ts +20 -0
- package/src/utils/etl-monitor/config-v2-adapter.util.ts +460 -0
- package/src/utils/etl-monitor/endpoint-config-normalizer.util.ts +383 -0
- package/src/utils/etl-monitor/index.ts +10 -0
- package/src/utils/etl-monitor/jsonpath-resolver.util.ts +118 -0
- package/src/utils/etl-monitor/normalization-examples.ts +0 -0
- package/src/utils/etl-monitor/schema-inspector.util.ts +76 -0
- package/src/utils/etl-monitor/semantic-type-detector.util.ts +76 -0
- package/src/utils/etl-monitor/status-mapper.util.ts +105 -0
- package/src/utils/etl-monitor/value-formatters.util.ts +167 -0
- package/src/utils/etl-task/etl-task-card.util.test.ts +239 -0
- package/src/utils/etl-task/etl-task-card.util.ts +276 -0
- package/src/utils/etl-task/index.ts +1 -0
- package/src/utils/html-entities.utils.ts +34 -0
- package/src/utils/parameter-resolution.ts +218 -0
- package/src/views/etl-monitor-builder/add-monitor-modal.component.scss +0 -0
- package/src/views/etl-monitor-builder/add-monitor-modal.component.tsx +210 -0
- package/src/views/etl-monitor-builder/etl-monitor-builder.component.scss +157 -0
- package/src/views/etl-monitor-builder/etl-monitor-builder.component.tsx +252 -0
- package/src/views/etl-monitor-builder/etl-monitor-builder.store.ts +126 -0
- package/tools/scss-stub.js +12 -0
- package/tools/setup-tests.ts +1 -0
- package/translations/en.json +65 -0
- package/tsconfig.json +43 -0
- package/webpack.config.js +30 -0
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ETL Task card metadata utilities
|
|
3
|
+
*
|
|
4
|
+
* Pure helpers used by the ETL Tasks admin page to derive card metadata
|
|
5
|
+
* (last run, next run, duration) from the OpenMRS task definition and the
|
|
6
|
+
* configured task -> monitor binding.
|
|
7
|
+
*
|
|
8
|
+
* No framework imports — safe to unit test in isolation.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { formatDuration } from '../etl-monitor/value-formatters.util';
|
|
12
|
+
import type { ETLMonitorDto } from '../../types/etl-monitor';
|
|
13
|
+
|
|
14
|
+
const DAY_MS = 86400000;
|
|
15
|
+
const MONTHS = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Parse a date leniently. OpenMRS serializes dates with colon-less UTC
|
|
19
|
+
* offsets (e.g. `+0300`) which Safari's `Date` parser rejects; normalize
|
|
20
|
+
* them to `+03:00` before parsing. Returns null for unusable values.
|
|
21
|
+
*/
|
|
22
|
+
export function parseLenientDate(value?: string | number | Date | null): Date | null {
|
|
23
|
+
if (value === null || value === undefined || value === '') return null;
|
|
24
|
+
if (value instanceof Date) return isNaN(value.getTime()) ? null : value;
|
|
25
|
+
if (typeof value === 'number') return isNaN(value) ? null : new Date(value);
|
|
26
|
+
if (typeof value !== 'string') return null;
|
|
27
|
+
|
|
28
|
+
const normalized = value.trim().replace(/([+-]\d{2})(\d{2})$/, '$1:$2');
|
|
29
|
+
const date = new Date(normalized);
|
|
30
|
+
return isNaN(date.getTime()) ? null : date;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Local calendar-day difference between a date and now: 0 = today,
|
|
35
|
+
* -1 = yesterday, etc.
|
|
36
|
+
*/
|
|
37
|
+
function calendarDayOffset(date: Date, now: Date): number {
|
|
38
|
+
const startOfDay = (d: Date) => new Date(d.getFullYear(), d.getMonth(), d.getDate()).getTime();
|
|
39
|
+
return Math.round((startOfDay(date) - startOfDay(now)) / DAY_MS);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** `Today, 09:12` / `Yesterday, 21:04` / `12 Aug, 09:12` */
|
|
43
|
+
function formatDayTimeLabel(date: Date, now: Date = new Date()): string {
|
|
44
|
+
const hh = String(date.getHours()).padStart(2, '0');
|
|
45
|
+
const mm = String(date.getMinutes()).padStart(2, '0');
|
|
46
|
+
const time = `${hh}:${mm}`;
|
|
47
|
+
const offset = calendarDayOffset(date, now);
|
|
48
|
+
if (offset === 0) return `Today, ${time}`;
|
|
49
|
+
if (offset === -1) return `Yesterday, ${time}`;
|
|
50
|
+
return `${date.getDate()} ${MONTHS[date.getMonth()]}, ${time}`;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Format a last-run timestamp for the card metadata row.
|
|
55
|
+
*/
|
|
56
|
+
export function formatLastRun(value?: string | number | Date | null): string {
|
|
57
|
+
const date = parseLenientDate(value);
|
|
58
|
+
if (!date) return value === null || value === undefined || value === '' ? '—' : String(value);
|
|
59
|
+
return formatDayTimeLabel(date);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Compute the next run time from the last execution time plus the repeat
|
|
64
|
+
* interval (seconds). Returns null when the task is not repeating or the
|
|
65
|
+
* inputs are unusable. Never clamps to the current time.
|
|
66
|
+
*/
|
|
67
|
+
export function computeNextRun(
|
|
68
|
+
lastExecutionTime?: string | number | Date | null,
|
|
69
|
+
repeatIntervalSeconds?: number | null,
|
|
70
|
+
): Date | null {
|
|
71
|
+
const last = parseLenientDate(lastExecutionTime);
|
|
72
|
+
if (!last) return null;
|
|
73
|
+
if (typeof repeatIntervalSeconds !== 'number' || !isFinite(repeatIntervalSeconds) || repeatIntervalSeconds <= 0) {
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
return new Date(last.getTime() + repeatIntervalSeconds * 1000);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Format a computed next-run date for the card metadata row.
|
|
81
|
+
*/
|
|
82
|
+
export function formatNextRun(nextRun: Date | null): string {
|
|
83
|
+
return nextRun ? formatDayTimeLabel(nextRun) : 'Not scheduled';
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Format the repeat interval (seconds) for the details view, e.g.
|
|
88
|
+
* `Every 30 min`, `Every 6 h`, `Every 2 d`.
|
|
89
|
+
*/
|
|
90
|
+
export function formatRepeatInterval(seconds?: number | null): string {
|
|
91
|
+
if (typeof seconds !== 'number' || !isFinite(seconds) || seconds <= 0) return '—';
|
|
92
|
+
if (seconds < 60) return `Every ${seconds} s`;
|
|
93
|
+
const trim = (n: number) => String(Math.round(n * 10) / 10);
|
|
94
|
+
if (seconds < 3600) return `Every ${trim(seconds / 60)} min`;
|
|
95
|
+
if (seconds < DAY_MS / 1000) return `Every ${trim(seconds / 3600)} h`;
|
|
96
|
+
return `Every ${trim(seconds / 86400)} d`;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Resolve the ETL monitor referenced by a config value. The value may be a
|
|
101
|
+
* monitor uuid (matched exactly, then case-insensitively) or a monitor code
|
|
102
|
+
* (case-insensitive).
|
|
103
|
+
*/
|
|
104
|
+
export function findMonitorByRef(
|
|
105
|
+
monitors: ETLMonitorDto[] | undefined | null,
|
|
106
|
+
ref?: string | null,
|
|
107
|
+
): ETLMonitorDto | undefined {
|
|
108
|
+
if (!monitors || monitors.length === 0) return undefined;
|
|
109
|
+
if (typeof ref !== 'string' || !ref.trim()) return undefined;
|
|
110
|
+
const value = ref.trim();
|
|
111
|
+
|
|
112
|
+
return (
|
|
113
|
+
monitors.find((m) => m?.uuid === value) ??
|
|
114
|
+
monitors.find((m) => m?.uuid?.toLowerCase() === value.toLowerCase()) ??
|
|
115
|
+
monitors.find((m) => m?.code?.toLowerCase() === value.toLowerCase())
|
|
116
|
+
);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export interface ResolvedProgressMonitor {
|
|
120
|
+
/** The raw config value (trimmed) this entry came from */
|
|
121
|
+
ref: string;
|
|
122
|
+
/** The resolved monitor, or undefined when the ref matched nothing */
|
|
123
|
+
monitor?: ETLMonitorDto;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Resolve the `etlTasks.progressMonitors` config list against the available
|
|
128
|
+
* monitors, in config order. Entries that resolve to the same monitor are
|
|
129
|
+
* deduplicated; unresolved entries are kept so typos stay visible.
|
|
130
|
+
*/
|
|
131
|
+
export function resolveProgressMonitors(
|
|
132
|
+
monitors: ETLMonitorDto[] | undefined | null,
|
|
133
|
+
refs?: string[] | null,
|
|
134
|
+
): ResolvedProgressMonitor[] {
|
|
135
|
+
if (!refs || refs.length === 0) return [];
|
|
136
|
+
|
|
137
|
+
const seenUuids = new Set<string>();
|
|
138
|
+
const resolved: ResolvedProgressMonitor[] = [];
|
|
139
|
+
|
|
140
|
+
for (const ref of refs) {
|
|
141
|
+
if (typeof ref !== 'string' || !ref.trim()) continue;
|
|
142
|
+
const monitor = findMonitorByRef(monitors, ref);
|
|
143
|
+
if (monitor) {
|
|
144
|
+
if (seenUuids.has(monitor.uuid)) continue;
|
|
145
|
+
seenUuids.add(monitor.uuid);
|
|
146
|
+
}
|
|
147
|
+
resolved.push({ ref: ref.trim(), monitor });
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
return resolved;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
const DURATION_KEY_PATTERN = /duration|elapsed|runtime/i;
|
|
154
|
+
const SECONDS_KEY_PATTERN = /sec/i;
|
|
155
|
+
const MILLISECONDS_KEY_PATTERN = /milli|_ms\b|ms$/i;
|
|
156
|
+
const MAX_SEARCH_DEPTH = 3;
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Normalize a numeric duration to milliseconds using its key as a hint:
|
|
160
|
+
* `*ms*`/`*milli*` keys stay as-is, `*seconds*` keys scale by 1000, and
|
|
161
|
+
* bare values under 1000 are assumed to be seconds (sub-second ETL
|
|
162
|
+
* durations are rare, while bare seconds are common).
|
|
163
|
+
*/
|
|
164
|
+
function normalizeDurationMs(value: number, key: string): number {
|
|
165
|
+
if (MILLISECONDS_KEY_PATTERN.test(key)) return value;
|
|
166
|
+
if (SECONDS_KEY_PATTERN.test(key)) return value * 1000;
|
|
167
|
+
return value < 1000 ? value * 1000 : value;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
function durationFromEntry(key: string, value: unknown): string | null {
|
|
171
|
+
if (value === null || value === undefined || value === '') return null;
|
|
172
|
+
if (typeof value === 'number' && isFinite(value)) {
|
|
173
|
+
return formatDuration(normalizeDurationMs(value, key));
|
|
174
|
+
}
|
|
175
|
+
if (typeof value === 'string') {
|
|
176
|
+
const trimmed = value.trim();
|
|
177
|
+
if (!trimmed) return null;
|
|
178
|
+
const numeric = Number(trimmed);
|
|
179
|
+
if (!isNaN(numeric) && trimmed !== '') {
|
|
180
|
+
return formatDuration(normalizeDurationMs(numeric, key));
|
|
181
|
+
}
|
|
182
|
+
return trimmed;
|
|
183
|
+
}
|
|
184
|
+
return null;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Search a monitor's raw response for a duration-like value
|
|
189
|
+
* (keys matching /duration|elapsed|runtime/i, bounded depth, cycle-safe).
|
|
190
|
+
* Returns the formatted duration string, or null when nothing usable is found.
|
|
191
|
+
*/
|
|
192
|
+
export function extractDurationFromMonitorData(rawResponse: unknown): string | null {
|
|
193
|
+
const visited = new WeakSet<object>();
|
|
194
|
+
|
|
195
|
+
const search = (node: unknown, key: string, depth: number): string | null => {
|
|
196
|
+
if (node === null || node === undefined || depth > MAX_SEARCH_DEPTH) return null;
|
|
197
|
+
|
|
198
|
+
if (DURATION_KEY_PATTERN.test(key)) {
|
|
199
|
+
const result = durationFromEntry(key, node);
|
|
200
|
+
if (result !== null) return result;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
if (typeof node !== 'object') return null;
|
|
204
|
+
if (visited.has(node as object)) return null;
|
|
205
|
+
visited.add(node as object);
|
|
206
|
+
|
|
207
|
+
const entries = Array.isArray(node)
|
|
208
|
+
? node.map((item, index) => [String(index), item] as const)
|
|
209
|
+
: Object.entries(node as Record<string, unknown>);
|
|
210
|
+
|
|
211
|
+
for (const [childKey, childValue] of entries) {
|
|
212
|
+
const result = search(childValue, childKey, depth + 1);
|
|
213
|
+
if (result !== null) return result;
|
|
214
|
+
}
|
|
215
|
+
return null;
|
|
216
|
+
};
|
|
217
|
+
|
|
218
|
+
return search(rawResponse, '', 0);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
const STATUS_KEY_PATTERN = /^(status|state)$/i;
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* Count entries in a monitor's raw response whose status matches a pattern.
|
|
225
|
+
* Two shapes are recognized (bounded depth, cycle-safe):
|
|
226
|
+
* - arrays of objects with a `status`/`state` string field, e.g.
|
|
227
|
+
* `{ jobs: [{ status: 'RUNNING' }, { status: 'COMPLETED' }] }`
|
|
228
|
+
* - numeric count fields whose key matches the pattern, e.g. `{ runningCount: 3 }`
|
|
229
|
+
*
|
|
230
|
+
* Returns undefined when nothing matches (no data / unknown), so callers can
|
|
231
|
+
* distinguish "confirmed zero" from "no signal".
|
|
232
|
+
*/
|
|
233
|
+
export function countStatusEntries(rawResponse: unknown, valuePattern: RegExp): number | undefined {
|
|
234
|
+
if (rawResponse === null || rawResponse === undefined || typeof rawResponse !== 'object') return undefined;
|
|
235
|
+
|
|
236
|
+
const visited = new WeakSet<object>();
|
|
237
|
+
let found: number | undefined = undefined;
|
|
238
|
+
|
|
239
|
+
const walk = (node: unknown, depth: number): void => {
|
|
240
|
+
if (node === null || node === undefined || depth > MAX_SEARCH_DEPTH) return;
|
|
241
|
+
if (typeof node !== 'object' || visited.has(node as object)) return;
|
|
242
|
+
visited.add(node as object);
|
|
243
|
+
|
|
244
|
+
const entries = Array.isArray(node)
|
|
245
|
+
? node.map((item) => ['', item] as const)
|
|
246
|
+
: Object.entries(node as Record<string, unknown>);
|
|
247
|
+
|
|
248
|
+
for (const [key, value] of entries) {
|
|
249
|
+
if (typeof value === 'number' && isFinite(value) && valuePattern.test(key)) {
|
|
250
|
+
found = (found ?? 0) + value;
|
|
251
|
+
} else if (STATUS_KEY_PATTERN.test(key) && typeof value === 'string' && valuePattern.test(value)) {
|
|
252
|
+
found = (found ?? 0) + 1;
|
|
253
|
+
} else if (typeof value === 'object' && value !== null) {
|
|
254
|
+
walk(value, depth + 1);
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
};
|
|
258
|
+
|
|
259
|
+
walk(rawResponse, 0);
|
|
260
|
+
return found;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* Sum `countStatusEntries` across several monitor raw responses.
|
|
265
|
+
* Returns undefined only when no response yields a signal.
|
|
266
|
+
*/
|
|
267
|
+
export function sumStatusCounts(rawResponses: Array<unknown | null | undefined>, valuePattern: RegExp): number | undefined {
|
|
268
|
+
let total: number | undefined = undefined;
|
|
269
|
+
for (const raw of rawResponses) {
|
|
270
|
+
const count = countStatusEntries(raw, valuePattern);
|
|
271
|
+
if (count !== undefined) {
|
|
272
|
+
total = (total ?? 0) + count;
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
return total;
|
|
276
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './etl-task-card.util';
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Safely decodes common HTML entities that OpenMRS may return
|
|
3
|
+
* after sanitisation (e.g. < > & " ')
|
|
4
|
+
*
|
|
5
|
+
* This is intentionally small and dependency-free.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
export function decodeHtmlEntities(input?: string | null): string {
|
|
9
|
+
if (!input || typeof input !== 'string') return input ?? '';
|
|
10
|
+
|
|
11
|
+
// Fast exit if nothing looks encoded
|
|
12
|
+
if (!/[&]/.test(input)) return input;
|
|
13
|
+
|
|
14
|
+
// IMPORTANT: Decode & FIRST to handle double-encoded entities like &lt; -> <
|
|
15
|
+
|
|
16
|
+
let decoded = input;
|
|
17
|
+
let prevLength: number;
|
|
18
|
+
|
|
19
|
+
// Apply decoding repeatedly until no more changes (handles double/multiple encoding)
|
|
20
|
+
do {
|
|
21
|
+
prevLength = decoded.length;
|
|
22
|
+
decoded = decoded
|
|
23
|
+
.replace(/&/g, '&') // Must be first to handle double-encoding
|
|
24
|
+
.replace(/</g, '<')
|
|
25
|
+
.replace(/>/g, '>')
|
|
26
|
+
.replace(/"/g, '"')
|
|
27
|
+
.replace(/'/g, "'")
|
|
28
|
+
.replace(///g, '/')
|
|
29
|
+
.replace(/`/g, '`')
|
|
30
|
+
.replace(/=/g, '=');
|
|
31
|
+
} while (decoded.length !== prevLength && /[&]/.test(decoded));
|
|
32
|
+
|
|
33
|
+
return decoded;
|
|
34
|
+
}
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parameter Resolution Utilities
|
|
3
|
+
*
|
|
4
|
+
* Utilities for resolving relative periods to actual dates.
|
|
5
|
+
* Used for date range parameters in reports.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import dayjs from 'dayjs';
|
|
9
|
+
|
|
10
|
+
export type RelativePeriod =
|
|
11
|
+
| 'TODAY'
|
|
12
|
+
| 'YESTERDAY'
|
|
13
|
+
| 'THIS_WEEK'
|
|
14
|
+
| 'LAST_WEEK'
|
|
15
|
+
| 'THIS_MONTH'
|
|
16
|
+
| 'LAST_MONTH'
|
|
17
|
+
| 'THIS_QUARTER'
|
|
18
|
+
| 'LAST_QUARTER'
|
|
19
|
+
| 'THIS_YEAR'
|
|
20
|
+
| 'LAST_YEAR'
|
|
21
|
+
| 'LAST_7_DAYS'
|
|
22
|
+
| 'LAST_30_DAYS'
|
|
23
|
+
| 'LAST_90_DAYS';
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Relative period options for UI dropdown
|
|
27
|
+
*/
|
|
28
|
+
export const RELATIVE_PERIOD_OPTIONS: Array<{ value: RelativePeriod; label: string }> = [
|
|
29
|
+
{ value: 'TODAY', label: 'Today' },
|
|
30
|
+
{ value: 'YESTERDAY', label: 'Yesterday' },
|
|
31
|
+
{ value: 'THIS_WEEK', label: 'This Week' },
|
|
32
|
+
{ value: 'LAST_WEEK', label: 'Last Week' },
|
|
33
|
+
{ value: 'THIS_MONTH', label: 'This Month' },
|
|
34
|
+
{ value: 'LAST_MONTH', label: 'Last Month' },
|
|
35
|
+
{ value: 'THIS_QUARTER', label: 'This Quarter' },
|
|
36
|
+
{ value: 'LAST_QUARTER', label: 'Last Quarter' },
|
|
37
|
+
{ value: 'THIS_YEAR', label: 'This Year' },
|
|
38
|
+
{ value: 'LAST_YEAR', label: 'Last Year' },
|
|
39
|
+
{ value: 'LAST_7_DAYS', label: 'Last 7 Days' },
|
|
40
|
+
{ value: 'LAST_30_DAYS', label: 'Last 30 Days' },
|
|
41
|
+
{ value: 'LAST_90_DAYS', label: 'Last 90 Days' },
|
|
42
|
+
];
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Resolves a relative period to a date range
|
|
46
|
+
* @param period - The relative period identifier
|
|
47
|
+
* @returns Object with start and end dates
|
|
48
|
+
*/
|
|
49
|
+
export function resolveRelativePeriod(period: RelativePeriod): { start: Date; end: Date } {
|
|
50
|
+
const now = new Date();
|
|
51
|
+
|
|
52
|
+
switch (period) {
|
|
53
|
+
case 'TODAY':
|
|
54
|
+
return { start: now, end: now };
|
|
55
|
+
|
|
56
|
+
case 'YESTERDAY': {
|
|
57
|
+
const yesterday = new Date(now);
|
|
58
|
+
yesterday.setDate(now.getDate() - 1);
|
|
59
|
+
return { start: yesterday, end: yesterday };
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
case 'THIS_WEEK': {
|
|
63
|
+
const weekStart = new Date(now);
|
|
64
|
+
weekStart.setDate(now.getDate() - now.getDay());
|
|
65
|
+
const weekEnd = new Date(now);
|
|
66
|
+
weekEnd.setDate(now.getDate() + (6 - now.getDay()));
|
|
67
|
+
return { start: weekStart, end: weekEnd };
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
case 'LAST_WEEK': {
|
|
71
|
+
const lastWeekStart = new Date(now);
|
|
72
|
+
lastWeekStart.setDate(now.getDate() - now.getDay() - 7);
|
|
73
|
+
const lastWeekEnd = new Date(now);
|
|
74
|
+
lastWeekEnd.setDate(now.getDate() - now.getDay() - 1);
|
|
75
|
+
return { start: lastWeekStart, end: lastWeekEnd };
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
case 'THIS_MONTH': {
|
|
79
|
+
return {
|
|
80
|
+
start: new Date(now.getFullYear(), now.getMonth(), 1),
|
|
81
|
+
end: new Date(now.getFullYear(), now.getMonth() + 1, 0),
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
case 'LAST_MONTH': {
|
|
86
|
+
return {
|
|
87
|
+
start: new Date(now.getFullYear(), now.getMonth() - 1, 1),
|
|
88
|
+
end: new Date(now.getFullYear(), now.getMonth(), 0),
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
case 'THIS_QUARTER': {
|
|
93
|
+
const quarter = Math.floor(now.getMonth() / 3);
|
|
94
|
+
return {
|
|
95
|
+
start: new Date(now.getFullYear(), quarter * 3, 1),
|
|
96
|
+
end: new Date(now.getFullYear(), (quarter + 1) * 3, 0),
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
case 'LAST_QUARTER': {
|
|
101
|
+
const currentQuarter = Math.floor(now.getMonth() / 3);
|
|
102
|
+
if (currentQuarter === 0) {
|
|
103
|
+
return {
|
|
104
|
+
start: new Date(now.getFullYear() - 1, 9, 1),
|
|
105
|
+
end: new Date(now.getFullYear() - 1, 12, 0),
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
return {
|
|
109
|
+
start: new Date(now.getFullYear(), (currentQuarter - 1) * 3, 1),
|
|
110
|
+
end: new Date(now.getFullYear(), currentQuarter * 3, 0),
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
case 'THIS_YEAR': {
|
|
115
|
+
return {
|
|
116
|
+
start: new Date(now.getFullYear(), 0, 1),
|
|
117
|
+
end: new Date(now.getFullYear(), 12, 0),
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
case 'LAST_YEAR': {
|
|
122
|
+
return {
|
|
123
|
+
start: new Date(now.getFullYear() - 1, 0, 1),
|
|
124
|
+
end: new Date(now.getFullYear() - 1, 12, 0),
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
case 'LAST_7_DAYS': {
|
|
129
|
+
const sevenDaysAgo = new Date(now);
|
|
130
|
+
sevenDaysAgo.setDate(now.getDate() - 7);
|
|
131
|
+
return { start: sevenDaysAgo, end: now };
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
case 'LAST_30_DAYS': {
|
|
135
|
+
const thirtyDaysAgo = new Date(now);
|
|
136
|
+
thirtyDaysAgo.setDate(now.getDate() - 30);
|
|
137
|
+
return { start: thirtyDaysAgo, end: now };
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
case 'LAST_90_DAYS': {
|
|
141
|
+
const ninetyDaysAgo = new Date(now);
|
|
142
|
+
ninetyDaysAgo.setDate(now.getDate() - 90);
|
|
143
|
+
return { start: ninetyDaysAgo, end: now };
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
default:
|
|
147
|
+
throw new Error(`Unknown relative period: ${period}`);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Formats a date as YYYY-MM-DD
|
|
153
|
+
*/
|
|
154
|
+
export function formatDate(date: Date): string {
|
|
155
|
+
return dayjs(date).format('YYYY-MM-DD');
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Formats a date as DD/MM/YYYY for display
|
|
160
|
+
*/
|
|
161
|
+
export function formatDisplayDate(date: Date): string {
|
|
162
|
+
return dayjs(date).format('DD/MM/YYYY');
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Parses a date string in YYYY-MM-DD format to Date object
|
|
167
|
+
*/
|
|
168
|
+
export function parseDate(dateString: string): Date | null {
|
|
169
|
+
const parsed = dayjs(dateString, 'YYYY-MM-DD', true);
|
|
170
|
+
return parsed.isValid() ? parsed.toDate() : null;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Validates if a string is a valid date in YYYY-MM-DD format
|
|
175
|
+
*/
|
|
176
|
+
export function isValidDate(dateString: string): boolean {
|
|
177
|
+
return dayjs(dateString, 'YYYY-MM-DD', true).isValid();
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Gets a display label for a relative period
|
|
182
|
+
*/
|
|
183
|
+
export function getRelativePeriodLabel(period: RelativePeriod): string {
|
|
184
|
+
const option = RELATIVE_PERIOD_OPTIONS.find(opt => opt.value === period);
|
|
185
|
+
return option?.label || period;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Resolves a parameter value to an actual date string
|
|
190
|
+
* Handles both FIXED and RELATIVE modes
|
|
191
|
+
*/
|
|
192
|
+
export function resolveParameterValue(
|
|
193
|
+
value: { mode: 'FIXED' | 'RELATIVE'; fixedDate?: string; relativePeriod?: RelativePeriod }
|
|
194
|
+
): string {
|
|
195
|
+
if (!value) {
|
|
196
|
+
return formatDate(new Date());
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
if (value.mode === 'FIXED' && value.fixedDate) {
|
|
200
|
+
return value.fixedDate;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
if (value.mode === 'RELATIVE' && value.relativePeriod) {
|
|
204
|
+
const { start } = resolveRelativePeriod(value.relativePeriod);
|
|
205
|
+
return formatDate(start);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
return formatDate(new Date());
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* Type guard to check if a value is a date parameter value
|
|
213
|
+
*/
|
|
214
|
+
export function isDateParameterValue(
|
|
215
|
+
value: any
|
|
216
|
+
): value is { mode: 'FIXED' | 'RELATIVE'; fixedDate?: string; relativePeriod?: RelativePeriod } {
|
|
217
|
+
return value && typeof value === 'object' && 'mode' in value;
|
|
218
|
+
}
|
|
File without changes
|