@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
@@ -7,7 +7,7 @@ import './kpi-viz-editor.js'
7
7
  import './kpi-grade-editor.js'
8
8
 
9
9
  import { CommonButtonStyles, CommonHeaderStyles, CommonGristStyles, ScrollbarStyles } from '@operato/styles'
10
- import { PageView, store } from '@operato/shell'
10
+ import { PageView } from '@operato/shell'
11
11
  import { css, html } from 'lit'
12
12
  import { customElement, property, query, state } from 'lit/decorators.js'
13
13
  import { ScopedElementsMixin } from '@open-wc/scoped-elements'
@@ -19,7 +19,6 @@ import { OxPopup, OxPrompt } from '@operato/popup'
19
19
  import { isMobileDevice } from '@operato/utils'
20
20
  import { p13n } from '@operato/p13n'
21
21
 
22
- import { connect } from 'pwa-helpers/connect-mixin'
23
22
  import gql from 'graphql-tag'
24
23
 
25
24
  import { KpiImporter } from './kpi-importer'
@@ -27,39 +26,33 @@ import { KpiGradeEditor } from './kpi-grade-editor'
27
26
  import { KpiVizEditor } from './kpi-viz-editor'
28
27
 
29
28
  @customElement('kpi-list-page')
30
- export class KpiListPage extends connect(store)(p13n(localize(i18next)(ScopedElementsMixin(PageView)))) {
31
- static styles = [
29
+ export class KpiListPage extends p13n(localize(i18next)(ScopedElementsMixin(PageView))) { static styles = [
32
30
  ScrollbarStyles,
33
31
  CommonGristStyles,
34
32
  CommonHeaderStyles,
35
33
  css`
36
- :host {
37
- display: flex;
34
+ :host { display: flex;
38
35
 
39
36
  width: 100%;
40
37
 
41
38
  --grid-record-emphasized-background-color: #8b0000;
42
39
  --grid-record-emphasized-color: #ff6b6b;
43
- }
40
+ }
44
41
 
45
- ox-grist {
46
- overflow-y: auto;
42
+ ox-grist { overflow-y: auto;
47
43
  flex: 1;
48
- }
44
+ }
49
45
 
50
- ox-filters-form {
51
- flex: 1;
52
- }
46
+ ox-filters-form { flex: 1;
47
+ }
53
48
  `
54
49
  ]
55
50
 
56
- static get scopedElements() {
57
- return {
58
- 'kpi-importer': KpiImporter,
51
+ static get scopedElements() { return { 'kpi-importer': KpiImporter,
59
52
  'kpi-grade-editor': KpiGradeEditor,
60
53
  'kpi-viz-editor': KpiVizEditor
61
- }
62
- }
54
+ }
55
+ }
63
56
 
64
57
  @property({ type: Object }) gristConfig: any
65
58
  @property({ type: String }) mode: 'CARD' | 'GRID' | 'LIST' = isMobileDevice() ? 'CARD' : 'GRID'
@@ -70,109 +63,81 @@ export class KpiListPage extends connect(store)(p13n(localize(i18next)(ScopedEle
70
63
  @state() availableVariablesLoaded = false
71
64
  @state() hierarchicalView = false
72
65
 
73
- async getAvailableKpiMetricVariables(currentKpi?: any) {
74
- // Leaf KPI인 경우: kpi-metric을 변수로 사용
75
- if (!currentKpi || currentKpi.isLeaf) {
76
- if (this.availableVariablesLoaded) {
77
- return this.availableVariables
78
- }
79
- const response = await client.query({
80
- query: gql`
81
- query {
82
- kpiMetrics {
83
- items {
84
- name
66
+ async getAvailableKpiMetricVariables(currentKpi?: any) { // Leaf KPI인 경우: kpi-metric을 변수로 사용
67
+ if (!currentKpi || currentKpi.isLeaf) { if (this.availableVariablesLoaded) { return this.availableVariables
68
+ }
69
+ const response = await client.query({ query: gql`
70
+ query { kpiMetrics { items { name
85
71
  description
86
72
  unit
87
- }
88
- }
89
- }
73
+ }
74
+ }
75
+ }
90
76
  `
91
- })
92
- this.availableVariables = (response.data.kpiMetrics.items || []).map(metric => ({
93
- name: metric.name,
77
+ })
78
+ this.availableVariables = (response.data.kpiMetrics.items || []).map(metric => ({ name: metric.name,
94
79
  description: metric.description,
95
80
  type: 'kpi-metric',
96
81
  unit: metric.unit
97
- }))
82
+ }))
98
83
  this.availableVariablesLoaded = true
99
84
  return this.availableVariables
100
- }
85
+ }
101
86
 
102
87
  // 부모 KPI인 경우: 자식 KPI를 변수로 사용
103
- try {
104
- const response = await client.query({
105
- query: gql`
106
- query ($id: String!) {
107
- kpi(id: $id) {
108
- children {
109
- id
88
+ try { const response = await client.query({ query: gql`
89
+ query ($id: String!) { kpi(id: $id) { children { id
110
90
  name
111
91
  description
112
- }
113
- }
114
- }
92
+ }
93
+ }
94
+ }
115
95
  `,
116
- variables: {
117
- id: currentKpi.id
118
- }
119
- })
96
+ variables: { id: currentKpi.id
97
+ }
98
+ })
120
99
 
121
- return (response.data.kpi.children || []).map(childKpi => ({
122
- name: childKpi.name,
100
+ return (response.data.kpi.children || []).map(childKpi => ({ name: childKpi.name,
123
101
  description: childKpi.description,
124
102
  type: 'child-kpi'
125
- }))
126
- } catch (error) {
127
- console.error('Failed to fetch child KPIs:', error)
103
+ }))
104
+ } catch (error) { console.error('Failed to fetch child KPIs:', error)
128
105
  return []
129
- }
130
- }
131
-
132
- get context() {
133
- return {
134
- title: i18next.t('title.kpi list'),
135
- search: {
136
- handler: (search: string) => {
137
- this.grist.searchText = search
138
- },
106
+ }
107
+ }
108
+
109
+ get context() { return { title: i18next.t('title.kpi list'),
110
+ search: { handler: (search: string) => { this.grist.searchText = search
111
+ },
139
112
  value: this.grist.searchText
140
- },
141
- filter: {
142
- handler: () => {
143
- this.grist.toggleHeadroom()
144
- }
145
- },
113
+ },
114
+ filter: { handler: () => { this.grist.toggleHeadroom()
115
+ }
116
+ },
146
117
  help: 'kpi/kpi',
147
118
  actions: [
148
- {
149
- title: this.hierarchicalView ? 'List View' : 'Tree View',
119
+ { title: this.hierarchicalView ? 'List View' : 'Tree View',
150
120
  action: this._toggleHierarchicalView.bind(this),
151
121
  icon: this.hierarchicalView ? 'list' : 'account_tree'
152
- },
153
- {
154
- title: i18next.t('button.save'),
122
+ },
123
+ { title: i18next.t('button.save'),
155
124
  action: this._updateKpi.bind(this),
156
125
  ...CommonButtonStyles.save
157
- },
158
- {
159
- title: i18next.t('button.delete'),
126
+ },
127
+ { title: i18next.t('button.delete'),
160
128
  action: this._deleteKpi.bind(this),
161
129
  ...CommonButtonStyles.delete
162
- }
130
+ }
163
131
  ],
164
- exportable: {
165
- name: i18next.t('title.kpi list'),
132
+ exportable: { name: i18next.t('title.kpi list'),
166
133
  data: this.exportHandler.bind(this)
167
- },
168
- importable: {
169
- handler: this.importHandler.bind(this)
170
- }
171
- }
172
- }
134
+ },
135
+ importable: { handler: this.importHandler.bind(this)
136
+ }
137
+ }
138
+ }
173
139
 
174
- render() {
175
- const mode = this.mode || (isMobileDevice() ? 'CARD' : 'GRID')
140
+ render() { const mode = this.mode || (isMobileDevice() ? 'CARD' : 'GRID')
176
141
 
177
142
  return html`
178
143
  <ox-grist
@@ -202,17 +167,13 @@ export class KpiListPage extends connect(store)(p13n(localize(i18next)(ScopedEle
202
167
  <ox-grist-personalizer slot="setting"></ox-grist-personalizer>
203
168
  </ox-grist>
204
169
  `
205
- }
170
+ }
206
171
 
207
- connectedCallback() {
208
- super.connectedCallback()
172
+ connectedCallback() { super.connectedCallback()
209
173
  this.fetchKpiMetrics()
210
- }
174
+ }
211
175
 
212
- async pageInitialized(lifecycle: any) {
213
- this.gristConfig = {
214
- list: {
215
- fields: ['name', 'description'],
176
+ async pageInitialized(lifecycle: any) { this.gristConfig = { list: { fields: ['name', 'description'],
216
177
  details: [
217
178
  'name',
218
179
  'description',
@@ -230,91 +191,74 @@ export class KpiListPage extends connect(store)(p13n(localize(i18next)(ScopedEle
230
191
  'updater',
231
192
  'thumbnail'
232
193
  ]
233
- },
194
+ },
234
195
  columns: [
235
196
  { type: 'gutter', gutterName: 'sequence' },
236
197
  { type: 'gutter', gutterName: 'row-selector', multiple: true },
237
198
  // KPI 실적값 계산 버튼 추가
238
- {
239
- type: 'gutter',
199
+ { type: 'gutter',
240
200
  gutterName: 'button',
241
201
  icon: 'calculate',
242
202
  title: '실적값 계산',
243
- handlers: {
244
- click: (columns, data, column, record, rowIndex) => {
245
- this._calculateKpiValue(record)
246
- }
247
- }
248
- },
249
- {
250
- type: 'string',
203
+ handlers: { click: (columns, data, column, record, rowIndex) => { this._calculateKpiValue(record)
204
+ }
205
+ }
206
+ },
207
+ { type: 'string',
251
208
  name: 'name',
252
209
  header: '이름',
253
210
  record: { editable: true },
254
211
  filter: 'search',
255
212
  sortable: true,
256
213
  width: 120
257
- },
258
- {
259
- type: 'string',
214
+ },
215
+ { type: 'string',
260
216
  name: 'description',
261
217
  header: i18next.t('field.description'),
262
218
  record: { editable: true },
263
219
  filter: 'search',
264
220
  width: 200
265
- },
266
- {
267
- type: 'resource-object',
221
+ },
222
+ { type: 'resource-object',
268
223
  name: 'parent',
269
224
  label: true,
270
225
  header: '상위 KPI',
271
- record: {
272
- editable: true,
273
- options: {
274
- title: i18next.t('title.lookup KPI'),
226
+ record: { editable: true,
227
+ options: { title: i18next.t('title.lookup KPI'),
275
228
  queryName: 'kpis',
276
- basicArgs: {
277
- filters: [{ name: 'isLeaf', operator: 'eq', value: false }]
278
- }
279
- }
280
- },
229
+ basicArgs: { filters: [{ name: 'isLeaf', operator: 'eq', value: false }]
230
+ }
231
+ }
232
+ },
281
233
  width: 200
282
- },
283
- {
284
- type: 'boolean',
234
+ },
235
+ { type: 'boolean',
285
236
  name: 'isLeaf',
286
237
  header: '리프 KPI',
287
238
  record: { editable: true },
288
239
  width: 100
289
- },
290
- {
291
- type: 'formula',
240
+ },
241
+ { type: 'formula',
292
242
  name: 'formula',
293
243
  header: '산식',
294
- record: {
295
- editable: true,
296
- availableVariables: async (value: string, column: ColumnConfig, record: any) => {
297
- // Leaf KPI인 경우: kpi-metric을 변수로 사용
298
- if (!record || record.isLeaf) {
299
- return await this.getAvailableKpiMetricVariables(record)
300
- }
244
+ record: { editable: true,
245
+ availableVariables: async (value: string, column: ColumnConfig, record: any) => { // Leaf KPI인 경우: kpi-metric을 변수로 사용
246
+ if (!record || record.isLeaf) { return await this.getAvailableKpiMetricVariables(record)
247
+ }
301
248
  // 부모 KPI인 경우: 자식 KPI를 변수로 사용
302
- return (record.children || []).map(child => ({
303
- name: child.name,
249
+ return (record.children || []).map(child => ({ name: child.name,
304
250
  description: child.description || child.name,
305
251
  type: 'child-kpi',
306
252
  unit: ''
307
- }))
308
- }
309
- },
253
+ }))
254
+ }
255
+ },
310
256
  width: 320
311
- },
312
- {
313
- type: 'select',
257
+ },
258
+ { type: 'select',
314
259
  name: 'periodType',
315
260
  header: '계산주기',
316
- record: {
317
- editable: true,
261
+ record: { editable: true,
318
262
  options: [
319
263
  { value: '', display: '' },
320
264
  { value: 'DAY', display: '일' },
@@ -325,39 +269,32 @@ export class KpiListPage extends connect(store)(p13n(localize(i18next)(ScopedEle
325
269
  { value: 'RANGE', display: '범위' },
326
270
  { value: 'ALLTIME', display: '전체' }
327
271
  ]
328
- },
272
+ },
329
273
  width: 80
330
- },
331
- {
332
- type: 'formula',
274
+ },
275
+ { type: 'formula',
333
276
  name: 'scoreFormula',
334
277
  header: '성과점수수식',
335
- record: {
336
- editable: true,
337
- availableVariables: async (value: string, column: ColumnConfig, record: any) => {
338
- // leaf 이면 value를 사용
339
- if (record.isLeaf) {
340
- return [
341
- {
342
- name: 'value',
278
+ record: { editable: true,
279
+ availableVariables: async (value: string, column: ColumnConfig, record: any) => { // leaf 이면 value를 사용
280
+ if (record.isLeaf) { return [
281
+ { name: 'value',
343
282
  description: 'KPI 실적값',
344
283
  type: 'kpi-value',
345
284
  unit: ''
346
- }
285
+ }
347
286
  ]
348
- }
287
+ }
349
288
  // 부모 이면 자식 KPI들을 변수로 사용
350
- return record.children.map(child => ({
351
- name: child.name,
289
+ return record.children.map(child => ({ name: child.name,
352
290
  description: child.description || child.name,
353
291
  type: 'kpi-score',
354
292
  unit: ''
355
- }))
356
- },
293
+ }))
294
+ },
357
295
  includeDefaultFunctions: false,
358
296
  availableFunctions: [
359
- {
360
- name: 'INTEGRATE()',
297
+ { name: 'INTEGRATE()',
361
298
  description: '수치 적분',
362
299
  template: 'INTEGRATE({func}, {a}, {b}, {n})',
363
300
  syntax: 'INTEGRATE(func, a, b, n)',
@@ -365,9 +302,8 @@ export class KpiListPage extends connect(store)(p13n(localize(i18next)(ScopedEle
365
302
  returnType: 'number',
366
303
  help: '사다리꼴 적분법을 사용하여 수치 적분을 계산합니다.',
367
304
  examples: ['INTEGRATE(x => x*x, 0, 1, 1000)', 'INTEGRATE([효율성], 0, 1)']
368
- },
369
- {
370
- name: 'BETA_FUNCTION()',
305
+ },
306
+ { name: 'BETA_FUNCTION()',
371
307
  description: '베타 함수',
372
308
  template: 'BETA_FUNCTION({x}, {alpha}, {beta})',
373
309
  syntax: 'BETA_FUNCTION(x, alpha, beta)',
@@ -375,9 +311,8 @@ export class KpiListPage extends connect(store)(p13n(localize(i18next)(ScopedEle
375
311
  returnType: 'number',
376
312
  help: '베타 분포 함수를 계산합니다. t^(α-1) × (1-t)^(β-1)',
377
313
  examples: ['BETA_FUNCTION(0.5, 2, 3)', 'BETA_FUNCTION([품질지수], 3, 2)']
378
- },
379
- {
380
- name: 'INCOMPLETE_BETA()',
314
+ },
315
+ { name: 'INCOMPLETE_BETA()',
381
316
  description: '불완전 베타 함수',
382
317
  template: 'INCOMPLETE_BETA({x}, {alpha}, {beta})',
383
318
  syntax: 'INCOMPLETE_BETA(x, alpha, beta)',
@@ -385,9 +320,8 @@ export class KpiListPage extends connect(store)(p13n(localize(i18next)(ScopedEle
385
320
  returnType: 'number',
386
321
  help: '불완전 베타 함수를 수치 적분으로 계산합니다.',
387
322
  examples: ['INCOMPLETE_BETA(0.7, 2, 3)', 'INCOMPLETE_BETA([목표달성률]/100, 2, 3)']
388
- },
389
- {
390
- name: 'COMPLETE_BETA()',
323
+ },
324
+ { name: 'COMPLETE_BETA()',
391
325
  description: '완전 베타 함수',
392
326
  template: 'COMPLETE_BETA({alpha}, {beta})',
393
327
  syntax: 'COMPLETE_BETA(alpha, beta)',
@@ -395,9 +329,8 @@ export class KpiListPage extends connect(store)(p13n(localize(i18next)(ScopedEle
395
329
  returnType: 'number',
396
330
  help: '완전 베타 함수 B(α,β)를 계산합니다.',
397
331
  examples: ['COMPLETE_BETA(2, 3)', 'COMPLETE_BETA(3, 2)']
398
- },
399
- {
400
- name: 'PERFORMANCE_INDEX()',
332
+ },
333
+ { name: 'PERFORMANCE_INDEX()',
401
334
  description: '성과 지수',
402
335
  template: 'PERFORMANCE_INDEX({x}, {alpha1}, {beta1}, {alpha2}, {beta2})',
403
336
  syntax: 'PERFORMANCE_INDEX(x, alpha1, beta1, alpha2, beta2)',
@@ -409,9 +342,8 @@ export class KpiListPage extends connect(store)(p13n(localize(i18next)(ScopedEle
409
342
  returnType: 'number',
410
343
  help: '성과 지수를 계산합니다: 1 - (불완전 베타 / 완전 베타)',
411
344
  examples: ['PERFORMANCE_INDEX(0.8, 2, 3, 2, 3)', 'PERFORMANCE_INDEX([성과점수]/100, 2, 3, 2, 3)']
412
- },
413
- {
414
- name: 'EXP()',
345
+ },
346
+ { name: 'EXP()',
415
347
  description: '지수 함수',
416
348
  template: 'EXP({x})',
417
349
  syntax: 'EXP(x)',
@@ -419,9 +351,8 @@ export class KpiListPage extends connect(store)(p13n(localize(i18next)(ScopedEle
419
351
  returnType: 'number',
420
352
  help: '자연상수 e의 x제곱을 계산합니다.',
421
353
  examples: ['EXP(1)', 'EXP([효율성])']
422
- },
423
- {
424
- name: 'LOG()',
354
+ },
355
+ { name: 'LOG()',
425
356
  description: '자연 로그',
426
357
  template: 'LOG({x})',
427
358
  syntax: 'LOG(x)',
@@ -429,9 +360,8 @@ export class KpiListPage extends connect(store)(p13n(localize(i18next)(ScopedEle
429
360
  returnType: 'number',
430
361
  help: '자연 로그 ln(x)를 계산합니다.',
431
362
  examples: ['LOG(2.718)', 'LOG([성과점수])']
432
- },
433
- {
434
- name: 'POW()',
363
+ },
364
+ { name: 'POW()',
435
365
  description: '거듭제곱',
436
366
  template: 'POW({x}, {y})',
437
367
  syntax: 'POW(x, y)',
@@ -439,9 +369,8 @@ export class KpiListPage extends connect(store)(p13n(localize(i18next)(ScopedEle
439
369
  returnType: 'number',
440
370
  help: 'x의 y제곱을 계산합니다.',
441
371
  examples: ['POW(2, 3)', 'POW([효율성], 2)']
442
- },
443
- {
444
- name: 'EXPONENTIAL_DECAY()',
372
+ },
373
+ { name: 'EXPONENTIAL_DECAY()',
445
374
  description: '지수 감쇠',
446
375
  template: 'EXPONENTIAL_DECAY({value}, {scale}, {power})',
447
376
  syntax: 'EXPONENTIAL_DECAY(value, scale, power)',
@@ -449,42 +378,35 @@ export class KpiListPage extends connect(store)(p13n(localize(i18next)(ScopedEle
449
378
  returnType: 'number',
450
379
  help: '지수 감쇠 함수 exp(-(value/scale)^power)를 계산합니다.',
451
380
  examples: ['EXPONENTIAL_DECAY(50, 100, 2)', 'EXPONENTIAL_DECAY([목표달성률], 50, 2)']
452
- }
381
+ }
453
382
  ]
454
- },
383
+ },
455
384
  width: 200
456
- },
457
- {
458
- type: 'string',
385
+ },
386
+ { type: 'string',
459
387
  name: 'grades',
460
388
  header: '성과지수 Lookup',
461
- record: {
462
- editable: false,
463
- renderer: (v, c, r) => {
464
- if (r.grades && r.grades.length > 0) {
465
- return html`<span style="color: #4caf50; cursor: pointer;" @click=${() => this._editGrades(r)}>
389
+ record: { editable: false,
390
+ renderer: (v, c, r) => { if (r.grades && r.grades.length > 0) { return html`<span style="color: #4caf50; cursor: pointer;" @click=${() => this._editGrades(r)}>
466
391
  ${r.grades.length}개 등급 설정됨
467
392
  </span>`
468
- } else {
469
- return html`<span style="color: #999; cursor: pointer;" @click=${() => this._editGrades(r)}>
393
+ } else { return html`<span style="color: #999; cursor: pointer;" @click=${() => this._editGrades(r)}>
470
394
  등급 설정 없음
471
395
  </span>`
472
- }
473
- }
474
- },
396
+ }
397
+ }
398
+ },
475
399
  width: 150
476
- },
477
- {
478
- type: 'number',
400
+ },
401
+ { type: 'number',
479
402
  name: 'weight',
480
403
  header: '가중치',
481
404
  record: { editable: true },
482
405
  filter: true,
483
406
  sortable: true,
484
407
  width: 80
485
- },
486
- {
487
- type: 'checkbox',
408
+ },
409
+ { type: 'checkbox',
488
410
  name: 'active',
489
411
  label: true,
490
412
  header: i18next.t('field.active'),
@@ -492,17 +414,14 @@ export class KpiListPage extends connect(store)(p13n(localize(i18next)(ScopedEle
492
414
  filter: true,
493
415
  sortable: true,
494
416
  width: 60
495
- },
417
+ },
496
418
  { type: 'string', name: 'state', header: '상태', record: { editable: false }, width: 100 },
497
- {
498
- type: 'string',
419
+ { type: 'string',
499
420
  name: 'vizType',
500
421
  hidden: true,
501
422
  header: '시각화 설정',
502
- record: {
503
- editable: false,
504
- renderer: (v, c, r) => {
505
- const vizType = r.vizType || 'CARD'
423
+ record: { editable: false,
424
+ renderer: (v, c, r) => { const vizType = r.vizType || 'CARD'
506
425
  const vizMeta = r.vizMeta || {}
507
426
  const color = vizMeta.color || '#2196f3'
508
427
  const icon = vizMeta.icon || 'dashboard'
@@ -515,67 +434,54 @@ export class KpiListPage extends connect(store)(p13n(localize(i18next)(ScopedEle
515
434
  </md-icon>
516
435
  </div>
517
436
  `
518
- }
519
- },
437
+ }
438
+ },
520
439
  width: 150
521
- },
440
+ },
522
441
  { type: 'crontab', name: 'schedule', header: '스케줄', record: { editable: true }, width: 120 },
523
442
  { type: 'string', name: 'scheduleId', header: '스케줄ID', record: { editable: false }, width: 120 },
524
443
  { type: 'timezone', name: 'timezone', header: '타임존', record: { editable: true }, width: 120 },
525
444
  { type: 'number', name: 'version', header: '버전', record: { editable: false }, width: 80 },
526
445
  { type: 'datetime', name: 'createdAt', header: '생성일', record: { editable: false }, width: 180 },
527
- {
528
- type: 'datetime',
446
+ { type: 'datetime',
529
447
  name: 'updatedAt',
530
448
  header: i18next.t('field.updated_at'),
531
449
  record: { editable: false },
532
450
  sortable: true,
533
451
  width: 180
534
- },
535
- {
536
- type: 'resource-object',
452
+ },
453
+ { type: 'resource-object',
537
454
  name: 'creator',
538
455
  header: '생성자',
539
456
  record: { editable: false, renderer: (v, c, r) => r.creator?.name },
540
457
  width: 120
541
- },
542
- {
543
- type: 'resource-object',
458
+ },
459
+ { type: 'resource-object',
544
460
  name: 'updater',
545
461
  header: i18next.t('field.updater'),
546
462
  record: { editable: false, renderer: (v, c, r) => r.updater?.name },
547
463
  sortable: true,
548
464
  width: 120
549
- },
465
+ },
550
466
  { type: 'string', name: 'thumbnail', header: '썸네일', record: { editable: false }, width: 120 }
551
467
  ],
552
- rows: {
553
- appendable: false,
554
- selectable: {
555
- multiple: true
556
- }
557
- },
468
+ rows: { appendable: false,
469
+ selectable: { multiple: true
470
+ }
471
+ },
558
472
  sorters: [{ name: 'name' }]
559
- }
560
- }
561
-
562
- async pageUpdated(changes: any, lifecycle: any) {
563
- if (this.active) {
564
- // do something here when this page just became as active
565
- }
566
- }
567
-
568
- async fetchHandler({ page = 1, limit = 100, sortings = [], filters = [] }: FetchOption) {
569
- if (this.hierarchicalView) {
570
- return this.fetchHierarchicalData()
571
- }
572
-
573
- const response = await client.query({
574
- query: gql`
575
- query ($filters: [Filter!], $pagination: Pagination, $sortings: [Sorting!]) {
576
- responses: kpis(filters: $filters, pagination: $pagination, sortings: $sortings) {
577
- items {
578
- id
473
+ }
474
+ }
475
+
476
+ async pageUpdated(changes: any, lifecycle: any) { if (this.active) { // do something here when this page just became as active
477
+ }
478
+ }
479
+
480
+ async fetchHandler({ page = 1, limit = 100, sortings = [], filters = [] }: FetchOption) { if (this.hierarchicalView) { return this.fetchHierarchicalData()
481
+ }
482
+
483
+ const response = await client.query({ query: gql`
484
+ query ($filters: [Filter!], $pagination: Pagination, $sortings: [Sorting!]) { responses: kpis(filters: $filters, pagination: $pagination, sortings: $sortings) { items { id
579
485
  name
580
486
  description
581
487
  active
@@ -590,236 +496,179 @@ export class KpiListPage extends connect(store)(p13n(localize(i18next)(ScopedEle
590
496
  scheduleId
591
497
  timezone
592
498
  version
593
- parent {
594
- id
499
+ parent { id
595
500
  name
596
- }
597
- children {
598
- id
501
+ }
502
+ children { id
599
503
  name
600
504
  description
601
- }
505
+ }
602
506
  isLeaf
603
- updater {
604
- id
507
+ updater { id
605
508
  name
606
- }
509
+ }
607
510
  updatedAt
608
- creator {
609
- id
511
+ creator { id
610
512
  name
611
- }
513
+ }
612
514
  createdAt
613
- }
515
+ }
614
516
  total
615
- }
616
- }
517
+ }
518
+ }
617
519
  `,
618
- variables: {
619
- filters,
520
+ variables: { filters,
620
521
  pagination: { page, limit },
621
522
  sortings
622
- }
623
- })
523
+ }
524
+ })
624
525
 
625
- return {
626
- total: response.data.responses.total || 0,
526
+ return { total: response.data.responses.total || 0,
627
527
  records: response.data.responses.items || []
628
- }
629
- }
630
-
631
- async fetchKpiMetrics() {
632
- const response = await client.query({
633
- query: gql`
634
- query {
635
- kpiMetrics {
636
- items {
637
- name
528
+ }
529
+ }
530
+
531
+ async fetchKpiMetrics() { const response = await client.query({ query: gql`
532
+ query { kpiMetrics { items { name
638
533
  description
639
534
  unit
640
- }
641
- }
642
- }
535
+ }
536
+ }
537
+ }
643
538
  `
644
- })
539
+ })
645
540
 
646
- if (!response.errors) {
647
- this.availableVariables = (response.data.kpiMetrics.items || []).map(metric => ({
648
- name: metric.name,
541
+ if (!response.errors) { this.availableVariables = (response.data.kpiMetrics.items || []).map(metric => ({ name: metric.name,
649
542
  description: metric.description,
650
543
  type: 'kpi-metric',
651
544
  unit: metric.unit
652
- }))
653
- }
654
- }
655
-
656
- async _deleteKpi() {
657
- if (
658
- await OxPrompt.open({
659
- title: i18next.t('text.are_you_sure'),
545
+ }))
546
+ }
547
+ }
548
+
549
+ async _deleteKpi() { if (
550
+ await OxPrompt.open({ title: i18next.t('text.are_you_sure'),
660
551
  text: i18next.t('text.sure_to_x', { x: i18next.t('text.delete') }),
661
552
  confirmButton: { text: i18next.t('button.confirm') },
662
553
  cancelButton: { text: i18next.t('button.cancel') }
663
- })
664
- ) {
665
- const ids = this.grist.selected.map(record => record.id)
666
- if (ids && ids.length > 0) {
667
- const response = await client.mutate({
668
- mutation: gql`
669
- mutation ($ids: [String!]!) {
670
- deleteKpis(ids: $ids)
671
- }
554
+ })
555
+ ) { const ids = this.grist.selected.map(record => record.id)
556
+ if (ids && ids.length > 0) { const response = await client.mutate({ mutation: gql`
557
+ mutation ($ids: [String!]!) { deleteKpis(ids: $ids)
558
+ }
672
559
  `,
673
- variables: {
674
- ids
675
- }
676
- })
677
-
678
- if (!response.errors) {
679
- this.grist.fetch()
680
- notify({
681
- message: i18next.t('text.info_x_successfully', { x: i18next.t('text.delete') })
682
- })
683
- }
684
- }
685
- }
686
- }
687
-
688
- async _updateKpi() {
689
- let patches = this.grist.dirtyRecords
690
- if (patches && patches.length) {
691
- patches = patches.map(patch => {
692
- let patchField: any = patch.id ? { id: patch.id } : {}
560
+ variables: { ids
561
+ }
562
+ })
563
+
564
+ if (!response.errors) { this.grist.fetch()
565
+ notify({ message: i18next.t('text.info_x_successfully', { x: i18next.t('text.delete') })
566
+ })
567
+ }
568
+ }
569
+ }
570
+ }
571
+
572
+ async _updateKpi() { let patches = this.grist.dirtyRecords
573
+ if (patches && patches.length) { patches = patches.map(patch => { let patchField: any = patch.id ? { id: patch.id } : {}
693
574
  const dirtyFields = patch.__dirtyfields__
694
- for (let key in dirtyFields) {
695
- patchField[key] = dirtyFields[key].after
696
- }
575
+ for (let key in dirtyFields) { patchField[key] = dirtyFields[key].after
576
+ }
697
577
  patchField.cuFlag = patch.__dirty__
698
578
 
699
579
  return patchField
700
- })
580
+ })
701
581
 
702
- const response = await client.mutate({
703
- mutation: gql`
704
- mutation ($patches: [KpiPatch!]!) {
705
- updateMultipleKpi(patches: $patches) {
706
- name
707
- }
708
- }
582
+ const response = await client.mutate({ mutation: gql`
583
+ mutation ($patches: [KpiPatch!]!) { updateMultipleKpi(patches: $patches) { name
584
+ }
585
+ }
709
586
  `,
710
- variables: {
711
- patches
712
- }
713
- })
714
-
715
- if (!response.errors) {
716
- this.grist.fetch()
717
- }
718
- }
719
- }
720
-
721
- async creationCallback(kpi) {
722
- try {
723
- const response = await client.query({
724
- query: gql`
725
- mutation ($kpi: NewKpi!) {
726
- createKpi(kpi: $kpi) {
727
- id
728
- }
729
- }
587
+ variables: { patches
588
+ }
589
+ })
590
+
591
+ if (!response.errors) { this.grist.fetch()
592
+ }
593
+ }
594
+ }
595
+
596
+ async creationCallback(kpi) { try { const response = await client.query({ query: gql`
597
+ mutation ($kpi: NewKpi!) { createKpi(kpi: $kpi) { id
598
+ }
599
+ }
730
600
  `,
731
- variables: {
732
- kpi
733
- },
734
- context: {
735
- hasUpload: true
736
- }
737
- })
738
-
739
- if (!response.errors) {
740
- this.grist.fetch()
601
+ variables: { kpi
602
+ },
603
+ context: { hasUpload: true
604
+ }
605
+ })
606
+
607
+ if (!response.errors) { this.grist.fetch()
741
608
  document.dispatchEvent(
742
- new CustomEvent('notify', {
743
- detail: {
744
- message: i18next.t('text.data_created_successfully')
745
- }
746
- })
609
+ new CustomEvent('notify', { detail: { message: i18next.t('text.data_created_successfully')
610
+ }
611
+ })
747
612
  )
748
- }
613
+ }
749
614
 
750
615
  return true
751
- } catch (ex) {
752
- console.error(ex)
616
+ } catch (ex) { console.error(ex)
753
617
  document.dispatchEvent(
754
- new CustomEvent('notify', {
755
- detail: {
756
- type: 'error',
618
+ new CustomEvent('notify', { detail: { type: 'error',
757
619
  message: i18next.t('text.error')
758
- }
759
- })
620
+ }
621
+ })
760
622
  )
761
623
  return false
762
- }
763
- }
624
+ }
625
+ }
764
626
 
765
- async exportHandler() {
766
- const exportTargets = this.grist.selected.length ? this.grist.selected : this.grist.dirtyData.records
627
+ async exportHandler() { const exportTargets = this.grist.selected.length ? this.grist.selected : this.grist.dirtyData.records
767
628
  const targetFieldSet = new Set(['id', 'name', 'description', 'active'])
768
629
 
769
- return exportTargets.map(kpi => {
770
- let tempObj = {}
771
- for (const field of targetFieldSet) {
772
- tempObj[field] = kpi[field]
773
- }
630
+ return exportTargets.map(kpi => { let tempObj = {}
631
+ for (const field of targetFieldSet) { tempObj[field] = kpi[field]
632
+ }
774
633
 
775
634
  return tempObj
776
- })
777
- }
635
+ })
636
+ }
778
637
 
779
- async importHandler(records) {
780
- const popup = openPopup(
638
+ async importHandler(records) { const popup = openPopup(
781
639
  html`
782
640
  <kpi-importer
783
641
  .kpis=${records}
784
- @imported=${() => {
785
- history.back()
642
+ @imported=${() => { history.back()
786
643
  this.grist.fetch()
787
- }}
644
+ }}
788
645
  ></kpi-importer>
789
646
  `,
790
- {
791
- backdrop: true,
647
+ { backdrop: true,
792
648
  size: 'large',
793
649
  title: i18next.t('title.import kpi')
794
- }
650
+ }
795
651
  )
796
652
 
797
- popup.onclosed = () => {
798
- this.grist.fetch()
799
- }
800
- }
653
+ popup.onclosed = () => { this.grist.fetch()
654
+ }
655
+ }
801
656
 
802
- async _editGrades(kpi: any) {
803
- if (!kpi.id) {
804
- notify({
805
- message: 'KPI를 먼저 저장한 후에 등급 설정을 할 수 있습니다.'
806
- })
657
+ async _editGrades(kpi: any) { if (!kpi.id) { notify({ message: 'KPI를 먼저 저장한 후에 등급 설정을 할 수 있습니다.'
658
+ })
807
659
 
808
660
  return
809
- }
661
+ }
810
662
 
811
- const popup = await openPopup(html` <kpi-grade-editor .kpi=${kpi}></kpi-grade-editor> `, {
812
- title: `${kpi.name} - 등급 설정`,
663
+ const popup = await openPopup(html` <kpi-grade-editor .kpi=${kpi}></kpi-grade-editor> `, { title: `${kpi.name} - 등급 설정`,
813
664
  size: 'large'
814
- })
665
+ })
815
666
 
816
- popup.onclosed = () => {
817
- this.grist.fetch()
818
- }
819
- }
667
+ popup.onclosed = () => { this.grist.fetch()
668
+ }
669
+ }
820
670
 
821
- async _editViz(kpi: any) {
822
- const popup = await openPopup(
671
+ async _editViz(kpi: any) { const popup = await openPopup(
823
672
  html`
824
673
  <kpi-viz-editor
825
674
  .kpi=${kpi}
@@ -827,82 +676,58 @@ export class KpiListPage extends connect(store)(p13n(localize(i18next)(ScopedEle
827
676
  .onCancel=${() => popup.close()}
828
677
  ></kpi-viz-editor>
829
678
  `,
830
- {
831
- title: `${kpi.name} - 시각화 설정`,
679
+ { title: `${kpi.name} - 시각화 설정`,
832
680
  size: 'large'
833
- }
681
+ }
834
682
  )
835
- }
836
-
837
- async _onVizUpdated(kpiId: string, vizType: string, vizMeta: any) {
838
- try {
839
- const response = await client.mutate({
840
- mutation: gql`
841
- mutation ($id: String!, $patch: KpiPatch!) {
842
- updateKpi(id: $id, patch: $patch) {
843
- id
683
+ }
684
+
685
+ async _onVizUpdated(kpiId: string, vizType: string, vizMeta: any) { try { const response = await client.mutate({ mutation: gql`
686
+ mutation ($id: String!, $patch: KpiPatch!) { updateKpi(id: $id, patch: $patch) { id
844
687
  name
845
688
  vizType
846
689
  vizMeta
847
- }
848
- }
690
+ }
691
+ }
849
692
  `,
850
- variables: {
851
- id: kpiId,
693
+ variables: { id: kpiId,
852
694
  patch: { vizType, vizMeta }
853
- }
854
- })
855
-
856
- if (!response.errors) {
857
- this.grist.fetch()
858
- notify({
859
- message: '시각화 설정이 성공적으로 업데이트되었습니다.'
860
- })
861
- }
862
- } catch (error) {
863
- notify({
864
- message: '시각화 설정 업데이트 중 오류가 발생했습니다.'
865
- })
866
- }
867
- }
868
-
869
- async _calculateKpiValue(kpi) {
870
- try {
871
- const response = await client.mutate({
872
- mutation: gql`
873
- mutation ($kpiId: String!) {
874
- calculateKpiValue(kpiId: $kpiId) {
875
- id
695
+ }
696
+ })
697
+
698
+ if (!response.errors) { this.grist.fetch()
699
+ notify({ message: '시각화 설정이 성공적으로 업데이트되었습니다.'
700
+ })
701
+ }
702
+ } catch (error) { notify({ message: '시각화 설정 업데이트 중 오류가 발생했습니다.'
703
+ })
704
+ }
705
+ }
706
+
707
+ async _calculateKpiValue(kpi) { try { const response = await client.mutate({ mutation: gql`
708
+ mutation ($kpiId: String!) { calculateKpiValue(kpiId: $kpiId) { id
876
709
  value
877
710
  valueDate
878
711
  org
879
- }
880
- }
712
+ }
713
+ }
881
714
  `,
882
- variables: {
883
- kpiId: kpi.id
884
- }
885
- })
886
- if (!response.errors) {
887
- notify({ message: 'KPI 실적값이 성공적으로 계산되었습니다.' })
715
+ variables: { kpiId: kpi.id
716
+ }
717
+ })
718
+ if (!response.errors) { notify({ message: 'KPI 실적값이 성공적으로 계산되었습니다.' })
888
719
  this.grist.fetch()
889
- }
890
- } catch (error) {
891
- notify({ message: 'KPI 실적값 계산 중 오류가 발생했습니다.' })
892
- }
893
- }
894
-
895
- _toggleHierarchicalView() {
896
- this.hierarchicalView = !this.hierarchicalView
720
+ }
721
+ } catch (error) { notify({ message: 'KPI 실적값 계산 중 오류가 발생했습니다.' })
722
+ }
723
+ }
724
+
725
+ _toggleHierarchicalView() { this.hierarchicalView = !this.hierarchicalView
897
726
  this.grist.fetch()
898
- }
899
-
900
- async fetchHierarchicalData() {
901
- const response = await client.query({
902
- query: gql`
903
- query {
904
- kpiTree {
905
- id
727
+ }
728
+
729
+ async fetchHierarchicalData() { const response = await client.query({ query: gql`
730
+ query { kpiTree { id
906
731
  name
907
732
  description
908
733
  active
@@ -917,79 +742,68 @@ export class KpiListPage extends connect(store)(p13n(localize(i18next)(ScopedEle
917
742
  scheduleId
918
743
  timezone
919
744
  version
920
- parent {
921
- id
745
+ parent { id
922
746
  name
923
- }
924
- children {
925
- id
747
+ }
748
+ children { id
926
749
  name
927
750
  description
928
751
  active
929
752
  isLeaf
930
753
  weight
931
- children {
932
- id
754
+ children { id
933
755
  name
934
756
  description
935
757
  active
936
758
  isLeaf
937
759
  weight
938
- children {
939
- id
760
+ children { id
940
761
  name
941
762
  description
942
763
  active
943
764
  isLeaf
944
765
  weight
945
- }
946
- }
947
- }
766
+ }
767
+ }
768
+ }
948
769
  isLeaf
949
- updater {
950
- id
770
+ updater { id
951
771
  name
952
- }
772
+ }
953
773
  updatedAt
954
- creator {
955
- id
774
+ creator { id
956
775
  name
957
- }
776
+ }
958
777
  createdAt
959
- }
960
- }
778
+ }
779
+ }
961
780
  `
962
- })
781
+ })
963
782
 
964
783
  const flattenedRecords = this.flattenTreeData(response.data.kpiTree)
965
784
 
966
- return {
967
- total: flattenedRecords.length,
785
+ return { total: flattenedRecords.length,
968
786
  records: flattenedRecords
969
- }
970
- }
787
+ }
788
+ }
971
789
 
972
- flattenTreeData(treeData: any[], level = 0): any[] {
973
- const flattened: any[] = []
790
+ flattenTreeData(treeData: any[], level = 0): any[] { const flattened: any[] = []
974
791
 
975
- for (const item of treeData) {
976
- const flattenedItem = {
977
- ...item,
792
+ for (const item of treeData) { const flattenedItem = { ...item,
978
793
  __level: level,
979
794
  __hasChildren: item.children && item.children.length > 0,
980
795
  __expanded: true
981
- }
796
+ }
982
797
 
983
798
  // Add indentation to name for visual hierarchy
984
799
  flattenedItem.name = ' '.repeat(level) + (level > 0 ? '└ ' : '') + item.name
985
800
 
986
801
  flattened.push(flattenedItem)
987
802
 
988
- if (item.children && item.children.length > 0) {
989
- flattened.push(...this.flattenTreeData(item.children, level + 1))
990
- }
991
- }
803
+ if (item.children && item.children.length > 0) { flattened.push(...this.flattenTreeData(item.children, level + 1))
804
+ }
805
+ }
992
806
 
993
807
  return flattened
994
- }
808
+ }
995
809
  }