@things-factory/reference-app 5.0.0-zeta.9 → 5.0.0

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 (73) hide show
  1. package/client/bootstrap.js +10 -10
  2. package/client/components/ocr-viewpart.js +1 -1
  3. package/client/editors/id-selector.js +1 -1
  4. package/client/menu.js +10 -5
  5. package/client/pages/data-entry/data-entry-form.js +117 -0
  6. package/client/pages/data-entry/data-entry-generator-popup.js +110 -0
  7. package/client/pages/data-set/data-item-list.js +277 -0
  8. package/client/pages/data-set/data-set-importer.js +103 -0
  9. package/client/pages/data-set/data-set-list-page.js +738 -0
  10. package/client/pages/ocr-page.js +1 -1
  11. package/client/pages/operation/operation-api.js +85 -0
  12. package/client/pages/operation/operation-master.js +432 -0
  13. package/client/pages/pending-job-page.js +1 -1
  14. package/client/pages/product/product-api.js +150 -0
  15. package/client/pages/product/product-master.js +888 -0
  16. package/client/pages/product-combination-settings-popup.js +395 -0
  17. package/client/pages/product-combinations-popup.js +372 -0
  18. package/client/pages/product-details-popup.js +744 -0
  19. package/client/pages/upload-page.js +1 -1
  20. package/client/route.js +12 -0
  21. package/db.sqlite +0 -0
  22. package/dist-server/constants/index.js +18 -0
  23. package/dist-server/constants/index.js.map +1 -0
  24. package/dist-server/constants/type-constants.js +26 -0
  25. package/dist-server/constants/type-constants.js.map +1 -0
  26. package/dist-server/controllers/create-data-sample-mockup.js +236 -0
  27. package/dist-server/controllers/create-data-sample-mockup.js.map +1 -0
  28. package/dist-server/controllers/index.js +17 -0
  29. package/dist-server/controllers/index.js.map +1 -1
  30. package/dist-server/service/data-sample-mockup/data-sample-mockup-mutation.js +40 -0
  31. package/dist-server/service/data-sample-mockup/data-sample-mockup-mutation.js.map +1 -0
  32. package/dist-server/service/data-sample-mockup/data-sample-mockup-type.js +28 -0
  33. package/dist-server/service/data-sample-mockup/data-sample-mockup-type.js.map +1 -0
  34. package/dist-server/service/data-sample-mockup/index.js +7 -0
  35. package/dist-server/service/data-sample-mockup/index.js.map +1 -0
  36. package/dist-server/service/index.js +5 -2
  37. package/dist-server/service/index.js.map +1 -1
  38. package/dist-server/service/reference/reference-mutation.js +6 -3
  39. package/dist-server/service/reference/reference-mutation.js.map +1 -1
  40. package/dist-server/service/reference/reference-query.js +7 -4
  41. package/dist-server/service/reference/reference-query.js.map +1 -1
  42. package/logs/.08636eb59927f12972f6774f5947c8507b3564c2-audit.json +3 -28
  43. package/logs/.5e5d741d8b7784a2fbad65eedc0fd46946aaf6f2-audit.json +18 -48
  44. package/logs/application-2022-07-22-10.log +26 -0
  45. package/logs/{connections-2022-06-26-22.log → connections-2022-07-12-00.log} +0 -0
  46. package/logs/{connections-2022-06-26-23.log → connections-2022-07-14-14.log} +0 -0
  47. package/logs/{connections-2022-06-27-00.log → connections-2022-07-14-15.log} +0 -0
  48. package/logs/{connections-2022-06-28-21.log → connections-2022-07-14-16.log} +0 -0
  49. package/logs/{connections-2022-06-28-22.log → connections-2022-07-14-17.log} +0 -0
  50. package/logs/{connections-2022-06-29-08.log → connections-2022-07-22-10.log} +0 -0
  51. package/package.json +58 -56
  52. package/server/constants/index.ts +1 -0
  53. package/server/constants/type-constants.ts +24 -0
  54. package/server/controllers/create-data-sample-mockup.ts +268 -0
  55. package/server/controllers/index.ts +1 -0
  56. package/server/service/data-sample-mockup/data-sample-mockup-mutation.ts +18 -0
  57. package/server/service/data-sample-mockup/data-sample-mockup-type.ts +10 -0
  58. package/server/service/data-sample-mockup/index.ts +4 -0
  59. package/server/service/index.ts +5 -2
  60. package/server/service/reference/reference-mutation.ts +5 -3
  61. package/server/service/reference/reference-query.ts +8 -7
  62. package/things-factory.config.js +8 -0
  63. package/translations/en.json +6 -1
  64. package/translations/ko.json +7 -1
  65. package/logs/application-2022-06-28-21.log +0 -30
  66. package/logs/application-2022-06-28-22.log +0 -15
  67. package/logs/application-2022-06-29-08.log +0 -12
  68. package/logs/application-2022-06-29-09.log +0 -12
  69. package/logs/application-2022-06-29-10.log +0 -4
  70. package/logs/application-2022-06-29-12.log +0 -4
  71. package/logs/connections-2022-06-29-09.log +0 -0
  72. package/logs/connections-2022-06-29-10.log +0 -0
  73. package/logs/connections-2022-06-29-12.log +0 -0
@@ -141,16 +141,16 @@ export default function bootstrap() {
141
141
  }
142
142
  })
143
143
 
144
- store.dispatch({
145
- type: ADD_MORENDA,
146
- morenda: {
147
- icon: html` <mwc-icon>help</mwc-icon> `,
148
- name: html` <i18n-msg msgid="text.help"></i18n-msg> `,
149
- action: () => {
150
- navigate('help')
151
- }
152
- }
153
- })
144
+ // store.dispatch({
145
+ // type: ADD_MORENDA,
146
+ // morenda: {
147
+ // icon: html` <mwc-icon>help</mwc-icon> `,
148
+ // name: html` <i18n-msg msgid="text.help"></i18n-msg> `,
149
+ // action: () => {
150
+ // navigate('help')
151
+ // }
152
+ // }
153
+ // })
154
154
 
155
155
  store.dispatch({
156
156
  type: ADD_SETTING,
@@ -2,7 +2,7 @@ import './camera-capturer'
2
2
  import '@material/mwc-button'
3
3
  import '@operato/ocr/ox-ocr-helper.js'
4
4
 
5
- import { gql } from 'graphql-tag'
5
+ import gql from 'graphql-tag'
6
6
  import { css, html, LitElement } from 'lit'
7
7
 
8
8
  import { client } from '@things-factory/shell'
@@ -70,7 +70,7 @@ export class IdSelector extends LitElement {
70
70
  .selectedRecords=${this.selectedRecords}
71
71
  >
72
72
  <div id="filters" slot="headroom">
73
- <ox-filters-form></ox-filters-form>
73
+ <ox-filters-form autofocus></ox-filters-form>
74
74
  </div>
75
75
  </ox-grist>
76
76
 
package/client/menu.js CHANGED
@@ -140,6 +140,16 @@ export function getMenuTemplate() {
140
140
  icon: 'settings',
141
141
  description: '데이타 수집을 위한 개념과 기능들을 소개합니다.',
142
142
  menus: [
143
+ {
144
+ name: 'Operation',
145
+ icon: 'schema',
146
+ path: 'operation_master'
147
+ },
148
+ {
149
+ name: 'Product',
150
+ icon: 'inventory',
151
+ path: 'product_master'
152
+ },
143
153
  {
144
154
  icon: 'display_settings',
145
155
  name: 'Data Set Master',
@@ -169,11 +179,6 @@ export function getMenuTemplate() {
169
179
  icon: 'newspaper',
170
180
  name: 'Data Report',
171
181
  path: 'data-report-list'
172
- },
173
- {
174
- icon: 'newspaper',
175
- name: 'Jasper Data Report',
176
- path: 'jasper-report-dev-samples'
177
182
  }
178
183
  ]
179
184
  }
@@ -0,0 +1,117 @@
1
+ import '@operato/dataset/ox-data-entry-form.js'
2
+
3
+ import gql from 'graphql-tag'
4
+ import { css, html, LitElement } from 'lit'
5
+
6
+ import { openPopup } from '@operato/layout'
7
+ import { client } from '@operato/graphql'
8
+ import { i18next, localize } from '@operato/i18n'
9
+ import { ScrollbarStyles } from '@operato/styles'
10
+
11
+ import './data-entry-generator-popup'
12
+
13
+ class DataEntryForm extends localize(i18next)(LitElement) {
14
+ static get properties() {
15
+ return {
16
+ dataSet: Object
17
+ }
18
+ }
19
+
20
+ static get styles() {
21
+ return [
22
+ ScrollbarStyles,
23
+ css`
24
+ :host {
25
+ display: flex;
26
+ flex-direction: column;
27
+
28
+ background-color: #fff;
29
+ }
30
+
31
+ ox-data-entry-form {
32
+ flex: 1;
33
+ padding: 10px;
34
+ overflow: auto;
35
+ }
36
+
37
+ .button-container {
38
+ display: flex;
39
+ margin-left: auto;
40
+ padding: var(--padding-default);
41
+ }
42
+
43
+ mwc-button {
44
+ margin-left: var(--margin-default);
45
+ }
46
+ `
47
+ ]
48
+ }
49
+
50
+ get entryForm() {
51
+ return this.renderRoot.querySelector('ox-data-entry-form')
52
+ }
53
+
54
+ render() {
55
+ return html`
56
+ <ox-data-entry-form .dataSet=${this.dataSet}></ox-data-entry-form>
57
+ <div class="button-container">
58
+ ${process.env.NODE_ENV == 'development'
59
+ ? html`<mwc-button raised @click=${this._generateMockupData.bind(this)}>${i18next.t('button.generate_mockup')}</mwc-button>`
60
+ : html``
61
+ }
62
+ <mwc-button raised @click=${this._updateDataItems.bind(this)}>${i18next.t('button.save')}</mwc-button>
63
+ </div>
64
+ `
65
+ }
66
+
67
+ async _generateMockupData() {
68
+ this.popup = openPopup(
69
+ html`
70
+ <data-entry-generator-popup
71
+ .dataSetId="${this.dataSet.id}"
72
+ @created="${e => {
73
+ this.popup && this.popup.close()
74
+ }}"
75
+ ></data-entry-generator-popup>
76
+ `,
77
+ {
78
+ backdrop: true,
79
+ escapable: false,
80
+ size: 'small',
81
+ title: `${i18next.t('title.generate_mockup')}`
82
+ }
83
+ )
84
+ }
85
+
86
+ async _updateDataItems() {
87
+ const data = this.entryForm.buildValue()
88
+ const dataSample = {
89
+ dataSet: {
90
+ id: this.dataSet.id
91
+ },
92
+ data
93
+ }
94
+
95
+ const response = await client.mutate({
96
+ mutation: gql`
97
+ mutation ($dataSample: NewDataSample!) {
98
+ createDataSample(dataSample: $dataSample) {
99
+ id
100
+ collectedAt
101
+ }
102
+ }
103
+ `,
104
+ variables: {
105
+ dataSample
106
+ }
107
+ })
108
+
109
+ if (!response.errors) {
110
+ document.dispatchEvent(
111
+ new CustomEvent('notify', { detail: { message: i18next.t('text.data sample created successfully') } })
112
+ )
113
+ }
114
+ }
115
+ }
116
+
117
+ window.customElements.define('data-entry-form', DataEntryForm)
@@ -0,0 +1,110 @@
1
+ import { SingleColumnFormStyles } from '@things-factory/form-ui'
2
+ import '@things-factory/grist-ui'
3
+ import { i18next, localize } from '@things-factory/i18n-base'
4
+ import { client } from '@things-factory/shell'
5
+ import { gqlBuilder } from '@things-factory/utils'
6
+ import gql from 'graphql-tag'
7
+ import { css, html, LitElement } from 'lit-element'
8
+ import { notify } from '@things-factory/layout-base'
9
+
10
+ class DataEntryGeneratorPopup extends localize(i18next)(LitElement) {
11
+ static get properties() {
12
+ return {
13
+ dataSetId: String,
14
+ numSamples: Number
15
+ }
16
+ }
17
+
18
+ static get styles() {
19
+ return [
20
+ SingleColumnFormStyles,
21
+ css`
22
+ :host {
23
+ padding: 10px;
24
+ display: flex;
25
+ flex-direction: column;
26
+ overflow-x: overlay;
27
+ background-color: var(--main-section-background-color);
28
+ }
29
+ .button-container {
30
+ padding: var(--button-container-padding);
31
+ margin: var(--button-container-margin);
32
+ text-align: var(--button-container-align);
33
+ background-color: var(--button-container-background);
34
+ height: var(--button-container-height);
35
+ }
36
+ `
37
+ ]
38
+ }
39
+
40
+ render() {
41
+ return html`
42
+ <form id="input-form" name="generation" class="single-column-form" @submit=${e => this.onSubmit(e)}>
43
+ <fieldset>
44
+ <label>${i18next.t('label.num_samples')}</label>
45
+ <input type="number" .value=${this.numSamples} min="1" name="num_samples" required/>
46
+ </fieldset>
47
+ </form>
48
+
49
+ <div class="button-container">
50
+ <mwc-button raised @click="${this.generateMockupData}" label="${i18next.t('button.submit')}"></mwc-button>
51
+ </div>
52
+ `
53
+ }
54
+
55
+ async firstUpdated() {
56
+
57
+ }
58
+
59
+ async onSubmit(e) {
60
+ e.preventDefault()
61
+ this.generateMockupData()
62
+ }
63
+
64
+
65
+ serializeFormData() {
66
+ let obj = {}
67
+
68
+ Array.from(this.shadowRoot.querySelectorAll('form#input-form input')).forEach(field => {
69
+ if (!field.hasAttribute('hidden') && field.value) {
70
+ obj[field.name] = field.type === 'checkbox' ? field.checked : field.value
71
+ }
72
+ })
73
+
74
+ return obj
75
+ }
76
+
77
+ async generateMockupData() {
78
+ const params = this.serializeFormData()
79
+
80
+ const response = await client.mutate({
81
+ mutation: gql`
82
+ mutation ($params: DataSampleMockupInfo!) {
83
+ generateMockupData(params: $params)
84
+ }
85
+ `,
86
+ variables: {
87
+ params: {
88
+ dataSetId: this.dataSetId,
89
+ numSamples: parseInt(params['num_samples'])
90
+ }
91
+ }
92
+ })
93
+
94
+ if (!response.errors) {
95
+ this.dispatchEvent(new CustomEvent('created', {}))
96
+
97
+ document.dispatchEvent(
98
+ new CustomEvent('notify', { detail: { message: i18next.t('text.mockup data created successfully') } })
99
+ )
100
+
101
+ }
102
+ }
103
+
104
+ constructor() {
105
+ super()
106
+ this.numSamples = 1
107
+ }
108
+ }
109
+
110
+ window.customElements.define('data-entry-generator-popup', DataEntryGeneratorPopup)
@@ -0,0 +1,277 @@
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 DataItemList extends localize(i18next)(LitElement) {
9
+ static get properties() {
10
+ return {
11
+ dataSet: 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._deleteDataItems.bind(this)}>${i18next.t('button.delete')}</mwc-button>
59
+ <mwc-button raised @click=${this._updateDataItems.bind(this)}>${i18next.t('button.save')}</mwc-button>
60
+ </div>
61
+ `
62
+ }
63
+
64
+ async firstUpdated() {
65
+ this.gristConfig = {
66
+ list: { fields: ['name', 'description', 'active'] },
67
+ columns: [
68
+ { type: 'gutter', gutterName: 'row-selector', multiple: true },
69
+ {
70
+ type: 'gutter',
71
+ gutterName: 'button',
72
+ icon: 'add',
73
+ handlers: {
74
+ click: 'record-copy'
75
+ }
76
+ },
77
+ { type: 'gutter', gutterName: 'sequence' },
78
+ {
79
+ type: 'gutter',
80
+ gutterName: 'button',
81
+ icon: 'arrow_upward',
82
+ handlers: {
83
+ click: 'move-up'
84
+ }
85
+ },
86
+ {
87
+ type: 'gutter',
88
+ gutterName: 'button',
89
+ icon: 'arrow_downward',
90
+ handlers: {
91
+ click: 'move-down'
92
+ }
93
+ },
94
+ {
95
+ type: 'string',
96
+ name: 'name',
97
+ header: i18next.t('field.name'),
98
+ record: {
99
+ editable: true
100
+ },
101
+ width: 140
102
+ },
103
+ {
104
+ type: 'string',
105
+ name: 'description',
106
+ header: i18next.t('field.description'),
107
+ record: {
108
+ editable: true
109
+ },
110
+ width: 180
111
+ },
112
+ {
113
+ type: 'checkbox',
114
+ name: 'active',
115
+ label: true,
116
+ header: i18next.t('field.active'),
117
+ record: {
118
+ editable: true
119
+ },
120
+ sortable: true,
121
+ width: 60
122
+ },
123
+ {
124
+ type: 'checkbox',
125
+ name: 'hidden',
126
+ label: true,
127
+ header: i18next.t('field.hidden'),
128
+ record: {
129
+ editable: true
130
+ },
131
+ sortable: true,
132
+ width: 60
133
+ },
134
+ {
135
+ type: 'string',
136
+ name: 'tag',
137
+ header: i18next.t('field.tag'),
138
+ record: {
139
+ editable: true
140
+ },
141
+ width: 180
142
+ },
143
+ {
144
+ type: 'select',
145
+ name: 'type',
146
+ header: i18next.t('field.type'),
147
+ record: {
148
+ options: ['', 'number', 'text', 'select', 'boolean', 'file'],
149
+ editable: true
150
+ },
151
+ width: 120
152
+ },
153
+ {
154
+ type: 'parameters',
155
+ name: 'options',
156
+ header: i18next.t('field.options'),
157
+ record: {
158
+ editable: true,
159
+ renderer: 'json5',
160
+ options: async (value, column, record, row, field) => {
161
+ return {
162
+ name: record.type,
163
+ help: '',
164
+ spec:
165
+ record.type === 'select'
166
+ ? [
167
+ {
168
+ type: 'options' /* property-editor type */,
169
+ name: 'options',
170
+ label: 'options'
171
+ }
172
+ ]
173
+ : [],
174
+ context: this.grist,
175
+ objectified: true
176
+ }
177
+ }
178
+ },
179
+ width: 120
180
+ },
181
+ {
182
+ type: 'string',
183
+ name: 'unit',
184
+ header: i18next.t('field.unit'),
185
+ record: {
186
+ editable: true
187
+ },
188
+ width: 120
189
+ },
190
+ {
191
+ type: 'number',
192
+ name: 'quota',
193
+ header: i18next.t('field.quota'),
194
+ record: {
195
+ editable: true
196
+ },
197
+ width: 60
198
+ },
199
+ {
200
+ type: 'data-item-spec',
201
+ name: 'spec',
202
+ header: i18next.t('field.spec'),
203
+ record: {
204
+ editable: true,
205
+ options: {
206
+ name: '',
207
+ help: '',
208
+ objectified: true /* prevent from stringifying */
209
+ }
210
+ },
211
+ width: 200
212
+ }
213
+ ],
214
+ rows: {
215
+ selectable: {
216
+ multiple: true
217
+ }
218
+ },
219
+ pagination: {
220
+ infinite: true
221
+ },
222
+ sorters: []
223
+ }
224
+ }
225
+
226
+ async fetchHandler({ filters, page, limit, sortings = [] }) {
227
+ const dataItems = this.dataSet.dataItems || []
228
+
229
+ return {
230
+ total: dataItems.length,
231
+ records: dataItems
232
+ }
233
+ }
234
+
235
+ async _updateDataItems() {
236
+ this.grist.commit()
237
+
238
+ const response = await client.mutate({
239
+ mutation: gql`
240
+ mutation ($id: String!, $patch: DataSetPatch!) {
241
+ updateDataSet(id: $id, patch: $patch) {
242
+ name
243
+ }
244
+ }
245
+ `,
246
+ variables: {
247
+ id: this.dataSet.id,
248
+ patch: {
249
+ dataItems: this.grist.data.records,
250
+ cuFlag: 'M'
251
+ }
252
+ }
253
+ })
254
+
255
+ if (!response.errors) {
256
+ await document.dispatchEvent(
257
+ new CustomEvent('notify', {
258
+ detail: {
259
+ message: i18next.t('text.info_x_successfully', {
260
+ x: i18next.t('button.save')
261
+ })
262
+ }
263
+ })
264
+ )
265
+ }
266
+ }
267
+
268
+ async _deleteDataItems() {
269
+ if (!confirm(i18next.t('text.sure_to_x', { x: i18next.t('text.delete') }))) {
270
+ return
271
+ }
272
+
273
+ this.grist.deleteSelectedRecords(false)
274
+ }
275
+ }
276
+
277
+ window.customElements.define('data-item-list', DataItemList)
@@ -0,0 +1,103 @@
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 DataSetImporter extends LitElement {
11
+ static get properties() {
12
+ return {
13
+ columns: Object,
14
+ scenarios: 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="${{ records: this.scenarios }}"
79
+ ></ox-grist>
80
+
81
+ <div class="button-container">
82
+ <mwc-button raised @click="${this.save.bind(this)}">${i18next.t('button.save')}</mwc-button>
83
+ </div>
84
+ `
85
+ }
86
+
87
+ async save() {
88
+ const response = await client.mutate({
89
+ mutation: gql`
90
+ mutation importDataSets($dataSets: [DataSetPatch!]!) {
91
+ importDataSets(dataSets: $dataSets)
92
+ }
93
+ `,
94
+ variables: { dataSets: this.dataSets }
95
+ })
96
+
97
+ if (response.error?.length) return
98
+
99
+ this.dispatchEvent(new CustomEvent('imported'))
100
+ }
101
+ }
102
+
103
+ customElements.define('data-set-importer', DataSetImporter)