@things-factory/kpi 9.0.30 → 9.0.32
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/README.md +1 -2
- package/client/charts/kpi-boxplot-chart.ts +182 -42
- package/client/charts/kpi-mini-trend-chart.ts +125 -0
- package/client/charts/kpi-radar-chart.ts +9 -9
- package/client/charts/kpi-trend-chart.ts +163 -0
- package/client/google-map/common-google-map.ts +45 -7
- package/client/google-map/google-map-loader.ts +1 -1
- package/client/pages/kpi/kpi-list-page.ts +196 -32
- package/client/pages/kpi/kpi-overview.ts +9 -11
- package/client/pages/kpi/kpi-tree-page.ts +409 -0
- package/client/pages/kpi/kpi-view.ts +187 -0
- package/client/pages/kpi-dashboard/cards/kpi-level1-card.ts +1 -1
- package/client/pages/kpi-dashboard/cards/kpi-level2-comparison.ts +1 -1
- package/client/pages/kpi-dashboard/cards/kpi-level3-comparison.ts +1 -1
- package/client/pages/kpi-dashboard/components/kpi-chart-toggle.ts +1 -2
- package/client/pages/kpi-dashboard/components/kpi-left-panel.ts +437 -0
- package/client/pages/kpi-dashboard/components/kpi-map-panel.ts +243 -30
- package/client/pages/kpi-dashboard/components/kpi-region-popup.ts +356 -0
- package/client/pages/kpi-dashboard/kpi-dashboard-map.ts +50 -592
- package/client/pages/kpi-dashboard/kpi-dashboard.ts +28 -30
- package/client/pages/kpi-history/kpi-history-list-page.ts +11 -11
- package/client/pages/kpi-metric/kpi-metric-list-page.ts +10 -2
- package/client/pages/kpi-metric-value/kpi-metric-value-editor-page.ts +7 -7
- package/client/pages/kpi-metric-value/kpi-metric-value-importer.ts +2 -2
- package/client/pages/kpi-metric-value/kpi-metric-value-list-page.ts +16 -8
- package/client/pages/kpi-metric-value/kpi-metric-value-manual-entry-form.ts +5 -5
- package/client/pages/kpi-statistic/kpi-statistic-editor-page.ts +1 -2
- package/client/pages/kpi-statistic/kpi-statistic-list-page.ts +10 -2
- package/client/pages/kpi-value/kpi-value-editor-page.ts +11 -7
- package/client/pages/kpi-value/kpi-value-list-page.ts +31 -7
- package/client/route.ts +2 -9
- package/design-entities.md +8 -12
- package/dist-client/charts/kpi-boxplot-chart.d.ts +2 -0
- package/dist-client/charts/kpi-boxplot-chart.js +168 -42
- package/dist-client/charts/kpi-boxplot-chart.js.map +1 -1
- package/dist-client/charts/kpi-mini-trend-chart.d.ts +14 -0
- package/dist-client/charts/kpi-mini-trend-chart.js +148 -0
- package/dist-client/charts/kpi-mini-trend-chart.js.map +1 -0
- package/dist-client/charts/kpi-radar-chart.js +9 -9
- package/dist-client/charts/kpi-radar-chart.js.map +1 -1
- package/dist-client/charts/kpi-trend-chart.d.ts +25 -0
- package/dist-client/charts/kpi-trend-chart.js +186 -0
- package/dist-client/charts/kpi-trend-chart.js.map +1 -0
- package/dist-client/google-map/common-google-map.js +40 -7
- package/dist-client/google-map/common-google-map.js.map +1 -1
- package/dist-client/google-map/google-map-loader.js +1 -1
- package/dist-client/google-map/google-map-loader.js.map +1 -1
- package/dist-client/pages/kpi/kpi-list-page.d.ts +19 -3
- package/dist-client/pages/kpi/kpi-list-page.js +188 -32
- package/dist-client/pages/kpi/kpi-list-page.js.map +1 -1
- package/dist-client/pages/kpi/kpi-overview.js +9 -11
- package/dist-client/pages/kpi/kpi-overview.js.map +1 -1
- package/dist-client/pages/kpi/kpi-tree-page.d.ts +59 -0
- package/dist-client/pages/kpi/kpi-tree-page.js +403 -0
- package/dist-client/pages/kpi/kpi-tree-page.js.map +1 -0
- package/dist-client/pages/kpi/kpi-view.d.ts +12 -0
- package/dist-client/pages/kpi/kpi-view.js +191 -0
- package/dist-client/pages/kpi/kpi-view.js.map +1 -0
- package/dist-client/pages/kpi-dashboard/cards/kpi-level1-card.js +1 -1
- package/dist-client/pages/kpi-dashboard/cards/kpi-level1-card.js.map +1 -1
- package/dist-client/pages/kpi-dashboard/cards/kpi-level2-comparison.js +1 -1
- package/dist-client/pages/kpi-dashboard/cards/kpi-level2-comparison.js.map +1 -1
- package/dist-client/pages/kpi-dashboard/cards/kpi-level3-comparison.js +1 -1
- package/dist-client/pages/kpi-dashboard/cards/kpi-level3-comparison.js.map +1 -1
- package/dist-client/pages/kpi-dashboard/components/kpi-chart-toggle.js +1 -2
- package/dist-client/pages/kpi-dashboard/components/kpi-chart-toggle.js.map +1 -1
- package/dist-client/pages/kpi-dashboard/components/kpi-left-panel.d.ts +24 -0
- package/dist-client/pages/kpi-dashboard/components/kpi-left-panel.js +440 -0
- package/dist-client/pages/kpi-dashboard/components/kpi-left-panel.js.map +1 -0
- package/dist-client/pages/kpi-dashboard/components/kpi-map-panel.d.ts +11 -1
- package/dist-client/pages/kpi-dashboard/components/kpi-map-panel.js +243 -22
- package/dist-client/pages/kpi-dashboard/components/kpi-map-panel.js.map +1 -1
- package/dist-client/pages/kpi-dashboard/components/kpi-region-popup.d.ts +23 -0
- package/dist-client/pages/kpi-dashboard/components/kpi-region-popup.js +369 -0
- package/dist-client/pages/kpi-dashboard/components/kpi-region-popup.js.map +1 -0
- package/dist-client/pages/kpi-dashboard/kpi-dashboard-map.d.ts +6 -15
- package/dist-client/pages/kpi-dashboard/kpi-dashboard-map.js +47 -588
- package/dist-client/pages/kpi-dashboard/kpi-dashboard-map.js.map +1 -1
- package/dist-client/pages/kpi-dashboard/kpi-dashboard.js +28 -30
- package/dist-client/pages/kpi-dashboard/kpi-dashboard.js.map +1 -1
- package/dist-client/pages/kpi-history/kpi-history-list-page.d.ts +6 -1
- package/dist-client/pages/kpi-history/kpi-history-list-page.js +11 -11
- package/dist-client/pages/kpi-history/kpi-history-list-page.js.map +1 -1
- package/dist-client/pages/kpi-metric/kpi-metric-list-page.d.ts +5 -0
- package/dist-client/pages/kpi-metric/kpi-metric-list-page.js +10 -2
- package/dist-client/pages/kpi-metric/kpi-metric-list-page.js.map +1 -1
- package/dist-client/pages/kpi-metric-value/kpi-metric-value-editor-page.d.ts +1 -1
- package/dist-client/pages/kpi-metric-value/kpi-metric-value-editor-page.js +8 -8
- package/dist-client/pages/kpi-metric-value/kpi-metric-value-editor-page.js.map +1 -1
- package/dist-client/pages/kpi-metric-value/kpi-metric-value-importer.js +2 -2
- package/dist-client/pages/kpi-metric-value/kpi-metric-value-importer.js.map +1 -1
- package/dist-client/pages/kpi-metric-value/kpi-metric-value-list-page.d.ts +5 -0
- package/dist-client/pages/kpi-metric-value/kpi-metric-value-list-page.js +16 -8
- package/dist-client/pages/kpi-metric-value/kpi-metric-value-list-page.js.map +1 -1
- package/dist-client/pages/kpi-metric-value/kpi-metric-value-manual-entry-form.d.ts +1 -1
- package/dist-client/pages/kpi-metric-value/kpi-metric-value-manual-entry-form.js +6 -6
- package/dist-client/pages/kpi-metric-value/kpi-metric-value-manual-entry-form.js.map +1 -1
- package/dist-client/pages/kpi-statistic/kpi-statistic-editor-page.js +1 -2
- package/dist-client/pages/kpi-statistic/kpi-statistic-editor-page.js.map +1 -1
- package/dist-client/pages/kpi-statistic/kpi-statistic-list-page.d.ts +5 -0
- package/dist-client/pages/kpi-statistic/kpi-statistic-list-page.js +10 -2
- package/dist-client/pages/kpi-statistic/kpi-statistic-list-page.js.map +1 -1
- package/dist-client/pages/kpi-value/kpi-value-editor-page.d.ts +2 -1
- package/dist-client/pages/kpi-value/kpi-value-editor-page.js +16 -8
- package/dist-client/pages/kpi-value/kpi-value-editor-page.js.map +1 -1
- package/dist-client/pages/kpi-value/kpi-value-list-page.d.ts +5 -0
- package/dist-client/pages/kpi-value/kpi-value-list-page.js +31 -7
- package/dist-client/pages/kpi-value/kpi-value-list-page.js.map +1 -1
- package/dist-client/route.d.ts +1 -1
- package/dist-client/route.js +2 -8
- package/dist-client/route.js.map +1 -1
- package/dist-client/tsconfig.tsbuildinfo +1 -1
- package/dist-server/controllers/kpi-metric-value-provider.d.ts +1 -1
- package/dist-server/controllers/kpi-metric-value-provider.js +4 -4
- package/dist-server/controllers/kpi-metric-value-provider.js.map +1 -1
- package/dist-server/controllers/kpi-value-provider.d.ts +1 -1
- package/dist-server/controllers/kpi-value-provider.js +3 -3
- package/dist-server/controllers/kpi-value-provider.js.map +1 -1
- package/dist-server/migrations/1752190849680-seed-kpi-metrics.d.ts +6 -0
- package/dist-server/migrations/1752190849680-seed-kpi-metrics.js +101 -0
- package/dist-server/migrations/1752190849680-seed-kpi-metrics.js.map +1 -0
- package/dist-server/migrations/1752190849681-seed-kpi.d.ts +5 -0
- package/dist-server/migrations/1752190849681-seed-kpi.js +315 -0
- package/dist-server/migrations/1752190849681-seed-kpi.js.map +1 -0
- package/dist-server/migrations/1752192090123-add-grades-to-kpi.d.ts +7 -0
- package/dist-server/migrations/1752192090123-add-grades-to-kpi.js +51 -0
- package/dist-server/migrations/1752192090123-add-grades-to-kpi.js.map +1 -0
- package/dist-server/migrations/1752192090124-add-kpi-statistics.d.ts +5 -0
- package/dist-server/migrations/1752192090124-add-kpi-statistics.js +710 -0
- package/dist-server/migrations/1752192090124-add-kpi-statistics.js.map +1 -0
- package/dist-server/migrations/1752192090128-seed-kpi-org-scope.d.ts +6 -0
- package/dist-server/migrations/1752192090128-seed-kpi-org-scope.js +111 -0
- package/dist-server/migrations/1752192090128-seed-kpi-org-scope.js.map +1 -0
- package/dist-server/migrations/1752192090129-seed-kpi-values.d.ts +6 -0
- package/dist-server/migrations/1752192090129-seed-kpi-values.js +187 -0
- package/dist-server/migrations/1752192090129-seed-kpi-values.js.map +1 -0
- package/dist-server/migrations/grade-data/x11-performance-table.json +962 -0
- package/dist-server/migrations/grade-data/x12-performance-table.json +611 -0
- package/dist-server/migrations/grade-data/x14-performance-table.json +42 -0
- package/dist-server/migrations/grade-data/x21-performance-table.json +889 -0
- package/dist-server/migrations/grade-data/x22-performance-table.json +1064 -0
- package/dist-server/migrations/grade-data/x23-performance-table.json +42 -0
- package/dist-server/migrations/grade-data/x31-performance-table.json +644 -0
- package/dist-server/migrations/grade-data/x32-performance-table.json +993 -0
- package/dist-server/migrations/grade-data/x33-performance-table.json +195 -0
- package/dist-server/migrations/grade-data/x34-performance-table.json +12 -0
- package/dist-server/migrations/grade-data/x35-performance-table.json +42 -0
- package/dist-server/migrations/grade-data/x41-performance-table.json +825 -0
- package/dist-server/migrations/grade-data/x42-performance-table.json +786 -0
- package/dist-server/migrations/grade-data/x43-performance-table.json +12 -0
- package/dist-server/migrations/grade-data/x44-performance-table.json +42 -0
- package/dist-server/migrations/grade-data/x51-performance-table.json +924 -0
- package/dist-server/migrations/grade-data/x52-performance-table.json +42 -0
- package/dist-server/migrations/grade-data/x61-performance-table.json +261 -0
- package/dist-server/migrations/grade-data/x62-performance-table.json +42 -0
- package/dist-server/migrations/seed-data/kpi-metrics-seed.json +454 -0
- package/dist-server/migrations/seed-data/kpi-org-scope-seed.json +1676 -0
- package/dist-server/migrations/seed-data/kpi-values-seed.json +402 -0
- package/dist-server/migrations/seed-data/kpis-seed.json +488 -0
- package/dist-server/service/index.d.ts +3 -7
- package/dist-server/service/index.js +5 -12
- package/dist-server/service/index.js.map +1 -1
- package/dist-server/service/kpi/aggregate-kpi.js +30 -13
- package/dist-server/service/kpi/aggregate-kpi.js.map +1 -1
- package/dist-server/service/kpi/kpi-formula.service.d.ts +15 -0
- package/dist-server/service/kpi/kpi-formula.service.js +90 -0
- package/dist-server/service/kpi/kpi-formula.service.js.map +1 -1
- package/dist-server/service/kpi/kpi-history.d.ts +0 -3
- package/dist-server/service/kpi/kpi-history.js +0 -10
- package/dist-server/service/kpi/kpi-history.js.map +1 -1
- package/dist-server/service/kpi/kpi-mutation.d.ts +1 -1
- package/dist-server/service/kpi/kpi-mutation.js +57 -20
- package/dist-server/service/kpi/kpi-mutation.js.map +1 -1
- package/dist-server/service/kpi/kpi-query.d.ts +7 -3
- package/dist-server/service/kpi/kpi-query.js +126 -10
- package/dist-server/service/kpi/kpi-query.js.map +1 -1
- package/dist-server/service/kpi/kpi-type.d.ts +4 -2
- package/dist-server/service/kpi/kpi-type.js +12 -4
- package/dist-server/service/kpi/kpi-type.js.map +1 -1
- package/dist-server/service/kpi/kpi.d.ts +4 -3
- package/dist-server/service/kpi/kpi.js +20 -8
- package/dist-server/service/kpi/kpi.js.map +1 -1
- package/dist-server/service/kpi-metric/aggregate-kpi-metric.js +46 -11
- package/dist-server/service/kpi-metric/aggregate-kpi-metric.js.map +1 -1
- package/dist-server/service/kpi-metric-value/kpi-metric-value-mutation.d.ts +1 -1
- package/dist-server/service/kpi-metric-value/kpi-metric-value-mutation.js +6 -6
- package/dist-server/service/kpi-metric-value/kpi-metric-value-mutation.js.map +1 -1
- package/dist-server/service/kpi-metric-value/kpi-metric-value-type.d.ts +2 -2
- package/dist-server/service/kpi-metric-value/kpi-metric-value-type.js +4 -4
- package/dist-server/service/kpi-metric-value/kpi-metric-value-type.js.map +1 -1
- package/dist-server/service/kpi-metric-value/kpi-metric-value.d.ts +1 -1
- package/dist-server/service/kpi-metric-value/kpi-metric-value.js +3 -3
- package/dist-server/service/kpi-metric-value/kpi-metric-value.js.map +1 -1
- package/dist-server/service/kpi-org-scope/index.d.ts +9 -0
- package/dist-server/service/kpi-org-scope/index.js +14 -0
- package/dist-server/service/kpi-org-scope/index.js.map +1 -0
- package/dist-server/service/kpi-org-scope/kpi-org-scope-mutation.d.ts +8 -0
- package/dist-server/service/kpi-org-scope/kpi-org-scope-mutation.js +170 -0
- package/dist-server/service/kpi-org-scope/kpi-org-scope-mutation.js.map +1 -0
- package/dist-server/service/kpi-org-scope/kpi-org-scope-query.d.ts +14 -0
- package/dist-server/service/kpi-org-scope/kpi-org-scope-query.js +152 -0
- package/dist-server/service/kpi-org-scope/kpi-org-scope-query.js.map +1 -0
- package/dist-server/service/kpi-org-scope/kpi-org-scope-type.d.ts +26 -0
- package/dist-server/service/kpi-org-scope/kpi-org-scope-type.js +101 -0
- package/dist-server/service/kpi-org-scope/kpi-org-scope-type.js.map +1 -0
- package/dist-server/service/kpi-org-scope/kpi-org-scope.d.ts +26 -0
- package/dist-server/service/kpi-org-scope/kpi-org-scope.js +135 -0
- package/dist-server/service/kpi-org-scope/kpi-org-scope.js.map +1 -0
- package/dist-server/service/kpi-statistic/kpi-statistic.d.ts +1 -0
- package/dist-server/service/kpi-statistic/kpi-statistic.js +11 -0
- package/dist-server/service/kpi-statistic/kpi-statistic.js.map +1 -1
- package/dist-server/service/kpi-value/kpi-value-mutation.js +71 -7
- package/dist-server/service/kpi-value/kpi-value-mutation.js.map +1 -1
- package/dist-server/service/kpi-value/kpi-value-type.d.ts +4 -2
- package/dist-server/service/kpi-value/kpi-value-type.js +12 -4
- package/dist-server/service/kpi-value/kpi-value-type.js.map +1 -1
- package/dist-server/service/kpi-value/kpi-value.d.ts +3 -1
- package/dist-server/service/kpi-value/kpi-value.js +11 -5
- package/dist-server/service/kpi-value/kpi-value.js.map +1 -1
- package/dist-server/service/utils/value-date-util.d.ts +1 -0
- package/dist-server/service/utils/value-date-util.js +41 -0
- package/dist-server/service/utils/value-date-util.js.map +1 -1
- package/dist-server/tsconfig.json +10 -0
- package/dist-server/tsconfig.tsbuildinfo +1 -1
- package/package.json +7 -6
- package/server/@types/index.d.ts +11 -0
- package/server/controllers/kpi-metric-value-provider.ts +5 -5
- package/server/controllers/kpi-value-provider.ts +4 -4
- package/server/migrations/1752190849680-seed-kpi-metrics.ts +124 -0
- package/server/migrations/1752190849681-seed-kpi.ts +356 -0
- package/server/migrations/1752192090123-add-grades-to-kpi.ts +67 -0
- package/server/migrations/1752192090124-add-kpi-statistics.ts +719 -0
- package/server/migrations/1752192090128-seed-kpi-org-scope.ts +132 -0
- package/server/migrations/1752192090129-seed-kpi-values.ts +207 -0
- package/server/migrations/grade-data/x11-performance-table.json +962 -0
- package/server/migrations/grade-data/x12-performance-table.json +611 -0
- package/server/migrations/grade-data/x14-performance-table.json +42 -0
- package/server/migrations/grade-data/x21-performance-table.json +889 -0
- package/server/migrations/grade-data/x22-performance-table.json +1064 -0
- package/server/migrations/grade-data/x23-performance-table.json +42 -0
- package/server/migrations/grade-data/x31-performance-table.json +644 -0
- package/server/migrations/grade-data/x32-performance-table.json +993 -0
- package/server/migrations/grade-data/x33-performance-table.json +195 -0
- package/server/migrations/grade-data/x34-performance-table.json +12 -0
- package/server/migrations/grade-data/x35-performance-table.json +42 -0
- package/server/migrations/grade-data/x41-performance-table.json +825 -0
- package/server/migrations/grade-data/x42-performance-table.json +786 -0
- package/server/migrations/grade-data/x43-performance-table.json +12 -0
- package/server/migrations/grade-data/x44-performance-table.json +42 -0
- package/server/migrations/grade-data/x51-performance-table.json +924 -0
- package/server/migrations/grade-data/x52-performance-table.json +42 -0
- package/server/migrations/grade-data/x61-performance-table.json +261 -0
- package/server/migrations/grade-data/x62-performance-table.json +42 -0
- package/server/migrations/seed-data/kpi-metrics-seed.json +454 -0
- package/server/migrations/seed-data/kpi-org-scope-seed.json +1676 -0
- package/server/migrations/seed-data/kpi-values-seed.json +402 -0
- package/server/migrations/seed-data/kpis-seed.json +488 -0
- package/server/service/index.ts +5 -12
- package/server/service/kpi/aggregate-kpi.ts +31 -13
- package/server/service/kpi/kpi-formula.service.ts +101 -0
- package/server/service/kpi/kpi-history.ts +0 -8
- package/server/service/kpi/kpi-mutation.ts +59 -19
- package/server/service/kpi/kpi-query.ts +118 -8
- package/server/service/kpi/kpi-type.ts +10 -4
- package/server/service/kpi/kpi.ts +17 -7
- package/server/service/kpi-metric/aggregate-kpi-metric.ts +55 -11
- package/server/service/kpi-metric-value/kpi-metric-value-mutation.ts +6 -6
- package/server/service/kpi-metric-value/kpi-metric-value-type.ts +4 -4
- package/server/service/kpi-metric-value/kpi-metric-value.ts +3 -3
- package/server/service/kpi-org-scope/index.ts +11 -0
- package/server/service/kpi-org-scope/kpi-org-scope-mutation.ts +173 -0
- package/server/service/kpi-org-scope/kpi-org-scope-query.ts +127 -0
- package/server/service/kpi-org-scope/kpi-org-scope-type.ts +68 -0
- package/server/service/kpi-org-scope/kpi-org-scope.ts +123 -0
- package/server/service/kpi-statistic/kpi-statistic.ts +10 -0
- package/server/service/kpi-value/kpi-value-mutation.ts +73 -7
- package/server/service/kpi-value/kpi-value-type.ts +10 -4
- package/server/service/kpi-value/kpi-value.ts +10 -5
- package/server/service/utils/value-date-util.ts +47 -0
- package/server/types/global.d.ts +8 -0
- package/things-factory.config.js +1 -0
- package/translations/en.json +15 -3
- package/translations/ja.json +13 -3
- package/translations/ko.json +15 -3
- package/translations/ms.json +13 -3
- package/translations/zh.json +13 -3
- package/client/google-map/script-loader.ts +0 -173
- package/client/pages/kpi-category/kpi-category-importer.ts +0 -90
- package/client/pages/kpi-category/kpi-category-list-page.ts +0 -537
- package/client/pages/kpi-category/kpi-category-value-calculator.ts +0 -233
- package/client/pages/kpi-category-value/kpi-category-value-list-page.ts +0 -404
- package/dist-client/google-map/script-loader.d.ts +0 -3
- package/dist-client/google-map/script-loader.js +0 -144
- package/dist-client/google-map/script-loader.js.map +0 -1
- package/dist-client/pages/kpi-category/kpi-category-importer.d.ts +0 -23
- package/dist-client/pages/kpi-category/kpi-category-importer.js +0 -92
- package/dist-client/pages/kpi-category/kpi-category-importer.js.map +0 -1
- package/dist-client/pages/kpi-category/kpi-category-list-page.d.ts +0 -74
- package/dist-client/pages/kpi-category/kpi-category-list-page.js +0 -517
- package/dist-client/pages/kpi-category/kpi-category-list-page.js.map +0 -1
- package/dist-client/pages/kpi-category/kpi-category-value-calculator.d.ts +0 -13
- package/dist-client/pages/kpi-category/kpi-category-value-calculator.js +0 -256
- package/dist-client/pages/kpi-category/kpi-category-value-calculator.js.map +0 -1
- package/dist-client/pages/kpi-category-value/kpi-category-value-list-page.d.ts +0 -63
- package/dist-client/pages/kpi-category-value/kpi-category-value-list-page.js +0 -393
- package/dist-client/pages/kpi-category-value/kpi-category-value-list-page.js.map +0 -1
- package/dist-server/service/kpi-category/index.d.ts +0 -6
- package/dist-server/service/kpi-category/index.js +0 -10
- package/dist-server/service/kpi-category/index.js.map +0 -1
- package/dist-server/service/kpi-category/kpi-category-mutation.d.ts +0 -9
- package/dist-server/service/kpi-category/kpi-category-mutation.js +0 -221
- package/dist-server/service/kpi-category/kpi-category-mutation.js.map +0 -1
- package/dist-server/service/kpi-category/kpi-category-query.d.ts +0 -18
- package/dist-server/service/kpi-category/kpi-category-query.js +0 -115
- package/dist-server/service/kpi-category/kpi-category-query.js.map +0 -1
- package/dist-server/service/kpi-category/kpi-category-type.d.ts +0 -24
- package/dist-server/service/kpi-category/kpi-category-type.js +0 -100
- package/dist-server/service/kpi-category/kpi-category-type.js.map +0 -1
- package/dist-server/service/kpi-category/kpi-category.d.ts +0 -22
- package/dist-server/service/kpi-category/kpi-category.js +0 -106
- package/dist-server/service/kpi-category/kpi-category.js.map +0 -1
- package/dist-server/service/kpi-category-value/index.d.ts +0 -6
- package/dist-server/service/kpi-category-value/index.js +0 -10
- package/dist-server/service/kpi-category-value/index.js.map +0 -1
- package/dist-server/service/kpi-category-value/kpi-category-value-mutation.d.ts +0 -8
- package/dist-server/service/kpi-category-value/kpi-category-value-mutation.js +0 -102
- package/dist-server/service/kpi-category-value/kpi-category-value-mutation.js.map +0 -1
- package/dist-server/service/kpi-category-value/kpi-category-value-query.d.ts +0 -13
- package/dist-server/service/kpi-category-value/kpi-category-value-query.js +0 -91
- package/dist-server/service/kpi-category-value/kpi-category-value-query.js.map +0 -1
- package/dist-server/service/kpi-category-value/kpi-category-value-type.d.ts +0 -19
- package/dist-server/service/kpi-category-value/kpi-category-value-type.js +0 -73
- package/dist-server/service/kpi-category-value/kpi-category-value-type.js.map +0 -1
- package/dist-server/service/kpi-category-value/kpi-category-value.d.ts +0 -19
- package/dist-server/service/kpi-category-value/kpi-category-value.js +0 -91
- package/dist-server/service/kpi-category-value/kpi-category-value.js.map +0 -1
- package/helps/kpi/kpi-category.md +0 -160
- package/server/service/kpi-category/index.ts +0 -7
- package/server/service/kpi-category/kpi-category-mutation.ts +0 -217
- package/server/service/kpi-category/kpi-category-query.ts +0 -87
- package/server/service/kpi-category/kpi-category-type.ts +0 -73
- package/server/service/kpi-category/kpi-category.ts +0 -95
- package/server/service/kpi-category-value/index.ts +0 -7
- package/server/service/kpi-category-value/kpi-category-value-mutation.ts +0 -88
- package/server/service/kpi-category-value/kpi-category-value-query.ts +0 -62
- package/server/service/kpi-category-value/kpi-category-value-type.ts +0 -48
- package/server/service/kpi-category-value/kpi-category-value.ts +0 -79
package/README.md
CHANGED
|
@@ -3,14 +3,13 @@
|
|
|
3
3
|
## 1. KPI란?
|
|
4
4
|
|
|
5
5
|
- KPI(Key Performance Indicator)는 조직의 목표 달성도를 수치로 측정/관리하는 핵심 지표입니다.
|
|
6
|
-
- KPI는 산식(formula), 목표값(target),
|
|
6
|
+
- KPI는 산식(formula), 목표값(target), 버전(version), 상태(state) 등 다양한 속성을 가집니다.
|
|
7
7
|
- KPI의 실적값(Performance Value)은 특정 시점/조직/프로젝트별로 기록됩니다.
|
|
8
8
|
|
|
9
9
|
## 2. KPI 모듈의 주요 엔티티
|
|
10
10
|
|
|
11
11
|
- **Kpi**: KPI 정의(이름, 산식, 카테고리, 상태, 버전 등)
|
|
12
12
|
- **KpiValue**: KPI별 실적값(날짜, 값, 입력방식, 소스 등)
|
|
13
|
-
- **KpiCategory**: KPI 분류(트리 구조 가능)
|
|
14
13
|
- **KpiMetric**: KPI 산식에 사용되는 원천 데이터 항목(코드, 단위, 데이터셋 등)
|
|
15
14
|
- **KpiGrade**: KPI별 등급/구간(구간별 점수, 색상 등)
|
|
16
15
|
- **KpiHistory**: KPI의 버전별 이력(스냅샷)
|
|
@@ -5,7 +5,7 @@ import * as d3 from 'd3'
|
|
|
5
5
|
@customElement('kpi-boxplot-chart')
|
|
6
6
|
export class KpiBoxplotChart extends LitElement {
|
|
7
7
|
@property({ type: Array }) data: any[] = []
|
|
8
|
-
@property({ type: Array }) groups: string[] = []
|
|
8
|
+
@property({ type: Array }) groups: string[] = [] // 조직 단위 목록 (빈 배열이면 데이터에서 자동 추출)
|
|
9
9
|
@property({ type: String }) minKey: string = 'min'
|
|
10
10
|
@property({ type: String }) maxKey: string = 'max'
|
|
11
11
|
@property({ type: String }) meanKey: string = 'mean'
|
|
@@ -14,6 +14,7 @@ export class KpiBoxplotChart extends LitElement {
|
|
|
14
14
|
@property({ type: String }) q3Key: string = 'q3'
|
|
15
15
|
@property({ type: String }) valueKey: string = 'value'
|
|
16
16
|
@property({ type: String }) currentGroup: string = ''
|
|
17
|
+
@property({ type: Boolean }) independentScale: boolean = false
|
|
17
18
|
|
|
18
19
|
static styles = css`
|
|
19
20
|
:host {
|
|
@@ -64,32 +65,83 @@ export class KpiBoxplotChart extends LitElement {
|
|
|
64
65
|
this.drawBoxplot()
|
|
65
66
|
}
|
|
66
67
|
|
|
68
|
+
private getOrgValue = (d: any) => d.org || d.group
|
|
69
|
+
|
|
67
70
|
drawBoxplot() {
|
|
68
71
|
const svg = d3.select(this.renderRoot.querySelector('#boxplot'))
|
|
69
72
|
svg.selectAll('*').remove()
|
|
73
|
+
|
|
74
|
+
// 데이터 검증
|
|
75
|
+
if (!this.data || this.data.length === 0) {
|
|
76
|
+
return
|
|
77
|
+
}
|
|
70
78
|
const w = this.chartWidth || 300
|
|
71
79
|
const h = this.chartHeight || 300
|
|
72
|
-
const margin = {
|
|
80
|
+
const margin = {
|
|
81
|
+
top: 20,
|
|
82
|
+
right: 20,
|
|
83
|
+
bottom: 40,
|
|
84
|
+
left: this.independentScale ? 20 : 40
|
|
85
|
+
}
|
|
73
86
|
const plotW = w - margin.left - margin.right
|
|
74
87
|
const plotH = h - margin.top - margin.bottom
|
|
75
88
|
|
|
76
|
-
// x축: 그룹
|
|
77
|
-
const
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
const
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
89
|
+
// x축: 조직 단위 (그룹) - org 또는 group 필드 지원
|
|
90
|
+
const groups = this.groups.length > 0 ? this.groups : [...new Set(this.data.map(d => this.getOrgValue(d)).filter(org => org != null))]
|
|
91
|
+
if (groups.length === 0) {
|
|
92
|
+
console.warn('No valid groups found in data:', this.data)
|
|
93
|
+
return // 유효한 그룹이 없으면 차트를 그리지 않음
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
console.log('Boxplot groups:', groups)
|
|
97
|
+
console.log('Data org values:', this.data.map(d => this.getOrgValue(d)))
|
|
98
|
+
console.log('Full data:', this.data)
|
|
99
|
+
console.log('Chart dimensions:', { w, h, plotW, plotH })
|
|
100
|
+
|
|
101
|
+
const x = d3.scaleBand().domain(groups).range([0, plotW]).padding(0.4)
|
|
102
|
+
|
|
103
|
+
// y축 스케일 설정
|
|
104
|
+
let y: d3.ScaleLinear<number, number>
|
|
105
|
+
|
|
106
|
+
if (this.independentScale) {
|
|
107
|
+
// 독립 스케일: 각 시리즈별로 개별 스케일 생성
|
|
108
|
+
const yScales = this.data.map(d => {
|
|
109
|
+
const values = [
|
|
110
|
+
d[this.minKey],
|
|
111
|
+
d[this.maxKey],
|
|
112
|
+
d[this.q1Key],
|
|
113
|
+
d[this.q3Key],
|
|
114
|
+
d[this.medianKey],
|
|
115
|
+
d[this.meanKey],
|
|
116
|
+
d[this.valueKey]
|
|
117
|
+
]
|
|
118
|
+
const min = d3.min(values) ?? 0
|
|
119
|
+
const max = d3.max(values) ?? 1
|
|
120
|
+
return {
|
|
121
|
+
org: d.org,
|
|
122
|
+
scale: d3.scaleLinear().domain([min, max]).nice().range([plotH, 0])
|
|
123
|
+
}
|
|
124
|
+
})
|
|
125
|
+
|
|
126
|
+
// 기본 y축은 첫 번째 스케일 사용
|
|
127
|
+
y = yScales[0]?.scale || d3.scaleLinear().domain([0, 1]).range([plotH, 0])
|
|
128
|
+
} else {
|
|
129
|
+
// 통합 스케일: 모든 데이터를 하나의 스케일에 맞춤
|
|
130
|
+
const allValues = this.data.flatMap(d => [
|
|
131
|
+
d[this.minKey],
|
|
132
|
+
d[this.maxKey],
|
|
133
|
+
d[this.q1Key],
|
|
134
|
+
d[this.q3Key],
|
|
135
|
+
d[this.medianKey],
|
|
136
|
+
d[this.meanKey],
|
|
137
|
+
d[this.valueKey]
|
|
138
|
+
])
|
|
139
|
+
y = d3
|
|
140
|
+
.scaleLinear()
|
|
141
|
+
.domain([d3.min(allValues) ?? 0, d3.max(allValues) ?? 1])
|
|
142
|
+
.nice()
|
|
143
|
+
.range([plotH, 0])
|
|
144
|
+
}
|
|
93
145
|
|
|
94
146
|
const g = svg
|
|
95
147
|
.attr('width', w)
|
|
@@ -98,66 +150,154 @@ export class KpiBoxplotChart extends LitElement {
|
|
|
98
150
|
.attr('transform', `translate(${margin.left},${margin.top})`)
|
|
99
151
|
|
|
100
152
|
// 축
|
|
101
|
-
|
|
153
|
+
if (!this.independentScale) {
|
|
154
|
+
g.append('g').call(d3.axisLeft(y))
|
|
155
|
+
}
|
|
102
156
|
g.append('g').attr('transform', `translate(0,${plotH})`).call(d3.axisBottom(x))
|
|
103
157
|
|
|
104
|
-
// 박스플롯
|
|
105
|
-
|
|
106
|
-
|
|
158
|
+
// 박스플롯 - 각 그룹별로 박스 생성
|
|
159
|
+
groups.forEach(groupName => {
|
|
160
|
+
// 해당 그룹의 데이터 찾기
|
|
161
|
+
const groupData = this.data.find(d => this.getOrgValue(d) === groupName)
|
|
162
|
+
console.log(`Searching for group: ${groupName}, found:`, groupData)
|
|
163
|
+
if (!groupData) {
|
|
164
|
+
console.warn(`No data found for group: ${groupName}. Available data:`, this.data.map(d => ({ org: d.org, keys: Object.keys(d) })))
|
|
165
|
+
return
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// 필수 필드 검증
|
|
169
|
+
const requiredFields = [this.minKey, this.maxKey, this.q1Key, this.q3Key, this.medianKey, this.meanKey]
|
|
170
|
+
const missingFields = requiredFields.filter(field => groupData[field] == null)
|
|
171
|
+
if (missingFields.length > 0) {
|
|
172
|
+
console.warn(`Missing required fields for group ${groupName}:`, missingFields)
|
|
173
|
+
console.log('Group data:', groupData)
|
|
174
|
+
return
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
const gx = x(groupName) ?? 0
|
|
178
|
+
console.log(`Drawing box for ${groupName} at x=${gx}, bandwidth=${x.bandwidth()}`)
|
|
179
|
+
|
|
180
|
+
// 독립 스케일 사용 시 해당 그룹의 스케일 찾기
|
|
181
|
+
let currentY = y
|
|
182
|
+
if (this.independentScale) {
|
|
183
|
+
const values = [
|
|
184
|
+
groupData[this.minKey],
|
|
185
|
+
groupData[this.maxKey],
|
|
186
|
+
groupData[this.q1Key],
|
|
187
|
+
groupData[this.q3Key],
|
|
188
|
+
groupData[this.medianKey],
|
|
189
|
+
groupData[this.meanKey],
|
|
190
|
+
groupData[this.valueKey]
|
|
191
|
+
]
|
|
192
|
+
const min = d3.min(values) ?? 0
|
|
193
|
+
const max = d3.max(values) ?? 1
|
|
194
|
+
currentY = d3.scaleLinear().domain([min, max]).nice().range([plotH, 0])
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// Outlier 계산 (1.5 * IQR 규칙)
|
|
198
|
+
const iqr = groupData[this.q3Key] - groupData[this.q1Key]
|
|
199
|
+
const lowerFence = groupData[this.q1Key] - 1.5 * iqr
|
|
200
|
+
const upperFence = groupData[this.q3Key] + 1.5 * iqr
|
|
201
|
+
|
|
202
|
+
// 실제 min/max 값 (fence 내부)
|
|
203
|
+
const actualMin = Math.max(groupData[this.minKey], lowerFence)
|
|
204
|
+
const actualMax = Math.min(groupData[this.maxKey], upperFence)
|
|
205
|
+
|
|
107
206
|
// 박스
|
|
108
207
|
g.append('rect')
|
|
109
208
|
.attr('x', gx)
|
|
110
|
-
.attr('y',
|
|
209
|
+
.attr('y', currentY(groupData[this.q3Key]))
|
|
111
210
|
.attr('width', x.bandwidth())
|
|
112
|
-
.attr('height',
|
|
113
|
-
.attr('fill',
|
|
211
|
+
.attr('height', currentY(groupData[this.q1Key]) - currentY(groupData[this.q3Key]))
|
|
212
|
+
.attr('fill', this.getOrgValue(groupData) === this.currentGroup ? '#2196f3' : '#bbb')
|
|
114
213
|
.attr('opacity', 0.5)
|
|
214
|
+
|
|
115
215
|
// 중앙선(중앙값)
|
|
116
216
|
g.append('line')
|
|
117
217
|
.attr('x1', gx)
|
|
118
218
|
.attr('x2', gx + x.bandwidth())
|
|
119
|
-
.attr('y1',
|
|
120
|
-
.attr('y2',
|
|
219
|
+
.attr('y1', currentY(groupData[this.medianKey]))
|
|
220
|
+
.attr('y2', currentY(groupData[this.medianKey]))
|
|
121
221
|
.attr('stroke', '#333')
|
|
122
222
|
.attr('stroke-width', 2)
|
|
123
|
-
|
|
223
|
+
|
|
224
|
+
// 수염 (fence 내부의 min-max)
|
|
124
225
|
g.append('line')
|
|
125
226
|
.attr('x1', gx + x.bandwidth() / 2)
|
|
126
227
|
.attr('x2', gx + x.bandwidth() / 2)
|
|
127
|
-
.attr('y1',
|
|
128
|
-
.attr('y2',
|
|
228
|
+
.attr('y1', currentY(actualMin))
|
|
229
|
+
.attr('y2', currentY(actualMax))
|
|
129
230
|
.attr('stroke', '#333')
|
|
130
|
-
|
|
231
|
+
|
|
232
|
+
// min/max 선 (fence 내부)
|
|
131
233
|
g.append('line')
|
|
132
234
|
.attr('x1', gx + x.bandwidth() / 4)
|
|
133
235
|
.attr('x2', gx + (x.bandwidth() * 3) / 4)
|
|
134
|
-
.attr('y1',
|
|
135
|
-
.attr('y2',
|
|
236
|
+
.attr('y1', currentY(actualMin))
|
|
237
|
+
.attr('y2', currentY(actualMin))
|
|
136
238
|
.attr('stroke', '#333')
|
|
137
239
|
g.append('line')
|
|
138
240
|
.attr('x1', gx + x.bandwidth() / 4)
|
|
139
241
|
.attr('x2', gx + (x.bandwidth() * 3) / 4)
|
|
140
|
-
.attr('y1',
|
|
141
|
-
.attr('y2',
|
|
242
|
+
.attr('y1', currentY(actualMax))
|
|
243
|
+
.attr('y2', currentY(actualMax))
|
|
142
244
|
.attr('stroke', '#333')
|
|
245
|
+
|
|
246
|
+
// Outlier 표시 (fence 외부의 값들)
|
|
247
|
+
if (groupData[this.minKey] < lowerFence) {
|
|
248
|
+
g.append('circle')
|
|
249
|
+
.attr('cx', gx + x.bandwidth() / 2)
|
|
250
|
+
.attr('cy', currentY(groupData[this.minKey]))
|
|
251
|
+
.attr('r', 3)
|
|
252
|
+
.attr('fill', '#ff4444')
|
|
253
|
+
.attr('stroke', '#333')
|
|
254
|
+
.attr('stroke-width', 1)
|
|
255
|
+
}
|
|
256
|
+
if (groupData[this.maxKey] > upperFence) {
|
|
257
|
+
g.append('circle')
|
|
258
|
+
.attr('cx', gx + x.bandwidth() / 2)
|
|
259
|
+
.attr('cy', currentY(groupData[this.maxKey]))
|
|
260
|
+
.attr('r', 3)
|
|
261
|
+
.attr('fill', '#ff4444')
|
|
262
|
+
.attr('stroke', '#333')
|
|
263
|
+
.attr('stroke-width', 1)
|
|
264
|
+
}
|
|
265
|
+
|
|
143
266
|
// 평균값
|
|
144
267
|
g.append('circle')
|
|
145
268
|
.attr('cx', gx + x.bandwidth() / 2)
|
|
146
|
-
.attr('cy',
|
|
269
|
+
.attr('cy', currentY(groupData[this.meanKey]))
|
|
147
270
|
.attr('r', 4)
|
|
148
271
|
.attr('fill', 'orange')
|
|
149
272
|
})
|
|
150
273
|
// 현재 그룹 값 강조
|
|
151
|
-
this.
|
|
152
|
-
|
|
274
|
+
if (this.currentGroup && groups.includes(this.currentGroup)) {
|
|
275
|
+
const currentGroupData = this.data.find(d => this.getOrgValue(d) === this.currentGroup)
|
|
276
|
+
if (currentGroupData) {
|
|
277
|
+
let currentY = y
|
|
278
|
+
if (this.independentScale) {
|
|
279
|
+
const values = [
|
|
280
|
+
currentGroupData[this.minKey],
|
|
281
|
+
currentGroupData[this.maxKey],
|
|
282
|
+
currentGroupData[this.q1Key],
|
|
283
|
+
currentGroupData[this.q3Key],
|
|
284
|
+
currentGroupData[this.medianKey],
|
|
285
|
+
currentGroupData[this.meanKey],
|
|
286
|
+
currentGroupData[this.valueKey]
|
|
287
|
+
]
|
|
288
|
+
const min = d3.min(values) ?? 0
|
|
289
|
+
const max = d3.max(values) ?? 1
|
|
290
|
+
currentY = d3.scaleLinear().domain([min, max]).nice().range([plotH, 0])
|
|
291
|
+
}
|
|
292
|
+
|
|
153
293
|
g.append('circle')
|
|
154
|
-
.attr('cx', x(
|
|
155
|
-
.attr('cy',
|
|
294
|
+
.attr('cx', x(this.currentGroup) + x.bandwidth() / 2)
|
|
295
|
+
.attr('cy', currentY(currentGroupData[this.valueKey]))
|
|
156
296
|
.attr('r', 6)
|
|
157
297
|
.attr('fill', '#e91e63')
|
|
158
298
|
.attr('stroke', '#fff')
|
|
159
299
|
.attr('stroke-width', 2)
|
|
160
300
|
}
|
|
161
|
-
}
|
|
301
|
+
}
|
|
162
302
|
}
|
|
163
303
|
}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import { LitElement, html, css } from 'lit'
|
|
2
|
+
import { customElement, property } from 'lit/decorators.js'
|
|
3
|
+
import * as d3 from 'd3'
|
|
4
|
+
|
|
5
|
+
@customElement('kpi-mini-trend-chart')
|
|
6
|
+
export class KpiMiniTrendChart extends LitElement {
|
|
7
|
+
@property({ type: Array }) data: number[] = []
|
|
8
|
+
@property({ type: Number }) width: number = 60
|
|
9
|
+
@property({ type: Number }) height: number = 30
|
|
10
|
+
@property({ type: String }) lineColor: string = '#2196f3'
|
|
11
|
+
@property({ type: Number }) strokeWidth: number = 1.5
|
|
12
|
+
@property({ type: Boolean }) showPoints: boolean = true
|
|
13
|
+
@property({ type: Number }) pointRadius: number = 1.5
|
|
14
|
+
|
|
15
|
+
static styles = css`
|
|
16
|
+
:host {
|
|
17
|
+
display: block;
|
|
18
|
+
width: 100%;
|
|
19
|
+
height: 100%;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
.mini-chart {
|
|
23
|
+
width: 100%;
|
|
24
|
+
height: 100%;
|
|
25
|
+
background: #f8f9fa;
|
|
26
|
+
border-radius: 4px;
|
|
27
|
+
display: flex;
|
|
28
|
+
align-items: center;
|
|
29
|
+
justify-content: center;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
.trend-line {
|
|
33
|
+
fill: none;
|
|
34
|
+
stroke-linecap: round;
|
|
35
|
+
stroke-linejoin: round;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
.data-point {
|
|
39
|
+
fill: white;
|
|
40
|
+
stroke-width: 1;
|
|
41
|
+
}
|
|
42
|
+
`
|
|
43
|
+
|
|
44
|
+
render() {
|
|
45
|
+
return html`
|
|
46
|
+
<div class="mini-chart">
|
|
47
|
+
<svg
|
|
48
|
+
id="mini-trend"
|
|
49
|
+
width=${this.width}
|
|
50
|
+
height=${this.height}
|
|
51
|
+
viewBox="0 0 ${this.width} ${this.height}"
|
|
52
|
+
preserveAspectRatio="xMidYMid meet"
|
|
53
|
+
></svg>
|
|
54
|
+
</div>
|
|
55
|
+
`
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
updated() {
|
|
59
|
+
this.drawMiniTrend()
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
drawMiniTrend() {
|
|
63
|
+
if (!this.data || this.data.length === 0) return
|
|
64
|
+
|
|
65
|
+
const svg = d3.select(this.renderRoot.querySelector('#mini-trend'))
|
|
66
|
+
svg.selectAll('*').remove()
|
|
67
|
+
|
|
68
|
+
const margin = { top: 2, right: 2, bottom: 2, left: 2 }
|
|
69
|
+
const width = this.width - margin.left - margin.right
|
|
70
|
+
const height = this.height - margin.top - margin.bottom
|
|
71
|
+
|
|
72
|
+
// 스케일 설정
|
|
73
|
+
const xScale = d3
|
|
74
|
+
.scaleLinear()
|
|
75
|
+
.domain([0, this.data.length - 1])
|
|
76
|
+
.range([0, width])
|
|
77
|
+
|
|
78
|
+
const yScale = d3
|
|
79
|
+
.scaleLinear()
|
|
80
|
+
.domain([0, d3.max(this.data) || 100])
|
|
81
|
+
.range([height, 0])
|
|
82
|
+
|
|
83
|
+
// SVG 설정
|
|
84
|
+
svg.attr('width', this.width).attr('height', this.height)
|
|
85
|
+
const g = svg.append('g').attr('transform', `translate(${margin.left},${margin.top})`)
|
|
86
|
+
|
|
87
|
+
// 라인 생성기
|
|
88
|
+
const line = d3
|
|
89
|
+
.line<number>()
|
|
90
|
+
.x((d, i) => xScale(i))
|
|
91
|
+
.y(d => yScale(d))
|
|
92
|
+
.curve(d3.curveMonotoneX)
|
|
93
|
+
|
|
94
|
+
// 트렌드 라인 그리기
|
|
95
|
+
g.append('path')
|
|
96
|
+
.datum(this.data)
|
|
97
|
+
.attr('class', 'trend-line')
|
|
98
|
+
.attr('d', line as any)
|
|
99
|
+
.attr('stroke', this.lineColor)
|
|
100
|
+
.attr('stroke-width', this.strokeWidth)
|
|
101
|
+
|
|
102
|
+
// 데이터 포인트 그리기 (첫 번째와 마지막 포인트만)
|
|
103
|
+
if (this.showPoints && this.data.length > 0) {
|
|
104
|
+
// 첫 번째 포인트
|
|
105
|
+
g.append('circle')
|
|
106
|
+
.attr('class', 'data-point')
|
|
107
|
+
.attr('cx', xScale(0))
|
|
108
|
+
.attr('cy', yScale(this.data[0]))
|
|
109
|
+
.attr('r', this.pointRadius)
|
|
110
|
+
.attr('stroke', '#4caf50')
|
|
111
|
+
.attr('fill', 'white')
|
|
112
|
+
|
|
113
|
+
// 마지막 포인트
|
|
114
|
+
if (this.data.length > 1) {
|
|
115
|
+
g.append('circle')
|
|
116
|
+
.attr('class', 'data-point')
|
|
117
|
+
.attr('cx', xScale(this.data.length - 1))
|
|
118
|
+
.attr('cy', yScale(this.data[this.data.length - 1]))
|
|
119
|
+
.attr('r', this.pointRadius)
|
|
120
|
+
.attr('stroke', '#2196f3')
|
|
121
|
+
.attr('fill', 'white')
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
@@ -66,11 +66,11 @@ export class KpiRadarChart extends LitElement {
|
|
|
66
66
|
const r = Math.min(w, h) / 2 - 40
|
|
67
67
|
const angleSlice = (2 * Math.PI) / (this.categories.length || 1)
|
|
68
68
|
|
|
69
|
-
// 데이터 변환: {
|
|
70
|
-
const
|
|
71
|
-
.groups(this.data, d => d.
|
|
72
|
-
.map(([
|
|
73
|
-
|
|
69
|
+
// 데이터 변환: { org, values: [ {category, value} ... ] }
|
|
70
|
+
const orgData = d3
|
|
71
|
+
.groups(this.data, d => d.org)
|
|
72
|
+
.map(([org, values]) => ({
|
|
73
|
+
org,
|
|
74
74
|
values: this.categories.map((cat, i) => {
|
|
75
75
|
const found = values.find(v => v.category === cat)
|
|
76
76
|
return { category: cat, value: found ? found[this.valueKey] : 0 }
|
|
@@ -110,7 +110,7 @@ export class KpiRadarChart extends LitElement {
|
|
|
110
110
|
})
|
|
111
111
|
|
|
112
112
|
// 그룹별 폴리곤
|
|
113
|
-
|
|
113
|
+
orgData.forEach(gd => {
|
|
114
114
|
// 마지막에 첫 점을 한 번 더 추가
|
|
115
115
|
const closedValues = [...gd.values, gd.values[0]]
|
|
116
116
|
const line = d3
|
|
@@ -120,9 +120,9 @@ export class KpiRadarChart extends LitElement {
|
|
|
120
120
|
g.append('path')
|
|
121
121
|
.datum(closedValues)
|
|
122
122
|
.attr('d', line as any)
|
|
123
|
-
.attr('fill', gd.
|
|
124
|
-
.attr('stroke', gd.
|
|
125
|
-
.attr('stroke-width', gd.
|
|
123
|
+
.attr('fill', gd.org === this.currentGroup ? 'rgba(33,150,243,0.4)' : 'rgba(200,200,200,0.2)')
|
|
124
|
+
.attr('stroke', gd.org === this.currentGroup ? '#2196f3' : '#aaa')
|
|
125
|
+
.attr('stroke-width', gd.org === this.currentGroup ? 3 : 1)
|
|
126
126
|
})
|
|
127
127
|
}
|
|
128
128
|
}
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import { LitElement, html, css } from 'lit'
|
|
2
|
+
import { customElement, property } from 'lit/decorators.js'
|
|
3
|
+
import * as d3 from 'd3'
|
|
4
|
+
|
|
5
|
+
@customElement('kpi-trend-chart')
|
|
6
|
+
export class KpiTrendChart extends LitElement {
|
|
7
|
+
@property({ type: Array }) data: { date: string; value: number; color?: string }[] = []
|
|
8
|
+
@property({ type: String }) valueKey: string = 'value'
|
|
9
|
+
@property({ type: String }) dateKey: string = 'date'
|
|
10
|
+
@property({ type: Number }) width: number = 400
|
|
11
|
+
@property({ type: Number }) height: number = 200
|
|
12
|
+
@property({ type: String }) lineColor: string = '#2196f3'
|
|
13
|
+
@property({ type: Number }) strokeWidth: number = 2
|
|
14
|
+
@property({ type: Boolean }) showPoints: boolean = true
|
|
15
|
+
@property({ type: Number }) pointRadius: number = 4
|
|
16
|
+
|
|
17
|
+
private chartWidth = 0
|
|
18
|
+
private chartHeight = 0
|
|
19
|
+
private resizeObserver?: ResizeObserver
|
|
20
|
+
|
|
21
|
+
static styles = css`
|
|
22
|
+
:host {
|
|
23
|
+
display: block;
|
|
24
|
+
width: 100%;
|
|
25
|
+
height: 100%;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
.chart-container {
|
|
29
|
+
width: 100%;
|
|
30
|
+
height: 100%;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
.trend-line {
|
|
34
|
+
fill: none;
|
|
35
|
+
stroke-linecap: round;
|
|
36
|
+
stroke-linejoin: round;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
.data-point {
|
|
40
|
+
fill: white;
|
|
41
|
+
stroke-width: 2;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
.axis line,
|
|
45
|
+
.axis path {
|
|
46
|
+
stroke: #ddd;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
.axis text {
|
|
50
|
+
font-size: 10px;
|
|
51
|
+
fill: #666;
|
|
52
|
+
}
|
|
53
|
+
`
|
|
54
|
+
|
|
55
|
+
render() {
|
|
56
|
+
return html`
|
|
57
|
+
<div class="chart-container">
|
|
58
|
+
<svg
|
|
59
|
+
id="trend-chart"
|
|
60
|
+
width=${this.chartWidth || this.width}
|
|
61
|
+
height=${this.chartHeight || this.height}
|
|
62
|
+
viewBox="0 0 ${this.chartWidth || this.width} ${this.chartHeight || this.height}"
|
|
63
|
+
preserveAspectRatio="xMidYMid meet"
|
|
64
|
+
></svg>
|
|
65
|
+
</div>
|
|
66
|
+
`
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
connectedCallback() {
|
|
70
|
+
super.connectedCallback()
|
|
71
|
+
this.resizeObserver = new ResizeObserver(entries => {
|
|
72
|
+
for (const entry of entries) {
|
|
73
|
+
const rect = entry.contentRect
|
|
74
|
+
this.chartWidth = rect.width
|
|
75
|
+
this.chartHeight = rect.height
|
|
76
|
+
this.requestUpdate()
|
|
77
|
+
}
|
|
78
|
+
})
|
|
79
|
+
this.resizeObserver.observe(this)
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
disconnectedCallback() {
|
|
83
|
+
this.resizeObserver?.disconnect()
|
|
84
|
+
super.disconnectedCallback()
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
updated() {
|
|
88
|
+
this.drawTrendChart()
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
drawTrendChart() {
|
|
92
|
+
if (!this.data || this.data.length === 0) return
|
|
93
|
+
|
|
94
|
+
const svg = d3.select(this.renderRoot.querySelector('#trend-chart'))
|
|
95
|
+
svg.selectAll('*').remove()
|
|
96
|
+
|
|
97
|
+
const margin = { top: 20, right: 20, bottom: 30, left: 40 }
|
|
98
|
+
const width = this.chartWidth || this.width
|
|
99
|
+
const height = this.chartHeight || this.height
|
|
100
|
+
const chartWidth = width - margin.left - margin.right
|
|
101
|
+
const chartHeight = height - margin.top - margin.bottom
|
|
102
|
+
|
|
103
|
+
// 데이터 파싱
|
|
104
|
+
const parsedData = this.data.map(d => ({
|
|
105
|
+
date: new Date(d[this.dateKey]),
|
|
106
|
+
value: +d[this.valueKey],
|
|
107
|
+
color: d.color || this.lineColor
|
|
108
|
+
}))
|
|
109
|
+
|
|
110
|
+
// 스케일 설정
|
|
111
|
+
const xScale = d3
|
|
112
|
+
.scaleTime()
|
|
113
|
+
.domain(d3.extent(parsedData, d => d.date) as [Date, Date])
|
|
114
|
+
.range([0, chartWidth])
|
|
115
|
+
|
|
116
|
+
const yScale = d3
|
|
117
|
+
.scaleLinear()
|
|
118
|
+
.domain([0, d3.max(parsedData, d => d.value) || 100])
|
|
119
|
+
.range([chartHeight, 0])
|
|
120
|
+
|
|
121
|
+
// SVG 설정
|
|
122
|
+
svg.attr('width', width).attr('height', height)
|
|
123
|
+
const g = svg.append('g').attr('transform', `translate(${margin.left},${margin.top})`)
|
|
124
|
+
|
|
125
|
+
// 축 생성
|
|
126
|
+
const xAxis = d3.axisBottom(xScale).tickFormat(d3.timeFormat('%m/%d')).ticks(5)
|
|
127
|
+
|
|
128
|
+
const yAxis = d3.axisLeft(yScale).ticks(5)
|
|
129
|
+
|
|
130
|
+
g.append('g').attr('class', 'axis').attr('transform', `translate(0,${chartHeight})`).call(xAxis)
|
|
131
|
+
|
|
132
|
+
g.append('g').attr('class', 'axis').call(yAxis)
|
|
133
|
+
|
|
134
|
+
// 라인 생성기
|
|
135
|
+
const line = d3
|
|
136
|
+
.line<{ date: Date; value: number; color: string }>()
|
|
137
|
+
.x(d => xScale(d.date))
|
|
138
|
+
.y(d => yScale(d.value))
|
|
139
|
+
.curve(d3.curveMonotoneX)
|
|
140
|
+
|
|
141
|
+
// 트렌드 라인 그리기
|
|
142
|
+
g.append('path')
|
|
143
|
+
.datum(parsedData)
|
|
144
|
+
.attr('class', 'trend-line')
|
|
145
|
+
.attr('d', line as any)
|
|
146
|
+
.attr('stroke', this.lineColor)
|
|
147
|
+
.attr('stroke-width', this.strokeWidth)
|
|
148
|
+
|
|
149
|
+
// 데이터 포인트 그리기
|
|
150
|
+
if (this.showPoints) {
|
|
151
|
+
g.selectAll('.data-point')
|
|
152
|
+
.data(parsedData)
|
|
153
|
+
.enter()
|
|
154
|
+
.append('circle')
|
|
155
|
+
.attr('class', 'data-point')
|
|
156
|
+
.attr('cx', d => xScale(d.date))
|
|
157
|
+
.attr('cy', d => yScale(d.value))
|
|
158
|
+
.attr('r', this.pointRadius)
|
|
159
|
+
.attr('stroke', d => d.color)
|
|
160
|
+
.attr('fill', 'white')
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|