@things-factory/organization 6.2.103 → 6.2.111

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