@mixd-id/web-scaffold 0.1.2301231353 → 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.2301231353",
4
+ "version": "0.1.2301231354",
5
5
  "scripts": {
6
6
  "dev": "vite serve",
7
7
  "build": "vite build",
@@ -44,10 +44,9 @@
44
44
  </div>
45
45
 
46
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="summary.items" :columns="summary.columns" class="flex-1"></VirtualTable>
48
-
47
+ <VirtualTable v-if="preset.summary.mode === 'table'" :items="summaryItems" :columns="summaryColumns" class="flex-1"></VirtualTable>
49
48
  <Bar v-else-if="preset.summary.mode === 'bar'" :data="chartData" :options="chartOptions" class="flex-1 w-full p-2"/>
50
-
49
+ <Line v-else-if="preset.summary.mode === 'line'" :data="chartData" :options="chartOptions" class="flex-1 w-full p-2"/>
51
50
  </div>
52
51
 
53
52
  <VirtualTable v-if="!hideDetails" class="flex-1" :columns="presetColumns" :items="items"
@@ -255,7 +254,18 @@
255
254
  </div>
256
255
 
257
256
  <div class="p-3">
258
- <label class="text-text-400 flex-1">Row</label>
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>
259
269
  <div class="flex flex-row mt-2 gap-2">
260
270
  <Dropdown v-model="preset.summary.rows[0].key" class="flex-1" @change="load">
261
271
  <option value="" disabled selected>{{ $t('Add Column') }}</option>
@@ -264,15 +274,21 @@
264
274
  <Dropdown v-model="preset.summary.rows[0].format" class="w-[100px]" @change="load">
265
275
  <option value="">{{ $t('Default') }}</option>
266
276
  <option value="date">{{ $t('Date') }}</option>
277
+ <option value="month">{{ $t('Month') }}</option>
278
+ <option value="year">{{ $t('Year') }}</option>
267
279
  </Dropdown>
268
280
  </div>
281
+ <Checkbox class="mt-2" v-if="preset.summary.mode === 'table'" v-model="preset.summary.showRowTotal">
282
+ Row Total
283
+ </Checkbox>
269
284
  </div>
270
285
 
271
286
  <div class="p-3">
272
- <label class="text-text-400 flex-1">Column</label>
287
+ <label class="text-text-400 flex-1">{{ preset.summary.mode === 'table' ? $t('Column') : $t('Dimension') }}</label>
273
288
  <div class="flex flex-row mt-2 gap-2">
274
289
  <Dropdown v-model="preset.summary.columns[0].key" class="flex-1" @change="load">
275
290
  <option value="" disabled selected>{{ $t('Add Column') }}</option>
291
+ <option value="(none)">{{ $t('None') }}</option>
276
292
  <option v-for="column in filterableColumns" :value="column.key">{{ column.label }}</option>
277
293
  </Dropdown>
278
294
  <Dropdown v-model="preset.summary.columns[0].format" class="w-[100px]" @change="load">
@@ -280,28 +296,23 @@
280
296
  <option value="date">{{ $t('Date') }}</option>
281
297
  </Dropdown>
282
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>
283
305
  </div>
284
306
 
285
307
  <div class="p-3">
286
308
  <label class="text-text-400 flex-1">Values</label>
287
309
  <div class="flex flex-row mt-2 gap-2">
288
- <Dropdown v-model="preset.summary.values[0].key" class="flex-1" @change="load">
289
- <option value="" disabled selected>{{ $t('Add Column') }}</option>
290
- <option v-for="column in filterableColumns" :value="column.key">{{ column.label }}</option>
291
- </Dropdown>
292
- <Dropdown v-model="preset.summary.values[0].aggregrate" class="w-[100px]" @change="load">
293
- <option value="">{{ $t('Default') }}</option>
310
+ <Dropdown v-model="preset.summary.values[0].aggregrate" class="flex-1" @change="load">
311
+ <option value="" disabled selected>{{ $t('Select') }}</option>
294
312
  <option value="count">{{ $t('Count') }}</option>
295
- </Dropdown>
296
- </div>
297
- </div>
298
-
299
- <div class="p-3">
300
- <label class="text-text-400 flex-1">Type</label>
301
- <div class="mt-2">
302
- <Dropdown v-model="preset.summary.mode" class="flex-1" @change="load">
303
- <option value="table">{{ $t('Table') }}</option>
304
- <option value="bar">{{ $t('Bar Chart') }}</option>
313
+ <option value="max">{{ $t('Max') }}</option>
314
+ <option value="min">{{ $t('Min') }}</option>
315
+ <option value="avg">{{ $t('Avg') }}</option>
305
316
  </Dropdown>
306
317
  </div>
307
318
  </div>
@@ -310,8 +321,6 @@
310
321
 
311
322
  </div>
312
323
 
313
- <div class="px-6 py-3 flex flex-row gap-2"></div>
314
-
315
324
  </div>
316
325
 
317
326
  </div>
@@ -325,14 +334,13 @@
325
334
 
326
335
  import throttle from "lodash/throttle";
327
336
  import {urlQuery} from "../utils/helpers.mjs";
328
- import { Bar } from 'vue-chartjs'
329
- import { Chart as ChartJS, Title, Tooltip, Legend, BarElement, CategoryScale, LinearScale } from 'chart.js'
330
-
331
- ChartJS.register(Title, Tooltip, Legend, BarElement, CategoryScale, LinearScale)
337
+ import { Bar, Line } from 'vue-chartjs'
338
+ import Chart from 'chart.js/auto'
339
+ import dayjs from "dayjs";
332
340
 
333
341
  export default{
334
342
 
335
- components: { Bar },
343
+ components: { Bar, Line },
336
344
 
337
345
  inject: [ 'socket', 'confirm', 'alert', 'socketEmit', 'toast' ],
338
346
 
@@ -421,6 +429,19 @@ export default{
421
429
 
422
430
  var style = getComputedStyle(document.body)
423
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
+ }
424
445
 
425
446
  return {
426
447
  responsive: true,
@@ -431,12 +452,20 @@ export default{
431
452
  scales: {
432
453
  x: {
433
454
  grid: {
434
- color: `rgb(${gridColor})`
435
- }
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
436
463
  },
437
464
  y: {
438
465
  grid: {
439
- color: `rgb(${gridColor})`
466
+ color: function(context){
467
+ return `rgb(${gridColor})`
468
+ }
440
469
  }
441
470
  }
442
471
  }
@@ -447,9 +476,21 @@ export default{
447
476
 
448
477
  //console.log(this.summary)
449
478
 
479
+ const column = this.summary.columns.filter((_) => _.key === 'dfDate').pop()
480
+
450
481
  const labels = []
451
482
  this.summary.items.forEach((item) => {
452
- labels.push(item.dfDate)
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)
453
494
  })
454
495
 
455
496
  const datasets = []
@@ -489,6 +530,52 @@ export default{
489
530
  if(this.preset && this.preset.summary && this.preset.summary.hideDetails)
490
531
  return this.preset.summary.hideDetails
491
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
492
579
  }
493
580
 
494
581
  },
@@ -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':
@@ -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){
@@ -164,6 +165,154 @@ let ListPage1 = {
164
165
  }
165
166
  },
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
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
314
+ },
315
+
167
316
  async subscribe(params, socket){
168
317
  const { name } = params
169
318
  if(name){