@things-factory/kpi 9.0.15 → 9.0.17
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-grade-editor.ts +80 -207
- package/client/pages/kpi/kpi-list-page.ts +53 -11
- package/client/pages/kpi/kpi-viz-editor.ts +353 -0
- package/client/pages/kpi-dashboard/kpi-dashboard.ts +128 -1
- package/dist-client/pages/kpi/kpi-grade-editor.d.ts +13 -13
- package/dist-client/pages/kpi/kpi-grade-editor.js +84 -197
- package/dist-client/pages/kpi/kpi-grade-editor.js.map +1 -1
- package/dist-client/pages/kpi/kpi-list-page.d.ts +6 -2
- package/dist-client/pages/kpi/kpi-list-page.js +50 -10
- package/dist-client/pages/kpi/kpi-list-page.js.map +1 -1
- package/dist-client/pages/kpi/kpi-viz-editor.d.ts +20 -0
- package/dist-client/pages/kpi/kpi-viz-editor.js +364 -0
- package/dist-client/pages/kpi/kpi-viz-editor.js.map +1 -0
- package/dist-client/pages/kpi-dashboard/kpi-dashboard.d.ts +2 -0
- package/dist-client/pages/kpi-dashboard/kpi-dashboard.js +117 -1
- package/dist-client/pages/kpi-dashboard/kpi-dashboard.js.map +1 -1
- package/dist-client/tsconfig.tsbuildinfo +1 -1
- package/dist-server/service/index.d.ts +2 -2
- package/dist-server/service/kpi/kpi-type.d.ts +3 -3
- package/dist-server/service/kpi/kpi-type.js +4 -4
- package/dist-server/service/kpi/kpi-type.js.map +1 -1
- package/dist-server/service/kpi/kpi.d.ts +18 -1
- package/dist-server/service/kpi/kpi.js +26 -4
- package/dist-server/service/kpi/kpi.js.map +1 -1
- package/dist-server/tsconfig.tsbuildinfo +1 -1
- package/package.json +2 -2
- package/server/service/kpi/kpi-type.ts +7 -7
- package/server/service/kpi/kpi.ts +27 -4
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
import '@material/web/button/elevated-button.js'
|
|
2
2
|
import '@material/web/button/filled-button.js'
|
|
3
3
|
import '@material/web/button/text-button.js'
|
|
4
|
-
import '@material/web/textfield/outlined-text-field.js'
|
|
5
|
-
import '@material/web/select/outlined-select.js'
|
|
6
|
-
import '@material/web/select/select-option.js'
|
|
7
4
|
import '@material/web/icon/icon.js'
|
|
5
|
+
import '@operato/data-grist/ox-grist.js'
|
|
6
|
+
import deepEquals from 'lodash-es/isEqual'
|
|
8
7
|
|
|
8
|
+
import gql from 'graphql-tag'
|
|
9
9
|
import { LitElement, css, html } from 'lit'
|
|
10
|
-
import { customElement, property, state } from 'lit/decorators.js'
|
|
11
|
-
import {
|
|
10
|
+
import { customElement, property, state, query } from 'lit/decorators.js'
|
|
11
|
+
import { DataGrist } from '@operato/data-grist/ox-grist.js'
|
|
12
|
+
import { i18next, localize } from '@operato/i18n'
|
|
12
13
|
import { client } from '@operato/graphql'
|
|
13
14
|
import { notify } from '@operato/layout'
|
|
14
|
-
import
|
|
15
|
-
import {
|
|
15
|
+
import { FetchOption } from '@operato/data-grist'
|
|
16
|
+
import { CommonHeaderStyles } from '@operato/styles'
|
|
16
17
|
|
|
17
|
-
// KPI 등급 타입 정의 (서버와 동일한 구조)
|
|
18
18
|
interface KpiGrade {
|
|
19
19
|
name: string
|
|
20
20
|
minValue: number
|
|
@@ -27,268 +27,141 @@ interface KpiGrade {
|
|
|
27
27
|
type KpiGrades = KpiGrade[]
|
|
28
28
|
|
|
29
29
|
@customElement('kpi-grade-editor')
|
|
30
|
-
export class KpiGradeEditor extends
|
|
30
|
+
export class KpiGradeEditor extends localize(i18next)(LitElement) {
|
|
31
31
|
static styles = [
|
|
32
32
|
CommonHeaderStyles,
|
|
33
|
-
ScrollbarStyles,
|
|
34
33
|
css`
|
|
35
34
|
:host {
|
|
36
35
|
display: flex;
|
|
37
36
|
flex-direction: column;
|
|
38
|
-
background-color: var(--md-sys-color-surface, #f4f6fa);
|
|
39
|
-
}
|
|
40
37
|
|
|
41
|
-
|
|
42
|
-
flex: 1;
|
|
43
|
-
margin-bottom: 20px;
|
|
44
|
-
overflow-y: auto;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
.grade-item {
|
|
48
|
-
display: flex;
|
|
49
|
-
align-items: center;
|
|
50
|
-
gap: 10px;
|
|
51
|
-
padding: 10px;
|
|
52
|
-
border: 1px solid #ddd;
|
|
53
|
-
border-radius: 4px;
|
|
54
|
-
margin-bottom: 10px;
|
|
55
|
-
background: #f9f9f9;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
.grade-item:hover {
|
|
59
|
-
background: #f0f0f0;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
.grade-inputs {
|
|
63
|
-
display: flex;
|
|
64
|
-
gap: 10px;
|
|
65
|
-
flex: 1;
|
|
38
|
+
background-color: var(--md-sys-color-surface);
|
|
66
39
|
}
|
|
67
40
|
|
|
68
|
-
|
|
41
|
+
ox-grist {
|
|
69
42
|
flex: 1;
|
|
70
43
|
}
|
|
71
|
-
|
|
72
|
-
.grade-actions {
|
|
73
|
-
display: flex;
|
|
74
|
-
gap: 5px;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
.footer span {
|
|
78
|
-
font-size: 0.8em;
|
|
79
|
-
color: var(--md-sys-color-on-surface);
|
|
80
|
-
line-height: 1.5;
|
|
81
|
-
padding: 10px;
|
|
82
|
-
}
|
|
83
44
|
`
|
|
84
45
|
]
|
|
85
46
|
|
|
86
|
-
@property({ type: Object }) kpi: any
|
|
87
|
-
|
|
88
|
-
@state()
|
|
47
|
+
@property({ type: Object }) kpi: any = { grades: [] }
|
|
48
|
+
|
|
49
|
+
@state() grades: KpiGrades = this.kpi?.grades || []
|
|
50
|
+
@state() gristConfig: any = null
|
|
51
|
+
@query('ox-grist') grist!: DataGrist
|
|
89
52
|
|
|
90
|
-
|
|
91
|
-
super.connectedCallback()
|
|
53
|
+
async firstUpdated() {
|
|
92
54
|
if (this.kpi?.grades) {
|
|
93
55
|
this.grades = [...this.kpi.grades]
|
|
94
56
|
}
|
|
57
|
+
|
|
58
|
+
this.gristConfig = {
|
|
59
|
+
list: { fields: ['name', 'minValue', 'maxValue', 'score', 'color', 'description'] },
|
|
60
|
+
columns: [
|
|
61
|
+
{ type: 'gutter', gutterName: 'row-selector', multiple: true, fixed: true },
|
|
62
|
+
{ type: 'gutter', gutterName: 'sequence', fixed: true },
|
|
63
|
+
{ type: 'gutter', gutterName: 'button', fixed: true, icon: 'add', handlers: { click: 'record-copy' } },
|
|
64
|
+
{ type: 'gutter', gutterName: 'button', fixed: true, icon: 'arrow_upward', handlers: { click: 'move-up' } },
|
|
65
|
+
{ type: 'gutter', gutterName: 'button', fixed: true, icon: 'arrow_downward', handlers: { click: 'move-down' } },
|
|
66
|
+
{ type: 'string', name: 'name', header: '등급명', record: { editable: true }, width: 100 },
|
|
67
|
+
{ type: 'number', name: 'minValue', header: '최소값', record: { editable: true }, width: 100 },
|
|
68
|
+
{ type: 'number', name: 'maxValue', header: '최대값', record: { editable: true }, width: 100 },
|
|
69
|
+
{ type: 'number', name: 'score', header: '점수', record: { editable: true }, width: 80 },
|
|
70
|
+
{ type: 'color', name: 'color', header: '색상', record: { editable: true }, width: 100 },
|
|
71
|
+
{ type: 'string', name: 'description', header: '설명', record: { editable: true }, width: 200 }
|
|
72
|
+
],
|
|
73
|
+
rows: { selectable: { multiple: true } },
|
|
74
|
+
pagination: { infinite: true }
|
|
75
|
+
}
|
|
95
76
|
}
|
|
96
77
|
|
|
97
78
|
render() {
|
|
98
79
|
return html`
|
|
99
|
-
<
|
|
100
|
-
|
|
80
|
+
<ox-grist .mode=${'GRID'} .config=${this.gristConfig} .fetchHandler=${this.fetchHandler.bind(this)}></ox-grist>
|
|
101
81
|
<div class="footer">
|
|
102
|
-
<button type="button" @click=${this._addGrade}><md-icon>add</md-icon>등급 추가</button>
|
|
103
82
|
<div filler></div>
|
|
104
|
-
<button
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
_renderGradeItem(grade: KpiGrade, index: number) {
|
|
111
|
-
return html`
|
|
112
|
-
<div class="grade-item">
|
|
113
|
-
<div class="grade-inputs">
|
|
114
|
-
<md-outlined-text-field
|
|
115
|
-
label="등급명"
|
|
116
|
-
value=${grade.name}
|
|
117
|
-
@input=${(e: any) => this._updateGrade(index, 'name', e.target.value)}
|
|
118
|
-
></md-outlined-text-field>
|
|
119
|
-
|
|
120
|
-
<md-outlined-text-field
|
|
121
|
-
label="최소값"
|
|
122
|
-
type="number"
|
|
123
|
-
value=${grade.minValue}
|
|
124
|
-
@input=${(e: any) => this._updateGrade(index, 'minValue', parseFloat(e.target.value))}
|
|
125
|
-
></md-outlined-text-field>
|
|
126
|
-
|
|
127
|
-
<md-outlined-text-field
|
|
128
|
-
label="최대값"
|
|
129
|
-
type="number"
|
|
130
|
-
value=${grade.maxValue}
|
|
131
|
-
@input=${(e: any) => this._updateGrade(index, 'maxValue', parseFloat(e.target.value))}
|
|
132
|
-
></md-outlined-text-field>
|
|
133
|
-
|
|
134
|
-
<md-outlined-text-field
|
|
135
|
-
label="점수"
|
|
136
|
-
type="number"
|
|
137
|
-
value=${grade.score || ''}
|
|
138
|
-
@input=${(e: any) => this._updateGrade(index, 'score', parseFloat(e.target.value))}
|
|
139
|
-
></md-outlined-text-field>
|
|
140
|
-
|
|
141
|
-
<md-outlined-text-field
|
|
142
|
-
label="색상"
|
|
143
|
-
value=${grade.color || ''}
|
|
144
|
-
@input=${(e: any) => this._updateGrade(index, 'color', e.target.value)}
|
|
145
|
-
></md-outlined-text-field>
|
|
146
|
-
</div>
|
|
147
|
-
|
|
148
|
-
<div class="grade-actions">
|
|
149
|
-
<md-icon-button @click=${() => this._removeGrade(index)}>
|
|
150
|
-
<md-icon>delete</md-icon>
|
|
151
|
-
</md-icon-button>
|
|
152
|
-
</div>
|
|
83
|
+
<button danger @click=${this._deleteGrades.bind(this)}>
|
|
84
|
+
<md-icon>delete</md-icon>${i18next.t('button.delete')}
|
|
85
|
+
</button>
|
|
86
|
+
<button done type="button" @click=${this._updateGrades}><md-icon>save</md-icon>저장</button>
|
|
153
87
|
</div>
|
|
154
88
|
`
|
|
155
89
|
}
|
|
156
90
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
_addGrade() {
|
|
164
|
-
const newGrade: KpiGrade = {
|
|
165
|
-
name: '',
|
|
166
|
-
minValue: 0,
|
|
167
|
-
maxValue: 0,
|
|
168
|
-
score: 0,
|
|
169
|
-
color: '#4caf50',
|
|
170
|
-
description: ''
|
|
91
|
+
async fetchHandler({ page, limit, sorters = [] }: FetchOption) {
|
|
92
|
+
return {
|
|
93
|
+
total: this.grades.length,
|
|
94
|
+
records: this.grades
|
|
171
95
|
}
|
|
172
|
-
this.grades.push(newGrade)
|
|
173
|
-
this.isDirty = true
|
|
174
|
-
this.requestUpdate()
|
|
175
96
|
}
|
|
176
97
|
|
|
177
|
-
|
|
178
|
-
this.grades.
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
98
|
+
async _updateGrades() {
|
|
99
|
+
this.grades = this.grist.dirtyData.records
|
|
100
|
+
.map(patch => {
|
|
101
|
+
const { name, minValue, maxValue, score, color, description } = patch
|
|
102
|
+
return { name, minValue, maxValue, score, color, description }
|
|
103
|
+
})
|
|
104
|
+
.sort((a, b) => a.minValue - b.minValue) as any
|
|
182
105
|
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
case '5grade':
|
|
186
|
-
this.grades = [
|
|
187
|
-
{ name: 'A', minValue: 0, maxValue: 1.5, score: 95, color: '#4caf50', description: '우수' },
|
|
188
|
-
{ name: 'B', minValue: 1.5, maxValue: 2.5, score: 85, color: '#ff9800', description: '양호' },
|
|
189
|
-
{ name: 'C', minValue: 2.5, maxValue: 3.5, score: 75, color: '#ffc107', description: '보통' },
|
|
190
|
-
{ name: 'D', minValue: 3.5, maxValue: 4.5, score: 65, color: '#ff9800', description: '미흡' },
|
|
191
|
-
{ name: 'E', minValue: 4.5, maxValue: 999, score: 55, color: '#f44336', description: '불량' }
|
|
192
|
-
]
|
|
193
|
-
break
|
|
194
|
-
case '3grade':
|
|
195
|
-
this.grades = [
|
|
196
|
-
{ name: '우수', minValue: 0, maxValue: 1.5, score: 95, color: '#4caf50', description: '목표 달성' },
|
|
197
|
-
{ name: '양호', minValue: 1.5, maxValue: 2.5, score: 85, color: '#ff9800', description: '기준 달성' },
|
|
198
|
-
{ name: '미흡', minValue: 2.5, maxValue: 999, score: 75, color: '#f44336', description: '개선 필요' }
|
|
199
|
-
]
|
|
200
|
-
break
|
|
201
|
-
case 'continuous':
|
|
202
|
-
this.grades = [
|
|
203
|
-
{ name: '0.999999', minValue: 0, maxValue: 0.025, score: 0.999999, color: '#4caf50' },
|
|
204
|
-
{ name: '0.944189368', minValue: 0.025, maxValue: 0.05, score: 0.944189368, color: '#4caf50' },
|
|
205
|
-
{ name: '0.888379735', minValue: 0.05, maxValue: 0.075, score: 0.888379735, color: '#4caf50' }
|
|
206
|
-
]
|
|
207
|
-
break
|
|
208
|
-
case 'clear':
|
|
209
|
-
this.grades = []
|
|
210
|
-
break
|
|
106
|
+
if (!this._validateGrades()) {
|
|
107
|
+
return
|
|
211
108
|
}
|
|
212
|
-
this.isDirty = true
|
|
213
|
-
this.requestUpdate()
|
|
214
|
-
}
|
|
215
109
|
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
grades: this.grades
|
|
110
|
+
if (!deepEquals(this.kpi?.grades, this.grades)) {
|
|
111
|
+
try {
|
|
112
|
+
const response = await client.mutate({
|
|
113
|
+
mutation: gql`
|
|
114
|
+
mutation ($id: String!, $patch: KpiPatch!) {
|
|
115
|
+
updateKpi(id: $id, patch: $patch) {
|
|
116
|
+
id
|
|
117
|
+
name
|
|
118
|
+
grades
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
`,
|
|
122
|
+
variables: {
|
|
123
|
+
id: this.kpi.id,
|
|
124
|
+
patch: { grades: this.grades }
|
|
231
125
|
}
|
|
232
126
|
})
|
|
233
|
-
)
|
|
234
127
|
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
message: '등급 저장 중 오류가 발생했습니다.'
|
|
239
|
-
}
|
|
128
|
+
this.grades = response.data.updateKpi.grades
|
|
129
|
+
this.grist.fetch()
|
|
130
|
+
} catch (error) {
|
|
131
|
+
notify({ message: '등급 저장 중 오류가 발생했습니다.' })
|
|
132
|
+
}
|
|
240
133
|
}
|
|
241
134
|
}
|
|
242
135
|
|
|
243
136
|
_validateGrades(): boolean {
|
|
244
|
-
// 최소 1개 등급 필요
|
|
245
137
|
if (this.grades.length === 0) {
|
|
246
|
-
notify({
|
|
247
|
-
message: '최소 1개 이상의 등급을 설정해야 합니다.'
|
|
248
|
-
})
|
|
138
|
+
notify({ message: '최소 1개 이상의 등급을 설정해야 합니다.' })
|
|
249
139
|
return false
|
|
250
140
|
}
|
|
251
|
-
|
|
252
|
-
// 등급명 중복 체크
|
|
253
141
|
const names = this.grades.map(g => g.name)
|
|
254
142
|
const uniqueNames = new Set(names)
|
|
255
143
|
if (names.length !== uniqueNames.size) {
|
|
256
|
-
notify({
|
|
257
|
-
message: '등급명이 중복되었습니다.'
|
|
258
|
-
})
|
|
144
|
+
notify({ message: '등급명이 중복되었습니다.' })
|
|
259
145
|
return false
|
|
260
146
|
}
|
|
261
|
-
|
|
262
|
-
// 값 범위 체크
|
|
263
147
|
for (let i = 0; i < this.grades.length; i++) {
|
|
264
148
|
const grade = this.grades[i]
|
|
265
149
|
if (grade.minValue >= grade.maxValue) {
|
|
266
|
-
notify({
|
|
267
|
-
message: `등급 "${grade.name}"의 최소값이 최대값보다 크거나 같습니다.`
|
|
268
|
-
})
|
|
150
|
+
notify({ message: `등급 "${grade.name}"의 최소값이 최대값보다 크거나 같습니다.` })
|
|
269
151
|
return false
|
|
270
152
|
}
|
|
271
|
-
|
|
272
|
-
// 연속성 체크
|
|
273
153
|
if (i > 0) {
|
|
274
154
|
const prevGrade = this.grades[i - 1]
|
|
275
155
|
if (prevGrade.maxValue !== grade.minValue) {
|
|
276
|
-
notify({
|
|
277
|
-
message: `등급 "${prevGrade.name}"과 "${grade.name}" 사이에 간격이 있습니다.`
|
|
278
|
-
})
|
|
156
|
+
notify({ message: `등급 "${prevGrade.name}"과 "${grade.name}" 사이에 간격이 있습니다.` })
|
|
279
157
|
return false
|
|
280
158
|
}
|
|
281
159
|
}
|
|
282
160
|
}
|
|
283
|
-
|
|
284
161
|
return true
|
|
285
162
|
}
|
|
286
163
|
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
const popup = this.closest('ox-popup') as any
|
|
290
|
-
if (popup && popup.close) {
|
|
291
|
-
popup.close()
|
|
292
|
-
}
|
|
164
|
+
async _deleteGrades() {
|
|
165
|
+
this.grist.deleteSelectedRecords(true)
|
|
293
166
|
}
|
|
294
167
|
}
|
|
@@ -3,6 +3,7 @@ import '@material/web/button/elevated-button.js'
|
|
|
3
3
|
import '@operato/data-grist/ox-grist.js'
|
|
4
4
|
import '@operato/data-grist/ox-filters-form.js'
|
|
5
5
|
import '@operato/data-grist/ox-record-creator.js'
|
|
6
|
+
import './kpi-viz-editor.js'
|
|
6
7
|
|
|
7
8
|
import { CommonButtonStyles, CommonHeaderStyles, CommonGristStyles, ScrollbarStyles } from '@operato/styles'
|
|
8
9
|
import { PageView, store } from '@operato/shell'
|
|
@@ -21,6 +22,7 @@ import gql from 'graphql-tag'
|
|
|
21
22
|
|
|
22
23
|
import { KpiImporter } from './kpi-importer'
|
|
23
24
|
import { KpiGradeEditor } from './kpi-grade-editor'
|
|
25
|
+
import { KpiVizEditor } from './kpi-viz-editor'
|
|
24
26
|
|
|
25
27
|
@customElement('kpi-list-page')
|
|
26
28
|
export class KpiListPage extends connect(store)(localize(i18next)(ScopedElementsMixin(PageView))) {
|
|
@@ -52,7 +54,8 @@ export class KpiListPage extends connect(store)(localize(i18next)(ScopedElements
|
|
|
52
54
|
static get scopedElements() {
|
|
53
55
|
return {
|
|
54
56
|
'kpi-importer': KpiImporter,
|
|
55
|
-
'kpi-grade-editor': KpiGradeEditor
|
|
57
|
+
'kpi-grade-editor': KpiGradeEditor,
|
|
58
|
+
'kpi-viz-editor': KpiVizEditor
|
|
56
59
|
}
|
|
57
60
|
}
|
|
58
61
|
|
|
@@ -237,7 +240,30 @@ export class KpiListPage extends connect(store)(localize(i18next)(ScopedElements
|
|
|
237
240
|
width: 60
|
|
238
241
|
},
|
|
239
242
|
{ type: 'string', name: 'state', header: '상태', record: { editable: false }, width: 100 },
|
|
240
|
-
{
|
|
243
|
+
{
|
|
244
|
+
type: 'string',
|
|
245
|
+
name: 'vizType',
|
|
246
|
+
header: '시각화 설정',
|
|
247
|
+
record: {
|
|
248
|
+
editable: false,
|
|
249
|
+
renderer: (v, c, r) => {
|
|
250
|
+
const vizType = r.vizType || 'CARD'
|
|
251
|
+
const vizMeta = r.vizMeta || {}
|
|
252
|
+
const color = vizMeta.color || '#2196f3'
|
|
253
|
+
const icon = vizMeta.icon || 'dashboard'
|
|
254
|
+
|
|
255
|
+
return html`
|
|
256
|
+
<div style="display:flex;align-items:center;gap:8px;">
|
|
257
|
+
<span style="color:${color};cursor:pointer;" @click=${() => this._editViz(r)}> ${vizType} </span>
|
|
258
|
+
<md-icon style="color:${color};font-size:16px;cursor:pointer;" @click=${() => this._editViz(r)}>
|
|
259
|
+
${icon}
|
|
260
|
+
</md-icon>
|
|
261
|
+
</div>
|
|
262
|
+
`
|
|
263
|
+
}
|
|
264
|
+
},
|
|
265
|
+
width: 150
|
|
266
|
+
},
|
|
241
267
|
{ type: 'string', name: 'schedule', header: '스케줄', record: { editable: true }, width: 120 },
|
|
242
268
|
{ type: 'string', name: 'scheduleId', header: '스케줄ID', record: { editable: false }, width: 120 },
|
|
243
269
|
{ type: 'string', name: 'timezone', header: '타임존', record: { editable: true }, width: 100 },
|
|
@@ -295,6 +321,8 @@ export class KpiListPage extends connect(store)(localize(i18next)(ScopedElements
|
|
|
295
321
|
description
|
|
296
322
|
active
|
|
297
323
|
grades
|
|
324
|
+
vizType
|
|
325
|
+
vizMeta
|
|
298
326
|
category {
|
|
299
327
|
id
|
|
300
328
|
name
|
|
@@ -475,20 +503,33 @@ export class KpiListPage extends connect(store)(localize(i18next)(ScopedElements
|
|
|
475
503
|
}
|
|
476
504
|
|
|
477
505
|
async _editGrades(kpi: any) {
|
|
506
|
+
const popup = await openPopup(html` <kpi-grade-editor .kpi=${kpi}></kpi-grade-editor> `, {
|
|
507
|
+
title: `${kpi.name} - 등급 설정`,
|
|
508
|
+
size: 'large'
|
|
509
|
+
})
|
|
510
|
+
|
|
511
|
+
popup.onclosed = () => {
|
|
512
|
+
this.grist.fetch()
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
async _editViz(kpi: any) {
|
|
478
517
|
const popup = await openPopup(
|
|
479
518
|
html`
|
|
480
|
-
<kpi-
|
|
519
|
+
<kpi-viz-editor
|
|
520
|
+
.kpi=${kpi}
|
|
521
|
+
.onSave=${(vizType: string, vizMeta: any) => this._onVizUpdated(kpi.id, vizType, vizMeta)}
|
|
522
|
+
.onCancel=${() => popup.close()}
|
|
523
|
+
></kpi-viz-editor>
|
|
481
524
|
`,
|
|
482
525
|
{
|
|
483
|
-
title: `${kpi.name} -
|
|
526
|
+
title: `${kpi.name} - 시각화 설정`,
|
|
484
527
|
size: 'large'
|
|
485
528
|
}
|
|
486
529
|
)
|
|
487
530
|
}
|
|
488
531
|
|
|
489
|
-
async
|
|
490
|
-
const { kpiId, grades } = detail
|
|
491
|
-
|
|
532
|
+
async _onVizUpdated(kpiId: string, vizType: string, vizMeta: any) {
|
|
492
533
|
try {
|
|
493
534
|
const response = await client.mutate({
|
|
494
535
|
mutation: gql`
|
|
@@ -496,25 +537,26 @@ export class KpiListPage extends connect(store)(localize(i18next)(ScopedElements
|
|
|
496
537
|
updateKpi(id: $id, patch: $patch) {
|
|
497
538
|
id
|
|
498
539
|
name
|
|
499
|
-
|
|
540
|
+
vizType
|
|
541
|
+
vizMeta
|
|
500
542
|
}
|
|
501
543
|
}
|
|
502
544
|
`,
|
|
503
545
|
variables: {
|
|
504
546
|
id: kpiId,
|
|
505
|
-
patch: {
|
|
547
|
+
patch: { vizType, vizMeta }
|
|
506
548
|
}
|
|
507
549
|
})
|
|
508
550
|
|
|
509
551
|
if (!response.errors) {
|
|
510
552
|
this.grist.fetch()
|
|
511
553
|
notify({
|
|
512
|
-
message: '
|
|
554
|
+
message: '시각화 설정이 성공적으로 업데이트되었습니다.'
|
|
513
555
|
})
|
|
514
556
|
}
|
|
515
557
|
} catch (error) {
|
|
516
558
|
notify({
|
|
517
|
-
message: '
|
|
559
|
+
message: '시각화 설정 업데이트 중 오류가 발생했습니다.'
|
|
518
560
|
})
|
|
519
561
|
}
|
|
520
562
|
}
|