@things-factory/dataset 5.0.1 → 5.0.4

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 (66) hide show
  1. package/client/pages/data-key-set/data-key-item-list.js +196 -0
  2. package/client/pages/data-key-set/data-key-set-importer.js +107 -0
  3. package/client/pages/data-key-set/data-key-set-list-page.js +354 -0
  4. package/client/pages/data-ooc/data-ooc-list-page.js +60 -0
  5. package/client/pages/data-sample/data-sample-list-page.js +60 -0
  6. package/client/pages/data-sample/data-sample-search-page.js +406 -0
  7. package/client/pages/data-sample/data-sample-view.js +1 -49
  8. package/client/pages/data-set/data-set-list-page.js +20 -1
  9. package/client/route.js +9 -1
  10. package/dist-server/controllers/create-data-sample.js +15 -3
  11. package/dist-server/controllers/create-data-sample.js.map +1 -1
  12. package/dist-server/service/data-key-set/data-key-item-type.js +58 -0
  13. package/dist-server/service/data-key-set/data-key-item-type.js.map +1 -0
  14. package/dist-server/service/data-key-set/data-key-set-mutation.js +179 -0
  15. package/dist-server/service/data-key-set/data-key-set-mutation.js.map +1 -0
  16. package/dist-server/service/data-key-set/data-key-set-query.js +110 -0
  17. package/dist-server/service/data-key-set/data-key-set-query.js.map +1 -0
  18. package/dist-server/service/data-key-set/data-key-set-type.js +94 -0
  19. package/dist-server/service/data-key-set/data-key-set-type.js.map +1 -0
  20. package/dist-server/service/data-key-set/data-key-set.js +101 -0
  21. package/dist-server/service/data-key-set/data-key-set.js.map +1 -0
  22. package/dist-server/service/data-key-set/index.js +9 -0
  23. package/dist-server/service/data-key-set/index.js.map +1 -0
  24. package/dist-server/service/data-ooc/data-ooc.js +32 -0
  25. package/dist-server/service/data-ooc/data-ooc.js.map +1 -1
  26. package/dist-server/service/data-sample/data-sample-query.js +29 -1
  27. package/dist-server/service/data-sample/data-sample-query.js.map +1 -1
  28. package/dist-server/service/data-sample/data-sample.js +42 -0
  29. package/dist-server/service/data-sample/data-sample.js.map +1 -1
  30. package/dist-server/service/data-set/data-item-type.js +1 -1
  31. package/dist-server/service/data-set/data-item-type.js.map +1 -1
  32. package/dist-server/service/data-set/data-set-mutation.js +8 -8
  33. package/dist-server/service/data-set/data-set-mutation.js.map +1 -1
  34. package/dist-server/service/data-set/data-set-query.js +16 -1
  35. package/dist-server/service/data-set/data-set-query.js.map +1 -1
  36. package/dist-server/service/data-set/data-set-type.js +14 -6
  37. package/dist-server/service/data-set/data-set-type.js.map +1 -1
  38. package/dist-server/service/data-set/data-set.js +32 -54
  39. package/dist-server/service/data-set/data-set.js.map +1 -1
  40. package/dist-server/service/data-set-history/data-set-history-query.js +3 -1
  41. package/dist-server/service/data-set-history/data-set-history-query.js.map +1 -1
  42. package/dist-server/service/index.js +4 -1
  43. package/dist-server/service/index.js.map +1 -1
  44. package/package.json +3 -3
  45. package/server/controllers/create-data-sample.ts +19 -12
  46. package/server/service/data-key-set/data-key-item-type.ts +31 -0
  47. package/server/service/data-key-set/data-key-set-mutation.ts +198 -0
  48. package/server/service/data-key-set/data-key-set-query.ts +65 -0
  49. package/server/service/data-key-set/data-key-set-type.ts +57 -0
  50. package/server/service/data-key-set/data-key-set.ts +81 -0
  51. package/server/service/data-key-set/index.ts +6 -0
  52. package/server/service/data-ooc/data-ooc.ts +20 -0
  53. package/server/service/data-sample/data-sample-query.ts +29 -0
  54. package/server/service/data-sample/data-sample.ts +34 -0
  55. package/server/service/data-set/data-item-type.ts +1 -1
  56. package/server/service/data-set/data-set-mutation.ts +8 -8
  57. package/server/service/data-set/data-set-query.ts +13 -1
  58. package/server/service/data-set/data-set-type.ts +6 -0
  59. package/server/service/data-set/data-set.ts +24 -48
  60. package/server/service/data-set-history/data-set-history-query.ts +3 -1
  61. package/server/service/index.ts +4 -1
  62. package/things-factory.config.js +8 -0
  63. package/translations/en.json +11 -0
  64. package/translations/ko.json +11 -0
  65. package/translations/ms.json +12 -1
  66. package/translations/zh.json +11 -0
@@ -0,0 +1,196 @@
1
+ import gql from 'graphql-tag'
2
+ import { css, html, LitElement } from 'lit'
3
+
4
+ import { client } from '@operato/graphql'
5
+ import { i18next, localize } from '@operato/i18n'
6
+ import { isMobileDevice } from '@operato/utils'
7
+
8
+ class DataKeyItemList extends localize(i18next)(LitElement) {
9
+ static get properties() {
10
+ return {
11
+ dataKeySet: Object,
12
+ gristConfig: Object
13
+ }
14
+ }
15
+
16
+ static get styles() {
17
+ return [
18
+ css`
19
+ :host {
20
+ display: flex;
21
+ flex-direction: column;
22
+
23
+ background-color: #fff;
24
+ }
25
+
26
+ ox-grist {
27
+ flex: 1;
28
+ }
29
+
30
+ .button-container {
31
+ display: flex;
32
+ margin-left: auto;
33
+ padding: var(--padding-default);
34
+ }
35
+
36
+ [danger] {
37
+ --mdc-theme-primary: var(--mdc-danger-button-primary-color);
38
+ }
39
+ mwc-button {
40
+ margin-left: var(--margin-default);
41
+ }
42
+ `
43
+ ]
44
+ }
45
+
46
+ get grist() {
47
+ return this.renderRoot.querySelector('ox-grist')
48
+ }
49
+
50
+ render() {
51
+ return html`
52
+ <ox-grist
53
+ .mode=${isMobileDevice() ? 'LIST' : 'GRID'}
54
+ .config=${this.gristConfig}
55
+ .fetchHandler=${this.fetchHandler.bind(this)}
56
+ ></ox-grist>
57
+ <div class="button-container">
58
+ <mwc-button raised danger @click=${this._deleteDataKeyItems.bind(this)}
59
+ >${i18next.t('button.delete')}</mwc-button
60
+ >
61
+ <mwc-button raised @click=${this._updateDataKeyItems.bind(this)}>${i18next.t('button.save')}</mwc-button>
62
+ </div>
63
+ `
64
+ }
65
+
66
+ async firstUpdated() {
67
+ this.gristConfig = {
68
+ list: { fields: ['name', 'description', 'active'] },
69
+ columns: [
70
+ { type: 'gutter', gutterName: 'row-selector', multiple: true },
71
+ {
72
+ type: 'gutter',
73
+ gutterName: 'button',
74
+ icon: 'add',
75
+ handlers: {
76
+ click: 'record-copy'
77
+ }
78
+ },
79
+ { type: 'gutter', gutterName: 'sequence' },
80
+ {
81
+ type: 'gutter',
82
+ gutterName: 'button',
83
+ icon: 'arrow_upward',
84
+ handlers: {
85
+ click: 'move-up'
86
+ }
87
+ },
88
+ {
89
+ type: 'gutter',
90
+ gutterName: 'button',
91
+ icon: 'arrow_downward',
92
+ handlers: {
93
+ click: 'move-down'
94
+ }
95
+ },
96
+ {
97
+ type: 'string',
98
+ name: 'name',
99
+ header: i18next.t('field.name'),
100
+ record: {
101
+ editable: true
102
+ },
103
+ width: 140
104
+ },
105
+ {
106
+ type: 'string',
107
+ name: 'description',
108
+ header: i18next.t('field.description'),
109
+ record: {
110
+ editable: true
111
+ },
112
+ width: 180
113
+ },
114
+ {
115
+ type: 'string',
116
+ name: 'dataKey',
117
+ header: i18next.t('field.data-key'),
118
+ record: {
119
+ editable: true
120
+ },
121
+ width: 180
122
+ },
123
+ {
124
+ type: 'string',
125
+ name: 'tKey',
126
+ header: i18next.t('field.t-key'),
127
+ record: {
128
+ editable: true
129
+ },
130
+ width: 180
131
+ }
132
+ ],
133
+ rows: {
134
+ selectable: {
135
+ multiple: true
136
+ }
137
+ },
138
+ pagination: {
139
+ infinite: true
140
+ },
141
+ sorters: []
142
+ }
143
+ }
144
+
145
+ async fetchHandler({ filters, page, limit, sortings = [] }) {
146
+ const dataKeyItems = this.dataKeySet.dataKeyItems || []
147
+
148
+ return {
149
+ total: dataKeyItems.length,
150
+ records: dataKeyItems
151
+ }
152
+ }
153
+
154
+ async _updateDataKeyItems() {
155
+ this.grist.commit()
156
+
157
+ const response = await client.mutate({
158
+ mutation: gql`
159
+ mutation ($id: String!, $patch: DataKeySetPatch!) {
160
+ updateDataKeySet(id: $id, patch: $patch) {
161
+ name
162
+ }
163
+ }
164
+ `,
165
+ variables: {
166
+ id: this.dataKeySet.id,
167
+ patch: {
168
+ dataKeyItems: this.grist.data.records,
169
+ cuFlag: 'M'
170
+ }
171
+ }
172
+ })
173
+
174
+ if (!response.errors) {
175
+ await document.dispatchEvent(
176
+ new CustomEvent('notify', {
177
+ detail: {
178
+ message: i18next.t('text.info_x_successfully', {
179
+ x: i18next.t('button.save')
180
+ })
181
+ }
182
+ })
183
+ )
184
+ }
185
+ }
186
+
187
+ async _deleteDataKeyItems() {
188
+ if (!confirm(i18next.t('text.sure_to_x', { x: i18next.t('text.delete') }))) {
189
+ return
190
+ }
191
+
192
+ this.grist.deleteSelectedRecords(false)
193
+ }
194
+ }
195
+
196
+ window.customElements.define('data-key-item-list', DataKeyItemList)
@@ -0,0 +1,107 @@
1
+ import '@operato/data-grist'
2
+
3
+ import gql from 'graphql-tag'
4
+ import { css, html, LitElement } from 'lit'
5
+
6
+ import { client } from '@operato/graphql'
7
+ import { i18next } from '@operato/i18n'
8
+ import { isMobileDevice } from '@operato/utils'
9
+
10
+ export class DataKeySetImporter extends LitElement {
11
+ static get properties() {
12
+ return {
13
+ columns: Object,
14
+ dataKeySets: Array
15
+ }
16
+ }
17
+
18
+ constructor() {
19
+ super()
20
+ this.columns = {
21
+ list: { fields: ['name', 'description'] },
22
+ pagination: { infinite: true },
23
+ columns: [
24
+ {
25
+ type: 'string',
26
+ name: 'name',
27
+ header: i18next.t('field.name'),
28
+ width: 150
29
+ },
30
+ {
31
+ type: 'string',
32
+ name: 'description',
33
+ header: i18next.t('field.description'),
34
+ width: 200
35
+ },
36
+ {
37
+ type: 'checkbox',
38
+ name: 'active',
39
+ header: i18next.t('field.active'),
40
+ width: 60
41
+ }
42
+ ]
43
+ }
44
+ }
45
+
46
+ static get styles() {
47
+ return [
48
+ css`
49
+ :host {
50
+ display: flex;
51
+ flex-direction: column;
52
+
53
+ background-color: #fff;
54
+ }
55
+
56
+ ox-grist {
57
+ flex: 1;
58
+ }
59
+
60
+ .button-container {
61
+ display: flex;
62
+ margin-left: auto;
63
+ padding: var(--padding-default);
64
+ }
65
+
66
+ mwc-button {
67
+ margin-left: var(--margin-default);
68
+ }
69
+ `
70
+ ]
71
+ }
72
+
73
+ render() {
74
+ return html`
75
+ <ox-grist
76
+ .mode=${isMobileDevice() ? 'LIST' : 'GRID'}
77
+ .config=${this.columns}
78
+ .data=${
79
+ {
80
+ records: this.dataKeySets
81
+ }
82
+ }
83
+ ></ox-grist>
84
+
85
+ <div class="button-container">
86
+ <mwc-button raised @click="${this.save.bind(this)}">${i18next.t('button.save')}</mwc-button>
87
+ </div>
88
+ `
89
+ }
90
+
91
+ async save() {
92
+ const response = await client.mutate({
93
+ mutation: gql`
94
+ mutation importDataKeySets($dataKeySets: [DataKeySetPatch!]!) {
95
+ importDataKeySets(dataKeySets: $dataKeySets)
96
+ }
97
+ `,
98
+ variables: { dataKeySets: this.dataKeySets }
99
+ })
100
+
101
+ if (response.error?.length) return
102
+
103
+ this.dispatchEvent(new CustomEvent('imported'))
104
+ }
105
+ }
106
+
107
+ window.customElements.define('data-key-set-importer', DataKeySetImporter)
@@ -0,0 +1,354 @@
1
+ import '@operato/data-grist'
2
+ import './data-key-set-importer.js'
3
+ import './data-key-item-list'
4
+
5
+ import gql from 'graphql-tag'
6
+ import { css, html } from 'lit'
7
+ import { connect } from 'pwa-helpers/connect-mixin'
8
+
9
+ import { client } from '@operato/graphql'
10
+ import { i18next, localize } from '@operato/i18n'
11
+ import { notify, openPopup } from '@operato/layout'
12
+ import { navigate, PageView, store } from '@operato/shell'
13
+ import { CommonButtonStyles, CommonGristStyles, ScrollbarStyles } from '@operato/styles'
14
+ import { isMobileDevice } from '@operato/utils'
15
+
16
+ export class DataKeySetListPage extends connect(store)(localize(i18next)(PageView)) {
17
+ static get properties() {
18
+ return {
19
+ gristConfig: Object,
20
+ mode: String
21
+ }
22
+ }
23
+
24
+ static get styles() {
25
+ return [
26
+ ScrollbarStyles,
27
+ CommonGristStyles,
28
+ css`
29
+ :host {
30
+ display: flex;
31
+
32
+ width: 100%;
33
+
34
+ --grid-record-emphasized-background-color: red;
35
+ --grid-record-emphasized-color: yellow;
36
+ }
37
+ `
38
+ ]
39
+ }
40
+
41
+ get context() {
42
+ return {
43
+ title: i18next.t('title.data-key-set list'),
44
+ help: 'dataset/data-key-set',
45
+ actions: [
46
+ {
47
+ title: i18next.t('button.save'),
48
+ action: this._updateDataKeySet.bind(this),
49
+ ...CommonButtonStyles.save
50
+ },
51
+ {
52
+ title: i18next.t('button.delete'),
53
+ action: this._deleteDataKeySet.bind(this),
54
+ ...CommonButtonStyles.delete
55
+ }
56
+ ],
57
+ exportable: {
58
+ name: i18next.t('title.data-key-set list'),
59
+ data: this.exportHandler.bind(this)
60
+ },
61
+ importable: {
62
+ handler: this.importHandler.bind(this)
63
+ }
64
+ }
65
+ }
66
+
67
+ render() {
68
+ const mode = this.mode || (isMobileDevice() ? 'CARD' : 'GRID')
69
+
70
+ return html`
71
+ <ox-grist .mode=${mode} .config=${this.gristConfig} .fetchHandler=${this.fetchHandler.bind(this)}>
72
+ <div slot="headroom">
73
+ <div id="filters">
74
+ <ox-filters-form autofocus></ox-filters-form>
75
+ </div>
76
+
77
+ <div id="sorters">
78
+ Sort
79
+ <mwc-icon
80
+ @click=${e => {
81
+ const target = e.currentTarget
82
+ this.renderRoot.querySelector('#sorter-control').open({
83
+ right: 0,
84
+ top: target.offsetTop + target.offsetHeight
85
+ })
86
+ }}
87
+ >expand_more</mwc-icon
88
+ >
89
+ <ox-popup id="sorter-control">
90
+ <ox-sorters-control> </ox-sorters-control>
91
+ </ox-popup>
92
+ </div>
93
+
94
+ <div id="modes">
95
+ <mwc-icon @click=${() => (this.mode = 'GRID')} ?active=${mode == 'GRID'}>grid_on</mwc-icon>
96
+ <mwc-icon @click=${() => (this.mode = 'LIST')} ?active=${mode == 'LIST'}>format_list_bulleted</mwc-icon>
97
+ <mwc-icon @click=${() => (this.mode = 'CARD')} ?active=${mode == 'CARD'}>apps</mwc-icon>
98
+ </div>
99
+ </div>
100
+ </ox-grist>
101
+ `
102
+ }
103
+
104
+ get grist() {
105
+ return this.renderRoot.querySelector('ox-grist')
106
+ }
107
+
108
+ async pageInitialized(lifecycle) {
109
+ this.gristConfig = {
110
+ list: {
111
+ fields: ['name', 'description'],
112
+ details: ['active', 'updatedAt']
113
+ },
114
+ columns: [
115
+ { type: 'gutter', gutterName: 'sequence' },
116
+ { type: 'gutter', gutterName: 'row-selector', multiple: true },
117
+ {
118
+ type: 'gutter',
119
+ gutterName: 'button',
120
+ icon: 'reorder',
121
+ handlers: {
122
+ click: (columns, data, column, record, rowIndex) => {
123
+ if (!record.id) return
124
+ const popup = openPopup(html` <data-key-item-list .dataKeySet=${record}></data-key-item-list> `, {
125
+ backdrop: true,
126
+ help: 'data-set/ui/data-key-item-list',
127
+ size: 'large',
128
+ title: i18next.t('title.data-key-item list')
129
+ })
130
+ popup.onclosed = () => {
131
+ this.grist.fetch()
132
+ }
133
+ }
134
+ }
135
+ },
136
+ {
137
+ type: 'gutter',
138
+ gutterName: 'button',
139
+ icon: 'newspaper',
140
+ handlers: {
141
+ click: (columns, data, column, record, rowIndex) => {
142
+ navigate(`/data-sample-search/${record.id}`)
143
+ }
144
+ }
145
+ },
146
+ {
147
+ type: 'string',
148
+ name: 'name',
149
+ header: i18next.t('field.name'),
150
+ record: {
151
+ editable: true
152
+ },
153
+ filter: 'search',
154
+ sortable: true,
155
+ width: 150
156
+ },
157
+ {
158
+ type: 'string',
159
+ name: 'description',
160
+ header: i18next.t('field.description'),
161
+ record: {
162
+ editable: true
163
+ },
164
+ filter: 'search',
165
+ width: 200
166
+ },
167
+ {
168
+ type: 'checkbox',
169
+ name: 'active',
170
+ label: true,
171
+ header: i18next.t('field.active'),
172
+ record: {
173
+ editable: true
174
+ },
175
+ filter: true,
176
+ sortable: true,
177
+ width: 60
178
+ },
179
+ {
180
+ type: 'resource-object',
181
+ name: 'updater',
182
+ header: i18next.t('field.updater'),
183
+ record: {
184
+ editable: false
185
+ },
186
+ sortable: true,
187
+ width: 120
188
+ },
189
+ {
190
+ type: 'datetime',
191
+ name: 'updatedAt',
192
+ header: i18next.t('field.updated_at'),
193
+ record: {
194
+ editable: false
195
+ },
196
+ sortable: true,
197
+ width: 180
198
+ }
199
+ ],
200
+ rows: {
201
+ selectable: {
202
+ multiple: true
203
+ }
204
+ },
205
+ sorters: [
206
+ {
207
+ name: 'name'
208
+ }
209
+ ]
210
+ }
211
+ }
212
+
213
+ async pageUpdated(changes, lifecycle) {
214
+ if (this.active) {
215
+ // do something here when this page just became as active
216
+ }
217
+ }
218
+
219
+ async fetchHandler({ page, limit, sortings = [], filters = [] }) {
220
+ const response = await client.query({
221
+ query: gql`
222
+ query ($filters: [Filter!], $pagination: Pagination, $sortings: [Sorting!]) {
223
+ responses: dataKeySets(filters: $filters, pagination: $pagination, sortings: $sortings) {
224
+ items {
225
+ id
226
+ name
227
+ description
228
+ active
229
+ dataKeyItems {
230
+ name
231
+ description
232
+ dataKey
233
+ tKey
234
+ }
235
+ updater {
236
+ id
237
+ name
238
+ }
239
+ updatedAt
240
+ }
241
+ total
242
+ }
243
+ }
244
+ `,
245
+ variables: {
246
+ filters,
247
+ pagination: { page, limit },
248
+ sortings
249
+ }
250
+ })
251
+
252
+ return {
253
+ total: response.data.responses.total || 0,
254
+ records: response.data.responses.items || []
255
+ }
256
+ }
257
+
258
+ async _deleteDataKeySet() {
259
+ if (confirm(i18next.t('text.sure_to_x', { x: i18next.t('text.delete') }))) {
260
+ const ids = this.grist.selected.map(record => record.id)
261
+ if (ids && ids.length > 0) {
262
+ const response = await client.mutate({
263
+ mutation: gql`
264
+ mutation ($ids: [String!]!) {
265
+ deleteDataKeySets(ids: $ids)
266
+ }
267
+ `,
268
+ variables: {
269
+ ids
270
+ }
271
+ })
272
+
273
+ if (!response.errors) {
274
+ this.grist.fetch()
275
+ notify({
276
+ message: i18next.t('text.info_x_successfully', { x: i18next.t('text.delete') })
277
+ })
278
+ }
279
+ }
280
+ }
281
+ }
282
+
283
+ async _updateDataKeySet() {
284
+ let patches = this.grist.dirtyRecords
285
+ if (patches && patches.length) {
286
+ patches = patches.map(patch => {
287
+ let patchField = patch.id ? { id: patch.id } : {}
288
+ const dirtyFields = patch.__dirtyfields__
289
+ for (let key in dirtyFields) {
290
+ patchField[key] = dirtyFields[key].after
291
+ }
292
+ patchField.cuFlag = patch.__dirty__
293
+
294
+ return patchField
295
+ })
296
+
297
+ const response = await client.mutate({
298
+ mutation: gql`
299
+ mutation ($patches: [DataKeySetPatch!]!) {
300
+ updateMultipleDataKeySet(patches: $patches) {
301
+ name
302
+ }
303
+ }
304
+ `,
305
+ variables: {
306
+ patches
307
+ }
308
+ })
309
+
310
+ if (!response.errors) {
311
+ this.grist.fetch()
312
+ }
313
+ }
314
+ }
315
+
316
+ async exportHandler() {
317
+ const exportTargets = this.grist.selected.length ? this.grist.selected : this.grist.dirtyData.records
318
+ const targetFieldSet = new Set(['id', 'name', 'description', 'active'])
319
+
320
+ return exportTargets.map(dataKeySet => {
321
+ let tempObj = {}
322
+ for (const field of targetFieldSet) {
323
+ tempObj[field] = dataKeySet[field]
324
+ }
325
+
326
+ return tempObj
327
+ })
328
+ }
329
+
330
+ async importHandler(records) {
331
+ const popup = openPopup(
332
+ html`
333
+ <data-key-set-importer
334
+ .dataKeySets=${records}
335
+ @imported=${() => {
336
+ history.back()
337
+ this.grist.fetch()
338
+ }}
339
+ ></data-key-set-importer>
340
+ `,
341
+ {
342
+ backdrop: true,
343
+ size: 'large',
344
+ title: i18next.t('title.import data-key-set')
345
+ }
346
+ )
347
+
348
+ popup.onclosed = () => {
349
+ this.grist.fetch()
350
+ }
351
+ }
352
+ }
353
+
354
+ window.customElements.define('data-key-set-list-page', DataKeySetListPage)