@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.
- package/client/pages/kpi/kpi-list-page.ts +339 -525
- package/client/pages/kpi/kpi-tree-page.ts +135 -207
- package/client/pages/kpi-metric/kpi-metric-list-page.ts +146 -226
- package/client/pages/kpi-metric-value/kpi-metric-value-editor-page.ts +187 -295
- package/client/pages/kpi-metric-value/kpi-metric-value-list-page.ts +123 -194
- package/client/pages/kpi-metric-value/kpi-metric-value-manual-entry-page.ts +57 -91
- package/client/pages/kpi-statistic/kpi-statistic-editor-page.ts +180 -278
- package/client/pages/kpi-statistic/kpi-statistic-list-page.ts +186 -286
- package/client/pages/kpi-value/kpi-value-editor-page.ts +189 -292
- package/client/pages/kpi-value/kpi-value-list-page.ts +170 -264
- package/dist-client/pages/kpi/kpi-list-page.d.ts +0 -6
- package/dist-client/pages/kpi/kpi-list-page.js +150 -282
- package/dist-client/pages/kpi/kpi-list-page.js.map +1 -1
- package/dist-client/pages/kpi/kpi-tree-page.d.ts +1 -7
- package/dist-client/pages/kpi/kpi-tree-page.js +76 -127
- package/dist-client/pages/kpi/kpi-tree-page.js.map +1 -1
- package/dist-client/pages/kpi-metric/kpi-metric-list-page.d.ts +0 -6
- package/dist-client/pages/kpi-metric/kpi-metric-list-page.js +62 -116
- package/dist-client/pages/kpi-metric/kpi-metric-list-page.js.map +1 -1
- package/dist-client/pages/kpi-metric-value/kpi-metric-value-editor-page.d.ts +1 -7
- package/dist-client/pages/kpi-metric-value/kpi-metric-value-editor-page.js +82 -140
- package/dist-client/pages/kpi-metric-value/kpi-metric-value-editor-page.js.map +1 -1
- package/dist-client/pages/kpi-metric-value/kpi-metric-value-list-page.d.ts +0 -6
- package/dist-client/pages/kpi-metric-value/kpi-metric-value-list-page.js +54 -98
- package/dist-client/pages/kpi-metric-value/kpi-metric-value-list-page.js.map +1 -1
- package/dist-client/pages/kpi-metric-value/kpi-metric-value-manual-entry-page.d.ts +1 -7
- package/dist-client/pages/kpi-metric-value/kpi-metric-value-manual-entry-page.js +30 -57
- package/dist-client/pages/kpi-metric-value/kpi-metric-value-manual-entry-page.js.map +1 -1
- package/dist-client/pages/kpi-statistic/kpi-statistic-editor-page.d.ts +1 -7
- package/dist-client/pages/kpi-statistic/kpi-statistic-editor-page.js +91 -153
- package/dist-client/pages/kpi-statistic/kpi-statistic-editor-page.js.map +1 -1
- package/dist-client/pages/kpi-statistic/kpi-statistic-list-page.d.ts +0 -6
- package/dist-client/pages/kpi-statistic/kpi-statistic-list-page.js +81 -155
- package/dist-client/pages/kpi-statistic/kpi-statistic-list-page.js.map +1 -1
- package/dist-client/pages/kpi-value/kpi-value-editor-page.d.ts +1 -7
- package/dist-client/pages/kpi-value/kpi-value-editor-page.js +80 -136
- package/dist-client/pages/kpi-value/kpi-value-editor-page.js.map +1 -1
- package/dist-client/pages/kpi-value/kpi-value-list-page.d.ts +0 -6
- package/dist-client/pages/kpi-value/kpi-value-list-page.js +73 -134
- package/dist-client/pages/kpi-value/kpi-value-list-page.js.map +1 -1
- package/dist-client/tsconfig.tsbuildinfo +1 -1
- package/dist-server/service/index.d.ts +1 -1
- package/dist-server/tsconfig.tsbuildinfo +1 -1
- package/package.json +18 -18
- package/client/tsconfig.json +0 -11
- package/dist-server/tsconfig.json +0 -10
- package/server/@types/index.d.ts +0 -11
- package/server/calculator/evaluator.ts +0 -45
- package/server/calculator/functions.ts +0 -67
- package/server/calculator/index.ts +0 -4
- package/server/calculator/parser.ts +0 -137
- package/server/calculator/provider.ts +0 -10
- package/server/controllers/index.ts +0 -2
- package/server/controllers/kpi-metric-value-provider.ts +0 -79
- package/server/controllers/kpi-value-provider.ts +0 -51
- package/server/index.ts +0 -6
- package/server/migrations/1752190849680-seed-kpi-metrics.ts +0 -124
- package/server/migrations/1752190849681-seed-kpi.ts +0 -356
- package/server/migrations/1752192090123-add-grades-to-kpi.ts +0 -67
- package/server/migrations/1752192090124-add-kpi-statistics.ts +0 -719
- package/server/migrations/1752192090128-seed-kpi-org-scope.ts +0 -132
- package/server/migrations/1752192090129-seed-kpi-values.ts +0 -207
- package/server/migrations/grade-data/x11-performance-table.json +0 -962
- package/server/migrations/grade-data/x12-performance-table.json +0 -611
- package/server/migrations/grade-data/x14-performance-table.json +0 -42
- package/server/migrations/grade-data/x21-performance-table.json +0 -889
- package/server/migrations/grade-data/x22-performance-table.json +0 -1064
- package/server/migrations/grade-data/x23-performance-table.json +0 -42
- package/server/migrations/grade-data/x31-performance-table.json +0 -644
- package/server/migrations/grade-data/x32-performance-table.json +0 -993
- package/server/migrations/grade-data/x33-performance-table.json +0 -195
- package/server/migrations/grade-data/x34-performance-table.json +0 -12
- package/server/migrations/grade-data/x35-performance-table.json +0 -42
- package/server/migrations/grade-data/x41-performance-table.json +0 -825
- package/server/migrations/grade-data/x42-performance-table.json +0 -786
- package/server/migrations/grade-data/x43-performance-table.json +0 -12
- package/server/migrations/grade-data/x44-performance-table.json +0 -42
- package/server/migrations/grade-data/x51-performance-table.json +0 -924
- package/server/migrations/grade-data/x52-performance-table.json +0 -42
- package/server/migrations/grade-data/x61-performance-table.json +0 -261
- package/server/migrations/grade-data/x62-performance-table.json +0 -42
- package/server/migrations/index.ts +0 -9
- package/server/migrations/seed-data/kpi-metrics-seed.json +0 -454
- package/server/migrations/seed-data/kpi-org-scope-seed.json +0 -1676
- package/server/migrations/seed-data/kpi-scopes-seed.json +0 -121
- package/server/migrations/seed-data/kpi-values-seed.json +0 -402
- package/server/migrations/seed-data/kpis-seed.json +0 -488
- package/server/migrations/seed-data/scope-definitions-seed.json +0 -90
- package/server/routes.ts +0 -81
- package/server/service/index.ts +0 -51
- package/server/service/kpi/aggregate-kpi.ts +0 -103
- package/server/service/kpi/event-subscriber.ts +0 -29
- package/server/service/kpi/index.ts +0 -9
- package/server/service/kpi/kpi-formula.service.ts +0 -164
- package/server/service/kpi/kpi-grade.types.ts +0 -28
- package/server/service/kpi/kpi-history.ts +0 -126
- package/server/service/kpi/kpi-mutation.ts +0 -553
- package/server/service/kpi/kpi-query.ts +0 -224
- package/server/service/kpi/kpi-type.ts +0 -151
- package/server/service/kpi/kpi.ts +0 -254
- package/server/service/kpi-alert/index.ts +0 -3
- package/server/service/kpi-alert/kpi-alert-query.ts +0 -59
- package/server/service/kpi-alert/kpi-alert-type.ts +0 -20
- package/server/service/kpi-metric/aggregate-kpi-metric.ts +0 -132
- package/server/service/kpi-metric/index.ts +0 -7
- package/server/service/kpi-metric/kpi-metric-mutation.ts +0 -309
- package/server/service/kpi-metric/kpi-metric-query.ts +0 -70
- package/server/service/kpi-metric/kpi-metric-type.ts +0 -111
- package/server/service/kpi-metric/kpi-metric.ts +0 -134
- package/server/service/kpi-metric-value/index.ts +0 -7
- package/server/service/kpi-metric-value/kpi-metric-value-mutation.ts +0 -270
- package/server/service/kpi-metric-value/kpi-metric-value-query.ts +0 -62
- package/server/service/kpi-metric-value/kpi-metric-value-type.ts +0 -82
- package/server/service/kpi-metric-value/kpi-metric-value.ts +0 -93
- package/server/service/kpi-org-scope/index.ts +0 -6
- package/server/service/kpi-org-scope/kpi-org-scope-mutation.ts +0 -173
- package/server/service/kpi-org-scope/kpi-org-scope-query.ts +0 -127
- package/server/service/kpi-org-scope/kpi-org-scope-type.ts +0 -68
- package/server/service/kpi-org-scope/kpi-org-scope.ts +0 -123
- package/server/service/kpi-scope/index.ts +0 -11
- package/server/service/kpi-scope/kpi-scope-mutation.ts +0 -129
- package/server/service/kpi-scope/kpi-scope-query.ts +0 -63
- package/server/service/kpi-scope/kpi-scope-type.ts +0 -96
- package/server/service/kpi-scope/kpi-scope.ts +0 -143
- package/server/service/kpi-statistic/index.ts +0 -7
- package/server/service/kpi-statistic/kpi-statistic-batch.service.ts +0 -231
- package/server/service/kpi-statistic/kpi-statistic-calculation.service.ts +0 -410
- package/server/service/kpi-statistic/kpi-statistic-mutation.ts +0 -291
- package/server/service/kpi-statistic/kpi-statistic-query.ts +0 -146
- package/server/service/kpi-statistic/kpi-statistic-type.ts +0 -152
- package/server/service/kpi-statistic/kpi-statistic.ts +0 -199
- package/server/service/kpi-value/index.ts +0 -7
- package/server/service/kpi-value/kpi-value-mutation.ts +0 -432
- package/server/service/kpi-value/kpi-value-query.ts +0 -61
- package/server/service/kpi-value/kpi-value-score.service.ts +0 -106
- package/server/service/kpi-value/kpi-value-type.ts +0 -122
- package/server/service/kpi-value/kpi-value.ts +0 -160
- package/server/service/utils/value-date-util.ts +0 -119
- package/server/tsconfig.json +0 -10
- package/server/types/global.d.ts +0 -8
|
@@ -3,25 +3,22 @@ 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
|
|
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 KpiValueData {
|
|
17
|
-
kpiId: string
|
|
15
|
+
interface KpiValueData { kpiId: string
|
|
18
16
|
kpiName: string
|
|
19
17
|
periodType: string
|
|
20
18
|
values: { [date: string]: { value: number | null; score?: number; isDirty?: boolean } }
|
|
21
19
|
}
|
|
22
20
|
|
|
23
|
-
interface EditorCell {
|
|
24
|
-
date: string
|
|
21
|
+
interface EditorCell { date: string
|
|
25
22
|
value: number | null
|
|
26
23
|
score?: number
|
|
27
24
|
isEditable: boolean
|
|
@@ -29,89 +26,77 @@ interface EditorCell {
|
|
|
29
26
|
}
|
|
30
27
|
|
|
31
28
|
@customElement('kpi-value-editor-page')
|
|
32
|
-
export class KpiValueEditorPage extends
|
|
33
|
-
static styles = [
|
|
29
|
+
export class KpiValueEditorPage extends localize(i18next)(ScopedElementsMixin(PageView)) { static styles = [
|
|
34
30
|
CommonHeaderStyles,
|
|
35
31
|
ScrollbarStyles,
|
|
36
32
|
css`
|
|
37
|
-
:host {
|
|
38
|
-
display: flex;
|
|
33
|
+
:host { display: flex;
|
|
39
34
|
flex-direction: column;
|
|
40
35
|
padding: 20px;
|
|
41
36
|
overflow-x: auto;
|
|
42
|
-
|
|
37
|
+
}
|
|
43
38
|
|
|
44
|
-
.header {
|
|
45
|
-
display: flex;
|
|
39
|
+
.header { display: flex;
|
|
46
40
|
gap: 16px;
|
|
47
41
|
align-items: center;
|
|
48
42
|
margin-bottom: 20px;
|
|
49
43
|
padding: 16px;
|
|
50
44
|
background: var(--md-sys-color-surface-container);
|
|
51
45
|
border-radius: 8px;
|
|
52
|
-
|
|
46
|
+
}
|
|
53
47
|
|
|
54
|
-
.controls {
|
|
55
|
-
display: flex;
|
|
48
|
+
.controls { display: flex;
|
|
56
49
|
gap: 12px;
|
|
57
50
|
align-items: center;
|
|
58
|
-
|
|
51
|
+
}
|
|
59
52
|
|
|
60
|
-
.table-container {
|
|
61
|
-
flex: 1;
|
|
53
|
+
.table-container { flex: 1;
|
|
62
54
|
overflow: auto;
|
|
63
55
|
border: 1px solid var(--md-sys-color-outline);
|
|
64
56
|
border-radius: 8px;
|
|
65
|
-
|
|
57
|
+
}
|
|
66
58
|
|
|
67
|
-
table {
|
|
68
|
-
width: 100%;
|
|
59
|
+
table { width: 100%;
|
|
69
60
|
border-collapse: collapse;
|
|
70
61
|
min-width: max-content;
|
|
71
|
-
|
|
62
|
+
}
|
|
72
63
|
|
|
73
|
-
th {
|
|
74
|
-
background: var(--md-sys-color-surface-container-low);
|
|
64
|
+
th { background: var(--md-sys-color-surface-container-low);
|
|
75
65
|
font-weight: 500;
|
|
76
66
|
padding: 8px 12px;
|
|
77
67
|
border: 1px solid var(--md-sys-color-outline-variant);
|
|
78
68
|
min-width: 80px;
|
|
79
69
|
height: 120px;
|
|
80
70
|
vertical-align: middle;
|
|
81
|
-
|
|
71
|
+
}
|
|
82
72
|
|
|
83
|
-
td {
|
|
84
|
-
padding: 8px 12px;
|
|
73
|
+
td { padding: 8px 12px;
|
|
85
74
|
border: 1px solid var(--md-sys-color-outline-variant);
|
|
86
75
|
min-width: 80px;
|
|
87
76
|
height: 60px;
|
|
88
77
|
text-align: right;
|
|
89
78
|
vertical-align: middle;
|
|
90
|
-
|
|
79
|
+
}
|
|
91
80
|
|
|
92
|
-
.kpi-header {
|
|
93
|
-
position: sticky;
|
|
81
|
+
.kpi-header { position: sticky;
|
|
94
82
|
left: 0;
|
|
95
83
|
top: 0;
|
|
96
84
|
z-index: 3;
|
|
97
|
-
|
|
85
|
+
}
|
|
98
86
|
|
|
99
|
-
.kpi-name {
|
|
100
|
-
position: sticky;
|
|
87
|
+
.kpi-name { position: sticky;
|
|
101
88
|
left: 0;
|
|
102
89
|
background: var(--md-sys-color-surface);
|
|
103
90
|
font-weight: 500;
|
|
104
91
|
min-width: 200px;
|
|
105
92
|
text-align: left;
|
|
106
93
|
z-index: 2;
|
|
107
|
-
|
|
94
|
+
}
|
|
108
95
|
|
|
109
|
-
tr:hover {
|
|
110
|
-
|
|
111
|
-
}
|
|
96
|
+
tr:hover { background: var(--md-sys-color-surface-container-high);
|
|
97
|
+
}
|
|
112
98
|
|
|
113
|
-
th.date-header {
|
|
114
|
-
position: sticky;
|
|
99
|
+
th.date-header { position: sticky;
|
|
115
100
|
top: 0;
|
|
116
101
|
z-index: 2;
|
|
117
102
|
text-align: center;
|
|
@@ -123,90 +108,77 @@ export class KpiValueEditorPage extends connect(store)(localize(i18next)(ScopedE
|
|
|
123
108
|
display: flex;
|
|
124
109
|
align-items: center;
|
|
125
110
|
justify-content: center;
|
|
126
|
-
|
|
111
|
+
}
|
|
127
112
|
|
|
128
|
-
td.editable-cell {
|
|
129
|
-
cursor: pointer;
|
|
113
|
+
td.editable-cell { cursor: pointer;
|
|
130
114
|
background: var(--md-sys-color-primary-container);
|
|
131
115
|
color: var(--md-sys-color-on-primary-container);
|
|
132
|
-
|
|
116
|
+
}
|
|
133
117
|
|
|
134
|
-
td.editable-cell:hover {
|
|
135
|
-
|
|
136
|
-
}
|
|
118
|
+
td.editable-cell:hover { background: var(--md-sys-color-primary-container-high);
|
|
119
|
+
}
|
|
137
120
|
|
|
138
|
-
td.highlighted-cell {
|
|
139
|
-
background: var(--md-sys-color-secondary-container);
|
|
121
|
+
td.highlighted-cell { background: var(--md-sys-color-secondary-container);
|
|
140
122
|
color: var(--md-sys-color-on-secondary-container);
|
|
141
123
|
font-weight: 500;
|
|
142
|
-
|
|
124
|
+
}
|
|
143
125
|
|
|
144
|
-
td.highlighted-cell:hover {
|
|
145
|
-
|
|
146
|
-
}
|
|
126
|
+
td.highlighted-cell:hover { background: var(--md-sys-color-secondary-container-high);
|
|
127
|
+
}
|
|
147
128
|
|
|
148
|
-
td.disabled-cell {
|
|
149
|
-
background: var(--md-sys-color-surface-container);
|
|
129
|
+
td.disabled-cell { background: var(--md-sys-color-surface-container);
|
|
150
130
|
color: var(--md-sys-color-on-surface-variant);
|
|
151
131
|
cursor: not-allowed;
|
|
152
|
-
|
|
132
|
+
}
|
|
153
133
|
|
|
154
|
-
.value-input {
|
|
155
|
-
width: 100%;
|
|
134
|
+
.value-input { width: 100%;
|
|
156
135
|
text-align: center;
|
|
157
136
|
border: none;
|
|
158
137
|
background: transparent;
|
|
159
138
|
color: inherit;
|
|
160
139
|
font-size: 12px;
|
|
161
140
|
padding: 2px;
|
|
162
|
-
|
|
141
|
+
}
|
|
163
142
|
|
|
164
|
-
.value-input:focus {
|
|
165
|
-
outline: 2px solid var(--md-sys-color-primary);
|
|
143
|
+
.value-input:focus { outline: 2px solid var(--md-sys-color-primary);
|
|
166
144
|
border-radius: 4px;
|
|
167
|
-
|
|
145
|
+
}
|
|
168
146
|
|
|
169
|
-
.period-type-badge {
|
|
170
|
-
font-size: 10px;
|
|
147
|
+
.period-type-badge { font-size: 10px;
|
|
171
148
|
padding: 2px 6px;
|
|
172
149
|
border-radius: 4px;
|
|
173
150
|
background: var(--md-sys-color-tertiary-container);
|
|
174
151
|
color: var(--md-sys-color-on-tertiary-container);
|
|
175
152
|
margin-left: 8px;
|
|
176
|
-
|
|
153
|
+
}
|
|
177
154
|
|
|
178
|
-
.loading {
|
|
179
|
-
display: flex;
|
|
155
|
+
.loading { display: flex;
|
|
180
156
|
justify-content: center;
|
|
181
157
|
align-items: center;
|
|
182
158
|
height: 200px;
|
|
183
159
|
color: var(--md-sys-color-on-surface-variant);
|
|
184
|
-
|
|
160
|
+
}
|
|
185
161
|
|
|
186
|
-
.error {
|
|
187
|
-
color: var(--md-sys-color-error);
|
|
162
|
+
.error { color: var(--md-sys-color-error);
|
|
188
163
|
padding: 16px;
|
|
189
164
|
text-align: center;
|
|
190
|
-
|
|
165
|
+
}
|
|
191
166
|
|
|
192
|
-
.legend {
|
|
193
|
-
display: flex;
|
|
167
|
+
.legend { display: flex;
|
|
194
168
|
gap: 16px;
|
|
195
169
|
margin-top: 16px;
|
|
196
170
|
font-size: 12px;
|
|
197
|
-
|
|
171
|
+
}
|
|
198
172
|
|
|
199
|
-
.legend-item {
|
|
200
|
-
display: flex;
|
|
173
|
+
.legend-item { display: flex;
|
|
201
174
|
align-items: center;
|
|
202
175
|
gap: 4px;
|
|
203
|
-
|
|
176
|
+
}
|
|
204
177
|
|
|
205
|
-
.legend-color {
|
|
206
|
-
width: 16px;
|
|
178
|
+
.legend-color { width: 16px;
|
|
207
179
|
height: 16px;
|
|
208
180
|
border-radius: 2px;
|
|
209
|
-
|
|
181
|
+
}
|
|
210
182
|
`
|
|
211
183
|
]
|
|
212
184
|
|
|
@@ -222,42 +194,35 @@ export class KpiValueEditorPage extends connect(store)(localize(i18next)(ScopedE
|
|
|
222
194
|
@state() private editingCell: { kpiId: string; date: string } | null = null
|
|
223
195
|
@state() private _existingValues: any[] = []
|
|
224
196
|
|
|
225
|
-
get context() {
|
|
226
|
-
return {
|
|
227
|
-
title: i18next.t('title.kpi value editor'),
|
|
197
|
+
get context() { return { title: i18next.t('title.kpi value editor'),
|
|
228
198
|
actions: [
|
|
229
|
-
{
|
|
230
|
-
title: i18next.t('button.save'),
|
|
199
|
+
{ title: i18next.t('button.save'),
|
|
231
200
|
action: this._saveValues.bind(this),
|
|
232
201
|
...CommonButtonStyles.save
|
|
233
|
-
|
|
234
|
-
{
|
|
235
|
-
title: i18next.t('button.cancel'),
|
|
202
|
+
},
|
|
203
|
+
{ title: i18next.t('button.cancel'),
|
|
236
204
|
action: this._cancel.bind(this),
|
|
237
205
|
...CommonButtonStyles.cancel
|
|
238
|
-
|
|
206
|
+
}
|
|
239
207
|
]
|
|
240
|
-
|
|
241
|
-
|
|
208
|
+
}
|
|
209
|
+
}
|
|
242
210
|
|
|
243
|
-
render() {
|
|
244
|
-
if (this.loading) {
|
|
245
|
-
return html`
|
|
211
|
+
render() { if (this.loading) { return html`
|
|
246
212
|
<div class="loading">
|
|
247
213
|
<md-icon>hourglass_empty</md-icon>
|
|
248
214
|
<span>데이터를 불러오는 중...</span>
|
|
249
215
|
</div>
|
|
250
216
|
`
|
|
251
|
-
|
|
217
|
+
}
|
|
252
218
|
|
|
253
|
-
if (this.error) {
|
|
254
|
-
return html`
|
|
219
|
+
if (this.error) { return html`
|
|
255
220
|
<div class="error">
|
|
256
221
|
<md-icon>error</md-icon>
|
|
257
222
|
<span>${this.error}</span>
|
|
258
223
|
</div>
|
|
259
224
|
`
|
|
260
|
-
|
|
225
|
+
}
|
|
261
226
|
|
|
262
227
|
return html`
|
|
263
228
|
<div class="header">
|
|
@@ -360,13 +325,11 @@ export class KpiValueEditorPage extends connect(store)(localize(i18next)(ScopedE
|
|
|
360
325
|
</div>
|
|
361
326
|
</div>
|
|
362
327
|
`
|
|
363
|
-
|
|
328
|
+
}
|
|
364
329
|
|
|
365
|
-
private _renderKpiCells(kpi: KpiValueData) {
|
|
366
|
-
const cells = this._getCellsForKpi(kpi)
|
|
330
|
+
private _renderKpiCells(kpi: KpiValueData) { const cells = this._getCellsForKpi(kpi)
|
|
367
331
|
|
|
368
|
-
return cells.map(cell => {
|
|
369
|
-
const isEditing = this.editingCell?.kpiId === kpi.kpiId && this.editingCell?.date === cell.date
|
|
332
|
+
return cells.map(cell => { const isEditing = this.editingCell?.kpiId === kpi.kpiId && this.editingCell?.date === cell.date
|
|
370
333
|
const cellClass = this._getCellClass(cell)
|
|
371
334
|
|
|
372
335
|
return html`
|
|
@@ -386,34 +349,29 @@ export class KpiValueEditorPage extends connect(store)(localize(i18next)(ScopedE
|
|
|
386
349
|
: html` <span>${cell.value !== null ? cell.value.toLocaleString() : '-'}</span> `}
|
|
387
350
|
</div>
|
|
388
351
|
`
|
|
389
|
-
|
|
390
|
-
|
|
352
|
+
})
|
|
353
|
+
}
|
|
391
354
|
|
|
392
|
-
private _getCellsForKpi(kpi: KpiValueData): EditorCell[] {
|
|
393
|
-
const cells: EditorCell[] = []
|
|
355
|
+
private _getCellsForKpi(kpi: KpiValueData): EditorCell[] { const cells: EditorCell[] = []
|
|
394
356
|
const periodType = kpi.periodType
|
|
395
357
|
|
|
396
|
-
this.dates.forEach(date => {
|
|
397
|
-
const isEditable = this._isDateEditableForPeriodType(date, periodType)
|
|
358
|
+
this.dates.forEach(date => { const isEditable = this._isDateEditableForPeriodType(date, periodType)
|
|
398
359
|
const isHighlighted = this._isDateHighlightedForPeriodType(date, periodType)
|
|
399
360
|
|
|
400
|
-
cells.push({
|
|
401
|
-
date,
|
|
361
|
+
cells.push({ date,
|
|
402
362
|
value: kpi.values[date]?.value || null,
|
|
403
363
|
score: kpi.values[date]?.score,
|
|
404
364
|
isEditable,
|
|
405
365
|
isHighlighted
|
|
406
|
-
|
|
407
|
-
|
|
366
|
+
})
|
|
367
|
+
})
|
|
408
368
|
|
|
409
369
|
return cells
|
|
410
|
-
|
|
370
|
+
}
|
|
411
371
|
|
|
412
|
-
private _isDateEditableForPeriodType(date: string, periodType: string): boolean {
|
|
413
|
-
const targetDate = new Date(date)
|
|
372
|
+
private _isDateEditableForPeriodType(date: string, periodType: string): boolean { const targetDate = new Date(date)
|
|
414
373
|
|
|
415
|
-
switch (periodType) {
|
|
416
|
-
case 'DAY':
|
|
374
|
+
switch (periodType) { case 'DAY':
|
|
417
375
|
return true // 모든 날짜 편집 가능
|
|
418
376
|
case 'WEEK':
|
|
419
377
|
// 해당 주의 첫 번째 날짜만 편집 가능
|
|
@@ -433,14 +391,12 @@ export class KpiValueEditorPage extends connect(store)(localize(i18next)(ScopedE
|
|
|
433
391
|
return date === yearStart.toISOString().split('T')[0]
|
|
434
392
|
default:
|
|
435
393
|
return true
|
|
436
|
-
|
|
437
|
-
|
|
394
|
+
}
|
|
395
|
+
}
|
|
438
396
|
|
|
439
|
-
private _isDateHighlightedForPeriodType(date: string, periodType: string): boolean {
|
|
440
|
-
const targetDate = new Date(date)
|
|
397
|
+
private _isDateHighlightedForPeriodType(date: string, periodType: string): boolean { const targetDate = new Date(date)
|
|
441
398
|
|
|
442
|
-
switch (periodType) {
|
|
443
|
-
case 'DAY':
|
|
399
|
+
switch (periodType) { case 'DAY':
|
|
444
400
|
return false // 하이라이트 없음
|
|
445
401
|
case 'WEEK':
|
|
446
402
|
// 해당 주의 모든 날짜 하이라이트
|
|
@@ -467,37 +423,28 @@ export class KpiValueEditorPage extends connect(store)(localize(i18next)(ScopedE
|
|
|
467
423
|
return date >= yearStart.toISOString().split('T')[0] && date <= yearEnd.toISOString().split('T')[0]
|
|
468
424
|
default:
|
|
469
425
|
return false
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
private _getCellClass(cell: EditorCell): string {
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
if (cell.
|
|
478
|
-
|
|
479
|
-
}
|
|
480
|
-
if (cell.isEditable) {
|
|
481
|
-
return 'editable-cell'
|
|
482
|
-
}
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
private _getCellClass(cell: EditorCell): string { if (!cell.isEditable) { return 'disabled-cell'
|
|
430
|
+
}
|
|
431
|
+
if (cell.isHighlighted) { return 'highlighted-cell'
|
|
432
|
+
}
|
|
433
|
+
if (cell.isEditable) { return 'editable-cell'
|
|
434
|
+
}
|
|
483
435
|
return ''
|
|
484
|
-
|
|
436
|
+
}
|
|
485
437
|
|
|
486
|
-
private _formatDate(date: string): string {
|
|
487
|
-
const d = new Date(date)
|
|
438
|
+
private _formatDate(date: string): string { const d = new Date(date)
|
|
488
439
|
return d.toLocaleDateString('ko-KR', { month: 'numeric', day: 'numeric' })
|
|
489
|
-
|
|
440
|
+
}
|
|
490
441
|
|
|
491
|
-
private _startEdit(kpiId: string, date: string) {
|
|
492
|
-
|
|
493
|
-
}
|
|
442
|
+
private _startEdit(kpiId: string, date: string) { this.editingCell = { kpiId, date }
|
|
443
|
+
}
|
|
494
444
|
|
|
495
|
-
private _finishEdit(kpiId: string, date: string, value: number) {
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
if (!kpi.values[date]) {
|
|
499
|
-
kpi.values[date] = { value: 0, isDirty: false }
|
|
500
|
-
}
|
|
445
|
+
private _finishEdit(kpiId: string, date: string, value: number) { const kpi = this.kpis.find(k => k.kpiId === kpiId)
|
|
446
|
+
if (kpi) { if (!kpi.values[date]) { kpi.values[date] = { value: 0, isDirty: false }
|
|
447
|
+
}
|
|
501
448
|
|
|
502
449
|
// 값이 변경되었는지 확인
|
|
503
450
|
const originalValue = this._findExistingValue(kpiId, date)?.value
|
|
@@ -508,17 +455,15 @@ export class KpiValueEditorPage extends connect(store)(localize(i18next)(ScopedE
|
|
|
508
455
|
|
|
509
456
|
// score는 저장 시 서버에서 계산되므로 여기서는 제거
|
|
510
457
|
delete kpi.values[date].score
|
|
511
|
-
|
|
458
|
+
}
|
|
512
459
|
this.editingCell = null
|
|
513
|
-
|
|
460
|
+
}
|
|
514
461
|
|
|
515
|
-
private _renderCellContent(kpi: KpiValueData, date: string) {
|
|
516
|
-
const isEditing = this.editingCell?.kpiId === kpi.kpiId && this.editingCell?.date === date
|
|
462
|
+
private _renderCellContent(kpi: KpiValueData, date: string) { const isEditing = this.editingCell?.kpiId === kpi.kpiId && this.editingCell?.date === date
|
|
517
463
|
const value = kpi.values[date]?.value
|
|
518
464
|
const score = kpi.values[date]?.score
|
|
519
465
|
|
|
520
|
-
if (isEditing) {
|
|
521
|
-
return html`
|
|
466
|
+
if (isEditing) { return html`
|
|
522
467
|
<input
|
|
523
468
|
type="number"
|
|
524
469
|
step="0.01"
|
|
@@ -529,7 +474,7 @@ export class KpiValueEditorPage extends connect(store)(localize(i18next)(ScopedE
|
|
|
529
474
|
autofocus
|
|
530
475
|
/>
|
|
531
476
|
`
|
|
532
|
-
|
|
477
|
+
}
|
|
533
478
|
|
|
534
479
|
return html`
|
|
535
480
|
<div style="display: flex; flex-direction: column; gap: 2px;">
|
|
@@ -541,73 +486,58 @@ export class KpiValueEditorPage extends connect(store)(localize(i18next)(ScopedE
|
|
|
541
486
|
: ''}
|
|
542
487
|
</div>
|
|
543
488
|
`
|
|
544
|
-
|
|
489
|
+
}
|
|
545
490
|
|
|
546
|
-
private _calculateScore(value: number, periodType: string): number {
|
|
547
|
-
// 간단한 score 계산 로직 (0-1 범위)
|
|
491
|
+
private _calculateScore(value: number, periodType: string): number { // 간단한 score 계산 로직 (0-1 범위)
|
|
548
492
|
// 실제로는 KPI의 formula나 기준값을 사용해야 함
|
|
549
493
|
if (value <= 0) return 0
|
|
550
494
|
if (value >= 1000) return 1
|
|
551
495
|
return Math.min(value / 1000, 1)
|
|
552
|
-
|
|
496
|
+
}
|
|
553
497
|
|
|
554
|
-
private async _loadData() {
|
|
555
|
-
if (!this.startDate || !this.endDate) {
|
|
556
|
-
this.error = '시작일, 종료일을 모두 입력해주세요.'
|
|
498
|
+
private async _loadData() { if (!this.startDate || !this.endDate) { this.error = '시작일, 종료일을 모두 입력해주세요.'
|
|
557
499
|
return
|
|
558
|
-
|
|
500
|
+
}
|
|
559
501
|
|
|
560
502
|
this.loading = true
|
|
561
503
|
this.error = ''
|
|
562
504
|
|
|
563
|
-
try {
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
query: gql`
|
|
567
|
-
query ($filters: [Filter!]) {
|
|
568
|
-
kpis(filters: $filters) {
|
|
569
|
-
items {
|
|
570
|
-
id
|
|
505
|
+
try { // KPI 목록 조회
|
|
506
|
+
const kpisResponse = await client.query({ query: gql`
|
|
507
|
+
query ($filters: [Filter!]) { kpis(filters: $filters) { items { id
|
|
571
508
|
name
|
|
572
509
|
periodType
|
|
573
510
|
active
|
|
574
|
-
|
|
511
|
+
}
|
|
575
512
|
total
|
|
576
|
-
|
|
577
|
-
|
|
513
|
+
}
|
|
514
|
+
}
|
|
578
515
|
`,
|
|
579
|
-
variables: {
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
})
|
|
516
|
+
variables: { filters: [{ name: 'active', operator: 'eq', value: true }]
|
|
517
|
+
}
|
|
518
|
+
})
|
|
583
519
|
|
|
584
520
|
// KPI Value 데이터 조회
|
|
585
|
-
const valuesResponse = await client.query({
|
|
586
|
-
|
|
587
|
-
query ($filters: [Filter!]) {
|
|
588
|
-
kpiValues(filters: $filters) {
|
|
589
|
-
items {
|
|
590
|
-
id
|
|
521
|
+
const valuesResponse = await client.query({ query: gql`
|
|
522
|
+
query ($filters: [Filter!]) { kpiValues(filters: $filters) { items { id
|
|
591
523
|
kpiId
|
|
592
524
|
valueDate
|
|
593
525
|
value
|
|
594
526
|
score
|
|
595
|
-
kpiOrgScope {
|
|
596
|
-
id
|
|
527
|
+
kpiOrgScope { id
|
|
597
528
|
org
|
|
598
|
-
|
|
599
|
-
|
|
529
|
+
}
|
|
530
|
+
}
|
|
600
531
|
total
|
|
601
|
-
|
|
602
|
-
|
|
532
|
+
}
|
|
533
|
+
}
|
|
603
534
|
`,
|
|
604
|
-
variables: {
|
|
605
|
-
filters: [
|
|
535
|
+
variables: { filters: [
|
|
606
536
|
...(this.org ? [{ name: 'org', operator: 'eq', value: this.org }] : []),
|
|
607
537
|
{ name: 'valueDate', operator: 'between', value: [this.startDate, this.endDate] }
|
|
608
538
|
]
|
|
609
|
-
|
|
610
|
-
|
|
539
|
+
}
|
|
540
|
+
})
|
|
611
541
|
|
|
612
542
|
// 날짜 배열 생성
|
|
613
543
|
this.dates = this._generateDateArray(this.startDate, this.endDate)
|
|
@@ -616,36 +546,29 @@ export class KpiValueEditorPage extends connect(store)(localize(i18next)(ScopedE
|
|
|
616
546
|
console.log('생성된 날짜 배열:', this.dates)
|
|
617
547
|
|
|
618
548
|
// KPI 목록을 기준으로 데이터 구성
|
|
619
|
-
this.kpis = kpisResponse.data.kpis.items.map((kpi: any) => ({
|
|
620
|
-
kpiId: kpi.id,
|
|
549
|
+
this.kpis = kpisResponse.data.kpis.items.map((kpi: any) => ({ kpiId: kpi.id,
|
|
621
550
|
kpiName: kpi.name,
|
|
622
551
|
periodType: kpi.periodType,
|
|
623
552
|
values: {}
|
|
624
|
-
|
|
553
|
+
}))
|
|
625
554
|
|
|
626
555
|
// 기존 KPI Value 데이터 저장
|
|
627
556
|
this._existingValues = valuesResponse.data.kpiValues.items
|
|
628
557
|
|
|
629
558
|
// KPI Value 데이터를 해당 KPI에 매핑
|
|
630
|
-
valuesResponse.data.kpiValues.items.forEach((value: any) => {
|
|
631
|
-
|
|
632
|
-
if (kpi) {
|
|
633
|
-
kpi.values[value.valueDate] = {
|
|
634
|
-
value: value.value,
|
|
559
|
+
valuesResponse.data.kpiValues.items.forEach((value: any) => { const kpi = this.kpis.find(k => k.kpiId === value.kpiId)
|
|
560
|
+
if (kpi) { kpi.values[value.valueDate] = { value: value.value,
|
|
635
561
|
score: value.score,
|
|
636
562
|
isDirty: false
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
})
|
|
640
566
|
|
|
641
567
|
// KPI Value가 없는 KPI도 빈 값으로 초기화
|
|
642
|
-
this.kpis.forEach(kpi => {
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
}
|
|
647
|
-
})
|
|
648
|
-
})
|
|
568
|
+
this.kpis.forEach(kpi => { this.dates.forEach(date => { if (!kpi.values[date]) { kpi.values[date] = { value: null, score: undefined, isDirty: false }
|
|
569
|
+
}
|
|
570
|
+
})
|
|
571
|
+
})
|
|
649
572
|
|
|
650
573
|
// 디버깅 정보 출력
|
|
651
574
|
console.log('KPI 총 개수:', kpisResponse.data.kpis.total)
|
|
@@ -657,122 +580,96 @@ export class KpiValueEditorPage extends connect(store)(localize(i18next)(ScopedE
|
|
|
657
580
|
|
|
658
581
|
// 강제 업데이트
|
|
659
582
|
this.requestUpdate()
|
|
660
|
-
|
|
661
|
-
console.error('데이터 로드 중 오류:', error)
|
|
583
|
+
} catch (error) { console.error('데이터 로드 중 오류:', error)
|
|
662
584
|
this.error = '데이터를 불러오는 중 오류가 발생했습니다.'
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
}
|
|
585
|
+
} finally { this.loading = false
|
|
586
|
+
}
|
|
587
|
+
}
|
|
667
588
|
|
|
668
|
-
private _generateDateArray(startDate: string, endDate: string): string[] {
|
|
669
|
-
const dates: string[] = []
|
|
589
|
+
private _generateDateArray(startDate: string, endDate: string): string[] { const dates: string[] = []
|
|
670
590
|
const start = new Date(startDate)
|
|
671
591
|
const end = new Date(endDate)
|
|
672
592
|
|
|
673
|
-
for (let d = new Date(start); d <= end; d.setDate(d.getDate() + 1)) {
|
|
674
|
-
|
|
675
|
-
}
|
|
593
|
+
for (let d = new Date(start); d <= end; d.setDate(d.getDate() + 1)) { dates.push(d.toLocaleDateString('sv-SE'))
|
|
594
|
+
}
|
|
676
595
|
|
|
677
596
|
return dates
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
private async _saveValues() {
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
if (data.isDirty && data.value !== null && data.value !== undefined) {
|
|
688
|
-
const existingValue = this._findExistingValue(kpi.kpiId, date)
|
|
689
|
-
|
|
690
|
-
if (existingValue) {
|
|
691
|
-
// 기존 데이터 업데이트
|
|
692
|
-
patches.push({
|
|
693
|
-
id: existingValue.id,
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
private async _saveValues() { try { const patches: any[] = []
|
|
600
|
+
|
|
601
|
+
this.kpis.forEach(kpi => { Object.entries(kpi.values).forEach(([date, data]) => { // dirty 상태인 데이터만 처리
|
|
602
|
+
if (data.isDirty && data.value !== null && data.value !== undefined) { const existingValue = this._findExistingValue(kpi.kpiId, date)
|
|
603
|
+
|
|
604
|
+
if (existingValue) { // 기존 데이터 업데이트
|
|
605
|
+
patches.push({ id: existingValue.id,
|
|
694
606
|
value: data.value,
|
|
695
607
|
org: this.org,
|
|
696
608
|
cuFlag: 'M'
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
patches.push({
|
|
701
|
-
kpiId: kpi.kpiId,
|
|
609
|
+
})
|
|
610
|
+
} else { // 새로운 데이터 생성
|
|
611
|
+
patches.push({ kpiId: kpi.kpiId,
|
|
702
612
|
valueDate: date,
|
|
703
613
|
value: data.value,
|
|
704
614
|
org: this.org,
|
|
705
615
|
cuFlag: '+'
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
if (patches.length === 0) {
|
|
713
|
-
notify({ message: '저장할 데이터가 없습니다.' })
|
|
616
|
+
})
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
})
|
|
620
|
+
})
|
|
621
|
+
|
|
622
|
+
if (patches.length === 0) { notify({ message: '저장할 데이터가 없습니다.' })
|
|
714
623
|
return
|
|
715
|
-
|
|
624
|
+
}
|
|
716
625
|
|
|
717
626
|
// 단일 mutation으로 생성과 업데이트 처리
|
|
718
|
-
const response = await client.mutate({
|
|
719
|
-
|
|
720
|
-
mutation ($patches: [KpiValuePatch!]!) {
|
|
721
|
-
updateMultipleKpiValue(patches: $patches) {
|
|
722
|
-
id
|
|
627
|
+
const response = await client.mutate({ mutation: gql`
|
|
628
|
+
mutation ($patches: [KpiValuePatch!]!) { updateMultipleKpiValue(patches: $patches) { id
|
|
723
629
|
value
|
|
724
630
|
score
|
|
725
631
|
valueDate
|
|
726
632
|
kpiId
|
|
727
|
-
|
|
728
|
-
|
|
633
|
+
}
|
|
634
|
+
}
|
|
729
635
|
`,
|
|
730
636
|
variables: { patches }
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
if (!response.errors) {
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
const kpi = this.kpis.find(k => k.kpiId === savedValue.kpiId)
|
|
737
|
-
if (kpi && kpi.values[savedValue.valueDate]) {
|
|
738
|
-
kpi.values[savedValue.valueDate].score = savedValue.score
|
|
637
|
+
})
|
|
638
|
+
|
|
639
|
+
if (!response.errors) { // 서버에서 계산된 score로 UI 업데이트
|
|
640
|
+
response.data.updateMultipleKpiValue.forEach((savedValue: any) => { const kpi = this.kpis.find(k => k.kpiId === savedValue.kpiId)
|
|
641
|
+
if (kpi && kpi.values[savedValue.valueDate]) { kpi.values[savedValue.valueDate].score = savedValue.score
|
|
739
642
|
kpi.values[savedValue.valueDate].isDirty = false // 저장 후 dirty 상태 해제
|
|
740
|
-
|
|
741
|
-
|
|
643
|
+
}
|
|
644
|
+
})
|
|
742
645
|
|
|
743
646
|
notify({ message: 'KPI 값이 성공적으로 저장되었습니다.' })
|
|
744
647
|
this.requestUpdate() // UI 강제 업데이트
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
console.error('저장 중 오류:', error)
|
|
648
|
+
}
|
|
649
|
+
} catch (error) { console.error('저장 중 오류:', error)
|
|
748
650
|
notify({ message: '저장 중 오류가 발생했습니다.' })
|
|
749
|
-
|
|
750
|
-
|
|
651
|
+
}
|
|
652
|
+
}
|
|
751
653
|
|
|
752
|
-
private _findExistingValue(kpiId: string, date: string) {
|
|
753
|
-
// 기존 로드된 KPI Value 데이터에서 찾기
|
|
654
|
+
private _findExistingValue(kpiId: string, date: string) { // 기존 로드된 KPI Value 데이터에서 찾기
|
|
754
655
|
return this._existingValues?.find((v: any) => v.kpiId === kpiId && v.valueDate === date)
|
|
755
|
-
|
|
656
|
+
}
|
|
756
657
|
|
|
757
|
-
private _cancel() {
|
|
758
|
-
// 편집 취소 로직
|
|
658
|
+
private _cancel() { // 편집 취소 로직
|
|
759
659
|
this.editingCell = null
|
|
760
660
|
this._loadData() // 원본 데이터로 복원
|
|
761
|
-
|
|
661
|
+
}
|
|
762
662
|
|
|
763
|
-
async pageInitialized(lifecycle: any) {
|
|
764
|
-
|
|
765
|
-
if (!this.startDate) {
|
|
766
|
-
const today = new Date()
|
|
663
|
+
async pageInitialized(lifecycle: any) { // 기본값 설정 - 지난 1개월
|
|
664
|
+
if (!this.startDate) { const today = new Date()
|
|
767
665
|
const oneMonthAgo = new Date(today.getFullYear(), today.getMonth() - 1, today.getDate())
|
|
768
666
|
this.startDate = oneMonthAgo.toLocaleDateString('sv-SE')
|
|
769
|
-
|
|
770
|
-
if (!this.endDate) {
|
|
771
|
-
const today = new Date()
|
|
667
|
+
}
|
|
668
|
+
if (!this.endDate) { const today = new Date()
|
|
772
669
|
this.endDate = today.toLocaleDateString('sv-SE')
|
|
773
|
-
|
|
670
|
+
}
|
|
774
671
|
|
|
775
672
|
// 페이지 초기화 시 자동으로 데이터 로드
|
|
776
673
|
await this._loadData()
|
|
777
|
-
|
|
674
|
+
}
|
|
778
675
|
}
|