@things-factory/dataset 8.0.0-beta.9 → 8.0.2

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 (134) hide show
  1. package/client/activities/activity-data-collect-edit.ts +105 -0
  2. package/client/activities/activity-data-collect-view.ts +91 -0
  3. package/client/activities/activity-data-review-edit.ts +278 -0
  4. package/client/activities/activity-data-review-view.ts +226 -0
  5. package/client/activities/activity-ooc-resolve-edit.ts +195 -0
  6. package/client/activities/activity-ooc-resolve-view.ts +143 -0
  7. package/client/activities/activity-ooc-review-edit.ts +173 -0
  8. package/client/activities/activity-ooc-review-view.ts +129 -0
  9. package/client/bootstrap.ts +35 -0
  10. package/client/components/data-entry-form.ts +109 -0
  11. package/client/index.ts +1 -0
  12. package/client/pages/data-archive/data-archive-list-page.ts +277 -0
  13. package/client/pages/data-archive/data-archive-request-popup.ts +177 -0
  14. package/client/pages/data-entry/data-entry-list-page.ts +464 -0
  15. package/client/pages/data-key-set/data-key-item-list.ts +183 -0
  16. package/client/pages/data-key-set/data-key-set-importer.ts +89 -0
  17. package/client/pages/data-key-set/data-key-set-list-page.ts +413 -0
  18. package/client/pages/data-ooc/data-ooc-list-page.ts +549 -0
  19. package/client/pages/data-ooc/data-ooc-page.ts +164 -0
  20. package/client/pages/data-ooc/data-ooc-view.ts +236 -0
  21. package/client/pages/data-ooc/data-oocs-page.ts +200 -0
  22. package/client/pages/data-report/data-report-embed-page.ts +108 -0
  23. package/client/pages/data-report/data-report-list-page.ts +454 -0
  24. package/client/pages/data-report/data-report-samples-page.ts +174 -0
  25. package/client/pages/data-report/jasper-report-oocs-page.ts +110 -0
  26. package/client/pages/data-report/jasper-report-samples-crosstab-page.ts +110 -0
  27. package/client/pages/data-report/jasper-report-samples-page.ts +110 -0
  28. package/client/pages/data-sample/data-sample-list-page.ts +442 -0
  29. package/client/pages/data-sample/data-sample-page.ts +55 -0
  30. package/client/pages/data-sample/data-sample-search-page.ts +424 -0
  31. package/client/pages/data-sample/data-sample-view.ts +292 -0
  32. package/client/pages/data-sample/data-samples-page.ts +249 -0
  33. package/client/pages/data-sensor/data-sensor-list-page.ts +456 -0
  34. package/client/pages/data-set/data-item-list.ts +304 -0
  35. package/client/pages/data-set/data-set-importer.ts +89 -0
  36. package/client/pages/data-set/data-set-list-page.ts +1078 -0
  37. package/client/pages/data-summary/data-summary-list-page.ts +363 -0
  38. package/client/pages/data-summary/data-summary-period-page.ts +439 -0
  39. package/client/pages/data-summary/data-summary-search-page.ts +426 -0
  40. package/client/pages/data-summary/data-summary-view.ts +133 -0
  41. package/client/route.ts +91 -0
  42. package/client/tsconfig.json +13 -0
  43. package/dist-client/activities/activity-data-review-edit.js +19 -10
  44. package/dist-client/activities/activity-data-review-edit.js.map +1 -1
  45. package/dist-client/activities/activity-data-review-view.js +80 -0
  46. package/dist-client/activities/activity-data-review-view.js.map +1 -1
  47. package/dist-client/pages/data-entry/data-entry-list-page.js +2 -2
  48. package/dist-client/pages/data-entry/data-entry-list-page.js.map +1 -1
  49. package/dist-client/tsconfig.tsbuildinfo +1 -1
  50. package/dist-server/controllers/create-data-ooc.js +2 -0
  51. package/dist-server/controllers/create-data-ooc.js.map +1 -1
  52. package/dist-server/service/data-archive/index.d.ts +1 -1
  53. package/dist-server/service/data-ooc/index.d.ts +1 -1
  54. package/dist-server/service/data-sample/data-sample-query.d.ts +1 -1
  55. package/dist-server/service/data-sample/data-sample-query.js +3 -3
  56. package/dist-server/service/data-sample/data-sample-query.js.map +1 -1
  57. package/dist-server/service/data-sample/index.d.ts +1 -1
  58. package/dist-server/service/data-set/index.d.ts +1 -1
  59. package/dist-server/service/index.d.ts +2 -2
  60. package/dist-server/tsconfig.tsbuildinfo +1 -1
  61. package/package.json +26 -26
  62. package/server/activities/activity-data-collect.ts +100 -0
  63. package/server/activities/activity-data-review.ts +109 -0
  64. package/server/activities/activity-ooc-resolve.ts +123 -0
  65. package/server/activities/activity-ooc-review.ts +95 -0
  66. package/server/activities/index.ts +11 -0
  67. package/server/controllers/create-data-ooc.ts +80 -0
  68. package/server/controllers/create-data-sample.ts +323 -0
  69. package/server/controllers/data-use-case.ts +98 -0
  70. package/server/controllers/finalize-data-collection.ts +388 -0
  71. package/server/controllers/index.ts +6 -0
  72. package/server/controllers/issue-data-collection-task.ts +70 -0
  73. package/server/controllers/issue-ooc-resolve.ts +58 -0
  74. package/server/controllers/issue-ooc-review.ts +52 -0
  75. package/server/controllers/jasper-report.ts +186 -0
  76. package/server/controllers/query-data-summary-by-period.ts +178 -0
  77. package/server/controllers/shiny-report.ts +54 -0
  78. package/server/engine/index.ts +1 -0
  79. package/server/engine/task/create-data-sample.ts +100 -0
  80. package/server/engine/task/index.ts +2 -0
  81. package/server/engine/task/issue-collect-data.ts +45 -0
  82. package/server/index.ts +8 -0
  83. package/server/routes.ts +188 -0
  84. package/server/service/data-archive/data-archive-mutation.ts +273 -0
  85. package/server/service/data-archive/data-archive-query.ts +58 -0
  86. package/server/service/data-archive/data-archive-type.ts +48 -0
  87. package/server/service/data-archive/data-archive.ts +69 -0
  88. package/server/service/data-archive/index.ts +6 -0
  89. package/server/service/data-key-set/data-key-item-type.ts +31 -0
  90. package/server/service/data-key-set/data-key-set-mutation.ts +201 -0
  91. package/server/service/data-key-set/data-key-set-query.ts +68 -0
  92. package/server/service/data-key-set/data-key-set-type.ts +70 -0
  93. package/server/service/data-key-set/data-key-set.ts +86 -0
  94. package/server/service/data-key-set/index.ts +6 -0
  95. package/server/service/data-ooc/data-ooc-mutation.ts +154 -0
  96. package/server/service/data-ooc/data-ooc-query.ts +106 -0
  97. package/server/service/data-ooc/data-ooc-subscription.ts +48 -0
  98. package/server/service/data-ooc/data-ooc-type.ts +71 -0
  99. package/server/service/data-ooc/data-ooc.ts +259 -0
  100. package/server/service/data-ooc/index.ts +7 -0
  101. package/server/service/data-sample/data-sample-mutation.ts +18 -0
  102. package/server/service/data-sample/data-sample-query.ts +215 -0
  103. package/server/service/data-sample/data-sample-type.ts +47 -0
  104. package/server/service/data-sample/data-sample.ts +193 -0
  105. package/server/service/data-sample/index.ts +6 -0
  106. package/server/service/data-sensor/data-sensor-mutation.ts +116 -0
  107. package/server/service/data-sensor/data-sensor-query.ts +76 -0
  108. package/server/service/data-sensor/data-sensor-type.ts +104 -0
  109. package/server/service/data-sensor/data-sensor.ts +126 -0
  110. package/server/service/data-sensor/index.ts +6 -0
  111. package/server/service/data-set/data-item-type.ts +155 -0
  112. package/server/service/data-set/data-set-mutation.ts +552 -0
  113. package/server/service/data-set/data-set-query.ts +461 -0
  114. package/server/service/data-set/data-set-type.ts +204 -0
  115. package/server/service/data-set/data-set.ts +326 -0
  116. package/server/service/data-set/index.ts +6 -0
  117. package/server/service/data-set-history/data-set-history-query.ts +126 -0
  118. package/server/service/data-set-history/data-set-history-type.ts +12 -0
  119. package/server/service/data-set-history/data-set-history.ts +217 -0
  120. package/server/service/data-set-history/event-subscriber.ts +17 -0
  121. package/server/service/data-set-history/index.ts +7 -0
  122. package/server/service/data-spec/data-spec-manager.ts +21 -0
  123. package/server/service/data-spec/data-spec-query.ts +21 -0
  124. package/server/service/data-spec/data-spec.ts +45 -0
  125. package/server/service/data-spec/index.ts +5 -0
  126. package/server/service/data-summary/data-summary-mutation.ts +45 -0
  127. package/server/service/data-summary/data-summary-query.ts +179 -0
  128. package/server/service/data-summary/data-summary-type.ts +86 -0
  129. package/server/service/data-summary/data-summary.ts +170 -0
  130. package/server/service/data-summary/index.ts +7 -0
  131. package/server/service/index.ts +57 -0
  132. package/server/tsconfig.json +10 -0
  133. package/server/utils/config-resolver.ts +29 -0
  134. package/server/utils/index.ts +1 -0
@@ -0,0 +1,464 @@
1
+ import '@material/web/icon/icon.js'
2
+ import '@operato/data-grist'
3
+ import '@operato/context/ox-context-page-toolbar.js'
4
+ import '@operato/board/ox-board-viewer.js'
5
+ import '../../components/data-entry-form.js'
6
+
7
+ import gql from 'graphql-tag'
8
+ import { css, html } from 'lit'
9
+ import { customElement, property, query, state } from 'lit/decorators.js'
10
+
11
+ import { connect } from 'pwa-helpers/connect-mixin'
12
+
13
+ import { ColumnConfig, DataGrist, FetchOption, SortersControl, getRenderer } from '@operato/data-grist'
14
+ import { OxDataUseCase } from '@operato/dataset'
15
+ import { client } from '@operato/graphql'
16
+ import { i18next, localize } from '@operato/i18n'
17
+ import { openPopup, notify } from '@operato/layout'
18
+ import { InheritedValueType, navigate, PageView, store } from '@operato/shell'
19
+ import { CommonHeaderStyles, ScrollbarStyles } from '@operato/styles'
20
+ import { provider } from '@things-factory/board-ui/dist-client'
21
+ import { OxPopup } from '@operato/popup'
22
+
23
+ const USECASE_OPTIONS = () => {
24
+ return ['', ...OxDataUseCase.getUseCaseNames()].map(name => {
25
+ return {
26
+ display: name,
27
+ value: name
28
+ }
29
+ })
30
+ }
31
+
32
+ const showEntryView = async (columns, data, column, record, rowIndex) => {
33
+ if (!record.isEntryAllowed) {
34
+ notify({
35
+ message: 'You are not allowed to enter data for this dataset',
36
+ level: 'error'
37
+ })
38
+ return
39
+ }
40
+
41
+ const { name, entryType, entryView } = record
42
+ const title = `${name} - ${i18next.t('title.data-entry-form')}`
43
+
44
+ switch (entryType) {
45
+ case 'generated':
46
+ openPopup(html` <data-entry-form .dataSet=${record} style="background-color: white;"></data-entry-form> `, {
47
+ closable: true,
48
+ backdrop: true,
49
+ size: 'large',
50
+ title
51
+ })
52
+ break
53
+
54
+ case 'board':
55
+ const board = {
56
+ id: entryView
57
+ }
58
+ openPopup(
59
+ html`
60
+ <ox-board-viewer
61
+ style="background-color: white;"
62
+ .board=${board}
63
+ .provider=${provider}
64
+ hide-fullscreen
65
+ hide-navigation
66
+ ></ox-board-viewer>
67
+ `,
68
+ {
69
+ closable: true,
70
+ backdrop: true,
71
+ size: 'large',
72
+ title
73
+ }
74
+ )
75
+
76
+ // navigate(`board-viewer/${entryView}?interactive=true&title=${title}`)
77
+ break
78
+
79
+ case 'custom-element':
80
+ break
81
+
82
+ case 'page':
83
+ navigate(entryView)
84
+ break
85
+
86
+ case 'external':
87
+ window.open(entryView, '_blank')
88
+ break
89
+ }
90
+ }
91
+
92
+ const issueDataEntry = async (columns, data, column, record, rowIndex) => {
93
+ if (!record.isSupervisor) {
94
+ notify({
95
+ message: 'You are not allowed to issue data collection for this dataset',
96
+ level: 'error'
97
+ })
98
+ return
99
+ }
100
+
101
+ const response = await client.mutate({
102
+ mutation: gql`
103
+ mutation ($dataSetId: String!) {
104
+ issueDataCollection(dataSetId: $dataSetId)
105
+ }
106
+ `,
107
+ variables: { dataSetId: record.id }
108
+ })
109
+
110
+ if (response.errors) {
111
+ notify({
112
+ message: response.errors[0].message,
113
+ level: 'error'
114
+ })
115
+ } else {
116
+ notify({
117
+ message: 'Data entry task issued successfully'
118
+ })
119
+ }
120
+ }
121
+
122
+ @customElement('data-entry-list-page')
123
+ export class DataEntryListPage extends connect(store)(localize(i18next)(PageView)) {
124
+ static styles = [
125
+ ScrollbarStyles,
126
+ CommonHeaderStyles,
127
+ css`
128
+ :host {
129
+ display: flex;
130
+
131
+ width: 100%;
132
+
133
+ --grid-record-emphasized-background-color: #8b0000;
134
+ --grid-record-emphasized-color: #ff6b6b;
135
+ }
136
+
137
+ ox-grist {
138
+ overflow-y: auto;
139
+ flex: 1;
140
+ }
141
+
142
+ .header {
143
+ grid-template-areas: 'filters actions';
144
+ }
145
+ `
146
+ ]
147
+
148
+ @state() private gristConfig: any
149
+
150
+ @query('ox-grist') private grist!: DataGrist
151
+
152
+ get context() {
153
+ return {
154
+ title: i18next.t('title.data-entry list'),
155
+ search: {
156
+ handler: (search: string) => {
157
+ this.grist.searchText = search
158
+ },
159
+ value: this.grist?.searchText || ''
160
+ },
161
+ filter: {
162
+ handler: () => {
163
+ this.grist.toggleHeadroom()
164
+ }
165
+ },
166
+ help: 'dataset/data-entry-list',
167
+ toolbar: false
168
+ }
169
+ }
170
+
171
+ render() {
172
+ return html`
173
+ <ox-grist
174
+ mode="CARD"
175
+ .config=${this.gristConfig}
176
+ .fetchHandler=${this.fetchHandler.bind(this)}
177
+ ?url-params-sensitive=${this.active}
178
+ >
179
+ <div slot="headroom" class="header">
180
+ <div class="filters">
181
+ <ox-filters-form autofocus without-search></ox-filters-form>
182
+ </div>
183
+ </div>
184
+ </ox-grist>
185
+ `
186
+ }
187
+
188
+ async pageInitialized(lifecycle) {
189
+ this.gristConfig = {
190
+ list: {
191
+ thumbnail: 'entryView',
192
+ fields: ['name', 'description'],
193
+ details: ['schedule', 'type', 'useCase', 'latestCollectedAt', 'prevSchedule', 'nextSchedule']
194
+ },
195
+ columns: [
196
+ {
197
+ type: 'gutter',
198
+ gutterName: 'button',
199
+ icon: record => (record?.isSupervisor || record?.isEntryAllowed ? 'assignment_add' : ''),
200
+ iconOnly: false,
201
+ width: 96,
202
+ fixed: true,
203
+ title: record =>
204
+ record?.isSupervisor || record?.isEntryAllowed ? i18next.t('button.assign-data-collection') : '',
205
+ handlers: {
206
+ click: issueDataEntry
207
+ }
208
+ },
209
+ {
210
+ type: 'gutter',
211
+ gutterName: 'button',
212
+ icon: record => (record?.isEntryAllowed ? 'fact_check' : ''),
213
+ iconOnly: false,
214
+ width: 96,
215
+ fixed: true,
216
+ title: record => (record?.isEntryAllowed ? i18next.t('button.enter-data') : ''),
217
+ handlers: {
218
+ click: showEntryView
219
+ }
220
+ },
221
+ {
222
+ type: 'string',
223
+ name: 'name',
224
+ header: i18next.t('field.name'),
225
+ record: {
226
+ editable: false
227
+ },
228
+ filter: 'search',
229
+ sortable: true,
230
+ width: 150
231
+ },
232
+ {
233
+ type: 'string',
234
+ name: 'description',
235
+ header: i18next.t('field.description'),
236
+ record: {
237
+ editable: false
238
+ },
239
+ filter: 'search',
240
+ width: 200
241
+ },
242
+ {
243
+ type: 'select',
244
+ name: 'type',
245
+ label: true,
246
+ header: i18next.t('field.type'),
247
+ record: {
248
+ editable: false,
249
+ options: [
250
+ {},
251
+ {
252
+ display: i18next.t('text.manually collected'),
253
+ value: 'manual'
254
+ },
255
+ {
256
+ display: i18next.t('text.automatically collected'),
257
+ value: 'automatic'
258
+ }
259
+ ]
260
+ },
261
+ sortable: true,
262
+ filter: true,
263
+ width: 60
264
+ },
265
+ {
266
+ type: 'select',
267
+ name: 'useCase',
268
+ label: true,
269
+ header: i18next.t('field.use-case'),
270
+ record: {
271
+ editable: false,
272
+ options: USECASE_OPTIONS
273
+ },
274
+ sortable: true,
275
+ filter: {
276
+ operator: 'eq',
277
+ options: USECASE_OPTIONS /* in case select options type is a function, filter should have its own options */
278
+ },
279
+ width: 80
280
+ },
281
+ {
282
+ type: 'crontab',
283
+ name: 'schedule',
284
+ label: true,
285
+ header: i18next.t('field.schedule'),
286
+ record: {
287
+ editable: false
288
+ },
289
+ width: 80
290
+ },
291
+ {
292
+ type: 'resource-object',
293
+ name: 'entryRole',
294
+ header: i18next.t('field.entry-role'),
295
+ record: {
296
+ editable: false
297
+ },
298
+ width: 120
299
+ },
300
+ {
301
+ type: 'resource-object',
302
+ name: 'supervisoryRole',
303
+ header: i18next.t('field.supervisory-role'),
304
+ record: {
305
+ editable: false
306
+ },
307
+ width: 120
308
+ },
309
+ {
310
+ type: 'datetime',
311
+ name: 'latestCollectedAt',
312
+ label: true,
313
+ header: i18next.t('field.latest-collected-at'),
314
+ record: {
315
+ editable: false
316
+ },
317
+ width: 180
318
+ },
319
+ {
320
+ type: 'datetime',
321
+ name: 'prevSchedule',
322
+ label: true,
323
+ header: i18next.t('field.prev-schedule'),
324
+ record: {
325
+ editable: false
326
+ },
327
+ width: 180
328
+ },
329
+ {
330
+ type: 'datetime',
331
+ name: 'nextSchedule',
332
+ label: true,
333
+ header: i18next.t('field.next-schedule'),
334
+ record: {
335
+ editable: false
336
+ },
337
+ width: 180
338
+ },
339
+ {
340
+ type: 'string',
341
+ name: 'entryView',
342
+ hidden: true,
343
+ record: {
344
+ editable: false,
345
+ renderer: function (value, column, record, rowIndex, field) {
346
+ const type = record.entryType !== 'board' ? 'string' : 'image'
347
+
348
+ let renderedValue
349
+
350
+ if (record.entryType === 'board' && record.entryBoard?.thumbnail) {
351
+ renderedValue = record.entryBoard.thumbnail
352
+ } else {
353
+ renderedValue = html`<md-icon style="--md-icon-size: 64px;">assignment</md-icon>`
354
+ }
355
+
356
+ return getRenderer(type)(renderedValue, column, record, rowIndex, field)
357
+ }
358
+ },
359
+ handlers: {
360
+ click: (columns, data, column, record, rowIndex) => {
361
+ if (record?.isEntryAllowed) {
362
+ showEntryView(columns, data, column, record, rowIndex)
363
+ }
364
+ }
365
+ }
366
+ }
367
+ ],
368
+ rows: {
369
+ appendable: false,
370
+ editable: false,
371
+ selectable: {
372
+ multiple: false
373
+ },
374
+ classifier: function (record, rowIndex) {}
375
+ },
376
+ sorters: [
377
+ {
378
+ name: 'name'
379
+ }
380
+ ]
381
+ }
382
+ }
383
+
384
+ async fetchHandler({ page, limit, sortings = [], filters = [] }: FetchOption) {
385
+ const response = await client.query({
386
+ query: gql`
387
+ query ($filters: [Filter!], $pagination: Pagination, $sortings: [Sorting!], $inherited: InheritedValueType) {
388
+ responses: dataSetsForEntry(
389
+ filters: $filters
390
+ pagination: $pagination
391
+ sortings: $sortings
392
+ inherited: $inherited
393
+ ) {
394
+ items {
395
+ id
396
+ name
397
+ description
398
+ partitionKeys
399
+ active
400
+ type
401
+ useCase
402
+ schedule
403
+ timezone
404
+ isSupervisor
405
+ isEntryAllowed
406
+ entryRole {
407
+ id
408
+ name
409
+ }
410
+ supervisoryRole {
411
+ id
412
+ name
413
+ }
414
+ entryType
415
+ entryView
416
+ entryBoard {
417
+ thumbnail
418
+ }
419
+ monitorType
420
+ monitorView
421
+ updater {
422
+ id
423
+ name
424
+ }
425
+ reportType
426
+ reportView
427
+ updatedAt
428
+ dataItems {
429
+ name
430
+ description
431
+ active
432
+ hidden
433
+ tag
434
+ group
435
+ type
436
+ unit
437
+ options
438
+ quota
439
+ spec
440
+ stat
441
+ }
442
+ latestCollectedAt
443
+ nextSchedule
444
+ prevSchedule
445
+ nextSummarySchedule
446
+ }
447
+ total
448
+ }
449
+ }
450
+ `,
451
+ variables: {
452
+ filters,
453
+ pagination: { page, limit },
454
+ sortings,
455
+ inherited: InheritedValueType.Include
456
+ }
457
+ })
458
+
459
+ return {
460
+ total: response.data.responses.total || 0,
461
+ records: response.data.responses.items || []
462
+ }
463
+ }
464
+ }
@@ -0,0 +1,183 @@
1
+ import '@material/web/icon/icon.js'
2
+
3
+ import gql from 'graphql-tag'
4
+ import { css, html, LitElement } from 'lit'
5
+ import { customElement, property, query, state } from 'lit/decorators.js'
6
+
7
+ import { client } from '@operato/graphql'
8
+ import { i18next, localize } from '@operato/i18n'
9
+ import { isMobileDevice } from '@operato/utils'
10
+ import { DataGrist, FetchOption } from '@operato/data-grist'
11
+ import { CommonHeaderStyles } from '@operato/styles'
12
+
13
+ @customElement('data-key-item-list')
14
+ export class DataKeyItemList extends localize(i18next)(LitElement) {
15
+ static styles = [
16
+ CommonHeaderStyles,
17
+ css`
18
+ :host {
19
+ display: flex;
20
+ flex-direction: column;
21
+
22
+ background-color: var(--md-sys-color-surface);
23
+ }
24
+
25
+ ox-grist {
26
+ flex: 1;
27
+ }
28
+ `
29
+ ]
30
+
31
+ @property({ type: Object }) dataKeySet: any
32
+ @state() gristConfig: any
33
+ @query('ox-grist') private grist!: DataGrist
34
+
35
+ render() {
36
+ return html`
37
+ <ox-grist
38
+ .mode=${isMobileDevice() ? 'LIST' : 'GRID'}
39
+ .config=${this.gristConfig}
40
+ .fetchHandler=${this.fetchHandler.bind(this)}
41
+ ></ox-grist>
42
+
43
+ <div class="footer">
44
+ <div filler></div>
45
+ <button danger @click=${this._deleteDataKeyItems.bind(this)}>
46
+ <md-icon>delete_forever</md-icon>${i18next.t('button.delete')}
47
+ </button>
48
+ <button @click=${this._updateDataKeyItems.bind(this)} done>
49
+ <md-icon>save</md-icon>${i18next.t('button.save')}
50
+ </button>
51
+ </div>
52
+ `
53
+ }
54
+
55
+ async firstUpdated() {
56
+ this.gristConfig = {
57
+ list: { fields: ['name', 'description', 'active'] },
58
+ columns: [
59
+ { type: 'gutter', gutterName: 'row-selector', multiple: true },
60
+ {
61
+ type: 'gutter',
62
+ gutterName: 'button',
63
+ icon: 'add',
64
+ handlers: {
65
+ click: 'record-copy'
66
+ }
67
+ },
68
+ { type: 'gutter', gutterName: 'sequence' },
69
+ {
70
+ type: 'gutter',
71
+ gutterName: 'button',
72
+ icon: 'arrow_upward',
73
+ handlers: {
74
+ click: 'move-up'
75
+ }
76
+ },
77
+ {
78
+ type: 'gutter',
79
+ gutterName: 'button',
80
+ icon: 'arrow_downward',
81
+ handlers: {
82
+ click: 'move-down'
83
+ }
84
+ },
85
+ {
86
+ type: 'string',
87
+ name: 'name',
88
+ header: i18next.t('field.name'),
89
+ record: {
90
+ editable: true
91
+ },
92
+ width: 140
93
+ },
94
+ {
95
+ type: 'string',
96
+ name: 'description',
97
+ header: i18next.t('field.description'),
98
+ record: {
99
+ editable: true
100
+ },
101
+ width: 180
102
+ },
103
+ {
104
+ type: 'string',
105
+ name: 'dataKey',
106
+ header: i18next.t('field.data-key'),
107
+ record: {
108
+ editable: true
109
+ },
110
+ width: 180
111
+ },
112
+ {
113
+ type: 'string',
114
+ name: 'tKey',
115
+ header: i18next.t('field.t-key'),
116
+ record: {
117
+ editable: true
118
+ },
119
+ width: 180
120
+ }
121
+ ],
122
+ rows: {
123
+ selectable: {
124
+ multiple: true
125
+ }
126
+ },
127
+ pagination: {
128
+ infinite: true
129
+ },
130
+ sorters: []
131
+ }
132
+ }
133
+
134
+ async fetchHandler({ filters, page, limit, sortings = [] }: FetchOption) {
135
+ const dataKeyItems = this.dataKeySet.dataKeyItems || []
136
+
137
+ return {
138
+ total: dataKeyItems.length,
139
+ records: dataKeyItems
140
+ }
141
+ }
142
+
143
+ async _updateDataKeyItems() {
144
+ this.grist.commit()
145
+
146
+ const response = await client.mutate({
147
+ mutation: gql`
148
+ mutation ($id: String!, $patch: DataKeySetPatch!) {
149
+ updateDataKeySet(id: $id, patch: $patch) {
150
+ name
151
+ }
152
+ }
153
+ `,
154
+ variables: {
155
+ id: this.dataKeySet.id,
156
+ patch: {
157
+ dataKeyItems: this.grist.data.records,
158
+ cuFlag: 'M'
159
+ }
160
+ }
161
+ })
162
+
163
+ if (!response.errors) {
164
+ await document.dispatchEvent(
165
+ new CustomEvent('notify', {
166
+ detail: {
167
+ message: i18next.t('text.info_x_successfully', {
168
+ x: i18next.t('button.save')
169
+ })
170
+ }
171
+ })
172
+ )
173
+ }
174
+ }
175
+
176
+ async _deleteDataKeyItems() {
177
+ if (!confirm(i18next.t('text.sure_to_x', { x: i18next.t('text.delete') }))) {
178
+ return
179
+ }
180
+
181
+ this.grist.deleteSelectedRecords(false)
182
+ }
183
+ }