@things-factory/dataset 6.0.67 → 6.0.70

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 (56) hide show
  1. package/client/pages/data-set/data-set-list-page.ts +1 -1
  2. package/client/pages/data-summary/data-summary-list-page.ts +390 -0
  3. package/client/pages/data-summary/data-summary-search-page.ts +417 -0
  4. package/client/pages/data-summary/data-summary-view.ts +119 -0
  5. package/client/route.ts +12 -0
  6. package/dist-client/pages/data-set/data-set-list-page.js +1 -1
  7. package/dist-client/pages/data-set/data-set-list-page.js.map +1 -1
  8. package/dist-client/pages/data-summary/data-summary-importer.d.ts +22 -0
  9. package/dist-client/pages/data-summary/data-summary-importer.js +100 -0
  10. package/dist-client/pages/data-summary/data-summary-importer.js.map +1 -0
  11. package/dist-client/pages/data-summary/data-summary-list-page.d.ts +57 -0
  12. package/dist-client/pages/data-summary/data-summary-list-page.js +389 -0
  13. package/dist-client/pages/data-summary/data-summary-list-page.js.map +1 -0
  14. package/dist-client/pages/data-summary/data-summary-search-page.d.ts +63 -0
  15. package/dist-client/pages/data-summary/data-summary-search-page.js +407 -0
  16. package/dist-client/pages/data-summary/data-summary-search-page.js.map +1 -0
  17. package/dist-client/pages/data-summary/data-summary-view.d.ts +1 -0
  18. package/dist-client/pages/data-summary/data-summary-view.js +100 -0
  19. package/dist-client/pages/data-summary/data-summary-view.js.map +1 -0
  20. package/dist-client/route.js +9 -0
  21. package/dist-client/route.js.map +1 -1
  22. package/dist-client/tsconfig.tsbuildinfo +1 -1
  23. package/dist-server/controllers/create-data-sample.js +1 -1
  24. package/dist-server/controllers/create-data-sample.js.map +1 -1
  25. package/dist-server/controllers/generate-data-summary.js +94 -0
  26. package/dist-server/controllers/generate-data-summary.js.map +1 -0
  27. package/dist-server/controllers/generate-summary-data.js +88 -0
  28. package/dist-server/controllers/generate-summary-data.js.map +1 -0
  29. package/dist-server/service/data-summary/data-summary-mutation.js +28 -0
  30. package/dist-server/service/data-summary/data-summary-mutation.js.map +1 -0
  31. package/dist-server/service/data-summary/data-summary-query.js +136 -0
  32. package/dist-server/service/data-summary/data-summary-query.js.map +1 -0
  33. package/dist-server/service/data-summary/data-summary-type.js +57 -0
  34. package/dist-server/service/data-summary/data-summary-type.js.map +1 -0
  35. package/dist-server/service/data-summary/data-summary.js +188 -0
  36. package/dist-server/service/data-summary/data-summary.js.map +1 -0
  37. package/dist-server/service/data-summary/index.js +10 -0
  38. package/dist-server/service/data-summary/index.js.map +1 -0
  39. package/dist-server/service/index.js +4 -0
  40. package/dist-server/service/index.js.map +1 -1
  41. package/dist-server/tsconfig.tsbuildinfo +1 -1
  42. package/helps/dataset/data-summary.md +160 -0
  43. package/package.json +5 -5
  44. package/server/controllers/create-data-sample.ts +1 -1
  45. package/server/controllers/generate-data-summary.ts +124 -0
  46. package/server/service/data-summary/data-summary-mutation.ts +19 -0
  47. package/server/service/data-summary/data-summary-query.ts +96 -0
  48. package/server/service/data-summary/data-summary-type.ts +40 -0
  49. package/server/service/data-summary/data-summary.ts +165 -0
  50. package/server/service/data-summary/index.ts +7 -0
  51. package/server/service/index.ts +4 -0
  52. package/things-factory.config.js +10 -1
  53. package/translations/en.json +8 -0
  54. package/translations/ko.json +8 -0
  55. package/translations/ms.json +8 -0
  56. package/translations/zh.json +8 -0
@@ -0,0 +1,417 @@
1
+ import '@operato/data-grist'
2
+ import './data-summary-view.js'
3
+
4
+ import gql from 'graphql-tag'
5
+ import { css, html } from 'lit'
6
+ import { customElement, property, query, state } from 'lit/decorators.js'
7
+ import { connect } from 'pwa-helpers/connect-mixin'
8
+
9
+ import {
10
+ getEditor,
11
+ getRenderer,
12
+ ColumnConfig,
13
+ DataGrist,
14
+ FetchOption,
15
+ SortersControl,
16
+ GristRecord
17
+ } from '@operato/data-grist'
18
+ import { client } from '@operato/graphql'
19
+ import { i18next, localize } from '@operato/i18n'
20
+ import { openPopup } from '@operato/layout'
21
+ import { PageView, store } from '@operato/shell'
22
+ import { CommonGristStyles, ScrollbarStyles } from '@operato/styles'
23
+ import { isMobileDevice } from '@operato/utils'
24
+ import { OxPopup } from '@operato/popup'
25
+
26
+ @customElement('data-summary-search-page')
27
+ export class DataSummarySearchPage extends connect(store)(localize(i18next)(PageView)) {
28
+ static styles = [
29
+ ScrollbarStyles,
30
+ CommonGristStyles,
31
+ css`
32
+ :host {
33
+ display: flex;
34
+ flex-direction: column;
35
+
36
+ overflow: hidden;
37
+ }
38
+
39
+ ox-grist {
40
+ overflow-y: auto;
41
+ flex: 1;
42
+ }
43
+ `
44
+ ]
45
+
46
+ @state() dataKeySetId?: string
47
+ @state() dataKeySet: any
48
+
49
+ @state() gristConfig: any
50
+ @state() mode: 'CARD' | 'GRID' | 'LIST' = isMobileDevice() ? 'CARD' : 'GRID'
51
+
52
+ @query('ox-grist') private grist!: DataGrist
53
+ @query('#sorter-control') private sortersControl!: OxPopup
54
+
55
+ get context() {
56
+ return {
57
+ search: {
58
+ handler: (search: string) => {
59
+ this.grist.searchText = search
60
+ },
61
+ placeholder: i18next.t('title.data-summary search'),
62
+ value: this.grist.searchText
63
+ },
64
+ filter: {
65
+ handler: () => {
66
+ this.grist.toggleHeadroom()
67
+ }
68
+ },
69
+ help: 'dataset/data-summary-search',
70
+ actions: [],
71
+ exportable: {
72
+ name: i18next.t('title.data-summary search'),
73
+ data: this._exportableData.bind(this)
74
+ }
75
+ }
76
+ }
77
+
78
+ render() {
79
+ const mode = this.mode || (isMobileDevice() ? 'LIST' : 'GRID')
80
+
81
+ return html`
82
+ <ox-grist
83
+ .mode=${mode}
84
+ .config=${this.gristConfig}
85
+ .fetchHandler=${this.fetchHandler.bind(this)}
86
+ ?url-params-sensitive=${this.active}
87
+ >
88
+ <div slot="headroom">
89
+ <div id="filters">
90
+ <ox-filters-form autofocus without-search></ox-filters-form>
91
+ </div>
92
+
93
+ <div id="sorters">
94
+ Sort
95
+ <mwc-icon
96
+ @click=${e => {
97
+ const target = e.currentTarget
98
+ this.sortersControl.open({
99
+ right: 0,
100
+ top: target.offsetTop + target.offsetHeight
101
+ })
102
+ }}
103
+ >expand_more</mwc-icon
104
+ >
105
+ <ox-popup id="sorter-control">
106
+ <ox-sorters-control> </ox-sorters-control>
107
+ </ox-popup>
108
+ </div>
109
+
110
+ <div id="modes">
111
+ <mwc-icon @click=${() => (this.mode = 'GRID')} ?active=${mode == 'GRID'}>grid_on</mwc-icon>
112
+ <mwc-icon @click=${() => (this.mode = 'LIST')} ?active=${mode == 'LIST'}>format_list_bulleted</mwc-icon>
113
+ <mwc-icon @click=${() => (this.mode = 'CARD')} ?active=${mode == 'CARD'}>apps</mwc-icon>
114
+ </div>
115
+ </div>
116
+ </ox-grist>
117
+ `
118
+ }
119
+
120
+ pageUpdated(changes, lifecycle) {
121
+ if (this.active) {
122
+ this.dataKeySetId = lifecycle.resourceId
123
+ return
124
+ }
125
+ }
126
+
127
+ async updated(changes) {
128
+ if (changes.has('dataKeySetId')) {
129
+ const response = await client.query({
130
+ query: gql`
131
+ query ($id: String!) {
132
+ dataKeySet(id: $id) {
133
+ id
134
+ name
135
+ description
136
+ dataKeyItems {
137
+ name
138
+ description
139
+ dataKey
140
+ tKey
141
+ }
142
+ }
143
+ }
144
+ `,
145
+ variables: {
146
+ id: this.dataKeySetId
147
+ }
148
+ })
149
+
150
+ this.dataKeySet = response.data.dataKeySet
151
+ this.refreshGristConfig()
152
+
153
+ this.grist.fetch()
154
+ }
155
+ }
156
+
157
+ getDataKeyColumns() {
158
+ return (
159
+ this.dataKeySet?.dataKeyItems.map((item, index) => {
160
+ return {
161
+ type: 'string',
162
+ name: `key0${index + 1}`,
163
+ header: i18next.t(item.tKey),
164
+ record: {
165
+ editable: false
166
+ },
167
+ sortable: true,
168
+ filter: 'i_like',
169
+ width: 120,
170
+ imex: true
171
+ }
172
+ }) || []
173
+ )
174
+ }
175
+
176
+ refreshGristConfig() {
177
+ const today = new Date().toISOString().split('T')[0]
178
+
179
+ this.gristConfig = {
180
+ list: { fields: ['dataSet', 'data', 'updater', 'updatedAt'] },
181
+ columns: [
182
+ { type: 'gutter', gutterName: 'sequence' },
183
+ { type: 'gutter', gutterName: 'row-selector', multiple: true },
184
+ {
185
+ type: 'gutter',
186
+ gutterName: 'button',
187
+ icon: 'assignment',
188
+ title: i18next.t('title.open data summary view'),
189
+ handlers: {
190
+ click: (columns, data, column, record, rowIndex) => {
191
+ openPopup(
192
+ html`
193
+ <data-summary-view data-summary-id=${record.id} style="background-color: white;"></data-summary-view>
194
+ `,
195
+ {
196
+ backdrop: true,
197
+ size: 'large',
198
+ title: i18next.t('title.data-summary view')
199
+ }
200
+ )
201
+ }
202
+ }
203
+ },
204
+ {
205
+ type: 'string',
206
+ name: 'name',
207
+ label: true,
208
+ header: i18next.t('field.name'),
209
+ record: {
210
+ editable: false
211
+ },
212
+ filter: 'search',
213
+ sortable: true,
214
+ width: 120,
215
+ imex: true
216
+ },
217
+ {
218
+ type: 'string',
219
+ name: 'description',
220
+ label: true,
221
+ header: i18next.t('field.description'),
222
+ record: {
223
+ editable: false
224
+ },
225
+ filter: 'search',
226
+ width: 150,
227
+ imex: true
228
+ },
229
+ ...this.getDataKeyColumns(),
230
+ {
231
+ type: 'checkbox',
232
+ name: 'ooc',
233
+ header: i18next.t('field.ooc'),
234
+ record: {
235
+ editable: false
236
+ },
237
+ width: 30
238
+ },
239
+ {
240
+ type: 'checkbox',
241
+ name: 'oos',
242
+ header: i18next.t('field.oos'),
243
+ record: {
244
+ editable: false
245
+ },
246
+ width: 30
247
+ },
248
+ {
249
+ type: 'json5',
250
+ name: 'data',
251
+ header: i18next.t('field.data'),
252
+ record: {
253
+ editable: false
254
+ },
255
+ hidden: true,
256
+ imex: true
257
+ },
258
+ {
259
+ type: 'string',
260
+ name: 'workDate',
261
+ header: i18next.t('field.work-date'),
262
+ sortable: true,
263
+ filter: {
264
+ type: 'date',
265
+ operator: 'between',
266
+ value: [today, today]
267
+ },
268
+ width: 80,
269
+ imex: true
270
+ },
271
+ {
272
+ type: 'string',
273
+ name: 'workShift',
274
+ header: i18next.t('field.work-shift'),
275
+ sortable: true,
276
+ width: 60,
277
+ imex: true
278
+ },
279
+ {
280
+ type: 'resource-object',
281
+ name: 'updater',
282
+ header: i18next.t('field.updater'),
283
+ sortable: true,
284
+ width: 120,
285
+ imex: true
286
+ },
287
+ {
288
+ type: 'datetime',
289
+ name: 'updatedAt',
290
+ header: i18next.t('field.updated_at'),
291
+ sortable: true,
292
+ width: 180,
293
+ imex: true
294
+ }
295
+ ],
296
+ rows: {
297
+ appendable: false,
298
+ selectable: {
299
+ multiple: true
300
+ },
301
+ classifier: function (record, rowIndex) {
302
+ var emphasized
303
+ if (record['oos']) {
304
+ emphasized = ['red']
305
+ } else if (record['ooc']) {
306
+ emphasized = 'orange'
307
+ }
308
+
309
+ return {
310
+ emphasized
311
+ }
312
+ }
313
+ },
314
+ sorters: [
315
+ {
316
+ name: 'workDate',
317
+ desc: true
318
+ }
319
+ ]
320
+ }
321
+ }
322
+
323
+ async fetchHandler({ page, limit, sortings = [], filters = [] }: FetchOption) {
324
+ if (!this.dataKeySetId) {
325
+ return { records: [], total: 0 }
326
+ }
327
+
328
+ const response = await client.query({
329
+ query: gql`
330
+ query ($dataKeySetId: String!, $filters: [Filter!], $pagination: Pagination, $sortings: [Sorting!]) {
331
+ responses: dataSummariesByDataKeySet(
332
+ dataKeySetId: $dataKeySetId
333
+ filters: $filters
334
+ pagination: $pagination
335
+ sortings: $sortings
336
+ ) {
337
+ items {
338
+ id
339
+ name
340
+ description
341
+ dataSet {
342
+ id
343
+ }
344
+ workDate
345
+ workShift
346
+ key01
347
+ key02
348
+ key03
349
+ key04
350
+ key05
351
+ summary
352
+ count
353
+ countOoc
354
+ countOos
355
+ updater {
356
+ id
357
+ name
358
+ }
359
+ updatedAt
360
+ }
361
+ total
362
+ }
363
+ }
364
+ `,
365
+ variables: {
366
+ dataKeySetId: this.dataKeySetId,
367
+ filters,
368
+ pagination: { page, limit },
369
+ sortings
370
+ }
371
+ })
372
+
373
+ return {
374
+ total: response.data.responses.total || 0,
375
+ records: response.data.responses.items || []
376
+ }
377
+ }
378
+
379
+ _exportableData() {
380
+ let records = [] as GristRecord[]
381
+ if (this.grist.selected && this.grist.selected.length > 0) {
382
+ records = this.grist.selected
383
+ } else {
384
+ records = this.grist.data.records
385
+ }
386
+
387
+ var headerSetting = this.grist.compiledConfig.columns
388
+ .filter(column => column.type !== 'gutter' && column.record !== undefined && column.imex !== undefined)
389
+ .map(column => {
390
+ return column.imex === true
391
+ ? {
392
+ header: column.header.renderer(column),
393
+ key: column.name,
394
+ width: column.width,
395
+ type: column.type
396
+ }
397
+ : column.imex
398
+ })
399
+
400
+ var data = records.map(item => {
401
+ return {
402
+ id: item.id,
403
+ ...this.gristConfig.columns
404
+ .filter(column => column.type !== 'gutter' && column.record !== undefined && column.imex !== undefined)
405
+ .reduce((record, column) => {
406
+ const key = column.imex === true ? column.name : column.imex.key
407
+ record[key] = key
408
+ .split('.')
409
+ .reduce((obj, key) => (obj && obj[key] !== 'undefined' ? obj[key] : undefined), item)
410
+ return record
411
+ }, {})
412
+ }
413
+ })
414
+
415
+ return { header: headerSetting, data: data }
416
+ }
417
+ }
@@ -0,0 +1,119 @@
1
+ // import '@operato/dataset/ox-data-summary-view.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 { ScrollbarStyles } from '@operato/styles'
10
+ import { DataItem } from '@operato/dataset'
11
+
12
+ type DataSummary = {
13
+ name: string
14
+ description: string
15
+ dataItems?: DataItem[]
16
+ workDate?: string
17
+ workShift?: string
18
+ key01?: string
19
+ key02?: string
20
+ key03?: string
21
+ key04?: string
22
+ key05?: string
23
+ count?: number
24
+ countOoc?: number
25
+ countOos?: number
26
+ summary?: { [key: string]: any }
27
+ }
28
+
29
+ @customElement('data-summary-view')
30
+ class DataSummaryView extends localize(i18next)(LitElement) {
31
+ static styles = [
32
+ ScrollbarStyles,
33
+ css`
34
+ :host {
35
+ display: flex;
36
+ flex-direction: column;
37
+
38
+ background-color: #fff;
39
+ }
40
+
41
+ div[content] {
42
+ flex: 1;
43
+
44
+ display: flex;
45
+ overflow: auto;
46
+ }
47
+
48
+ ox-data-summary-view {
49
+ flex: 1;
50
+ padding: var(--padding-wide);
51
+ overflow: auto;
52
+ }
53
+ `
54
+ ]
55
+
56
+ @property({
57
+ type: String,
58
+ attribute: 'data-summary-id'
59
+ })
60
+ dataSummaryId?: string
61
+
62
+ @state() dataSummary?: DataSummary
63
+
64
+ render() {
65
+ return html`
66
+ <div content>
67
+ ${this.dataSummary
68
+ ? html`<ox-data-summary-view .dataSummary=${this.dataSummary}></ox-data-summary-view>`
69
+ : html``}
70
+ </div>
71
+ `
72
+ }
73
+
74
+ updated(changes) {
75
+ if (changes.has('dataSummaryId')) {
76
+ this.fetchDataSummary()
77
+ }
78
+ }
79
+
80
+ async fetchDataSummary() {
81
+ const id = this.dataSummaryId
82
+
83
+ const response = await client.query({
84
+ query: gql`
85
+ query ($id: String!) {
86
+ dataSummary(id: $id) {
87
+ id
88
+ name
89
+ description
90
+ dataSet {
91
+ id
92
+ }
93
+ workDate
94
+ workShift
95
+ key01
96
+ key02
97
+ key03
98
+ key04
99
+ key05
100
+ data
101
+ count
102
+ countOoc
103
+ countOos
104
+ updater {
105
+ id
106
+ name
107
+ }
108
+ updatedAt
109
+ }
110
+ }
111
+ `,
112
+ variables: {
113
+ id
114
+ }
115
+ })
116
+
117
+ this.dataSummary = response.data.dataSummary
118
+ }
119
+ }
package/client/route.ts CHANGED
@@ -16,6 +16,14 @@ export default function route(page) {
16
16
  import('./pages/data-sample/data-sample-search-page.js')
17
17
  return page
18
18
 
19
+ case 'data-summary-list':
20
+ import('./pages/data-summary/data-summary-list-page.js')
21
+ return page
22
+
23
+ case 'data-summary-search':
24
+ import('./pages/data-summary/data-summary-search-page.js')
25
+ return page
26
+
19
27
  case 'data-report-samples':
20
28
  import('./pages/data-report/data-report-samples-page.js')
21
29
  return page
@@ -59,5 +67,9 @@ export default function route(page) {
59
67
  case 'data-key-set-list':
60
68
  import('./pages/data-key-set/data-key-set-list-page.js')
61
69
  return page
70
+
71
+ case 'data-summary-list':
72
+ import('./pages/data-summary/data-summary-list-page')
73
+ return page
62
74
  }
63
75
  }
@@ -200,7 +200,7 @@ let DataSetListPage = class DataSetListPage extends connect(store)(localize(i18n
200
200
  type: 'gutter',
201
201
  gutterName: 'button',
202
202
  title: i18next.t('title.data collecting schedule (un)register'),
203
- icon: record => (!record || !record.name ? '' : record.scheduleId ? 'pause' : 'play_arrow'),
203
+ icon: record => (!record || record.scheduleId ? 'event_available' : ''),
204
204
  handlers: {
205
205
  click: (columns, data, column, record, rowIndex) => {
206
206
  if (!record || !record.name) {