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

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