@things-factory/organization 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 (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,450 @@
1
+ import '@operato/data-tree'
2
+ import '@operato/context/ox-context-page-toolbar.js'
3
+ import '@operato/data-grist/ox-record-creator.js'
4
+
5
+ import { CommonHeaderStyles, ScrollbarStyles } from '@operato/styles'
6
+ import { PageView, store } from '@operato/shell'
7
+ import { css, html } from 'lit'
8
+ import { customElement, property, query, state } from 'lit/decorators.js'
9
+ import { ScopedElementsMixin } from '@open-wc/scoped-elements'
10
+ import { client } from '@operato/graphql'
11
+ import { i18next, localize } from '@operato/i18n'
12
+ import { isMobileDevice } from '@operato/utils'
13
+ import { DataGrist, FetchOption } from '@operato/data-grist'
14
+ import { notify } from '@operato/layout'
15
+ import { OxPrompt } from '@operato/popup/ox-prompt.js'
16
+
17
+ import { connect } from 'pwa-helpers/connect-mixin'
18
+ import gql from 'graphql-tag'
19
+
20
+ import { DepartmentImporter } from './department-importer'
21
+ import { Department } from '../../types/department'
22
+
23
+ const SubDepartmentFragment = gql`
24
+ fragment SubDepartmentFragment on Department {
25
+ id
26
+ controlNo
27
+ name
28
+ description
29
+
30
+ manager {
31
+ id
32
+ name
33
+ controlNo
34
+ photo
35
+ email
36
+ }
37
+ active
38
+ picture
39
+
40
+ updater {
41
+ id
42
+ name
43
+ }
44
+ updatedAt
45
+ }
46
+ `
47
+
48
+ @customElement('department-list-page')
49
+ export class DepartmentListPage extends connect(store)(localize(i18next)(ScopedElementsMixin(PageView))) {
50
+ static styles = [
51
+ ScrollbarStyles,
52
+ CommonHeaderStyles,
53
+ css`
54
+ :host {
55
+ display: flex;
56
+
57
+ width: 100%;
58
+
59
+ --grid-record-emphasized-background-color: #8b0000;
60
+ --grid-record-emphasized-color: #ff6b6b;
61
+ }
62
+
63
+ ox-grist {
64
+ overflow-y: auto;
65
+ flex: 1;
66
+ }
67
+
68
+ .header {
69
+ grid-template-areas: 'filters actions';
70
+ }
71
+ `
72
+ ]
73
+
74
+ static get scopedElements() {
75
+ return {
76
+ 'department-importer': DepartmentImporter
77
+ }
78
+ }
79
+
80
+ @state() root?: Department
81
+ @state() selected?: Department
82
+
83
+ @query('ox-grist') private grist!: DataGrist
84
+
85
+ get context() {
86
+ return {
87
+ title: i18next.t('title.department list'),
88
+ help: 'organization/department',
89
+ actions: [
90
+ {
91
+ icon: 'add',
92
+ title: i18next.t('button.add'),
93
+ action: () => this.grist.addRecord()
94
+ },
95
+ // {
96
+ // icon: 'add',
97
+ // title: i18next.t('button.add-sibling-dept'),
98
+ // action: () => this.grist.addSiblingNodes()
99
+ // },
100
+ {
101
+ icon: 'save',
102
+ title: i18next.t('button.save'),
103
+ action: this.save.bind(this)
104
+ },
105
+ {
106
+ icon: 'delete',
107
+ title: i18next.t('button.delete'),
108
+ action: this.delete.bind(this),
109
+ emphasis: {
110
+ danger: true
111
+ }
112
+ }
113
+ ].filter(Boolean),
114
+ toolbar: false
115
+ }
116
+ }
117
+
118
+ @property({ type: Object }) gristConfig: any
119
+
120
+ render() {
121
+ const mode = isMobileDevice() ? 'CARD' : 'GRID'
122
+
123
+ return html`
124
+ <ox-grist .mode=${mode} .config=${this.gristConfig} .fetchHandler=${this.fetchHandler.bind(this)}>
125
+ <div slot="headroom" class="header">
126
+ <div class="filters">
127
+ <ox-filters-form class="filter" autofocus without-search></ox-filters-form>
128
+ </div>
129
+
130
+ <ox-context-page-toolbar class="actions" .context=${this.context}></ox-context-page-toolbar>
131
+ </div>
132
+ </ox-grist>
133
+ `
134
+ }
135
+
136
+ async fetchHandler({ page = 1, limit = 100, sortings = [], filters = [] }: FetchOption) {
137
+ const response = await client.query({
138
+ query: gql`
139
+ query ($filters: [Filter!], $pagination: Pagination, $sortings: [Sorting!]) {
140
+ responses: departmentRoots(filters: $filters, pagination: $pagination, sortings: $sortings) {
141
+ total
142
+ items {
143
+ ...SubDepartmentFragment
144
+ children(filters: $filters, sortings: $sortings) {
145
+ ...SubDepartmentFragment
146
+ children(filters: $filters, sortings: $sortings) {
147
+ ...SubDepartmentFragment
148
+ children(filters: $filters, sortings: $sortings) {
149
+ ...SubDepartmentFragment
150
+ children(filters: $filters, sortings: $sortings) {
151
+ ...SubDepartmentFragment
152
+ children(filters: $filters, sortings: $sortings) {
153
+ ...SubDepartmentFragment
154
+ children(filters: $filters, sortings: $sortings) {
155
+ ...SubDepartmentFragment
156
+ children(filters: $filters, sortings: $sortings) {
157
+ ...SubDepartmentFragment
158
+ }
159
+ }
160
+ }
161
+ }
162
+ }
163
+ }
164
+ }
165
+ }
166
+ }
167
+ }
168
+
169
+ ${SubDepartmentFragment}
170
+ `,
171
+ variables: {
172
+ filters,
173
+ pagination: { page, limit },
174
+ sortings
175
+ }
176
+ })
177
+
178
+ const { items: records, total } = response.data.responses
179
+
180
+ return {
181
+ total,
182
+ records
183
+ }
184
+ }
185
+
186
+ async pageInitialized(lifecycle: any) {
187
+ this.gristConfig = {
188
+ pagination: { pages: [50, 100, 200] },
189
+ list: {
190
+ thumbnail: 'profile',
191
+ fields: ['controlNo', 'name'],
192
+ details: ['email', 'manager', 'updatedAt']
193
+ },
194
+ columns: [
195
+ {
196
+ type: 'gutter',
197
+ gutterName: 'dirty',
198
+ fixed: true
199
+ },
200
+ {
201
+ type: 'tree',
202
+ name: 'name',
203
+ label: true,
204
+ header: i18next.t('label.name'),
205
+ record: {
206
+ editable: true,
207
+ options: {
208
+ selectable: true
209
+ }
210
+ },
211
+ filter: 'search',
212
+ sortable: true,
213
+ width: 200,
214
+ fixed: true,
215
+ handlers: {
216
+ contextmenu: 'contextmenu-tree-mutation'
217
+ }
218
+ },
219
+ { name: 'id', hidden: true },
220
+ {
221
+ type: 'string',
222
+ name: 'controlNo',
223
+ header: i18next.t('label.control-no'),
224
+ record: {
225
+ editable: true
226
+ },
227
+ filter: 'search',
228
+ sortable: true,
229
+ width: 110
230
+ },
231
+ {
232
+ type: 'string',
233
+ name: 'description',
234
+ header: i18next.t('label.description'),
235
+ record: {
236
+ editable: true
237
+ },
238
+ filter: 'search',
239
+ sortable: true,
240
+ width: 110
241
+ },
242
+ {
243
+ type: 'resource-object',
244
+ name: 'manager',
245
+ header: i18next.t('label.manager'),
246
+ record: {
247
+ editable: true,
248
+ options: {
249
+ title: i18next.t('title.employee list'),
250
+ queryName: 'employees',
251
+ pagination: { pages: [50, 100, 200] },
252
+ basicArgs: {
253
+ filters: [
254
+ {
255
+ name: 'active',
256
+ operator: 'eq',
257
+ value: true
258
+ }
259
+ ]
260
+ },
261
+ list: { fields: ['controlNo', 'name', 'email'] },
262
+ columns: [
263
+ { name: 'id', hidden: true },
264
+ {
265
+ name: 'controlNo',
266
+ width: 120,
267
+ header: { renderer: () => i18next.t('field.control-no') },
268
+ filter: 'search',
269
+ sortable: true
270
+ },
271
+ {
272
+ name: 'name',
273
+ width: 120,
274
+ header: { renderer: () => i18next.t('field.name') },
275
+ filter: 'search',
276
+ sortable: true
277
+ },
278
+ {
279
+ name: 'email',
280
+ width: 150,
281
+ header: { renderer: () => i18next.t('label.email') },
282
+ filter: false,
283
+ sortable: true
284
+ }
285
+ ],
286
+ valueField: 'id',
287
+ nameField: 'name',
288
+ descriptionField: 'controlNo'
289
+ }
290
+ },
291
+ sortable: true,
292
+ width: 120
293
+ },
294
+ {
295
+ type: 'checkbox',
296
+ name: 'active',
297
+ label: true,
298
+ header: i18next.t('field.active'),
299
+ width: 70,
300
+ record: {
301
+ align: 'center',
302
+ editable: true
303
+ },
304
+ filter: true,
305
+ sortable: true
306
+ },
307
+ {
308
+ type: 'resource-object',
309
+ name: 'updater',
310
+ header: i18next.t('field.updater'),
311
+ width: 90,
312
+ sortable: false
313
+ },
314
+ {
315
+ type: 'datetime',
316
+ name: 'updatedAt',
317
+ header: i18next.t('field.updated_at'),
318
+ width: 180,
319
+ sortable: true
320
+ },
321
+ {
322
+ type: 'image',
323
+ name: 'picture',
324
+ record: {
325
+ renderer: function (value, column, record, rowIndex, field) {
326
+ return html`<ox-pfp-view
327
+ style="height:90%; width: unset; aspect-ratio: 1 / 1;"
328
+ .profile=${record.picture}
329
+ .name=${record.name}
330
+ ></ox-pfp-view>`
331
+ }
332
+ },
333
+ hidden: true
334
+ }
335
+ ],
336
+ rows: {
337
+ appendable: false,
338
+ selectable: {
339
+ multiple: true
340
+ }
341
+ },
342
+ sorters: [
343
+ {
344
+ name: 'controlNo'
345
+ }
346
+ ],
347
+ tree: {
348
+ childrenProperty: 'children',
349
+ expanded: () => true
350
+ }
351
+ }
352
+ }
353
+
354
+ async pageUpdated(changes: any, lifecycle: any) {
355
+ if (this.active) {
356
+ // do something here when this page just became as active
357
+ }
358
+ }
359
+
360
+ async delete() {
361
+ if (!this.grist.selected || this.grist.selected.length == 0) {
362
+ await OxPrompt.open({
363
+ title: 'select department first.',
364
+ confirmButton: { text: i18next.t('button.confirm') }
365
+ })
366
+ return
367
+ }
368
+
369
+ if (this.grist.selected.find(selected => selected.children && selected.children.length > 0)) {
370
+ await OxPrompt.open({
371
+ title: 'Department having children cannot be deleted.',
372
+ confirmButton: { text: i18next.t('button.confirm') }
373
+ })
374
+ return
375
+ }
376
+
377
+ if (
378
+ await OxPrompt.open({
379
+ title: i18next.t('text.sure_to_x', { x: i18next.t('text.delete') }),
380
+ confirmButton: { text: i18next.t('button.confirm') },
381
+ cancelButton: { text: i18next.t('button.cancel') }
382
+ })
383
+ ) {
384
+ const temporaries = this.grist.selected.filter(record => !record.id)
385
+
386
+ if (temporaries && temporaries.length > 0) {
387
+ this.grist.remove
388
+ }
389
+
390
+ const ids = this.grist.selected.filter(record => record.id).map(record => record.id)
391
+
392
+ if (ids && ids.length > 0) {
393
+ const response = await client.mutate({
394
+ mutation: gql`
395
+ mutation ($ids: [String!]!) {
396
+ deleteDepartments(ids: $ids)
397
+ }
398
+ `,
399
+ variables: {
400
+ ids
401
+ }
402
+ })
403
+
404
+ if (!response.errors) {
405
+ this.grist.fetch()
406
+ notify({
407
+ message: i18next.t('text.info_x_successfully', { x: i18next.t('text.delete') })
408
+ })
409
+ }
410
+ }
411
+ }
412
+ }
413
+
414
+ async save() {
415
+ let patches = this.grist.dirtyRecords
416
+ if (patches && patches.length) {
417
+ patches = patches.map(patch => {
418
+ let patchField: any = patch.id ? { id: patch.id } : {}
419
+ const dirtyFields = patch.__dirtyfields__
420
+ for (let key in dirtyFields) {
421
+ patchField[key] = dirtyFields[key].after
422
+ }
423
+ patchField.parent = patch.parent
424
+ patchField.cuFlag = patch.__dirty__
425
+
426
+ return patchField
427
+ })
428
+
429
+ const response = await client.mutate({
430
+ mutation: gql`
431
+ mutation ($patches: [DepartmentPatch!]!) {
432
+ updateMultipleDepartment(patches: $patches) {
433
+ name
434
+ }
435
+ }
436
+ `,
437
+ variables: {
438
+ patches
439
+ },
440
+ context: {
441
+ hasUpload: true
442
+ }
443
+ })
444
+
445
+ if (!response.errors) {
446
+ this.grist.fetch()
447
+ }
448
+ }
449
+ }
450
+ }