@things-factory/kpi 9.0.23 → 9.0.25

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 (76) hide show
  1. package/client/pages/kpi/kpi-viz-editor.ts +1 -1
  2. package/client/pages/kpi-category-value/kpi-category-value-list-page.ts +404 -0
  3. package/client/pages/kpi-metric-value/kpi-metric-value-editor-page.ts +763 -0
  4. package/client/pages/kpi-metric-value/kpi-metric-value-list-page.ts +12 -0
  5. package/client/pages/kpi-value/kpi-value-editor-page.ts +774 -0
  6. package/client/pages/kpi-value/kpi-value-list-page.ts +13 -0
  7. package/client/route.ts +16 -0
  8. package/dist-client/pages/kpi/kpi-viz-editor.d.ts +0 -1
  9. package/dist-client/pages/kpi/kpi-viz-editor.js +1 -1
  10. package/dist-client/pages/kpi/kpi-viz-editor.js.map +1 -1
  11. package/dist-client/pages/kpi-category-value/kpi-category-value-list-page.d.ts +63 -0
  12. package/dist-client/pages/kpi-category-value/kpi-category-value-list-page.js +393 -0
  13. package/dist-client/pages/kpi-category-value/kpi-category-value-list-page.js.map +1 -0
  14. package/dist-client/pages/kpi-metric-value/kpi-metric-value-editor-page.d.ts +58 -0
  15. package/dist-client/pages/kpi-metric-value/kpi-metric-value-editor-page.js +736 -0
  16. package/dist-client/pages/kpi-metric-value/kpi-metric-value-editor-page.js.map +1 -0
  17. package/dist-client/pages/kpi-metric-value/kpi-metric-value-list-page.d.ts +1 -0
  18. package/dist-client/pages/kpi-metric-value/kpi-metric-value-list-page.js +11 -0
  19. package/dist-client/pages/kpi-metric-value/kpi-metric-value-list-page.js.map +1 -1
  20. package/dist-client/pages/kpi-value/kpi-value-editor-page.d.ts +55 -0
  21. package/dist-client/pages/kpi-value/kpi-value-editor-page.js +748 -0
  22. package/dist-client/pages/kpi-value/kpi-value-editor-page.js.map +1 -0
  23. package/dist-client/pages/kpi-value/kpi-value-list-page.d.ts +9 -2
  24. package/dist-client/pages/kpi-value/kpi-value-list-page.js +12 -0
  25. package/dist-client/pages/kpi-value/kpi-value-list-page.js.map +1 -1
  26. package/dist-client/route.d.ts +1 -1
  27. package/dist-client/route.js +12 -0
  28. package/dist-client/route.js.map +1 -1
  29. package/dist-client/tsconfig.tsbuildinfo +1 -1
  30. package/dist-server/service/index.d.ts +4 -2
  31. package/dist-server/service/index.js +5 -0
  32. package/dist-server/service/index.js.map +1 -1
  33. package/dist-server/service/kpi-category/kpi-category-mutation.d.ts +2 -3
  34. package/dist-server/service/kpi-category/kpi-category-mutation.js +149 -78
  35. package/dist-server/service/kpi-category/kpi-category-mutation.js.map +1 -1
  36. package/dist-server/service/kpi-category/kpi-category-query.d.ts +1 -9
  37. package/dist-server/service/kpi-category/kpi-category-query.js +3 -165
  38. package/dist-server/service/kpi-category/kpi-category-query.js.map +1 -1
  39. package/dist-server/service/kpi-category-value/index.d.ts +6 -0
  40. package/dist-server/service/kpi-category-value/index.js +10 -0
  41. package/dist-server/service/kpi-category-value/index.js.map +1 -0
  42. package/dist-server/service/kpi-category-value/kpi-category-value-mutation.d.ts +8 -0
  43. package/dist-server/service/kpi-category-value/kpi-category-value-mutation.js +102 -0
  44. package/dist-server/service/kpi-category-value/kpi-category-value-mutation.js.map +1 -0
  45. package/dist-server/service/kpi-category-value/kpi-category-value-query.d.ts +13 -0
  46. package/dist-server/service/kpi-category-value/kpi-category-value-query.js +91 -0
  47. package/dist-server/service/kpi-category-value/kpi-category-value-query.js.map +1 -0
  48. package/dist-server/service/kpi-category-value/kpi-category-value-type.d.ts +19 -0
  49. package/dist-server/service/kpi-category-value/kpi-category-value-type.js +73 -0
  50. package/dist-server/service/kpi-category-value/kpi-category-value-type.js.map +1 -0
  51. package/dist-server/service/kpi-category-value/kpi-category-value.d.ts +19 -0
  52. package/dist-server/service/kpi-category-value/kpi-category-value.js +91 -0
  53. package/dist-server/service/kpi-category-value/kpi-category-value.js.map +1 -0
  54. package/dist-server/service/kpi-metric-value/kpi-metric-value-mutation.js +20 -0
  55. package/dist-server/service/kpi-metric-value/kpi-metric-value-mutation.js.map +1 -1
  56. package/dist-server/service/kpi-value/kpi-value-mutation.d.ts +1 -0
  57. package/dist-server/service/kpi-value/kpi-value-mutation.js +60 -0
  58. package/dist-server/service/kpi-value/kpi-value-mutation.js.map +1 -1
  59. package/dist-server/tsconfig.tsbuildinfo +1 -1
  60. package/package.json +5 -5
  61. package/server/service/index.ts +5 -0
  62. package/server/service/kpi-category/kpi-category-mutation.ts +154 -81
  63. package/server/service/kpi-category/kpi-category-query.ts +1 -155
  64. package/server/service/kpi-category-value/index.ts +7 -0
  65. package/server/service/kpi-category-value/kpi-category-value-mutation.ts +88 -0
  66. package/server/service/kpi-category-value/kpi-category-value-query.ts +62 -0
  67. package/server/service/kpi-category-value/kpi-category-value-type.ts +48 -0
  68. package/server/service/kpi-category-value/kpi-category-value.ts +79 -0
  69. package/server/service/kpi-metric-value/kpi-metric-value-mutation.ts +28 -0
  70. package/server/service/kpi-value/kpi-value-mutation.ts +66 -0
  71. package/things-factory.config.js +3 -0
  72. package/translations/en.json +3 -0
  73. package/translations/ja.json +3 -0
  74. package/translations/ko.json +3 -0
  75. package/translations/ms.json +3 -0
  76. package/translations/zh.json +3 -0
@@ -1,5 +1,5 @@
1
1
  import '@material/web/button/elevated-button.js'
2
- import '@material/web/select/outlined-select.js'
2
+ // import '@material/web/select/outlined-select.js'
3
3
  import '@material/web/textfield/outlined-text-field.js'
4
4
  import '@material/web/icon/icon.js'
5
5
 
@@ -0,0 +1,404 @@
1
+ import '@material/web/icon/icon.js'
2
+ import '@material/web/button/elevated-button.js'
3
+ import '@operato/data-grist/ox-grist.js'
4
+ import '@operato/data-grist/ox-filters-form.js'
5
+ import '@operato/data-grist/ox-record-creator.js'
6
+
7
+ import { CommonButtonStyles, CommonHeaderStyles, CommonGristStyles, ScrollbarStyles } from '@operato/styles'
8
+ import { PageView, store } from '@operato/shell'
9
+ import { css, html } from 'lit'
10
+ import { customElement, property, query } from 'lit/decorators.js'
11
+ import { ScopedElementsMixin } from '@open-wc/scoped-elements'
12
+ import { ColumnConfig, DataGrist, FetchOption } from '@operato/data-grist'
13
+ import { client } from '@operato/graphql'
14
+ import { i18next, localize } from '@operato/i18n'
15
+ import { notify, openPopup } from '@operato/layout'
16
+ import { OxPopup, OxPrompt } from '@operato/popup'
17
+ import { isMobileDevice } from '@operato/utils'
18
+
19
+ import { connect } from 'pwa-helpers/connect-mixin'
20
+ import gql from 'graphql-tag'
21
+
22
+ @customElement('kpi-category-value-list-page')
23
+ export class KpiCategoryValueListPage extends connect(store)(localize(i18next)(ScopedElementsMixin(PageView))) {
24
+ static styles = [
25
+ ScrollbarStyles,
26
+ CommonGristStyles,
27
+ CommonHeaderStyles,
28
+ css`
29
+ :host {
30
+ display: flex;
31
+
32
+ width: 100%;
33
+
34
+ --grid-record-emphasized-background-color: #8b0000;
35
+ --grid-record-emphasized-color: #ff6b6b;
36
+ }
37
+
38
+ ox-grist {
39
+ overflow-y: auto;
40
+ flex: 1;
41
+ }
42
+
43
+ ox-filters-form {
44
+ flex: 1;
45
+ }
46
+ `
47
+ ]
48
+
49
+ @property({ type: Object }) gristConfig: any
50
+ @property({ type: String }) mode: 'CARD' | 'GRID' | 'LIST' = isMobileDevice() ? 'CARD' : 'GRID'
51
+
52
+ @query('ox-grist') private grist!: DataGrist
53
+
54
+ get context() {
55
+ return {
56
+ title: i18next.t('title.kpi category value list'),
57
+ search: {
58
+ handler: (search: string) => {
59
+ this.grist.searchText = search
60
+ },
61
+ value: this.grist.searchText
62
+ },
63
+ filter: {
64
+ handler: () => {
65
+ this.grist.toggleHeadroom()
66
+ }
67
+ },
68
+ help: 'kpi/kpi-category-value',
69
+ actions: [
70
+ {
71
+ title: i18next.t('button.save'),
72
+ action: this._updateKpiCategoryValue.bind(this),
73
+ ...CommonButtonStyles.save
74
+ },
75
+ {
76
+ title: i18next.t('button.delete'),
77
+ action: this._deleteKpiCategoryValue.bind(this),
78
+ ...CommonButtonStyles.delete
79
+ }
80
+ ],
81
+ exportable: {
82
+ name: i18next.t('title.kpi category value list'),
83
+ data: this.exportHandler.bind(this)
84
+ },
85
+ importable: {
86
+ handler: this.importHandler.bind(this)
87
+ }
88
+ }
89
+ }
90
+
91
+ render() {
92
+ const mode = this.mode || (isMobileDevice() ? 'CARD' : 'GRID')
93
+
94
+ return html`
95
+ <ox-grist .mode=${mode} .config=${this.gristConfig} .fetchHandler=${this.fetchHandler.bind(this)}>
96
+ <div slot="headroom" class="header">
97
+ <div class="filters">
98
+ <ox-filters-form autofocus without-search></ox-filters-form>
99
+
100
+ <div id="modes">
101
+ <md-icon @click=${() => (this.mode = 'GRID')} ?active=${mode == 'GRID'}>grid_on</md-icon>
102
+ <md-icon @click=${() => (this.mode = 'LIST')} ?active=${mode == 'LIST'}>format_list_bulleted</md-icon>
103
+ <md-icon @click=${() => (this.mode = 'CARD')} ?active=${mode == 'CARD'}>apps</md-icon>
104
+ </div>
105
+
106
+ <ox-record-creator id="add" .callback=${this.creationCallback.bind(this)}>
107
+ <button>
108
+ <md-icon>add</md-icon>
109
+ </button>
110
+ </ox-record-creator>
111
+ </div>
112
+ </div>
113
+ </ox-grist>
114
+ `
115
+ }
116
+
117
+ async pageInitialized(lifecycle: any) {
118
+ this.gristConfig = {
119
+ list: {
120
+ fields: ['category', 'valueDate', 'score', 'group', 'createdAt', 'updatedAt', 'creator', 'updater'],
121
+ details: ['category', 'valueDate', 'score', 'group', 'createdAt', 'updatedAt', 'creator', 'updater']
122
+ },
123
+ columns: [
124
+ { type: 'gutter', gutterName: 'sequence' },
125
+ { type: 'gutter', gutterName: 'row-selector', multiple: true },
126
+ // KPI Category Value 계산 버튼 추가
127
+ {
128
+ type: 'gutter',
129
+ gutterName: 'button',
130
+ icon: 'calculate',
131
+ title: '계산',
132
+ handlers: {
133
+ click: (columns, data, column, record, rowIndex) => {
134
+ this._calculateKpiCategoryValue(record)
135
+ }
136
+ }
137
+ },
138
+ {
139
+ type: 'string',
140
+ name: 'category',
141
+ header: '카테고리',
142
+ record: { editable: false, renderer: (v, c, r) => r.category?.name },
143
+ width: 150
144
+ },
145
+ { type: 'string', name: 'valueDate', header: '실적일', record: { editable: true }, width: 120 },
146
+ { type: 'number', name: 'score', header: '성과점수', record: { editable: false }, width: 120 },
147
+ { type: 'string', name: 'group', header: '그룹', record: { editable: false }, width: 120 },
148
+ { type: 'datetime', name: 'createdAt', header: '생성일', record: { editable: false }, width: 180 },
149
+ { type: 'datetime', name: 'updatedAt', header: '수정일', record: { editable: false }, width: 180 },
150
+ {
151
+ type: 'resource-object',
152
+ name: 'creator',
153
+ header: '생성자',
154
+ record: { editable: false, renderer: (v, c, r) => r.creator?.name },
155
+ width: 120
156
+ },
157
+ {
158
+ type: 'resource-object',
159
+ name: 'updater',
160
+ header: '수정자',
161
+ record: { editable: false, renderer: (v, c, r) => r.updater?.name },
162
+ width: 120
163
+ }
164
+ ]
165
+ }
166
+ }
167
+
168
+ async pageUpdated(changes: any, lifecycle: any) {
169
+ if (changes.has('mode')) {
170
+ this.grist.mode = this.mode
171
+ }
172
+ }
173
+
174
+ async fetchHandler({ page = 1, limit = 100, sortings = [], filters = [] }: FetchOption) {
175
+ const response = await client.query({
176
+ query: gql`
177
+ query ($filters: [Filter!], $pagination: Pagination, $sortings: [Sorting!]) {
178
+ responses: kpiCategoryValues(filters: $filters, pagination: $pagination, sortings: $sortings) {
179
+ items {
180
+ id
181
+ category {
182
+ id
183
+ name
184
+ }
185
+ valueDate
186
+ score
187
+ group
188
+ updater {
189
+ id
190
+ name
191
+ }
192
+ updatedAt
193
+ creator {
194
+ id
195
+ name
196
+ }
197
+ createdAt
198
+ }
199
+ total
200
+ }
201
+ }
202
+ `,
203
+ variables: {
204
+ filters,
205
+ pagination: { page, limit },
206
+ sortings
207
+ }
208
+ })
209
+
210
+ return {
211
+ total: response.data.responses.total || 0,
212
+ records: response.data.responses.items || []
213
+ }
214
+ }
215
+
216
+ async _deleteKpiCategoryValue() {
217
+ if (
218
+ await OxPrompt.open({
219
+ title: i18next.t('text.are_you_sure'),
220
+ text: i18next.t('text.sure_to_x', { x: i18next.t('text.delete') }),
221
+ confirmButton: { text: i18next.t('button.confirm') },
222
+ cancelButton: { text: i18next.t('button.cancel') }
223
+ })
224
+ ) {
225
+ const ids = this.grist.selected.map(record => record.id)
226
+ if (ids && ids.length > 0) {
227
+ const response = await client.mutate({
228
+ mutation: gql`
229
+ mutation ($ids: [String!]!) {
230
+ deleteKpiCategoryValues(ids: $ids)
231
+ }
232
+ `,
233
+ variables: {
234
+ ids
235
+ }
236
+ })
237
+
238
+ if (!response.errors) {
239
+ this.grist.fetch()
240
+ notify({
241
+ message: i18next.t('text.info_x_successfully', { x: i18next.t('text.delete') })
242
+ })
243
+ }
244
+ }
245
+ }
246
+ }
247
+
248
+ async _updateKpiCategoryValue() {
249
+ let patches = this.grist.dirtyRecords
250
+ if (patches && patches.length) {
251
+ patches = patches.map(patch => {
252
+ let patchField: any = patch.id ? { id: patch.id } : {}
253
+ const dirtyFields = patch.__dirtyfields__
254
+ for (let key in dirtyFields) {
255
+ patchField[key] = dirtyFields[key].after
256
+ }
257
+ patchField.cuFlag = patch.__dirty__
258
+
259
+ return patchField
260
+ })
261
+
262
+ const response = await client.mutate({
263
+ mutation: gql`
264
+ mutation ($patches: [KpiCategoryValuePatch!]!) {
265
+ updateMultipleKpiCategoryValue(patches: $patches) {
266
+ id
267
+ category {
268
+ id
269
+ name
270
+ }
271
+ valueDate
272
+ score
273
+ group
274
+ updater {
275
+ id
276
+ name
277
+ }
278
+ updatedAt
279
+ creator {
280
+ id
281
+ name
282
+ }
283
+ createdAt
284
+ }
285
+ }
286
+ `,
287
+ variables: {
288
+ patches
289
+ }
290
+ })
291
+
292
+ if (!response.errors) {
293
+ this.grist.fetch()
294
+ notify({
295
+ message: i18next.t('text.info_x_successfully', { x: i18next.t('text.save') })
296
+ })
297
+ }
298
+ }
299
+ }
300
+
301
+ async _calculateKpiCategoryValue(kpiCategoryValue) {
302
+ try {
303
+ const response = await client.mutate({
304
+ mutation: gql`
305
+ mutation ($categoryId: String!, $valueDate: String, $group: String) {
306
+ calculateKpiValue(categoryId: $categoryId, valueDate: $valueDate, group: $group) {
307
+ score
308
+ valueDate
309
+ group
310
+ }
311
+ }
312
+ `,
313
+ variables: {
314
+ categoryId: kpiCategoryValue.category.id,
315
+ valueDate: kpiCategoryValue.valueDate,
316
+ group: kpiCategoryValue.group
317
+ }
318
+ })
319
+
320
+ if (!response.errors) {
321
+ const result = response.data.calculateKpiValue
322
+
323
+ // 팝업으로 결과 표시
324
+ await openPopup(html`
325
+ <div style="padding: 20px;">
326
+ <h3>KPI Category Value 계산 결과</h3>
327
+ <p><strong>성과점수:</strong> ${result.score || 'N/A'}</p>
328
+ <p><strong>실적일:</strong> ${result.valueDate || 'N/A'}</p>
329
+ <p><strong>그룹:</strong> ${result.group || 'N/A'}</p>
330
+ </div>
331
+ `)
332
+
333
+ // 목록 새로고침
334
+ this.grist.fetch()
335
+
336
+ notify({
337
+ message: 'KPI Category Value 계산이 완료되었습니다.'
338
+ })
339
+ }
340
+ } catch (error) {
341
+ console.error('KPI Category Value 계산 중 오류:', error)
342
+ notify({
343
+ message: 'KPI Category Value 계산 중 오류가 발생했습니다.'
344
+ })
345
+ }
346
+ }
347
+
348
+ async creationCallback(kpiCategoryValue) {
349
+ const response = await client.mutate({
350
+ mutation: gql`
351
+ mutation ($kpiCategoryValue: NewKpiCategoryValue!) {
352
+ createKpiCategoryValue(kpiCategoryValue: $kpiCategoryValue) {
353
+ id
354
+ category {
355
+ id
356
+ name
357
+ }
358
+ valueDate
359
+ score
360
+ group
361
+ updater {
362
+ id
363
+ name
364
+ }
365
+ updatedAt
366
+ creator {
367
+ id
368
+ name
369
+ }
370
+ createdAt
371
+ }
372
+ }
373
+ `,
374
+ variables: {
375
+ kpiCategoryValue
376
+ }
377
+ })
378
+
379
+ if (!response.errors) {
380
+ this.grist.fetch()
381
+ notify({
382
+ message: i18next.t('text.info_x_successfully', { x: i18next.t('text.create') })
383
+ })
384
+ }
385
+ }
386
+
387
+ async exportHandler() {
388
+ // 임시로 빈 배열 반환
389
+ return []
390
+ }
391
+
392
+ async importHandler(records) {
393
+ const kpiCategoryValues = records.map(record => ({
394
+ categoryId: record.category?.id || record.category,
395
+ valueDate: record.valueDate,
396
+ score: record.score,
397
+ group: record.group
398
+ }))
399
+
400
+ for (const kpiCategoryValue of kpiCategoryValues) {
401
+ await this.creationCallback(kpiCategoryValue)
402
+ }
403
+ }
404
+ }