@mixd-id/web-scaffold 0.1.2301231352 → 0.1.2301231354

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.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@mixd-id/web-scaffold",
3
3
  "private": false,
4
- "version": "0.1.2301231352",
4
+ "version": "0.1.2301231354",
5
5
  "scripts": {
6
6
  "dev": "vite serve",
7
7
  "build": "vite build",
@@ -24,6 +24,7 @@
24
24
  "@vueuse/core": "^9.0.2",
25
25
  "adm-zip": "^0.5.10",
26
26
  "axios": "^1.3.4",
27
+ "chart.js": "^4.2.1",
27
28
  "compression": "^1.7.4",
28
29
  "cookie-parser": "^1.4.6",
29
30
  "cors": "^2.8.5",
@@ -41,13 +42,13 @@
41
42
  "serve-static": "^1.15.0",
42
43
  "tailwindcss": "^3.2.4",
43
44
  "vue": "^3.2.25",
45
+ "vue-chartjs": "^5.2.0",
44
46
  "vue-router": "^4.0.14"
45
47
  },
46
48
  "devDependencies": {
47
49
  "@vitejs/plugin-vue": "^2.2.0",
48
50
  "autoprefixer": "^10.4.4",
49
51
  "postcss": "^8.4.12",
50
- "sequelize": "^6.29.0",
51
52
  "vite": "^2.8.0"
52
53
  },
53
54
  "description": "This scaffold based on express vitejs vuejs",
@@ -56,7 +56,8 @@ export default {
56
56
  return {
57
57
  computedStyle: null,
58
58
  isOpen: false,
59
- context: null
59
+ context: null,
60
+ closing: false
60
61
  }
61
62
  },
62
63
 
@@ -80,16 +81,17 @@ export default {
80
81
  if(typeof window === 'undefined') return
81
82
 
82
83
  const transitionEnd = () => {
83
- this.$emit('open')
84
84
  window.addEventListener('click', this.onDismiss)
85
85
  window.addEventListener('scroll', this.onDismiss)
86
86
  this.$refs.contextMenu.removeEventListener('transitionend', transitionEnd)
87
+ this.$emit('open')
87
88
  }
88
89
 
90
+ this.$refs.contextMenu.removeEventListener('transitionend', transitionEnd)
89
91
  this.$refs.contextMenu.addEventListener('transitionend', transitionEnd)
90
- window.setTimeout(() => {
92
+ this.$nextTick(() => {
91
93
  this.$refs.contextMenu.classList.add(this.$style.active)
92
- }, 77)
94
+ })
93
95
  },
94
96
 
95
97
  onDismiss(e){
@@ -104,6 +106,14 @@ export default {
104
106
 
105
107
  open(caller, context){
106
108
 
109
+ if(this.isOpen){
110
+ return this.close(() => {
111
+ window.setTimeout(() => {
112
+ this.open(caller, context)
113
+ }, 30)
114
+ })
115
+ }
116
+
107
117
  if(caller){
108
118
  this.isOpen = true
109
119
  }
@@ -181,10 +191,9 @@ export default {
181
191
  else{
182
192
  console.warn('Invalid context menu caller', this.caller)
183
193
  }
184
-
185
194
  },
186
195
 
187
- close(){
196
+ close(fn){
188
197
  const transitionEnd = () => {
189
198
  window.removeEventListener('click', this.onDismiss)
190
199
  window.removeEventListener('scroll', this.onDismiss)
@@ -194,7 +203,9 @@ export default {
194
203
  this.computedStyle = null
195
204
  this.isOpen = false
196
205
  this.$emit('dismiss')
206
+ if(fn) fn()
197
207
  }
208
+ this.$refs.contextMenu.removeEventListener('transitionend', transitionEnd)
198
209
  this.$refs.contextMenu.addEventListener('transitionend', transitionEnd)
199
210
  this.$refs.contextMenu.classList.remove(this.$style.active)
200
211
  }
@@ -43,16 +43,13 @@
43
43
  </div>
44
44
  </div>
45
45
 
46
- <VirtualScroll v-if="isMobile.value" :items="items" class="flex-1" @scroll-end="loadNext">
47
- <template #item="{ item, index }">
48
- <slot v-if="$slots.mobile" name="mobile" :item="item" :index="index"></slot>
49
- <div v-else>
50
- <div class="h-[2rem] bg-base-300 rounded-lg my-2"></div>
51
- </div>
52
- </template>
53
- </VirtualScroll>
46
+ <div class="flex-1 mb-4 flex flex-col bg-base-500 border-text-50 border-[1px]" v-if="summary">
47
+ <VirtualTable v-if="preset.summary.mode === 'table'" :items="summaryItems" :columns="summaryColumns" class="flex-1"></VirtualTable>
48
+ <Bar v-else-if="preset.summary.mode === 'bar'" :data="chartData" :options="chartOptions" class="flex-1 w-full p-2"/>
49
+ <Line v-else-if="preset.summary.mode === 'line'" :data="chartData" :options="chartOptions" class="flex-1 w-full p-2"/>
50
+ </div>
54
51
 
55
- <VirtualTable v-else class="flex-1" :columns="presetColumns" :items="items"
52
+ <VirtualTable v-if="!hideDetails" class="flex-1" :columns="presetColumns" :items="items"
56
53
  :appearances="appearances" @scroll-end="loadNext" :pinned="pinned">
57
54
  <template v-for="column in presetColumns" #[colOf(column.key)]="{}">
58
55
  <div :class="getHeader(column)" @click="openColumnOptions(column.key, $event.target.closest('.' + $style.header))">
@@ -163,7 +160,7 @@
163
160
  </div>
164
161
 
165
162
  <div class="flex justify-center">
166
- <Tabs v-model="layout.presetTab" :items="[{text:'Columns',value:'column'},{text:'Filters',value:'filter'},{text:'Sorts',value:'sort'}]" />
163
+ <Tabs v-model="layout.presetTab" :items="tabItems" />
167
164
  </div>
168
165
  </div>
169
166
 
@@ -187,7 +184,7 @@
187
184
  </ListItem>
188
185
  </div>
189
186
 
190
- <div class="flex flex-col" v-if="layout.presetTab === 'filter'">
187
+ <div class="flex flex-col" v-else-if="layout.presetTab === 'filter'">
191
188
  <div v-if="filterableColumns.length > 0">
192
189
  <ListPage1Filter v-if="preset.filters" v-for="filter in preset.filters"
193
190
  :filter="filter" :column="columns[filter.key]"
@@ -206,7 +203,7 @@
206
203
  </div>
207
204
  </div>
208
205
 
209
- <div class="flex flex-col" v-if="layout.presetTab === 'sort'">
206
+ <div class="flex flex-col" v-else-if="layout.presetTab === 'sort'">
210
207
  <div v-if="sortableColumns.length > 0" class="my-8">
211
208
  <div v-for="sort in preset.sorts" class="py-4">
212
209
  <div class="flex flex-row items-center gap-2">
@@ -244,9 +241,85 @@
244
241
  </div>
245
242
  </div>
246
243
 
247
- </div>
244
+ <div class="flex flex-col gap-3 py-8" v-else-if="layout.presetTab === 'summary'">
245
+
246
+ <div class="flex flex-row items-center p-3">
247
+ <label class="text-text-400 flex-1">Enabled</label>
248
+ <Checkbox v-model="preset.summary.enabled" @change="load"/>
249
+ </div>
250
+
251
+ <div class="flex flex-row items-center p-3">
252
+ <label class="text-text-400 flex-1">Hide Details</label>
253
+ <Checkbox v-model="preset.summary.hideDetails" @change="load"/>
254
+ </div>
255
+
256
+ <div class="p-3">
257
+ <label class="text-text-400 flex-1">Type</label>
258
+ <div class="mt-2">
259
+ <Dropdown v-model="preset.summary.mode" class="flex-1" @change="load">
260
+ <option value="table">{{ $t('Table') }}</option>
261
+ <option value="bar">{{ $t('Bar Chart') }}</option>
262
+ <option value="line">{{ $t('Line Chart') }}</option>
263
+ </Dropdown>
264
+ </div>
265
+ </div>
266
+
267
+ <div class="p-3">
268
+ <label class="text-text-400 flex-1">{{ preset.summary.mode === 'table' ? $t('Row') : $t('X-axis') }}</label>
269
+ <div class="flex flex-row mt-2 gap-2">
270
+ <Dropdown v-model="preset.summary.rows[0].key" class="flex-1" @change="load">
271
+ <option value="" disabled selected>{{ $t('Add Column') }}</option>
272
+ <option v-for="column in filterableColumns" :value="column.key">{{ column.label }}</option>
273
+ </Dropdown>
274
+ <Dropdown v-model="preset.summary.rows[0].format" class="w-[100px]" @change="load">
275
+ <option value="">{{ $t('Default') }}</option>
276
+ <option value="date">{{ $t('Date') }}</option>
277
+ <option value="month">{{ $t('Month') }}</option>
278
+ <option value="year">{{ $t('Year') }}</option>
279
+ </Dropdown>
280
+ </div>
281
+ <Checkbox class="mt-2" v-if="preset.summary.mode === 'table'" v-model="preset.summary.showRowTotal">
282
+ Row Total
283
+ </Checkbox>
284
+ </div>
248
285
 
249
- <div class="px-6 py-3 flex flex-row gap-2"></div>
286
+ <div class="p-3">
287
+ <label class="text-text-400 flex-1">{{ preset.summary.mode === 'table' ? $t('Column') : $t('Dimension') }}</label>
288
+ <div class="flex flex-row mt-2 gap-2">
289
+ <Dropdown v-model="preset.summary.columns[0].key" class="flex-1" @change="load">
290
+ <option value="" disabled selected>{{ $t('Add Column') }}</option>
291
+ <option value="(none)">{{ $t('None') }}</option>
292
+ <option v-for="column in filterableColumns" :value="column.key">{{ column.label }}</option>
293
+ </Dropdown>
294
+ <Dropdown v-model="preset.summary.columns[0].format" class="w-[100px]" @change="load">
295
+ <option value="">{{ $t('Default') }}</option>
296
+ <option value="date">{{ $t('Date') }}</option>
297
+ </Dropdown>
298
+ </div>
299
+ <Checkbox class="mt-2" v-if="preset.summary.mode === 'table'" v-model="preset.summary.showColumnTotal">
300
+ Column Total
301
+ </Checkbox>
302
+ <Checkbox class="mt-2" v-else-if="preset.summary.mode === 'bar'" v-model="preset.summary.stacked">
303
+ Stacked
304
+ </Checkbox>
305
+ </div>
306
+
307
+ <div class="p-3">
308
+ <label class="text-text-400 flex-1">Values</label>
309
+ <div class="flex flex-row mt-2 gap-2">
310
+ <Dropdown v-model="preset.summary.values[0].aggregrate" class="flex-1" @change="load">
311
+ <option value="" disabled selected>{{ $t('Select') }}</option>
312
+ <option value="count">{{ $t('Count') }}</option>
313
+ <option value="max">{{ $t('Max') }}</option>
314
+ <option value="min">{{ $t('Min') }}</option>
315
+ <option value="avg">{{ $t('Avg') }}</option>
316
+ </Dropdown>
317
+ </div>
318
+ </div>
319
+
320
+ </div>
321
+
322
+ </div>
250
323
 
251
324
  </div>
252
325
 
@@ -261,9 +334,14 @@
261
334
 
262
335
  import throttle from "lodash/throttle";
263
336
  import {urlQuery} from "../utils/helpers.mjs";
337
+ import { Bar, Line } from 'vue-chartjs'
338
+ import Chart from 'chart.js/auto'
339
+ import dayjs from "dayjs";
264
340
 
265
341
  export default{
266
342
 
343
+ components: { Bar, Line },
344
+
267
345
  inject: [ 'socket', 'confirm', 'alert', 'socketEmit', 'toast' ],
268
346
 
269
347
  props:{
@@ -345,12 +423,169 @@ export default{
345
423
  currentFilters(){
346
424
  if(!this.preset.filters) return []
347
425
  return this.preset.filters.filter((_) => _.key === this.selectedColumn)
426
+ },
427
+
428
+ chartOptions(){
429
+
430
+ var style = getComputedStyle(document.body)
431
+ var gridColor = style.getPropertyValue('--text-50')
432
+ var gridColor2 = style.getPropertyValue('--text-200')
433
+
434
+ const baseColumn = this.summary.columns[0]
435
+ const baseColumnType = baseColumn.type
436
+
437
+ let highGrids = []
438
+ if(baseColumnType === 'date'){
439
+ this.summary.items.forEach((item) => {
440
+ if(dayjs(item.dfDate).day() === 1){
441
+ highGrids.push(dayjs(item.dfDate).format('D MMM'))
442
+ }
443
+ })
444
+ }
445
+
446
+ return {
447
+ responsive: true,
448
+ maintainAspectRatio: false,
449
+ plugins: {
450
+ legend: true
451
+ },
452
+ scales: {
453
+ x: {
454
+ grid: {
455
+ color: function(context){
456
+ if(baseColumnType === 'date' && context.tick && highGrids.includes(context.tick.label)){
457
+ return `rgb(${gridColor2})`
458
+ }
459
+ return `rgb(${gridColor})`
460
+ }
461
+ },
462
+ stacked: this.preset.summary.stacked
463
+ },
464
+ y: {
465
+ grid: {
466
+ color: function(context){
467
+ return `rgb(${gridColor})`
468
+ }
469
+ }
470
+ }
471
+ }
472
+ }
473
+ },
474
+
475
+ chartData(){
476
+
477
+ //console.log(this.summary)
478
+
479
+ const column = this.summary.columns.filter((_) => _.key === 'dfDate').pop()
480
+
481
+ const labels = []
482
+ this.summary.items.forEach((item) => {
483
+
484
+ let label = item.dfDate
485
+ switch(column.type){
486
+
487
+ case 'date':
488
+ const dateFormat = column.format ?? 'D MMM YY HH:mm:ss'
489
+ const djs = dayjs(item.dfDate)
490
+ label = djs.isValid() ? djs.format(dateFormat) : item.dfDate
491
+ break
492
+ }
493
+ labels.push(label)
494
+ })
495
+
496
+ const datasets = []
497
+ this.summary.columns.forEach((column) => {
498
+ if(column.key === 'dfDate') return
499
+
500
+ const data = []
501
+ this.summary.items.forEach((item) => {
502
+ data.push(parseInt(item[column.key]))
503
+ })
504
+
505
+ const dataset = {
506
+ label: column.key,
507
+ data,
508
+ backgroundColor: this.chartOpt.backgroundColors[datasets.length % 9],
509
+ borderColor: this.chartOpt.borderColors[datasets.length % 9]
510
+ }
511
+
512
+ datasets.push(dataset)
513
+ })
514
+
515
+ //console.log(labels)
516
+ //console.log(datasets)
517
+
518
+ return {
519
+ labels,
520
+ datasets
521
+ }
522
+
523
+ /*return {
524
+ labels: [ 'January', 'February', 'March' ],
525
+ datasets: [ { data: [40, 20, 12] } ]
526
+ }*/
527
+ },
528
+
529
+ hideDetails(){
530
+ if(this.preset && this.preset.summary && this.preset.summary.hideDetails)
531
+ return this.preset.summary.hideDetails
532
+ return false;
533
+ },
534
+
535
+ summaryColumns(){
536
+
537
+ const columns = [ ...this.summary.columns ]
538
+
539
+ if(this.preset.summary.showColumnTotal){
540
+ columns.push({ key:"_total", label:"Total", visible:true, type:"number" })
541
+ }
542
+
543
+ return columns
544
+ },
545
+
546
+ summaryItems(){
547
+
548
+ const items = [ ...this.summary.items ]
549
+
550
+ //console.log('#1', this.preset.summary.showColumnTotal, this.preset.summary.showRowTotal, items.length)
551
+
552
+ if(this.preset.summary.showColumnTotal){
553
+ items.forEach((item) => {
554
+ let total = 0
555
+ for(let key in item){
556
+ if([ 'dfDate', '_total' ].includes(key)) continue
557
+ total += parseInt(item[key])
558
+ }
559
+ item._total = total
560
+ })
561
+ }
562
+
563
+ if(this.preset.summary.showRowTotal){
564
+ const totalItem = { dfDate:"Total" }
565
+ this.summary.items.forEach((item) => {
566
+
567
+ for(let key in item){
568
+ if([ 'dfDate' ].includes(key)) continue
569
+
570
+ if(!(key in totalItem))
571
+ totalItem[key] = 0
572
+ totalItem[key] += parseInt(item[key])
573
+ }
574
+ })
575
+ items.push(totalItem)
576
+ }
577
+
578
+ return items
348
579
  }
349
580
 
350
581
  },
351
582
 
352
583
  data(){
353
584
  return {
585
+ chartOpt: {
586
+ backgroundColors: [ '#5D9CEC', '#4FC1E9', '#48CFAD', '#A0D468', '#FFCE54', '#FC6E51', '#ED5565', '#AC92EC', '#EC87C0' ],
587
+ borderColors: [ '#4A89DC', '#3BAFDA', '#37BC9B', '#8CC152', '#F6BB42', '#E9573F', '#DA4453', '#967ADC', '#D770AD' ],
588
+ },
354
589
  layout: {
355
590
  presetOpen: false,
356
591
  presetTab: 'column',
@@ -358,12 +593,19 @@ export default{
358
593
  presetFilterSelector: null,
359
594
  presetSortSelector: null,
360
595
  },
596
+ tabItems: [
597
+ { text:'Columns', value:'column' },
598
+ { text:'Filters', value:'filter' },
599
+ { text:'Sorts', value:'sort' },
600
+ { text:'Summary', value:'summary' },
601
+ ],
361
602
  name: null,
362
603
  columns: [],
363
604
  selectedColumn: null,
364
605
  presets: [],
365
606
  presetIdx: -1,
366
607
  items: [],
608
+ summary: null,
367
609
  count: null,
368
610
  hasNext: false,
369
611
  modal: null
@@ -399,6 +641,7 @@ export default{
399
641
  this.patchPresets()
400
642
  this.$nextTick(() => {
401
643
  this.items = data.items
644
+ this.summary = data.summary
402
645
  this.hasNext = data.hasNext
403
646
  this.count = data.count
404
647
  })
@@ -452,8 +695,10 @@ export default{
452
695
  preset: this.preset
453
696
  }, (res) => {
454
697
  const data = res.data ? res.data : res
455
- //console.log('load', data)
456
- Object.assign(this.$data, data)
698
+ this.items = data.items
699
+ this.summary = data.summary
700
+ this.hasNext = data.hasNext
701
+ this.count = data.count
457
702
  })
458
703
  },
459
704
 
@@ -331,12 +331,13 @@ export default{
331
331
  }
332
332
 
333
333
  let text = value
334
+ let dateFormat, val
334
335
  switch(column.type){
335
336
 
336
337
  case 'date':
337
- const dateFormat = column.format ?? 'DD MMM YY HH:mm'
338
- const val = dayjs(value)
339
- text = val.isValid() ? val.format(dateFormat) : ''
338
+ dateFormat = column.format ?? 'D MMM YY HH:mm:ss'
339
+ val = dayjs(value)
340
+ text = val.isValid() ? val.format(dateFormat) : value
340
341
  break
341
342
 
342
343
  case 'currency':
@@ -1,5 +1,5 @@
1
- const { Op } = require('sequelize')
2
- const Sequelize = require('sequelize')
1
+ const { Op } = module.parent.require('sequelize')
2
+ const Sequelize = module.parent.require('sequelize')
3
3
  const dayjs = require("dayjs");
4
4
  const util = require("util");
5
5
 
@@ -107,11 +107,12 @@ let ListPage1 = {
107
107
  }
108
108
 
109
109
  if(preset.search){
110
+ const tableName = this.model.tableName
110
111
  where = {
111
112
  ...where,
112
- [Op.and]: Sequelize.literal('MATCH(tag) AGAINST (? IN BOOLEAN MODE)')
113
+ [Op.and]: Sequelize.literal(`MATCH(\`${tableName}\`.tag) AGAINST (? IN BOOLEAN MODE)`)
113
114
  }
114
- replacements.push(ftW)
115
+ replacements.push(preset.search)
115
116
  }
116
117
 
117
118
  if(afterItem){
@@ -124,7 +125,7 @@ let ListPage1 = {
124
125
 
125
126
  let attributeIncludes
126
127
  if(this.getAttributeIncludes){
127
- attributeIncludes = this.getAttributeIncludes()
128
+ attributeIncludes = this.getAttributeIncludes(preset)
128
129
  }
129
130
 
130
131
  const { rows:items, count } = await this.model.findAndCountAll({
@@ -136,7 +137,7 @@ let ListPage1 = {
136
137
  order,
137
138
  limit: itemsPerPage + 1,
138
139
  replacements,
139
- include: this.getModelIncludes ? this.getModelIncludes() : undefined
140
+ include: this.getModelIncludes ? this.getModelIncludes(preset) : undefined
140
141
  })
141
142
 
142
143
  const hasNext = items.length > itemsPerPage
@@ -144,6 +145,14 @@ let ListPage1 = {
144
145
  items.splice(items.length - 1, 1)
145
146
  }
146
147
 
148
+ let summary
149
+ if(!afterItem && preset.summary && preset.summary.enabled){
150
+ summary = await this.loadSummary(preset, {
151
+ where,
152
+ replacements
153
+ })
154
+ }
155
+
147
156
  if(this.socket){
148
157
  this.socket.joinWithArgs(this.channel, { preset })
149
158
  }
@@ -151,8 +160,157 @@ let ListPage1 = {
151
160
  return {
152
161
  items,
153
162
  hasNext,
154
- count
163
+ count,
164
+ summary
165
+ }
166
+ },
167
+
168
+ async loadSummary(preset, modelParams){
169
+
170
+ const { columns, rows, values } = preset.summary
171
+
172
+ if(!Array.isArray(columns) || columns.length !== 1) return
173
+ if(!Array.isArray(rows) || rows.length !== 1) return
174
+ if(!Array.isArray(values) || values.length !== 1) return
175
+
176
+ const col = columns[0]
177
+ let columnValues = []
178
+ let colKey
179
+ if(col.key !== '(none)'){
180
+ colKey = this.model.rawAttributes[col.key].field
181
+ columnValues = await this.model.findAll({
182
+ attributes: [
183
+ [ Sequelize.fn("DISTINCT", Sequelize.col(colKey)), 'cols' ]
184
+ ],
185
+ order: [
186
+ [ colKey, 'asc' ]
187
+ ],
188
+ ...modelParams
189
+ })
190
+ //console.log(columnValues)
191
+ }
192
+
193
+ const attributes = []
194
+ const group = []
195
+ const summaryColumns = [
196
+ /*{ key:"dfDate", label:"Date", width:"200px", visible:true },
197
+ { key:"type-1", label:"Type 1", type:"number", visible:true },
198
+ { key:"type-2", label:"Type 2", type:"number", visible:true },
199
+ { key:"type-3", label:"Type 3", type:"number", visible:true },
200
+ { key:"type-4", label:"Type 4", type:"number", visible:true },
201
+ { key:"type-8", label:"Type 8", type:"number", visible:true },*/
202
+ ]
203
+
204
+ const row = rows[0]
205
+ const rowKey = this.model.rawAttributes[row.key].field
206
+ const rowLabel = this.columns[row.key].label
207
+ switch(row.format){
208
+ case 'date':
209
+ attributes.push([Sequelize.fn("DATE_FORMAT", Sequelize.col(rowKey), '%Y-%m-%d'), "dfDate"])
210
+ group.push([Sequelize.fn("DATE_FORMAT", Sequelize.col(rowKey), '%Y-%m-%d'), "dfDate"])
211
+ summaryColumns.push({ key:"dfDate", label:rowLabel, width:"200px", visible:true, type:"date", format:"D MMM" })
212
+ break
213
+
214
+ case 'month':
215
+ attributes.push([Sequelize.fn("DATE_FORMAT", Sequelize.col(rowKey), '%Y-%m'), "dfDate"])
216
+ group.push([Sequelize.fn("DATE_FORMAT", Sequelize.col(rowKey), '%Y-%m'), "dfDate"])
217
+ summaryColumns.push({ key:"dfDate", label:rowLabel, width:"200px", visible:true, type:"date", format:"MMM YYYY" })
218
+ break
219
+
220
+ case 'year':
221
+ attributes.push([Sequelize.fn("DATE_FORMAT", Sequelize.col(rowKey), '%Y'), "dfDate"])
222
+ group.push([Sequelize.fn("DATE_FORMAT", Sequelize.col(rowKey), '%Y'), "dfDate"])
223
+ summaryColumns.push({ key:"dfDate", label:rowLabel, width:"200px", visible:true, type:"date", format:"YYYY" })
224
+ break
225
+
226
+ default:
227
+ attributes.push([Sequelize.col(rowKey), "dfDate"])
228
+ group.push([Sequelize.col(rowKey), "dfDate"])
229
+ summaryColumns.push({ key:"dfDate", label:rowLabel, width:"200px", visible:true })
230
+ break
155
231
  }
232
+
233
+ const value = values[0]
234
+ if(columnValues.length > 0){
235
+ columnValues.forEach((obj) => {
236
+ const columnValue = obj.dataValues['cols']
237
+
238
+ switch(value.aggregrate){
239
+ case 'count':
240
+ attributes.push([Sequelize.fn("SUM", Sequelize.fn("IF", Sequelize.literal(`${colKey} = '${columnValue}'`), 1, 0)), colKey + "-" + columnValue])
241
+ summaryColumns.push({ key:colKey + "-" + columnValue, label:(columnValue ?? '').toString(), visible:true, aggregrate:"count" })
242
+ break
243
+
244
+ case 'min':
245
+ attributes.push([Sequelize.fn("MIN", colKey), colKey + "-" + columnValue])
246
+ summaryColumns.push({ key:colKey + "-" + columnValue, label:(columnValue ?? '').toString(), visible:true, aggregrate:"count" })
247
+ break
248
+
249
+ case 'max':
250
+ attributes.push([Sequelize.fn("MAX", colKey), colKey + "-" + columnValue])
251
+ summaryColumns.push({ key:colKey + "-" + columnValue, label:(columnValue ?? '').toString(), visible:true, aggregrate:"count" })
252
+ break
253
+
254
+ case 'avg':
255
+ attributes.push([Sequelize.fn("AVG", colKey), colKey + "-" + columnValue])
256
+ summaryColumns.push({ key:colKey + "-" + columnValue, label:(columnValue ?? '').toString(), visible:true, aggregrate:"count" })
257
+ break
258
+ }
259
+ })
260
+ }
261
+ else{
262
+ switch(value.aggregrate) {
263
+ case 'count':
264
+ default:
265
+ attributes.push([Sequelize.fn('count', '*'), 'Count'])
266
+ summaryColumns.push({ key:'Count', label:'Count', type:"number", visible:true, aggregrate:"count" })
267
+ }
268
+ }
269
+
270
+ //console.log(attributes)
271
+
272
+ const summaryItems = await this.model.findAll({
273
+ attributes,
274
+ group,
275
+ order: [
276
+ [ 'dfDate', 'asc' ]
277
+ ],
278
+ ...modelParams
279
+ })
280
+ /*const summaryItems = await this.model.findAll({
281
+ attributes: [
282
+ [Sequelize.fn("DATE_FORMAT", Sequelize.col("created_at"), '%Y-%m-%d'), "dfDate"],
283
+ [Sequelize.fn("SUM", Sequelize.fn("IF", Sequelize.literal("type = 1"), 1, 0)), "type-1"],
284
+ [Sequelize.literal("SUM(IF(type = 2, 1, 0))"), "type-2"],
285
+ [Sequelize.literal("SUM(IF(type = 3, 1, 0))"), "type-3"],
286
+ [Sequelize.literal("SUM(IF(type = 4, 1, 0))"), "type-4"],
287
+ [Sequelize.literal("SUM(IF(type = 8, 1, 0))"), "type-8"],
288
+ ],
289
+ group: [
290
+ [Sequelize.fn("DATE_FORMAT", Sequelize.col("created_at"), '%Y-%m-%d')]
291
+ ],
292
+ order: [
293
+ [ 'dfDate', 'asc' ]
294
+ ],
295
+ ...modelParams
296
+ })*/
297
+ //console.log(summaryItems)
298
+
299
+ const summary = {
300
+ type: "table",
301
+ columns: summaryColumns,
302
+ items: summaryItems
303
+ /*items: [
304
+ { date:"21 Jan 23", "type-1":12, "type-2":11, "type-3":16, "type-4":2, "type-8":8 },
305
+ { date:"22 Jan 23", "type-1":51, "type-2":87, "type-3":86, "type-4":3, "type-8":9 },
306
+ { date:"23 Jan 23", "type-1":14, "type-2":66, "type-3":77, "type-4":4, "type-8":18 },
307
+ { date:"24 Jan 23", "type-1":7, "type-2":3, "type-3":63, "type-4":5, "type-8":28 },
308
+ { date:"25 Jan 23", "type-1":88, "type-2":55, "type-3":68, "type-4":6, "type-8":38 },
309
+ { date:"26 Jan 23", "type-1":172, "type-2":13, "type-3":6, "type-4":7, "type-8":48 }
310
+ ]*/
311
+ }
312
+
313
+ return summary
156
314
  },
157
315
 
158
316
  async subscribe(params, socket){