@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,114 +3,99 @@ 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 KpiMetricValueData {
17
- metricId: string
15
+ interface KpiMetricValueData { metricId: string
18
16
  metricName: string
19
17
  periodType: string
20
18
  values: { [date: string]: { value: number | null; isDirty?: boolean } }
21
19
  }
22
20
 
23
- interface EditorCell {
24
- date: string
21
+ interface EditorCell { date: string
25
22
  value: number | null
26
23
  isEditable: boolean
27
24
  isHighlighted: boolean
28
25
  }
29
26
 
30
27
  @customElement('kpi-metric-value-editor-page')
31
- export class KpiMetricValueEditorPage extends connect(store)(localize(i18next)(ScopedElementsMixin(PageView))) {
32
- static styles = [
28
+ export class KpiMetricValueEditorPage extends localize(i18next)(ScopedElementsMixin(PageView)) { static styles = [
33
29
  CommonHeaderStyles,
34
30
  ScrollbarStyles,
35
31
  css`
36
- :host {
37
- display: flex;
32
+ :host { display: flex;
38
33
  flex-direction: column;
39
34
  padding: 20px;
40
35
  overflow-x: auto;
41
- }
36
+ }
42
37
 
43
- .header {
44
- display: flex;
38
+ .header { display: flex;
45
39
  gap: 16px;
46
40
  align-items: center;
47
41
  margin-bottom: 20px;
48
42
  padding: 16px;
49
43
  background: var(--md-sys-color-surface-container);
50
44
  border-radius: 8px;
51
- }
45
+ }
52
46
 
53
- .controls {
54
- display: flex;
47
+ .controls { display: flex;
55
48
  gap: 12px;
56
49
  align-items: center;
57
- }
50
+ }
58
51
 
59
- .table-container {
60
- flex: 1;
52
+ .table-container { flex: 1;
61
53
  overflow: auto;
62
54
  border: 1px solid var(--md-sys-color-outline);
63
55
  border-radius: 8px;
64
- }
56
+ }
65
57
 
66
- table {
67
- width: 100%;
58
+ table { width: 100%;
68
59
  border-collapse: collapse;
69
60
  min-width: max-content;
70
- }
61
+ }
71
62
 
72
- th {
73
- background: var(--md-sys-color-surface-container-low);
63
+ th { background: var(--md-sys-color-surface-container-low);
74
64
  font-weight: 500;
75
65
  padding: 8px 12px;
76
66
  border: 1px solid var(--md-sys-color-outline-variant);
77
67
  min-width: 80px;
78
68
  height: 120px;
79
69
  vertical-align: middle;
80
- }
70
+ }
81
71
 
82
- td {
83
- padding: 8px 12px;
72
+ td { padding: 8px 12px;
84
73
  border: 1px solid var(--md-sys-color-outline-variant);
85
74
  min-width: 80px;
86
75
  height: 60px;
87
76
  text-align: right;
88
77
  vertical-align: middle;
89
- }
78
+ }
90
79
 
91
- .metric-header {
92
- position: sticky;
80
+ .metric-header { position: sticky;
93
81
  left: 0;
94
82
  top: 0;
95
83
  z-index: 3;
96
- }
84
+ }
97
85
 
98
- .metric-name {
99
- position: sticky;
86
+ .metric-name { position: sticky;
100
87
  left: 0;
101
88
  background: var(--md-sys-color-surface);
102
89
  font-weight: 500;
103
90
  min-width: 200px;
104
91
  text-align: left;
105
92
  z-index: 2;
106
- }
93
+ }
107
94
 
108
- tr:hover {
109
- background: var(--md-sys-color-surface-container-high);
110
- }
95
+ tr:hover { background: var(--md-sys-color-surface-container-high);
96
+ }
111
97
 
112
- th.date-header {
113
- position: sticky;
98
+ th.date-header { position: sticky;
114
99
  top: 0;
115
100
  z-index: 2;
116
101
  text-align: center;
@@ -122,90 +107,77 @@ export class KpiMetricValueEditorPage extends connect(store)(localize(i18next)(S
122
107
  display: flex;
123
108
  align-items: center;
124
109
  justify-content: center;
125
- }
110
+ }
126
111
 
127
- td.editable-cell {
128
- cursor: pointer;
112
+ td.editable-cell { cursor: pointer;
129
113
  background: var(--md-sys-color-primary-container);
130
114
  color: var(--md-sys-color-on-primary-container);
131
- }
115
+ }
132
116
 
133
- td.editable-cell:hover {
134
- background: var(--md-sys-color-primary-container-high);
135
- }
117
+ td.editable-cell:hover { background: var(--md-sys-color-primary-container-high);
118
+ }
136
119
 
137
- td.highlighted-cell {
138
- background: var(--md-sys-color-secondary-container);
120
+ td.highlighted-cell { background: var(--md-sys-color-secondary-container);
139
121
  color: var(--md-sys-color-on-secondary-container);
140
122
  font-weight: 500;
141
- }
123
+ }
142
124
 
143
- td.highlighted-cell:hover {
144
- background: var(--md-sys-color-secondary-container-high);
145
- }
125
+ td.highlighted-cell:hover { background: var(--md-sys-color-secondary-container-high);
126
+ }
146
127
 
147
- td.disabled-cell {
148
- background: var(--md-sys-color-surface-container);
128
+ td.disabled-cell { background: var(--md-sys-color-surface-container);
149
129
  color: var(--md-sys-color-on-surface-variant);
150
130
  cursor: not-allowed;
151
- }
131
+ }
152
132
 
153
- .value-input {
154
- width: 100%;
133
+ .value-input { width: 100%;
155
134
  text-align: center;
156
135
  border: none;
157
136
  background: transparent;
158
137
  color: inherit;
159
138
  font-size: 12px;
160
139
  padding: 2px;
161
- }
140
+ }
162
141
 
163
- .value-input:focus {
164
- outline: 2px solid var(--md-sys-color-primary);
142
+ .value-input:focus { outline: 2px solid var(--md-sys-color-primary);
165
143
  border-radius: 4px;
166
- }
144
+ }
167
145
 
168
- .period-type-badge {
169
- font-size: 10px;
146
+ .period-type-badge { font-size: 10px;
170
147
  padding: 2px 6px;
171
148
  border-radius: 4px;
172
149
  background: var(--md-sys-color-tertiary-container);
173
150
  color: var(--md-sys-color-on-tertiary-container);
174
151
  margin-left: 8px;
175
- }
152
+ }
176
153
 
177
- .loading {
178
- display: flex;
154
+ .loading { display: flex;
179
155
  justify-content: center;
180
156
  align-items: center;
181
157
  height: 200px;
182
158
  color: var(--md-sys-color-on-surface-variant);
183
- }
159
+ }
184
160
 
185
- .error {
186
- color: var(--md-sys-color-error);
161
+ .error { color: var(--md-sys-color-error);
187
162
  padding: 16px;
188
163
  text-align: center;
189
- }
164
+ }
190
165
 
191
- .legend {
192
- display: flex;
166
+ .legend { display: flex;
193
167
  gap: 16px;
194
168
  margin-top: 16px;
195
169
  font-size: 12px;
196
- }
170
+ }
197
171
 
198
- .legend-item {
199
- display: flex;
172
+ .legend-item { display: flex;
200
173
  align-items: center;
201
174
  gap: 4px;
202
- }
175
+ }
203
176
 
204
- .legend-color {
205
- width: 16px;
177
+ .legend-color { width: 16px;
206
178
  height: 16px;
207
179
  border-radius: 2px;
208
- }
180
+ }
209
181
  `
210
182
  ]
211
183
 
@@ -220,42 +192,35 @@ export class KpiMetricValueEditorPage extends connect(store)(localize(i18next)(S
220
192
  @state() private editingCell: { metricId: string; date: string } | null = null
221
193
  @state() private _existingValues: any[] = []
222
194
 
223
- get context() {
224
- return {
225
- title: i18next.t('title.kpi metric value editor'),
195
+ get context() { return { title: i18next.t('title.kpi metric value editor'),
226
196
  actions: [
227
- {
228
- title: i18next.t('button.save'),
197
+ { title: i18next.t('button.save'),
229
198
  action: this._saveValues.bind(this),
230
199
  ...CommonButtonStyles.save
231
- },
232
- {
233
- title: i18next.t('button.cancel'),
200
+ },
201
+ { title: i18next.t('button.cancel'),
234
202
  action: this._cancel.bind(this),
235
203
  ...CommonButtonStyles.cancel
236
- }
204
+ }
237
205
  ]
238
- }
239
- }
206
+ }
207
+ }
240
208
 
241
- render() {
242
- if (this.loading) {
243
- return html`
209
+ render() { if (this.loading) { return html`
244
210
  <div class="loading">
245
211
  <md-icon>hourglass_empty</md-icon>
246
212
  <span>데이터를 불러오는 중...</span>
247
213
  </div>
248
214
  `
249
- }
215
+ }
250
216
 
251
- if (this.error) {
252
- return html`
217
+ if (this.error) { return html`
253
218
  <div class="error">
254
219
  <md-icon>error</md-icon>
255
220
  <span>${this.error}</span>
256
221
  </div>
257
222
  `
258
- }
223
+ }
259
224
 
260
225
  return html`
261
226
  <div class="header">
@@ -357,40 +322,32 @@ export class KpiMetricValueEditorPage extends connect(store)(localize(i18next)(S
357
322
  </div>
358
323
  </div>
359
324
  `
360
- }
325
+ }
361
326
 
362
- private _renderCellContent(metric: KpiMetricValueData, date: string) {
363
- const isEditing = this.editingCell?.metricId === metric.metricId && this.editingCell?.date === date
327
+ private _renderCellContent(metric: KpiMetricValueData, date: string) { const isEditing = this.editingCell?.metricId === metric.metricId && this.editingCell?.date === date
364
328
 
365
329
  // periodType에 따라 값을 가져오는 방식 결정
366
330
  let value: number | null = null
367
331
 
368
- if (metric.periodType === 'DAY') {
369
- // DAY인 경우 해당 날짜의 값을 사용
332
+ if (metric.periodType === 'DAY') { // DAY인 경우 해당 날짜의 값을 사용
370
333
  value = metric.values[date]?.value
371
- } else if (metric.periodType === 'WEEK') {
372
- // WEEK인 경우 해당 주의 월요일의 값을 사용
334
+ } else if (metric.periodType === 'WEEK') { // WEEK인 경우 해당 주의 월요일의 값을 사용
373
335
  const weekKey = this._getWeekKey(date)
374
336
  value = metric.values[weekKey]?.value
375
- } else if (metric.periodType === 'MONTH') {
376
- // MONTH인 경우 해당 월의 값을 사용
337
+ } else if (metric.periodType === 'MONTH') { // MONTH인 경우 해당 월의 값을 사용
377
338
  const monthKey = this._getMonthKey(date)
378
339
  value = metric.values[monthKey]?.value
379
- } else if (metric.periodType === 'QUARTER') {
380
- // QUARTER인 경우 해당 분기의 값을 사용
340
+ } else if (metric.periodType === 'QUARTER') { // QUARTER인 경우 해당 분기의 값을 사용
381
341
  const quarterKey = this._getQuarterKey(date)
382
342
  value = metric.values[quarterKey]?.value
383
- } else if (metric.periodType === 'YEAR') {
384
- // YEAR인 경우 해당 연도의 값을 사용
343
+ } else if (metric.periodType === 'YEAR') { // YEAR인 경우 해당 연도의 값을 사용
385
344
  const yearKey = this._getYearKey(date)
386
345
  value = metric.values[yearKey]?.value
387
- } else {
388
- // 기본값: DAY와 동일
346
+ } else { // 기본값: DAY와 동일
389
347
  value = metric.values[date]?.value
390
- }
348
+ }
391
349
 
392
- if (isEditing) {
393
- console.log('Rendering input for editing, value:', value)
350
+ if (isEditing) { console.log('Rendering input for editing, value:', value)
394
351
  return html`
395
352
  <input
396
353
  type="number"
@@ -402,7 +359,7 @@ export class KpiMetricValueEditorPage extends connect(store)(localize(i18next)(S
402
359
  autofocus
403
360
  />
404
361
  `
405
- }
362
+ }
406
363
 
407
364
  return html`
408
365
  <div style="display: flex; flex-direction: column; gap: 2px;">
@@ -411,36 +368,27 @@ export class KpiMetricValueEditorPage extends connect(store)(localize(i18next)(S
411
368
  </span>
412
369
  </div>
413
370
  `
414
- }
371
+ }
415
372
 
416
- private _formatDate(date: string): string {
417
- const d = new Date(date)
373
+ private _formatDate(date: string): string { const d = new Date(date)
418
374
  return d.toLocaleDateString('ko-KR', { month: 'numeric', day: 'numeric' })
419
- }
375
+ }
420
376
 
421
- private _startEdit(metricId: string, date: string) {
422
- this.editingCell = { metricId, date }
423
- }
377
+ private _startEdit(metricId: string, date: string) { this.editingCell = { metricId, date }
378
+ }
424
379
 
425
- private _finishEdit(metricId: string, date: string, value: number) {
426
- const metric = this.metrics.find(m => m.metricId === metricId)
427
- if (metric) {
428
- // periodType에 따라 저장할 키 결정
380
+ private _finishEdit(metricId: string, date: string, value: number) { const metric = this.metrics.find(m => m.metricId === metricId)
381
+ if (metric) { // periodType에 따라 저장할 결정
429
382
  let storageKey = date
430
383
 
431
- if (metric.periodType === 'WEEK') {
432
- storageKey = this._getWeekKey(date)
433
- } else if (metric.periodType === 'MONTH') {
434
- storageKey = this._getMonthKey(date)
435
- } else if (metric.periodType === 'QUARTER') {
436
- storageKey = this._getQuarterKey(date)
437
- } else if (metric.periodType === 'YEAR') {
438
- storageKey = this._getYearKey(date)
439
- }
384
+ if (metric.periodType === 'WEEK') { storageKey = this._getWeekKey(date)
385
+ } else if (metric.periodType === 'MONTH') { storageKey = this._getMonthKey(date)
386
+ } else if (metric.periodType === 'QUARTER') { storageKey = this._getQuarterKey(date)
387
+ } else if (metric.periodType === 'YEAR') { storageKey = this._getYearKey(date)
388
+ }
440
389
 
441
- if (!metric.values[storageKey]) {
442
- metric.values[storageKey] = { value: 0, isDirty: false }
443
- }
390
+ if (!metric.values[storageKey]) { metric.values[storageKey] = { value: 0, isDirty: false }
391
+ }
444
392
 
445
393
  // 값이 변경되었는지 확인
446
394
  const originalValue = this._findExistingValue(metricId, storageKey)?.value
@@ -448,139 +396,107 @@ export class KpiMetricValueEditorPage extends connect(store)(localize(i18next)(S
448
396
 
449
397
  metric.values[storageKey].value = value
450
398
  metric.values[storageKey].isDirty = isChanged
451
- }
399
+ }
452
400
  this.editingCell = null
453
- }
401
+ }
454
402
 
455
- private async _loadData() {
456
- if (!this.startDate || !this.endDate) {
457
- this.error = '시작일, 종료일을 모두 입력해주세요.'
403
+ private async _loadData() { if (!this.startDate || !this.endDate) { this.error = '시작일, 종료일을 모두 입력해주세요.'
458
404
  return
459
- }
405
+ }
460
406
 
461
407
  this.loading = true
462
408
  this.error = ''
463
409
 
464
- try {
465
- // KPI Metric 목록 조회
466
- const metricsResponse = await client.query({
467
- query: gql`
468
- query ($filters: [Filter!]) {
469
- kpiMetrics(filters: $filters) {
470
- items {
471
- id
410
+ try { // KPI Metric 목록 조회
411
+ const metricsResponse = await client.query({ query: gql`
412
+ query ($filters: [Filter!]) { kpiMetrics(filters: $filters) { items { id
472
413
  name
473
414
  periodType
474
415
  active
475
- }
416
+ }
476
417
  total
477
- }
478
- }
418
+ }
419
+ }
479
420
  `,
480
- variables: {
481
- filters: [{ name: 'active', operator: 'eq', value: true }]
482
- }
483
- })
421
+ variables: { filters: [{ name: 'active', operator: 'eq', value: true }]
422
+ }
423
+ })
484
424
 
485
425
  // KPI Metric Value 데이터 조회
486
- const valuesResponse = await client.query({
487
- query: gql`
488
- query ($filters: [Filter!]) {
489
- kpiMetricValues(filters: $filters) {
490
- items {
491
- id
426
+ const valuesResponse = await client.query({ query: gql`
427
+ query ($filters: [Filter!]) { kpiMetricValues(filters: $filters) { items { id
492
428
  metricId
493
429
  valueDate
494
430
  value
495
431
  org
496
- }
432
+ }
497
433
  total
498
- }
499
- }
434
+ }
435
+ }
500
436
  `,
501
- variables: {
502
- filters: [
437
+ variables: { filters: [
503
438
  ...(this.org ? [{ name: 'org', operator: 'eq', value: this.org }] : []),
504
439
  { name: 'valueDate', operator: 'between', value: [this.startDate, this.endDate] }
505
440
  ]
506
- }
507
- })
441
+ }
442
+ })
508
443
 
509
444
  // 날짜 배열 생성
510
445
  this.dates = this._generateDateArray(this.startDate, this.endDate)
511
446
 
512
447
  // KPI Metric 목록을 기준으로 데이터 구성
513
- this.metrics = metricsResponse.data.kpiMetrics.items.map((metric: any) => ({
514
- metricId: metric.id,
448
+ this.metrics = metricsResponse.data.kpiMetrics.items.map((metric: any) => ({ metricId: metric.id,
515
449
  metricName: metric.name,
516
450
  periodType: metric.periodType,
517
451
  values: {}
518
- }))
452
+ }))
519
453
 
520
454
  // 기존 KPI Metric Value 데이터 저장
521
455
  this._existingValues = valuesResponse.data.kpiMetricValues.items
522
456
 
523
457
  // KPI Metric Value 데이터를 해당 Metric에 매핑
524
- valuesResponse.data.kpiMetricValues.items.forEach((value: any) => {
525
- const metric = this.metrics.find(m => m.metricId === value.metricId)
526
- if (metric) {
527
- metric.values[value.valueDate] = {
528
- value: value.value,
458
+ valuesResponse.data.kpiMetricValues.items.forEach((value: any) => { const metric = this.metrics.find(m => m.metricId === value.metricId)
459
+ if (metric) { metric.values[value.valueDate] = { value: value.value,
529
460
  isDirty: false
530
- }
531
- }
532
- })
461
+ }
462
+ }
463
+ })
533
464
 
534
465
  // KPI Metric Value가 없는 Metric도 빈 값으로 초기화
535
- this.metrics.forEach(metric => {
536
- this.dates.forEach(date => {
537
- if (!metric.values[date]) {
538
- metric.values[date] = { value: null, isDirty: false }
539
- }
540
- })
541
- })
466
+ this.metrics.forEach(metric => { this.dates.forEach(date => { if (!metric.values[date]) { metric.values[date] = { value: null, isDirty: false }
467
+ }
468
+ })
469
+ })
542
470
 
543
471
  this.requestUpdate()
544
- } catch (error) {
545
- console.error('데이터 로드 중 오류:', error)
472
+ } catch (error) { console.error('데이터 로드 중 오류:', error)
546
473
  this.error = '데이터를 불러오는 중 오류가 발생했습니다.'
547
- } finally {
548
- this.loading = false
549
- }
550
- }
474
+ } finally { this.loading = false
475
+ }
476
+ }
551
477
 
552
- private _generateDateArray(startDate: string, endDate: string): string[] {
553
- const dates: string[] = []
478
+ private _generateDateArray(startDate: string, endDate: string): string[] { const dates: string[] = []
554
479
  const start = new Date(startDate)
555
480
  const end = new Date(endDate)
556
481
 
557
482
  // 일별로 생성 (기본)
558
- for (let d = new Date(start); d <= end; d.setDate(d.getDate() + 1)) {
559
- dates.push(d.toLocaleDateString('sv-SE'))
560
- }
483
+ for (let d = new Date(start); d <= end; d.setDate(d.getDate() + 1)) { dates.push(d.toLocaleDateString('sv-SE'))
484
+ }
561
485
 
562
486
  return dates
563
- }
487
+ }
564
488
 
565
- private async _saveValues() {
566
- try {
567
- const patches: any[] = []
489
+ private async _saveValues() { try { const patches: any[] = []
568
490
 
569
- this.metrics.forEach(metric => {
570
- Object.entries(metric.values).forEach(([date, data]) => {
571
- // dirty 상태인 데이터만 처리
572
- if (data.isDirty && data.value !== null && data.value !== undefined) {
573
- const existingValue = this._findExistingValue(metric.metricId, date)
491
+ this.metrics.forEach(metric => { Object.entries(metric.values).forEach(([date, data]) => { // dirty 상태인 데이터만 처리
492
+ if (data.isDirty && data.value !== null && data.value !== undefined) { const existingValue = this._findExistingValue(metric.metricId, date)
574
493
 
575
- if (existingValue) {
576
- patches.push({
577
- id: existingValue.id,
494
+ if (existingValue) { patches.push({ id: existingValue.id,
578
495
  value: data.value,
579
496
  org: this.org,
580
497
  cuFlag: 'M'
581
- })
582
- } else {
583
- console.log(
498
+ })
499
+ } else { console.log(
584
500
  'Creating new value for metric:',
585
501
  metric.metricName,
586
502
  'periodType:',
@@ -590,122 +506,102 @@ export class KpiMetricValueEditorPage extends connect(store)(localize(i18next)(S
590
506
  )
591
507
  const normalizedDate = this._normalizeDateByPeriodType(date, metric.periodType)
592
508
  console.log('Normalized date:', normalizedDate)
593
- patches.push({
594
- metricId: metric.metricId,
509
+ patches.push({ metricId: metric.metricId,
595
510
  valueDate: normalizedDate,
596
511
  value: data.value,
597
512
  org: this.org,
598
513
  cuFlag: '+'
599
- })
600
- }
601
- }
602
- })
603
- })
604
-
605
- if (patches.length === 0) {
606
- notify({ message: '저장할 데이터가 없습니다.' })
514
+ })
515
+ }
516
+ }
517
+ })
518
+ })
519
+
520
+ if (patches.length === 0) { notify({ message: '저장할 데이터가 없습니다.' })
607
521
  return
608
- }
522
+ }
609
523
 
610
524
  // 디버깅: patches 내용 확인
611
525
  console.log('Sending patches:', patches)
612
526
 
613
527
  // 단일 mutation으로 생성과 업데이트 처리
614
- const response = await client.mutate({
615
- mutation: gql`
616
- mutation ($patches: [KpiMetricValuePatch!]!) {
617
- updateMultipleKpiMetricValue(patches: $patches) {
618
- id
528
+ const response = await client.mutate({ mutation: gql`
529
+ mutation ($patches: [KpiMetricValuePatch!]!) { updateMultipleKpiMetricValue(patches: $patches) { id
619
530
  value
620
531
  valueDate
621
532
  metricId
622
- }
623
- }
533
+ }
534
+ }
624
535
  `,
625
536
  variables: { patches }
626
- })
537
+ })
627
538
 
628
- if (!response.errors) {
629
- // 저장 dirty 상태 해제
630
- response.data.updateMultipleKpiMetricValue.forEach((savedValue: any) => {
631
- const metric = this.metrics.find(m => m.metricId === savedValue.metricId)
632
- if (metric && metric.values[savedValue.valueDate]) {
633
- metric.values[savedValue.valueDate].isDirty = false
634
- }
635
- })
539
+ if (!response.errors) { // 저장 후 dirty 상태 해제
540
+ response.data.updateMultipleKpiMetricValue.forEach((savedValue: any) => { const metric = this.metrics.find(m => m.metricId === savedValue.metricId)
541
+ if (metric && metric.values[savedValue.valueDate]) { metric.values[savedValue.valueDate].isDirty = false
542
+ }
543
+ })
636
544
 
637
545
  notify({ message: 'KPI Metric 값이 성공적으로 저장되었습니다.' })
638
546
  this.requestUpdate()
639
- }
640
- } catch (error) {
641
- console.error('저장 중 오류:', error)
547
+ }
548
+ } catch (error) { console.error('저장 중 오류:', error)
642
549
  notify({ message: '저장 중 오류가 발생했습니다.' })
643
- }
644
- }
550
+ }
551
+ }
645
552
 
646
- private _findExistingValue(metricId: string, date: string) {
647
- // 기존 로드된 KPI Metric Value 데이터에서 찾기
553
+ private _findExistingValue(metricId: string, date: string) { // 기존 로드된 KPI Metric Value 데이터에서 찾기
648
554
  return this._existingValues?.find((v: any) => v.metricId === metricId && v.valueDate === date)
649
- }
555
+ }
650
556
 
651
- private _getMonthKey(date: string): string {
652
- const dateObj = new Date(date)
557
+ private _getMonthKey(date: string): string { const dateObj = new Date(date)
653
558
  return `${dateObj.getFullYear()}-${String(dateObj.getMonth() + 1).padStart(2, '0')}`
654
- }
559
+ }
655
560
 
656
- private _getQuarterKey(date: string): string {
657
- const dateObj = new Date(date)
561
+ private _getQuarterKey(date: string): string { const dateObj = new Date(date)
658
562
  const quarter = Math.floor(dateObj.getMonth() / 3) + 1
659
563
  return `${dateObj.getFullYear()}-Q${quarter}`
660
- }
564
+ }
661
565
 
662
- private _getYearKey(date: string): string {
663
- const dateObj = new Date(date)
566
+ private _getYearKey(date: string): string { const dateObj = new Date(date)
664
567
  return `${dateObj.getFullYear()}`
665
- }
568
+ }
666
569
 
667
- private _getWeekKey(date: string): string {
668
- const dateObj = new Date(date)
570
+ private _getWeekKey(date: string): string { const dateObj = new Date(date)
669
571
  const dayOfWeek = dateObj.getDay()
670
572
  const daysToMonday = dayOfWeek === 0 ? 6 : dayOfWeek - 1
671
573
  const monday = new Date(dateObj)
672
574
  monday.setDate(dateObj.getDate() - daysToMonday)
673
575
  return monday.toLocaleDateString('sv-SE')
674
- }
576
+ }
675
577
 
676
- private _getMondayOfWeek(date: string): string {
677
- const dateObj = new Date(date)
578
+ private _getMondayOfWeek(date: string): string { const dateObj = new Date(date)
678
579
  const dayOfWeek = dateObj.getDay()
679
580
  const daysToMonday = dayOfWeek === 0 ? 6 : dayOfWeek - 1
680
581
  const monday = new Date(dateObj)
681
582
  monday.setDate(dateObj.getDate() - daysToMonday)
682
583
  return monday.toLocaleDateString('sv-SE')
683
- }
584
+ }
684
585
 
685
- private _getFirstDayOfMonth(date: string): string {
686
- const dateObj = new Date(date)
586
+ private _getFirstDayOfMonth(date: string): string { const dateObj = new Date(date)
687
587
  return new Date(dateObj.getFullYear(), dateObj.getMonth(), 1).toLocaleDateString('sv-SE')
688
- }
588
+ }
689
589
 
690
- private _getFirstDayOfQuarter(date: string): string {
691
- const dateObj = new Date(date)
590
+ private _getFirstDayOfQuarter(date: string): string { const dateObj = new Date(date)
692
591
  const quarter = Math.floor(dateObj.getMonth() / 3)
693
592
  const firstMonthOfQuarter = quarter * 3
694
593
  return new Date(dateObj.getFullYear(), firstMonthOfQuarter, 1).toLocaleDateString('sv-SE')
695
- }
594
+ }
696
595
 
697
- private _getFirstDayOfYear(date: string): string {
698
- const dateObj = new Date(date)
596
+ private _getFirstDayOfYear(date: string): string { const dateObj = new Date(date)
699
597
  return new Date(dateObj.getFullYear(), 0, 1).toLocaleDateString('sv-SE')
700
- }
598
+ }
701
599
 
702
- private _normalizeDateByPeriodType(date: string, periodType: string): string {
703
- console.log('Normalizing date:', date, 'for periodType:', periodType)
600
+ private _normalizeDateByPeriodType(date: string, periodType: string): string { console.log('Normalizing date:', date, 'for periodType:', periodType)
704
601
 
705
602
  const dateObj = new Date(date)
706
603
 
707
- switch (periodType) {
708
- case 'DAY':
604
+ switch (periodType) { case 'DAY':
709
605
  console.log('DAY - returning original date:', date)
710
606
  return date // 그대로 사용
711
607
  case 'WEEK':
@@ -736,28 +632,24 @@ export class KpiMetricValueEditorPage extends connect(store)(localize(i18next)(S
736
632
  default:
737
633
  console.log('DEFAULT - returning original date:', date)
738
634
  return date // 기본값
739
- }
740
- }
635
+ }
636
+ }
741
637
 
742
- private _cancel() {
743
- // 편집 취소 로직
638
+ private _cancel() { // 편집 취소 로직
744
639
  this.editingCell = null
745
640
  this._loadData() // 원본 데이터로 복원
746
- }
641
+ }
747
642
 
748
- async pageInitialized(lifecycle: any) {
749
- // 기본값 설정 - 지난 1개월
750
- if (!this.startDate) {
751
- const today = new Date()
643
+ async pageInitialized(lifecycle: any) { // 기본값 설정 - 지난 1개월
644
+ if (!this.startDate) { const today = new Date()
752
645
  const oneMonthAgo = new Date(today.getFullYear(), today.getMonth() - 1, today.getDate())
753
646
  this.startDate = oneMonthAgo.toLocaleDateString('sv-SE')
754
- }
755
- if (!this.endDate) {
756
- const today = new Date()
647
+ }
648
+ if (!this.endDate) { const today = new Date()
757
649
  this.endDate = today.toLocaleDateString('sv-SE')
758
- }
650
+ }
759
651
 
760
652
  // 페이지 초기화 시 자동으로 데이터 로드
761
653
  await this._loadData()
762
- }
654
+ }
763
655
  }