@things-factory/dataset 6.0.81 → 6.0.83

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