@things-factory/kpi 9.1.19 → 10.0.0-beta.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (140) hide show
  1. package/client/pages/kpi/kpi-list-page.ts +339 -525
  2. package/client/pages/kpi/kpi-tree-page.ts +135 -207
  3. package/client/pages/kpi-metric/kpi-metric-list-page.ts +146 -226
  4. package/client/pages/kpi-metric-value/kpi-metric-value-editor-page.ts +187 -295
  5. package/client/pages/kpi-metric-value/kpi-metric-value-list-page.ts +123 -194
  6. package/client/pages/kpi-metric-value/kpi-metric-value-manual-entry-page.ts +57 -91
  7. package/client/pages/kpi-statistic/kpi-statistic-editor-page.ts +180 -278
  8. package/client/pages/kpi-statistic/kpi-statistic-list-page.ts +186 -286
  9. package/client/pages/kpi-value/kpi-value-editor-page.ts +189 -292
  10. package/client/pages/kpi-value/kpi-value-list-page.ts +170 -264
  11. package/dist-client/pages/kpi/kpi-list-page.d.ts +0 -6
  12. package/dist-client/pages/kpi/kpi-list-page.js +150 -282
  13. package/dist-client/pages/kpi/kpi-list-page.js.map +1 -1
  14. package/dist-client/pages/kpi/kpi-tree-page.d.ts +1 -7
  15. package/dist-client/pages/kpi/kpi-tree-page.js +76 -127
  16. package/dist-client/pages/kpi/kpi-tree-page.js.map +1 -1
  17. package/dist-client/pages/kpi-metric/kpi-metric-list-page.d.ts +0 -6
  18. package/dist-client/pages/kpi-metric/kpi-metric-list-page.js +62 -116
  19. package/dist-client/pages/kpi-metric/kpi-metric-list-page.js.map +1 -1
  20. package/dist-client/pages/kpi-metric-value/kpi-metric-value-editor-page.d.ts +1 -7
  21. package/dist-client/pages/kpi-metric-value/kpi-metric-value-editor-page.js +82 -140
  22. package/dist-client/pages/kpi-metric-value/kpi-metric-value-editor-page.js.map +1 -1
  23. package/dist-client/pages/kpi-metric-value/kpi-metric-value-list-page.d.ts +0 -6
  24. package/dist-client/pages/kpi-metric-value/kpi-metric-value-list-page.js +54 -98
  25. package/dist-client/pages/kpi-metric-value/kpi-metric-value-list-page.js.map +1 -1
  26. package/dist-client/pages/kpi-metric-value/kpi-metric-value-manual-entry-page.d.ts +1 -7
  27. package/dist-client/pages/kpi-metric-value/kpi-metric-value-manual-entry-page.js +30 -57
  28. package/dist-client/pages/kpi-metric-value/kpi-metric-value-manual-entry-page.js.map +1 -1
  29. package/dist-client/pages/kpi-statistic/kpi-statistic-editor-page.d.ts +1 -7
  30. package/dist-client/pages/kpi-statistic/kpi-statistic-editor-page.js +91 -153
  31. package/dist-client/pages/kpi-statistic/kpi-statistic-editor-page.js.map +1 -1
  32. package/dist-client/pages/kpi-statistic/kpi-statistic-list-page.d.ts +0 -6
  33. package/dist-client/pages/kpi-statistic/kpi-statistic-list-page.js +81 -155
  34. package/dist-client/pages/kpi-statistic/kpi-statistic-list-page.js.map +1 -1
  35. package/dist-client/pages/kpi-value/kpi-value-editor-page.d.ts +1 -7
  36. package/dist-client/pages/kpi-value/kpi-value-editor-page.js +80 -136
  37. package/dist-client/pages/kpi-value/kpi-value-editor-page.js.map +1 -1
  38. package/dist-client/pages/kpi-value/kpi-value-list-page.d.ts +0 -6
  39. package/dist-client/pages/kpi-value/kpi-value-list-page.js +73 -134
  40. package/dist-client/pages/kpi-value/kpi-value-list-page.js.map +1 -1
  41. package/dist-client/tsconfig.tsbuildinfo +1 -1
  42. package/dist-server/service/index.d.ts +1 -1
  43. package/dist-server/tsconfig.tsbuildinfo +1 -1
  44. package/package.json +18 -18
  45. package/client/tsconfig.json +0 -11
  46. package/dist-server/tsconfig.json +0 -10
  47. package/server/@types/index.d.ts +0 -11
  48. package/server/calculator/evaluator.ts +0 -45
  49. package/server/calculator/functions.ts +0 -67
  50. package/server/calculator/index.ts +0 -4
  51. package/server/calculator/parser.ts +0 -137
  52. package/server/calculator/provider.ts +0 -10
  53. package/server/controllers/index.ts +0 -2
  54. package/server/controllers/kpi-metric-value-provider.ts +0 -79
  55. package/server/controllers/kpi-value-provider.ts +0 -51
  56. package/server/index.ts +0 -6
  57. package/server/migrations/1752190849680-seed-kpi-metrics.ts +0 -124
  58. package/server/migrations/1752190849681-seed-kpi.ts +0 -356
  59. package/server/migrations/1752192090123-add-grades-to-kpi.ts +0 -67
  60. package/server/migrations/1752192090124-add-kpi-statistics.ts +0 -719
  61. package/server/migrations/1752192090128-seed-kpi-org-scope.ts +0 -132
  62. package/server/migrations/1752192090129-seed-kpi-values.ts +0 -207
  63. package/server/migrations/grade-data/x11-performance-table.json +0 -962
  64. package/server/migrations/grade-data/x12-performance-table.json +0 -611
  65. package/server/migrations/grade-data/x14-performance-table.json +0 -42
  66. package/server/migrations/grade-data/x21-performance-table.json +0 -889
  67. package/server/migrations/grade-data/x22-performance-table.json +0 -1064
  68. package/server/migrations/grade-data/x23-performance-table.json +0 -42
  69. package/server/migrations/grade-data/x31-performance-table.json +0 -644
  70. package/server/migrations/grade-data/x32-performance-table.json +0 -993
  71. package/server/migrations/grade-data/x33-performance-table.json +0 -195
  72. package/server/migrations/grade-data/x34-performance-table.json +0 -12
  73. package/server/migrations/grade-data/x35-performance-table.json +0 -42
  74. package/server/migrations/grade-data/x41-performance-table.json +0 -825
  75. package/server/migrations/grade-data/x42-performance-table.json +0 -786
  76. package/server/migrations/grade-data/x43-performance-table.json +0 -12
  77. package/server/migrations/grade-data/x44-performance-table.json +0 -42
  78. package/server/migrations/grade-data/x51-performance-table.json +0 -924
  79. package/server/migrations/grade-data/x52-performance-table.json +0 -42
  80. package/server/migrations/grade-data/x61-performance-table.json +0 -261
  81. package/server/migrations/grade-data/x62-performance-table.json +0 -42
  82. package/server/migrations/index.ts +0 -9
  83. package/server/migrations/seed-data/kpi-metrics-seed.json +0 -454
  84. package/server/migrations/seed-data/kpi-org-scope-seed.json +0 -1676
  85. package/server/migrations/seed-data/kpi-scopes-seed.json +0 -121
  86. package/server/migrations/seed-data/kpi-values-seed.json +0 -402
  87. package/server/migrations/seed-data/kpis-seed.json +0 -488
  88. package/server/migrations/seed-data/scope-definitions-seed.json +0 -90
  89. package/server/routes.ts +0 -81
  90. package/server/service/index.ts +0 -51
  91. package/server/service/kpi/aggregate-kpi.ts +0 -103
  92. package/server/service/kpi/event-subscriber.ts +0 -29
  93. package/server/service/kpi/index.ts +0 -9
  94. package/server/service/kpi/kpi-formula.service.ts +0 -164
  95. package/server/service/kpi/kpi-grade.types.ts +0 -28
  96. package/server/service/kpi/kpi-history.ts +0 -126
  97. package/server/service/kpi/kpi-mutation.ts +0 -553
  98. package/server/service/kpi/kpi-query.ts +0 -224
  99. package/server/service/kpi/kpi-type.ts +0 -151
  100. package/server/service/kpi/kpi.ts +0 -254
  101. package/server/service/kpi-alert/index.ts +0 -3
  102. package/server/service/kpi-alert/kpi-alert-query.ts +0 -59
  103. package/server/service/kpi-alert/kpi-alert-type.ts +0 -20
  104. package/server/service/kpi-metric/aggregate-kpi-metric.ts +0 -132
  105. package/server/service/kpi-metric/index.ts +0 -7
  106. package/server/service/kpi-metric/kpi-metric-mutation.ts +0 -309
  107. package/server/service/kpi-metric/kpi-metric-query.ts +0 -70
  108. package/server/service/kpi-metric/kpi-metric-type.ts +0 -111
  109. package/server/service/kpi-metric/kpi-metric.ts +0 -134
  110. package/server/service/kpi-metric-value/index.ts +0 -7
  111. package/server/service/kpi-metric-value/kpi-metric-value-mutation.ts +0 -270
  112. package/server/service/kpi-metric-value/kpi-metric-value-query.ts +0 -62
  113. package/server/service/kpi-metric-value/kpi-metric-value-type.ts +0 -82
  114. package/server/service/kpi-metric-value/kpi-metric-value.ts +0 -93
  115. package/server/service/kpi-org-scope/index.ts +0 -6
  116. package/server/service/kpi-org-scope/kpi-org-scope-mutation.ts +0 -173
  117. package/server/service/kpi-org-scope/kpi-org-scope-query.ts +0 -127
  118. package/server/service/kpi-org-scope/kpi-org-scope-type.ts +0 -68
  119. package/server/service/kpi-org-scope/kpi-org-scope.ts +0 -123
  120. package/server/service/kpi-scope/index.ts +0 -11
  121. package/server/service/kpi-scope/kpi-scope-mutation.ts +0 -129
  122. package/server/service/kpi-scope/kpi-scope-query.ts +0 -63
  123. package/server/service/kpi-scope/kpi-scope-type.ts +0 -96
  124. package/server/service/kpi-scope/kpi-scope.ts +0 -143
  125. package/server/service/kpi-statistic/index.ts +0 -7
  126. package/server/service/kpi-statistic/kpi-statistic-batch.service.ts +0 -231
  127. package/server/service/kpi-statistic/kpi-statistic-calculation.service.ts +0 -410
  128. package/server/service/kpi-statistic/kpi-statistic-mutation.ts +0 -291
  129. package/server/service/kpi-statistic/kpi-statistic-query.ts +0 -146
  130. package/server/service/kpi-statistic/kpi-statistic-type.ts +0 -152
  131. package/server/service/kpi-statistic/kpi-statistic.ts +0 -199
  132. package/server/service/kpi-value/index.ts +0 -7
  133. package/server/service/kpi-value/kpi-value-mutation.ts +0 -432
  134. package/server/service/kpi-value/kpi-value-query.ts +0 -61
  135. package/server/service/kpi-value/kpi-value-score.service.ts +0 -106
  136. package/server/service/kpi-value/kpi-value-type.ts +0 -122
  137. package/server/service/kpi-value/kpi-value.ts +0 -160
  138. package/server/service/utils/value-date-util.ts +0 -119
  139. package/server/tsconfig.json +0 -10
  140. package/server/types/global.d.ts +0 -8
@@ -1,132 +0,0 @@
1
- import { MigrationInterface, QueryRunner } from 'typeorm'
2
- import { Domain, getRepository } from '@things-factory/shell'
3
- import { KpiOrgScope } from '../service/kpi-org-scope/kpi-org-scope'
4
- import * as fs from 'fs'
5
- import * as path from 'path'
6
-
7
- interface KpiOrgScopeSeedData {
8
- org: string
9
- scope01?: string
10
- scope02?: string
11
- scope03?: string
12
- scope04?: string
13
- scope05?: string
14
- name?: string
15
- entityType?: string
16
- entityId?: string
17
- entityName?: string
18
- }
19
-
20
- export class SeedKpiOrgScope1752192090128 implements MigrationInterface {
21
- name = 'SeedKpiOrgScope1752192090128'
22
-
23
- public async up(queryRunner: QueryRunner): Promise<void> {
24
- try {
25
- const repository = getRepository(KpiOrgScope)
26
- const domainRepository = getRepository(Domain)
27
-
28
- // SYSTEM 도메인 확인
29
- const domain: Domain = await domainRepository.findOneBy({ name: 'SYSTEM' })
30
- if (!domain) {
31
- console.log('❌ SYSTEM domain not found')
32
- return
33
- }
34
-
35
- // JSON 파일 읽기
36
- const seedFilePath = path.join(__dirname, 'seed-data', 'kpi-org-scope-seed.json')
37
- const seedData: KpiOrgScopeSeedData[] = JSON.parse(fs.readFileSync(seedFilePath, 'utf8'))
38
-
39
- console.log(`🏗️ Processing ${seedData.length} KPI org-scope mappings from JSON seed`)
40
-
41
- for (const orgScopeData of seedData) {
42
- try {
43
- // entityType과 entityId가 있으면 사용, 없으면 기본값 설정
44
- const entityType = orgScopeData.entityType || 'Project'
45
- const entityId = orgScopeData.entityId || `project-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`
46
- const entityName = orgScopeData.entityName || orgScopeData.name || orgScopeData.org
47
-
48
- const existingOrgScope = await repository.findOne({
49
- where: {
50
- entityType,
51
- entityId,
52
- domain: { id: domain.id }
53
- }
54
- })
55
-
56
- if (existingOrgScope) {
57
- console.log(`⚠️ KPI Org-Scope already exists: ${orgScopeData.org}`)
58
- continue
59
- }
60
-
61
- const orgScope = await repository.save({
62
- entityType,
63
- entityId,
64
- entityName,
65
- org: orgScopeData.org, // legacy field
66
- scope01: orgScopeData.scope01,
67
- scope02: orgScopeData.scope02,
68
- scope03: orgScopeData.scope03,
69
- scope04: orgScopeData.scope04,
70
- scope05: orgScopeData.scope05,
71
- domain
72
- })
73
-
74
- console.log(`✅ Created KPI Org-Scope: ${orgScope.entityName} [${orgScope.entityType}:${orgScope.entityId}] (${orgScope.scope01})`)
75
- } catch (error) {
76
- console.error(`❌ Error creating KPI Org-Scope "${orgScopeData.org}":`, error)
77
- }
78
- }
79
-
80
- console.log(`🎉 Successfully processed KPI org-scope mappings from JSON seed`)
81
- } catch (error) {
82
- console.error('❌ Error seeding KPI org-scope mappings from JSON:', error)
83
- throw error
84
- }
85
- }
86
-
87
- public async down(queryRunner: QueryRunner): Promise<void> {
88
- try {
89
- const repository = getRepository(KpiOrgScope)
90
- const domainRepository = getRepository(Domain)
91
-
92
- const domain: Domain = await domainRepository.findOneBy({ name: 'SYSTEM' })
93
- if (!domain) {
94
- console.log('⚠️ SYSTEM domain not found. Nothing to delete.')
95
- return
96
- }
97
-
98
- // JSON 파일 읽기
99
- const seedFilePath = path.join(__dirname, 'seed-data', 'kpi-org-scope-seed.json')
100
- const seedData: KpiOrgScopeSeedData[] = JSON.parse(fs.readFileSync(seedFilePath, 'utf8'))
101
-
102
- console.log(`🗑️ Cleaning up ${seedData.length} KPI org-scope mappings`)
103
-
104
- for (const orgScopeData of seedData.reverse()) {
105
- try {
106
- const entityType = orgScopeData.entityType || 'Project'
107
- const entityId = orgScopeData.entityId || `project-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`
108
-
109
- const orgScope = await repository.findOne({
110
- where: {
111
- entityType,
112
- entityId,
113
- domain: { id: domain.id }
114
- }
115
- })
116
-
117
- if (orgScope) {
118
- await repository.delete(orgScope.id)
119
- console.log(`✅ Deleted KPI Org-Scope: ${orgScopeData.org}`)
120
- }
121
- } catch (error) {
122
- console.error(`❌ Error deleting KPI Org-Scope "${orgScopeData.org}":`, error)
123
- }
124
- }
125
-
126
- console.log(`🎉 Successfully cleaned up KPI org-scope mappings`)
127
- } catch (error) {
128
- console.error('❌ Error cleaning up KPI org-scope mappings:', error)
129
- throw error
130
- }
131
- }
132
- }
@@ -1,207 +0,0 @@
1
- import { MigrationInterface, QueryRunner } from 'typeorm'
2
- import { Domain, getRepository } from '@things-factory/shell'
3
- import { KpiValue, KpiValueInputType } from '../service/kpi-value/kpi-value'
4
- import { Kpi, KpiPeriodType } from '../service/kpi/kpi'
5
- import { KpiOrgScope } from '../service/kpi-org-scope/kpi-org-scope'
6
- import * as fs from 'fs'
7
- import * as path from 'path'
8
-
9
- interface KpiValueSeedData {
10
- kpi_name: string
11
- org: string
12
- value_date: string
13
- value: number
14
- score?: number
15
- period_type: string
16
- input_type?: string
17
- source?: string
18
- }
19
-
20
- export class SeedKpiValues1752192090129 implements MigrationInterface {
21
- name = 'SeedKpiValues1752192090129'
22
-
23
- public async up(queryRunner: QueryRunner): Promise<void> {
24
- try {
25
- const repository = getRepository(KpiValue)
26
- const domainRepository = getRepository(Domain)
27
- const kpiRepository = getRepository(Kpi)
28
- const kpiOrgScopeRepository = getRepository(KpiOrgScope)
29
-
30
- // SYSTEM 도메인 확인
31
- const domain: Domain = await domainRepository.findOneBy({ name: 'SYSTEM' })
32
- if (!domain) {
33
- console.log('❌ SYSTEM domain not found')
34
- return
35
- }
36
-
37
- // JSON 파일 읽기
38
- const seedFilePath = path.join(__dirname, 'seed-data', 'kpi-values-seed.json')
39
- const seedData: KpiValueSeedData[] = JSON.parse(fs.readFileSync(seedFilePath, 'utf8'))
40
-
41
- console.log(`📊 Processing ${seedData.length} KPI values from JSON seed`)
42
-
43
- // 모든 KPI 미리 로드 (성능 최적화)
44
- const kpis = await kpiRepository.find({
45
- where: { domain: { id: domain.id } }
46
- })
47
- const kpiMap = new Map<string, Kpi>()
48
- kpis.forEach(kpi => kpiMap.set(kpi.name, kpi))
49
-
50
- // 모든 KpiOrgScope 미리 로드 (성능 최적화)
51
- const kpiOrgScopes = await kpiOrgScopeRepository.find({
52
- where: { domain: { id: domain.id } }
53
- })
54
- const kpiOrgScopeMap = new Map<string, KpiOrgScope>()
55
- kpiOrgScopes.forEach(scope => {
56
- // org 필드(legacy)와 entityName 둘 다 키로 사용
57
- if (scope.org) kpiOrgScopeMap.set(scope.org, scope)
58
- if (scope.entityName) kpiOrgScopeMap.set(scope.entityName, scope)
59
- })
60
-
61
- for (const valueData of seedData) {
62
- try {
63
- const kpi = kpiMap.get(valueData.kpi_name)
64
- if (!kpi) {
65
- console.log(`⚠️ KPI not found: ${valueData.kpi_name}`)
66
- continue
67
- }
68
-
69
- const kpiOrgScope = kpiOrgScopeMap.get(valueData.org)
70
- if (!kpiOrgScope) {
71
- console.log(`⚠️ KpiOrgScope not found: ${valueData.org}`)
72
- continue
73
- }
74
-
75
- // 기존 값 확인
76
- const existingValue = await repository.findOne({
77
- where: {
78
- kpi: { id: kpi.id },
79
- kpiOrgScope: { id: kpiOrgScope.id },
80
- valueDate: valueData.value_date,
81
- domain: { id: domain.id }
82
- }
83
- })
84
-
85
- if (existingValue) {
86
- console.log(`⚠️ KPI Value already exists: ${valueData.kpi_name} - ${valueData.org}`)
87
- continue
88
- }
89
-
90
- // KpiValueInputType 변환
91
- let inputType: KpiValueInputType | undefined
92
- if (valueData.input_type === 'MANUAL') {
93
- inputType = KpiValueInputType.MANUAL
94
- } else if (valueData.input_type === 'AUTO') {
95
- inputType = KpiValueInputType.AUTO
96
- }
97
-
98
- // KpiPeriodType 변환
99
- let periodType: KpiPeriodType = KpiPeriodType.DAY
100
- if (valueData.period_type === 'MONTH') {
101
- periodType = KpiPeriodType.MONTH
102
- } else if (valueData.period_type === 'QUARTER') {
103
- periodType = KpiPeriodType.QUARTER
104
- } else if (valueData.period_type === 'YEAR') {
105
- periodType = KpiPeriodType.YEAR
106
- }
107
-
108
- const kpiValue = await repository.save({
109
- kpi,
110
- kpiOrgScope,
111
- valueDate: valueData.value_date,
112
- value: valueData.value,
113
- score: valueData.score,
114
- periodType,
115
- inputType,
116
- source: valueData.source,
117
- version: kpi.version || 1,
118
- domain
119
- })
120
-
121
- console.log(`✅ Created KPI Value: ${valueData.kpi_name} - ${valueData.org} = ${valueData.value}`)
122
- } catch (error) {
123
- console.error(`❌ Error creating KPI Value "${valueData.kpi_name} - ${valueData.org}":`, error)
124
- }
125
- }
126
-
127
- console.log(`🎉 Successfully processed KPI values from JSON seed`)
128
- } catch (error) {
129
- console.error('❌ Error seeding KPI values from JSON:', error)
130
- throw error
131
- }
132
- }
133
-
134
- public async down(queryRunner: QueryRunner): Promise<void> {
135
- try {
136
- const repository = getRepository(KpiValue)
137
- const domainRepository = getRepository(Domain)
138
- const kpiRepository = getRepository(Kpi)
139
- const kpiOrgScopeRepository = getRepository(KpiOrgScope)
140
-
141
- const domain: Domain = await domainRepository.findOneBy({ name: 'SYSTEM' })
142
- if (!domain) {
143
- console.log('⚠️ SYSTEM domain not found. Nothing to delete.')
144
- return
145
- }
146
-
147
- // JSON 파일 읽기
148
- const seedFilePath = path.join(__dirname, 'seed-data', 'kpi-values-seed.json')
149
- const seedData: KpiValueSeedData[] = JSON.parse(fs.readFileSync(seedFilePath, 'utf8'))
150
-
151
- console.log(`🗑️ Cleaning up ${seedData.length} KPI values`)
152
-
153
- // 모든 KPI 미리 로드
154
- const kpis = await kpiRepository.find({
155
- where: { domain: { id: domain.id } }
156
- })
157
- const kpiMap = new Map<string, Kpi>()
158
- kpis.forEach(kpi => kpiMap.set(kpi.name, kpi))
159
-
160
- // 모든 KpiOrgScope 미리 로드
161
- const kpiOrgScopes = await kpiOrgScopeRepository.find({
162
- where: { domain: { id: domain.id } }
163
- })
164
- const kpiOrgScopeMap = new Map<string, KpiOrgScope>()
165
- kpiOrgScopes.forEach(scope => {
166
- // org 필드(legacy)와 entityName 둘 다 키로 사용
167
- if (scope.org) kpiOrgScopeMap.set(scope.org, scope)
168
- if (scope.entityName) kpiOrgScopeMap.set(scope.entityName, scope)
169
- })
170
-
171
- for (const valueData of seedData.reverse()) {
172
- try {
173
- const kpi = kpiMap.get(valueData.kpi_name)
174
- if (!kpi) {
175
- continue
176
- }
177
-
178
- const kpiOrgScope = kpiOrgScopeMap.get(valueData.org)
179
- if (!kpiOrgScope) {
180
- continue
181
- }
182
-
183
- const kpiValue = await repository.findOne({
184
- where: {
185
- kpi: { id: kpi.id },
186
- kpiOrgScope: { id: kpiOrgScope.id },
187
- valueDate: valueData.value_date,
188
- domain: { id: domain.id }
189
- }
190
- })
191
-
192
- if (kpiValue) {
193
- await repository.delete(kpiValue.id)
194
- console.log(`✅ Deleted KPI Value: ${valueData.kpi_name} - ${valueData.org}`)
195
- }
196
- } catch (error) {
197
- console.error(`❌ Error deleting KPI Value "${valueData.kpi_name} - ${valueData.org}":`, error)
198
- }
199
- }
200
-
201
- console.log(`🎉 Successfully cleaned up KPI values`)
202
- } catch (error) {
203
- console.error('❌ Error cleaning up KPI values:', error)
204
- throw error
205
- }
206
- }
207
- }