@things-factory/organization 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 (94) hide show
  1. package/client/bootstrap.ts +23 -0
  2. package/client/component/approval-line-brief.ts +119 -0
  3. package/client/component/approval-line-items-editor-popup.ts +91 -0
  4. package/client/component/approval-line-items-editor.ts +325 -0
  5. package/client/component/approval-line-selector.ts +235 -0
  6. package/client/component/approval-line-templates-manager.ts +229 -0
  7. package/client/component/approval-line-view.ts +122 -0
  8. package/client/component/assignees-editor-popup.ts +79 -0
  9. package/client/component/assignees-editor.ts +217 -0
  10. package/client/component/assignees-view.ts +55 -0
  11. package/client/component/department-selector.ts +151 -0
  12. package/client/component/department-view.ts +107 -0
  13. package/client/component/index.ts +16 -0
  14. package/client/component/recipients-editor-popup.ts +79 -0
  15. package/client/component/recipients-editor.ts +212 -0
  16. package/client/component/recipients-view.ts +55 -0
  17. package/client/grist-editor/grist-editor-approval-line.ts +70 -0
  18. package/client/grist-editor/grist-editor-assignees.ts +69 -0
  19. package/client/grist-editor/grist-editor-department-object.ts +78 -0
  20. package/client/grist-editor/grist-editor-recipients.ts +69 -0
  21. package/client/grist-editor/grist-renderer-approval-line.ts +13 -0
  22. package/client/grist-editor/grist-renderer-assignees.ts +13 -0
  23. package/client/grist-editor/grist-renderer-department-object.ts +13 -0
  24. package/client/grist-editor/grist-renderer-recipients.ts +13 -0
  25. package/client/index.ts +2 -0
  26. package/client/pages/approval-line/common-approval-line-templates-page.ts +382 -0
  27. package/client/pages/approval-line/my-approval-line-templates-page.ts +385 -0
  28. package/client/pages/department/department-importer.ts +87 -0
  29. package/client/pages/department/department-list-page.ts +450 -0
  30. package/client/pages/department/department-tree-page.ts +379 -0
  31. package/client/pages/employee/employee-importer.ts +87 -0
  32. package/client/pages/employee/employee-list-page.ts +772 -0
  33. package/client/pages/employee/employees-by-department.ts +519 -0
  34. package/client/route.ts +27 -0
  35. package/client/tsconfig.json +13 -0
  36. package/client/types/approval-line.ts +52 -0
  37. package/client/types/contact.ts +51 -0
  38. package/client/types/department.ts +29 -0
  39. package/client/types/employee.ts +50 -0
  40. package/client/types/index.ts +5 -0
  41. package/client/types/org-member.ts +27 -0
  42. package/dist-client/bootstrap.js +1 -8
  43. package/dist-client/bootstrap.js.map +1 -1
  44. package/dist-client/pages/employee/employee-list-page.js +3 -3
  45. package/dist-client/pages/employee/employee-list-page.js.map +1 -1
  46. package/dist-client/pages/employee/employees-by-department.js +2 -2
  47. package/dist-client/pages/employee/employees-by-department.js.map +1 -1
  48. package/dist-client/tsconfig.tsbuildinfo +1 -1
  49. package/dist-server/service/employee/employee-history.d.ts +2 -6
  50. package/dist-server/service/employee/employee-history.js +3 -23
  51. package/dist-server/service/employee/employee-history.js.map +1 -1
  52. package/dist-server/service/employee/employee-query.js +1 -1
  53. package/dist-server/service/employee/employee-query.js.map +1 -1
  54. package/dist-server/service/employee/employee-type.d.ts +5 -13
  55. package/dist-server/service/employee/employee-type.js +7 -39
  56. package/dist-server/service/employee/employee-type.js.map +1 -1
  57. package/dist-server/service/employee/employee.d.ts +2 -6
  58. package/dist-server/service/employee/employee.js +3 -23
  59. package/dist-server/service/employee/employee.js.map +1 -1
  60. package/dist-server/tsconfig.tsbuildinfo +1 -1
  61. package/package.json +12 -12
  62. package/server/controllers/register-employee-as-system-user.ts +136 -0
  63. package/server/index.ts +3 -0
  64. package/server/migrations/1723861013111-seed-organization-codes.ts +127 -0
  65. package/server/migrations/index.ts +9 -0
  66. package/server/routes.ts +26 -0
  67. package/server/service/approval-line/approval-line-item.ts +42 -0
  68. package/server/service/approval-line/approval-line-mutation.ts +394 -0
  69. package/server/service/approval-line/approval-line-query.ts +208 -0
  70. package/server/service/approval-line/approval-line-type.ts +63 -0
  71. package/server/service/approval-line/approval-line.ts +123 -0
  72. package/server/service/approval-line/index.ts +7 -0
  73. package/server/service/department/department-history.ts +141 -0
  74. package/server/service/department/department-mutation.ts +231 -0
  75. package/server/service/department/department-query.ts +131 -0
  76. package/server/service/department/department-type.ts +74 -0
  77. package/server/service/department/department.ts +116 -0
  78. package/server/service/department/event-subscriber.ts +17 -0
  79. package/server/service/department/index.ts +9 -0
  80. package/server/service/employee/employee-history.ts +173 -0
  81. package/server/service/employee/employee-mutation.ts +386 -0
  82. package/server/service/employee/employee-query.ts +172 -0
  83. package/server/service/employee/employee-type.ts +176 -0
  84. package/server/service/employee/employee.ts +177 -0
  85. package/server/service/employee/event-subscriber.ts +17 -0
  86. package/server/service/employee/index.ts +9 -0
  87. package/server/service/index.ts +39 -0
  88. package/server/tsconfig.json +10 -0
  89. package/dist-client/filters-form/filter-department-object.d.ts +0 -3
  90. package/dist-client/filters-form/filter-department-object.js +0 -8
  91. package/dist-client/filters-form/filter-department-object.js.map +0 -1
  92. package/dist-client/filters-form/ox-filter-department-object.d.ts +0 -15
  93. package/dist-client/filters-form/ox-filter-department-object.js +0 -130
  94. package/dist-client/filters-form/ox-filter-department-object.js.map +0 -1
@@ -0,0 +1,235 @@
1
+ import '@material/web/icon/icon.js'
2
+ import '@operato/data-grist'
3
+ import './approval-line-view'
4
+
5
+ import gql from 'graphql-tag'
6
+ import { css, html, LitElement } from 'lit'
7
+ import { customElement, property, query } from 'lit/decorators.js'
8
+
9
+ import {
10
+ getRenderer,
11
+ DataGrist,
12
+ FetchOption,
13
+ GristEventHandler,
14
+ GristData,
15
+ GristRecord,
16
+ ZERO_DATA
17
+ } from '@operato/data-grist'
18
+ import { client } from '@operato/graphql'
19
+ import { i18next } from '@operato/i18n'
20
+ import { closePopup } from '@operato/popup'
21
+ import { isMobileDevice } from '@operato/utils'
22
+ import { CommonHeaderStyles } from '@operato/styles'
23
+
24
+ import { ApprovalLineView } from './approval-line-view'
25
+
26
+ @customElement('approval-line-selector')
27
+ export class ApprovalLineSelector extends LitElement {
28
+ static styles = [
29
+ CommonHeaderStyles,
30
+ css`
31
+ :host {
32
+ display: flex;
33
+ flex-direction: column;
34
+
35
+ background-color: var(--md-sys-color-surface);
36
+
37
+ width: var(--overlay-center-normal-width, 50%);
38
+ height: var(--overlay-center-normal-height, 50%);
39
+ }
40
+
41
+ approval-line-view {
42
+ min-height: 100px;
43
+ }
44
+
45
+ ox-grist {
46
+ flex: 1;
47
+ }
48
+ `
49
+ ]
50
+
51
+ @property({ type: String }) value?: string
52
+ @property({ type: Object }) config: any
53
+ @property({ type: Object }) data: GristData = ZERO_DATA
54
+ @property({ type: Object }) confirmCallback?: (record?: GristRecord) => Promise<void>
55
+ @property({ type: Array }) selectedRecords: GristRecord[] = []
56
+
57
+ @query('ox-grist') grist!: DataGrist
58
+ @query('approval-line-view') view?: ApprovalLineView
59
+
60
+ render() {
61
+ return html`
62
+ <approval-line-view></approval-line-view>
63
+ <ox-grist
64
+ .mode=${isMobileDevice() ? 'LIST' : 'GRID'}
65
+ .config=${this.config}
66
+ .data=${this.data}
67
+ .fetchHandler=${this.fetchHandler.bind(this)}
68
+ .selectedRecords=${this.selectedRecords}
69
+ @select-record-change=${e => {
70
+ this.view!.model = this.selected?.model || []
71
+ }}
72
+ >
73
+ </ox-grist>
74
+
75
+ <div class="footer">
76
+ <button @click=${this.onEmpty.bind(this)}>
77
+ <md-icon>check_box_outline_blank</md-icon>${i18next.t('button.empty')}
78
+ </button>
79
+ <div filler></div>
80
+ <button @click=${this.onCancel.bind(this)}><md-icon>cancel</md-icon>${i18next.t('button.cancel')}</button>
81
+ <button @click=${this.onConfirm.bind(this)} done><md-icon>done</md-icon>${i18next.t('button.confirm')}</button>
82
+ </div>
83
+ `
84
+ }
85
+
86
+ searchText(value: string) {
87
+ this.grist!.searchText = value
88
+ }
89
+
90
+ onEmpty() {
91
+ this.confirmCallback && this.confirmCallback()
92
+ closePopup(this)
93
+ }
94
+
95
+ onCancel() {
96
+ closePopup(this)
97
+ }
98
+
99
+ onConfirm() {
100
+ this.confirmCallback && this.confirmCallback(this.selected)
101
+ closePopup(this)
102
+ }
103
+
104
+ async fetchHandler({ page = 1, limit = 100, sortings = [], filters = [] }: FetchOption) {
105
+ const response = await client.query({
106
+ query: gql`
107
+ query ($filters: [Filter!], $pagination: Pagination, $sortings: [Sorting!]) {
108
+ responses: approvalLineReferences(filters: $filters, pagination: $pagination, sortings: $sortings) {
109
+ items {
110
+ id
111
+ name
112
+ description
113
+ ownerType
114
+ owner {
115
+ id
116
+ name
117
+ description
118
+ controlNo
119
+ }
120
+ model {
121
+ type
122
+ approver {
123
+ id
124
+ name
125
+ description
126
+ controlNo
127
+ }
128
+ }
129
+ }
130
+ total
131
+ }
132
+ }
133
+ `,
134
+ variables: {
135
+ filters,
136
+ pagination: { page, limit },
137
+ sortings
138
+ }
139
+ })
140
+
141
+ return {
142
+ total: response.data.responses.total || 0,
143
+ records: response.data.responses.items || []
144
+ }
145
+ }
146
+
147
+ async firstUpdated() {
148
+ this.config = {
149
+ list: {
150
+ fields: ['name', 'description'],
151
+ details: ['ownerType', 'owner']
152
+ },
153
+ columns: [
154
+ { type: 'gutter', gutterName: 'sequence' },
155
+ { type: 'gutter', gutterName: 'row-selector', multiple: false },
156
+ {
157
+ type: 'string',
158
+ name: 'name',
159
+ header: i18next.t('field.name'),
160
+ record: {
161
+ editable: false
162
+ },
163
+ filter: 'search',
164
+ sortable: true,
165
+ width: 150
166
+ },
167
+ {
168
+ type: 'string',
169
+ name: 'description',
170
+ header: i18next.t('field.description'),
171
+ record: {
172
+ editable: false
173
+ },
174
+ filter: 'search',
175
+ width: 200
176
+ },
177
+ {
178
+ type: 'string',
179
+ name: 'ownerType',
180
+ header: i18next.t('field.owner-type'),
181
+ record: {
182
+ editable: false
183
+ },
184
+ width: 200
185
+ },
186
+ {
187
+ type: 'resource-object',
188
+ name: 'owner',
189
+ header: i18next.t('field.owner'),
190
+ record: {
191
+ editable: false,
192
+ renderer: function (value, column, record, rowIndex, field) {
193
+ var options = {}
194
+ switch (record.ownerType) {
195
+ case 'Employee':
196
+ options = {
197
+ valueField: 'id',
198
+ nameField: 'name',
199
+ descriptionField: 'controlNo'
200
+ }
201
+ break
202
+ case 'Common':
203
+ default:
204
+ return
205
+ }
206
+
207
+ var dynamicRecord = { ...column.record, options }
208
+
209
+ return getRenderer(column.type)(value, { ...column, record: dynamicRecord }, record, rowIndex, field)
210
+ }
211
+ },
212
+ width: 200
213
+ }
214
+ ],
215
+ rows: {
216
+ selectable: {
217
+ multiple: false
218
+ },
219
+ appendable: false,
220
+ handlers: {
221
+ click: 'select-row',
222
+ dblclick: ((columns, data, column, record, rowIndex, field) => {
223
+ this.onConfirm()
224
+ }) as GristEventHandler
225
+ }
226
+ }
227
+ }
228
+ }
229
+
230
+ get selected() {
231
+ var selected = this.grist.selected
232
+
233
+ return selected && selected.length > 0 ? selected[0] : undefined
234
+ }
235
+ }
@@ -0,0 +1,229 @@
1
+ import '@material/web/icon/icon.js'
2
+
3
+ import gql from 'graphql-tag'
4
+ import { css, html, LitElement } from 'lit'
5
+ import { customElement, property, query } from 'lit/decorators.js'
6
+
7
+ import { client } from '@operato/graphql'
8
+ import { i18next, localize } from '@operato/i18n'
9
+ import { FetchOption, getEditor, getRenderer } from '@operato/data-grist'
10
+ import { isMobileDevice } from '@operato/utils'
11
+ import { CommonHeaderStyles } from '@operato/styles'
12
+
13
+ import { ApprovalLine } from '../types/approval-line'
14
+
15
+ /**
16
+ * 결재선 관리를 위해서
17
+ * 1. 결재선 리스트를 조회한다.
18
+ * 2. 결재선을 생성, 삭제, 수정한다.
19
+ */
20
+ @customElement('approval-line-templates-manager')
21
+ export class ApprovalLineTemplatesManager extends localize(i18next)(LitElement) {
22
+ static styles = [
23
+ CommonHeaderStyles,
24
+ css`
25
+ :host {
26
+ display: flex;
27
+ flex-direction: column;
28
+
29
+ background-color: var(--md-sys-color-surface);
30
+ }
31
+
32
+ ox-grist {
33
+ flex: 1;
34
+ }
35
+ `
36
+ ]
37
+
38
+ @property({ type: Object }) gristConfig?: any
39
+ @property({ type: Object }) approvalLine?: ApprovalLine
40
+
41
+ @query('ox-grist') grist?: HTMLElement & { fetch: any; commit: any; deleteSelectedRecords: any; data: any }
42
+
43
+ render() {
44
+ return html`
45
+ <ox-grist
46
+ .mode=${isMobileDevice() ? 'CARD' : 'GRID'}
47
+ .config=${this.gristConfig}
48
+ .fetchHandler=${this.fetchHandler.bind(this)}
49
+ ></ox-grist>
50
+
51
+ <div class="footer">
52
+ <button danger @click=${this._deleteDataItems.bind(this)}>
53
+ <md-icon>delete</md-icon>${i18next.t('button.delete')}
54
+ </button>
55
+ <button @click=${this._updateDataItems.bind(this)} done>
56
+ <md-icon>save</md-icon>${i18next.t('button.save')}
57
+ </button>
58
+ </div>
59
+ `
60
+ }
61
+
62
+ async firstUpdated() {
63
+ this.gristConfig = {
64
+ list: {
65
+ fields: ['name', 'description', 'active']
66
+ },
67
+ columns: [
68
+ { type: 'gutter', gutterName: 'row-selector', multiple: true },
69
+ { type: 'gutter', gutterName: 'sequence' },
70
+ {
71
+ type: 'gutter',
72
+ gutterName: 'button',
73
+ icon: 'arrow_upward',
74
+ handlers: {
75
+ click: 'move-up'
76
+ }
77
+ },
78
+ {
79
+ type: 'gutter',
80
+ gutterName: 'button',
81
+ icon: 'arrow_downward',
82
+ handlers: {
83
+ click: 'move-down'
84
+ }
85
+ },
86
+ {
87
+ type: 'select',
88
+ name: 'type',
89
+ header: i18next.t('field.type'),
90
+ record: {
91
+ editable: true,
92
+ options: ['', 'Employee', 'Department', 'Role']
93
+ },
94
+ width: 140
95
+ },
96
+ {
97
+ type: 'resource-object',
98
+ name: 'approver',
99
+ header: i18next.t('field.approver'),
100
+ record: {
101
+ editable: true,
102
+ editor: function (value, column, record, rowIndex, field) {
103
+ var options = {}
104
+ switch (record.type) {
105
+ case 'Employee':
106
+ options = {
107
+ title: i18next.t('title.employee list'),
108
+ queryName: 'employees',
109
+ columns: [
110
+ { name: 'id', hidden: true },
111
+ {
112
+ name: 'controlNo',
113
+ header: { renderer: () => i18next.t('field.control-no') },
114
+ filter: 'search'
115
+ },
116
+ { name: 'name', header: { renderer: () => i18next.t('field.name') }, filter: 'search' }
117
+ ],
118
+ list: { fields: ['name', 'control-no'] },
119
+ valueField: 'id',
120
+ nameField: 'name',
121
+ descriptionField: 'controlNo'
122
+ }
123
+ break
124
+ case 'Department':
125
+ options = {
126
+ title: i18next.t('title.department list'),
127
+ queryName: 'departments'
128
+ }
129
+ break
130
+ case 'Role':
131
+ options = {
132
+ title: i18next.t('title.lookup role'),
133
+ queryName: 'roles'
134
+ }
135
+ break
136
+ default:
137
+ options = {}
138
+ }
139
+
140
+ var dynamicRecord = { ...column.record, options }
141
+
142
+ return getEditor(column.type)(value, { ...column, record: dynamicRecord }, record, rowIndex, field)
143
+ },
144
+ renderer: function (value, column, record, rowIndex, field) {
145
+ var options = {}
146
+ switch (record.type) {
147
+ case 'Employee':
148
+ options = {
149
+ valueField: 'id',
150
+ nameField: 'name',
151
+ descriptionField: 'controlNo'
152
+ }
153
+ break
154
+ case 'Department':
155
+ case 'Role':
156
+ default:
157
+ break
158
+ }
159
+
160
+ var dynamicRecord = { ...column.record, options }
161
+
162
+ return getRenderer(column.type)(value, { ...column, record: dynamicRecord }, record, rowIndex, field)
163
+ }
164
+ },
165
+ width: 180
166
+ }
167
+ ],
168
+ rows: {
169
+ selectable: {
170
+ multiple: true
171
+ }
172
+ },
173
+ pagination: {
174
+ infinite: true
175
+ },
176
+ sorters: []
177
+ }
178
+ }
179
+
180
+ async fetchHandler({ filters, page, limit, sortings = [] }: FetchOption) {
181
+ const model = this.approvalLine?.model || []
182
+
183
+ return {
184
+ total: model.length,
185
+ records: model
186
+ }
187
+ }
188
+
189
+ async _updateDataItems() {
190
+ this.grist?.commit()
191
+
192
+ const response = await client.mutate({
193
+ mutation: gql`
194
+ mutation ($id: String!, $patch: ApprovalLinePatch!) {
195
+ updateApprovalLine(id: $id, patch: $patch) {
196
+ name
197
+ }
198
+ }
199
+ `,
200
+ variables: {
201
+ id: this.approvalLine!.id,
202
+ patch: {
203
+ model: this.grist?.data.records,
204
+ cuFlag: 'M'
205
+ }
206
+ }
207
+ })
208
+
209
+ if (!response.errors) {
210
+ await document.dispatchEvent(
211
+ new CustomEvent('notify', {
212
+ detail: {
213
+ message: i18next.t('text.info_x_successfully', {
214
+ x: i18next.t('button.save')
215
+ })
216
+ }
217
+ })
218
+ )
219
+ }
220
+ }
221
+
222
+ async _deleteDataItems() {
223
+ if (!confirm(i18next.t('text.sure_to_x', { x: i18next.t('text.delete') }))) {
224
+ return
225
+ }
226
+
227
+ this.grist?.deleteSelectedRecords(false)
228
+ }
229
+ }
@@ -0,0 +1,122 @@
1
+ import { css, html, LitElement, TemplateResult } from 'lit'
2
+ import { customElement, property, query } from 'lit/decorators.js'
3
+
4
+ import { i18next, localize } from '@operato/i18n'
5
+ import { ApprovalLineItem } from '../types/approval-line'
6
+ import { OrgMemberTargetType } from '../types/org-member'
7
+
8
+ @customElement('approval-line-view')
9
+ export class ApprovalLineView extends localize(i18next)(LitElement) {
10
+ static styles = [
11
+ css`
12
+ :host {
13
+ display: flex;
14
+ flex-direction: row;
15
+ flex-wrap: wrap;
16
+ padding: 10px;
17
+ justify-content: center;
18
+ align-items: center;
19
+
20
+ --md-icon-size: 3em;
21
+ color: var(--md-sys-color-on-surface);
22
+ background-color: var(--md-sys-color-surface-variant);
23
+ }
24
+
25
+ ol {
26
+ position: relative;
27
+ margin: 0;
28
+ padding: 0;
29
+ display: flex;
30
+ gap: 25px;
31
+ list-style-type: none;
32
+ }
33
+
34
+ li {
35
+ text-align: center;
36
+ font-size: var(--fontsize-default);
37
+ }
38
+
39
+ li:before {
40
+ content: '';
41
+ height: 2px;
42
+ width: 25px;
43
+ display: block;
44
+ position: absolute;
45
+ margin-left: -25px;
46
+ margin-top: 10px;
47
+ background-color: var(--md-sys-color-primary);
48
+ opacity: 0.4;
49
+ }
50
+
51
+ li:first-child:before {
52
+ display: none;
53
+ }
54
+
55
+ span {
56
+ display: block;
57
+ margin: auto;
58
+ margin-bottom: var(--spacing-small);
59
+ color: var(--md-sys-color-primary);
60
+ background-color: var(--md-sys-color-surface-variant);
61
+ border-radius: 5px;
62
+ border: 2px solid var(--md-sys-color-primary);
63
+ font-size: var(--fontsize-small);
64
+ line-height: 1;
65
+ padding: var(--spacing-small) var(--spacing-medium);
66
+ }
67
+
68
+ [approver][current] span {
69
+ background-color: var(--md-sys-color-primary);
70
+ border-color: var(--md-sys-color-primary);
71
+ color: var(--md-sys-color-on-primary);
72
+ }
73
+
74
+ [approver][current] {
75
+ font-weight: bold;
76
+ }
77
+
78
+ [approver][current] ~ li span {
79
+ background-color: rgba(0, 0, 0, 0.5);
80
+ border-color: transparent;
81
+ color: var(--md-sys-color-on-primary);
82
+ }
83
+
84
+ [approver][current] ~ li:before {
85
+ background-color: rgba(0, 0, 0, 0.4);
86
+ opacity: 1;
87
+ }
88
+ `
89
+ ]
90
+
91
+ @property({ type: Array }) model?: ApprovalLineItem[]
92
+ @property({ type: Number }) current?: number = -1
93
+
94
+ render() {
95
+ const items = this.model || []
96
+
97
+ return html`
98
+ <ol>
99
+ ${this.model ? html` <li approver><span>ME</span>${i18next.t('label.myself')}</li> ` : html``}
100
+ ${items.map((item, order) => this.renderItem(item, order + 1))}
101
+ </ol>
102
+ `
103
+ }
104
+
105
+ renderItem(item: ApprovalLineItem, order: number): TemplateResult {
106
+ const { type, approver } = item
107
+ const { name } = approver || {
108
+ name: [OrgMemberTargetType.Myself, OrgMemberTargetType.MySupervisor, OrgMemberTargetType.MyDepartment].includes(
109
+ type!
110
+ )
111
+ ? i18next.t(`label.${type}`)
112
+ : ''
113
+ }
114
+
115
+ return html`
116
+ <li approver ?current=${this.current == order}>
117
+ <span>${i18next.t('label.' + type)}</span>
118
+ ${name}
119
+ </li>
120
+ `
121
+ }
122
+ }
@@ -0,0 +1,79 @@
1
+ import '@material/web/icon/icon.js'
2
+ import { css, html, LitElement } from 'lit'
3
+ import { customElement, property, query, state } from 'lit/decorators.js'
4
+
5
+ import { i18next, localize } from '@operato/i18n'
6
+ import { CommonHeaderStyles } from '@operato/styles'
7
+ import { closePopup } from '@operato/popup'
8
+
9
+ import { AssigneesEditor } from './assignees-editor'
10
+ import { AssigneeItem } from '../types/org-member'
11
+
12
+ /**
13
+ * 결재선의 각 결재자 리스트를 편집한다.
14
+ */
15
+ @customElement('assignees-editor-popup')
16
+ export class AssigneesEditorPopup extends localize(i18next)(LitElement) {
17
+ static styles = [
18
+ CommonHeaderStyles,
19
+ css`
20
+ :host {
21
+ display: flex;
22
+ flex-direction: column;
23
+
24
+ background-color: var(--md-sys-color-surface);
25
+ }
26
+
27
+ assignees-editor {
28
+ flex: 1;
29
+ }
30
+ `
31
+ ]
32
+
33
+ @property({ type: Array }) value?: AssigneeItem[]
34
+
35
+ @property({ type: Object }) confirmCallback?: (value?: AssigneeItem[] | null) => void
36
+ @query('assignees-editor') editor!: AssigneesEditor
37
+
38
+ /* this.value는 (원인불명으로) 값이 Reset되므로, 변화값을 유지하도록 별도로 changedValue를 사용함 */
39
+ private changedValue?: AssigneeItem[] = this.value
40
+
41
+ render() {
42
+ return html`
43
+ <assignees-editor
44
+ .value=${this.value}
45
+ @change=${(e: CustomEvent) => {
46
+ this.changedValue = [...e.detail]
47
+ }}
48
+ ></assignees-editor>
49
+
50
+ <div class="footer">
51
+ <button @click=${this.onEmpty.bind(this)}>
52
+ <md-icon>check_box_outline_blank</md-icon>${i18next.t('button.empty')}
53
+ </button>
54
+ <div filler></div>
55
+ <button @click=${this.onCancel.bind(this)}><md-icon>cancel</md-icon>${i18next.t('button.cancel')}</button>
56
+ <button @click=${this.onConfirm.bind(this)} done><md-icon>done</md-icon>${i18next.t('button.confirm')}</button>
57
+ </div>
58
+ `
59
+ }
60
+
61
+ firstUpdated() {
62
+ this.changedValue = this.value
63
+ }
64
+
65
+ onEmpty() {
66
+ this.confirmCallback && this.confirmCallback(null)
67
+ closePopup(this)
68
+ }
69
+
70
+ onCancel() {
71
+ closePopup(this)
72
+ }
73
+
74
+ onConfirm() {
75
+ this.confirmCallback && this.confirmCallback(this.changedValue)
76
+
77
+ closePopup(this)
78
+ }
79
+ }