@things-factory/organization 8.0.5 → 9.0.0-beta.12

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 (97) hide show
  1. package/dist-client/bootstrap.js +8 -1
  2. package/dist-client/bootstrap.js.map +1 -1
  3. package/dist-client/filters-form/filter-department-object.d.ts +3 -0
  4. package/dist-client/filters-form/filter-department-object.js +8 -0
  5. package/dist-client/filters-form/filter-department-object.js.map +1 -0
  6. package/dist-client/filters-form/ox-filter-department-object.d.ts +15 -0
  7. package/dist-client/filters-form/ox-filter-department-object.js +130 -0
  8. package/dist-client/filters-form/ox-filter-department-object.js.map +1 -0
  9. package/dist-client/pages/employee/employee-list-page.js +3 -3
  10. package/dist-client/pages/employee/employee-list-page.js.map +1 -1
  11. package/dist-client/pages/employee/employees-by-department.js +2 -2
  12. package/dist-client/pages/employee/employees-by-department.js.map +1 -1
  13. package/dist-client/tsconfig.tsbuildinfo +1 -1
  14. package/dist-server/controllers/register-employee-as-system-user.d.ts +1 -1
  15. package/dist-server/controllers/register-employee-as-system-user.js +5 -5
  16. package/dist-server/controllers/register-employee-as-system-user.js.map +1 -1
  17. package/dist-server/service/employee/employee-history.d.ts +6 -2
  18. package/dist-server/service/employee/employee-history.js +23 -3
  19. package/dist-server/service/employee/employee-history.js.map +1 -1
  20. package/dist-server/service/employee/employee-query.js +1 -1
  21. package/dist-server/service/employee/employee-query.js.map +1 -1
  22. package/dist-server/service/employee/employee-type.d.ts +13 -5
  23. package/dist-server/service/employee/employee-type.js +39 -7
  24. package/dist-server/service/employee/employee-type.js.map +1 -1
  25. package/dist-server/service/employee/employee.d.ts +6 -2
  26. package/dist-server/service/employee/employee.js +23 -3
  27. package/dist-server/service/employee/employee.js.map +1 -1
  28. package/dist-server/tsconfig.tsbuildinfo +1 -1
  29. package/package.json +12 -12
  30. package/client/bootstrap.ts +0 -23
  31. package/client/component/approval-line-brief.ts +0 -119
  32. package/client/component/approval-line-items-editor-popup.ts +0 -91
  33. package/client/component/approval-line-items-editor.ts +0 -325
  34. package/client/component/approval-line-selector.ts +0 -235
  35. package/client/component/approval-line-templates-manager.ts +0 -229
  36. package/client/component/approval-line-view.ts +0 -122
  37. package/client/component/assignees-editor-popup.ts +0 -79
  38. package/client/component/assignees-editor.ts +0 -217
  39. package/client/component/assignees-view.ts +0 -55
  40. package/client/component/department-selector.ts +0 -151
  41. package/client/component/department-view.ts +0 -107
  42. package/client/component/index.ts +0 -16
  43. package/client/component/recipients-editor-popup.ts +0 -79
  44. package/client/component/recipients-editor.ts +0 -212
  45. package/client/component/recipients-view.ts +0 -55
  46. package/client/grist-editor/grist-editor-approval-line.ts +0 -70
  47. package/client/grist-editor/grist-editor-assignees.ts +0 -69
  48. package/client/grist-editor/grist-editor-department-object.ts +0 -78
  49. package/client/grist-editor/grist-editor-recipients.ts +0 -69
  50. package/client/grist-editor/grist-renderer-approval-line.ts +0 -13
  51. package/client/grist-editor/grist-renderer-assignees.ts +0 -13
  52. package/client/grist-editor/grist-renderer-department-object.ts +0 -13
  53. package/client/grist-editor/grist-renderer-recipients.ts +0 -13
  54. package/client/index.ts +0 -2
  55. package/client/pages/approval-line/common-approval-line-templates-page.ts +0 -382
  56. package/client/pages/approval-line/my-approval-line-templates-page.ts +0 -385
  57. package/client/pages/department/department-importer.ts +0 -87
  58. package/client/pages/department/department-list-page.ts +0 -450
  59. package/client/pages/department/department-tree-page.ts +0 -379
  60. package/client/pages/employee/employee-importer.ts +0 -87
  61. package/client/pages/employee/employee-list-page.ts +0 -772
  62. package/client/pages/employee/employees-by-department.ts +0 -519
  63. package/client/route.ts +0 -27
  64. package/client/tsconfig.json +0 -13
  65. package/client/types/approval-line.ts +0 -52
  66. package/client/types/contact.ts +0 -51
  67. package/client/types/department.ts +0 -29
  68. package/client/types/employee.ts +0 -50
  69. package/client/types/index.ts +0 -5
  70. package/client/types/org-member.ts +0 -27
  71. package/server/controllers/register-employee-as-system-user.ts +0 -136
  72. package/server/index.ts +0 -3
  73. package/server/migrations/1723861013111-seed-organization-codes.ts +0 -127
  74. package/server/migrations/index.ts +0 -9
  75. package/server/routes.ts +0 -26
  76. package/server/service/approval-line/approval-line-item.ts +0 -42
  77. package/server/service/approval-line/approval-line-mutation.ts +0 -394
  78. package/server/service/approval-line/approval-line-query.ts +0 -208
  79. package/server/service/approval-line/approval-line-type.ts +0 -63
  80. package/server/service/approval-line/approval-line.ts +0 -123
  81. package/server/service/approval-line/index.ts +0 -7
  82. package/server/service/department/department-history.ts +0 -141
  83. package/server/service/department/department-mutation.ts +0 -231
  84. package/server/service/department/department-query.ts +0 -131
  85. package/server/service/department/department-type.ts +0 -74
  86. package/server/service/department/department.ts +0 -116
  87. package/server/service/department/event-subscriber.ts +0 -17
  88. package/server/service/department/index.ts +0 -9
  89. package/server/service/employee/employee-history.ts +0 -173
  90. package/server/service/employee/employee-mutation.ts +0 -386
  91. package/server/service/employee/employee-query.ts +0 -172
  92. package/server/service/employee/employee-type.ts +0 -176
  93. package/server/service/employee/employee.ts +0 -177
  94. package/server/service/employee/event-subscriber.ts +0 -17
  95. package/server/service/employee/index.ts +0 -9
  96. package/server/service/index.ts +0 -39
  97. package/server/tsconfig.json +0 -10
@@ -1,772 +0,0 @@
1
- import '@operato/data-grist'
2
- import '@operato/context/ox-context-page-toolbar.js'
3
- import '@things-factory/contact/dist-client'
4
-
5
- import gql from 'graphql-tag'
6
- import { css, html } from 'lit'
7
- import { customElement, property, query } from 'lit/decorators.js'
8
- import { connect } from 'pwa-helpers/connect-mixin'
9
-
10
- import { ScopedElementsMixin } from '@open-wc/scoped-elements'
11
- import { Contact } from '@operato/contact/ox-contact.js'
12
- import { DataGrist, FetchOption, GristRecord } 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 { PageView, store } from '@operato/shell'
17
- import { CommonHeaderStyles, CommonGristStyles, ScrollbarStyles } from '@operato/styles'
18
- import { isMobileDevice } from '@operato/utils'
19
- import { p13n } from '@operato/p13n'
20
-
21
- import { EmployeeImporter } from './employee-importer'
22
-
23
- @customElement('employee-list-page')
24
- export class EmployeeListPage extends connect(store)(p13n(localize(i18next)(ScopedElementsMixin(PageView)))) {
25
- static styles = [
26
- ScrollbarStyles,
27
- CommonGristStyles,
28
- CommonHeaderStyles,
29
- css`
30
- :host {
31
- display: flex;
32
-
33
- width: 100%;
34
-
35
- --ox-pfp-size: 40px;
36
-
37
- --grid-record-emphasized-background-color: #8b0000;
38
- --grid-record-emphasized-color: #ff6b6b;
39
- }
40
-
41
- ox-grist {
42
- overflow-y: auto;
43
- flex: 1;
44
- }
45
-
46
- .header {
47
- grid-template-areas: 'filters actions';
48
- }
49
- `
50
- ]
51
-
52
- static get scopedElements() {
53
- return {
54
- 'employee-importer': EmployeeImporter
55
- }
56
- }
57
-
58
- @property({ type: Object }) gristConfig: any
59
- @property({ type: String }) mode: 'CARD' | 'GRID' | 'LIST' = isMobileDevice() ? 'CARD' : 'GRID'
60
-
61
- @query('ox-grist') private grist!: DataGrist
62
-
63
- get context() {
64
- return {
65
- title: i18next.t('title.employee list'),
66
- search: {
67
- handler: (search: string) => {
68
- this.grist.searchText = search
69
- },
70
- value: this.grist?.searchText || ''
71
- },
72
- filter: {
73
- handler: () => {
74
- this.grist.toggleHeadroom()
75
- }
76
- },
77
- help: 'organization/employee',
78
- actions: [
79
- {
80
- icon: 'save',
81
- title: i18next.t('button.save'),
82
- action: this.onUpdateEmployee.bind(this)
83
- },
84
- {
85
- icon: 'delete',
86
- title: i18next.t('button.delete'),
87
- action: this.onDeleteEmployee.bind(this),
88
- emphasis: {
89
- danger: true
90
- }
91
- }
92
- ],
93
- exportable: {
94
- name: i18next.t('title.employee list'),
95
- data: this.exportHandler.bind(this)
96
- },
97
- importable: {
98
- handler: this.importHandler.bind(this)
99
- },
100
- toolbar: false
101
- }
102
- }
103
-
104
- render() {
105
- const mode = this.mode || (isMobileDevice() ? 'CARD' : 'GRID')
106
-
107
- return html`
108
- <ox-grist
109
- .mode=${mode}
110
- .config=${this.gristConfig}
111
- .fetchHandler=${this.fetchHandler.bind(this)}
112
- .personalConfigProvider=${this.getPagePreferenceProvider('ox-grist')!}
113
- >
114
- <div slot="headroom" class="header">
115
- <div class="filters">
116
- <ox-filters-form class="filter" autofocus without-search></ox-filters-form>
117
- </div>
118
-
119
- <ox-context-page-toolbar class="actions" .context=${this.context}></ox-context-page-toolbar>
120
- </div>
121
-
122
- <ox-grist-personalizer slot="setting"></ox-grist-personalizer>
123
- </ox-grist>
124
- `
125
- }
126
-
127
- async pageInitialized(lifecycle: any) {
128
- this.gristConfig = {
129
- pagination: { pages: [50, 100, 200] },
130
- list: {
131
- thumbnail: 'profile',
132
- fields: ['controlNo', 'name'],
133
- details: ['email', 'department', 'hiredOn', 'updatedAt']
134
- },
135
- columns: [
136
- { type: 'gutter', gutterName: 'sequence', fixed: true },
137
- { type: 'gutter', gutterName: 'row-selector', multiple: true, fixed: true },
138
- {
139
- type: 'gutter',
140
- gutterName: 'button',
141
- header: i18next.t('field.register-account'),
142
- icon: record => (record.id && !record.user ? 'badge' : ''),
143
- iconOnly: false,
144
- title: record => (record.id && !record.user ? i18next.t('button.register-account') : ''),
145
- width: 80,
146
- handlers: {
147
- click: (columns, data, column, record, rowIndex) => {
148
- if (!record || !record.id || record.user) {
149
- /* TODO record가 새로 추가된 것이면 리턴하도록 한다. */
150
- return
151
- }
152
-
153
- this.attachSystemUser(record)
154
- }
155
- }
156
- },
157
- {
158
- type: 'image',
159
- name: 'profile',
160
- header: i18next.t('button.edit-contact'),
161
- width: 80,
162
- record: {
163
- align: 'center',
164
- renderer: function (value, column, record, rowIndex, field) {
165
- return html`<ox-pfp-view .profile=${record.profile} .name=${record.name || '+'}></ox-pfp-view>`
166
- }
167
- },
168
- handlers: {
169
- click: async (columns, data, column, record, rowIndex) => {
170
- if (record && record.contact) {
171
- this.openContactPopup(record)
172
- } else {
173
- this.openContactSelector(record)
174
- }
175
- }
176
- }
177
- },
178
- {
179
- type: 'string',
180
- name: 'controlNo',
181
- fixed: true,
182
- header: i18next.t('field.control-no'),
183
- record: {
184
- editable: true,
185
- mandatory: true
186
- },
187
- filter: 'search',
188
- sortable: true,
189
- width: 105
190
- },
191
- {
192
- type: 'string',
193
- name: 'name',
194
- fixed: true,
195
- header: i18next.t('field.name'),
196
- record: {
197
- editable: true,
198
- mandatory: true
199
- },
200
- filter: 'search',
201
- sortable: true,
202
- width: 100
203
- },
204
- {
205
- type: 'string',
206
- name: 'alias',
207
- header: i18next.t('label.alias'),
208
- record: {
209
- editable: true
210
- },
211
- filter: 'search',
212
- sortable: false,
213
- width: 110
214
- },
215
- {
216
- type: 'resource-object',
217
- name: 'user',
218
- header: i18next.t('field.system-user'),
219
- record: {
220
- editable: true,
221
- options: {
222
- title: i18next.t('title.lookup system-user'),
223
- queryName: 'users',
224
- basicArgs: { filters: [{ name: 'userType', operator: 'eq', value: 'user' }] },
225
- descriptionField: 'email',
226
- columns: [
227
- { name: 'id', hidden: true },
228
- { name: 'name', header: i18next.t('field.name'), filter: 'search' },
229
- { name: 'email', header: i18next.t('field.email'), filter: 'search' }
230
- ],
231
- list: { fields: ['name', 'email'] }
232
- }
233
- },
234
- sortable: false,
235
- filter: 'search',
236
- width: 100
237
- },
238
- {
239
- type: 'code',
240
- name: 'type',
241
- header: i18next.t('field.type'),
242
- width: 115,
243
- sortable: true,
244
- filter: true,
245
- record: {
246
- editable: true,
247
- codeName: 'EMPLOYEE_TYPE',
248
- selectDispOpt: 'name'
249
- }
250
- },
251
- {
252
- type: 'department-object',
253
- name: 'department',
254
- header: i18next.t('field.department'),
255
- record: {
256
- editable: true
257
- },
258
- sortable: false,
259
- filter: true,
260
- width: 130
261
- },
262
- {
263
- type: 'resource-object',
264
- name: 'supervisor',
265
- header: i18next.t('field.supervisor'),
266
- record: {
267
- editable: true,
268
- options: {
269
- title: i18next.t('title.employee list'),
270
- queryName: 'employees',
271
- pagination: { pages: [50, 100, 200] },
272
- basicArgs: {
273
- filters: [
274
- {
275
- name: 'active',
276
- operator: 'eq',
277
- value: true
278
- }
279
- ]
280
- },
281
- list: { fields: ['controlNo', 'name', 'alias', 'hiredOn'] },
282
- columns: [
283
- { name: 'id', hidden: true },
284
- {
285
- name: 'controlNo',
286
- width: 120,
287
- header: { renderer: () => i18next.t('field.control-no') },
288
- filter: 'search',
289
- sortable: true
290
- },
291
- {
292
- name: 'name',
293
- width: 120,
294
- header: { renderer: () => i18next.t('field.name') },
295
- filter: 'search',
296
- sortable: true
297
- },
298
- {
299
- name: 'alias',
300
- width: 150,
301
- header: { renderer: () => i18next.t('label.alias') },
302
- filter: 'search',
303
- sortable: true
304
- },
305
- {
306
- type: 'code',
307
- name: 'type',
308
- width: 110,
309
- header: { renderer: () => i18next.t('label.type') },
310
- record: {
311
- editable: false,
312
- codeName: 'EMPLOYEE_TYPE',
313
- selectDispOpt: 'name'
314
- }
315
- },
316
- {
317
- type: 'code',
318
- name: 'jobPosition',
319
- width: 110,
320
- header: { renderer: () => i18next.t('label.job-position') },
321
- record: {
322
- editable: false,
323
- codeName: 'JOB_POSITION',
324
- selectDispOpt: 'name'
325
- }
326
- },
327
- {
328
- type: 'code',
329
- name: 'jobResponsibility',
330
- width: 200,
331
- header: { renderer: () => i18next.t('label.job-responsibility') },
332
- record: {
333
- editable: false,
334
- codeName: 'JOB_RESPONSIBILITY',
335
- selectDispOpt: 'name'
336
- }
337
- },
338
- {
339
- type: 'date',
340
- name: 'hiredOn',
341
- header: { renderer: () => i18next.t('field.hired-on') },
342
- width: 120
343
- }
344
- ],
345
- valueField: 'id',
346
- nameField: 'name',
347
- descriptionField: 'controlNo'
348
- }
349
- },
350
- sortable: false,
351
- width: 120
352
- },
353
- {
354
- type: 'string',
355
- name: 'email',
356
- header: i18next.t('field.email'),
357
- width: 200,
358
- record: {
359
- editable: false,
360
- renderer: function (value, column, record, rowIndex, field) {
361
- return record.contact ? record.contact.email : ''
362
- }
363
- },
364
- sortable: false
365
- },
366
- {
367
- type: 'string',
368
- name: 'phone',
369
- header: i18next.t('field.phone'),
370
- width: 130,
371
- record: {
372
- editable: false,
373
- renderer: function (value, column, record, rowIndex, field) {
374
- return record.contact ? record.contact.phone : ''
375
- }
376
- },
377
- sortable: false
378
- },
379
- {
380
- type: 'code',
381
- name: 'jobResponsibility',
382
- header: i18next.t('label.job-responsibility'),
383
- width: 175,
384
- record: {
385
- editable: true,
386
- codeName: 'JOB_RESPONSIBILITY',
387
- selectDispOpt: 'name'
388
- },
389
- filter: true
390
- },
391
- {
392
- type: 'code',
393
- name: 'jobPosition',
394
- header: i18next.t('label.job-position'),
395
- width: 100,
396
- record: {
397
- editable: true,
398
- codeName: 'JOB_POSITION',
399
- selectDispOpt: 'name'
400
- },
401
- filter: true
402
- },
403
- {
404
- type: 'date',
405
- name: 'hiredOn',
406
- header: i18next.t('field.hired-on'),
407
- width: 120,
408
- record: {
409
- editable: true
410
- },
411
- sortable: true
412
- },
413
- {
414
- type: 'date',
415
- name: 'retiredAt',
416
- header: i18next.t('label.retired-at'),
417
- width: 120,
418
- record: {
419
- editable: true
420
- }
421
- },
422
- {
423
- type: 'checkbox',
424
- name: 'active',
425
- label: true,
426
- header: i18next.t('field.active'),
427
- width: 70,
428
- record: {
429
- align: 'center',
430
- editable: true
431
- },
432
- filter: true,
433
- sortable: false
434
- },
435
- {
436
- type: 'string',
437
- name: 'note',
438
- header: i18next.t('field.note'),
439
- width: 200,
440
- record: {
441
- editable: true
442
- },
443
- filter: 'search'
444
- },
445
- {
446
- type: 'resource-object',
447
- name: 'updater',
448
- header: i18next.t('field.updater'),
449
- width: 90,
450
- sortable: false
451
- },
452
- {
453
- type: 'datetime',
454
- name: 'updatedAt',
455
- header: i18next.t('field.updated_at'),
456
- width: 180,
457
- sortable: true
458
- },
459
- {
460
- type: 'resource-object',
461
- name: 'contact',
462
- hidden: true
463
- }
464
- ],
465
- rows: {
466
- selectable: {
467
- multiple: true
468
- }
469
- },
470
- sorters: [
471
- {
472
- name: 'controlNo'
473
- }
474
- ]
475
- }
476
- }
477
-
478
- async pageUpdated(changes: any, lifecycle: any) {
479
- if (this.active) {
480
- // do something here when this page just became as active
481
- }
482
- }
483
-
484
- async fetchHandler({ page = 1, limit = 100, sortings = [], filters = [] }: FetchOption) {
485
- const response = await client.query({
486
- query: gql`
487
- query ($filters: [Filter!], $pagination: Pagination, $sortings: [Sorting!]) {
488
- responses: employees(filters: $filters, pagination: $pagination, sortings: $sortings) {
489
- items {
490
- id
491
- controlNo
492
- name
493
- alias
494
- type
495
- jobResponsibility
496
- jobPosition
497
- active
498
- email
499
- phone
500
- user {
501
- id
502
- name
503
- }
504
- department {
505
- id
506
- controlNo
507
- name
508
- description
509
- }
510
- supervisor {
511
- id
512
- name
513
- controlNo
514
- }
515
- note
516
- hiredOn
517
- retiredAt
518
- contact {
519
- id
520
- email
521
- phone
522
- address
523
- }
524
- profile {
525
- left
526
- top
527
- zoom
528
- picture
529
- }
530
- updater {
531
- id
532
- name
533
- }
534
- updatedAt
535
- }
536
- total
537
- }
538
- }
539
- `,
540
- variables: {
541
- filters,
542
- pagination: { page, limit },
543
- sortings
544
- }
545
- })
546
-
547
- const records = response.data.responses.items
548
-
549
- return {
550
- total: response.data.responses.total || 0,
551
- records
552
- }
553
- }
554
-
555
- async onDeleteEmployee() {
556
- if (confirm(i18next.t('text.sure_to_x', { x: i18next.t('text.delete') }))) {
557
- const ids = this.grist.selected.map(record => record.id)
558
- if (ids && ids.length > 0) {
559
- const response = await client.mutate({
560
- mutation: gql`
561
- mutation ($ids: [String!]!) {
562
- deleteEmployees(ids: $ids)
563
- }
564
- `,
565
- variables: {
566
- ids
567
- }
568
- })
569
-
570
- if (!response.errors) {
571
- this.grist.fetch()
572
- notify({
573
- message: i18next.t('text.info_x_successfully', { x: i18next.t('text.delete') })
574
- })
575
- }
576
- }
577
- }
578
- }
579
-
580
- async onUpdateEmployee() {
581
- let patches = this.grist.dirtyRecords
582
- if (patches && patches.length) {
583
- patches = patches.map(patch => {
584
- let patchField: any = patch.id ? { id: patch.id } : {}
585
- const dirtyFields = patch.__dirtyfields__
586
- for (let key in dirtyFields) {
587
- patchField[key] = dirtyFields[key].after
588
- }
589
- patchField.cuFlag = patch.__dirty__
590
-
591
- return patchField
592
- })
593
-
594
- const response = await client.mutate({
595
- mutation: gql`
596
- mutation ($patches: [EmployeePatch!]!) {
597
- updateMultipleEmployee(patches: $patches) {
598
- name
599
- }
600
- }
601
- `,
602
- variables: {
603
- patches
604
- }
605
- })
606
-
607
- if (!response.errors) {
608
- this.grist.fetch()
609
- }
610
- }
611
- }
612
-
613
- async exportHandler() {
614
- const exportTargets = this.grist.selected.length ? this.grist.selected : this.grist.dirtyData.records
615
- const targetFieldSet = new Set([
616
- 'id',
617
- 'controlNo',
618
- 'name',
619
- 'alias',
620
- 'type',
621
- 'hiredOn',
622
- 'retiredAt',
623
- 'active',
624
- 'note'
625
- ])
626
-
627
- return exportTargets.map(employee => {
628
- let tempObj = {}
629
- for (const field of targetFieldSet) {
630
- tempObj[field] = employee[field]
631
- }
632
-
633
- return tempObj
634
- })
635
- }
636
-
637
- async importHandler(records) {
638
- const popup = openPopup(
639
- html`
640
- <employee-importer
641
- .employees=${records}
642
- @imported=${() => {
643
- history.back()
644
- this.grist.fetch()
645
- }}
646
- ></employee-importer>
647
- `,
648
- {
649
- backdrop: true,
650
- size: 'large',
651
- title: i18next.t('title.import employee')
652
- }
653
- )
654
-
655
- popup.onclosed = () => {
656
- this.grist.fetch()
657
- }
658
- }
659
-
660
- async openContactPopup(record: GristRecord) {
661
- if (!record.contact?.id) {
662
- return
663
- }
664
-
665
- const popup = openPopup(
666
- html`
667
- <contact-popup
668
- .contactId=${record.contact.id}
669
- @change=${() => {
670
- this.grist.fetch()
671
- }}
672
- @detach=${() => {
673
- this.detachContact(record)
674
- }}
675
- detachable
676
- ></contact-popup>
677
- `,
678
- {
679
- backdrop: true,
680
- size: 'large',
681
- title: i18next.t('title.contact')
682
- }
683
- )
684
- }
685
-
686
- async openContactSelector(record: GristRecord) {
687
- const popup = openPopup(
688
- html`
689
- <contact-selector .confirmCallback=${selected => this.attachContact(record, selected)}></contact-selector>
690
- `,
691
- {
692
- backdrop: true,
693
- size: 'large',
694
- title: i18next.t('title.contact')
695
- }
696
- )
697
- }
698
-
699
- async attachContact(record: GristRecord, contact: Contact) {
700
- if (record.id) {
701
- const response = await client.mutate({
702
- mutation: gql`
703
- mutation ($id: String!, $contactId: String!) {
704
- attachContact(id: $id, contactId: $contactId) {
705
- id
706
- }
707
- }
708
- `,
709
- variables: {
710
- id: record.id,
711
- contactId: contact.id
712
- }
713
- })
714
-
715
- this.grist.fetch()
716
- } else if (contact?.id) {
717
- this.grist.addRecord({
718
- name: contact.name,
719
- contact: {
720
- id: contact.id,
721
- name: contact.name,
722
- email: (contact as any).email,
723
- phone: (contact as any).phone,
724
- address: (contact as any).address
725
- }
726
- })
727
- this.grist.refresh()
728
- await this.grist.requestUpdate()
729
-
730
- this.grist.checkDirties()
731
- }
732
- }
733
-
734
- async detachContact(record) {
735
- if (record.id) {
736
- const response = await client.mutate({
737
- mutation: gql`
738
- mutation ($id: String!) {
739
- detachContact(id: $id) {
740
- id
741
- }
742
- }
743
- `,
744
- variables: {
745
- id: record.id
746
- }
747
- })
748
-
749
- this.grist.fetch()
750
- } else {
751
- record.contact = null
752
- this.grist.refresh()
753
- }
754
- }
755
-
756
- async attachSystemUser(record) {
757
- if (!record.user) {
758
- await client.mutate({
759
- mutation: gql`
760
- mutation ($employeeId: String!) {
761
- registerEmployeeAsSystemUser(employeeId: $employeeId)
762
- }
763
- `,
764
- variables: {
765
- employeeId: record.id
766
- }
767
- })
768
- }
769
-
770
- this.grist.fetch()
771
- }
772
- }