@things-factory/dataset 5.0.0-alpha.3 → 5.0.0-alpha.30

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 (98) hide show
  1. package/README.md +13 -0
  2. package/assets/data-samples.jpg +0 -0
  3. package/client/bootstrap.js +16 -1
  4. package/client/pages/data-entry/data-entry-form.js +85 -0
  5. package/client/pages/data-entry/data-entry-list-page.js +464 -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-sample.js → data-sample/data-sample-list-page.js} +153 -59
  9. package/client/pages/data-sample/data-sample-view.js +97 -0
  10. package/client/pages/{data-sensor.js → data-sensor/data-sensor-list-page.js} +11 -16
  11. package/client/pages/{data-item-list.js → data-set/data-item-list.js} +70 -16
  12. package/client/pages/{data-set-importer.js → data-set/data-set-importer.js} +0 -0
  13. package/client/pages/{data-set.js → data-set/data-set-list-page.js} +282 -76
  14. package/client/route.js +14 -6
  15. package/dist-server/controllers/create-data-sample.js +133 -0
  16. package/dist-server/controllers/create-data-sample.js.map +1 -0
  17. package/dist-server/controllers/data-use-case.js +57 -0
  18. package/dist-server/controllers/data-use-case.js.map +1 -0
  19. package/dist-server/controllers/index.js +17 -0
  20. package/dist-server/controllers/index.js.map +1 -1
  21. package/dist-server/index.js +2 -0
  22. package/dist-server/index.js.map +1 -1
  23. package/dist-server/routes.js +9 -24
  24. package/dist-server/routes.js.map +1 -1
  25. package/dist-server/service/data-item/data-item-mutation.js +5 -1
  26. package/dist-server/service/data-item/data-item-mutation.js.map +1 -1
  27. package/dist-server/service/data-item/data-item-type.js +18 -6
  28. package/dist-server/service/data-item/data-item-type.js.map +1 -1
  29. package/dist-server/service/data-item/data-item.js +27 -7
  30. package/dist-server/service/data-item/data-item.js.map +1 -1
  31. package/dist-server/service/data-ooc/data-ooc-mutation.js +92 -0
  32. package/dist-server/service/data-ooc/data-ooc-mutation.js.map +1 -0
  33. package/dist-server/service/data-ooc/data-ooc-query.js +115 -0
  34. package/dist-server/service/data-ooc/data-ooc-query.js.map +1 -0
  35. package/dist-server/service/data-ooc/data-ooc-subscription.js +65 -0
  36. package/dist-server/service/data-ooc/data-ooc-subscription.js.map +1 -0
  37. package/dist-server/service/data-ooc/data-ooc-type.js +107 -0
  38. package/dist-server/service/data-ooc/data-ooc-type.js.map +1 -0
  39. package/dist-server/service/data-ooc/data-ooc.js +237 -0
  40. package/dist-server/service/data-ooc/data-ooc.js.map +1 -0
  41. package/dist-server/service/data-ooc/index.js +10 -0
  42. package/dist-server/service/data-ooc/index.js.map +1 -0
  43. package/dist-server/service/data-sample/data-sample-mutation.js +3 -105
  44. package/dist-server/service/data-sample/data-sample-mutation.js.map +1 -1
  45. package/dist-server/service/data-sample/data-sample-type.js +12 -42
  46. package/dist-server/service/data-sample/data-sample-type.js.map +1 -1
  47. package/dist-server/service/data-sample/data-sample.js +34 -3
  48. package/dist-server/service/data-sample/data-sample.js.map +1 -1
  49. package/dist-server/service/data-set/data-set-mutation.js +1 -2
  50. package/dist-server/service/data-set/data-set-mutation.js.map +1 -1
  51. package/dist-server/service/data-set/data-set-query.js +110 -1
  52. package/dist-server/service/data-set/data-set-query.js.map +1 -1
  53. package/dist-server/service/data-set/data-set-type.js +48 -4
  54. package/dist-server/service/data-set/data-set-type.js.map +1 -1
  55. package/dist-server/service/data-set/data-set.js +59 -15
  56. package/dist-server/service/data-set/data-set.js.map +1 -1
  57. package/dist-server/service/data-spec/data-spec-manager.js +20 -0
  58. package/dist-server/service/data-spec/data-spec-manager.js.map +1 -0
  59. package/dist-server/service/data-spec/data-spec-query.js +48 -0
  60. package/dist-server/service/data-spec/data-spec-query.js.map +1 -0
  61. package/dist-server/service/data-spec/data-spec.js +78 -0
  62. package/dist-server/service/data-spec/data-spec.js.map +1 -0
  63. package/dist-server/service/data-spec/index.js +8 -0
  64. package/dist-server/service/data-spec/index.js.map +1 -0
  65. package/dist-server/service/index.js +12 -4
  66. package/dist-server/service/index.js.map +1 -1
  67. package/package.json +18 -12
  68. package/server/controllers/create-data-sample.ts +175 -0
  69. package/server/controllers/data-use-case.ts +85 -0
  70. package/server/controllers/index.ts +1 -0
  71. package/server/index.ts +3 -0
  72. package/server/routes.ts +17 -31
  73. package/server/service/data-item/data-item-mutation.ts +6 -1
  74. package/server/service/data-item/data-item-type.ts +13 -6
  75. package/server/service/data-item/data-item.ts +21 -5
  76. package/server/service/data-ooc/data-ooc-mutation.ts +150 -0
  77. package/server/service/data-ooc/data-ooc-query.ts +63 -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 +6 -128
  83. package/server/service/data-sample/data-sample-type.ts +7 -28
  84. package/server/service/data-sample/data-sample.ts +33 -3
  85. package/server/service/data-set/data-set-mutation.ts +1 -4
  86. package/server/service/data-set/data-set-query.ts +87 -2
  87. package/server/service/data-set/data-set-type.ts +37 -4
  88. package/server/service/data-set/data-set.ts +52 -12
  89. package/server/service/data-spec/data-spec-manager.ts +21 -0
  90. package/server/service/data-spec/data-spec-query.ts +21 -0
  91. package/server/service/data-spec/data-spec.ts +44 -0
  92. package/server/service/data-spec/index.ts +5 -0
  93. package/server/service/index.ts +16 -8
  94. package/things-factory.config.js +14 -6
  95. package/translations/en.json +30 -0
  96. package/translations/ko.json +30 -1
  97. package/translations/ms.json +29 -0
  98. package/translations/zh.json +29 -0
@@ -1,4 +1,5 @@
1
1
  import '@operato/data-grist'
2
+ import './data-sample-view.js'
2
3
 
3
4
  import gql from 'graphql-tag'
4
5
  import { css, html } from 'lit'
@@ -6,12 +7,12 @@ import { connect } from 'pwa-helpers/connect-mixin'
6
7
 
7
8
  import { client } from '@operato/graphql'
8
9
  import { i18next, localize } from '@operato/i18n'
9
- import { notify } from '@operato/layout'
10
+ import { openPopup } from '@operato/layout'
10
11
  import { PageView, store } from '@operato/shell'
11
- import { CommonButtonStyles, ScrollbarStyles } from '@operato/styles'
12
+ import { ScrollbarStyles } from '@operato/styles'
12
13
  import { isMobileDevice } from '@operato/utils'
13
14
 
14
- export class DataSample extends connect(store)(localize(i18next)(PageView)) {
15
+ export class DataSampleListPage extends connect(store)(localize(i18next)(PageView)) {
15
16
  static get properties() {
16
17
  return {
17
18
  active: String,
@@ -53,14 +54,12 @@ export class DataSample extends connect(store)(localize(i18next)(PageView)) {
53
54
  get context() {
54
55
  return {
55
56
  title: i18next.t('title.data-sample list'),
56
- help: 'integration/ui/data-sample',
57
- actions: [
58
- {
59
- title: i18next.t('button.delete'),
60
- action: this._deleteDataSample.bind(this),
61
- ...CommonButtonStyles.delete
62
- }
63
- ]
57
+ help: 'dataset/data-sample',
58
+ actions: [],
59
+ exportable: {
60
+ name: i18next.t('title.data-sample list'),
61
+ data: this._exportableData.bind(this)
62
+ }
64
63
  }
65
64
  }
66
65
 
@@ -111,17 +110,35 @@ export class DataSample extends connect(store)(localize(i18next)(PageView)) {
111
110
  columns: [
112
111
  { type: 'gutter', gutterName: 'sequence' },
113
112
  { type: 'gutter', gutterName: 'row-selector', multiple: true },
113
+ {
114
+ type: 'gutter',
115
+ gutterName: 'button',
116
+ icon: 'assignment',
117
+ handlers: {
118
+ click: (columns, data, column, record, rowIndex) => {
119
+ openPopup(
120
+ html` <data-sample-view .dataSample=${record} style="background-color: white;"></data-sample-view> `,
121
+ {
122
+ backdrop: true,
123
+ size: 'large',
124
+ title: i18next.t('title.data-sample view')
125
+ }
126
+ )
127
+ }
128
+ }
129
+ },
114
130
  {
115
131
  type: 'string',
116
132
  name: 'name',
117
133
  label: true,
118
134
  header: i18next.t('field.name'),
119
135
  record: {
120
- editable: true
136
+ editable: false
121
137
  },
122
138
  filter: 'search',
123
139
  sortable: true,
124
- width: 150
140
+ width: 120,
141
+ imex: true
125
142
  },
126
143
  {
127
144
  type: 'string',
@@ -129,32 +146,63 @@ export class DataSample extends connect(store)(localize(i18next)(PageView)) {
129
146
  label: true,
130
147
  header: i18next.t('field.description'),
131
148
  record: {
132
- editable: true
149
+ editable: false
133
150
  },
134
151
  filter: 'search',
135
- width: 200
152
+ width: 150,
153
+ imex: true
136
154
  },
137
155
  {
138
- type: 'object',
156
+ type: 'resource-object',
139
157
  name: 'dataSet',
140
158
  header: i18next.t('field.data-set'),
141
159
  record: {
142
160
  editable: false
143
161
  },
144
162
  sortable: true,
145
- width: 120
163
+ width: 120,
164
+ imex: true
165
+ },
166
+ {
167
+ type: 'json5',
168
+ name: 'partitionKeys',
169
+ header: i18next.t('field.partition-keys'),
170
+ record: {
171
+ editable: false
172
+ },
173
+ width: 200,
174
+ imex: true
175
+ },
176
+ {
177
+ type: 'checkbox',
178
+ name: 'ooc',
179
+ header: i18next.t('field.ooc'),
180
+ record: {
181
+ editable: false
182
+ },
183
+ width: 30
146
184
  },
147
185
  {
148
- type: 'json',
186
+ type: 'checkbox',
187
+ name: 'oos',
188
+ header: i18next.t('field.oos'),
189
+ record: {
190
+ editable: false
191
+ },
192
+ width: 30
193
+ },
194
+ {
195
+ type: 'json5',
149
196
  name: 'data',
150
197
  header: i18next.t('field.data'),
151
198
  record: {
152
199
  editable: false
153
200
  },
154
- width: 200
201
+ width: 200,
202
+ imex: true
155
203
  },
156
204
  {
157
- type: 'json',
205
+ type: 'json5',
158
206
  name: 'spec',
159
207
  header: i18next.t('field.spec'),
160
208
  record: {
@@ -163,45 +211,78 @@ export class DataSample extends connect(store)(localize(i18next)(PageView)) {
163
211
  width: 200
164
212
  },
165
213
  {
166
- type: 'code',
214
+ type: 'text',
167
215
  name: 'rawData',
168
216
  header: i18next.t('field.raw-data'),
169
217
  record: {
170
218
  editable: false
171
219
  },
172
- width: 200
220
+ width: 200,
221
+ imex: true
173
222
  },
174
223
  {
175
- type: 'object',
176
- name: 'updater',
177
- header: i18next.t('field.updater'),
224
+ type: 'string',
225
+ name: 'workDate',
226
+ header: i18next.t('field.work-date'),
178
227
  sortable: true,
179
- width: 120
228
+ width: 80,
229
+ imex: true
180
230
  },
181
231
  {
182
- type: 'datetime',
183
- name: 'updatedAt',
184
- header: i18next.t('field.updated_at'),
232
+ type: 'string',
233
+ name: 'workShift',
234
+ header: i18next.t('field.work-shift'),
185
235
  sortable: true,
186
- width: 180
236
+ width: 40,
237
+ imex: true
187
238
  },
188
239
  {
189
240
  type: 'datetime',
190
241
  name: 'collectedAt',
191
242
  header: i18next.t('field.collected_at'),
192
243
  sortable: true,
193
- width: 180
244
+ width: 180,
245
+ imex: true
246
+ },
247
+ {
248
+ type: 'resource-object',
249
+ name: 'updater',
250
+ header: i18next.t('field.updater'),
251
+ sortable: true,
252
+ width: 120,
253
+ imex: true
254
+ },
255
+ {
256
+ type: 'datetime',
257
+ name: 'updatedAt',
258
+ header: i18next.t('field.updated_at'),
259
+ sortable: true,
260
+ width: 180,
261
+ imex: true
194
262
  }
195
263
  ],
196
264
  rows: {
197
265
  appendable: false,
198
266
  selectable: {
199
267
  multiple: true
268
+ },
269
+ classifier: function (record, rowIndex) {
270
+ var emphasized
271
+ if (record['oos']) {
272
+ emphasized = ['red']
273
+ } else if (record['ooc']) {
274
+ emphasized = 'orange'
275
+ }
276
+
277
+ return {
278
+ emphasized
279
+ }
200
280
  }
201
281
  },
202
282
  sorters: [
203
283
  {
204
- name: 'name'
284
+ name: 'updatedAt',
285
+ desc: true
205
286
  }
206
287
  ]
207
288
  }
@@ -235,8 +316,14 @@ export class DataSample extends connect(store)(localize(i18next)(PageView)) {
235
316
  name
236
317
  description
237
318
  }
319
+ partitionKeys
238
320
  data
321
+ rawData
239
322
  spec
323
+ ooc
324
+ oos
325
+ workDate
326
+ workShift
240
327
  updater {
241
328
  id
242
329
  name
@@ -261,37 +348,44 @@ export class DataSample extends connect(store)(localize(i18next)(PageView)) {
261
348
  }
262
349
  }
263
350
 
264
- async _deleteDataSample() {
265
- if (confirm(i18next.t('text.sure_to_x', { x: i18next.t('text.delete') }))) {
266
- const ids = this.grist.selected.map(record => record.id)
267
- if (ids && ids.length > 0) {
268
- const response = await client.mutate({
269
- mutation: gql`
270
- mutation ($ids: [String!]!) {
271
- deleteDataSamples(ids: $ids)
351
+ _exportableData() {
352
+ let records = []
353
+ if (this.grist.selected && this.grist.selected.length > 0) {
354
+ records = this.grist.selected
355
+ } else {
356
+ records = this.grist.data.records
357
+ }
358
+
359
+ var headerSetting = this.grist.compiledConfig.columns
360
+ .filter(column => column.type !== 'gutter' && column.record !== undefined && column.imex !== undefined)
361
+ .map(column => {
362
+ return column.imex === true
363
+ ? {
364
+ header: column.header.renderer(),
365
+ key: column.name,
366
+ width: column.width,
367
+ type: column.type
272
368
  }
273
- `,
274
- variables: {
275
- ids
276
- }
277
- })
369
+ : column.imex
370
+ })
278
371
 
279
- if (!response.errors) {
280
- this.grist.fetch()
281
- notify({
282
- message: i18next.t('text.info_x_successfully', { x: i18next.t('text.delete') })
283
- })
284
- }
372
+ var data = records.map(item => {
373
+ return {
374
+ id: item.id,
375
+ ...this.gristConfig.columns
376
+ .filter(column => column.type !== 'gutter' && column.record !== undefined && column.imex !== undefined)
377
+ .reduce((record, column) => {
378
+ const key = column.imex === true ? column.name : column.imex.key
379
+ record[key] = key
380
+ .split('.')
381
+ .reduce((obj, key) => (obj && obj[key] !== 'undefined' ? obj[key] : undefined), item)
382
+ return record
383
+ }, {})
285
384
  }
286
- }
287
- }
385
+ })
288
386
 
289
- async stateChanged(state) {
290
- if (this.active && this._currentPopupName && !state.layout.viewparts[this._currentPopupName]) {
291
- this.grist.fetch()
292
- this._currentPopupName = null
293
- }
387
+ return { header: headerSetting, data: data }
294
388
  }
295
389
  }
296
390
 
297
- window.customElements.define('data-sample-page', DataSample)
391
+ window.customElements.define('data-sample-list-page', DataSampleListPage)
@@ -0,0 +1,97 @@
1
+ import '@operato/dataset/ox-data-sample-view.js'
2
+
3
+ import { LitElement, css, html } from 'lit'
4
+ import { i18next, localize } from '@operato/i18n'
5
+
6
+ import { ScrollbarStyles } from '@operato/styles'
7
+ import { client } from '@operato/graphql'
8
+ import gql from 'graphql-tag'
9
+
10
+ class DataSampleView extends localize(i18next)(LitElement) {
11
+ static get properties() {
12
+ return {
13
+ dataSet: Object,
14
+ dataSample: Object
15
+ }
16
+ }
17
+
18
+ static get styles() {
19
+ return [
20
+ ScrollbarStyles,
21
+ css`
22
+ :host {
23
+ display: flex;
24
+ flex-direction: column;
25
+
26
+ background-color: #fff;
27
+ }
28
+
29
+ div[content] {
30
+ flex: 1;
31
+
32
+ display: flex;
33
+ overflow: auto;
34
+ }
35
+
36
+ ox-data-sample-view {
37
+ flex: 1;
38
+ padding: var(--padding-wide);
39
+ }
40
+ `
41
+ ]
42
+ }
43
+
44
+ get sampleView() {
45
+ return this.renderRoot.querySelector('ox-data-sample-view')
46
+ }
47
+
48
+ render() {
49
+ return html`
50
+ <div content>
51
+ <ox-data-sample-view .dataSet=${this.dataSet} .dataSample=${this.dataSample}></ox-data-sample-view>
52
+ </div>
53
+ `
54
+ }
55
+
56
+ updated(changes) {
57
+ if (changes.has('dataSample')) {
58
+ this.fetchDataSet()
59
+ }
60
+ }
61
+
62
+ async fetchDataSet() {
63
+ const id = this.dataSample?.dataSet?.id
64
+
65
+ if (id) {
66
+ const response = await client.query({
67
+ query: gql`
68
+ query ($id: String!) {
69
+ dataSet(id: $id) {
70
+ id
71
+ name
72
+ description
73
+ useCase
74
+ dataItems {
75
+ id
76
+ name
77
+ description
78
+ active
79
+ unit
80
+ tag
81
+ type
82
+ spec
83
+ }
84
+ }
85
+ }
86
+ `,
87
+ variables: {
88
+ id: this.dataSample.dataSet.id
89
+ }
90
+ })
91
+
92
+ this.dataSet = response.data.dataSet
93
+ }
94
+ }
95
+ }
96
+
97
+ window.customElements.define('data-sample-view', DataSampleView)
@@ -1,17 +1,17 @@
1
1
  import '@operato/data-grist'
2
2
 
3
- import gql from 'graphql-tag'
3
+ import { CommonButtonStyles, ScrollbarStyles } from '@operato/styles'
4
+ import { PageView, store } from '@operato/shell'
4
5
  import { css, html } from 'lit'
5
- import { connect } from 'pwa-helpers/connect-mixin'
6
+ import { i18next, localize } from '@operato/i18n'
6
7
 
7
8
  import { client } from '@operato/graphql'
8
- import { i18next, localize } from '@operato/i18n'
9
- import { notify } from '@operato/layout'
10
- import { PageView, store } from '@operato/shell'
11
- import { CommonButtonStyles, ScrollbarStyles } from '@operato/styles'
9
+ import { connect } from 'pwa-helpers/connect-mixin'
10
+ import gql from 'graphql-tag'
12
11
  import { isMobileDevice } from '@operato/utils'
12
+ import { notify } from '@operato/layout'
13
13
 
14
- export class DataSensor extends connect(store)(localize(i18next)(PageView)) {
14
+ export class DataSensorListPage extends connect(store)(localize(i18next)(PageView)) {
15
15
  static get properties() {
16
16
  return {
17
17
  active: String,
@@ -53,7 +53,7 @@ export class DataSensor extends connect(store)(localize(i18next)(PageView)) {
53
53
  get context() {
54
54
  return {
55
55
  title: i18next.t('title.data-sensor list'),
56
- help: 'integration/ui/data-sensor',
56
+ help: 'dataset/data-sensor',
57
57
  actions: [
58
58
  {
59
59
  title: i18next.t('button.copy'),
@@ -153,6 +153,7 @@ export class DataSensor extends connect(store)(localize(i18next)(PageView)) {
153
153
  editable: true
154
154
  },
155
155
  sortable: true,
156
+ filter: true,
156
157
  width: 60
157
158
  },
158
159
  {
@@ -176,6 +177,7 @@ export class DataSensor extends connect(store)(localize(i18next)(PageView)) {
176
177
  editable: true
177
178
  },
178
179
  sortable: true,
180
+ filter: 'search',
179
181
  width: 150
180
182
  },
181
183
  {
@@ -382,13 +384,6 @@ export class DataSensor extends connect(store)(localize(i18next)(PageView)) {
382
384
  }
383
385
  }
384
386
 
385
- async stateChanged(state) {
386
- if (this.active && this._currentPopupName && !state.layout.viewparts[this._currentPopupName]) {
387
- this.grist.fetch()
388
- this._currentPopupName = null
389
- }
390
- }
391
-
392
387
  async _copyDataSensor() {
393
388
  var selected = this.grist.selected
394
389
  if (selected.length == 0) return
@@ -443,4 +438,4 @@ export class DataSensor extends connect(store)(localize(i18next)(PageView)) {
443
438
  }
444
439
  }
445
440
 
446
- window.customElements.define('data-sensor-page', DataSensor)
441
+ window.customElements.define('data-sensor-list-page', DataSensorListPage)
@@ -1,8 +1,8 @@
1
- import gql from 'graphql-tag'
2
- import { css, html, LitElement } from 'lit'
1
+ import { LitElement, css, html } from 'lit'
2
+ import { i18next, localize } from '@operato/i18n'
3
3
 
4
4
  import { client } from '@operato/graphql'
5
- import { i18next, localize } from '@operato/i18n'
5
+ import gql from 'graphql-tag'
6
6
  import { isMobileDevice } from '@operato/utils'
7
7
 
8
8
  class DataItemList extends localize(i18next)(LitElement) {
@@ -33,9 +33,6 @@ class DataItemList extends localize(i18next)(LitElement) {
33
33
  padding: var(--padding-default);
34
34
  }
35
35
 
36
- form {
37
- position: relative;
38
- }
39
36
  [danger] {
40
37
  --mdc-theme-primary: var(--mdc-danger-button-primary-color);
41
38
  }
@@ -46,7 +43,7 @@ class DataItemList extends localize(i18next)(LitElement) {
46
43
  ]
47
44
  }
48
45
 
49
- get dataGrist() {
46
+ get grist() {
50
47
  return this.renderRoot.querySelector('ox-grist')
51
48
  }
52
49
 
@@ -64,8 +61,6 @@ class DataItemList extends localize(i18next)(LitElement) {
64
61
  `
65
62
  }
66
63
 
67
- async updated(changedProps) {}
68
-
69
64
  async firstUpdated() {
70
65
  this.gristConfig = {
71
66
  list: { fields: ['name', 'description', 'active'] },
@@ -135,6 +130,17 @@ class DataItemList extends localize(i18next)(LitElement) {
135
130
  sortable: true,
136
131
  width: 60
137
132
  },
133
+ {
134
+ type: 'checkbox',
135
+ name: 'hidden',
136
+ label: true,
137
+ header: i18next.t('field.hidden'),
138
+ record: {
139
+ editable: true
140
+ },
141
+ sortable: true,
142
+ width: 60
143
+ },
138
144
  {
139
145
  type: 'string',
140
146
  name: 'tag',
@@ -149,7 +155,44 @@ class DataItemList extends localize(i18next)(LitElement) {
149
155
  name: 'type',
150
156
  header: i18next.t('field.type'),
151
157
  record: {
152
- options: ['number', 'text', 'select', 'boolean'],
158
+ options: ['', 'number', 'text', 'select', 'boolean', 'file'],
159
+ editable: true
160
+ },
161
+ width: 120
162
+ },
163
+ {
164
+ type: 'parameters',
165
+ name: 'options',
166
+ header: i18next.t('field.options'),
167
+ record: {
168
+ editable: true,
169
+ renderer: 'json5',
170
+ options: async (value, column, record, row, field) => {
171
+ return {
172
+ name: record.type,
173
+ help: '',
174
+ spec:
175
+ record.type === 'select'
176
+ ? [
177
+ {
178
+ type: 'options' /* property-editor type */,
179
+ name: 'options',
180
+ label: 'options'
181
+ }
182
+ ]
183
+ : [],
184
+ context: this.grist,
185
+ objectified: true
186
+ }
187
+ }
188
+ },
189
+ width: 120
190
+ },
191
+ {
192
+ type: 'string',
193
+ name: 'unit',
194
+ header: i18next.t('field.unit'),
195
+ record: {
153
196
  editable: true
154
197
  },
155
198
  width: 120
@@ -164,11 +207,16 @@ class DataItemList extends localize(i18next)(LitElement) {
164
207
  width: 60
165
208
  },
166
209
  {
167
- type: 'json',
210
+ type: 'data-item-spec',
168
211
  name: 'spec',
169
212
  header: i18next.t('field.spec'),
170
213
  record: {
171
- editable: true
214
+ editable: true,
215
+ options: {
216
+ name: '',
217
+ help: '',
218
+ objectified: true /* prevent from stringifying */
219
+ }
172
220
  },
173
221
  width: 200
174
222
  }
@@ -207,7 +255,11 @@ class DataItemList extends localize(i18next)(LitElement) {
207
255
  description
208
256
  sequence
209
257
  active
258
+ hidden
259
+ tag
210
260
  type
261
+ options
262
+ unit
211
263
  quota
212
264
  spec
213
265
  }
@@ -224,7 +276,7 @@ class DataItemList extends localize(i18next)(LitElement) {
224
276
  }
225
277
 
226
278
  async _updateDataItems() {
227
- let patches = this.dataGrist._data.records
279
+ let patches = this.grist._data.records
228
280
  if (patches && patches.length) {
229
281
  patches = patches.map(patch => {
230
282
  var patchField = {}
@@ -250,14 +302,16 @@ class DataItemList extends localize(i18next)(LitElement) {
250
302
  }
251
303
  })
252
304
 
253
- if (!response.errors) this.dataGrist.fetch()
305
+ if (!response.errors) {
306
+ this.grist.fetch()
307
+ }
254
308
  }
255
309
  }
256
310
 
257
311
  async _deleteDataItems() {
258
312
  if (!confirm(i18next.t('text.sure_to_x', { x: i18next.t('text.delete') }))) return
259
313
 
260
- const ids = this.dataGrist.selected.map(record => record.id)
314
+ const ids = this.grist.selected.map(record => record.id)
261
315
  if (!(ids && ids.length > 0)) return
262
316
 
263
317
  const response = await client.mutate({
@@ -273,7 +327,7 @@ class DataItemList extends localize(i18next)(LitElement) {
273
327
 
274
328
  if (response.errors) return
275
329
 
276
- this.dataGrist.fetch()
330
+ this.grist.fetch()
277
331
  await document.dispatchEvent(
278
332
  new CustomEvent('notify', {
279
333
  detail: {