@things-factory/kpi 9.0.22 → 9.0.24

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 (189) hide show
  1. package/client/pages/kpi/kpi-list-page.ts +180 -22
  2. package/client/pages/kpi/kpi-viz-editor.ts +1 -1
  3. package/client/pages/kpi-category/kpi-category-list-page.ts +76 -3
  4. package/client/pages/kpi-category/kpi-category-value-calculator.ts +233 -0
  5. package/client/pages/kpi-category-value/kpi-category-value-list-page.ts +404 -0
  6. package/client/pages/kpi-metric/kpi-metric-list-page.ts +13 -1
  7. package/client/pages/kpi-metric-value/kpi-metric-value-editor-page.ts +763 -0
  8. package/client/pages/kpi-metric-value/kpi-metric-value-list-page.ts +55 -1
  9. package/client/pages/kpi-metric-value/kpi-metric-value-manual-entry-form.ts +3 -13
  10. package/client/pages/kpi-metric-value/kpi-metric-value-manual-entry-page.ts +13 -1
  11. package/client/pages/kpi-value/kpi-value-editor-page.ts +774 -0
  12. package/client/pages/kpi-value/kpi-value-list-page.ts +58 -1
  13. package/client/route.ts +16 -0
  14. package/dist-client/pages/kpi/kpi-list-page.d.ts +2 -1
  15. package/dist-client/pages/kpi/kpi-list-page.js +180 -22
  16. package/dist-client/pages/kpi/kpi-list-page.js.map +1 -1
  17. package/dist-client/pages/kpi/kpi-viz-editor.d.ts +0 -1
  18. package/dist-client/pages/kpi/kpi-viz-editor.js +1 -1
  19. package/dist-client/pages/kpi/kpi-viz-editor.js.map +1 -1
  20. package/dist-client/pages/kpi-category/kpi-category-list-page.d.ts +3 -0
  21. package/dist-client/pages/kpi-category/kpi-category-list-page.js +71 -3
  22. package/dist-client/pages/kpi-category/kpi-category-list-page.js.map +1 -1
  23. package/dist-client/pages/kpi-category/kpi-category-value-calculator.d.ts +13 -0
  24. package/dist-client/pages/kpi-category/kpi-category-value-calculator.js +256 -0
  25. package/dist-client/pages/kpi-category/kpi-category-value-calculator.js.map +1 -0
  26. package/dist-client/pages/kpi-category-value/kpi-category-value-list-page.d.ts +63 -0
  27. package/dist-client/pages/kpi-category-value/kpi-category-value-list-page.js +393 -0
  28. package/dist-client/pages/kpi-category-value/kpi-category-value-list-page.js.map +1 -0
  29. package/dist-client/pages/kpi-metric/kpi-metric-list-page.js +13 -1
  30. package/dist-client/pages/kpi-metric/kpi-metric-list-page.js.map +1 -1
  31. package/dist-client/pages/kpi-metric-value/kpi-metric-value-editor-page.d.ts +58 -0
  32. package/dist-client/pages/kpi-metric-value/kpi-metric-value-editor-page.js +736 -0
  33. package/dist-client/pages/kpi-metric-value/kpi-metric-value-editor-page.js.map +1 -0
  34. package/dist-client/pages/kpi-metric-value/kpi-metric-value-list-page.d.ts +5 -1
  35. package/dist-client/pages/kpi-metric-value/kpi-metric-value-list-page.js +50 -2
  36. package/dist-client/pages/kpi-metric-value/kpi-metric-value-list-page.js.map +1 -1
  37. package/dist-client/pages/kpi-metric-value/kpi-metric-value-manual-entry-form.js +3 -13
  38. package/dist-client/pages/kpi-metric-value/kpi-metric-value-manual-entry-form.js.map +1 -1
  39. package/dist-client/pages/kpi-metric-value/kpi-metric-value-manual-entry-page.js +13 -1
  40. package/dist-client/pages/kpi-metric-value/kpi-metric-value-manual-entry-page.js.map +1 -1
  41. package/dist-client/pages/kpi-value/kpi-value-editor-page.d.ts +55 -0
  42. package/dist-client/pages/kpi-value/kpi-value-editor-page.js +748 -0
  43. package/dist-client/pages/kpi-value/kpi-value-editor-page.js.map +1 -0
  44. package/dist-client/pages/kpi-value/kpi-value-list-page.d.ts +10 -2
  45. package/dist-client/pages/kpi-value/kpi-value-list-page.js +57 -1
  46. package/dist-client/pages/kpi-value/kpi-value-list-page.js.map +1 -1
  47. package/dist-client/route.d.ts +1 -1
  48. package/dist-client/route.js +12 -0
  49. package/dist-client/route.js.map +1 -1
  50. package/dist-client/tsconfig.tsbuildinfo +1 -1
  51. package/dist-server/calculator/evaluator.d.ts +8 -0
  52. package/dist-server/calculator/evaluator.js +42 -0
  53. package/dist-server/calculator/evaluator.js.map +1 -0
  54. package/dist-server/calculator/functions.d.ts +3 -0
  55. package/dist-server/calculator/functions.js +62 -0
  56. package/dist-server/calculator/functions.js.map +1 -0
  57. package/dist-server/calculator/index.d.ts +4 -0
  58. package/dist-server/calculator/index.js +8 -0
  59. package/dist-server/calculator/index.js.map +1 -0
  60. package/dist-server/calculator/parser.d.ts +21 -0
  61. package/dist-server/calculator/parser.js +121 -0
  62. package/dist-server/calculator/parser.js.map +1 -0
  63. package/dist-server/calculator/provider.d.ts +8 -0
  64. package/dist-server/calculator/provider.js +13 -0
  65. package/dist-server/calculator/provider.js.map +1 -0
  66. package/dist-server/controllers/kpi-metric-value-provider.d.ts +11 -0
  67. package/dist-server/controllers/kpi-metric-value-provider.js +63 -0
  68. package/dist-server/controllers/kpi-metric-value-provider.js.map +1 -0
  69. package/dist-server/controllers/kpi-value-provider.d.ts +11 -0
  70. package/dist-server/controllers/kpi-value-provider.js +46 -0
  71. package/dist-server/controllers/kpi-value-provider.js.map +1 -0
  72. package/dist-server/service/index.d.ts +4 -2
  73. package/dist-server/service/index.js +5 -0
  74. package/dist-server/service/index.js.map +1 -1
  75. package/dist-server/service/kpi/aggregate-kpi.js +4 -4
  76. package/dist-server/service/kpi/aggregate-kpi.js.map +1 -1
  77. package/dist-server/service/kpi/kpi-grade.types.d.ts +11 -10
  78. package/dist-server/service/kpi/kpi-grade.types.js.map +1 -1
  79. package/dist-server/service/kpi/kpi-history.d.ts +2 -2
  80. package/dist-server/service/kpi/kpi-history.js.map +1 -1
  81. package/dist-server/service/kpi/kpi-mutation.d.ts +2 -0
  82. package/dist-server/service/kpi/kpi-mutation.js +126 -4
  83. package/dist-server/service/kpi/kpi-mutation.js.map +1 -1
  84. package/dist-server/service/kpi/kpi-type.d.ts +8 -5
  85. package/dist-server/service/kpi/kpi-type.js +22 -8
  86. package/dist-server/service/kpi/kpi-type.js.map +1 -1
  87. package/dist-server/service/kpi/kpi.d.ts +6 -3
  88. package/dist-server/service/kpi/kpi.js +29 -9
  89. package/dist-server/service/kpi/kpi.js.map +1 -1
  90. package/dist-server/service/kpi-category/kpi-category-mutation.d.ts +3 -4
  91. package/dist-server/service/kpi-category/kpi-category-mutation.js +151 -80
  92. package/dist-server/service/kpi-category/kpi-category-mutation.js.map +1 -1
  93. package/dist-server/service/kpi-category/kpi-category-query.d.ts +5 -0
  94. package/dist-server/service/kpi-category/kpi-category-query.js +19 -1
  95. package/dist-server/service/kpi-category/kpi-category-query.js.map +1 -1
  96. package/dist-server/service/kpi-category/kpi-category-type.d.ts +3 -0
  97. package/dist-server/service/kpi-category/kpi-category-type.js +16 -1
  98. package/dist-server/service/kpi-category/kpi-category-type.js.map +1 -1
  99. package/dist-server/service/kpi-category/kpi-category.d.ts +2 -0
  100. package/dist-server/service/kpi-category/kpi-category.js +10 -1
  101. package/dist-server/service/kpi-category/kpi-category.js.map +1 -1
  102. package/dist-server/service/kpi-category-value/index.d.ts +6 -0
  103. package/dist-server/service/kpi-category-value/index.js +10 -0
  104. package/dist-server/service/kpi-category-value/index.js.map +1 -0
  105. package/dist-server/service/kpi-category-value/kpi-category-value-mutation.d.ts +8 -0
  106. package/dist-server/service/kpi-category-value/kpi-category-value-mutation.js +102 -0
  107. package/dist-server/service/kpi-category-value/kpi-category-value-mutation.js.map +1 -0
  108. package/dist-server/service/kpi-category-value/kpi-category-value-query.d.ts +13 -0
  109. package/dist-server/service/kpi-category-value/kpi-category-value-query.js +91 -0
  110. package/dist-server/service/kpi-category-value/kpi-category-value-query.js.map +1 -0
  111. package/dist-server/service/kpi-category-value/kpi-category-value-type.d.ts +19 -0
  112. package/dist-server/service/kpi-category-value/kpi-category-value-type.js +73 -0
  113. package/dist-server/service/kpi-category-value/kpi-category-value-type.js.map +1 -0
  114. package/dist-server/service/kpi-category-value/kpi-category-value.d.ts +19 -0
  115. package/dist-server/service/kpi-category-value/kpi-category-value.js +91 -0
  116. package/dist-server/service/kpi-category-value/kpi-category-value.js.map +1 -0
  117. package/dist-server/service/kpi-metric/kpi-metric-type.d.ts +5 -3
  118. package/dist-server/service/kpi-metric/kpi-metric-type.js +5 -3
  119. package/dist-server/service/kpi-metric/kpi-metric-type.js.map +1 -1
  120. package/dist-server/service/kpi-metric/kpi-metric.d.ts +2 -8
  121. package/dist-server/service/kpi-metric/kpi-metric.js +3 -14
  122. package/dist-server/service/kpi-metric/kpi-metric.js.map +1 -1
  123. package/dist-server/service/kpi-metric-value/kpi-metric-value-mutation.js +87 -45
  124. package/dist-server/service/kpi-metric-value/kpi-metric-value-mutation.js.map +1 -1
  125. package/dist-server/service/kpi-metric-value/kpi-metric-value.js +3 -2
  126. package/dist-server/service/kpi-metric-value/kpi-metric-value.js.map +1 -1
  127. package/dist-server/service/kpi-value/kpi-value-mutation.d.ts +3 -1
  128. package/dist-server/service/kpi-value/kpi-value-mutation.js +174 -6
  129. package/dist-server/service/kpi-value/kpi-value-mutation.js.map +1 -1
  130. package/dist-server/service/kpi-value/kpi-value-query.d.ts +0 -2
  131. package/dist-server/service/kpi-value/kpi-value-query.js +0 -12
  132. package/dist-server/service/kpi-value/kpi-value-query.js.map +1 -1
  133. package/dist-server/service/kpi-value/kpi-value-score.service.d.ts +26 -0
  134. package/dist-server/service/kpi-value/kpi-value-score.service.js +97 -0
  135. package/dist-server/service/kpi-value/kpi-value-score.service.js.map +1 -0
  136. package/dist-server/service/kpi-value/kpi-value-type.d.ts +2 -0
  137. package/dist-server/service/kpi-value/kpi-value-type.js +14 -0
  138. package/dist-server/service/kpi-value/kpi-value-type.js.map +1 -1
  139. package/dist-server/service/kpi-value/kpi-value.d.ts +1 -0
  140. package/dist-server/service/kpi-value/kpi-value.js +9 -1
  141. package/dist-server/service/kpi-value/kpi-value.js.map +1 -1
  142. package/dist-server/service/utils/value-date-util.d.ts +3 -0
  143. package/dist-server/service/utils/value-date-util.js +76 -0
  144. package/dist-server/service/utils/value-date-util.js.map +1 -0
  145. package/dist-server/tsconfig.tsbuildinfo +1 -1
  146. package/package.json +5 -5
  147. package/server/calculator/evaluator.ts +45 -0
  148. package/server/calculator/functions.ts +67 -0
  149. package/server/calculator/index.ts +4 -0
  150. package/server/calculator/parser.ts +128 -0
  151. package/server/calculator/provider.ts +10 -0
  152. package/server/controllers/kpi-metric-value-provider.ts +66 -0
  153. package/server/controllers/kpi-value-provider.ts +51 -0
  154. package/server/service/index.ts +5 -0
  155. package/server/service/kpi/aggregate-kpi.ts +4 -4
  156. package/server/service/kpi/kpi-grade.types.ts +11 -10
  157. package/server/service/kpi/kpi-history.ts +2 -2
  158. package/server/service/kpi/kpi-mutation.ts +128 -4
  159. package/server/service/kpi/kpi-type.ts +21 -9
  160. package/server/service/kpi/kpi.ts +32 -10
  161. package/server/service/kpi-category/kpi-category-mutation.ts +155 -82
  162. package/server/service/kpi-category/kpi-category-query.ts +21 -1
  163. package/server/service/kpi-category/kpi-category-type.ts +17 -6
  164. package/server/service/kpi-category/kpi-category.ts +10 -1
  165. package/server/service/kpi-category-value/index.ts +7 -0
  166. package/server/service/kpi-category-value/kpi-category-value-mutation.ts +88 -0
  167. package/server/service/kpi-category-value/kpi-category-value-query.ts +62 -0
  168. package/server/service/kpi-category-value/kpi-category-value-type.ts +48 -0
  169. package/server/service/kpi-category-value/kpi-category-value.ts +79 -0
  170. package/server/service/kpi-metric/kpi-metric-type.ts +7 -5
  171. package/server/service/kpi-metric/kpi-metric.ts +3 -15
  172. package/server/service/kpi-metric-value/kpi-metric-value-mutation.ts +95 -47
  173. package/server/service/kpi-metric-value/kpi-metric-value.ts +4 -2
  174. package/server/service/kpi-value/kpi-value-mutation.ts +176 -6
  175. package/server/service/kpi-value/kpi-value-query.ts +2 -8
  176. package/server/service/kpi-value/kpi-value-score.service.ts +112 -0
  177. package/server/service/kpi-value/kpi-value-type.ts +12 -0
  178. package/server/service/kpi-value/kpi-value.ts +8 -1
  179. package/server/service/utils/value-date-util.ts +72 -0
  180. package/things-factory.config.js +3 -0
  181. package/translations/en.json +3 -0
  182. package/translations/ja.json +3 -0
  183. package/translations/ko.json +3 -0
  184. package/translations/ms.json +3 -0
  185. package/translations/zh.json +3 -0
  186. package/dist-server/service/kpi-value/kpi-value-grade.service.d.ts +0 -34
  187. package/dist-server/service/kpi-value/kpi-value-grade.service.js +0 -117
  188. package/dist-server/service/kpi-value/kpi-value-grade.service.js.map +0 -1
  189. package/server/service/kpi-value/kpi-value-grade.service.ts +0 -127
@@ -68,7 +68,7 @@ export class KpiListPage extends connect(store)(localize(i18next)(ScopedElements
68
68
  @state() availableVariables: any[] = []
69
69
  @state() availableVariablesLoaded = false
70
70
 
71
- async getAvailableVariables() {
71
+ async getAvailableKpiMetricVariables() {
72
72
  if (this.availableVariablesLoaded) {
73
73
  return this.availableVariables
74
74
  }
@@ -166,24 +166,7 @@ export class KpiListPage extends connect(store)(localize(i18next)(ScopedElements
166
166
  async pageInitialized(lifecycle: any) {
167
167
  this.gristConfig = {
168
168
  list: {
169
- fields: [
170
- 'name',
171
- 'description',
172
- 'category',
173
- 'formula',
174
- 'active',
175
- 'state',
176
- 'vizType',
177
- 'schedule',
178
- 'scheduleId',
179
- 'timezone',
180
- 'version',
181
- 'createdAt',
182
- 'updatedAt',
183
- 'creator',
184
- 'updater',
185
- 'thumbnail'
186
- ],
169
+ fields: ['name', 'description'],
187
170
  details: [
188
171
  'name',
189
172
  'description',
@@ -206,6 +189,18 @@ export class KpiListPage extends connect(store)(localize(i18next)(ScopedElements
206
189
  columns: [
207
190
  { type: 'gutter', gutterName: 'sequence' },
208
191
  { type: 'gutter', gutterName: 'row-selector', multiple: true },
192
+ // KPI 실적값 계산 버튼 추가
193
+ {
194
+ type: 'gutter',
195
+ gutterName: 'button',
196
+ icon: 'calculate',
197
+ title: '실적값 계산',
198
+ handlers: {
199
+ click: (columns, data, column, record, rowIndex) => {
200
+ this._calculateKpiValue(record)
201
+ }
202
+ }
203
+ },
209
204
  {
210
205
  type: 'string',
211
206
  name: 'name',
@@ -244,15 +239,150 @@ export class KpiListPage extends connect(store)(localize(i18next)(ScopedElements
244
239
  record: {
245
240
  editable: true,
246
241
  availableVariables: async () => {
247
- return await this.getAvailableVariables()
242
+ return await this.getAvailableKpiMetricVariables()
248
243
  }
249
244
  },
250
245
  width: 320
251
246
  },
247
+ {
248
+ type: 'select',
249
+ name: 'periodType',
250
+ header: '계산주기',
251
+ record: {
252
+ editable: true,
253
+ options: [
254
+ { value: '', display: '' },
255
+ { value: 'DAY', display: '일' },
256
+ { value: 'WEEK', display: '주' },
257
+ { value: 'MONTH', display: '월' },
258
+ { value: 'QUARTER', display: '분기' },
259
+ { value: 'YEAR', display: '년' },
260
+ { value: 'RANGE', display: '범위' },
261
+ { value: 'ALLTIME', display: '전체' }
262
+ ]
263
+ },
264
+ width: 80
265
+ },
266
+ {
267
+ type: 'formula',
268
+ name: 'scoreFormula',
269
+ header: '성과점수수식',
270
+ record: {
271
+ editable: true,
272
+ availableVariables: async () => {
273
+ return [
274
+ {
275
+ name: 'value',
276
+ description: 'KPI 실적값',
277
+ type: 'kpi-value',
278
+ unit: ''
279
+ }
280
+ ]
281
+ },
282
+ includeDefaultFunctions: false,
283
+ availableFunctions: [
284
+ {
285
+ name: 'INTEGRATE()',
286
+ description: '수치 적분',
287
+ template: 'INTEGRATE({func}, {a}, {b}, {n})',
288
+ syntax: 'INTEGRATE(func, a, b, n)',
289
+ parameters: ['func - 적분할 함수', 'a - 하한값', 'b - 상한값', 'n - 분할 수 (기본값: 1000)'],
290
+ returnType: 'number',
291
+ help: '사다리꼴 적분법을 사용하여 수치 적분을 계산합니다.',
292
+ examples: ['INTEGRATE(x => x*x, 0, 1, 1000)', 'INTEGRATE([효율성], 0, 1)']
293
+ },
294
+ {
295
+ name: 'BETA_FUNCTION()',
296
+ description: '베타 함수',
297
+ template: 'BETA_FUNCTION({x}, {alpha}, {beta})',
298
+ syntax: 'BETA_FUNCTION(x, alpha, beta)',
299
+ parameters: ['x - 변수 (0 ≤ x ≤ 1)', 'alpha - 첫 번째 모수', 'beta - 두 번째 모수'],
300
+ returnType: 'number',
301
+ help: '베타 분포 함수를 계산합니다. t^(α-1) × (1-t)^(β-1)',
302
+ examples: ['BETA_FUNCTION(0.5, 2, 3)', 'BETA_FUNCTION([품질지수], 3, 2)']
303
+ },
304
+ {
305
+ name: 'INCOMPLETE_BETA()',
306
+ description: '불완전 베타 함수',
307
+ template: 'INCOMPLETE_BETA({x}, {alpha}, {beta})',
308
+ syntax: 'INCOMPLETE_BETA(x, alpha, beta)',
309
+ parameters: ['x - 상한값 (0 ≤ x ≤ 1)', 'alpha - 첫 번째 모수', 'beta - 두 번째 모수'],
310
+ returnType: 'number',
311
+ help: '불완전 베타 함수를 수치 적분으로 계산합니다.',
312
+ examples: ['INCOMPLETE_BETA(0.7, 2, 3)', 'INCOMPLETE_BETA([목표달성률]/100, 2, 3)']
313
+ },
314
+ {
315
+ name: 'COMPLETE_BETA()',
316
+ description: '완전 베타 함수',
317
+ template: 'COMPLETE_BETA({alpha}, {beta})',
318
+ syntax: 'COMPLETE_BETA(alpha, beta)',
319
+ parameters: ['alpha - 첫 번째 모수', 'beta - 두 번째 모수'],
320
+ returnType: 'number',
321
+ help: '완전 베타 함수 B(α,β)를 계산합니다.',
322
+ examples: ['COMPLETE_BETA(2, 3)', 'COMPLETE_BETA(3, 2)']
323
+ },
324
+ {
325
+ name: 'PERFORMANCE_INDEX()',
326
+ description: '성과 지수',
327
+ template: 'PERFORMANCE_INDEX({x}, {alpha1}, {beta1}, {alpha2}, {beta2})',
328
+ syntax: 'PERFORMANCE_INDEX(x, alpha1, beta1, alpha2, beta2)',
329
+ parameters: [
330
+ 'x - 성과 값 (0 ≤ x ≤ 1)',
331
+ 'alpha1, beta1 - 분자 베타 함수 모수',
332
+ 'alpha2, beta2 - 분모 베타 함수 모수'
333
+ ],
334
+ returnType: 'number',
335
+ help: '성과 지수를 계산합니다: 1 - (불완전 베타 / 완전 베타)',
336
+ examples: ['PERFORMANCE_INDEX(0.8, 2, 3, 2, 3)', 'PERFORMANCE_INDEX([성과점수]/100, 2, 3, 2, 3)']
337
+ },
338
+ {
339
+ name: 'EXP()',
340
+ description: '지수 함수',
341
+ template: 'EXP({x})',
342
+ syntax: 'EXP(x)',
343
+ parameters: ['x - 지수'],
344
+ returnType: 'number',
345
+ help: '자연상수 e의 x제곱을 계산합니다.',
346
+ examples: ['EXP(1)', 'EXP([효율성])']
347
+ },
348
+ {
349
+ name: 'LOG()',
350
+ description: '자연 로그',
351
+ template: 'LOG({x})',
352
+ syntax: 'LOG(x)',
353
+ parameters: ['x - 로그를 취할 값'],
354
+ returnType: 'number',
355
+ help: '자연 로그 ln(x)를 계산합니다.',
356
+ examples: ['LOG(2.718)', 'LOG([성과점수])']
357
+ },
358
+ {
359
+ name: 'POW()',
360
+ description: '거듭제곱',
361
+ template: 'POW({x}, {y})',
362
+ syntax: 'POW(x, y)',
363
+ parameters: ['x - 밑수', 'y - 지수'],
364
+ returnType: 'number',
365
+ help: 'x의 y제곱을 계산합니다.',
366
+ examples: ['POW(2, 3)', 'POW([효율성], 2)']
367
+ },
368
+ {
369
+ name: 'EXPONENTIAL_DECAY()',
370
+ description: '지수 감쇠',
371
+ template: 'EXPONENTIAL_DECAY({value}, {scale}, {power})',
372
+ syntax: 'EXPONENTIAL_DECAY(value, scale, power)',
373
+ parameters: ['value - 입력 값', 'scale - 스케일 파라미터', 'power - 지수 파라미터'],
374
+ returnType: 'number',
375
+ help: '지수 감쇠 함수 exp(-(value/scale)^power)를 계산합니다.',
376
+ examples: ['EXPONENTIAL_DECAY(50, 100, 2)', 'EXPONENTIAL_DECAY([목표달성률], 50, 2)']
377
+ }
378
+ ]
379
+ },
380
+ width: 200
381
+ },
252
382
  {
253
383
  type: 'string',
254
384
  name: 'grades',
255
- header: '등급 설정',
385
+ header: '성과지수 Lookup',
256
386
  record: {
257
387
  editable: false,
258
388
  renderer: (v, c, r) => {
@@ -369,11 +499,13 @@ export class KpiListPage extends connect(store)(localize(i18next)(ScopedElements
369
499
  name
370
500
  description
371
501
  active
502
+ formula
503
+ periodType
504
+ scoreFormula
372
505
  grades
373
506
  vizType
374
507
  vizMeta
375
508
  weight
376
- formula
377
509
  schedule
378
510
  scheduleId
379
511
  timezone
@@ -648,4 +780,30 @@ export class KpiListPage extends connect(store)(localize(i18next)(ScopedElements
648
780
  })
649
781
  }
650
782
  }
783
+
784
+ async _calculateKpiValue(kpi) {
785
+ try {
786
+ const response = await client.mutate({
787
+ mutation: gql`
788
+ mutation ($kpiId: String!) {
789
+ calculateKpiValue(kpiId: $kpiId) {
790
+ id
791
+ value
792
+ valueDate
793
+ group
794
+ }
795
+ }
796
+ `,
797
+ variables: {
798
+ kpiId: kpi.id
799
+ }
800
+ })
801
+ if (!response.errors) {
802
+ notify({ message: 'KPI 실적값이 성공적으로 계산되었습니다.' })
803
+ this.grist.fetch()
804
+ }
805
+ } catch (error) {
806
+ notify({ message: 'KPI 실적값 계산 중 오류가 발생했습니다.' })
807
+ }
808
+ }
651
809
  }
@@ -1,5 +1,5 @@
1
1
  import '@material/web/button/elevated-button.js'
2
- import '@material/web/select/outlined-select.js'
2
+ // import '@material/web/select/outlined-select.js'
3
3
  import '@material/web/textfield/outlined-text-field.js'
4
4
  import '@material/web/icon/icon.js'
5
5
 
@@ -20,6 +20,7 @@ import { connect } from 'pwa-helpers/connect-mixin'
20
20
  import gql from 'graphql-tag'
21
21
 
22
22
  import { KpiCategoryImporter } from './kpi-category-importer'
23
+ import { KpiCategoryValueCalculator } from './kpi-category-value-calculator'
23
24
 
24
25
  @customElement('kpi-category-list-page')
25
26
  export class KpiCategoryListPage extends connect(store)(localize(i18next)(ScopedElementsMixin(PageView))) {
@@ -50,7 +51,8 @@ export class KpiCategoryListPage extends connect(store)(localize(i18next)(Scoped
50
51
 
51
52
  static get scopedElements() {
52
53
  return {
53
- 'kpi-category-importer': KpiCategoryImporter
54
+ 'kpi-category-importer': KpiCategoryImporter,
55
+ 'kpi-category-value-calculator': KpiCategoryValueCalculator
54
56
  }
55
57
  }
56
58
 
@@ -159,12 +161,45 @@ export class KpiCategoryListPage extends connect(store)(localize(i18next)(Scoped
159
161
  async pageInitialized(lifecycle: any) {
160
162
  this.gristConfig = {
161
163
  list: {
162
- fields: ['name', 'description', 'active', 'formula', 'weight', 'createdAt', 'updatedAt', 'creator', 'updater'],
163
- details: ['name', 'description', 'active', 'formula', 'weight', 'createdAt', 'updatedAt', 'creator', 'updater']
164
+ fields: [
165
+ 'name',
166
+ 'description',
167
+ 'active',
168
+ 'formula',
169
+ 'weight',
170
+ 'periodType',
171
+ 'createdAt',
172
+ 'updatedAt',
173
+ 'creator',
174
+ 'updater'
175
+ ],
176
+ details: [
177
+ 'name',
178
+ 'description',
179
+ 'active',
180
+ 'formula',
181
+ 'weight',
182
+ 'periodType',
183
+ 'createdAt',
184
+ 'updatedAt',
185
+ 'creator',
186
+ 'updater'
187
+ ]
164
188
  },
165
189
  columns: [
166
190
  { type: 'gutter', gutterName: 'sequence' },
167
191
  { type: 'gutter', gutterName: 'row-selector', multiple: true },
192
+ {
193
+ type: 'gutter',
194
+ gutterName: 'button',
195
+ icon: 'calculate',
196
+ title: '실적값 계산',
197
+ handlers: {
198
+ click: (columns, data, column, record, rowIndex) => {
199
+ this.openCalculator(record)
200
+ }
201
+ }
202
+ },
168
203
  {
169
204
  type: 'string',
170
205
  name: 'name',
@@ -201,6 +236,26 @@ export class KpiCategoryListPage extends connect(store)(localize(i18next)(Scoped
201
236
  record: { editable: true },
202
237
  width: 80
203
238
  },
239
+ {
240
+ type: 'select',
241
+ name: 'periodType',
242
+ header: '계산주기',
243
+ record: {
244
+ editable: true,
245
+ options: [
246
+ { value: '', display: '' },
247
+ { value: 'DAY', display: '일' },
248
+ { value: 'WEEK', display: '주' },
249
+ { value: 'MONTH', display: '월' },
250
+ { value: 'QUARTER', display: '분기' },
251
+ { value: 'YEAR', display: '년' },
252
+ { value: 'RANGE', display: '범위' },
253
+ { value: 'ALLTIME', display: '전체' }
254
+ ]
255
+ },
256
+
257
+ width: 80
258
+ },
204
259
  {
205
260
  type: 'checkbox',
206
261
  name: 'active',
@@ -264,6 +319,7 @@ export class KpiCategoryListPage extends connect(store)(localize(i18next)(Scoped
264
319
  active
265
320
  formula
266
321
  weight
322
+ periodType
267
323
  updater {
268
324
  id
269
325
  name
@@ -461,4 +517,21 @@ export class KpiCategoryListPage extends connect(store)(localize(i18next)(Scoped
461
517
  this.grist.fetch()
462
518
  }
463
519
  }
520
+
521
+ private async openCalculator(record: any) {
522
+ const popup = openPopup(
523
+ html`
524
+ <kpi-category-value-calculator
525
+ .categoryId=${record.id}
526
+ .categoryName=${record.name}
527
+ @closed=${() => popup.close()}
528
+ ></kpi-category-value-calculator>
529
+ `,
530
+ {
531
+ backdrop: true,
532
+ size: 'large',
533
+ title: i18next.t('title.calculate kpi value')
534
+ }
535
+ )
536
+ }
464
537
  }
@@ -0,0 +1,233 @@
1
+ import '@material/web/icon/icon.js'
2
+
3
+ import { css, html, LitElement } from 'lit'
4
+ import { customElement, property, state } from 'lit/decorators.js'
5
+ import { client } from '@operato/graphql'
6
+ import { notify } from '@operato/layout'
7
+ import gql from 'graphql-tag'
8
+
9
+ // KpiPeriodType enum 정의 (서버와 동일하게)
10
+ enum KpiPeriodType {
11
+ DAY = 'DAY',
12
+ WEEK = 'WEEK',
13
+ MONTH = 'MONTH',
14
+ QUARTER = 'QUARTER',
15
+ YEAR = 'YEAR',
16
+ RANGE = 'RANGE',
17
+ ALLTIME = 'ALLTIME'
18
+ }
19
+
20
+ // periodType에 따른 마지막 일자 계산 함수
21
+ function getLastValueDate(periodType: KpiPeriodType = KpiPeriodType.DAY): string {
22
+ const now = new Date()
23
+
24
+ switch (periodType) {
25
+ case KpiPeriodType.DAY: {
26
+ const d = new Date(now)
27
+ d.setDate(d.getDate() - 1)
28
+ return d.toISOString().slice(0, 10)
29
+ }
30
+ case KpiPeriodType.MONTH: {
31
+ const d = new Date(now)
32
+ d.setMonth(d.getMonth() - 1)
33
+ return d.toISOString().slice(0, 7)
34
+ }
35
+ case KpiPeriodType.QUARTER: {
36
+ let year = now.getFullYear()
37
+ let quarter = Math.floor(now.getMonth() / 3)
38
+ if (quarter === 0) {
39
+ year -= 1
40
+ quarter = 4
41
+ }
42
+ return `${year}-Q${quarter}`
43
+ }
44
+ case KpiPeriodType.WEEK: {
45
+ const d = new Date(now)
46
+ d.setDate(d.getDate() - 7)
47
+ const year = d.getFullYear()
48
+ const week = getISOWeek(d)
49
+ return `${year}-W${week}`
50
+ }
51
+ case KpiPeriodType.ALLTIME:
52
+ return 'ALLTIME'
53
+ default: {
54
+ const d = new Date(now)
55
+ d.setDate(d.getDate() - 1)
56
+ return d.toISOString().slice(0, 10)
57
+ }
58
+ }
59
+ }
60
+
61
+ // ISO 주차 계산 함수
62
+ function getISOWeek(date: Date): number {
63
+ const tmp = new Date(date.getTime())
64
+ tmp.setHours(0, 0, 0, 0)
65
+ tmp.setDate(tmp.getDate() + 4 - (tmp.getDay() || 7))
66
+ const yearStart = new Date(tmp.getFullYear(), 0, 1)
67
+ const weekNo = Math.ceil(((tmp.getTime() - yearStart.getTime()) / 86400000 + 1) / 7)
68
+ return weekNo
69
+ }
70
+
71
+ @customElement('kpi-category-value-calculator')
72
+ export class KpiCategoryValueCalculator extends LitElement {
73
+ static styles = [
74
+ css`
75
+ :host {
76
+ display: block;
77
+ padding: 20px;
78
+ min-width: 400px;
79
+ }
80
+
81
+ .container {
82
+ display: flex;
83
+ flex-direction: column;
84
+ gap: 10px;
85
+ }
86
+
87
+ .form-group {
88
+ display: flex;
89
+ flex-direction: column;
90
+ margin-bottom: 15px;
91
+ }
92
+
93
+ label {
94
+ display: block;
95
+ margin-bottom: 5px;
96
+ font-weight: bold;
97
+ }
98
+
99
+ input,
100
+ select {
101
+ padding: 8px;
102
+ border: 1px solid #ccc;
103
+ border-radius: 4px;
104
+ }
105
+
106
+ .result {
107
+ margin-top: 20px;
108
+ padding: 15px;
109
+ background-color: #f5f5f5;
110
+ border-radius: 4px;
111
+ }
112
+
113
+ .result-value {
114
+ font-size: 18px;
115
+ font-weight: bold;
116
+ color: #2196f3;
117
+ }
118
+
119
+ .kpi-values {
120
+ margin-top: 10px;
121
+ }
122
+
123
+ .kpi-value-item {
124
+ display: flex;
125
+ justify-content: space-between;
126
+ padding: 5px 0;
127
+ border-bottom: 1px solid #eee;
128
+ }
129
+ `
130
+ ]
131
+
132
+ @property({ type: String }) categoryId = ''
133
+ @property({ type: String }) categoryName = ''
134
+ @state() valueDate = getLastValueDate()
135
+ @state() group = ''
136
+ @state() result: any = null
137
+ @state() loading = false
138
+
139
+ render() {
140
+ return html`
141
+ <div class="container">
142
+ <h3>${this.categoryName} - KPI 값 계산</h3>
143
+
144
+ <div class="form-group">
145
+ <label>계산 기준일</label>
146
+ <input type="date" .value=${this.valueDate} @input=${e => (this.valueDate = e.target.value)} />
147
+ </div>
148
+
149
+ <div class="form-group">
150
+ <label>그룹</label>
151
+ <input
152
+ type="text"
153
+ placeholder="그룹명 (선택사항)"
154
+ .value=${this.group}
155
+ @input=${e => (this.group = e.target.value)}
156
+ />
157
+ </div>
158
+
159
+ <button
160
+ @click=${this.calculateValue}
161
+ ?disabled=${this.loading}
162
+ style="width: 100%; padding: 10px; background: #2196f3; color: white; border: none; border-radius: 4px; cursor: pointer;"
163
+ >
164
+ ${this.loading ? '계산 중...' : '계산하기'}
165
+ </button>
166
+
167
+ ${this.result
168
+ ? html`
169
+ <div class="result">
170
+ <div class="result-value">
171
+ 계산 결과: ${this.result.value !== null ? this.result.value.toFixed(2) : 'N/A'}
172
+ </div>
173
+ ${this.result.kpiValues && this.result.kpiValues.length > 0
174
+ ? html`
175
+ <div class="kpi-values">
176
+ <h4>개별 KPI 값:</h4>
177
+ ${this.result.kpiValues.map(
178
+ item => html`
179
+ <div class="kpi-value-item">
180
+ <span>KPI ID: ${item.kpiId}</span>
181
+ <span>${item.value !== null ? item.value.toFixed(2) : 'N/A'}</span>
182
+ </div>
183
+ `
184
+ )}
185
+ </div>
186
+ `
187
+ : ''}
188
+ </div>
189
+ `
190
+ : ''}
191
+ </div>
192
+ `
193
+ }
194
+
195
+ async calculateValue() {
196
+ if (!this.categoryId) return
197
+
198
+ this.loading = true
199
+ try {
200
+ const response = await client.query({
201
+ query: gql`
202
+ query ($categoryId: String!, $valueDate: String, $group: String) {
203
+ calculateKpiValue(categoryId: $categoryId, valueDate: $valueDate, group: $group) {
204
+ value
205
+ valueDate
206
+ group
207
+ kpiValues {
208
+ kpiId
209
+ value
210
+ }
211
+ }
212
+ }
213
+ `,
214
+ variables: {
215
+ categoryId: this.categoryId,
216
+ valueDate: this.valueDate || null,
217
+ group: this.group || null
218
+ }
219
+ })
220
+
221
+ if (!response.errors) {
222
+ this.result = response.data.calculateKpiValue
223
+ }
224
+ } catch (error) {
225
+ console.error('KPI 값 계산 중 오류:', error)
226
+ notify({
227
+ message: 'KPI 값 계산 중 오류가 발생했습니다.'
228
+ })
229
+ } finally {
230
+ this.loading = false
231
+ }
232
+ }
233
+ }