@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
@@ -3,25 +3,21 @@ import '@material/web/button/elevated-button.js'
3
3
  import '@material/web/textfield/outlined-text-field.js'
4
4
 
5
5
  import { CommonButtonStyles, CommonHeaderStyles, ScrollbarStyles } from '@operato/styles'
6
- import { PageView, store } from '@operato/shell'
6
+ import { PageView } from '@operato/shell'
7
7
  import { css, html } from 'lit'
8
8
  import { customElement, property, state } from 'lit/decorators.js'
9
9
  import { ScopedElementsMixin } from '@open-wc/scoped-elements'
10
10
  import { client } from '@operato/graphql'
11
11
  import { i18next, localize } from '@operato/i18n'
12
12
  import { notify } from '@operato/layout'
13
- import { connect } from 'pwa-helpers/connect-mixin'
14
13
  import gql from 'graphql-tag'
15
14
 
16
- interface KpiStatisticData {
17
- kpi: {
18
- id: string
15
+ interface KpiStatisticData { kpi: { id: string
19
16
  name: string
20
- }
17
+ }
21
18
  valueDate: string
22
19
  periodType: string
23
- statistics: {
24
- count?: number
20
+ statistics: { count?: number
25
21
  sum?: number
26
22
  range?: number
27
23
  mean?: number
@@ -36,61 +32,53 @@ interface KpiStatisticData {
36
32
  lowerFence?: number
37
33
  upperFence?: number
38
34
  isDirty?: boolean
39
- }
35
+ }
40
36
  }
41
37
 
42
- interface EditorCell {
43
- field: string
38
+ interface EditorCell { field: string
44
39
  value: number | null
45
40
  isEditable: boolean
46
41
  isHighlighted: boolean
47
42
  }
48
43
 
49
44
  @customElement('kpi-statistic-editor-page')
50
- export class KpiStatisticEditorPage extends connect(store)(localize(i18next)(ScopedElementsMixin(PageView))) {
51
- static styles = [
45
+ export class KpiStatisticEditorPage extends localize(i18next)(ScopedElementsMixin(PageView)) { static styles = [
52
46
  CommonHeaderStyles,
53
47
  ScrollbarStyles,
54
48
  css`
55
- :host {
56
- display: flex;
49
+ :host { display: flex;
57
50
  flex-direction: column;
58
51
  padding: 20px;
59
52
  overflow-x: auto;
60
- }
53
+ }
61
54
 
62
- .header {
63
- display: flex;
55
+ .header { display: flex;
64
56
  gap: 16px;
65
57
  align-items: center;
66
58
  margin-bottom: 20px;
67
59
  padding: 16px;
68
60
  background: var(--md-sys-color-surface-container);
69
61
  border-radius: 8px;
70
- }
62
+ }
71
63
 
72
- .controls {
73
- display: flex;
64
+ .controls { display: flex;
74
65
  gap: 12px;
75
66
  align-items: center;
76
67
  flex-wrap: wrap;
77
- }
68
+ }
78
69
 
79
- .table-container {
80
- flex: 1;
70
+ .table-container { flex: 1;
81
71
  overflow: auto;
82
72
  border: 1px solid var(--md-sys-color-outline);
83
73
  border-radius: 8px;
84
- }
74
+ }
85
75
 
86
- table {
87
- width: 100%;
76
+ table { width: 100%;
88
77
  border-collapse: collapse;
89
78
  min-width: max-content;
90
- }
79
+ }
91
80
 
92
- th {
93
- background: var(--md-sys-color-surface-container-low);
81
+ th { background: var(--md-sys-color-surface-container-low);
94
82
  font-weight: 500;
95
83
  padding: 8px 12px;
96
84
  border: 1px solid var(--md-sys-color-outline-variant);
@@ -98,148 +86,127 @@ export class KpiStatisticEditorPage extends connect(store)(localize(i18next)(Sco
98
86
  height: 60px;
99
87
  vertical-align: middle;
100
88
  text-align: center;
101
- }
89
+ }
102
90
 
103
- td {
104
- padding: 8px 12px;
91
+ td { padding: 8px 12px;
105
92
  border: 1px solid var(--md-sys-color-outline-variant);
106
93
  min-width: 120px;
107
94
  height: 60px;
108
95
  text-align: center;
109
96
  vertical-align: middle;
110
- }
97
+ }
111
98
 
112
- .kpi-header {
113
- position: sticky;
99
+ .kpi-header { position: sticky;
114
100
  left: 0;
115
101
  top: 0;
116
102
  z-index: 3;
117
103
  min-width: 200px;
118
104
  text-align: left;
119
- }
105
+ }
120
106
 
121
- .field-header {
122
- position: sticky;
107
+ .field-header { position: sticky;
123
108
  left: 0;
124
109
  top: 0;
125
110
  z-index: 2;
126
111
  min-width: 200px;
127
112
  text-align: center;
128
- }
113
+ }
129
114
 
130
- .kpi-name {
131
- position: sticky;
115
+ .kpi-name { position: sticky;
132
116
  left: 0;
133
117
  background: var(--md-sys-color-surface);
134
118
  font-weight: 500;
135
119
  min-width: 200px;
136
120
  text-align: left;
137
121
  z-index: 2;
138
- }
122
+ }
139
123
 
140
- tr:hover {
141
- background: var(--md-sys-color-surface-container-high);
142
- }
124
+ tr:hover { background: var(--md-sys-color-surface-container-high);
125
+ }
143
126
 
144
- td.editable-cell {
145
- cursor: pointer;
127
+ td.editable-cell { cursor: pointer;
146
128
  background: var(--md-sys-color-primary-container);
147
129
  color: var(--md-sys-color-on-primary-container);
148
- }
130
+ }
149
131
 
150
- td.editable-cell:hover {
151
- background: var(--md-sys-color-primary-container-high);
152
- }
132
+ td.editable-cell:hover { background: var(--md-sys-color-primary-container-high);
133
+ }
153
134
 
154
- td.highlighted-cell {
155
- background: var(--md-sys-color-secondary-container);
135
+ td.highlighted-cell { background: var(--md-sys-color-secondary-container);
156
136
  color: var(--md-sys-color-on-secondary-container);
157
137
  font-weight: 500;
158
- }
138
+ }
159
139
 
160
- td.highlighted-cell:hover {
161
- background: var(--md-sys-color-secondary-container-high);
162
- }
140
+ td.highlighted-cell:hover { background: var(--md-sys-color-secondary-container-high);
141
+ }
163
142
 
164
- td.disabled-cell {
165
- background: var(--md-sys-color-surface-container);
143
+ td.disabled-cell { background: var(--md-sys-color-surface-container);
166
144
  color: var(--md-sys-color-on-surface-variant);
167
145
  cursor: not-allowed;
168
- }
146
+ }
169
147
 
170
- .value-input {
171
- width: 100%;
148
+ .value-input { width: 100%;
172
149
  text-align: center;
173
150
  border: none;
174
151
  background: transparent;
175
152
  color: inherit;
176
153
  font-size: 12px;
177
154
  padding: 2px;
178
- }
155
+ }
179
156
 
180
- .value-input:focus {
181
- outline: 2px solid var(--md-sys-color-primary);
157
+ .value-input:focus { outline: 2px solid var(--md-sys-color-primary);
182
158
  border-radius: 4px;
183
- }
159
+ }
184
160
 
185
- .period-badge {
186
- font-size: 10px;
161
+ .period-badge { font-size: 10px;
187
162
  padding: 2px 6px;
188
163
  border-radius: 4px;
189
164
  background: var(--md-sys-color-tertiary-container);
190
165
  color: var(--md-sys-color-on-tertiary-container);
191
166
  margin-left: 8px;
192
- }
167
+ }
193
168
 
194
- .loading {
195
- display: flex;
169
+ .loading { display: flex;
196
170
  justify-content: center;
197
171
  align-items: center;
198
172
  height: 200px;
199
173
  color: var(--md-sys-color-on-surface-variant);
200
- }
174
+ }
201
175
 
202
- .error {
203
- color: var(--md-sys-color-error);
176
+ .error { color: var(--md-sys-color-error);
204
177
  padding: 16px;
205
178
  text-align: center;
206
- }
179
+ }
207
180
 
208
- .legend {
209
- display: flex;
181
+ .legend { display: flex;
210
182
  gap: 16px;
211
183
  margin-top: 16px;
212
184
  font-size: 12px;
213
- }
185
+ }
214
186
 
215
- .legend-item {
216
- display: flex;
187
+ .legend-item { display: flex;
217
188
  align-items: center;
218
189
  gap: 4px;
219
- }
190
+ }
220
191
 
221
- .legend-color {
222
- width: 16px;
192
+ .legend-color { width: 16px;
223
193
  height: 16px;
224
194
  border-radius: 2px;
225
- }
195
+ }
226
196
 
227
- .field-group {
228
- display: flex;
197
+ .field-group { display: flex;
229
198
  flex-direction: column;
230
199
  gap: 4px;
231
- }
200
+ }
232
201
 
233
- .field-name {
234
- font-size: 10px;
202
+ .field-name { font-size: 10px;
235
203
  color: var(--md-sys-color-on-surface-variant);
236
204
  font-weight: 500;
237
- }
205
+ }
238
206
 
239
- .field-value {
240
- font-size: 12px;
207
+ .field-value { font-size: 12px;
241
208
  font-weight: 500;
242
- }
209
+ }
243
210
  `
244
211
  ]
245
212
 
@@ -271,42 +238,35 @@ export class KpiStatisticEditorPage extends connect(store)(localize(i18next)(Sco
271
238
  { name: 'upperFence', label: 'Upper Fence', group: 'fence' }
272
239
  ]
273
240
 
274
- get context() {
275
- return {
276
- title: i18next.t('title.kpi statistic editor'),
241
+ get context() { return { title: i18next.t('title.kpi statistic editor'),
277
242
  actions: [
278
- {
279
- title: i18next.t('button.save'),
243
+ { title: i18next.t('button.save'),
280
244
  action: this._saveStatistics.bind(this),
281
245
  ...CommonButtonStyles.save
282
- },
283
- {
284
- title: i18next.t('button.cancel'),
246
+ },
247
+ { title: i18next.t('button.cancel'),
285
248
  action: this._cancel.bind(this),
286
249
  ...CommonButtonStyles.cancel
287
- }
250
+ }
288
251
  ]
289
- }
290
- }
252
+ }
253
+ }
291
254
 
292
- render() {
293
- if (this.loading) {
294
- return html`
255
+ render() { if (this.loading) { return html`
295
256
  <div class="loading">
296
257
  <md-icon>hourglass_empty</md-icon>
297
258
  <span>데이터를 불러오는 중...</span>
298
259
  </div>
299
260
  `
300
- }
261
+ }
301
262
 
302
- if (this.error) {
303
- return html`
263
+ if (this.error) { return html`
304
264
  <div class="error">
305
265
  <md-icon>error</md-icon>
306
266
  <span>${this.error}</span>
307
267
  </div>
308
268
  `
309
- }
269
+ }
310
270
 
311
271
  return html`
312
272
  <div class="header">
@@ -398,38 +358,31 @@ export class KpiStatisticEditorPage extends connect(store)(localize(i18next)(Sco
398
358
  </div>
399
359
  </div>
400
360
  `
401
- }
361
+ }
402
362
 
403
- private _getCellClass(kpi: KpiStatisticData, fieldName: string): string {
404
- const isEditable = this._isFieldEditable(fieldName)
363
+ private _getCellClass(kpi: KpiStatisticData, fieldName: string): string { const isEditable = this._isFieldEditable(fieldName)
405
364
  const isHighlighted = this._isFieldHighlighted(fieldName)
406
365
 
407
- if (!isEditable) {
408
- return 'disabled-cell'
409
- }
410
- if (isHighlighted) {
411
- return 'highlighted-cell'
412
- }
366
+ if (!isEditable) { return 'disabled-cell'
367
+ }
368
+ if (isHighlighted) { return 'highlighted-cell'
369
+ }
413
370
  return 'editable-cell'
414
- }
371
+ }
415
372
 
416
- private _isFieldEditable(fieldName: string): boolean {
417
- // 모든 필드를 편집 가능하게 설정 (필요에 따라 제한 가능)
373
+ private _isFieldEditable(fieldName: string): boolean { // 모든 필드를 편집 가능하게 설정 (필요에 따라 제한 가능)
418
374
  return true
419
- }
375
+ }
420
376
 
421
- private _isFieldHighlighted(fieldName: string): boolean {
422
- // 중요 필드들을 하이라이트
377
+ private _isFieldHighlighted(fieldName: string): boolean { // 중요 필드들을 하이라이트
423
378
  const importantFields = ['count', 'mean', 'median', 'minimum', 'maximum', 'standardDeviation']
424
379
  return importantFields.includes(fieldName)
425
- }
380
+ }
426
381
 
427
- private _renderCellContent(kpi: KpiStatisticData, fieldName: string) {
428
- const isEditing = this.editingCell?.kpi?.id === kpi.kpi.id && this.editingCell?.field === fieldName
382
+ private _renderCellContent(kpi: KpiStatisticData, fieldName: string) { const isEditing = this.editingCell?.kpi?.id === kpi.kpi.id && this.editingCell?.field === fieldName
429
383
  const value = kpi.statistics[fieldName]
430
384
 
431
- if (isEditing) {
432
- return html`
385
+ if (isEditing) { return html`
433
386
  <input
434
387
  class="value-input"
435
388
  type="number"
@@ -440,76 +393,59 @@ export class KpiStatisticEditorPage extends connect(store)(localize(i18next)(Sco
440
393
  autofocus
441
394
  />
442
395
  `
443
- }
396
+ }
444
397
 
445
398
  return html`
446
399
  <span style="color: ${value !== null && value !== undefined ? 'inherit' : '#999'};">
447
400
  ${value !== null && value !== undefined ? value.toLocaleString() : '클릭하여 입력'}
448
401
  </span>
449
402
  `
450
- }
403
+ }
451
404
 
452
- private _startEdit(kpiId: string, fieldName: string) {
453
- this.editingCell = { kpi: { id: kpiId }, field: fieldName }
454
- }
405
+ private _startEdit(kpiId: string, fieldName: string) { this.editingCell = { kpi: { id: kpiId }, field: fieldName }
406
+ }
455
407
 
456
- private _finishEdit(kpiId: string, fieldName: string, value: number | null) {
457
- const kpi = this.kpis.find(k => k.kpi.id === kpiId)
458
- if (kpi) {
459
- const originalValue = this._findExistingStatistic(kpiId)?.[fieldName]
408
+ private _finishEdit(kpiId: string, fieldName: string, value: number | null) { const kpi = this.kpis.find(k => k.kpi.id === kpiId)
409
+ if (kpi) { const originalValue = this._findExistingStatistic(kpiId)?.[fieldName]
460
410
  const isChanged = originalValue !== value
461
411
 
462
412
  kpi.statistics[fieldName] = value
463
413
  kpi.statistics.isDirty = isChanged
464
- }
414
+ }
465
415
  this.editingCell = null
466
- }
416
+ }
467
417
 
468
- private async _loadData() {
469
- if (!this.valueDate) {
470
- this.error = '값 날짜를 입력해주세요.'
418
+ private async _loadData() { if (!this.valueDate) { this.error = '값 날짜를 입력해주세요.'
471
419
  return
472
- }
420
+ }
473
421
 
474
422
  this.loading = true
475
423
  this.error = ''
476
424
 
477
- try {
478
- // KPI 목록 조회
479
- const kpisResponse = await client.query({
480
- query: gql`
481
- query ($filters: [Filter!]) {
482
- kpis(filters: $filters) {
483
- items {
484
- id
425
+ try { // KPI 목록 조회
426
+ const kpisResponse = await client.query({ query: gql`
427
+ query ($filters: [Filter!]) { kpis(filters: $filters) { items { id
485
428
  name
486
429
  periodType
487
430
  active
488
- category: parent {
489
- id
431
+ category: parent { id
490
432
  name
491
- }
492
- }
433
+ }
434
+ }
493
435
  total
494
- }
495
- }
436
+ }
437
+ }
496
438
  `,
497
- variables: {
498
- filters: [{ name: 'active', operator: 'eq', value: true }]
499
- }
500
- })
439
+ variables: { filters: [{ name: 'active', operator: 'eq', value: true }]
440
+ }
441
+ })
501
442
 
502
443
  // 기존 KPI Statistic 데이터 조회
503
- const statisticsResponse = await client.query({
504
- query: gql`
505
- query ($filters: [Filter!]) {
506
- kpiStatistics(filters: $filters) {
507
- items {
508
- id
509
- kpi {
510
- id
444
+ const statisticsResponse = await client.query({ query: gql`
445
+ query ($filters: [Filter!]) { kpiStatistics(filters: $filters) { items { id
446
+ kpi { id
511
447
  name
512
- }
448
+ }
513
449
  valueDate
514
450
  periodType
515
451
  count
@@ -528,18 +464,17 @@ export class KpiStatisticEditorPage extends connect(store)(localize(i18next)(Sco
528
464
  upperFence
529
465
  additionalStatistics
530
466
  metadata
531
- }
467
+ }
532
468
  total
533
- }
534
- }
469
+ }
470
+ }
535
471
  `,
536
- variables: {
537
- filters: [
472
+ variables: { filters: [
538
473
  { name: 'valueDate', operator: 'eq', value: this.valueDate },
539
474
  { name: 'periodType', operator: 'eq', value: this.periodType }
540
475
  ]
541
- }
542
- })
476
+ }
477
+ })
543
478
 
544
479
  // KPI 목록을 기준으로 데이터 구성 (카테고리별로 필터링)
545
480
  console.log('KPI 원본 데이터:', kpisResponse.data.kpis.items)
@@ -549,12 +484,10 @@ export class KpiStatisticEditorPage extends connect(store)(localize(i18next)(Sco
549
484
 
550
485
  console.log('필터링된 KPI:', filteredKpis)
551
486
 
552
- this.kpis = filteredKpis.map((kpi: any) => ({
553
- kpi: kpi,
487
+ this.kpis = filteredKpis.map((kpi: any) => ({ kpi: kpi,
554
488
  valueDate: this.valueDate,
555
489
  periodType: this.periodType,
556
- statistics: {
557
- count: null,
490
+ statistics: { count: null,
558
491
  sum: null,
559
492
  range: null,
560
493
  mean: null,
@@ -569,20 +502,17 @@ export class KpiStatisticEditorPage extends connect(store)(localize(i18next)(Sco
569
502
  lowerFence: null,
570
503
  upperFence: null,
571
504
  isDirty: false
572
- }
573
- }))
505
+ }
506
+ }))
574
507
 
575
508
  // 기존 KPI Statistic 데이터 저장
576
509
  this._existingStatistics = statisticsResponse.data.kpiStatistics.items
577
510
  console.log('기존 통계 데이터:', this._existingStatistics)
578
511
 
579
512
  // 기존 통계 데이터를 해당 KPI에 매핑
580
- statisticsResponse.data.kpiStatistics.items.forEach((statistic: any) => {
581
- const kpi = this.kpis.find(k => k.kpi.id === statistic.kpi.id) // KPI ID로 매칭
513
+ statisticsResponse.data.kpiStatistics.items.forEach((statistic: any) => { const kpi = this.kpis.find(k => k.kpi.id === statistic.kpi.id) // KPI ID로 매칭
582
514
  console.log(`통계 데이터 매핑: statistic.kpi.id=${statistic.kpi.id}, 찾은 KPI:`, kpi?.kpi.name || '없음')
583
- if (kpi) {
584
- kpi.statistics = {
585
- count: statistic.count,
515
+ if (kpi) { kpi.statistics = { count: statistic.count,
586
516
  sum: statistic.sum,
587
517
  range: statistic.range,
588
518
  mean: statistic.mean,
@@ -597,9 +527,9 @@ export class KpiStatisticEditorPage extends connect(store)(localize(i18next)(Sco
597
527
  lowerFence: statistic.lowerFence,
598
528
  upperFence: statistic.upperFence,
599
529
  isDirty: false
600
- }
601
- }
602
- })
530
+ }
531
+ }
532
+ })
603
533
 
604
534
  console.log('KPI 총 개수:', kpisResponse.data.kpis.total)
605
535
  console.log('KPI 목록:', kpisResponse.data.kpis.items)
@@ -608,112 +538,87 @@ export class KpiStatisticEditorPage extends connect(store)(localize(i18next)(Sco
608
538
  console.log('최종 KPI 배열 길이:', this.kpis.length)
609
539
 
610
540
  this.requestUpdate()
611
- } catch (error) {
612
- console.error('데이터 로드 중 오류:', error)
541
+ } catch (error) { console.error('데이터 로드 중 오류:', error)
613
542
  this.error = '데이터를 불러오는 중 오류가 발생했습니다.'
614
- } finally {
615
- this.loading = false
616
- }
617
- }
618
-
619
- private async _saveStatistics() {
620
- try {
621
- const patches: any[] = []
622
-
623
- this.kpis.forEach(kpi => {
624
- // dirty 상태인 데이터만 처리
625
- if (kpi.statistics.isDirty) {
626
- const existingStatistic = this._findExistingStatistic(kpi.kpi.id)
627
-
628
- if (existingStatistic) {
629
- // 기존 데이터 업데이트
543
+ } finally { this.loading = false
544
+ }
545
+ }
546
+
547
+ private async _saveStatistics() { try { const patches: any[] = []
548
+
549
+ this.kpis.forEach(kpi => { // dirty 상태인 데이터만 처리
550
+ if (kpi.statistics.isDirty) { const existingStatistic = this._findExistingStatistic(kpi.kpi.id)
551
+
552
+ if (existingStatistic) { // 기존 데이터 업데이트
630
553
  const { isDirty, ...statisticsWithoutDirty } = kpi.statistics
631
- patches.push({
632
- id: existingStatistic.id,
554
+ patches.push({ id: existingStatistic.id,
633
555
  ...statisticsWithoutDirty,
634
556
  cuFlag: 'M'
635
- })
636
- } else {
637
- // 새로운 데이터 생성
557
+ })
558
+ } else { // 새로운 데이터 생성
638
559
  const { isDirty, ...statisticsWithoutDirty } = kpi.statistics
639
- patches.push({
640
- kpi: {
641
- id: kpi.kpi.id
642
- },
560
+ patches.push({ kpi: { id: kpi.kpi.id
561
+ },
643
562
  valueDate: kpi.valueDate,
644
563
  periodType: kpi.periodType,
645
564
  ...statisticsWithoutDirty,
646
565
  additionalStatistics: {},
647
- metadata: {
648
- calculationMethod: 'manual',
566
+ metadata: { calculationMethod: 'manual',
649
567
  lastCalculated: new Date(),
650
568
  dataCount: kpi.statistics.count || 0
651
- },
569
+ },
652
570
  cuFlag: '+'
653
- })
654
- }
655
- }
656
- })
571
+ })
572
+ }
573
+ }
574
+ })
657
575
 
658
- if (patches.length === 0) {
659
- notify({ message: '저장할 데이터가 없습니다.' })
576
+ if (patches.length === 0) { notify({ message: '저장할 데이터가 없습니다.' })
660
577
  return
661
- }
662
-
663
- const response = await client.mutate({
664
- mutation: gql`
665
- mutation ($patches: [KpiStatisticPatch!]!) {
666
- updateMultipleKpiStatistic(patches: $patches) {
667
- id
668
- kpi {
669
- id
578
+ }
579
+
580
+ const response = await client.mutate({ mutation: gql`
581
+ mutation ($patches: [KpiStatisticPatch!]!) { updateMultipleKpiStatistic(patches: $patches) { id
582
+ kpi { id
670
583
  name
671
- }
584
+ }
672
585
  valueDate
673
586
  periodType
674
- }
675
- }
587
+ }
588
+ }
676
589
  `,
677
590
  variables: { patches }
678
- })
591
+ })
679
592
 
680
- if (!response.errors) {
681
- // 저장 dirty 상태 해제
682
- this.kpis.forEach(kpi => {
683
- kpi.statistics.isDirty = false
684
- })
593
+ if (!response.errors) { // 저장 후 dirty 상태 해제
594
+ this.kpis.forEach(kpi => { kpi.statistics.isDirty = false
595
+ })
685
596
 
686
597
  notify({ message: 'KPI 통계값이 성공적으로 저장되었습니다.' })
687
598
  this.requestUpdate()
688
- }
689
- } catch (error) {
690
- console.error('저장 중 오류:', error)
599
+ }
600
+ } catch (error) { console.error('저장 중 오류:', error)
691
601
  notify({ message: '저장 중 오류가 발생했습니다.' })
692
- }
693
- }
602
+ }
603
+ }
694
604
 
695
- private _findExistingStatistic(kpiId: string) {
696
- return this._existingStatistics?.find((s: any) => s.kpi === kpiId)
697
- }
605
+ private _findExistingStatistic(kpiId: string) { return this._existingStatistics?.find((s: any) => s.kpi === kpiId)
606
+ }
698
607
 
699
- private _onTargetDateChange(targetDate: string) {
700
- this.targetDate = targetDate
608
+ private _onTargetDateChange(targetDate: string) { this.targetDate = targetDate
701
609
  this._calculateDateRange()
702
- }
610
+ }
703
611
 
704
- private _onPeriodChange(periodType: string) {
705
- this.periodType = periodType
612
+ private _onPeriodChange(periodType: string) { this.periodType = periodType
706
613
  this._calculateDateRange()
707
- }
614
+ }
708
615
 
709
- private _calculateDateRange() {
710
- if (!this.targetDate || !this.periodType) return
616
+ private _calculateDateRange() { if (!this.targetDate || !this.periodType) return
711
617
 
712
618
  const target = new Date(this.targetDate)
713
619
  let valueDate: string
714
620
 
715
- switch (this.periodType) {
716
- case 'DAY':
621
+ switch (this.periodType) { case 'DAY':
717
622
  valueDate = target.toISOString().split('T')[0]
718
623
  break
719
624
  case 'WEEK':
@@ -736,25 +641,22 @@ export class KpiStatisticEditorPage extends connect(store)(localize(i18next)(Sco
736
641
  break
737
642
  default:
738
643
  valueDate = target.toISOString().split('T')[0]
739
- }
644
+ }
740
645
 
741
646
  this.valueDate = valueDate
742
- }
647
+ }
743
648
 
744
- private _cancel() {
745
- this.editingCell = null
649
+ private _cancel() { this.editingCell = null
746
650
  this._loadData() // 원본 데이터로 복원
747
- }
651
+ }
748
652
 
749
- async pageInitialized(lifecycle: any) {
750
- // 기본값 설정 - 현재 날짜를 기준으로
751
- if (!this.targetDate) {
752
- this.targetDate = new Date().toLocaleDateString('sv-SE')
753
- }
653
+ async pageInitialized(lifecycle: any) { // 기본값 설정 - 현재 날짜를 기준으로
654
+ if (!this.targetDate) { this.targetDate = new Date().toLocaleDateString('sv-SE')
655
+ }
754
656
 
755
657
  // 기간 유형에 따라 날짜 범위 계산
756
658
  this._calculateDateRange()
757
659
 
758
660
  await this._loadData()
759
- }
661
+ }
760
662
  }