@mixd-id/web-scaffold 0.1.230406061 → 0.1.230406062

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.230406061",
4
+ "version": "0.1.230406062",
5
5
  "scripts": {
6
6
  "dev": "vite serve",
7
7
  "build": "vite build",
@@ -43,9 +43,12 @@ export default {
43
43
  if(Array.isArray(this.modelValue)){
44
44
  return this.modelValue.includes(this.value)
45
45
  }
46
- else{
46
+ else if(this.modelValue){
47
47
  return this.value === this.modelValue
48
48
  }
49
+ else{
50
+ return !!this.value
51
+ }
49
52
  }
50
53
  else if(this.trueValue && this.falseValue) {
51
54
  return this.modelValue === this.trueValue
@@ -75,13 +75,12 @@
75
75
  </Textbox>
76
76
  </div>
77
77
 
78
- <div class="grid grid-cols-4 gap-4 hidden">
79
- <div v-for="i in 4">
80
- <div class="p-3 rounded-lg bg-base-400 border-[1px] border-text-50">
81
- <label>Value</label>
82
- <h2>23,000</h2>
83
- </div>
84
- </div>
78
+ <div v-if="presetSummary && summary" class="h-[300px] max-h-[30vh] flex">
79
+ <Bar v-if="presetSummary.mode === 'bar'" :data="chartData" :options="chartOptions" class="flex-1 w-full p-2"/>
80
+ <Line v-else-if="presetSummary.mode === 'line'" :data="chartData" :options="chartOptions" class="flex-1 w-full p-2"/>
81
+ <VirtualTable v-else-if="presetSummary.mode === 'table'" :items="summaryItems" :columns="summaryColumns" class="flex-1"></VirtualTable>
82
+ <Gmaps v-else-if="presetSummary.mode === 'map'" :data="summary.items" :config="presetSummary.map"
83
+ class="flex-1 w-full p-2" :apiKey="mapApiKey" />
85
84
  </div>
86
85
 
87
86
  <div class="flex-1 flex" v-if="mediaPrefix">
@@ -168,8 +167,12 @@
168
167
  <script>
169
168
 
170
169
  import throttle from "lodash/throttle";
170
+ import { Bar, Line } from 'vue-chartjs'
171
+ import Chart from 'chart.js/auto'
172
+ import dayjs from "dayjs";
171
173
 
172
174
  export default{
175
+ components: {Line, Bar},
173
176
 
174
177
  emits: [ ],
175
178
 
@@ -281,15 +284,13 @@ export default{
281
284
  if(!this.src) return
282
285
 
283
286
  this.$refs.table1 ? this.$refs.table1.setState(2) : this.$refs.table2.setState(3)
284
- this.socketEmit2(this.src, { preset: this.preset })
287
+ this.socketEmit2(this.src, { columns:this.config.columns, preset:this.preset })
285
288
  .then((res) => {
286
289
  Object.assign(this.$data, res)
287
290
  })
288
291
  .finally(() => {
289
292
  this.$refs.table1 ? this.$refs.table1.setState(1) : this.$refs.table2.setState(1)
290
293
  })
291
-
292
- this.loadSummary()
293
294
  },
294
295
 
295
296
  loadNext(){
@@ -314,6 +315,10 @@ export default{
314
315
 
315
316
  if(!this.configStoreObj || 'reset' in ((this.$route ?? {}).query ?? {})){
316
317
  this.configLoaded = true
318
+
319
+ if('reset' in ((this.$route ?? {}).query ?? {}))
320
+ await this.saveConfig()
321
+
317
322
  return
318
323
  }
319
324
 
@@ -339,8 +344,9 @@ export default{
339
344
  preset: this.preset
340
345
  })
341
346
  .then((res) => {
342
- const data = res && res.data ? res.data : res
343
- this.summary = data
347
+ console.log('res', res)
348
+ /*const data = res && res.data ? res.data : res
349
+ this.summary = data*/
344
350
  })
345
351
  .catch((err) => {
346
352
  this.toast(err)
@@ -348,20 +354,46 @@ export default{
348
354
  },
349
355
 
350
356
  onHooks(model, event, items){
357
+
351
358
  if(model === this.subscriptionObj.model){
352
359
  switch(event){
353
360
 
354
361
  case 'create':
355
362
  case 'update':
356
- items.forEach((item) => {
357
- this.$util.unshift(this.items, item)
358
- })
363
+ if(Object.keys(items[0]).length <= 1 && Object.keys(items[0])[0] === 'uid'){
364
+ this.socketEmit2(this.src, {
365
+ columns:this.config.columns,
366
+ preset:{
367
+ ...this.preset,
368
+ filters: [{
369
+ enabled: true,
370
+ key: "uid",
371
+ filters: [
372
+ {
373
+ operator: "in",
374
+ value: items.map((_) => _.uid)
375
+ }
376
+ ]
377
+ }
378
+ ]
379
+ }
380
+ })
381
+ .then(({ items }) => {
382
+ this.$util.unshift(this.items, ...items)
383
+ })
384
+ }
385
+ else{
386
+ this.$util.unshift(this.items, ...items)
387
+ }
359
388
  break
360
389
 
361
390
  case 'remove':
362
391
  case 'destroy':
363
392
  items.forEach((item) => {
364
- const idx = this.items.findIndex((_) => _.id === item.id)
393
+ const idx = this.items.findIndex((_) => {
394
+ return item.uid ? _.uid === item.uid :
395
+ (item.id ? _.id === item.id : false)
396
+ })
365
397
  if(idx >= 0){
366
398
  this.items.splice(idx, 1)
367
399
  }
@@ -439,7 +471,54 @@ export default{
439
471
  this.socketEmit2(method, { name:model }).then()
440
472
  break
441
473
  }
442
- }
474
+ },
475
+
476
+ summaryColumns(){
477
+
478
+ const columns = [ ...this.summary.columns ]
479
+
480
+ if(this.presetSummary.mode === 'table' && this.presetSummary.table.showColumnTotal){
481
+ columns.push({ key:"_total", label:"Total", visible:true, type:"number" })
482
+ }
483
+
484
+ return columns
485
+ },
486
+
487
+ summaryItems(){
488
+
489
+ const items = [ ...this.summary.items ]
490
+
491
+ //console.log('#1', this.presetSummary.showColumnTotal, this.presetSummary.showRowTotal, items.length)
492
+
493
+ if(this.presetSummary.mode === 'table' && this.presetSummary.table.showColumnTotal){
494
+ items.forEach((item) => {
495
+ let total = 0
496
+ for(let key in item){
497
+ if([ 'dfDate', '_total' ].includes(key)) continue
498
+ total += parseInt(item[key])
499
+ }
500
+ item._total = total
501
+ })
502
+ }
503
+
504
+
505
+ if(this.presetSummary.mode === 'table' && this.presetSummary.table.showRowTotal){
506
+ const totalItem = { dfDate:"Total" }
507
+ this.summary.items.forEach((item) => {
508
+
509
+ for(let key in item){
510
+ if([ 'dfDate' ].includes(key)) continue
511
+
512
+ if(!(key in totalItem))
513
+ totalItem[key] = 0
514
+ totalItem[key] += parseInt(item[key])
515
+ }
516
+ })
517
+ items.push(totalItem)
518
+ }
519
+
520
+ return items
521
+ },
443
522
 
444
523
  },
445
524
 
@@ -447,6 +526,128 @@ export default{
447
526
 
448
527
  computed: {
449
528
 
529
+
530
+ chartOptions(){
531
+
532
+ var style = getComputedStyle(document.body)
533
+ var gridColor = style.getPropertyValue('--text-50')
534
+ var gridColor2 = style.getPropertyValue('--text-200')
535
+
536
+ const baseColumn = this.summary.columns[0] ?? {}
537
+ const baseColumnType = baseColumn.type
538
+
539
+ let highGrids = []
540
+ if(baseColumnType === 'date'){
541
+ this.summary.items.forEach((item) => {
542
+ if(dayjs(item.dfDate).day() === 1){
543
+ highGrids.push(dayjs(item.dfDate).format('D MMM'))
544
+ }
545
+ })
546
+ }
547
+
548
+ return {
549
+ responsive: true,
550
+ maintainAspectRatio: false,
551
+ plugins: {
552
+ legend: !this.presetSummary.hideLegends,
553
+ tooltip: {
554
+ callbacks: {
555
+ label: function(context) {
556
+
557
+ const labels = []
558
+
559
+ labels.push(context.dataset.label + ': ' + context.parsed.y)
560
+
561
+ if(context.parsed._stacks){
562
+ let total = 0
563
+ let percent = 0
564
+ for(let key in context.parsed._stacks.y){
565
+ if(!isNaN(parseInt(key))){
566
+ total += parseInt(context.parsed._stacks.y[key])
567
+ }
568
+ }
569
+ percent = Math.round(parseInt(context.parsed.y) / total * 100)
570
+ labels.push(`Total: ${total} (${percent}%)`)
571
+ }
572
+
573
+ return labels;
574
+ }
575
+ }
576
+ }
577
+ },
578
+ scales: {
579
+ x: {
580
+ grid: {
581
+ color: function(context){
582
+ if(baseColumnType === 'date' && context.tick && highGrids.includes(context.tick.label)){
583
+ return `rgb(${gridColor2})`
584
+ }
585
+ return `rgb(${gridColor})`
586
+ }
587
+ },
588
+ stacked: this.presetSummary.mode === 'bar' ? this.presetSummary[this.presetSummary.mode].stacked : undefined
589
+ },
590
+ y: {
591
+ grid: {
592
+ color: function(context){
593
+ return `rgb(${gridColor})`
594
+ }
595
+ },
596
+ stacked: this.presetSummary.mode === 'bar' ? this.presetSummary[this.presetSummary.mode].stacked : undefined
597
+ }
598
+ }
599
+ }
600
+ },
601
+
602
+ chartData(){
603
+
604
+ const column = this.summary.columns.filter((_) => _.key === 'dfDate').pop() ?? {}
605
+
606
+ const labels = []
607
+ this.summary.items.forEach((item) => {
608
+
609
+ let label = item.dfDate
610
+ switch(column.type){
611
+
612
+ case 'date':
613
+ const dateFormat = column.format ?? 'D MMM YY HH:mm:ss'
614
+ const djs = dayjs(item.dfDate)
615
+ label = djs.isValid() ? djs.format(dateFormat) : item.dfDate
616
+ break
617
+ }
618
+ labels.push(label)
619
+ })
620
+
621
+ const datasets = []
622
+ this.summary.columns.forEach((column) => {
623
+ if(column.key === 'dfDate') return
624
+
625
+ const data = []
626
+ this.summary.items.forEach((item) => {
627
+ data.push(parseInt(item[column.key]))
628
+ })
629
+
630
+ const dataset = {
631
+ label: column.label,
632
+ data,
633
+ backgroundColor: this.chartOpt.backgroundColors[datasets.length % 9],
634
+ borderColor: this.chartOpt.borderColors[datasets.length % 9]
635
+ }
636
+
637
+ datasets.push(dataset)
638
+ })
639
+
640
+ return {
641
+ labels,
642
+ datasets
643
+ }
644
+
645
+ /*return {
646
+ labels: [ 'January', 'February', 'March' ],
647
+ datasets: [ { data: [40, 20, 12] } ]
648
+ }*/
649
+ },
650
+
450
651
  subscriptionObj(){
451
652
  const splitted = ((this.subscription ?? '').toString()).split(':')
452
653
  const splitted2 = (splitted[1] ?? '').split(',')
@@ -539,6 +740,10 @@ export default{
539
740
  return c
540
741
  },
541
742
 
743
+ presetSummary(){
744
+ return ((this.preset ?? {}).summaries ?? []).filter((_) => _.enabled).pop()
745
+ },
746
+
542
747
  },
543
748
 
544
749
  data(){
@@ -549,7 +754,35 @@ export default{
549
754
  configLoaded: false,
550
755
  selectedColumn: null,
551
756
  copiedConfig: null,
552
- mediaPrefix: null
757
+ mediaPrefix: null,
758
+ summary: null,
759
+ mapApiKey: null,
760
+ chartOpt: {
761
+ backgroundColors: [
762
+ '#5D9CEC',
763
+ '#A0D468',
764
+ '#FFCE54',
765
+ '#FC6E51',
766
+ '#48CFAD',
767
+ '#AC92EC',
768
+ '#4FC1E9',
769
+ '#FFCE54',
770
+ '#ED5565',
771
+ '#EC87C0'
772
+ ],
773
+ borderColors: [
774
+ '#4A89DC',
775
+ '#8CC152',
776
+ '#F6BB42',
777
+ '#E9573F',
778
+ '#37BC9B',
779
+ '#967ADC',
780
+ '#3BAFDA',
781
+ '#F6BB42',
782
+ '#DA4453',
783
+ '#D770AD'
784
+ ],
785
+ },
553
786
  }
554
787
  },
555
788
 
@@ -0,0 +1,80 @@
1
+ <template>
2
+ <div :class="$style.comp">
3
+
4
+ <div class="p-3">
5
+ <label class="text-text-400 flex-1">{{ $t('X-axis') }}</label>
6
+ <div class="flex flex-row mt-2 gap-2">
7
+ <Dropdown v-model="summary.bar.rows[0].key" class="flex-1">
8
+ <option value="" disabled selected>{{ $t('Add Column') }}</option>
9
+ <option v-for="column in columns" :value="column.key">{{ column.label ?? column.key }}</option>
10
+ </Dropdown>
11
+ <Dropdown v-model="summary.bar.rows[0].format" class="w-[100px]">
12
+ <option value="">{{ $t('Default') }}</option>
13
+ <option value="date">{{ $t('Date') }}</option>
14
+ <option value="month">{{ $t('Month') }}</option>
15
+ <option value="quarter">{{ $t('Quarterly') }}</option>
16
+ <option value="year">{{ $t('Year') }}</option>
17
+ </Dropdown>
18
+ </div>
19
+ </div>
20
+
21
+ <div class="p-3">
22
+ <label class="text-text-400 flex-1">{{ $t('Column') }}</label>
23
+ <div class="flex flex-row mt-2 gap-2">
24
+ <Dropdown v-model="summary.bar.columns[0].key" class="flex-1">
25
+ <option value="" disabled selected>{{ $t('Add Column') }}</option>
26
+ <option value="(none)">{{ $t('None') }}</option>
27
+ <option v-for="column in columns" :value="column.key">{{ column.label ?? column.key }}</option>
28
+ </Dropdown>
29
+ <Dropdown v-model="summary.bar.columns[0].format" class="w-[100px]">
30
+ <option value="">{{ $t('Default') }}</option>
31
+ <option value="date">{{ $t('Date') }}</option>
32
+ </Dropdown>
33
+ </div>
34
+ <Checkbox class="mt-2" v-model="summary.bar.stacked">
35
+ Stacked
36
+ </Checkbox>
37
+ </div>
38
+
39
+ <div class="p-3">
40
+ <label class="text-text-400 flex-1">Values</label>
41
+ <div class="flex flex-row mt-2 gap-2">
42
+ <Dropdown v-model="summary.bar.values[0].aggregrate" class="flex-1">
43
+ <option value="" disabled selected>{{ $t('Select') }}</option>
44
+ <option value="count">{{ $t('Count') }}</option>
45
+ <option value="max">{{ $t('Max') }}</option>
46
+ <option value="min">{{ $t('Min') }}</option>
47
+ <option value="avg">{{ $t('Avg') }}</option>
48
+ </Dropdown>
49
+ </div>
50
+ </div>
51
+
52
+ </div>
53
+ </template>
54
+
55
+ <script>
56
+
57
+ export default{
58
+
59
+ props: {
60
+
61
+ columns: Array,
62
+
63
+ summary: {
64
+ type: Object,
65
+ required: true
66
+ }
67
+
68
+ }
69
+
70
+ }
71
+
72
+ </script>
73
+
74
+ <style module>
75
+
76
+ .comp{
77
+
78
+ }
79
+
80
+ </style>
@@ -0,0 +1,75 @@
1
+ <template>
2
+ <div :class="$style.comp">
3
+
4
+ <div class="p-3">
5
+ <label class="text-text-400 flex-1">{{ $t('X-axis') }}</label>
6
+ <div class="flex flex-row mt-2 gap-2">
7
+ <Dropdown v-model="openedPresetSummary.line.rows[0].key" class="flex-1">
8
+ <option value="" disabled selected>{{ $t('Add Column') }}</option>
9
+ <option v-for="column in filterableColumns" :value="column.key">{{ column.label ?? column.key }}</option>
10
+ </Dropdown>
11
+ <Dropdown v-model="openedPresetSummary.line.rows[0].format" class="w-[100px]">
12
+ <option value="">{{ $t('Default') }}</option>
13
+ <option value="date">{{ $t('Date') }}</option>
14
+ <option value="month">{{ $t('Month') }}</option>
15
+ <option value="quarter">{{ $t('Quarterly') }}</option>
16
+ <option value="year">{{ $t('Year') }}</option>
17
+ </Dropdown>
18
+ </div>
19
+ </div>
20
+
21
+ <div class="p-3">
22
+ <label class="text-text-400 flex-1">{{$t('Column') }}</label>
23
+ <div class="flex flex-row mt-2 gap-2">
24
+ <Dropdown v-model="openedPresetSummary.line.columns[0].key" class="flex-1">
25
+ <option value="" disabled selected>{{ $t('Add Column') }}</option>
26
+ <option value="(none)">{{ $t('None') }}</option>
27
+ <option v-for="column in filterableColumns" :value="column.key">{{ column.label ?? column.key }}</option>
28
+ </Dropdown>
29
+ <Dropdown v-model="openedPresetSummary.line.columns[0].format" class="w-[100px]">
30
+ <option value="">{{ $t('Default') }}</option>
31
+ <option value="date">{{ $t('Date') }}</option>
32
+ </Dropdown>
33
+ </div>
34
+ </div>
35
+
36
+ <div class="p-3">
37
+ <label class="text-text-400 flex-1">Values</label>
38
+ <div class="flex flex-row mt-2 gap-2">
39
+ <Dropdown v-model="openedPresetSummary.line.values[0].aggregrate" class="flex-1">
40
+ <option value="" disabled selected>{{ $t('Select') }}</option>
41
+ <option value="count">{{ $t('Count') }}</option>
42
+ <option value="max">{{ $t('Max') }}</option>
43
+ <option value="min">{{ $t('Min') }}</option>
44
+ <option value="avg">{{ $t('Avg') }}</option>
45
+ </Dropdown>
46
+ </div>
47
+ </div>
48
+
49
+ </div>
50
+ </template>
51
+
52
+ <script>
53
+
54
+ export default{
55
+
56
+ props: {
57
+
58
+ summary: {
59
+ type: Object,
60
+ required: true
61
+ }
62
+
63
+ }
64
+
65
+ }
66
+
67
+ </script>
68
+
69
+ <style module>
70
+
71
+ .comp{
72
+
73
+ }
74
+
75
+ </style>
@@ -0,0 +1,56 @@
1
+ <template>
2
+ <div :class="$style.comp">
3
+
4
+ <div class="p-3 flex flex-row gap-3">
5
+ <div class="flex-1">
6
+ <label class="text-text-400 flex-1">Map Type</label>
7
+ <Dropdown class="mt-2" v-model="openedPresetSummary.map.mapType">
8
+ <option value="" disabled selected>(Default)</option>
9
+ <option value="heatmap">Heatmap</option>
10
+ </Dropdown>
11
+ </div>
12
+ <div>
13
+ <label class="text-text-400 flex-1">Radius</label>
14
+ <div class="flex flex-row mt-2 gap-2">
15
+ <Textbox v-model="openedPresetSummary.map.mapRadius" class="w-[60px]" />
16
+ </div>
17
+ </div>
18
+ </div>
19
+
20
+ <div class="p-3">
21
+ <label class="text-text-400 flex-1">{{$t('Property') }}</label>
22
+ <div>
23
+ <Dropdown v-model="openedPresetSummary.map.key" class="flex-1">
24
+ <option value="" disabled selected>{{ $t('Select Column') }}</option>
25
+ <option v-for="column in coordinateColumns" :value="column.key">{{ column.label ?? column.key }}</option>
26
+ </Dropdown>
27
+ </div>
28
+ </div>
29
+
30
+ </div>
31
+ </template>
32
+
33
+ <script>
34
+
35
+ export default{
36
+
37
+ props: {
38
+
39
+ summary: {
40
+ type: Object,
41
+ required: true
42
+ }
43
+
44
+ }
45
+
46
+ }
47
+
48
+ </script>
49
+
50
+ <style module>
51
+
52
+ .comp{
53
+
54
+ }
55
+
56
+ </style>
@@ -67,13 +67,13 @@
67
67
  </div>
68
68
 
69
69
  <div class="flex justify-center">
70
- <Tabs v-model="config.presetTab" :items="tabItems" @change="tabChanged"/>
70
+ <Tabs v-model="presetTab" :items="tabItems" @change="tabChanged"/>
71
71
  </div>
72
72
  </div>
73
73
 
74
74
  <div class="flex-1 overflow-y-auto px-6 mb-6">
75
75
 
76
- <div class="p-2 mt-6 flex flex-col" v-if="!config.presetTab || config.presetTab === 'column'">
76
+ <div class="p-2 mt-6 flex flex-col" v-if="!presetTab || presetTab === 'column'">
77
77
  <ListItem :items="presetColumns" @reorder="reorderColumns">
78
78
  <template v-slot="{ item }">
79
79
  <div class="flex flex-row items-center gap-2" :key="item">
@@ -91,7 +91,7 @@
91
91
  </ListItem>
92
92
  </div>
93
93
 
94
- <div class="flex flex-col" v-else-if="config.presetTab === 'filter'">
94
+ <div class="flex flex-col" v-else-if="presetTab === 'filter'">
95
95
  <div v-if="filterableColumns.length > 0">
96
96
  <ListPage1Filter v-if="preset.filters" v-for="filter in preset.filters"
97
97
  :filter="filter" :column="config.columns[config.columns.findIndex((_) => _.key === filter.key)]"
@@ -110,7 +110,7 @@
110
110
  </div>
111
111
  </div>
112
112
 
113
- <div class="flex flex-col" v-else-if="config.presetTab === 'sort'">
113
+ <div class="flex flex-col" v-else-if="presetTab === 'sort'">
114
114
  <div v-if="sortableColumns.length > 0" class="my-8">
115
115
  <div v-for="sort in preset.sorts" class="py-4">
116
116
  <div class="flex flex-row items-center gap-2">
@@ -148,26 +148,30 @@
148
148
  </div>
149
149
  </div>
150
150
 
151
- <div class="flex flex-col gap-3 py-8" v-else-if="config.presetTab === 'summarys'">
151
+ <div class="flex flex-col gap-3 py-8" v-else-if="presetTab === 'summary'">
152
152
 
153
153
  <div v-if="config.summaryOpenIdx === -1">
154
- <div v-for="(summary, idx) in preset.summaries">
155
- <div class="flex flex-row items-center gap-3 border-text-50 border-[1px] p-2 rounded-lg">
156
- <div class="px-2">
157
- <Checkbox v-model="summary.enabled" @change="enableSummary(idx)" />
158
- </div>
159
- <div class="flex-1" @click="config.summaryOpenIdx = idx">
160
- <label>{{ summary.title }}</label>
161
- </div>
162
- <button type="button" class="px-2" v-if="preset.summaries.length > 1" @click="removeSummary(summary)">
163
- <svg width="16" height="16" viewBox="0 0 24 24" class="fill-text-300 hover:fill-red-500" xmlns="http://www.w3.org/2000/svg">
164
- <path fill-rule="evenodd" clip-rule="evenodd" d="M10 10.25C10.4142 10.25 10.75 10.5858 10.75 11V16C10.75 16.4142 10.4142 16.75 10 16.75C9.58579 16.75 9.25 16.4142 9.25 16V11C9.25 10.5858 9.58579 10.25 10 10.25Z"/>
165
- <path fill-rule="evenodd" clip-rule="evenodd" d="M14 10.25C14.4142 10.25 14.75 10.5858 14.75 11V16C14.75 16.4142 14.4142 16.75 14 16.75C13.5858 16.75 13.25 16.4142 13.25 16V11C13.25 10.5858 13.5858 10.25 14 10.25Z"/>
166
- <path fill-rule="evenodd" clip-rule="evenodd" d="M10 2.25C8.48122 2.25 7.25 3.48122 7.25 5V5.25H3C2.58579 5.25 2.25 5.58579 2.25 6C2.25 6.41421 2.58579 6.75 3 6.75H4.25V19C4.25 20.5188 5.48122 21.75 7 21.75H17C18.5188 21.75 19.75 20.5188 19.75 19V6.75H21C21.4142 6.75 21.75 6.41421 21.75 6C21.75 5.58579 21.4142 5.25 21 5.25H16.75V5C16.75 3.48122 15.5188 2.25 14 2.25H10ZM15.25 5.25V5C15.25 4.30964 14.6904 3.75 14 3.75H10C9.30964 3.75 8.75 4.30964 8.75 5V5.25H15.25ZM5.75 6.75V19C5.75 19.6904 6.30964 20.25 7 20.25H17C17.6904 20.25 18.25 19.6904 18.25 19V6.75H5.75Z"/>
167
- </svg>
168
- </button>
169
- </div>
170
- </div>
154
+
155
+ <div class="border-text-50 border-[1px] rounded-lg overflow-hidden divide-y divide-text-50">
156
+ <div v-for="(summary, idx) in preset.summaries">
157
+ <div class="flex flex-row items-center gap-3 px-2">
158
+ <div class="p-2">
159
+ <Checkbox :value="summary.enabled" @change="selectSummary(idx)" />
160
+ </div>
161
+ <div class="flex-1" @click="config.summaryOpenIdx = idx">
162
+ <label>{{ summary.title }}</label>
163
+ </div>
164
+ <button type="button" class="px-2" v-if="preset.summaries.length > 1" @click="removeSummary(summary)">
165
+ <svg width="16" height="16" viewBox="0 0 24 24" class="fill-text-300 hover:fill-red-500" xmlns="http://www.w3.org/2000/svg">
166
+ <path fill-rule="evenodd" clip-rule="evenodd" d="M10 10.25C10.4142 10.25 10.75 10.5858 10.75 11V16C10.75 16.4142 10.4142 16.75 10 16.75C9.58579 16.75 9.25 16.4142 9.25 16V11C9.25 10.5858 9.58579 10.25 10 10.25Z"/>
167
+ <path fill-rule="evenodd" clip-rule="evenodd" d="M14 10.25C14.4142 10.25 14.75 10.5858 14.75 11V16C14.75 16.4142 14.4142 16.75 14 16.75C13.5858 16.75 13.25 16.4142 13.25 16V11C13.25 10.5858 13.5858 10.25 14 10.25Z"/>
168
+ <path fill-rule="evenodd" clip-rule="evenodd" d="M10 2.25C8.48122 2.25 7.25 3.48122 7.25 5V5.25H3C2.58579 5.25 2.25 5.58579 2.25 6C2.25 6.41421 2.58579 6.75 3 6.75H4.25V19C4.25 20.5188 5.48122 21.75 7 21.75H17C18.5188 21.75 19.75 20.5188 19.75 19V6.75H21C21.4142 6.75 21.75 6.41421 21.75 6C21.75 5.58579 21.4142 5.25 21 5.25H16.75V5C16.75 3.48122 15.5188 2.25 14 2.25H10ZM15.25 5.25V5C15.25 4.30964 14.6904 3.75 14 3.75H10C9.30964 3.75 8.75 4.30964 8.75 5V5.25H15.25ZM5.75 6.75V19C5.75 19.6904 6.30964 20.25 7 20.25H17C17.6904 20.25 18.25 19.6904 18.25 19V6.75H5.75Z"/>
169
+ </svg>
170
+ </button>
171
+ </div>
172
+ </div>
173
+ </div>
174
+
171
175
  <div class="p-2 text-center">
172
176
  <button type="button" @click="addSummary">
173
177
  <svg width="21" height="21" viewBox="0 0 24 24" class="fill-text-300 hover:fill-primary" xmlns="http://www.w3.org/2000/svg">
@@ -215,187 +219,21 @@
215
219
  </div>
216
220
  </div>
217
221
 
218
- <div v-if="openedPresetSummary.mode === 'table'">
219
-
220
- <div class="p-3">
221
- <label class="text-text-400 flex-1">{{ $t('Row') }}</label>
222
- <div class="flex flex-row mt-2 gap-2">
223
- <Dropdown v-model="openedPresetSummary.table.rows[0].key" class="flex-1">
224
- <option value="" disabled selected>{{ $t('Add Column') }}</option>
225
- <option v-for="column in filterableColumns" :value="column.key">{{ column.label ?? column.key }}</option>
226
- </Dropdown>
227
- <Dropdown v-model="openedPresetSummary.table.rows[0].format" class="w-[100px]">
228
- <option value="">{{ $t('Default') }}</option>
229
- <option value="date">{{ $t('Date') }}</option>
230
- <option value="month">{{ $t('Month') }}</option>
231
- <option value="quarter">{{ $t('Quarterly') }}</option>
232
- <option value="year">{{ $t('Year') }}</option>
233
- </Dropdown>
234
- </div>
235
- <Checkbox class="mt-2" v-model="openedPresetSummary.table.showRowTotal">
236
- Row Total
237
- </Checkbox>
238
- </div>
239
-
240
- <div class="p-3">
241
- <label class="text-text-400 flex-1">{{$t('Column') }}</label>
242
- <div>
243
- <Dropdown v-model="openedPresetSummary.table.columns[0].key" class="flex-1">
244
- <option value="" disabled selected>{{ $t('Add Column') }}</option>
245
- <option value="(none)">{{ $t('(None)') }}</option>
246
- <option v-for="column in filterableColumns" :value="column.key">{{ column.label ?? column.key }}</option>
247
- </Dropdown>
248
- </div>
249
- <Checkbox class="mt-2" v-model="openedPresetSummary.table.showColumnTotal">
250
- Column Total
251
- </Checkbox>
252
- </div>
253
-
254
- <div class="p-3">
255
- <label class="text-text-400 flex-1">Values</label>
256
- <div class="flex flex-row mt-2 gap-2">
257
- <Dropdown v-model="openedPresetSummary.table.values[0].aggregrate" class="flex-1">
258
- <option value="" disabled selected>{{ $t('Select') }}</option>
259
- <option value="count">{{ $t('Count') }}</option>
260
- <option value="max">{{ $t('Max') }}</option>
261
- <option value="min">{{ $t('Min') }}</option>
262
- <option value="avg">{{ $t('Avg') }}</option>
263
- </Dropdown>
264
- </div>
265
- </div>
266
-
267
- </div>
268
-
269
- <div v-else-if="openedPresetSummary.mode === 'bar'">
270
-
271
- <div class="p-3">
272
- <label class="text-text-400 flex-1">{{ $t('X-axis') }}</label>
273
- <div class="flex flex-row mt-2 gap-2">
274
- <Dropdown v-model="openedPresetSummary.bar.rows[0].key" class="flex-1">
275
- <option value="" disabled selected>{{ $t('Add Column') }}</option>
276
- <option v-for="column in filterableColumns" :value="column.key">{{ column.label ?? column.key }}</option>
277
- </Dropdown>
278
- <Dropdown v-model="openedPresetSummary.bar.rows[0].format" class="w-[100px]">
279
- <option value="">{{ $t('Default') }}</option>
280
- <option value="date">{{ $t('Date') }}</option>
281
- <option value="month">{{ $t('Month') }}</option>
282
- <option value="quarter">{{ $t('Quarterly') }}</option>
283
- <option value="year">{{ $t('Year') }}</option>
284
- </Dropdown>
285
- </div>
286
- </div>
287
-
288
- <div class="p-3">
289
- <label class="text-text-400 flex-1">{{ $t('Column') }}</label>
290
- <div class="flex flex-row mt-2 gap-2">
291
- <Dropdown v-model="openedPresetSummary.bar.columns[0].key" class="flex-1">
292
- <option value="" disabled selected>{{ $t('Add Column') }}</option>
293
- <option value="(none)">{{ $t('None') }}</option>
294
- <option v-for="column in filterableColumns" :value="column.key">{{ column.label ?? column.key }}</option>
295
- </Dropdown>
296
- <Dropdown v-model="openedPresetSummary.bar.columns[0].format" class="w-[100px]">
297
- <option value="">{{ $t('Default') }}</option>
298
- <option value="date">{{ $t('Date') }}</option>
299
- </Dropdown>
300
- </div>
301
- <Checkbox class="mt-2" v-model="openedPresetSummary.bar.stacked">
302
- Stacked
303
- </Checkbox>
304
- </div>
305
-
306
- <div class="p-3">
307
- <label class="text-text-400 flex-1">Values</label>
308
- <div class="flex flex-row mt-2 gap-2">
309
- <Dropdown v-model="openedPresetSummary.bar.values[0].aggregrate" class="flex-1">
310
- <option value="" disabled selected>{{ $t('Select') }}</option>
311
- <option value="count">{{ $t('Count') }}</option>
312
- <option value="max">{{ $t('Max') }}</option>
313
- <option value="min">{{ $t('Min') }}</option>
314
- <option value="avg">{{ $t('Avg') }}</option>
315
- </Dropdown>
316
- </div>
317
- </div>
318
-
319
- </div>
320
-
321
- <div v-else-if="openedPresetSummary.mode === 'line'">
222
+ <ListViewTableSummary v-if="openedPresetSummary.mode === 'table'"
223
+ :summary="openedPresetSummary" />
322
224
 
323
- <div class="p-3">
324
- <label class="text-text-400 flex-1">{{ $t('X-axis') }}</label>
325
- <div class="flex flex-row mt-2 gap-2">
326
- <Dropdown v-model="openedPresetSummary.line.rows[0].key" class="flex-1">
327
- <option value="" disabled selected>{{ $t('Add Column') }}</option>
328
- <option v-for="column in filterableColumns" :value="column.key">{{ column.label ?? column.key }}</option>
329
- </Dropdown>
330
- <Dropdown v-model="openedPresetSummary.line.rows[0].format" class="w-[100px]">
331
- <option value="">{{ $t('Default') }}</option>
332
- <option value="date">{{ $t('Date') }}</option>
333
- <option value="month">{{ $t('Month') }}</option>
334
- <option value="quarter">{{ $t('Quarterly') }}</option>
335
- <option value="year">{{ $t('Year') }}</option>
336
- </Dropdown>
337
- </div>
338
- </div>
225
+ <ListViewBarSummary v-else-if="openedPresetSummary.mode === 'bar'"
226
+ :summary="openedPresetSummary" :columns="filterableColumns" />
339
227
 
340
- <div class="p-3">
341
- <label class="text-text-400 flex-1">{{$t('Column') }}</label>
342
- <div class="flex flex-row mt-2 gap-2">
343
- <Dropdown v-model="openedPresetSummary.line.columns[0].key" class="flex-1">
344
- <option value="" disabled selected>{{ $t('Add Column') }}</option>
345
- <option value="(none)">{{ $t('None') }}</option>
346
- <option v-for="column in filterableColumns" :value="column.key">{{ column.label ?? column.key }}</option>
347
- </Dropdown>
348
- <Dropdown v-model="openedPresetSummary.line.columns[0].format" class="w-[100px]">
349
- <option value="">{{ $t('Default') }}</option>
350
- <option value="date">{{ $t('Date') }}</option>
351
- </Dropdown>
352
- </div>
353
- </div>
354
-
355
- <div class="p-3">
356
- <label class="text-text-400 flex-1">Values</label>
357
- <div class="flex flex-row mt-2 gap-2">
358
- <Dropdown v-model="openedPresetSummary.line.values[0].aggregrate" class="flex-1">
359
- <option value="" disabled selected>{{ $t('Select') }}</option>
360
- <option value="count">{{ $t('Count') }}</option>
361
- <option value="max">{{ $t('Max') }}</option>
362
- <option value="min">{{ $t('Min') }}</option>
363
- <option value="avg">{{ $t('Avg') }}</option>
364
- </Dropdown>
365
- </div>
366
- </div>
367
-
368
- </div>
369
-
370
- <div v-else-if="openedPresetSummary.mode === 'map'">
371
-
372
- <div class="p-3 flex flex-row gap-3">
373
- <div class="flex-1">
374
- <label class="text-text-400 flex-1">Map Type</label>
375
- <Dropdown class="mt-2" v-model="openedPresetSummary.map.mapType">
376
- <option value="" disabled selected>(Default)</option>
377
- <option value="heatmap">Heatmap</option>
378
- </Dropdown>
379
- </div>
380
- <div>
381
- <label class="text-text-400 flex-1">Radius</label>
382
- <div class="flex flex-row mt-2 gap-2">
383
- <Textbox v-model="openedPresetSummary.map.mapRadius" class="w-[60px]" />
384
- </div>
385
- </div>
386
- </div>
228
+ <ListViewLineSummary v-else-if="openedPresetSummary.mode === 'line'"
229
+ :summary="openedPresetSummary" :columns="filterableColumns" />
387
230
 
388
- <div class="p-3">
389
- <label class="text-text-400 flex-1">{{$t('Property') }}</label>
390
- <div>
391
- <Dropdown v-model="openedPresetSummary.map.key" class="flex-1">
392
- <option value="" disabled selected>{{ $t('Select Column') }}</option>
393
- <option v-for="column in coordinateColumns" :value="column.key">{{ column.label ?? column.key }}</option>
394
- </Dropdown>
395
- </div>
396
- </div>
231
+ <ListViewMapSummary v-else-if="openedPresetSummary.mode === 'map'"
232
+ :summary="openedPresetSummary" :columns="filterableColumns" />
397
233
 
398
- </div>
234
+ <div class="p-3 mt-8">
235
+ <Button class="w-[80px]" variant="secondary" @click="config.summaryOpenIdx = -1">OK</Button>
236
+ </div>
399
237
 
400
238
  </div>
401
239
 
@@ -523,6 +361,12 @@ export default{
523
361
  this.configs.summaryOpenIdx = this.preset.summaries.length - 1
524
362
  },
525
363
 
364
+ selectSummary(idx){
365
+ for(let i in this.preset.summaries){
366
+ this.preset.summaries[i].enabled = parseInt(i) === idx
367
+ }
368
+ }
369
+
526
370
  },
527
371
 
528
372
 
@@ -621,24 +465,12 @@ export default{
621
465
  presetSortSelector: null,
622
466
  mediaPrefix: null,
623
467
  detailMode: false,
468
+ presetTab: 'column'
624
469
  }
625
470
  },
626
471
 
627
472
  mounted() {
628
473
  this.mediaPrefix = this.$el.clientWidth <= 480 ? 'sm' : 'md'
629
- },
630
-
631
- watch: {
632
-
633
- config: {
634
- immediate: true,
635
- handler(to){
636
- if(!this.config.presetTab){
637
- this.config.presetTab = 'column'
638
- }
639
- }
640
- }
641
-
642
474
  }
643
475
 
644
476
  }
@@ -0,0 +1,77 @@
1
+ <template>
2
+ <div :class="$style.comp">
3
+
4
+ <div class="p-3">
5
+ <label class="text-text-400 flex-1">{{ $t('Row') }}</label>
6
+ <div class="flex flex-row mt-2 gap-2">
7
+ <Dropdown v-model="summary.table.rows[0].key" class="flex-1">
8
+ <option value="" disabled selected>{{ $t('Add Column') }}</option>
9
+ <option v-for="column in filterableColumns" :value="column.key">{{ column.label ?? column.key }}</option>
10
+ </Dropdown>
11
+ <Dropdown v-model="summary.table.rows[0].format" class="w-[100px]">
12
+ <option value="">{{ $t('Default') }}</option>
13
+ <option value="date">{{ $t('Date') }}</option>
14
+ <option value="month">{{ $t('Month') }}</option>
15
+ <option value="quarter">{{ $t('Quarterly') }}</option>
16
+ <option value="year">{{ $t('Year') }}</option>
17
+ </Dropdown>
18
+ </div>
19
+ <Checkbox class="mt-2" v-model="summary.table.showRowTotal">
20
+ Row Total
21
+ </Checkbox>
22
+ </div>
23
+
24
+ <div class="p-3">
25
+ <label class="text-text-400 flex-1">{{$t('Column') }}</label>
26
+ <div>
27
+ <Dropdown v-model="summary.table.columns[0].key" class="flex-1">
28
+ <option value="" disabled selected>{{ $t('Add Column') }}</option>
29
+ <option value="(none)">{{ $t('(None)') }}</option>
30
+ <option v-for="column in filterableColumns" :value="column.key">{{ column.label ?? column.key }}</option>
31
+ </Dropdown>
32
+ </div>
33
+ <Checkbox class="mt-2" v-model="summary.table.showColumnTotal">
34
+ Column Total
35
+ </Checkbox>
36
+ </div>
37
+
38
+ <div class="p-3">
39
+ <label class="text-text-400 flex-1">Values</label>
40
+ <div class="flex flex-row mt-2 gap-2">
41
+ <Dropdown v-model="summary.table.values[0].aggregrate" class="flex-1">
42
+ <option value="" disabled selected>{{ $t('Select') }}</option>
43
+ <option value="count">{{ $t('Count') }}</option>
44
+ <option value="max">{{ $t('Max') }}</option>
45
+ <option value="min">{{ $t('Min') }}</option>
46
+ <option value="avg">{{ $t('Avg') }}</option>
47
+ </Dropdown>
48
+ </div>
49
+ </div>
50
+
51
+ </div>
52
+ </template>
53
+
54
+ <script>
55
+
56
+ export default{
57
+
58
+ props: {
59
+
60
+ summary: {
61
+ type: Object,
62
+ required: true
63
+ }
64
+
65
+ }
66
+
67
+ }
68
+
69
+ </script>
70
+
71
+ <style module>
72
+
73
+ .comp{
74
+
75
+ }
76
+
77
+ </style>
@@ -212,7 +212,7 @@ export default{
212
212
  <style module>
213
213
 
214
214
  .comp{
215
- @apply relative overflow-auto max-h-[100vh];
215
+ @apply relative overflow-y-auto overflow-x-hidden max-h-[100vh];
216
216
  }
217
217
 
218
218
  .scroller{
package/src/index.js CHANGED
@@ -254,6 +254,10 @@ export default{
254
254
  app.component('ListItem', defineAsyncComponent(() => import("./components/ListItem.vue")))
255
255
  app.component('ListView', defineAsyncComponent(() => import("./components/ListView.vue")))
256
256
  app.component('ListViewSettings', defineAsyncComponent(() => import("./components/ListViewSettings.vue")))
257
+ app.component('ListViewBarSummary', defineAsyncComponent(() => import("./components/ListViewBarSummary.vue")))
258
+ app.component('ListViewTableSummary', defineAsyncComponent(() => import("./components/ListViewTableSummary.vue")))
259
+ app.component('ListViewLineSummary', defineAsyncComponent(() => import("./components/ListViewLineSummary.vue")))
260
+ app.component('ListViewMapSummary', defineAsyncComponent(() => import("./components/ListViewMapSummary.vue")))
257
261
  app.component('Carousel', defineAsyncComponent(() => import("./components/Carousel.vue")))
258
262
  app.component('ContextMenu', defineAsyncComponent(() => import("./components/ContextMenu.vue")))
259
263
  app.component('Modal', defineAsyncComponent(() => import("./components/Modal.vue")))
@@ -90,36 +90,45 @@ let ListView = {
90
90
 
91
91
  async load(params){
92
92
 
93
- const { items, hasNext, count } = await this._load(params)
93
+ this.columns = params.columns
94
+
95
+ const { items, hasNext, count, derivedSql, where, replacements } = await this._load(params)
96
+
97
+ const summary = await this.loadSummary(params, {
98
+ derivedSql,
99
+ where,
100
+ replacements
101
+ })
94
102
 
95
103
  if(this.name){
96
104
  if(!this.socket.loadParams){
97
105
  this.socket.loadParams = {}
98
106
  }
99
-
100
107
  this.socket.loadParams[this.name] = params
101
108
  }
102
109
 
110
+ const extra = typeof this.postLoad === 'function' ? await this.postLoad() : undefined
111
+
103
112
  return {
104
113
  items,
105
114
  hasNext,
106
- count
115
+ count,
116
+ summary,
117
+ ...(extra ?? {})
107
118
  }
108
119
  },
109
120
 
110
- async loadSummary(params){
121
+ async loadSummary(params, loadResults){
111
122
 
112
123
  if(!this.conn){
113
124
  console.error('Unable to load summary, connection not found')
114
125
  return
115
126
  }
116
127
 
117
- const { preset = {} } = params
118
- const summary = preset.summaries.filter((_) => _.enabled).pop()
119
-
128
+ const summary = params.preset.summaries.filter((_) => _.enabled).pop()
120
129
  if(!summary) return
121
130
 
122
- const { derivedSql, where, replacements } = await this._load(params)
131
+ const { derivedSql, where, replacements } = loadResults
123
132
 
124
133
  switch(summary.mode){
125
134
 
@@ -134,153 +143,14 @@ let ListView = {
134
143
 
135
144
  case 'map':
136
145
  if(this.loadMapSummary){
137
- return await this.loadMapSummary(preset, {
146
+ return await this.loadMapSummary(params.preset, {
138
147
  derivedSql,
139
148
  where,
140
149
  replacements
141
150
  })
142
151
  }
143
152
  break
144
-
145
- }
146
-
147
- return
148
-
149
-
150
-
151
- /*const attributes = []
152
- const group = []
153
- const summaryColumns = [
154
- /!*{ key:"dfDate", label:"Date", width:"200px", visible:true },
155
- { key:"type-1", label:"Type 1", type:"number", visible:true },
156
- { key:"type-2", label:"Type 2", type:"number", visible:true },
157
- { key:"type-3", label:"Type 3", type:"number", visible:true },
158
- { key:"type-4", label:"Type 4", type:"number", visible:true },
159
- { key:"type-8", label:"Type 8", type:"number", visible:true },*!/
160
- ]
161
-
162
- const row = rows[0]
163
- const rowKey = row.key
164
- const rowLabel = this.columns[row.key].label
165
- switch(row.format){
166
- case 'date':
167
- attributes.push(`DATE_FORMAT(${rowKey}, '%Y-%m-%d') as dfDate`)
168
- group.push(`DATE_FORMAT(${rowKey}, '%Y-%m-%d')`)
169
- summaryColumns.push({ key:"dfDate", label:rowLabel, width:"200px", visible:true, type:"date", format:"D MMM" })
170
- break
171
-
172
- case 'month':
173
- attributes.push(`DATE_FORMAT(${rowKey}, '%Y-%m') as dfDate`)
174
- group.push(`DATE_FORMAT(${rowKey}, '%Y-%m')`)
175
- summaryColumns.push({ key:"dfDate", label:rowLabel, width:"200px", visible:true, type:"date", format:"MMM YYYY" })
176
- break
177
-
178
- case 'quarter':
179
- attributes.push(`CONCAT(YEAR(${rowKey}), ' Q', QUARTER(${rowKey})) as dfDate`)
180
- group.push(`CONCAT(YEAR(${rowKey}), ' Q', QUARTER(${rowKey}))`)
181
- summaryColumns.push({ key:"dfDate", label:rowLabel, width:"200px", visible:true, type:"date", format:"MMM YYYY" })
182
- break
183
-
184
- case 'year':
185
- attributes.push(`DATE_FORMAT(${rowKey}, '%Y') as dfDate`)
186
- group.push(`DATE_FORMAT(${rowKey}, '%Y')`)
187
- summaryColumns.push({ key:"dfDate", label:rowLabel, width:"200px", visible:true, type:"date", format:"YYYY" })
188
- break
189
-
190
- default:
191
- attributes.push(`${rowKey} as dfDate`)
192
- group.push(`${rowKey}`)
193
- summaryColumns.push({ key:"dfDate", label:rowLabel, width:"200px", visible:true })
194
- break
195
- }
196
-
197
- const value = values[0]
198
- if(columnValues.length > 0){
199
- columnValues.forEach((obj) => {
200
- const columnValue = obj.cols
201
-
202
- let columnLabel = (columnValue ?? '').toString()
203
- const baseColumn = this.columns[colKey]
204
- switch(baseColumn.type){
205
-
206
- case 'enum':
207
- const enumValue = baseColumn.typeParams.filter((_) => _.value === columnValue).pop()
208
- if(enumValue) columnLabel = enumValue.text
209
- break
210
- }
211
-
212
- switch(value.aggregrate){
213
- case 'count':
214
- const fn = columnValue === null ? `${colKey} is null` : `${colKey} = '${columnValue}'`
215
- attributes.push(`SUM(IF(${fn}, 1, 0)) as \`${colKey}-${columnValue}\``)
216
- summaryColumns.push({ key:colKey + "-" + columnValue, label:columnLabel, visible:true, aggregrate:"count" })
217
- break
218
-
219
- case 'min':
220
- attributes.push(`MIN(${colKey}) as \`${colKey}-${columnValue}\``)
221
- summaryColumns.push({ key:colKey + "-" + columnValue, label:columnLabel, visible:true, aggregrate:"count" })
222
- break
223
-
224
- case 'max':
225
- attributes.push(`MAX(${colKey}) as \`${colKey}-${columnValue}\``)
226
- summaryColumns.push({ key:colKey + "-" + columnValue, label:columnLabel, visible:true, aggregrate:"count" })
227
- break
228
-
229
- case 'avg':
230
- attributes.push(`AVG(${colKey}) as \`${colKey}-${columnValue}\``)
231
- summaryColumns.push({ key:colKey + "-" + columnValue, label:columnLabel, visible:true, aggregrate:"count" })
232
- break
233
- }
234
- })
235
- }
236
- else{
237
- switch(value.aggregrate) {
238
- case 'count':
239
- default:
240
- attributes.push(`COUNT(*) as \`Count\``)
241
- summaryColumns.push({ key:'Count', label:'Count', type:"number", visible:true, aggregrate:"count" })
242
- }
243
153
  }
244
-
245
- //console.log(attributes)
246
-
247
- let [ summaryItems ] = await this.conn.query(`
248
- SELECT
249
- ${attributes.join(', ')}
250
- FROM (
251
- ${derivedSql}
252
- ) as t1
253
- GROUP BY ${group[0]}
254
- ORDER BY dfDate ASC
255
- `)
256
-
257
- if(this.columns[rowKey] && this.columns[rowKey].type === 'enum'){
258
- summaryItems = summaryItems.map((_) => {
259
-
260
- const typeParam = this.columns[rowKey].typeParams.filter((__) => __.value === _.dfDate).pop()
261
- if(typeParam){
262
- _.dfDate = typeParam.text
263
- }
264
-
265
- return _
266
- })
267
- }
268
-
269
- const summary = {
270
- type: "table",
271
- columns: summaryColumns,
272
- items: summaryItems
273
- /!*items: [
274
- { date:"21 Jan 23", "type-1":12, "type-2":11, "type-3":16, "type-4":2, "type-8":8 },
275
- { date:"22 Jan 23", "type-1":51, "type-2":87, "type-3":86, "type-4":3, "type-8":9 },
276
- { date:"23 Jan 23", "type-1":14, "type-2":66, "type-3":77, "type-4":4, "type-8":18 },
277
- { date:"24 Jan 23", "type-1":7, "type-2":3, "type-3":63, "type-4":5, "type-8":28 },
278
- { date:"25 Jan 23", "type-1":88, "type-2":55, "type-3":68, "type-4":6, "type-8":38 },
279
- { date:"26 Jan 23", "type-1":172, "type-2":13, "type-3":6, "type-4":7, "type-8":48 }
280
- ]*!/
281
- }
282
-
283
- return summary*/
284
154
  },
285
155
 
286
156
  async subscribe(params, socket){
@@ -587,7 +457,7 @@ let ListView = {
587
457
 
588
458
  async loadBarSummary(summary, derivedSql){
589
459
 
590
- if(!summary.bar) return
460
+ if(!summary.bar && !this.columns) return
591
461
 
592
462
  const { columns, rows, values } = summary.bar
593
463
 
@@ -626,7 +496,7 @@ let ListView = {
626
496
 
627
497
  const row = rows[0]
628
498
  const rowKey = row.key
629
- const rowLabel = this.columns[row.key].label
499
+ const rowLabel = this.columns.find((_) => _.key === row.key).label
630
500
  switch(row.format){
631
501
  case 'date':
632
502
  attributes.push(`DATE_FORMAT(${rowKey}, '%Y-%m-%d') as dfDate`)
@@ -665,7 +535,7 @@ let ListView = {
665
535
  const columnValue = obj.cols
666
536
 
667
537
  let columnLabel = (columnValue ?? '').toString()
668
- const baseColumn = this.columns[colKey]
538
+ const baseColumn = this.columns.find((_) => _.key === colKey)
669
539
  switch(baseColumn.type){
670
540
 
671
541
  case 'enum':
@@ -787,7 +657,7 @@ let ListView = {
787
657
 
788
658
  const row = rows[0]
789
659
  const rowKey = row.key
790
- const rowLabel = this.columns[row.key].label
660
+ const rowLabel = this.columns.find((_) => _.key === row.key).label
791
661
  switch(row.format){
792
662
  case 'date':
793
663
  attributes.push(`DATE_FORMAT(${rowKey}, '%Y-%m-%d') as dfDate`)
@@ -826,7 +696,7 @@ let ListView = {
826
696
  const columnValue = obj.cols
827
697
 
828
698
  let columnLabel = (columnValue ?? '').toString()
829
- const baseColumn = this.columns[colKey]
699
+ const baseColumn = this.columns.find((_) => _.key === colKey)
830
700
  switch(baseColumn.type){
831
701
 
832
702
  case 'enum':
@@ -948,7 +818,7 @@ let ListView = {
948
818
 
949
819
  const row = rows[0]
950
820
  const rowKey = row.key
951
- const rowLabel = this.columns[row.key].label
821
+ const rowLabel = this.columns.find((_) => _.key === row.key).label
952
822
  switch(row.format){
953
823
  case 'date':
954
824
  attributes.push(`DATE_FORMAT(${rowKey}, '%Y-%m-%d') as dfDate`)
@@ -987,7 +857,7 @@ let ListView = {
987
857
  const columnValue = obj.cols
988
858
 
989
859
  let columnLabel = (columnValue ?? '').toString()
990
- const baseColumn = this.columns[colKey]
860
+ const baseColumn = this.columns.find((_) => _.key === colKey)
991
861
  switch(baseColumn.type){
992
862
 
993
863
  case 'enum':