@things-factory/meta-ui 8.0.0-beta.9 → 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,435 @@
1
+ import { closePopup } from '@operato/popup'
2
+
3
+ import { MetaApi } from '../utils/meta-api'
4
+ import { MetaUiUtil } from '../utils/meta-ui-util'
5
+ import { ValueUtil } from '../utils/value-util'
6
+ import { UiUtil } from '../utils/ui-util'
7
+ import { TermsUtil } from '../utils/terms-util'
8
+
9
+ import { MetaButtonMixin } from './meta-button-mixin'
10
+
11
+ /**
12
+ * @license
13
+ * Copyright © HatioLab Inc. All rights reserved.
14
+ * @author Shortstop shortstop@hatiolab.com
15
+ * @description 메뉴 메타 정보를 이용해 폼 화면 구성
16
+ */
17
+ export const MetaFormMixin = baseElement =>
18
+ class extends MetaButtonMixin(baseElement) {
19
+ /**
20
+ * @description 스타일 정의
21
+ **************************
22
+ * @returns {Array} 스타일
23
+ */
24
+ static get styles() {
25
+ return MetaApi.getBasicFormStyles()
26
+ }
27
+
28
+ /**
29
+ * @description 프로퍼티 정의
30
+ ***************************
31
+ * @returns {Object} 프로퍼티
32
+ */
33
+ static get properties() {
34
+ return {
35
+ /**
36
+ * @description 폽 컬럼 구성 정보
37
+ ******************************
38
+ * @type {Array}
39
+ */
40
+ formColumnConfig: Array,
41
+ /**
42
+ * @description 메뉴 메타 정보로 부터 추출한 후 폼을 위해 설정 정보 구성
43
+ ************************************************************
44
+ * @type {Object}
45
+ */
46
+ formConfigSet: Object,
47
+ /**
48
+ * @description 폼 상세 렌터링 정보
49
+ *******************************
50
+ * @type {Object}
51
+ */
52
+ formRenderConfig: Array,
53
+ /**
54
+ * @description 데이터 레코드
55
+ ***************************
56
+ * @type {Object}
57
+ */
58
+ record: Object
59
+ }
60
+ }
61
+
62
+ /**
63
+ * @description 컨텍스트
64
+ ***********************
65
+ * @returns {HTMLElement}
66
+ */
67
+ get context() {
68
+ return MetaUiUtil.getContextObject(this)
69
+ }
70
+
71
+ /******************************************************
72
+ * LifeCycle
73
+ ******************************************************/
74
+
75
+ /**
76
+ * @override connectedCallback
77
+ *******************************
78
+ */
79
+ async connectedCallback() {
80
+ // 메뉴 메타 정보 조회 및 기본 파싱
81
+ await this.getAndParseMenuMeta()
82
+
83
+ if (this.isElement) {
84
+ await this.parseFormConfigs()
85
+ }
86
+
87
+ if (this.activityDataSet) {
88
+ this.dataSet = this.activityDataSet
89
+ }
90
+
91
+ if (this.is_activity === true && !this.parent_id) {
92
+ this.parent_id = 'activity'
93
+ }
94
+
95
+ if (super.connectedCallback) {
96
+ await super.connectedCallback()
97
+ }
98
+
99
+ if (this.isElement) {
100
+ await this.fetch()
101
+ }
102
+
103
+ // 인풋 키 이벤트 처리
104
+ this.renderRoot.addEventListener('keydown', e => {
105
+ switch (e.key) {
106
+ case 'Esc':
107
+ case 'Escape':
108
+ // TODO 편집이 취소되어야 한다.
109
+ case 'Enter':
110
+ // 먼저 focus를 옮겨놓아야 focusout으로 인해서 popup이 닫히는 것을 방지할 수 있다.
111
+ this.focus()
112
+
113
+ if (this.currentTarget) {
114
+ this.currentTarget.removeAttribute('editing')
115
+ }
116
+
117
+ this.currentTarget = null
118
+ break
119
+ default:
120
+ }
121
+ })
122
+
123
+ // 인풋 클릭 처리 (readOnly <> editing)
124
+ this.renderRoot.addEventListener('click', e => {
125
+ e.stopPropagation()
126
+ // target should be 'ox-grid-field'
127
+ let target = e.target
128
+
129
+ if (this.currentTarget) {
130
+ this.focus()
131
+ this.currentTarget.removeAttribute('editing')
132
+ }
133
+
134
+ if (target.tagName !== 'OX-GRID-FIELD' || !target.column.record.editable) {
135
+ this.focus()
136
+ this.currentTarget = null
137
+ return
138
+ }
139
+
140
+ this.currentTarget = target
141
+ target.setAttribute('editing', 'true')
142
+ })
143
+
144
+ // 폼 뷰의 인풋에서 발생되는 변경 이벤트 처리
145
+ this.addEventListener('field-change', e => {
146
+ let { after, before, column, record, row } = e.detail
147
+
148
+ // before, after 값으로 달라진 값이 없으면 스킵
149
+ if (ValueUtil.isEquals(after, before)) {
150
+ return
151
+ }
152
+
153
+ // 유효성 체크 함수가 있는 경우 해당 함수 호출
154
+ let validation = column.validation
155
+ if (validation && typeof validation == 'function') {
156
+ if (!validation.call(this, after, before, record, column)) {
157
+ return
158
+ }
159
+ }
160
+
161
+ // 변경 값 셋팅
162
+ let colName = column.name
163
+ record[colName] = after
164
+
165
+ // 변경 필드 정보
166
+ record.__dirtyfields__ = record.__dirtyfields__ || {}
167
+ record.__origin__ = record.__origin__ || {}
168
+ record.__dirtyfields__[colName] = {
169
+ before: record.__origin__[colName],
170
+ after: record[colName]
171
+ }
172
+
173
+ // 같은 값으로 변경 되었다면
174
+ if (ValueUtil.isEquals(record.__dirtyfields__[colName].before, record.__dirtyfields__[colName].after)) {
175
+ delete record.__dirtyfields__[colName]
176
+ }
177
+
178
+ // dirty flag 설정
179
+ record.__dirty__ = ValueUtil.isNotEmpty(record.__dirtyfields__) ? 'M' : ''
180
+
181
+ this.record = { ...record }
182
+
183
+ // form-field-change 변경 이벤트 전파
184
+ this.dispatchEvent(
185
+ new CustomEvent('form-field-change', {
186
+ bubbles: true,
187
+ composed: true,
188
+ detail: e.detail
189
+ })
190
+ )
191
+ })
192
+ }
193
+
194
+ /**
195
+ * @override firstUpdated
196
+ ***************************
197
+ */
198
+ async firstUpdated() {
199
+ if (super.firstUpdated) {
200
+ await super.firstUpdated()
201
+ }
202
+ }
203
+
204
+ /**
205
+ * @override pageInitialized
206
+ *****************************
207
+ */
208
+ async pageInitialized() {
209
+ if (this.isPage) {
210
+ await this.parseFormConfigs()
211
+ }
212
+
213
+ if (super.pageInitialized) {
214
+ await super.pageInitialized()
215
+ }
216
+
217
+ if (this.isPage) {
218
+ await this.fetch()
219
+ }
220
+ }
221
+
222
+ /**
223
+ * @description 화면 그리기
224
+ *************************
225
+ * @returns {HTMLElement}
226
+ */
227
+ render() {
228
+ return MetaApi.getBasicFormHtml(this)
229
+ }
230
+
231
+ /**
232
+ * @descrtiption 메뉴 메타 정보로 부터 기본 폼 정보 구성을 위한 Configuration
233
+ *******************************************************************
234
+ * @param {Object} menuMeta 메뉴 메타 정보
235
+ */
236
+ parseBasicFormConfigs(menuMeta) {
237
+ this.formColumnConfig = menuMeta.grid_column
238
+ this.formRenderConfig = menuMeta.form
239
+ }
240
+
241
+ /**
242
+ * @descrtiption 폼 구성 정보 Configuration
243
+ *****************************************
244
+ */
245
+ async parseFormConfigs() {
246
+ this.formConfigSet = await MetaApi.parseFormConfigSet(this)
247
+ }
248
+
249
+ /********************************************************************
250
+ * Data Filtering
251
+ ********************************************************************/
252
+
253
+ /**
254
+ * @description 최초 조회된 원본 데이터
255
+ ***********************************
256
+ * @returns {Object} 원본 데이터 (변경 값 무시)
257
+ */
258
+ get orgData() {
259
+ return this.removeGarbageData(this.record.__origin__)
260
+ }
261
+
262
+ /**
263
+ * @description 변경된 데이터
264
+ **************************
265
+ * @returns {Object} 변경된 데이터만
266
+ */
267
+ get dirtyData() {
268
+ if (ValueUtil.isEmpty((this.record || {}).__dirtyfields__)) {
269
+ return {}
270
+ }
271
+
272
+ let retData = {}
273
+
274
+ Object.entries(this.record.__dirtyfields__).map(([key, value]) => {
275
+ if (key.startsWith('__') == false) {
276
+ retData[key] = value.after
277
+ }
278
+ })
279
+
280
+ return retData
281
+ }
282
+
283
+ /**
284
+ * @description 현재 데이터
285
+ *************************
286
+ * @returns {Object} 화면에 표현되어 있는 현재 데이터 (수정된 값 반영)
287
+ */
288
+ get currentData() {
289
+ return this.removeGarbageData(this.record)
290
+ }
291
+
292
+ /**
293
+ * @description 레코드 데이터에서 쓰레기 데이터 정리
294
+ ********************************************
295
+ * @returns {Object} __dirty__,__dirtyfields__,__origin__ 등 (__ 로 시작되는 데이터 삭제)
296
+ */
297
+ removeGarbageData(data) {
298
+ if (!data) return undefined
299
+
300
+ let retData = {}
301
+
302
+ Object.entries(data).map(([key, value]) => {
303
+ if (key.startsWith('__') == false) {
304
+ retData[key] = value
305
+ }
306
+ })
307
+
308
+ return retData
309
+ }
310
+
311
+ /**
312
+ * @description 트랜잭션을 위한 변경 데이터 + cuFlag 가져오기
313
+ *****************************************************
314
+ * @returns {Object}
315
+ */
316
+ get patchData() {
317
+ let dirtyData = this.dirtyData
318
+ let recordData = this.record
319
+ // 코드 인풋 타입
320
+ let codeInputColumns = (this.formColumnConfig || []).filter(x => x.type === 'code-input').map(x => x.name)
321
+
322
+ if (ValueUtil.isEmpty(dirtyData)) {
323
+ return undefined
324
+ }
325
+
326
+ // 기존 데이터 존재 여부 - 변경 플래그 판단
327
+ if (ValueUtil.isNotEmpty(recordData.id)) {
328
+ dirtyData.id = recordData.id
329
+ dirtyData.cuFlag = 'M'
330
+ } else {
331
+ dirtyData.cuFlag = '+'
332
+ }
333
+
334
+ // object 타입은 id만 전송되도록 변경
335
+ Object.keys(dirtyData).forEach(key => {
336
+ if (typeof dirtyData[key] === 'object' && codeInputColumns.includes(key) == false) {
337
+ dirtyData[key] = { id: dirtyData[key].id }
338
+ }
339
+ })
340
+
341
+ return [dirtyData]
342
+ }
343
+
344
+ /********************************************************************
345
+ * C R U D Functions
346
+ ********************************************************************/
347
+ /**
348
+ * @description 폼 데이터 조회
349
+ ***************************
350
+ */
351
+ async fetch() {
352
+ // 부모 ID가 없다면 리턴
353
+ if (ValueUtil.isEmpty(this.parent_id)) return
354
+
355
+ // 단건 조회 함수
356
+ let findOneFunc = ValueUtil.getParams(this.gqlInfo, 'query', 'find_one_func')
357
+ if (!findOneFunc) return
358
+
359
+ // 부모 ID로 조회
360
+ let data = await this.findOne(this.parent_id)
361
+
362
+ // 데이터 형식 맞추기
363
+ data['__seq__'] = 1
364
+ let orgData = {}
365
+ Object.assign(orgData, data)
366
+ data['__origin__'] = orgData
367
+ this.record = { ...data }
368
+ }
369
+
370
+ /**
371
+ * @description 폼 데이터 저장
372
+ *****************************
373
+ * @returns {Boolean} 저장 여부
374
+ */
375
+ async save() {
376
+ let patches = this.patchData
377
+
378
+ // 변경 여부 메시지 처리
379
+ if (ValueUtil.isEmpty(patches)) {
380
+ MetaApi.showAlertPopup('title.info', 'text.NOTHING_CHANGED', 'warning', 'confirm')
381
+ return
382
+ }
383
+
384
+ // 필수 입력 컬럼
385
+ let mandatoryColumns = {}
386
+ ;(this.formColumnConfig || [])
387
+ .filter(x => x.mandatory === true)
388
+ .forEach(x => {
389
+ mandatoryColumns[x.name] = x.header
390
+ })
391
+
392
+ // 필수 입력 값 체크
393
+ let allFields = this.currentData
394
+ for (let key in mandatoryColumns) {
395
+ if (allFields[key] === undefined || allFields[key] == '') {
396
+ UiUtil.showAlertPopup('text.check-mandatory', TermsUtil.tText('check-mandatory-field', { x: TermsUtil.tLabel(mandatoryColumns[key]) }), 'info', 'confirm')
397
+ return
398
+ }
399
+ }
400
+
401
+ // 저장 서비스 호출
402
+ let isRes = await this.updateMultiple(patches)
403
+ if (isRes) {
404
+ // 폼 데이터 조회
405
+ await this.fetch()
406
+
407
+ // 저장이 잘 되었고 팝업이라면 팝업 닫기
408
+ if (this.isPopup) {
409
+ closePopup(this)
410
+ }
411
+ }
412
+
413
+ // 저장 결과 리턴
414
+ return isRes
415
+ }
416
+
417
+ /**
418
+ * @description 데이터 클리어
419
+ ***************************
420
+ */
421
+ async clear() {
422
+ if (this.record) {
423
+ this.record = {}
424
+ }
425
+ }
426
+
427
+ /**
428
+ * @description 데이터 리턴
429
+ *************************
430
+ * @returns {Object}
431
+ */
432
+ getData() {
433
+ return this.currentData || {}
434
+ }
435
+ }