@things-factory/dataset 5.0.0-alpha.9 → 5.0.0-zeta.1

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 (105) hide show
  1. package/README.md +9 -0
  2. package/assets/data-samples.jpg +0 -0
  3. package/client/bootstrap.js +21 -1
  4. package/client/pages/{data-entry-form.js → data-entry/data-entry-form.js} +15 -3
  5. package/client/pages/data-entry/data-entry-list-page.js +390 -0
  6. package/client/pages/data-ooc/data-ooc-list-page.js +416 -0
  7. package/client/pages/data-ooc/data-ooc-view.js +183 -0
  8. package/client/pages/data-report/data-report-embed-page.js +113 -0
  9. package/client/pages/data-report/data-report-list-page.js +432 -0
  10. package/client/pages/data-report/jasper-report-oocs-page.js +122 -0
  11. package/client/pages/data-report/jasper-report-samples-crosstab-page.js +122 -0
  12. package/client/pages/data-report/jasper-report-samples-page.js +122 -0
  13. package/client/pages/data-sample/data-sample-list-page.js +372 -0
  14. package/client/pages/data-sample/data-sample-view.js +98 -0
  15. package/client/pages/{data-sensor.js → data-sensor/data-sensor-list-page.js} +43 -68
  16. package/client/pages/{data-item-list.js → data-set/data-item-list.js} +36 -11
  17. package/client/pages/{data-set-importer.js → data-set/data-set-importer.js} +0 -0
  18. package/client/pages/data-set/data-set-list-page.js +739 -0
  19. package/client/route.js +34 -6
  20. package/config/config.development.js +13 -0
  21. package/config/config.production.js +1 -0
  22. package/dist-server/controllers/create-data-sample.js +133 -0
  23. package/dist-server/controllers/create-data-sample.js.map +1 -0
  24. package/dist-server/controllers/data-use-case.js +57 -0
  25. package/dist-server/controllers/data-use-case.js.map +1 -0
  26. package/dist-server/controllers/index.js +18 -0
  27. package/dist-server/controllers/index.js.map +1 -1
  28. package/dist-server/controllers/jasper-report.js +156 -0
  29. package/dist-server/controllers/jasper-report.js.map +1 -0
  30. package/dist-server/index.js +1 -0
  31. package/dist-server/index.js.map +1 -1
  32. package/dist-server/routes.js +13 -24
  33. package/dist-server/routes.js.map +1 -1
  34. package/dist-server/service/data-item/data-item-query.js +6 -2
  35. package/dist-server/service/data-item/data-item-query.js.map +1 -1
  36. package/dist-server/service/data-item/data-item-type.js +15 -7
  37. package/dist-server/service/data-item/data-item-type.js.map +1 -1
  38. package/dist-server/service/data-item/data-item.js +17 -3
  39. package/dist-server/service/data-item/data-item.js.map +1 -1
  40. package/dist-server/service/data-ooc/data-ooc-mutation.js +92 -0
  41. package/dist-server/service/data-ooc/data-ooc-mutation.js.map +1 -0
  42. package/dist-server/service/data-ooc/data-ooc-query.js +120 -0
  43. package/dist-server/service/data-ooc/data-ooc-query.js.map +1 -0
  44. package/dist-server/service/data-ooc/data-ooc-subscription.js +65 -0
  45. package/dist-server/service/data-ooc/data-ooc-subscription.js.map +1 -0
  46. package/dist-server/service/data-ooc/data-ooc-type.js +107 -0
  47. package/dist-server/service/data-ooc/data-ooc-type.js.map +1 -0
  48. package/dist-server/service/data-ooc/data-ooc.js +237 -0
  49. package/dist-server/service/data-ooc/data-ooc.js.map +1 -0
  50. package/dist-server/service/data-ooc/index.js +10 -0
  51. package/dist-server/service/data-ooc/index.js.map +1 -0
  52. package/dist-server/service/data-sample/data-sample-mutation.js +2 -138
  53. package/dist-server/service/data-sample/data-sample-mutation.js.map +1 -1
  54. package/dist-server/service/data-sample/data-sample-query.js +7 -2
  55. package/dist-server/service/data-sample/data-sample-query.js.map +1 -1
  56. package/dist-server/service/data-sample/data-sample-type.js +12 -42
  57. package/dist-server/service/data-sample/data-sample-type.js.map +1 -1
  58. package/dist-server/service/data-sample/data-sample.js +34 -3
  59. package/dist-server/service/data-sample/data-sample.js.map +1 -1
  60. package/dist-server/service/data-sensor/data-sensor-query.js +7 -2
  61. package/dist-server/service/data-sensor/data-sensor-query.js.map +1 -1
  62. package/dist-server/service/data-set/data-set-mutation.js +40 -14
  63. package/dist-server/service/data-set/data-set-mutation.js.map +1 -1
  64. package/dist-server/service/data-set/data-set-query.js +190 -3
  65. package/dist-server/service/data-set/data-set-query.js.map +1 -1
  66. package/dist-server/service/data-set/data-set-type.js +84 -3
  67. package/dist-server/service/data-set/data-set-type.js.map +1 -1
  68. package/dist-server/service/data-set/data-set.js +110 -15
  69. package/dist-server/service/data-set/data-set.js.map +1 -1
  70. package/dist-server/service/index.js +6 -2
  71. package/dist-server/service/index.js.map +1 -1
  72. package/package.json +19 -13
  73. package/server/controllers/create-data-sample.ts +177 -0
  74. package/server/controllers/data-use-case.ts +85 -0
  75. package/server/controllers/index.ts +2 -0
  76. package/server/controllers/jasper-report.ts +170 -0
  77. package/server/index.ts +1 -0
  78. package/server/routes.ts +21 -31
  79. package/server/service/data-item/data-item-query.ts +8 -3
  80. package/server/service/data-item/data-item-type.ts +10 -6
  81. package/server/service/data-item/data-item.ts +15 -4
  82. package/server/service/data-ooc/data-ooc-mutation.ts +150 -0
  83. package/server/service/data-ooc/data-ooc-query.ts +69 -0
  84. package/server/service/data-ooc/data-ooc-subscription.ts +51 -0
  85. package/server/service/data-ooc/data-ooc-type.ts +68 -0
  86. package/server/service/data-ooc/data-ooc.ts +204 -0
  87. package/server/service/data-ooc/index.ts +7 -0
  88. package/server/service/data-sample/data-sample-mutation.ts +3 -168
  89. package/server/service/data-sample/data-sample-query.ts +9 -3
  90. package/server/service/data-sample/data-sample-type.ts +7 -28
  91. package/server/service/data-sample/data-sample.ts +33 -3
  92. package/server/service/data-sensor/data-sensor-query.ts +9 -3
  93. package/server/service/data-set/data-set-mutation.ts +53 -14
  94. package/server/service/data-set/data-set-query.ts +161 -4
  95. package/server/service/data-set/data-set-type.ts +65 -4
  96. package/server/service/data-set/data-set.ts +100 -12
  97. package/server/service/index.ts +6 -2
  98. package/things-factory.config.js +35 -7
  99. package/translations/en.json +46 -3
  100. package/translations/ko.json +45 -3
  101. package/translations/ms.json +44 -3
  102. package/translations/zh.json +44 -3
  103. package/client/pages/data-sample.js +0 -307
  104. package/client/pages/data-set.js +0 -457
  105. package/yarn-error.log +0 -23244
@@ -0,0 +1,739 @@
1
+ import '@operato/data-grist'
2
+ import './data-item-list.js'
3
+ import './data-set-importer.js'
4
+ import '../data-entry/data-entry-form.js'
5
+
6
+ import gql from 'graphql-tag'
7
+ import { css, html } from 'lit'
8
+ import moment from 'moment-timezone'
9
+ import { connect } from 'pwa-helpers/connect-mixin'
10
+
11
+ import { getEditor, getRenderer } from '@operato/data-grist'
12
+ import { OxDataUseCase } from '@operato/dataset'
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 { CommonButtonStyles, CommonGristStyles, ScrollbarStyles } from '@operato/styles'
18
+ import { isMobileDevice } from '@operato/utils'
19
+
20
+ const DEFAULT_TZ = Intl.DateTimeFormat().resolvedOptions().timeZone
21
+ const TIMEZONE_OPTIONS = ['', DEFAULT_TZ, ...moment.tz.names().filter(tz => tz !== DEFAULT_TZ)]
22
+
23
+ const ENTRY_TYPES = [
24
+ {
25
+ display: '',
26
+ value: ''
27
+ },
28
+ {
29
+ display: 'Generated',
30
+ value: 'generated'
31
+ },
32
+ {
33
+ display: 'Board',
34
+ value: 'board'
35
+ },
36
+ {
37
+ display: 'Page',
38
+ value: 'page'
39
+ },
40
+ {
41
+ display: 'External URL',
42
+ value: 'external'
43
+ }
44
+ ]
45
+
46
+ const MONITOR_TYPES = [
47
+ {
48
+ display: '',
49
+ value: ''
50
+ },
51
+ {
52
+ display: 'Generated',
53
+ value: 'generated'
54
+ },
55
+ {
56
+ display: 'Board',
57
+ value: 'board'
58
+ },
59
+ {
60
+ display: 'Page',
61
+ value: 'page'
62
+ },
63
+ {
64
+ display: 'External URL',
65
+ value: 'external'
66
+ }
67
+ ]
68
+
69
+ const REPORT_TYPES = [
70
+ {
71
+ display: '',
72
+ value: ''
73
+ },
74
+ {
75
+ display: 'Generated',
76
+ value: 'generated'
77
+ },
78
+ {
79
+ display: 'Embed',
80
+ value: 'embed'
81
+ },
82
+ {
83
+ display: 'Page',
84
+ value: 'page'
85
+ },
86
+ {
87
+ display: 'External URL',
88
+ value: 'external'
89
+ }
90
+ ]
91
+
92
+ const USECASE_OPTIONS = () => {
93
+ return ['', ...OxDataUseCase.getUseCaseNames()].map(name => {
94
+ return {
95
+ display: name,
96
+ value: name
97
+ }
98
+ })
99
+ }
100
+ export class DataSetListPage extends connect(store)(localize(i18next)(PageView)) {
101
+ static get properties() {
102
+ return {
103
+ gristConfig: Object,
104
+ mode: String
105
+ }
106
+ }
107
+
108
+ static get styles() {
109
+ return [
110
+ ScrollbarStyles,
111
+ CommonGristStyles,
112
+ css`
113
+ :host {
114
+ display: flex;
115
+
116
+ width: 100%;
117
+
118
+ --grid-record-emphasized-background-color: red;
119
+ --grid-record-emphasized-color: yellow;
120
+ }
121
+ `
122
+ ]
123
+ }
124
+
125
+ get context() {
126
+ return {
127
+ title: i18next.t('title.data-set list'),
128
+ help: 'dataset/data-set',
129
+ actions: [
130
+ {
131
+ title: i18next.t('button.copy'),
132
+ action: this._copyDataSet.bind(this),
133
+ ...CommonButtonStyles.copy
134
+ },
135
+ {
136
+ title: i18next.t('button.save'),
137
+ action: this._updateDataSet.bind(this),
138
+ ...CommonButtonStyles.save
139
+ },
140
+ {
141
+ title: i18next.t('button.delete'),
142
+ action: this._deleteDataSet.bind(this),
143
+ ...CommonButtonStyles.delete
144
+ }
145
+ ],
146
+ exportable: {
147
+ name: i18next.t('title.data-set list'),
148
+ data: this.exportHandler.bind(this)
149
+ },
150
+ importable: {
151
+ handler: this.importHandler.bind(this)
152
+ }
153
+ }
154
+ }
155
+
156
+ render() {
157
+ const mode = this.mode || (isMobileDevice() ? 'LIST' : 'GRID')
158
+
159
+ return html`
160
+ <ox-grist
161
+ .mode=${mode}
162
+ .config=${this.gristConfig}
163
+ .fetchHandler=${this.fetchHandler.bind(this)}
164
+ ?url-params-sensitive=${this.active}
165
+ >
166
+ <div slot="headroom">
167
+ <div id="filters">
168
+ <ox-filters-form></ox-filters-form>
169
+ </div>
170
+
171
+ <div id="sorters">
172
+ Sort
173
+ <mwc-icon
174
+ @click=${e => {
175
+ const target = e.currentTarget
176
+ this.renderRoot.querySelector('#sorter-control').open({
177
+ right: 0,
178
+ top: target.offsetTop + target.offsetHeight
179
+ })
180
+ }}
181
+ >expand_more</mwc-icon
182
+ >
183
+ <ox-popup id="sorter-control">
184
+ <ox-sorters-control> </ox-sorters-control>
185
+ </ox-popup>
186
+ </div>
187
+
188
+ <div id="modes">
189
+ <mwc-icon @click=${() => (this.mode = 'GRID')} ?active=${mode == 'GRID'}>grid_on</mwc-icon>
190
+ <mwc-icon @click=${() => (this.mode = 'LIST')} ?active=${mode == 'LIST'}>format_list_bulleted</mwc-icon>
191
+ <mwc-icon @click=${() => (this.mode = 'CARD')} ?active=${mode == 'CARD'}>apps</mwc-icon>
192
+ </div>
193
+ </div>
194
+ </ox-grist>
195
+ `
196
+ }
197
+
198
+ get grist() {
199
+ return this.renderRoot.querySelector('ox-grist')
200
+ }
201
+
202
+ async pageInitialized(lifecycle) {
203
+ this.gristConfig = {
204
+ list: {
205
+ fields: ['name', 'description'],
206
+ details: ['schedule', 'active']
207
+ },
208
+ columns: [
209
+ { type: 'gutter', gutterName: 'sequence' },
210
+ { type: 'gutter', gutterName: 'row-selector', multiple: true },
211
+ {
212
+ type: 'gutter',
213
+ gutterName: 'button',
214
+ icon: 'reorder',
215
+ handlers: {
216
+ click: (columns, data, column, record, rowIndex) => {
217
+ if (!record.id) return
218
+ const popup = openPopup(html` <data-item-list .dataSet=${record}></data-item-list> `, {
219
+ backdrop: true,
220
+ help: 'data-set/ui/data-item-list',
221
+ size: 'large',
222
+ title: i18next.t('title.data-item list')
223
+ })
224
+ popup.onclosed = () => {
225
+ this.grist.fetch()
226
+ }
227
+ }
228
+ }
229
+ },
230
+ {
231
+ type: 'gutter',
232
+ gutterName: 'button',
233
+ icon: 'fact_check',
234
+ handlers: {
235
+ click: (columns, data, column, record, rowIndex) => {
236
+ openPopup(
237
+ html` <data-entry-form .dataSet=${record} style="background-color: white;"></data-entry-form> `,
238
+ {
239
+ backdrop: true,
240
+ size: 'large',
241
+ title: i18next.t('title.data-entry-form')
242
+ }
243
+ )
244
+ }
245
+ }
246
+ },
247
+ {
248
+ type: 'string',
249
+ name: 'name',
250
+ header: i18next.t('field.name'),
251
+ record: {
252
+ editable: true
253
+ },
254
+ filter: 'search',
255
+ sortable: true,
256
+ width: 150,
257
+ imex: {
258
+ width: 25,
259
+ header: i18next.t('field.name'),
260
+ type: 'string',
261
+ key: 'name'
262
+ }
263
+ },
264
+ {
265
+ type: 'string',
266
+ name: 'description',
267
+ header: i18next.t('field.description'),
268
+ record: {
269
+ editable: true
270
+ },
271
+ filter: 'search',
272
+ width: 200,
273
+ imex: {
274
+ width: 33,
275
+ header: i18next.t('field.description'),
276
+ type: 'string',
277
+ key: 'description'
278
+ }
279
+ },
280
+ {
281
+ type: 'checkbox',
282
+ name: 'active',
283
+ label: true,
284
+ header: i18next.t('field.active'),
285
+ record: {
286
+ editable: true
287
+ },
288
+ filter: true,
289
+ sortable: true,
290
+ width: 60,
291
+ imex: {
292
+ width: 10,
293
+ header: i18next.t('field.active'),
294
+ type: 'checkbox',
295
+ key: 'active'
296
+ }
297
+ },
298
+ {
299
+ type: 'select',
300
+ name: 'type',
301
+ label: true,
302
+ header: i18next.t('field.type'),
303
+ record: {
304
+ editable: true,
305
+ options: [
306
+ {},
307
+ {
308
+ display: i18next.t('text.manually collected'),
309
+ value: 'manual'
310
+ },
311
+ {
312
+ display: i18next.t('text.automatically collected'),
313
+ value: 'automatic'
314
+ }
315
+ ]
316
+ },
317
+ sortable: true,
318
+ filter: true,
319
+ width: 60,
320
+ imex: {
321
+ width: 10,
322
+ header: i18next.t('field.type'),
323
+ type: 'select',
324
+ key: 'type'
325
+ }
326
+ },
327
+ {
328
+ type: 'select',
329
+ name: 'useCase',
330
+ label: true,
331
+ header: i18next.t('field.use-case'),
332
+ record: {
333
+ editable: true,
334
+ options: USECASE_OPTIONS
335
+ },
336
+ sortable: true,
337
+ filter: true,
338
+ width: 80
339
+ },
340
+ {
341
+ type: 'partition-keys',
342
+ name: 'partitionKeys',
343
+ header: i18next.t('field.partition-keys'),
344
+ record: {
345
+ editable: true,
346
+ options: {
347
+ objectified: true /* transfered as a object type */
348
+ }
349
+ },
350
+ width: 200
351
+ },
352
+ {
353
+ type: 'crontab',
354
+ name: 'schedule',
355
+ label: true,
356
+ header: i18next.t('field.schedule'),
357
+ record: {
358
+ editable: true,
359
+ options: {
360
+ objectified: true
361
+ }
362
+ },
363
+ width: 80,
364
+ label: true,
365
+ imex: {
366
+ width: 13,
367
+ header: i18next.t('field.schedule'),
368
+ type: 'string',
369
+ key: 'schedule'
370
+ }
371
+ },
372
+ {
373
+ type: 'select',
374
+ name: 'timezone',
375
+ header: i18next.t('field.timezone'),
376
+ record: {
377
+ editable: true,
378
+ options: TIMEZONE_OPTIONS
379
+ },
380
+ width: 120,
381
+ imex: {
382
+ width: 13,
383
+ header: i18next.t('field.timezone'),
384
+ type: 'select',
385
+ key: 'timezone'
386
+ }
387
+ },
388
+ {
389
+ type: 'resource-object',
390
+ name: 'supervisoryRole',
391
+ header: i18next.t('field.supervisory-role'),
392
+ record: {
393
+ editable: true,
394
+ options: {
395
+ queryName: 'roles'
396
+ }
397
+ },
398
+ sortable: true,
399
+ filter: 'like',
400
+ width: 120
401
+ },
402
+ {
403
+ type: 'resource-object',
404
+ name: 'entryRole',
405
+ header: i18next.t('field.entry-role'),
406
+ record: {
407
+ editable: true,
408
+ options: {
409
+ queryName: 'roles'
410
+ }
411
+ },
412
+ sortable: true,
413
+ width: 120
414
+ },
415
+ {
416
+ type: 'select',
417
+ name: 'entryType',
418
+ label: true,
419
+ header: i18next.t('field.entry-type'),
420
+ record: {
421
+ editable: true,
422
+ options: ENTRY_TYPES
423
+ },
424
+ width: 80
425
+ },
426
+ {
427
+ type: 'string',
428
+ name: 'entryView',
429
+ header: i18next.t('field.entry-view'),
430
+ record: {
431
+ editable: true,
432
+ editor: function (value, column, record, rowIndex, field) {
433
+ var type = record.entryType !== 'board' ? 'string' : 'board'
434
+ return getEditor(type)(value, column, record, rowIndex, field)
435
+ },
436
+ renderer: function (value, column, record, rowIndex, field) {
437
+ var type = record.entryType !== 'board' ? 'string' : 'board'
438
+ return getRenderer(type)(value, column, record, rowIndex, field)
439
+ }
440
+ },
441
+ width: 140
442
+ },
443
+ {
444
+ type: 'select',
445
+ name: 'monitorType',
446
+ label: true,
447
+ header: i18next.t('field.monitor-type'),
448
+ record: {
449
+ editable: true,
450
+ options: MONITOR_TYPES
451
+ },
452
+ width: 80
453
+ },
454
+ {
455
+ type: 'string',
456
+ name: 'monitorView',
457
+ header: i18next.t('field.monitor-view'),
458
+ record: {
459
+ editable: true,
460
+ editor: function (value, column, record, rowIndex, field) {
461
+ var type = record.monitorType !== 'board' ? 'string' : 'board'
462
+ return getEditor(type)(value, column, record, rowIndex, field)
463
+ },
464
+ renderer: function (value, column, record, rowIndex, field) {
465
+ var type = record.monitorType !== 'board' ? 'string' : 'board'
466
+ return getRenderer(type)(value, column, record, rowIndex, field)
467
+ }
468
+ },
469
+ width: 140
470
+ },
471
+ {
472
+ type: 'select',
473
+ name: 'reportType',
474
+ label: true,
475
+ header: i18next.t('field.report-type'),
476
+ record: {
477
+ editable: true,
478
+ options: REPORT_TYPES
479
+ },
480
+ width: 80
481
+ },
482
+ {
483
+ type: 'string',
484
+ name: 'reportView',
485
+ header: i18next.t('field.report-view'),
486
+ record: {
487
+ editable: true,
488
+ editor: function (value, column, record, rowIndex, field) {
489
+ var type = record.reportType !== 'custom' ? 'string' : 'script'
490
+ return getEditor(type)(value, column, record, rowIndex, field)
491
+ },
492
+ renderer: function (value, column, record, rowIndex, field) {
493
+ var type = record.reportType !== 'custom' ? 'string' : 'string'
494
+ return getRenderer(type)(value, column, record, rowIndex, field)
495
+ }
496
+ },
497
+ width: 140
498
+ },
499
+ {
500
+ type: 'file',
501
+ name: 'reportTemplate',
502
+ header: i18next.t('field.report-template'),
503
+ record: {
504
+ editable: true
505
+ },
506
+ width: 80
507
+ },
508
+ {
509
+ type: 'resource-object',
510
+ name: 'updater',
511
+ header: i18next.t('field.updater'),
512
+ record: {
513
+ editable: false
514
+ },
515
+ sortable: true,
516
+ width: 120
517
+ },
518
+ {
519
+ type: 'datetime',
520
+ name: 'updatedAt',
521
+ header: i18next.t('field.updated_at'),
522
+ record: {
523
+ editable: false
524
+ },
525
+ sortable: true,
526
+ width: 180
527
+ }
528
+ ],
529
+ rows: {
530
+ selectable: {
531
+ multiple: true
532
+ }
533
+ },
534
+ sorters: [
535
+ {
536
+ name: 'name'
537
+ }
538
+ ]
539
+ }
540
+ }
541
+
542
+ async fetchHandler({ page, limit, sortings = [], filters = [] }) {
543
+ const response = await client.query({
544
+ query: gql`
545
+ query ($filters: [Filter!], $pagination: Pagination, $sortings: [Sorting!]) {
546
+ responses: dataSets(filters: $filters, pagination: $pagination, sortings: $sortings) {
547
+ items {
548
+ id
549
+ name
550
+ description
551
+ partitionKeys
552
+ active
553
+ type
554
+ useCase
555
+ schedule
556
+ timezone
557
+ entryRole {
558
+ id
559
+ name
560
+ }
561
+ supervisoryRole {
562
+ id
563
+ name
564
+ }
565
+ entryType
566
+ entryView
567
+ monitorType
568
+ monitorView
569
+ reportType
570
+ reportView
571
+ reportTemplate
572
+ updater {
573
+ id
574
+ name
575
+ }
576
+ updatedAt
577
+ dataItems {
578
+ name
579
+ description
580
+ sequence
581
+ active
582
+ tag
583
+ type
584
+ unit
585
+ options
586
+ quota
587
+ spec
588
+ }
589
+ }
590
+ total
591
+ }
592
+ }
593
+ `,
594
+ variables: {
595
+ filters,
596
+ pagination: { page, limit },
597
+ sortings
598
+ }
599
+ })
600
+
601
+ return {
602
+ total: response.data.responses.total || 0,
603
+ records: response.data.responses.items || []
604
+ }
605
+ }
606
+
607
+ async _deleteDataSet() {
608
+ if (confirm(i18next.t('text.sure_to_x', { x: i18next.t('text.delete') }))) {
609
+ const ids = this.grist.selected.map(record => record.id)
610
+ if (ids && ids.length > 0) {
611
+ const response = await client.mutate({
612
+ mutation: gql`
613
+ mutation ($ids: [String!]!) {
614
+ deleteDataSets(ids: $ids)
615
+ }
616
+ `,
617
+ variables: {
618
+ ids
619
+ }
620
+ })
621
+
622
+ if (!response.errors) {
623
+ this.grist.fetch()
624
+ notify({
625
+ message: i18next.t('text.info_x_successfully', { x: i18next.t('text.delete') })
626
+ })
627
+ }
628
+ }
629
+ }
630
+ }
631
+
632
+ async _copyDataSet() {
633
+ var selected = this.grist.selected
634
+ if (selected.length == 0) return
635
+
636
+ if (!confirm(i18next.t('text.sure_to_x', { x: i18next.t('text.copy') }))) return
637
+ var response = await client.mutate({
638
+ mutation: gql`
639
+ mutation ($ids: [String!]!) {
640
+ copyDataSets(ids: $ids) {
641
+ id
642
+ }
643
+ }
644
+ `,
645
+ variables: {
646
+ ids: selected.map(r => r.id)
647
+ }
648
+ })
649
+
650
+ if (!response.errors) {
651
+ this.grist.fetch()
652
+ }
653
+ }
654
+
655
+ async _updateDataSet() {
656
+ let patches = this.grist.dirtyRecords
657
+ if (patches && patches.length) {
658
+ patches = patches.map(patch => {
659
+ let patchField = patch.id ? { id: patch.id } : {}
660
+ const dirtyFields = patch.__dirtyfields__
661
+ for (let key in dirtyFields) {
662
+ patchField[key] = dirtyFields[key].after
663
+ }
664
+ if (patchField['reportTemplate'] instanceof FileList) {
665
+ patchField['reportTemplate'] = patchField['reportTemplate'][0]
666
+ }
667
+ patchField.cuFlag = patch.__dirty__
668
+
669
+ return patchField
670
+ })
671
+
672
+ const response = await client.mutate({
673
+ mutation: gql`
674
+ mutation ($patches: [DataSetPatch!]!) {
675
+ updateMultipleDataSet(patches: $patches) {
676
+ name
677
+ }
678
+ }
679
+ `,
680
+ variables: {
681
+ patches
682
+ },
683
+ context: {
684
+ hasUpload: true
685
+ }
686
+ })
687
+
688
+ if (!response.errors) {
689
+ this.grist.fetch()
690
+ }
691
+ }
692
+ }
693
+
694
+ async exportHandler() {
695
+ var headerSetting = this.grist._config.columns
696
+ .filter(column => column.type !== 'gutter' && column.record !== undefined && column.imex !== undefined)
697
+ .map(column => {
698
+ return column.imex
699
+ })
700
+
701
+ let records = this.grist.data.records
702
+
703
+ var data = records.map(item => {
704
+ return {
705
+ id: item.id,
706
+ ...this.grist._config.columns
707
+ .filter(column => column.type !== 'gutter' && column.record !== undefined && column.imex !== undefined)
708
+ .reduce((record, column) => {
709
+ record[column.imex.key] = column.imex.key
710
+ .split('.')
711
+ .reduce((obj, key) => (obj && obj[key] !== 'undefined' ? obj[key] : undefined), item)
712
+ return record
713
+ }, {})
714
+ }
715
+ })
716
+ return { header: headerSetting, data: data }
717
+ }
718
+
719
+ async importHandler(records) {
720
+ openPopup(
721
+ html`
722
+ <data-set-importer
723
+ .dataSets=${records}
724
+ @imported=${() => {
725
+ history.back()
726
+ this.grist.fetch()
727
+ }}
728
+ ></data-set-importer>
729
+ `,
730
+ {
731
+ backdrop: true,
732
+ size: 'large',
733
+ title: i18next.t('title.import data-set')
734
+ }
735
+ )
736
+ }
737
+ }
738
+
739
+ window.customElements.define('data-set-list-page', DataSetListPage)