@things-factory/dataset 5.0.0-alpha.4 → 5.0.0-alpha.42

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