@things-factory/kpi 1.0.0-alpha.5 → 9.0.9
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 +74 -0
- package/client/pages/kpi/kpi-list-page.ts +97 -47
- package/client/pages/kpi/kpi-overview-sample.ts +260 -0
- package/client/pages/kpi/kpi-overview.ts +271 -0
- package/client/pages/kpi-category/kpi-category-list-page.ts +39 -50
- package/client/pages/kpi-dashboard/kpi-alert-panel.ts +114 -0
- package/client/pages/kpi-dashboard/kpi-dashboard.ts +285 -0
- package/client/pages/kpi-dashboard/kpi-grade-visualization.ts +75 -0
- package/client/pages/kpi-dashboard/kpi-history-viewer.ts +58 -0
- package/client/pages/kpi-dashboard/kpi-list-summary.ts +100 -0
- package/client/pages/kpi-dashboard/kpi-performance-summary.ts +124 -0
- package/client/pages/kpi-dashboard/kpi-value-entry.ts +78 -0
- package/client/pages/kpi-grade/kpi-grade-list-page.ts +58 -68
- package/client/pages/kpi-history/kpi-history-list-page.ts +146 -0
- package/client/pages/kpi-metric/kpi-metric-list-page.ts +67 -63
- package/client/pages/kpi-value/kpi-value-list-page.ts +73 -72
- package/client/pages/kpi-value/kpi-value-manual-entry-form.ts +168 -0
- package/client/pages/kpi-value/kpi-value-manual-entry-page.ts +173 -0
- package/client/route.ts +35 -0
- package/dist-client/pages/kpi/kpi-importer.js +16 -17
- package/dist-client/pages/kpi/kpi-importer.js.map +1 -1
- package/dist-client/pages/kpi/kpi-list-page.js +128 -68
- package/dist-client/pages/kpi/kpi-list-page.js.map +1 -1
- package/dist-client/pages/kpi/kpi-overview-sample.d.ts +36 -0
- package/dist-client/pages/kpi/kpi-overview-sample.js +264 -0
- package/dist-client/pages/kpi/kpi-overview-sample.js.map +1 -0
- package/dist-client/pages/kpi/kpi-overview.d.ts +14 -0
- package/dist-client/pages/kpi/kpi-overview.js +290 -0
- package/dist-client/pages/kpi/kpi-overview.js.map +1 -0
- package/dist-client/pages/kpi-category/kpi-category-importer.js +16 -17
- package/dist-client/pages/kpi-category/kpi-category-importer.js.map +1 -1
- package/dist-client/pages/kpi-category/kpi-category-list-page.js +70 -71
- package/dist-client/pages/kpi-category/kpi-category-list-page.js.map +1 -1
- package/dist-client/pages/kpi-dashboard/kpi-alert-panel.d.ts +18 -0
- package/dist-client/pages/kpi-dashboard/kpi-alert-panel.js +128 -0
- package/dist-client/pages/kpi-dashboard/kpi-alert-panel.js.map +1 -0
- package/dist-client/pages/kpi-dashboard/kpi-dashboard.d.ts +24 -0
- package/dist-client/pages/kpi-dashboard/kpi-dashboard.js +305 -0
- package/dist-client/pages/kpi-dashboard/kpi-dashboard.js.map +1 -0
- package/dist-client/pages/kpi-dashboard/kpi-grade-visualization.d.ts +12 -0
- package/dist-client/pages/kpi-dashboard/kpi-grade-visualization.js +82 -0
- package/dist-client/pages/kpi-dashboard/kpi-grade-visualization.js.map +1 -0
- package/dist-client/pages/kpi-dashboard/kpi-history-viewer.d.ts +11 -0
- package/dist-client/pages/kpi-dashboard/kpi-history-viewer.js +65 -0
- package/dist-client/pages/kpi-dashboard/kpi-history-viewer.js.map +1 -0
- package/dist-client/pages/kpi-dashboard/kpi-list-summary.d.ts +13 -0
- package/dist-client/pages/kpi-dashboard/kpi-list-summary.js +115 -0
- package/dist-client/pages/kpi-dashboard/kpi-list-summary.js.map +1 -0
- package/dist-client/pages/kpi-dashboard/kpi-performance-summary.d.ts +15 -0
- package/dist-client/pages/kpi-dashboard/kpi-performance-summary.js +139 -0
- package/dist-client/pages/kpi-dashboard/kpi-performance-summary.js.map +1 -0
- package/dist-client/pages/kpi-dashboard/kpi-value-entry.d.ts +7 -0
- package/dist-client/pages/kpi-dashboard/kpi-value-entry.js +86 -0
- package/dist-client/pages/kpi-dashboard/kpi-value-entry.js.map +1 -0
- package/dist-client/pages/kpi-grade/kpi-grade-importer.js +16 -17
- package/dist-client/pages/kpi-grade/kpi-grade-importer.js.map +1 -1
- package/dist-client/pages/kpi-grade/kpi-grade-list-page.js +89 -89
- package/dist-client/pages/kpi-grade/kpi-grade-list-page.js.map +1 -1
- package/dist-client/pages/kpi-history/kpi-history-list-page.d.ts +16 -0
- package/dist-client/pages/kpi-history/kpi-history-list-page.js +155 -0
- package/dist-client/pages/kpi-history/kpi-history-list-page.js.map +1 -0
- package/dist-client/pages/kpi-metric/kpi-metric-importer.js +16 -17
- package/dist-client/pages/kpi-metric/kpi-metric-importer.js.map +1 -1
- package/dist-client/pages/kpi-metric/kpi-metric-list-page.js +98 -84
- package/dist-client/pages/kpi-metric/kpi-metric-list-page.js.map +1 -1
- package/dist-client/pages/kpi-value/kpi-value-importer.js +16 -17
- package/dist-client/pages/kpi-value/kpi-value-importer.js.map +1 -1
- package/dist-client/pages/kpi-value/kpi-value-list-page.js +104 -93
- package/dist-client/pages/kpi-value/kpi-value-list-page.js.map +1 -1
- package/dist-client/pages/kpi-value/kpi-value-manual-entry-form.d.ts +14 -0
- package/dist-client/pages/kpi-value/kpi-value-manual-entry-form.js +201 -0
- package/dist-client/pages/kpi-value/kpi-value-manual-entry-form.js.map +1 -0
- package/dist-client/pages/kpi-value/kpi-value-manual-entry-page.d.ts +41 -0
- package/dist-client/pages/kpi-value/kpi-value-manual-entry-page.js +180 -0
- package/dist-client/pages/kpi-value/kpi-value-manual-entry-page.js.map +1 -0
- package/dist-client/route.d.ts +1 -1
- package/dist-client/route.js +27 -0
- package/dist-client/route.js.map +1 -1
- package/dist-client/tsconfig.tsbuildinfo +1 -1
- package/dist-server/index.d.ts +2 -1
- package/dist-server/index.js +2 -1
- package/dist-server/index.js.map +1 -1
- package/dist-server/migrations/1752188906708-SeedKpiCategory.d.ts +5 -0
- package/dist-server/migrations/1752188906708-SeedKpiCategory.js +56 -0
- package/dist-server/migrations/1752188906708-SeedKpiCategory.js.map +1 -0
- package/dist-server/migrations/1752190849681-SeedKpi.d.ts +5 -0
- package/dist-server/migrations/1752190849681-SeedKpi.js +107 -0
- package/dist-server/migrations/1752190849681-SeedKpi.js.map +1 -0
- package/dist-server/migrations/1752191090459-SeedKpiGrade.d.ts +5 -0
- package/dist-server/migrations/1752191090459-SeedKpiGrade.js +271 -0
- package/dist-server/migrations/1752191090459-SeedKpiGrade.js.map +1 -0
- package/dist-server/migrations/index.d.ts +1 -0
- package/dist-server/migrations/index.js +12 -0
- package/dist-server/migrations/index.js.map +1 -0
- package/dist-server/routes.d.ts +1 -0
- package/dist-server/routes.js +48 -0
- package/dist-server/routes.js.map +1 -1
- package/dist-server/service/index.d.ts +3 -4
- package/dist-server/service/index.js +4 -6
- package/dist-server/service/index.js.map +1 -1
- package/dist-server/service/kpi/aggregate-kpi.d.ts +8 -0
- package/dist-server/service/kpi/aggregate-kpi.js +88 -0
- package/dist-server/service/kpi/aggregate-kpi.js.map +1 -0
- package/dist-server/service/kpi/event-subscriber.d.ts +2 -0
- package/dist-server/service/kpi/event-subscriber.js +10 -0
- package/dist-server/service/kpi/event-subscriber.js.map +1 -1
- package/dist-server/service/kpi/index.d.ts +2 -1
- package/dist-server/service/kpi/kpi-formula.service.d.ts +24 -0
- package/dist-server/service/kpi/kpi-formula.service.js +64 -0
- package/dist-server/service/kpi/kpi-formula.service.js.map +1 -0
- package/dist-server/service/kpi/kpi-history.d.ts +6 -2
- package/dist-server/service/kpi/kpi-history.js +29 -11
- package/dist-server/service/kpi/kpi-history.js.map +1 -1
- package/dist-server/service/kpi/kpi-mutation.d.ts +2 -0
- package/dist-server/service/kpi/kpi-mutation.js +215 -10
- package/dist-server/service/kpi/kpi-mutation.js.map +1 -1
- package/dist-server/service/kpi/kpi-query.d.ts +10 -0
- package/dist-server/service/kpi/kpi-query.js +87 -3
- package/dist-server/service/kpi/kpi-query.js.map +1 -1
- package/dist-server/service/kpi/kpi-type.d.ts +14 -3
- package/dist-server/service/kpi/kpi-type.js +81 -18
- package/dist-server/service/kpi/kpi-type.js.map +1 -1
- package/dist-server/service/kpi/kpi.d.ts +14 -3
- package/dist-server/service/kpi/kpi.js +96 -19
- package/dist-server/service/kpi/kpi.js.map +1 -1
- package/dist-server/service/kpi-alert/index.d.ts +2 -0
- package/dist-server/service/kpi-alert/index.js +6 -0
- package/dist-server/service/kpi-alert/index.js.map +1 -0
- package/dist-server/service/kpi-alert/kpi-alert-query.d.ts +4 -0
- package/dist-server/service/kpi-alert/kpi-alert-query.js +71 -0
- package/dist-server/service/kpi-alert/kpi-alert-query.js.map +1 -0
- package/dist-server/service/kpi-alert/kpi-alert-type.d.ts +8 -0
- package/dist-server/service/kpi-alert/kpi-alert-type.js +33 -0
- package/dist-server/service/kpi-alert/kpi-alert-type.js.map +1 -0
- package/dist-server/service/kpi-category/kpi-category-mutation.d.ts +1 -1
- package/dist-server/service/kpi-category/kpi-category-mutation.js +36 -10
- package/dist-server/service/kpi-category/kpi-category-mutation.js.map +1 -1
- package/dist-server/service/kpi-category/kpi-category-query.d.ts +2 -0
- package/dist-server/service/kpi-category/kpi-category-query.js +18 -2
- package/dist-server/service/kpi-category/kpi-category-query.js.map +1 -1
- package/dist-server/service/kpi-category/kpi-category-type.d.ts +3 -5
- package/dist-server/service/kpi-category/kpi-category-type.js +16 -20
- package/dist-server/service/kpi-category/kpi-category-type.js.map +1 -1
- package/dist-server/service/kpi-category/kpi-category.d.ts +7 -9
- package/dist-server/service/kpi-category/kpi-category.js +40 -38
- package/dist-server/service/kpi-category/kpi-category.js.map +1 -1
- package/dist-server/service/kpi-grade/index.d.ts +1 -2
- package/dist-server/service/kpi-grade/index.js +2 -4
- package/dist-server/service/kpi-grade/index.js.map +1 -1
- package/dist-server/service/kpi-grade/kpi-grade-mutation.d.ts +1 -1
- package/dist-server/service/kpi-grade/kpi-grade-mutation.js +33 -10
- package/dist-server/service/kpi-grade/kpi-grade-mutation.js.map +1 -1
- package/dist-server/service/kpi-grade/kpi-grade-query.js +2 -2
- package/dist-server/service/kpi-grade/kpi-grade-query.js.map +1 -1
- package/dist-server/service/kpi-grade/kpi-grade-type.d.ts +13 -4
- package/dist-server/service/kpi-grade/kpi-grade-type.js +54 -18
- package/dist-server/service/kpi-grade/kpi-grade-type.js.map +1 -1
- package/dist-server/service/kpi-grade/kpi-grade.d.ts +8 -8
- package/dist-server/service/kpi-grade/kpi-grade.js +48 -40
- package/dist-server/service/kpi-grade/kpi-grade.js.map +1 -1
- package/dist-server/service/kpi-metric/aggregate-kpi-metric.d.ts +8 -0
- package/dist-server/service/kpi-metric/aggregate-kpi-metric.js +134 -0
- package/dist-server/service/kpi-metric/aggregate-kpi-metric.js.map +1 -0
- package/dist-server/service/kpi-metric/index.d.ts +1 -2
- package/dist-server/service/kpi-metric/index.js +2 -4
- package/dist-server/service/kpi-metric/index.js.map +1 -1
- package/dist-server/service/kpi-metric/kpi-metric-mutation.d.ts +1 -1
- package/dist-server/service/kpi-metric/kpi-metric-mutation.js +139 -13
- package/dist-server/service/kpi-metric/kpi-metric-mutation.js.map +1 -1
- package/dist-server/service/kpi-metric/kpi-metric-query.js +3 -3
- package/dist-server/service/kpi-metric/kpi-metric-query.js.map +1 -1
- package/dist-server/service/kpi-metric/kpi-metric-type.d.ts +17 -4
- package/dist-server/service/kpi-metric/kpi-metric-type.js +69 -11
- package/dist-server/service/kpi-metric/kpi-metric-type.js.map +1 -1
- package/dist-server/service/kpi-metric/kpi-metric.d.ts +11 -8
- package/dist-server/service/kpi-metric/kpi-metric.js +62 -37
- package/dist-server/service/kpi-metric/kpi-metric.js.map +1 -1
- package/dist-server/service/kpi-value/kpi-value-mutation.js +66 -11
- package/dist-server/service/kpi-value/kpi-value-mutation.js.map +1 -1
- package/dist-server/service/kpi-value/kpi-value-query.d.ts +2 -0
- package/dist-server/service/kpi-value/kpi-value-query.js +15 -2
- package/dist-server/service/kpi-value/kpi-value-query.js.map +1 -1
- package/dist-server/service/kpi-value/kpi-value-type.d.ts +19 -10
- package/dist-server/service/kpi-value/kpi-value-type.js +87 -24
- package/dist-server/service/kpi-value/kpi-value-type.js.map +1 -1
- package/dist-server/service/kpi-value/kpi-value.d.ts +15 -9
- package/dist-server/service/kpi-value/kpi-value.js +86 -32
- package/dist-server/service/kpi-value/kpi-value.js.map +1 -1
- package/dist-server/tsconfig.tsbuildinfo +1 -1
- package/implement-plan/01-entity-type-resolver.md +57 -0
- package/implement-plan/02-dataset-integration.md +35 -0
- package/implement-plan/03-kpivalue-automation.md +34 -0
- package/implement-plan/04-version-history.md +33 -0
- package/implement-plan/05-grade-visualization.md +33 -0
- package/implement-plan/06-graphql-frontend.md +30 -0
- package/implement-plan/07-test-docs.md +35 -0
- package/implement-plan/TODO.md +41 -0
- package/package.json +18 -6
- package/server/index.ts +2 -1
- package/server/migrations/1752188906708-SeedKpiCategory.ts +61 -0
- package/server/migrations/1752190849681-SeedKpi.ts +112 -0
- package/server/migrations/1752191090459-SeedKpiGrade.ts +270 -0
- package/server/migrations/index.ts +9 -0
- package/server/routes.ts +55 -0
- package/server/service/index.ts +4 -6
- package/server/service/kpi/aggregate-kpi.ts +82 -0
- package/server/service/kpi/event-subscriber.ts +12 -0
- package/server/service/kpi/kpi-formula.service.ts +63 -0
- package/server/service/kpi/kpi-history.ts +29 -21
- package/server/service/kpi/kpi-mutation.ts +194 -12
- package/server/service/kpi/kpi-query.ts +57 -2
- package/server/service/kpi/kpi-type.ts +72 -19
- package/server/service/kpi/kpi.ts +96 -20
- package/server/service/kpi-alert/index.ts +3 -0
- package/server/service/kpi-alert/kpi-alert-query.ts +59 -0
- package/server/service/kpi-alert/kpi-alert-type.ts +20 -0
- package/server/service/kpi-category/kpi-category-mutation.ts +17 -4
- package/server/service/kpi-category/kpi-category-query.ts +20 -3
- package/server/service/kpi-category/kpi-category-type.ts +17 -19
- package/server/service/kpi-category/kpi-category.ts +39 -37
- package/server/service/kpi-grade/index.ts +2 -4
- package/server/service/kpi-grade/kpi-grade-mutation.ts +13 -4
- package/server/service/kpi-grade/kpi-grade-query.ts +5 -2
- package/server/service/kpi-grade/kpi-grade-type.ts +46 -19
- package/server/service/kpi-grade/kpi-grade.ts +44 -38
- package/server/service/kpi-metric/aggregate-kpi-metric.ts +128 -0
- package/server/service/kpi-metric/index.ts +2 -4
- package/server/service/kpi-metric/kpi-metric-mutation.ts +123 -7
- package/server/service/kpi-metric/kpi-metric-query.ts +9 -3
- package/server/service/kpi-metric/kpi-metric-type.ts +56 -13
- package/server/service/kpi-metric/kpi-metric.ts +55 -35
- package/server/service/kpi-value/kpi-value-mutation.ts +52 -14
- package/server/service/kpi-value/kpi-value-query.ts +12 -2
- package/server/service/kpi-value/kpi-value-type.ts +80 -25
- package/server/service/kpi-value/kpi-value.ts +81 -29
- package/things-factory.config.js +12 -4
- package/translations/en.json +12 -1
- package/translations/ja.json +12 -1
- package/translations/ko.json +12 -1
- package/translations/ms.json +12 -1
- package/translations/zh.json +12 -1
- package/client/pages/kpe-metric/kpe-metric-importer.ts +0 -90
- package/client/pages/kpe-metric/kpe-metric-list-page.ts +0 -398
- package/client/pages/kpi-formula/kpi-formula-importer.ts +0 -90
- package/client/pages/kpi-formula/kpi-formula-list-page.ts +0 -398
- package/client/pages/metric/metric-importer.ts +0 -90
- package/client/pages/metric/metric-list-page.ts +0 -398
- package/dist-client/pages/kpe-metric/kpe-metric-importer.d.ts +0 -23
- package/dist-client/pages/kpe-metric/kpe-metric-importer.js +0 -93
- package/dist-client/pages/kpe-metric/kpe-metric-importer.js.map +0 -1
- package/dist-client/pages/kpe-metric/kpe-metric-list-page.d.ts +0 -66
- package/dist-client/pages/kpe-metric/kpe-metric-list-page.js +0 -370
- package/dist-client/pages/kpe-metric/kpe-metric-list-page.js.map +0 -1
- package/dist-client/pages/kpi-formula/kpi-formula-importer.d.ts +0 -23
- package/dist-client/pages/kpi-formula/kpi-formula-importer.js +0 -93
- package/dist-client/pages/kpi-formula/kpi-formula-importer.js.map +0 -1
- package/dist-client/pages/kpi-formula/kpi-formula-list-page.d.ts +0 -66
- package/dist-client/pages/kpi-formula/kpi-formula-list-page.js +0 -370
- package/dist-client/pages/kpi-formula/kpi-formula-list-page.js.map +0 -1
- package/dist-client/pages/metric/metric-importer.d.ts +0 -23
- package/dist-client/pages/metric/metric-importer.js +0 -93
- package/dist-client/pages/metric/metric-importer.js.map +0 -1
- package/dist-client/pages/metric/metric-list-page.d.ts +0 -66
- package/dist-client/pages/metric/metric-list-page.js +0 -370
- package/dist-client/pages/metric/metric-list-page.js.map +0 -1
- package/dist-server/service/kpi-formula/event-subscriber.d.ts +0 -7
- package/dist-server/service/kpi-formula/event-subscriber.js +0 -21
- package/dist-server/service/kpi-formula/event-subscriber.js.map +0 -1
- package/dist-server/service/kpi-formula/index.d.ts +0 -7
- package/dist-server/service/kpi-formula/index.js +0 -12
- package/dist-server/service/kpi-formula/index.js.map +0 -1
- package/dist-server/service/kpi-formula/kpi-formula-history.d.ts +0 -25
- package/dist-server/service/kpi-formula/kpi-formula-history.js +0 -128
- package/dist-server/service/kpi-formula/kpi-formula-history.js.map +0 -1
- package/dist-server/service/kpi-formula/kpi-formula-mutation.d.ts +0 -10
- package/dist-server/service/kpi-formula/kpi-formula-mutation.js +0 -128
- package/dist-server/service/kpi-formula/kpi-formula-mutation.js.map +0 -1
- package/dist-server/service/kpi-formula/kpi-formula-query.d.ts +0 -11
- package/dist-server/service/kpi-formula/kpi-formula-query.js +0 -79
- package/dist-server/service/kpi-formula/kpi-formula-query.js.map +0 -1
- package/dist-server/service/kpi-formula/kpi-formula-type.d.ts +0 -20
- package/dist-server/service/kpi-formula/kpi-formula-type.js +0 -77
- package/dist-server/service/kpi-formula/kpi-formula-type.js.map +0 -1
- package/dist-server/service/kpi-formula/kpi-formula.d.ts +0 -24
- package/dist-server/service/kpi-formula/kpi-formula.js +0 -109
- package/dist-server/service/kpi-formula/kpi-formula.js.map +0 -1
- package/dist-server/service/kpi-grade/event-subscriber.d.ts +0 -7
- package/dist-server/service/kpi-grade/event-subscriber.js +0 -21
- package/dist-server/service/kpi-grade/event-subscriber.js.map +0 -1
- package/dist-server/service/kpi-grade/kpi-grade-history.d.ts +0 -25
- package/dist-server/service/kpi-grade/kpi-grade-history.js +0 -128
- package/dist-server/service/kpi-grade/kpi-grade-history.js.map +0 -1
- package/dist-server/service/kpi-metric/event-subscriber.d.ts +0 -7
- package/dist-server/service/kpi-metric/event-subscriber.js +0 -21
- package/dist-server/service/kpi-metric/event-subscriber.js.map +0 -1
- package/dist-server/service/kpi-metric/kpi-metric-history.d.ts +0 -25
- package/dist-server/service/kpi-metric/kpi-metric-history.js +0 -128
- package/dist-server/service/kpi-metric/kpi-metric-history.js.map +0 -1
- package/server/service/kpi-formula/event-subscriber.ts +0 -17
- package/server/service/kpi-formula/index.ts +0 -9
- package/server/service/kpi-formula/kpi-formula-history.ts +0 -116
- package/server/service/kpi-formula/kpi-formula-mutation.ts +0 -137
- package/server/service/kpi-formula/kpi-formula-query.ts +0 -48
- package/server/service/kpi-formula/kpi-formula-type.ts +0 -55
- package/server/service/kpi-formula/kpi-formula.ts +0 -95
- package/server/service/kpi-grade/event-subscriber.ts +0 -17
- package/server/service/kpi-grade/kpi-grade-history.ts +0 -116
- package/server/service/kpi-metric/event-subscriber.ts +0 -17
- package/server/service/kpi-metric/kpi-metric-history.ts +0 -116
|
@@ -5,16 +5,28 @@ import { getRepository } from '@things-factory/shell'
|
|
|
5
5
|
import { createAttachment, deleteAttachmentsByRef } from '@things-factory/attachment-base'
|
|
6
6
|
import { Kpi } from './kpi'
|
|
7
7
|
import { NewKpi, KpiPatch } from './kpi-type'
|
|
8
|
+
import { KpiCategory } from '../kpi-category/kpi-category'
|
|
9
|
+
import { KpiHistory } from './kpi-history'
|
|
10
|
+
import { KpiFormulaService } from './kpi-formula.service'
|
|
11
|
+
import { Application, CallbackBase, registerSchedule, unregisterSchedule } from '@things-factory/scheduler-client'
|
|
8
12
|
|
|
9
13
|
@Resolver(Kpi)
|
|
10
14
|
export class KpiMutation {
|
|
11
15
|
@Directive('@transaction')
|
|
12
|
-
@Mutation(returns => Kpi, { description: '
|
|
13
|
-
async createKpi(
|
|
16
|
+
@Mutation(returns => Kpi, { description: 'Create a new KPI with the provided details.' })
|
|
17
|
+
async createKpi(
|
|
18
|
+
@Arg('kpi', { description: 'Input object containing details for the new KPI.' }) kpi: NewKpi,
|
|
19
|
+
@Ctx() context: ResolverContext
|
|
20
|
+
): Promise<Kpi> {
|
|
14
21
|
const { domain, user, tx } = context.state
|
|
15
22
|
|
|
23
|
+
let category = kpi.categoryId
|
|
24
|
+
? await getRepository(KpiCategory).findOne({ where: { id: kpi.categoryId } })
|
|
25
|
+
: undefined
|
|
26
|
+
|
|
16
27
|
const result = await getRepository(Kpi, tx).save({
|
|
17
28
|
...kpi,
|
|
29
|
+
category,
|
|
18
30
|
domain,
|
|
19
31
|
creator: user,
|
|
20
32
|
updater: user
|
|
@@ -34,16 +46,57 @@ export class KpiMutation {
|
|
|
34
46
|
)
|
|
35
47
|
}
|
|
36
48
|
|
|
49
|
+
if (kpi.formula) {
|
|
50
|
+
const formulaService = new KpiFormulaService()
|
|
51
|
+
const result = await formulaService.validateFormula(kpi.formula)
|
|
52
|
+
if (!result.valid) {
|
|
53
|
+
throw new Error(result.errors.join('\n'))
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// KPI 스케줄러 등록
|
|
58
|
+
if (kpi.schedule) {
|
|
59
|
+
const handle = await registerSchedule({
|
|
60
|
+
name: result.name,
|
|
61
|
+
client: {
|
|
62
|
+
application: Application,
|
|
63
|
+
group: `${domain.id}`,
|
|
64
|
+
type: 'kpi',
|
|
65
|
+
key: result.id,
|
|
66
|
+
operation: 'schedule'
|
|
67
|
+
},
|
|
68
|
+
type: 'cron',
|
|
69
|
+
schedule: kpi.schedule,
|
|
70
|
+
timezone: kpi.timezone,
|
|
71
|
+
task: {
|
|
72
|
+
type: 'rest',
|
|
73
|
+
connection: {
|
|
74
|
+
host: `${CallbackBase}/callback-schedule-for-kpi`,
|
|
75
|
+
headers: {
|
|
76
|
+
'Content-Type': 'application/json',
|
|
77
|
+
accept: '*/*'
|
|
78
|
+
}
|
|
79
|
+
},
|
|
80
|
+
data: {
|
|
81
|
+
domainId: domain.id,
|
|
82
|
+
kpiId: result.id
|
|
83
|
+
},
|
|
84
|
+
history_check: true,
|
|
85
|
+
failed_policy: 'retry_dlq',
|
|
86
|
+
max_retry_count: 3,
|
|
87
|
+
retry_period: 60
|
|
88
|
+
}
|
|
89
|
+
})
|
|
90
|
+
result.scheduleId = handle
|
|
91
|
+
await getRepository(Kpi, tx).save(result)
|
|
92
|
+
}
|
|
93
|
+
|
|
37
94
|
return result
|
|
38
95
|
}
|
|
39
96
|
|
|
40
97
|
@Directive('@transaction')
|
|
41
98
|
@Mutation(returns => Kpi, { description: 'To modify Kpi information' })
|
|
42
|
-
async updateKpi(
|
|
43
|
-
@Arg('id') id: string,
|
|
44
|
-
@Arg('patch') patch: KpiPatch,
|
|
45
|
-
@Ctx() context: ResolverContext
|
|
46
|
-
): Promise<Kpi> {
|
|
99
|
+
async updateKpi(@Arg('id') id: string, @Arg('patch') patch: KpiPatch, @Ctx() context: ResolverContext): Promise<Kpi> {
|
|
47
100
|
const { domain, user, tx } = context.state
|
|
48
101
|
|
|
49
102
|
const repository = getRepository(Kpi, tx)
|
|
@@ -51,9 +104,14 @@ export class KpiMutation {
|
|
|
51
104
|
where: { domain: { id: domain.id }, id }
|
|
52
105
|
})
|
|
53
106
|
|
|
107
|
+
let category = patch.categoryId
|
|
108
|
+
? await getRepository(KpiCategory).findOne({ where: { id: patch.categoryId } })
|
|
109
|
+
: kpi.category
|
|
110
|
+
|
|
54
111
|
const result = await repository.save({
|
|
55
112
|
...kpi,
|
|
56
113
|
...patch,
|
|
114
|
+
category,
|
|
57
115
|
updater: user
|
|
58
116
|
})
|
|
59
117
|
|
|
@@ -72,11 +130,66 @@ export class KpiMutation {
|
|
|
72
130
|
)
|
|
73
131
|
}
|
|
74
132
|
|
|
133
|
+
if (patch.formula) {
|
|
134
|
+
const formulaService = new KpiFormulaService()
|
|
135
|
+
const result = await formulaService.validateFormula(patch.formula)
|
|
136
|
+
if (!result.valid) {
|
|
137
|
+
throw new Error(result.errors.join('\n'))
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// KPI 스케줄러 등록/해제 갱신
|
|
142
|
+
if (patch.schedule) {
|
|
143
|
+
// 기존 스케줄러 해제
|
|
144
|
+
if (kpi.scheduleId) {
|
|
145
|
+
await unregisterSchedule(kpi.scheduleId)
|
|
146
|
+
}
|
|
147
|
+
// 새 스케줄러 등록
|
|
148
|
+
const handle = await registerSchedule({
|
|
149
|
+
name: result.name,
|
|
150
|
+
client: {
|
|
151
|
+
application: Application,
|
|
152
|
+
group: `${domain.id}`,
|
|
153
|
+
type: 'kpi',
|
|
154
|
+
key: result.id,
|
|
155
|
+
operation: 'schedule'
|
|
156
|
+
},
|
|
157
|
+
type: 'cron',
|
|
158
|
+
schedule: patch.schedule,
|
|
159
|
+
timezone: patch.timezone,
|
|
160
|
+
task: {
|
|
161
|
+
type: 'rest',
|
|
162
|
+
connection: {
|
|
163
|
+
host: `${CallbackBase}/callback-schedule-for-kpi`,
|
|
164
|
+
headers: {
|
|
165
|
+
'Content-Type': 'application/json',
|
|
166
|
+
accept: '*/*'
|
|
167
|
+
}
|
|
168
|
+
},
|
|
169
|
+
data: {
|
|
170
|
+
domainId: domain.id,
|
|
171
|
+
kpiId: result.id
|
|
172
|
+
},
|
|
173
|
+
history_check: true,
|
|
174
|
+
failed_policy: 'retry_dlq',
|
|
175
|
+
max_retry_count: 3,
|
|
176
|
+
retry_period: 60
|
|
177
|
+
}
|
|
178
|
+
})
|
|
179
|
+
result.scheduleId = handle
|
|
180
|
+
await repository.save(result)
|
|
181
|
+
} else if (patch.schedule === null && kpi.scheduleId) {
|
|
182
|
+
// 스케줄 해제 요청
|
|
183
|
+
await unregisterSchedule(kpi.scheduleId)
|
|
184
|
+
result.scheduleId = null
|
|
185
|
+
await repository.save(result)
|
|
186
|
+
}
|
|
187
|
+
|
|
75
188
|
return result
|
|
76
189
|
}
|
|
77
190
|
|
|
78
191
|
@Directive('@transaction')
|
|
79
|
-
@Mutation(returns =>
|
|
192
|
+
@Mutation(returns => Boolean, { description: "To modify multiple Kpis' information" })
|
|
80
193
|
async updateMultipleKpi(
|
|
81
194
|
@Arg('patches', type => [KpiPatch]) patches: KpiPatch[],
|
|
82
195
|
@Ctx() context: ResolverContext
|
|
@@ -155,6 +268,11 @@ export class KpiMutation {
|
|
|
155
268
|
async deleteKpi(@Arg('id') id: string, @Ctx() context: ResolverContext): Promise<boolean> {
|
|
156
269
|
const { domain, tx } = context.state
|
|
157
270
|
|
|
271
|
+
const kpi = await getRepository(Kpi, tx).findOne({ where: { domain: { id: domain.id }, id } })
|
|
272
|
+
if (kpi && kpi.scheduleId) {
|
|
273
|
+
await unregisterSchedule(kpi.scheduleId)
|
|
274
|
+
}
|
|
275
|
+
|
|
158
276
|
await getRepository(Kpi, tx).delete({ domain: { id: domain.id }, id })
|
|
159
277
|
await deleteAttachmentsByRef(null, { refBys: [id] }, context)
|
|
160
278
|
|
|
@@ -163,10 +281,7 @@ export class KpiMutation {
|
|
|
163
281
|
|
|
164
282
|
@Directive('@transaction')
|
|
165
283
|
@Mutation(returns => Boolean, { description: 'To delete multiple Kpis' })
|
|
166
|
-
async deleteKpis(
|
|
167
|
-
@Arg('ids', type => [String]) ids: string[],
|
|
168
|
-
@Ctx() context: ResolverContext
|
|
169
|
-
): Promise<boolean> {
|
|
284
|
+
async deleteKpis(@Arg('ids', type => [String]) ids: string[], @Ctx() context: ResolverContext): Promise<boolean> {
|
|
170
285
|
const { domain, tx } = context.state
|
|
171
286
|
|
|
172
287
|
await getRepository(Kpi, tx).delete({
|
|
@@ -195,4 +310,71 @@ export class KpiMutation {
|
|
|
195
310
|
|
|
196
311
|
return true
|
|
197
312
|
}
|
|
313
|
+
|
|
314
|
+
@Directive('@transaction')
|
|
315
|
+
@Mutation(returns => Kpi, { description: 'Release a KPI and create a version history.' })
|
|
316
|
+
async releaseKpi(@Arg('id') id: string, @Ctx() context: ResolverContext): Promise<Kpi> {
|
|
317
|
+
const { domain, user, tx } = context.state
|
|
318
|
+
const repository = getRepository(Kpi, tx)
|
|
319
|
+
const historyRepository = getRepository(KpiHistory, tx)
|
|
320
|
+
|
|
321
|
+
const kpi = await repository.findOne({
|
|
322
|
+
where: { domain: { id: domain.id }, id }
|
|
323
|
+
})
|
|
324
|
+
if (!kpi) throw `KPI given id(${id}) is not found`
|
|
325
|
+
if (kpi.state == 'RELEASE') throw `KPI given id(${id}) is already released`
|
|
326
|
+
|
|
327
|
+
// 히스토리에서 max version 가져오기
|
|
328
|
+
const maxHistory = await historyRepository
|
|
329
|
+
.createQueryBuilder('history')
|
|
330
|
+
.select('MAX(history.version)', 'max')
|
|
331
|
+
.where('history.originalId = :id', { id: kpi.id })
|
|
332
|
+
.getRawOne()
|
|
333
|
+
const nextVersion = maxHistory?.max ? Number(maxHistory.max) + 1 : 1
|
|
334
|
+
|
|
335
|
+
const updated = await repository.save({
|
|
336
|
+
...kpi,
|
|
337
|
+
version: nextVersion,
|
|
338
|
+
state: 'RELEASE',
|
|
339
|
+
updater: user
|
|
340
|
+
} as any)
|
|
341
|
+
return updated
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
@Directive('@transaction')
|
|
345
|
+
@Mutation(returns => Kpi, { description: 'Revert a KPI to a specific historical version.' })
|
|
346
|
+
async revertKpiVersion(
|
|
347
|
+
@Arg('id') id: string,
|
|
348
|
+
@Arg('version') version: number,
|
|
349
|
+
@Ctx() context: ResolverContext
|
|
350
|
+
): Promise<Kpi> {
|
|
351
|
+
const { domain, user, tx } = context.state
|
|
352
|
+
const repository = getRepository(Kpi, tx)
|
|
353
|
+
const historyRepository = getRepository(KpiHistory, tx)
|
|
354
|
+
|
|
355
|
+
const kpi = await repository.findOne({
|
|
356
|
+
where: { domain: { id: domain.id }, id }
|
|
357
|
+
})
|
|
358
|
+
if (!kpi) throw `KPI with id(${id}) is not found`
|
|
359
|
+
|
|
360
|
+
const kpiHistory = await historyRepository.findOne({
|
|
361
|
+
where: { domain: { id: domain.id }, originalId: id, version },
|
|
362
|
+
order: { version: 'DESC' }
|
|
363
|
+
})
|
|
364
|
+
if (!kpiHistory) throw `KPI with id:version(${id}:${version}) is not found`
|
|
365
|
+
|
|
366
|
+
const updated = await repository.save({
|
|
367
|
+
...kpi,
|
|
368
|
+
name: kpiHistory.name,
|
|
369
|
+
description: kpiHistory.description,
|
|
370
|
+
category: kpiHistory.category,
|
|
371
|
+
categoryId: kpiHistory.categoryId,
|
|
372
|
+
formula: kpiHistory.formula,
|
|
373
|
+
active: kpiHistory.active,
|
|
374
|
+
state: 'DRAFT',
|
|
375
|
+
thumbnail: kpiHistory.thumbnail,
|
|
376
|
+
updater: user
|
|
377
|
+
} as any)
|
|
378
|
+
return updated
|
|
379
|
+
}
|
|
198
380
|
}
|
|
@@ -4,11 +4,19 @@ import { Domain, getQueryBuilderFromListParams, getRepository, ListParam } from
|
|
|
4
4
|
import { User } from '@things-factory/auth-base'
|
|
5
5
|
import { Kpi } from './kpi'
|
|
6
6
|
import { KpiList } from './kpi-type'
|
|
7
|
+
import { KpiGrade } from '../kpi-grade/kpi-grade'
|
|
8
|
+
import { KpiValue } from '../kpi-value/kpi-value'
|
|
9
|
+
import { KpiHistory } from './kpi-history'
|
|
10
|
+
import { Int } from 'type-graphql'
|
|
11
|
+
import { KpiCategory } from '../kpi-category/kpi-category'
|
|
7
12
|
|
|
8
13
|
@Resolver(Kpi)
|
|
9
14
|
export class KpiQuery {
|
|
10
|
-
@Query(returns => Kpi!, { nullable: true, description: '
|
|
11
|
-
async kpi(
|
|
15
|
+
@Query(returns => Kpi!, { nullable: true, description: 'Fetch a single KPI by its unique identifier.' })
|
|
16
|
+
async kpi(
|
|
17
|
+
@Arg('id', { description: 'Unique identifier of the KPI to fetch.' }) id: string,
|
|
18
|
+
@Ctx() context: ResolverContext
|
|
19
|
+
): Promise<Kpi> {
|
|
12
20
|
const { domain } = context.state
|
|
13
21
|
|
|
14
22
|
return await getRepository(Kpi).findOne({
|
|
@@ -45,6 +53,11 @@ export class KpiQuery {
|
|
|
45
53
|
return attachment?.fullpath
|
|
46
54
|
}
|
|
47
55
|
|
|
56
|
+
@FieldResolver(type => [KpiGrade])
|
|
57
|
+
async grades(@Root() kpi: Kpi): Promise<KpiGrade[]> {
|
|
58
|
+
return await getRepository(KpiGrade).find({ where: { domain: { id: kpi.domainId }, kpi: { id: kpi.id } } })
|
|
59
|
+
}
|
|
60
|
+
|
|
48
61
|
@FieldResolver(type => Domain)
|
|
49
62
|
async domain(@Root() kpi: Kpi): Promise<Domain> {
|
|
50
63
|
return kpi.domainId && (await getRepository(Domain).findOneBy({ id: kpi.domainId }))
|
|
@@ -59,4 +72,46 @@ export class KpiQuery {
|
|
|
59
72
|
async creator(@Root() kpi: Kpi): Promise<User> {
|
|
60
73
|
return kpi.creatorId && (await getRepository(User).findOneBy({ id: kpi.creatorId }))
|
|
61
74
|
}
|
|
75
|
+
|
|
76
|
+
@FieldResolver(type => KpiValue, { nullable: true })
|
|
77
|
+
async value(@Root() kpi: Kpi, @Ctx() context): Promise<KpiValue | null> {
|
|
78
|
+
const { domain } = context.state
|
|
79
|
+
return await getRepository(KpiValue).findOne({
|
|
80
|
+
where: { domain: { id: domain.id }, kpi: { id: kpi.id } },
|
|
81
|
+
order: { valueDate: 'DESC' }
|
|
82
|
+
})
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
@FieldResolver(type => Number, { nullable: true })
|
|
86
|
+
targetValue(@Root() kpi: Kpi): number | undefined {
|
|
87
|
+
return kpi.vizMeta?.targetValue
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
@FieldResolver(type => String, { nullable: true })
|
|
91
|
+
unit(@Root() kpi: Kpi): string | undefined {
|
|
92
|
+
return kpi.vizMeta?.unit
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
@FieldResolver(type => KpiCategory, { nullable: true })
|
|
96
|
+
async category(@Root() kpi: Kpi): Promise<KpiCategory | null> {
|
|
97
|
+
if (!kpi.categoryId) return null
|
|
98
|
+
return await getRepository(KpiCategory).findOneBy({ id: kpi.categoryId })
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
@FieldResolver(type => [KpiHistory], { nullable: true })
|
|
102
|
+
async histories(
|
|
103
|
+
@Root() kpi: Kpi,
|
|
104
|
+
@Ctx() context,
|
|
105
|
+
@Arg('limit', type => Int, { nullable: true }) limit?: number
|
|
106
|
+
): Promise<KpiHistory[]> {
|
|
107
|
+
const { domain } = context.state
|
|
108
|
+
const qb = getRepository(KpiHistory)
|
|
109
|
+
.createQueryBuilder('history')
|
|
110
|
+
.where('history.domain = :domainId', { domainId: domain.id })
|
|
111
|
+
.andWhere('history.kpi = :kpiId', { kpiId: kpi.id })
|
|
112
|
+
.orderBy('history.updatedAt', 'DESC')
|
|
113
|
+
if (limit) qb.limit(limit)
|
|
114
|
+
else qb.limit(1)
|
|
115
|
+
return await qb.getMany()
|
|
116
|
+
}
|
|
62
117
|
}
|
|
@@ -6,49 +6,102 @@ import { ObjectRef, ScalarObject } from '@things-factory/shell'
|
|
|
6
6
|
|
|
7
7
|
import { Kpi, KpiStatus } from './kpi'
|
|
8
8
|
|
|
9
|
-
@InputType()
|
|
9
|
+
@InputType({ description: 'Input type for creating a new KPI. Used in mutations to provide KPI details.' })
|
|
10
10
|
export class NewKpi {
|
|
11
|
-
@Field()
|
|
11
|
+
@Field({ description: 'Name of the KPI.' })
|
|
12
12
|
name: string
|
|
13
13
|
|
|
14
|
-
@Field({ nullable: true })
|
|
14
|
+
@Field({ nullable: true, description: 'Detailed description of the KPI.' })
|
|
15
15
|
description?: string
|
|
16
16
|
|
|
17
|
-
@Field(type =>
|
|
18
|
-
|
|
17
|
+
@Field(type => ID, { nullable: true, description: 'ID of the category to which this KPI belongs.' })
|
|
18
|
+
categoryId?: string
|
|
19
|
+
|
|
20
|
+
@Field({ nullable: true, description: 'Calculation formula for the KPI, using metric codes and operators.' })
|
|
21
|
+
formula?: string
|
|
19
22
|
|
|
20
|
-
@Field({ nullable: true })
|
|
23
|
+
@Field({ nullable: true, description: 'Indicates whether this KPI is active and usable.' })
|
|
21
24
|
active?: boolean
|
|
22
25
|
|
|
23
|
-
@Field({ nullable: true })
|
|
24
|
-
|
|
26
|
+
@Field(type => KpiStatus, { nullable: true, description: 'Current state of the KPI (DRAFT, RELEASED, ARCHIVED).' })
|
|
27
|
+
state?: KpiStatus
|
|
25
28
|
|
|
26
|
-
@Field(type => GraphQLUpload, { nullable: true })
|
|
29
|
+
@Field(type => GraphQLUpload, { nullable: true, description: 'Thumbnail image or file for this KPI.' })
|
|
27
30
|
thumbnail?: FileUpload
|
|
31
|
+
|
|
32
|
+
@Field({
|
|
33
|
+
nullable: true,
|
|
34
|
+
description: 'Visualization type for this KPI (e.g., bar, line, gauge, progress, card, table, icon, badge, text).'
|
|
35
|
+
})
|
|
36
|
+
vizType?: string
|
|
37
|
+
|
|
38
|
+
@Field(type => ScalarObject, {
|
|
39
|
+
nullable: true,
|
|
40
|
+
description:
|
|
41
|
+
'Visualization options and metadata for this KPI, such as color, icon, thresholds, unit, decimal places, etc.'
|
|
42
|
+
})
|
|
43
|
+
vizMeta?: any
|
|
44
|
+
|
|
45
|
+
@Field({
|
|
46
|
+
nullable: true,
|
|
47
|
+
description: 'Cron schedule string for periodic KPI value aggregation (e.g., "0 0 * * *" for daily).'
|
|
48
|
+
})
|
|
49
|
+
schedule?: string
|
|
50
|
+
|
|
51
|
+
@Field({ nullable: true, description: 'Timezone for the KPI schedule.' })
|
|
52
|
+
timezone?: string
|
|
28
53
|
}
|
|
29
54
|
|
|
30
|
-
@InputType()
|
|
55
|
+
@InputType({ description: 'Input type for updating an existing KPI. Used in mutations to patch KPI details.' })
|
|
31
56
|
export class KpiPatch {
|
|
32
|
-
@Field(type => ID, { nullable: true })
|
|
57
|
+
@Field(type => ID, { nullable: true, description: 'ID of the KPI to update.' })
|
|
33
58
|
id?: string
|
|
34
59
|
|
|
35
|
-
@Field({ nullable: true })
|
|
60
|
+
@Field({ nullable: true, description: 'Name of the KPI.' })
|
|
36
61
|
name?: string
|
|
37
62
|
|
|
38
|
-
@Field({ nullable: true })
|
|
63
|
+
@Field({ nullable: true, description: 'Detailed description of the KPI.' })
|
|
39
64
|
description?: string
|
|
40
65
|
|
|
41
|
-
@Field(type =>
|
|
42
|
-
|
|
66
|
+
@Field(type => ID, { nullable: true, description: 'ID of the category to which this KPI belongs.' })
|
|
67
|
+
categoryId?: string
|
|
68
|
+
|
|
69
|
+
@Field({ nullable: true, description: 'Calculation formula for the KPI, using metric codes and operators.' })
|
|
70
|
+
formula?: string
|
|
43
71
|
|
|
44
|
-
@Field({ nullable: true })
|
|
72
|
+
@Field({ nullable: true, description: 'Indicates whether this KPI is active and usable.' })
|
|
45
73
|
active?: boolean
|
|
46
74
|
|
|
47
|
-
@Field(type =>
|
|
75
|
+
@Field(type => KpiStatus, { nullable: true, description: 'Current state of the KPI (DRAFT, RELEASED, ARCHIVED).' })
|
|
76
|
+
state?: KpiStatus
|
|
77
|
+
|
|
78
|
+
@Field(type => GraphQLUpload, { nullable: true, description: 'Thumbnail image or file for this KPI.' })
|
|
48
79
|
thumbnail?: FileUpload
|
|
49
|
-
|
|
50
|
-
@Field({
|
|
80
|
+
|
|
81
|
+
@Field({
|
|
82
|
+
nullable: true,
|
|
83
|
+
description: 'Visualization type for this KPI (e.g., bar, line, gauge, progress, card, table, icon, badge, text).'
|
|
84
|
+
})
|
|
85
|
+
vizType?: string
|
|
86
|
+
|
|
87
|
+
@Field(type => ScalarObject, {
|
|
88
|
+
nullable: true,
|
|
89
|
+
description:
|
|
90
|
+
'Visualization options and metadata for this KPI, such as color, icon, thresholds, unit, decimal places, etc.'
|
|
91
|
+
})
|
|
92
|
+
vizMeta?: any
|
|
93
|
+
|
|
94
|
+
@Field({ nullable: true, description: 'Custom flag for update operations (internal use).' })
|
|
51
95
|
cuFlag?: string
|
|
96
|
+
|
|
97
|
+
@Field({
|
|
98
|
+
nullable: true,
|
|
99
|
+
description: 'Cron schedule string for periodic KPI value aggregation (e.g., "0 0 * * *" for daily).'
|
|
100
|
+
})
|
|
101
|
+
schedule?: string
|
|
102
|
+
|
|
103
|
+
@Field({ nullable: true, description: 'Timezone for the KPI schedule.' })
|
|
104
|
+
timezone?: string
|
|
52
105
|
}
|
|
53
106
|
|
|
54
107
|
@ObjectType()
|
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
Column,
|
|
8
8
|
RelationId,
|
|
9
9
|
ManyToOne,
|
|
10
|
+
OneToMany,
|
|
10
11
|
VersionColumn,
|
|
11
12
|
PrimaryGeneratedColumn
|
|
12
13
|
} from 'typeorm'
|
|
@@ -14,15 +15,23 @@ import { ObjectType, Field, Int, ID, registerEnumType } from 'type-graphql'
|
|
|
14
15
|
|
|
15
16
|
import { Domain } from '@things-factory/shell'
|
|
16
17
|
import { User } from '@things-factory/auth-base'
|
|
18
|
+
import { KpiCategory } from '../kpi-category/kpi-category'
|
|
19
|
+
import { KpiGrade } from '../kpi-grade/kpi-grade'
|
|
20
|
+
import { config } from '@things-factory/env'
|
|
21
|
+
import { ScalarObject } from '@things-factory/shell'
|
|
22
|
+
|
|
23
|
+
const ORMCONFIG = config.get('ormconfig', {})
|
|
24
|
+
const DATABASE_TYPE = ORMCONFIG.type
|
|
17
25
|
|
|
18
26
|
export enum KpiStatus {
|
|
19
|
-
|
|
20
|
-
|
|
27
|
+
DRAFT = 'DRAFT',
|
|
28
|
+
RELEASE = 'RELEASE',
|
|
29
|
+
ARCHIVED = 'ARCHIVED'
|
|
21
30
|
}
|
|
22
31
|
|
|
23
32
|
registerEnumType(KpiStatus, {
|
|
24
33
|
name: 'KpiStatus',
|
|
25
|
-
description: '
|
|
34
|
+
description: 'State enumeration of a KPI (DRAFT, RELEASED, ARCHIVED)'
|
|
26
35
|
})
|
|
27
36
|
|
|
28
37
|
@Entity()
|
|
@@ -30,69 +39,136 @@ registerEnumType(KpiStatus, {
|
|
|
30
39
|
where: '"deleted_at" IS NULL',
|
|
31
40
|
unique: true
|
|
32
41
|
})
|
|
33
|
-
@ObjectType({
|
|
42
|
+
@ObjectType({
|
|
43
|
+
description:
|
|
44
|
+
'KPI entity. Represents a key performance indicator with calculation formula, target, category, and other attributes.'
|
|
45
|
+
})
|
|
34
46
|
export class Kpi {
|
|
35
47
|
@PrimaryGeneratedColumn('uuid')
|
|
36
|
-
@Field(type => ID)
|
|
48
|
+
@Field(type => ID, { description: 'Unique identifier for this KPI.' })
|
|
37
49
|
readonly id: string
|
|
38
|
-
|
|
50
|
+
|
|
39
51
|
@VersionColumn()
|
|
40
|
-
@Field({
|
|
52
|
+
@Field({
|
|
53
|
+
nullable: true,
|
|
54
|
+
description:
|
|
55
|
+
'Version number of the KPI. Increments on each modification. When the KPI is released, a snapshot is saved in kpi-history and the status becomes RELEASED. Editing after release increases the version and sets status to DRAFT.'
|
|
56
|
+
})
|
|
41
57
|
version?: number = 1
|
|
42
58
|
|
|
43
59
|
@ManyToOne(type => Domain)
|
|
44
|
-
@Field({ nullable: true })
|
|
60
|
+
@Field({ nullable: true, description: 'Domain (tenant) to which this KPI belongs.' })
|
|
45
61
|
domain?: Domain
|
|
46
62
|
|
|
47
63
|
@RelationId((kpi: Kpi) => kpi.domain)
|
|
64
|
+
@Field({ nullable: true, description: 'ID of the domain (tenant) for this KPI.' })
|
|
48
65
|
domainId?: string
|
|
49
66
|
|
|
50
67
|
@Column()
|
|
51
|
-
@Field({ nullable: true })
|
|
68
|
+
@Field({ nullable: true, description: 'Name of the KPI.' })
|
|
52
69
|
name?: string
|
|
53
70
|
|
|
54
71
|
@Column({ nullable: true })
|
|
55
|
-
@Field({ nullable: true })
|
|
72
|
+
@Field({ nullable: true, description: 'Detailed description of the KPI.' })
|
|
56
73
|
description?: string
|
|
57
74
|
|
|
75
|
+
@ManyToOne(() => KpiCategory, { nullable: true })
|
|
76
|
+
@Field(type => KpiCategory, { nullable: true, description: 'Category to which this KPI belongs.' })
|
|
77
|
+
category?: KpiCategory
|
|
78
|
+
|
|
79
|
+
@RelationId((kpi: Kpi) => kpi.category)
|
|
80
|
+
@Field({ nullable: true, description: 'ID of the category for this KPI.' })
|
|
81
|
+
categoryId?: string
|
|
82
|
+
|
|
83
|
+
@Column({
|
|
84
|
+
nullable: true,
|
|
85
|
+
type:
|
|
86
|
+
DATABASE_TYPE == 'mysql' || DATABASE_TYPE == 'mariadb'
|
|
87
|
+
? 'longtext'
|
|
88
|
+
: DATABASE_TYPE == 'oracle'
|
|
89
|
+
? 'clob'
|
|
90
|
+
: DATABASE_TYPE == 'mssql'
|
|
91
|
+
? 'nvarchar'
|
|
92
|
+
: 'varchar',
|
|
93
|
+
length: DATABASE_TYPE == 'mssql' ? 'MAX' : undefined
|
|
94
|
+
})
|
|
95
|
+
@Field({
|
|
96
|
+
nullable: true,
|
|
97
|
+
description:
|
|
98
|
+
'Calculation formula for the KPI. Expressed as a string using metric codes and operators, e.g., "defect_count / total_count * 100".'
|
|
99
|
+
})
|
|
100
|
+
formula?: string
|
|
101
|
+
|
|
58
102
|
@Column({ nullable: false, default: false })
|
|
59
|
-
@Field({ nullable: true })
|
|
103
|
+
@Field({ nullable: true, description: 'Indicates whether this KPI is active and usable.' })
|
|
60
104
|
active?: boolean
|
|
61
105
|
|
|
62
106
|
@Column({ nullable: true })
|
|
63
|
-
@Field({ nullable: true })
|
|
107
|
+
@Field({ nullable: true, description: 'Current state of the KPI (DRAFT, RELEASED, ARCHIVED).' })
|
|
64
108
|
state?: KpiStatus
|
|
65
109
|
|
|
66
110
|
@Column({ nullable: true })
|
|
67
|
-
@Field({
|
|
68
|
-
|
|
111
|
+
@Field({
|
|
112
|
+
nullable: true,
|
|
113
|
+
description: 'Visualization type for this KPI (e.g., bar, line, gauge, progress, card, table, icon, badge, text).'
|
|
114
|
+
})
|
|
115
|
+
vizType?: string
|
|
116
|
+
|
|
117
|
+
@Column({ type: 'json', nullable: true })
|
|
118
|
+
@Field(type => ScalarObject, {
|
|
119
|
+
nullable: true,
|
|
120
|
+
description:
|
|
121
|
+
'Visualization options and metadata for this KPI, such as color, icon, thresholds, unit, decimal places, etc.'
|
|
122
|
+
})
|
|
123
|
+
vizMeta?: any
|
|
124
|
+
|
|
125
|
+
@Column({ nullable: true })
|
|
126
|
+
@Field({
|
|
127
|
+
nullable: true,
|
|
128
|
+
description: 'Cron schedule string for periodic KPI value aggregation (e.g., "0 0 * * *" for daily).'
|
|
129
|
+
})
|
|
130
|
+
schedule?: string
|
|
131
|
+
|
|
132
|
+
@Column({ nullable: true })
|
|
133
|
+
@Field({ nullable: true, description: 'Schedule ID for the KPI (used for scheduler registration).' })
|
|
134
|
+
scheduleId?: string
|
|
135
|
+
|
|
136
|
+
@Column({ nullable: true })
|
|
137
|
+
@Field({ nullable: true, description: 'Timezone for the KPI schedule.' })
|
|
138
|
+
timezone?: string
|
|
69
139
|
|
|
70
140
|
@CreateDateColumn()
|
|
71
|
-
@Field({ nullable: true })
|
|
141
|
+
@Field({ nullable: true, description: 'Timestamp when this KPI was created.' })
|
|
72
142
|
createdAt?: Date
|
|
73
143
|
|
|
74
144
|
@UpdateDateColumn()
|
|
75
|
-
@Field({ nullable: true })
|
|
145
|
+
@Field({ nullable: true, description: 'Timestamp when this KPI was last updated.' })
|
|
76
146
|
updatedAt?: Date
|
|
77
147
|
|
|
78
148
|
@DeleteDateColumn()
|
|
79
|
-
@Field({ nullable: true })
|
|
149
|
+
@Field({ nullable: true, description: 'Timestamp when this KPI was deleted (soft delete).' })
|
|
80
150
|
deletedAt?: Date
|
|
81
151
|
|
|
82
152
|
@ManyToOne(type => User, { nullable: true })
|
|
83
|
-
@Field(type => User, { nullable: true })
|
|
153
|
+
@Field(type => User, { nullable: true, description: 'User who created this KPI.' })
|
|
84
154
|
creator?: User
|
|
85
155
|
|
|
86
156
|
@RelationId((kpi: Kpi) => kpi.creator)
|
|
157
|
+
@Field({ nullable: true, description: 'ID of the user who created this KPI.' })
|
|
87
158
|
creatorId?: string
|
|
88
159
|
|
|
89
160
|
@ManyToOne(type => User, { nullable: true })
|
|
90
|
-
@Field(type => User, { nullable: true })
|
|
161
|
+
@Field(type => User, { nullable: true, description: 'User who last updated this KPI.' })
|
|
91
162
|
updater?: User
|
|
92
163
|
|
|
93
164
|
@RelationId((kpi: Kpi) => kpi.updater)
|
|
165
|
+
@Field({ nullable: true, description: 'ID of the user who last updated this KPI.' })
|
|
94
166
|
updaterId?: string
|
|
95
167
|
|
|
96
|
-
@Field(type => String, { nullable: true })
|
|
168
|
+
@Field(type => String, { nullable: true, description: 'Thumbnail image or file path for this KPI.' })
|
|
97
169
|
thumbnail?: string
|
|
170
|
+
|
|
171
|
+
@OneToMany(type => KpiGrade, kpiGrade => kpiGrade.kpi)
|
|
172
|
+
@Field(type => [KpiGrade], { nullable: true, description: 'List of grades for this KPI.' })
|
|
173
|
+
grades?: KpiGrade[]
|
|
98
174
|
}
|