@things-factory/meta-ui 8.0.0-beta.8 → 8.0.0

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 (115) hide show
  1. package/client/bootstrap.ts +170 -0
  2. package/client/component/filter/filter-form-meta-code-select.ts +102 -0
  3. package/client/component/filter/filter-form-meta-object-select.ts +107 -0
  4. package/client/component/filter/filter-grist-meta-code-select.ts +97 -0
  5. package/client/component/filter/filter-grist-meta-object-select.ts +102 -0
  6. package/client/component/grist/editor/grist-editor-code-input.js +96 -0
  7. package/client/component/grist/editor/grist-editor-meta-code-selector.js +157 -0
  8. package/client/component/grist/editor/grist-editor-meta-object-selector.js +122 -0
  9. package/client/component/grist/renderer/grist-renderer-code-input.js +20 -0
  10. package/client/component/grist/renderer/grist-renderer-meta-code-selector.js +28 -0
  11. package/client/component/grist/renderer/grist-renderer-meta-object-selector.js +25 -0
  12. package/client/component/popup/code-input-editor-popup.js +111 -0
  13. package/client/component/popup/file-upload-popup.js +129 -0
  14. package/client/component/popup/meta-object-selector-popup.ts +356 -0
  15. package/client/component/popup/record-based-code-editor-popup.ts +141 -0
  16. package/client/dynamic-menus.ts +38 -0
  17. package/client/index.ts +18 -0
  18. package/client/load-components.ts +17 -0
  19. package/client/mixin/meta-base-mixin.js +323 -0
  20. package/client/mixin/meta-basic-grist-mixin.js +283 -0
  21. package/client/mixin/meta-button-mixin.js +116 -0
  22. package/client/mixin/meta-form-mixin.js +435 -0
  23. package/client/mixin/meta-grist-tab-mixin.js +335 -0
  24. package/client/mixin/meta-main-tab-mixin.js +267 -0
  25. package/client/mixin/meta-master-detail-mixin.js +395 -0
  26. package/client/mixin/meta-service-mixin.js +306 -0
  27. package/client/mixin/meta-tab-detail-mixin.js +283 -0
  28. package/client/mixin/meta-tab-mixin.js +190 -0
  29. package/client/pages/activity/meta-activity-define-page.js +422 -0
  30. package/client/pages/activity/meta-activity-list-page.js +262 -0
  31. package/client/pages/activity/meta-activity-viewer-element.js +35 -0
  32. package/client/pages/activity/meta-activity-writer-element.js +48 -0
  33. package/client/pages/activity/meta-activiy-mixin.js +79 -0
  34. package/client/pages/button-role/button-role-detail.js +50 -0
  35. package/client/pages/button-role/button-role-page.js +25 -0
  36. package/client/pages/doc-number/doc-number-page.js +24 -0
  37. package/client/pages/doc-number/next-doc-number-popup.js +25 -0
  38. package/client/pages/entity/config-entity.js +955 -0
  39. package/client/pages/entity/main-menu-selector.js +245 -0
  40. package/client/pages/history/history-copy-list-popup.js +145 -0
  41. package/client/pages/history/history-json-list-popup.js +159 -0
  42. package/client/pages/menu/dynamic-menu-template.js +92 -0
  43. package/client/pages/menu/dynamic-menu.ts +744 -0
  44. package/client/pages/menu/export-menu-popup.js +468 -0
  45. package/client/pages/meta-form-element.js +9 -0
  46. package/client/pages/meta-grist-element.js +12 -0
  47. package/client/pages/meta-grist-page.js +16 -0
  48. package/client/pages/meta-grist-tab-element.js +16 -0
  49. package/client/pages/meta-grist-tab-page.js +16 -0
  50. package/client/pages/meta-main-tab-element.js +12 -0
  51. package/client/pages/meta-main-tab-page.js +16 -0
  52. package/client/pages/meta-master-detail-element.js +12 -0
  53. package/client/pages/meta-master-detail-page.js +16 -0
  54. package/client/pages/meta-tab-detail-element.js +12 -0
  55. package/client/pages/meta-tab-detail-page.js +16 -0
  56. package/client/pages/meta-tab-element.js +15 -0
  57. package/client/pages/printer-device/printer-device-page.js +24 -0
  58. package/client/pages/template/doc-template-page.js +24 -0
  59. package/client/pages/template/template-file-page.js +24 -0
  60. package/client/pages/terms/config-terminology.js +214 -0
  61. package/client/pages/work-code/work-code-detail-popup.js +16 -0
  62. package/client/pages/work-code/work-code-page.js +23 -0
  63. package/client/route.ts +36 -0
  64. package/client/tsconfig.json +13 -0
  65. package/client/utils/grist-default-value.js +36 -0
  66. package/client/utils/meta-api.js +811 -0
  67. package/client/utils/meta-crypto.js +52 -0
  68. package/client/utils/meta-ui-util.js +3304 -0
  69. package/client/utils/rest-service-util.js +328 -0
  70. package/client/utils/service-util.js +1327 -0
  71. package/client/utils/terms-util.ts +119 -0
  72. package/client/utils/ui-util.js +338 -0
  73. package/client/utils/value-util.js +234 -0
  74. package/dist-client/tsconfig.tsbuildinfo +1 -1
  75. package/dist-client/utils/service-util.d.ts +2 -2
  76. package/dist-client/utils/service-util.js +4 -4
  77. package/dist-client/utils/service-util.js.map +1 -1
  78. package/dist-server/tsconfig.tsbuildinfo +1 -1
  79. package/package.json +24 -24
  80. package/server/activity/CommonActivity.ts +68 -0
  81. package/server/index.ts +3 -0
  82. package/server/routes.ts +61 -0
  83. package/server/service/button-role/button-role-mutation.ts +105 -0
  84. package/server/service/button-role/button-role-query.ts +53 -0
  85. package/server/service/button-role/button-role-type.ts +39 -0
  86. package/server/service/button-role/button-role.ts +61 -0
  87. package/server/service/button-role/index.ts +7 -0
  88. package/server/service/dynamic-menu/dynamic-menu-query.ts +270 -0
  89. package/server/service/dynamic-menu/dynamic-menu-type.ts +74 -0
  90. package/server/service/dynamic-menu/index.ts +3 -0
  91. package/server/service/entity-event-subscriber/entity-event-subscriber.ts +80 -0
  92. package/server/service/entity-event-subscriber/index.ts +3 -0
  93. package/server/service/index.ts +41 -0
  94. package/server/service/menu-button-auth/index.ts +7 -0
  95. package/server/service/menu-button-auth/menu-button-auth-mutation.ts +133 -0
  96. package/server/service/menu-button-auth/menu-button-auth-query.ts +138 -0
  97. package/server/service/menu-button-auth/menu-button-auth-type.ts +63 -0
  98. package/server/service/menu-button-auth/menu-button-auth.ts +92 -0
  99. package/server/service/meta-activity/index.ts +5 -0
  100. package/server/service/meta-activity/meta-activity-mutation.ts +191 -0
  101. package/server/service/meta-activity/meta-activity-query.ts +43 -0
  102. package/server/service/meta-activity/meta-activity-type.ts +56 -0
  103. package/server/service/set-translations/index.ts +3 -0
  104. package/server/service/set-translations/set-translation-resolver.ts +63 -0
  105. package/server/service/work-code/index.ts +6 -0
  106. package/server/service/work-code/work-code-mutation.ts +147 -0
  107. package/server/service/work-code/work-code-query.ts +67 -0
  108. package/server/service/work-code/work-code-type.ts +60 -0
  109. package/server/service/work-code/work-code.ts +83 -0
  110. package/server/service/work-code-detail/index.ts +6 -0
  111. package/server/service/work-code-detail/work-code-detail-mutation.ts +149 -0
  112. package/server/service/work-code-detail/work-code-detail-query.ts +59 -0
  113. package/server/service/work-code-detail/work-code-detail-type.ts +50 -0
  114. package/server/service/work-code-detail/work-code-detail.ts +82 -0
  115. package/server/tsconfig.json +9 -0
@@ -0,0 +1,1327 @@
1
+ import gql from 'graphql-tag'
2
+
3
+ import { asyncReplace } from 'lit/directives/async-replace.js'
4
+
5
+ import { store } from '@operato/shell'
6
+ import { getCodeByName } from '@things-factory/code-base'
7
+ import { EXPORT } from '@things-factory/export-base'
8
+ import { gqlBuilder } from '@things-factory/utils'
9
+ import { generateActivitySummary, startSubscribeActivitySummary } from '@things-factory/worklist'
10
+
11
+ import { client } from '@operato/graphql'
12
+
13
+ import { TermsUtil } from './terms-util'
14
+ import { UiUtil } from './ui-util'
15
+ import { ValueUtil } from './value-util'
16
+
17
+ /**
18
+ * @license
19
+ * Copyright © HatioLab Inc. All rights reserved.
20
+ * @author Shortstop <shortstop@hatiolab.com>
21
+ * @description 서버 측 서비스 핸들링 유틸리티 함수 정의
22
+ */
23
+ export class ServiceUtil {
24
+ /**
25
+ * @description 공통 코드 명으로 공통 코드 리스트 조회 후 리턴
26
+ *****************************************************
27
+ * @param {String, Array} codeSet 공통 코드 명 또는 표현될 Array
28
+ * @returns {Array} 코드 리스트
29
+ */
30
+ static async codeItems(codeSet) {
31
+ // CodeSet 이 Array 일때 (설정 값을 셀렉터로 변환)
32
+ if (Array.isArray(codeSet)) {
33
+ let codes = codeSet.map(codeValue => {
34
+ if (typeof codeValue === 'object') {
35
+ return { name: codeValue.value, description: codeValue.display }
36
+ } else {
37
+ return { name: codeValue, description: codeValue }
38
+ }
39
+ })
40
+
41
+ return codes
42
+ }
43
+
44
+ // codeSet이 문자이면 코드 조회
45
+ return await getCodeByName(codeSet)
46
+ }
47
+
48
+ /**
49
+ * @description code selector 에디터를 위한 공통 코드 조회
50
+ *****************************************************
51
+ * @param {String, Array} codeSet 공통 코드 명 또는 표현될 Array
52
+ * @returns {Array} 설정값 클리어를 위해 빈 값이 추가된 코드 리스트
53
+ */
54
+ static async getCodeSelectorData(codeSet) {
55
+ let codeItems = await ServiceUtil.codeItems(codeSet)
56
+ let codeSelectors = codeItems.map(c => {
57
+ return {
58
+ value: c.name,
59
+ display: c.description
60
+ }
61
+ })
62
+ codeSelectors.unshift({ value: '', display: '' })
63
+ return codeSelectors
64
+ }
65
+
66
+ /**
67
+ * 시나리오를 호출해 코드 정보를 가져온다.
68
+ *********************************
69
+ * @description 시나리오에서는 name, description 필드를 리턴해 줘야 한다.
70
+ * @param {String} scenarioName
71
+ * @param {Array} variables
72
+ */
73
+ static async getCodeByScenario(scenarioName, variables) {
74
+ let response = await ServiceUtil.callScenario(null, scenarioName, variables, false)
75
+ if (
76
+ response &&
77
+ response.data &&
78
+ response.data.runScenario &&
79
+ response.data.runScenario.result &&
80
+ response.data.runScenario.result.result
81
+ ) {
82
+ let codes = response.data.runScenario.result.result
83
+ codes.unshift({ value: '', display: '' })
84
+ return codes.map(x => {
85
+ return {
86
+ value: x.name,
87
+ display: x.description
88
+ }
89
+ })
90
+ } else {
91
+ return []
92
+ }
93
+ }
94
+
95
+ /**
96
+ * 엔티티 쿼리를 호출해 코드 정보를 가져온다.
97
+ ***********************************
98
+ * @description
99
+ * @param {Object} variables
100
+ */
101
+ static async getCodeByEntity({ queryName = undefined, codeField = 'name', dispField, filters = [], sorters = [] }) {
102
+ let dispFields = dispField.split(',')
103
+
104
+ let selectFields = `
105
+ ${codeField}
106
+ ${dispField ? dispFields.join('\n') : ''}
107
+ `
108
+
109
+ let result = await ServiceUtil.searchByPagination(queryName, filters, sorters, 0, 0, selectFields)
110
+ let codes = [{ value: '', display: '' }]
111
+
112
+ result.records.forEach(x => {
113
+ let disp = x[codeField]
114
+
115
+ if (dispField) {
116
+ let fields = dispField.split(',')
117
+ disp = x[fields[0]]
118
+
119
+ if (fields.length > 1) {
120
+ disp += `(${fields
121
+ .splice(1)
122
+ .map(field => {
123
+ return x[field]
124
+ })
125
+ .join(',')})`
126
+ }
127
+ }
128
+
129
+ codes.push({
130
+ value: x[codeField],
131
+ display: disp
132
+ })
133
+ })
134
+
135
+ return codes
136
+ }
137
+
138
+ /**
139
+ * @description 확장 속성 옵션 정보를 리턴
140
+ *************************************
141
+ * @param {String} entityName
142
+ * @return {Object}
143
+ */
144
+ static async getAttributeSetOptionFor(entityName) {
145
+ return {
146
+ objectified: true,
147
+ attributeSet: await ServiceUtil.getAttributeSetFor(entityName)
148
+ }
149
+ }
150
+
151
+ /**
152
+ * @description 확장 속성 메타 정보를 조회
153
+ *************************************
154
+ * @param {String} entityName
155
+ * @return {Object}
156
+ */
157
+ static async getAttributeSetFor(entityName) {
158
+ const response = await client.query({
159
+ query: gql`
160
+ query {
161
+ attributeSet: attributeSetByEntity(entity: "${entityName}") {
162
+ entity
163
+ description
164
+ items {
165
+ name
166
+ description
167
+ tag
168
+ type
169
+ active
170
+ hidden
171
+ options
172
+ }
173
+ }
174
+ }
175
+ `
176
+ })
177
+
178
+ return response.data?.attributeSet || {}
179
+ }
180
+
181
+ /**
182
+ * @description 현재 사용자의 권한에 맞는 메뉴 정보 요청
183
+ ***********************************************
184
+ * @returns {Array} 메뉴 바에 표시될 메뉴 리스트
185
+ */
186
+ static async myDynamicMenus() {
187
+ let menuItems = await ServiceUtil.searchMyDynamicMenus()
188
+ return menuItems != null ? ServiceUtil.arrangeMenuRoutes(menuItems) : null
189
+ }
190
+
191
+ /**
192
+ * @description 메뉴 틀에 맞게 재 배치
193
+ **************************************
194
+ * @param {Array} menuData 메뉴 정보에서 조회한 메뉴 리스트
195
+ * @returns {Object} 메뉴 바 틀에 맞게 재 배치된 메뉴 & 라우팅 정보
196
+ */
197
+ static arrangeMenuRoutes(resMenuData) {
198
+ let menuData = []
199
+
200
+ /**
201
+ * name : 메뉴 명 (DynamicMenu)
202
+ * category : 태그 명 (dynamic-menu)
203
+ * routing : 라우팅 (dynamic_menu)
204
+ * resourceUrl : 페이지 위치 (./pages/menu/dynamic-menu)
205
+ */
206
+ resMenuData.forEach(item => {
207
+ menuData.push(item)
208
+
209
+ if (item.children) {
210
+ item.children.sort(function (a, b) {
211
+ return a.rank - b.rank
212
+ })
213
+
214
+ item.children.forEach(subItem => {
215
+ if (subItem.hiddenFlag != true) {
216
+ subItem.parent = item
217
+ menuData.push(subItem)
218
+ }
219
+ })
220
+ }
221
+ })
222
+
223
+ // 1. 서버에서 조회한 메뉴 정보 중에 메인 메뉴를 추출
224
+ let mainMenus = menuData
225
+ .map(item => {
226
+ if (item.menuType == 'GROUP' || item.menuType == 'MENU') {
227
+ let pMenu = {
228
+ id: item.id,
229
+ parent: true,
230
+ name: TermsUtil.tMenu(item.name),
231
+ icon: item.iconPath,
232
+ type: item.menuType == 'GROUP' ? 'group' : '',
233
+ menus: []
234
+ }
235
+
236
+ if (item.routing) {
237
+ pMenu.path = item.routing
238
+ }
239
+
240
+ return pMenu
241
+ } else {
242
+ return { id: '0' }
243
+ }
244
+ })
245
+ .filter(i => i.id != '0')
246
+
247
+ // 2. 메인 메뉴 하위에 서브 메뉴를 추가
248
+ let routes = menuData
249
+ .map(item => {
250
+ if (item.menuType == 'SCREEN' || item.menuType == 'HIDDEN-PAGE') {
251
+ // 2.1 메뉴가 SCREEN 유형이라면 자신의 부모 메뉴를 찾아서
252
+ let parentMenu = mainMenus.find(main => main.id == item.parent.id)
253
+ if (parentMenu) {
254
+ let title = TermsUtil.tMenu(item.name)
255
+
256
+ // 2.2 좌측 메뉴에 표시되도록 부모 메뉴의 하위 메뉴로 추가하여 메뉴 트리 구성
257
+ if (item.menuType == 'SCREEN') {
258
+ parentMenu.menus.push({
259
+ id: item.id,
260
+ name: title,
261
+ path: item.routing,
262
+ icon: item.iconPath
263
+ })
264
+ }
265
+
266
+ // 2.3 애플리케이션 라우팅 정보를 모아 routes 정보로 사용
267
+ return {
268
+ id: item.id,
269
+ parent: false,
270
+ parent_id: parentMenu.id,
271
+ title: title,
272
+ tagname: item.category,
273
+ page: item.routing,
274
+ template: item.resourceUrl,
275
+ routing_type: item.routingType
276
+ }
277
+ } else {
278
+ return { id: '0' }
279
+ }
280
+ } else if (item.menuType == 'HOME') {
281
+ return {
282
+ id: item.id,
283
+ parent: true,
284
+ parent_id: null,
285
+ title: TermsUtil.tMenu(item.name),
286
+ tagname: item.category,
287
+ page: item.routing,
288
+ template: item.resourceUrl,
289
+ routing_type: item.routingType
290
+ }
291
+ } else {
292
+ return { id: '0' }
293
+ }
294
+ })
295
+ .filter(i => i != undefined && i.id != '0')
296
+
297
+ // 3. 메뉴 그룹 하위에 해당 하는 페이지 정렬
298
+ mainMenus
299
+ .filter(x => x.type == 'group' && x.menus && x.menus.length > 0)
300
+ .forEach(groupMenu => {
301
+ let groupIdx = mainMenus.findIndex(m => m.id == groupMenu.id) + 1
302
+ mainMenus.splice(groupIdx, 0, ...groupMenu.menus)
303
+ })
304
+
305
+ // 4. 업무, 결재 관련 메뉴 확인 및 badge 등록
306
+ let badgeCnt = 0
307
+ mainMenus.forEach(menu => {
308
+ if (menu.path == 'todo-list') {
309
+ // 내 업무함
310
+ badgeCnt++
311
+ menu.badge = () => asyncReplace(generateActivitySummary('numberOfToDos'))
312
+ menu.active = ({ path }) => /^activity\//.test(path)
313
+ } else if (menu.path == 'approval-pending-list') {
314
+ // 내 결재함
315
+ badgeCnt++
316
+ menu.badge = () => asyncReplace(generateActivitySummary('numberOfApprovalPendings'))
317
+ menu.active = ({ path }) => /^activity\//.test(path)
318
+ } else if (menu.path == 'draft-list') {
319
+ // 작성 중 업무
320
+ badgeCnt++
321
+ menu.badge = () => asyncReplace(generateActivitySummary('numberOfDrafts'))
322
+ }
323
+ })
324
+
325
+ if (badgeCnt > 0) {
326
+ startSubscribeActivitySummary()
327
+ }
328
+
329
+ // 5. 메뉴 데이터 리턴
330
+ return { menus: mainMenus, routes: routes }
331
+ }
332
+
333
+ /**
334
+ * @description 현재 사용자의 권한에 맞는 메뉴 정보 요청
335
+ ***********************************************
336
+ * @returns {Array} 메뉴 바에 표시될 메뉴 리스트
337
+ */
338
+ static async searchMyDynamicMenus() {
339
+ const response = await client.query({
340
+ query: gql`
341
+ query MyMenus($sortings: [Sorting!], $filters: [Filter!]) {
342
+ myMenus(sortings: $sortings, filters: $filters) {
343
+ items {
344
+ id
345
+ parent {
346
+ id
347
+ name
348
+ }
349
+ children(permittedOnly: true) {
350
+ id
351
+ name
352
+ description
353
+ menuType
354
+ category
355
+ routingType
356
+ routing
357
+ resourceUrl
358
+ iconPath
359
+ hiddenFlag
360
+ rank
361
+ }
362
+ name
363
+ description
364
+ menuType
365
+ category
366
+ routingType
367
+ routing
368
+ resourceUrl
369
+ iconPath
370
+ }
371
+ total
372
+ }
373
+ }
374
+ `,
375
+ variables: {
376
+ filters: [
377
+ {
378
+ name: 'hiddenFlag',
379
+ operator: 'noteq',
380
+ value: true
381
+ },
382
+ {
383
+ // name: 'menuType', operator: 'in', value: ['HOME', 'MENU', 'GROUP', 'SCREEN', 'HIDDEN-PAGE']
384
+ name: 'menuType',
385
+ operator: 'in',
386
+ value: ['HOME', 'MENU', 'GROUP']
387
+ }
388
+ ],
389
+ sortings: [{ name: 'rank' }]
390
+ }
391
+ })
392
+
393
+ return response.data?.myMenus.items || []
394
+ }
395
+
396
+ /**
397
+ * @description 응답이 처리된 후 Graphql Error 메시지 표시
398
+ ****************************************************
399
+ * @param {Object} response 응답
400
+ */
401
+ static async showGraphqlErrorResponse(response) {
402
+ if (response.errors && response.errors.length > 0) {
403
+ let error = response.errors[0]
404
+ let errContent = error.message
405
+ let errTitle = error.message
406
+
407
+ if (error.extensions) {
408
+ let errExt = error.extensions
409
+ errTitle = errExt.code
410
+ }
411
+
412
+ await UiUtil.showAlertPopup(errTitle, errContent, 'error', 'confirm')
413
+ }
414
+ }
415
+
416
+ /**
417
+ * @description Graphql 실행 시 Exception 표시
418
+ ********************************************
419
+ * @param {Object} exception graphql 에러
420
+ */
421
+ static async showGraphqlException(exception) {
422
+ if (exception) {
423
+ if (exception.networkError && exception.networkError.result) {
424
+ await ServiceUtil.showGraphqlErrorResponse(exception.networkError.result)
425
+ } else {
426
+ await UiUtil.showAlertPopup('Graphql Error', exception.message, 'error', 'confirm')
427
+ }
428
+ }
429
+ }
430
+
431
+ /**
432
+ * @description 그리드 데이터 내보내기
433
+ *********************************
434
+ * @param {Boolean} isElement 팝업으로 실행 중인지 여부
435
+ * @param {String} exportTitle 그리드 내보내기 용 타이틀
436
+ * @param {Grist} grist 그리드 오브젝트
437
+ * @returns {Object} { header: headerSetting, data: data } or Not
438
+ */
439
+ static async exportableData(isElement, exportTitle, grist) {
440
+ // 1. 헤더 설정
441
+ var headerSetting = grist._config.columns
442
+ .filter(
443
+ column =>
444
+ column.type !== 'gutter' && column.record !== undefined && column.imex !== undefined && column.hidden !== true
445
+ )
446
+ .map(column => {
447
+ return column.imex
448
+ })
449
+
450
+ // 2. 그리드에 표시된 데이터 추출
451
+ let records = grist.data.records
452
+ var data = records.map(item => {
453
+ return {
454
+ ...grist._config.columns
455
+ .filter(
456
+ column =>
457
+ column.type !== 'gutter' &&
458
+ column.record !== undefined &&
459
+ column.imex !== undefined &&
460
+ column.hidden !== true
461
+ )
462
+ .reduce((record, column) => {
463
+ record[column.imex.key] = column.imex.key.split('.').reduce((obj, key) => {
464
+ const val = obj && obj[key] !== 'undefined' ? obj[key] : undefined
465
+
466
+ if (column.type == 'select') {
467
+ return column?.record?.options?.find(option => option.value == val)?.display
468
+ } else if (column.type.includes('object')) {
469
+ return val?.name
470
+ }
471
+
472
+ return val
473
+ }, item)
474
+ return record
475
+ }, {})
476
+ }
477
+ })
478
+
479
+ // element로 호출된 경우 리턴 없이 바로 xlsx 내보내기
480
+ if (isElement) {
481
+ store.dispatch({
482
+ type: EXPORT,
483
+ exportable: {
484
+ extension: 'xlsx',
485
+ name: exportTitle,
486
+ data: { header: headerSetting, data: data }
487
+ }
488
+ })
489
+ } else {
490
+ return { header: headerSetting, data: data }
491
+ }
492
+ }
493
+
494
+ /**
495
+ * @description 서버 사이드를 거쳐 그리드 데이터 내보내기
496
+ ************************************************
497
+ * @param {String} exportFileName 그리드 내보내기 용 타이틀 (파일명)
498
+ * @param {String} exportQueryName 서버 사이드 GraphQL 쿼리명
499
+ * @param {Object} filterParam 필터용 파라미터
500
+ * @returns {Object} { header: headerSetting, data: data } or Not
501
+ */
502
+ static async exportableDataByServer(exportFileName, exportQueryName, filterParam) {
503
+ // 1. 내보내기 graphQL 요청
504
+ const response = await client.query({
505
+ query: gql`
506
+ query Query($filters: [Filter!]!) {
507
+ responses: ${exportQueryName}(filters: $filters)
508
+ }
509
+ `,
510
+ variables: {
511
+ filters: filterParam
512
+ }
513
+ })
514
+
515
+ // 2. 응답 처리
516
+ const data = response.data.responses
517
+ const bufferData = Buffer.from(data, 'base64')
518
+ const blob = new Blob([bufferData], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' })
519
+
520
+ const a = document.createElement('a')
521
+ a.href = window.URL.createObjectURL(blob)
522
+ a.download = `${exportFileName}.xlsx`
523
+ document.body.appendChild(a)
524
+ a.click()
525
+ a.remove()
526
+ }
527
+
528
+ /**
529
+ * @description 데이터 리스트 페이지네이션 조회
530
+ ****************************************
531
+ * @param {String} queryFunc 쿼리 함수
532
+ * @param {Array} filters 조회 조건
533
+ * @param {Array} sortings 소팅 조건
534
+ * @param {Number|undefined} page 현재 페이지
535
+ * @param {Number} limit 페이지 당 표시할 레코드 건수
536
+ * @param {String} selectFields 조회할 필드 리스트
537
+ * @returns {Array} 조회 결과 { records : [{...}], total : 100 }
538
+ */
539
+ static async searchByPagination(queryFunc, filters, sortings, page = 1, limit, selectFields) {
540
+ try {
541
+ const response = await client.query({
542
+ query: gql`
543
+ query ($filters: [Filter!], $pagination: Pagination, $sortings: [Sorting!]) {
544
+ ${queryFunc}(filters: $filters, pagination: $pagination, sortings: $sortings) {
545
+ items {
546
+ ${selectFields}
547
+ }
548
+ total
549
+ }
550
+ }
551
+ `,
552
+ variables: { filters, sortings, pagination: { page, limit } }
553
+ })
554
+
555
+ if (!response.errors) {
556
+ return {
557
+ records: response.data[queryFunc].items,
558
+ total: response.data[queryFunc].total
559
+ }
560
+ } else {
561
+ ServiceUtil.showGraphqlErrorResponse(response)
562
+ }
563
+ } catch (e) {
564
+ ServiceUtil.showGraphqlException(e)
565
+ }
566
+
567
+ return null
568
+ }
569
+
570
+ /**
571
+ * @description 레코드의 id를 이용해 데이터 한 건 조회
572
+ **********************************************
573
+ * @param {String} queryFunc 조회할 query 함수명
574
+ * @param {String} id 레코드 ID
575
+ * @param {String} selectFields 조회할 필드 리스트
576
+ * @return {Object} 조회한 레코드
577
+ */
578
+ static async findOne(queryFunc, id, selectFields) {
579
+ try {
580
+ let response = await client.query({
581
+ query: gql`
582
+ query ($id: String!) {
583
+ ${queryFunc}(id: $id) {
584
+ ${selectFields}
585
+ }
586
+ }
587
+ `,
588
+ variables: { id: id }
589
+ })
590
+
591
+ if (!response.errors) {
592
+ return response.data[queryFunc] || {}
593
+ } else {
594
+ ServiceUtil.showGraphqlErrorResponse(response)
595
+ }
596
+ } catch (e) {
597
+ ServiceUtil.showGraphqlException(e)
598
+ }
599
+
600
+ return null
601
+ }
602
+
603
+ /**
604
+ * @description 필터 정보를 이용해 데이터 한 건 조회
605
+ *********************************************
606
+ * @param {String} queryFunc 조회할 query 함수명
607
+ * @param {String} selectFields 조회할 필드 리스트
608
+ * @param {Array} filters 필터
609
+ * @return {Object} 조회한 레코드
610
+ */
611
+ static async findByFilters(queryFunc, selectFields, filters) {
612
+ try {
613
+ const response = await client.query({
614
+ query: gql`
615
+ query ($filters: [Filter!]) {
616
+ ${queryFunc}(filters: $filters) {
617
+ ${selectFields}
618
+ }
619
+ }
620
+ `,
621
+ variables: { filters }
622
+ })
623
+
624
+ if (!response.errors) {
625
+ return response.data[queryFunc] || {}
626
+ } else {
627
+ ServiceUtil.showGraphqlErrorResponse(response)
628
+ }
629
+ } catch (e) {
630
+ ServiceUtil.showGraphqlException(e)
631
+ }
632
+
633
+ return null
634
+ }
635
+
636
+ /**
637
+ * @description 필터 정보를 이용해 데이터 한 건 조회
638
+ *********************************************
639
+ * @param {String} queryFunc 조회할 query 함수명
640
+ * @param {String} selectFields 조회할 필드 리스트
641
+ * @param {Array} filters 필터
642
+ * @return {Object} 조회한 레코드
643
+ */
644
+ static async searchByFilters(queryFunc, selectFields, filters) {
645
+ try {
646
+ const response = await client.query({
647
+ query: gql`
648
+ query ($filters: [Filter!], $sortings: [Sorting!]) {
649
+ ${queryFunc}(filters: $filters, sortings: $sortings) {
650
+ items {
651
+ ${selectFields}
652
+ }
653
+ }
654
+ }
655
+ `,
656
+ variables: { filters }
657
+ })
658
+
659
+ if (!response.errors) {
660
+ return response.data[queryFunc].items || []
661
+ } else {
662
+ ServiceUtil.showGraphqlErrorResponse(response)
663
+ }
664
+ } catch (e) {
665
+ ServiceUtil.showGraphqlException(e)
666
+ }
667
+
668
+ return null
669
+ }
670
+
671
+ /**
672
+ * @description 그리드에서 선택된 행의 ID 리턴
673
+ ****************************************
674
+ * @param {Object} grist 그리스트
675
+ * @param {Boolean} alertWhenEmpty 선택된 행이 없는 경우 팝업을 실행할 것인지 여부
676
+ * @returns {Array} 선택 ID 리스트
677
+ */
678
+ static getSelectedIdList(grist, alertWhenEmpty) {
679
+ const ids = grist.selected.map(record => record.id)
680
+
681
+ if (ValueUtil.isEmpty(ids) && alertWhenEmpty == true) {
682
+ UiUtil.showAlertPopup('text.nothing_selected', 'text.there_is_nothing_to_delete', 'info', 'confirm')
683
+ }
684
+
685
+ return ids
686
+ }
687
+
688
+ /**
689
+ * @description 그리스트의 선택된 레코드 삭제 처리
690
+ ******************************************
691
+ * @param {Object} grist 그리스트
692
+ * @param {Object} mutationFunc 삭제 처리할 함수 명
693
+ * @returns {Boolean} 삭제 성공 여부
694
+ */
695
+ static async deleteListByGristSelected(grist, mutationFunc) {
696
+ let ids = ServiceUtil.getSelectedIdList(grist, true)
697
+
698
+ if (ValueUtil.isNotEmpty(ids)) {
699
+ let result = await ServiceUtil.deleteListByIds(ids, mutationFunc)
700
+ if (result) {
701
+ grist.fetch()
702
+ }
703
+ }
704
+ }
705
+
706
+ /**
707
+ * @description ID 배열로 레코드 삭제 처리
708
+ **************************************
709
+ * @param {Array} ids 삭제할 레코드 ID 리스트
710
+ * @param {Object} mutationFunc 삭제 처리할 함수
711
+ * @returns {Boolean} 삭제 성공 여부
712
+ */
713
+ static async deleteListByIds(ids, mutationFunc) {
714
+ const answer = await UiUtil.showAlertPopup('button.delete', 'text.are_you_sure', 'question', 'delete', 'cancel')
715
+ if (answer.value) {
716
+ try {
717
+ const response = await client.query({
718
+ query: gql`
719
+ mutation {
720
+ ${mutationFunc}(${gqlBuilder.buildArgs({ ids })})
721
+ }`
722
+ })
723
+
724
+ if (!response.errors) {
725
+ UiUtil.showToast('info', TermsUtil.tText('success to delete'))
726
+ return true
727
+ } else {
728
+ ServiceUtil.showGraphqlErrorResponse(response)
729
+ }
730
+ } catch (e) {
731
+ ServiceUtil.showGraphqlException(e)
732
+ }
733
+ }
734
+
735
+ return false
736
+ }
737
+
738
+ /**
739
+ * @description 그리스트에서 변경, 추가된 여러 데이터를 한 꺼번에 업데이트
740
+ *************************************************************
741
+ * @param {Object} grist 그리스트
742
+ * @param {String} mutationFunc updateMultiple을 위한 서버 측 mutation 함수 명
743
+ * @returns {Boolean} 업데이트 성공 여부
744
+ */
745
+ static async updateMultipleData(grist, mutationFunc) {
746
+ let patches = ServiceUtil.patchesForUpdateMultiple(grist)
747
+
748
+ if (ValueUtil.isNotEmpty(patches)) {
749
+ let result = await ServiceUtil.updateMultiple(mutationFunc, patches)
750
+ if (result) {
751
+ grist.fetch()
752
+ return result
753
+ }
754
+ }
755
+
756
+ return false
757
+ }
758
+
759
+ /**
760
+ * @description 변경, 추가된 여러 데이터를 한 꺼번에 업데이트
761
+ ***************************************************
762
+ * @param {String} mutationFunc updateMultiple을 위한 서버 측 mutation 함수 명
763
+ * @param {Array} patches 변경, 추가된 여러 데이터
764
+ * @returns {Boolean} 업데이트 성공 여부
765
+ */
766
+ static async updateMultiple(mutationFunc, patches) {
767
+ try {
768
+ const response = await client.query({
769
+ query: gql`
770
+ mutation {
771
+ ${mutationFunc}(${gqlBuilder.buildArgs({ patches })}) {
772
+ id
773
+ }
774
+ }`,
775
+ context: {
776
+ hasUpload: true
777
+ }
778
+ })
779
+
780
+ if (!response.errors) {
781
+ UiUtil.showToast('info', TermsUtil.tText('success to save'))
782
+ return true
783
+ } else {
784
+ ServiceUtil.showGraphqlErrorResponse(response)
785
+ }
786
+ } catch (e) {
787
+ ServiceUtil.showGraphqlException(e)
788
+ }
789
+
790
+ return false
791
+ }
792
+
793
+ /**
794
+ * @description 그리스트에서 변경, 추가된 내용 추출
795
+ ********************************************
796
+ * @param {Object} grist 그리스트
797
+ * @param {Boolean} isObjectOrigin
798
+ * @returns {Array} 그리스트에서 변경, 추가된 데이터 리스트
799
+ */
800
+ static patchesForUpdateMultiple(grist, isObjectOrigin = false) {
801
+ // 그리드 변경 리스트
802
+ let cudRecords = grist.dirtyRecords
803
+ // 필수 입력 컬럼
804
+ let mandatoryColumns = {}
805
+
806
+ ;((grist.config || {}).columns || [])
807
+ .filter(x => (x.record || {}).mandatory === true)
808
+ .forEach(x => {
809
+ mandatoryColumns[x.name] = x.header
810
+ })
811
+
812
+ // 코드 인풋 타입
813
+ let codeInputColumns = ((grist.config || {}).columns || []).filter(x => x.type === 'code-input').map(x => x.name)
814
+
815
+ // 변경 없음
816
+ if (!cudRecords || cudRecords.length == 0) {
817
+ UiUtil.showAlertPopup('text.nothing_changed', 'text.NOTHING_CHANGED', 'info', 'confirm')
818
+ return
819
+ }
820
+
821
+ let patchList = []
822
+
823
+ // root records
824
+ for (let idx in cudRecords) {
825
+ let record = cudRecords[idx]
826
+ let patchData = record.id ? { id: record.id } : {}
827
+
828
+ // 필수 입력 값 체크
829
+ for (let key in mandatoryColumns) {
830
+ if (record[key] === undefined || record[key] == '') {
831
+ console.log(record)
832
+ UiUtil.showAlertPopup(
833
+ 'text.check-mandatory',
834
+ TermsUtil.tText('check-mandatory-field', { x: mandatoryColumns[key] }),
835
+ 'info',
836
+ 'confirm'
837
+ )
838
+ return
839
+ }
840
+ }
841
+
842
+ // 레코드 정보에서 저장 가능 유형으로 변환
843
+ for (let key in record) {
844
+ if (
845
+ ![
846
+ 'creator',
847
+ 'updater',
848
+ 'createdAt',
849
+ 'updatedAt',
850
+ '__dirty__',
851
+ '__dirtyfields__',
852
+ '__origin__',
853
+ '__seq__',
854
+ '__selected__'
855
+ ].includes(key)
856
+ ) {
857
+ // code-input 타입은 오브젝트 값을 가지지만 id 만 가져가지 않는다. 예외 처리
858
+ if (
859
+ codeInputColumns.includes(key) == false &&
860
+ record[key] &&
861
+ typeof record[key] === 'object' &&
862
+ isObjectOrigin == false
863
+ ) {
864
+ patchData[key] = { id: record[key].id }
865
+ } else {
866
+ patchData[key] = record[key]
867
+ }
868
+ }
869
+ }
870
+
871
+ patchData.cuFlag = record.__dirty__
872
+ patchList.push(patchData)
873
+ }
874
+
875
+ return patchList
876
+ }
877
+
878
+ /**
879
+ * @description 한 건 레코드 업데이트
880
+ *********************************
881
+ * @param {String} mutationFunc 서버 측 업데이트 mutation 함수 명
882
+ * @param {String} id 변경할 레코드의 id
883
+ * @param {Object} patch 변경할 레코드
884
+ * @returns {Boolean} 업데이트 성공 여부
885
+ */
886
+ static async updateOne(mutationFunc, id, patch) {
887
+ try {
888
+ let gqlStr = gql`
889
+ mutation {
890
+ ${mutationFunc}(id: "${id}", ${gqlBuilder.buildArgs({ patch })}) {
891
+ id
892
+ }
893
+ }`
894
+
895
+ const response = await client.query({
896
+ query: gqlStr,
897
+ context: {
898
+ hasUpload: true
899
+ }
900
+ })
901
+
902
+ if (!response.errors) {
903
+ UiUtil.showToast('info', TermsUtil.tText('success to save'))
904
+ return true
905
+ } else {
906
+ ServiceUtil.showGraphqlErrorResponse(response)
907
+ }
908
+ } catch (e) {
909
+ ServiceUtil.showGraphqlException(e)
910
+ }
911
+
912
+ return false
913
+ }
914
+
915
+ /**
916
+ * @description Graphql 서비스 호출
917
+ ***********************************
918
+ * @param {Object} pageView 호출 페이지 뷰
919
+ * @param {Object} action 서비스 설정 정보
920
+ * @param {Object} filterParam 필터용 파라미터
921
+ * @param {Object} dataParams 파라미터
922
+ * @returns {Object} 서비스 호출 결과
923
+ */
924
+ static async callService(pageView, action, filterParam, dataParams) {
925
+ if (action.graphql_type == 'query') {
926
+ return await ServiceUtil.callQueryService(action, filterParam, dataParams)
927
+ } else if (action.graphql_type == 'export') {
928
+ return await ServiceUtil.exportableDataByServer(action.export_file_name, action.query_name, filterParam)
929
+ } else {
930
+ return await ServiceUtil.callMutationService(action, filterParam, dataParams)
931
+ }
932
+ }
933
+
934
+ /**
935
+ * @description Graphql Query 서비스 호출
936
+ ***************************************
937
+ * @param {Object} action 서비스 액션
938
+ * @param {Object} filterParam 필터용 파라미터
939
+ * @param {Object} dataParams 데이터 파라미터
940
+ * @returns {Object} 서비스 호출 결과
941
+ */
942
+ static async callQueryService(action, filterParam, dataParams) {
943
+ // 1. GraphQL 설정 추출
944
+ let queryType = action.query_type
945
+ let queryFunc = action.query_name
946
+ let selectFields = action.select
947
+ let filters = action.filters
948
+ let sorters = action.sorters
949
+
950
+ // 2. Filter 정보가 존재한다면 filterParam 정보로 부터 값 변수 치환
951
+ if (filters && filters.length > 0 && filterParam) {
952
+ let filterParamKeys = Object.keys(filterParam)
953
+
954
+ filters.forEach(filter => {
955
+ let value = filter.value
956
+ if (typeof value == 'string' && value.startsWith('$')) {
957
+ let valueField = value.replace('$', '')
958
+ if (filterParamKeys.includes(valueField)) {
959
+ filter.value = filterParam[valueField]
960
+ }
961
+ }
962
+ })
963
+ }
964
+
965
+ // 3. dataParams 정보가 존재한다면 데이터 dataParams 정보로 부터 값 변수 치환
966
+ if (filters && filters.length > 0 && dataParams) {
967
+ let dataParam = Array.isArray(dataParams) ? dataParams[0] : dataParams
968
+ let dataParamKeys = Object.keys(dataParam)
969
+
970
+ filters.forEach(filter => {
971
+ let value = filter.value
972
+ if (typeof value == 'string' && value.startsWith('$')) {
973
+ let valueField = value.replace('$', '')
974
+ if (dataParamKeys.includes(valueField)) {
975
+ filter.value = dataParam[valueField]
976
+ }
977
+ }
978
+ })
979
+ }
980
+
981
+ // 4. ID로 하나의 데이터만 조회
982
+ if (queryType == 'singleById') {
983
+ // ID값 추출
984
+ let idField = action.id_field
985
+ let idValue = filterParam[idField]
986
+ if (!idValue) {
987
+ idValue = Array.isArray(dataParams) ? dataParams[0][idField] : dataParams[idField]
988
+ }
989
+
990
+ if (!idValue) {
991
+ throw new Error('id_value_not_found')
992
+ }
993
+
994
+ // ID값이 있다면 ID 값으로 조회
995
+ return await ServiceUtil.findOne(queryFunc, idValue, selectFields)
996
+
997
+ // 5. filter 정보로 하나의 데이터만 조회
998
+ } else if (queryType == 'singleByFilters') {
999
+ return await ServiceUtil.findByFilters(queryFunc, selectFields, filters)
1000
+
1001
+ // 6. 화면이 grid이면서 페이지네이션을 사용하지 않는 경우
1002
+ } else if (queryType == 'multi') {
1003
+ return await ServiceUtil.searchByFilters(queryFunc, selectFields, filters)
1004
+
1005
+ // 7. 화면이 grid이면서 페이지네이션을 사용하는 경우
1006
+ } else if (queryType == 'pagination') {
1007
+ let page = 1
1008
+ let limit = action.limit
1009
+ return await ServiceUtil.searchByPagination(queryFunc, filters, sorters, page, limit, selectFields)
1010
+ } else {
1011
+ throw new Error('invalid queryType [' + queryType + ']')
1012
+ }
1013
+ }
1014
+
1015
+ /**
1016
+ * @description Graphql Mutation 서비스 호출
1017
+ ******************************************
1018
+ * @param {Object} action 서비스 액션
1019
+ * @param {Object} filterParam 필터용 파라미터
1020
+ * @param {Object} dataParams 데이터 파라미터
1021
+ * @returns {Object} 서비스 호출 결과
1022
+ */
1023
+ static async callMutationService(action, filterParam, dataParams) {
1024
+ // 1. 필수 정보 체크
1025
+ if (!action.mutation_name) {
1026
+ throw new Error('mutation name not found!')
1027
+ }
1028
+
1029
+ if (!action.mutation_type) {
1030
+ throw new Error('mutation type not found!')
1031
+ }
1032
+
1033
+ // 2. GraphQL 설정 추출
1034
+ let data = action.data
1035
+ let mutationParams = Array.isArray(dataParams) ? [] : {}
1036
+
1037
+ if (data) {
1038
+ // 3. 파라미터 정보로 부터 변수 치환 처리
1039
+ if (Array.isArray(mutationParams)) {
1040
+ if (ValueUtil.isNotEmpty(dataParams)) {
1041
+ dataParams.forEach(dp => {
1042
+ let sourceData = JSON.parse(JSON.stringify(data))
1043
+ let dataParam = ValueUtil.replaceVariableObject(sourceData, dp)
1044
+ mutationParams.push(dataParam)
1045
+ })
1046
+ }
1047
+ } else {
1048
+ if (ValueUtil.isNotEmpty(dataParams)) {
1049
+ mutationParams = ValueUtil.replaceVariableObject(data, dataParams)
1050
+ }
1051
+ }
1052
+
1053
+ // 4. 파라미터 정보가 없다면 필터 정보로 변수 처리
1054
+ if (ValueUtil.isEmpty(mutationParams)) {
1055
+ mutationParams = ValueUtil.replaceVariableObject(data, filterParam)
1056
+ }
1057
+ }
1058
+
1059
+ try {
1060
+ // 5. ID 값 추출
1061
+ let idValue = action.id_field
1062
+ ? ValueUtil.isNotEmpty(filterParam)
1063
+ ? filterParam[action.id_field]
1064
+ : Array.isArray(dataParams)
1065
+ ? dataParams[0][action.id_field]
1066
+ : dataParams[action.id_field]
1067
+ : null
1068
+ // 6. Mutation 호출
1069
+ let response = await ServiceUtil.callMutation(
1070
+ action,
1071
+ idValue,
1072
+ ValueUtil.isEmpty(mutationParams) ? dataParams : mutationParams
1073
+ )
1074
+
1075
+ if (!response.errors) {
1076
+ UiUtil.showToast('info', TermsUtil.tText('success to process'))
1077
+ return response
1078
+ } else {
1079
+ ServiceUtil.showGraphqlErrorResponse(response)
1080
+ }
1081
+ } catch (e) {
1082
+ ServiceUtil.showGraphqlException(e)
1083
+ }
1084
+ }
1085
+
1086
+ /**
1087
+ * @description Graphql Mutation 서비스 호출
1088
+ ******************************************
1089
+ * @param {Object} action 서비스 액션
1090
+ * @param {String} idValue ID 값
1091
+ * @param {Object} data 파라미터
1092
+ * @returns {Object} 서비스 호출 결과
1093
+ */
1094
+ static async callMutation(action, idValue, data) {
1095
+ // mutation Type 추출
1096
+ let mutationType = action.mutation_type
1097
+
1098
+ // 1. 파라미터가 id 뿐인 경우
1099
+ if (mutationType == 'id') {
1100
+ return await ServiceUtil.callMutationById(action, idValue)
1101
+
1102
+ // 2. 파라미터가 id 배열인 경우
1103
+ } else if (mutationType == 'ids') {
1104
+ return await ServiceUtil.callMutationByIds(action, data)
1105
+
1106
+ // 3. 파라미터가 id, data인 경우
1107
+ } else if (mutationType == 'id-data') {
1108
+ return await ServiceUtil.callMutationByIdData(action, idValue, data)
1109
+
1110
+ // 4. 파라미터가 data인 경우
1111
+ } else if (mutationType == 'data') {
1112
+ return await ServiceUtil.callMutationByData(action, data)
1113
+ }
1114
+ }
1115
+
1116
+ /**
1117
+ * @description Graphql Mutation 서비스 호출 - 파라미터는 ID
1118
+ *******************************************************
1119
+ * @param {Object} action 서비스 액션
1120
+ * @param {Object} idValue ID 값
1121
+ * @returns {Object} 서비스 호출 결과
1122
+ */
1123
+ static async callMutationById(action, idValue) {
1124
+ // ID 값이 없으면 에러
1125
+ if (!idValue) {
1126
+ throw new Error('id_value_not_found')
1127
+ }
1128
+
1129
+ let mutationName = action.mutation_name
1130
+ let returnFields = action.return_fields
1131
+
1132
+ return await client.query({
1133
+ query: gql`
1134
+ mutation ($id: String!) {
1135
+ ${mutationName}(id: $id)
1136
+ ${returnFields ? '{' + returnFields + '}' : ''}
1137
+ }`,
1138
+ variables: { id: idValue }
1139
+ })
1140
+ }
1141
+
1142
+ /**
1143
+ * @description Graphql Mutation 서비스 호출 - 파라미터는 ID
1144
+ *******************************************************
1145
+ * @param {Object} action 서비스 액션
1146
+ * @param {Array} dataList 데이터 배열
1147
+ * @returns {Object} 서비스 호출 결과
1148
+ */
1149
+ static async callMutationByIds(action, dataList) {
1150
+ let paramName = action.param_name
1151
+
1152
+ // 파라미터 이름이 없으면 에러
1153
+ if (!paramName) {
1154
+ throw new Error('param name not found')
1155
+ }
1156
+
1157
+ let ids = dataList.map(d => {
1158
+ return d[action.id_field]
1159
+ })
1160
+ let mutationName = action.mutation_name
1161
+ let returnFields = action.return_fields
1162
+
1163
+ return await client.query({
1164
+ query: gql`
1165
+ mutation ($ids: [String!]!) {
1166
+ ${mutationName}(${paramName}: $ids)
1167
+ ${returnFields ? '{' + returnFields + '}' : ''}
1168
+ }`,
1169
+ variables: { ids: ids }
1170
+ })
1171
+ }
1172
+
1173
+ /**
1174
+ * @description Graphql Mutation 서비스 호출 - 파라미터는 ID, Data
1175
+ *************************************************************
1176
+ * @param {Object} action 서비스 액션
1177
+ * @param {String} idValue ID 값
1178
+ * @param {Object} data 파라미터
1179
+ * @returns {Object} 서비스 호출 결과
1180
+ */
1181
+ static async callMutationByIdData(action, idValue, data) {
1182
+ let paramName = action.param_name
1183
+ let paramType = action.param_type
1184
+
1185
+ // 파라미터 타입이 없으면 에러
1186
+ if (!paramType) {
1187
+ throw new Error('param type not found')
1188
+ }
1189
+
1190
+ // 파라미터 이름이 없으면 에러
1191
+ if (!paramName) {
1192
+ throw new Error('param name not found')
1193
+ }
1194
+
1195
+ // ID 값이 있다면 ID 값으로 조회
1196
+ if (!idValue) {
1197
+ idValue = data[action.id_field]
1198
+ }
1199
+
1200
+ if (!idValue) {
1201
+ throw new Error('id_value_not_found')
1202
+ }
1203
+
1204
+ let mutationName = action.mutation_name
1205
+ let returnFields = action.return_fields
1206
+
1207
+ return await client.query({
1208
+ query: gql`
1209
+ mutation ($id: String!, $patch: ${paramType}!) {
1210
+ ${mutationName}(id: $id, ${paramName}: $patch)
1211
+ ${returnFields ? '{' + returnFields + '}' : ''}
1212
+ }`,
1213
+ variables: { id: idValue, patch: data }
1214
+ })
1215
+ }
1216
+
1217
+ /**
1218
+ * @description Graphql Mutation 서비스 호출 - 파라미터는 Data
1219
+ ********************************************************
1220
+ * @param {Object} action 서비스 액션
1221
+ * @param {Object} data 파라미터
1222
+ * @returns {Object} 서비스 호출 결과
1223
+ */
1224
+ static async callMutationByData(action, data) {
1225
+ let paramType = action.param_type
1226
+ let paramName = action.param_name
1227
+
1228
+ // 파라미터 타입이 없으면 에러
1229
+ if (!paramType) {
1230
+ throw new Error('param type not found')
1231
+ }
1232
+
1233
+ // 파라미터 이름이 없으면 에러
1234
+ if (!paramName) {
1235
+ throw new Error('param name not found')
1236
+ }
1237
+
1238
+ let mutationName = action.mutation_name
1239
+ let returnFields = action.return_fields
1240
+
1241
+ return await client.query({
1242
+ query: gql`
1243
+ mutation ($patch: ${paramType}!) {
1244
+ ${mutationName}(${paramName}: $patch)
1245
+ ${returnFields ? '{' + returnFields + '}' : ''}
1246
+ }`,
1247
+ variables: { patch: data }
1248
+ })
1249
+ }
1250
+
1251
+ /**
1252
+ * @description 시나리오 서비스를 호출
1253
+ **********************************
1254
+ * @param {String} buttonName 시나리오 호출할 버튼 명, 버튼 명이 없다면 사용자에게 확인 과정을 생략한다.
1255
+ * @param {String} scenarioName 호출할 시나리오 명
1256
+ * @param {Object} variables 시나리오 호출 변수
1257
+ * @param {Object} useMessage 컨펌, 완료 메시지 사용 여부
1258
+ * @returns {Object | Boolean} 시나리오 호출 response or false
1259
+ */
1260
+ static async callScenario(buttonName, scenarioName, variables, useMessage = true) {
1261
+ let confirm = true
1262
+
1263
+ if (buttonName && useMessage === true) {
1264
+ const anwer = await UiUtil.showAlertPopup(
1265
+ 'button.' + buttonName,
1266
+ 'text.are_you_sure',
1267
+ 'question',
1268
+ 'confirm',
1269
+ 'cancel'
1270
+ )
1271
+ confirm = anwer.value
1272
+ }
1273
+
1274
+ if (confirm) {
1275
+ try {
1276
+ const response = await client.query({
1277
+ query: gql`
1278
+ mutation ($scenarioName: String!, $variables: Object) {
1279
+ runScenario(scenarioName: $scenarioName, variables: $variables) {
1280
+ state
1281
+ message
1282
+ result
1283
+ __typename
1284
+ }
1285
+ }
1286
+ `,
1287
+ variables: { scenarioName: scenarioName, variables: variables }
1288
+ })
1289
+
1290
+ if (!response.errors) {
1291
+ if (ServiceUtil.checkClientError(response)) {
1292
+ if (useMessage === true) UiUtil.showToast('info', TermsUtil.tText('success to process'))
1293
+ return response
1294
+ }
1295
+ } else {
1296
+ ServiceUtil.showGraphqlErrorResponse(response)
1297
+ }
1298
+ } catch (e) {
1299
+ ServiceUtil.showGraphqlException(e)
1300
+ }
1301
+ }
1302
+
1303
+ return false
1304
+ }
1305
+
1306
+ /**
1307
+ * @description 시나리오 서비스 리턴값에 에러가 포함되어 있는지 체크
1308
+ *******************************************************
1309
+ * @param {Object} response 시나리오 서비스 호출 리턴 값, 리턴 형태 error : {type : 'error', title : '에러 타이틀 (다국어 가능)', message : '에러 메시지 (다국어 가능)'}
1310
+ * @returns {Boolean} 에러가 없으면 true 그렇지 않으면 false
1311
+ */
1312
+ static checkClientError(response) {
1313
+ if (
1314
+ response &&
1315
+ response.data &&
1316
+ response.data.runScenario &&
1317
+ response.data.runScenario.result &&
1318
+ response.data.runScenario.result.error
1319
+ ) {
1320
+ let scenarioError = response.data.runScenario.result.error
1321
+ UiUtil.showAlertPopup(scenarioError.title, scenarioError.message, 'error', 'confirm')
1322
+ return false
1323
+ } else {
1324
+ return true
1325
+ }
1326
+ }
1327
+ }