@mixd-id/web-scaffold 0.1.250801007 → 0.1.250801009

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.250801007",
4
+ "version": "0.1.250801009",
5
5
  "scripts": {
6
6
  "dev": "vite serve",
7
7
  "build": "vite build",
@@ -19,13 +19,13 @@
19
19
  </div>
20
20
  </slot>
21
21
 
22
- <slot name="default"></slot>
22
+ <slot name="default" :context="context"></slot>
23
23
 
24
24
  </div>
25
25
  </Transition>
26
26
  </Teleport>
27
27
  <div v-else :class="computedClass">
28
- <slot name="default"></slot>
28
+ <slot name="default" :context="context"></slot>
29
29
  </div>
30
30
  </template>
31
31
 
@@ -76,7 +76,8 @@ export default{
76
76
  data(){
77
77
  return {
78
78
  mounted: false,
79
- _state: false
79
+ _state: false,
80
+ context: {}
80
81
  }
81
82
  },
82
83
 
@@ -100,7 +101,9 @@ export default{
100
101
 
101
102
  },
102
103
 
103
- open(){
104
+ open(context = {}){
105
+ this.context = context
106
+
104
107
  if(this.query){
105
108
 
106
109
  }
@@ -129,6 +132,11 @@ export default{
129
132
 
130
133
  class: String,
131
134
 
135
+ dismissable: {
136
+ type: Boolean,
137
+ default: false
138
+ },
139
+
132
140
  float: Boolean,
133
141
 
134
142
  height: {
@@ -218,4 +226,13 @@ html[data-theme='dark'] .overlay{
218
226
  @apply bg-black/50;
219
227
  }
220
228
 
229
+ @media screen and (max-width: 640px){
230
+
231
+ .modal {
232
+ width: 100% !important;
233
+ max-height: 90vh;
234
+ @apply self-end;
235
+ }
236
+
237
+ }
221
238
  </style>
@@ -1,5 +1,6 @@
1
1
  <template>
2
- <div class="flex flex-col">
2
+ <div class="flex flex-col bg-base-400 md:bg-transparent"
3
+ v-intersect="onVisibleChange">
3
4
 
4
5
  <div v-if="readyState >= 1"
5
6
  class="flex-1 flex flex-row">
@@ -36,14 +37,14 @@
36
37
  :preset="preset"
37
38
  :presetSelector="$refs.presetSelector"
38
39
  :compPrefix="compPrefix">
39
- <div class="flex flex-col md:flex-row gap-3 md:items-end overflow-hidden p-3 md:p-0 md:panel-none border-b-[1px] border-border-50 md:border-none"
40
+ <div class="flex flex-col md:flex-row gap-3 md:items-end overflow-hidden p-3 pb-1 md:p-0"
40
41
  :class="headerClass">
41
-
42
- <div class="flex md:flex-1 flex-row items-end gap-3">
42
+ <div class="flex md:flex-1 flex-row items-end md:gap-3 py-1 md:py-0">
43
+ <slot name="left"></slot>
43
44
 
44
45
  <div class="flex flex-row gap-2 md:gap-3 items-center px-3" ref="title">
45
46
  <div class="flex flex-col whitespace-nowrap text-ellipsis overflow-hidden">
46
- <div class="cursor-pointer group leading-5" @click="$refs.contextMenu.open($refs.title)">
47
+ <div class="cursor-pointer group leading-4" @click="$refs.contextMenu.open($refs.title)">
47
48
  <small class="text-text-400 text-xs">{{ title ?? 'Untitled' }}</small>
48
49
  <div class="flex flex-row items-baseline gap-1">
49
50
  <h3 class="whitespace-nowrap relative text-ellipsis overflow-hidden group-hover:text-primary">
@@ -90,8 +91,9 @@
90
91
 
91
92
  <slot name="toolbar">
92
93
  </slot>
94
+
93
95
  <slot name="right-toolbar">
94
- <div class="flex flex-row gap-2" v-if="Boolean(toolbar)">
96
+ <div class="hidden md:flex flex-row gap-2" v-if="Boolean(toolbar)">
95
97
  <Textbox v-if="canSearch"
96
98
  class="flex-1 md:w-[240px]"
97
99
  placeholder="Search..."
@@ -106,26 +108,6 @@
106
108
  </div>
107
109
  </template>
108
110
  </Textbox>
109
-
110
- <div v-if="false && $slots.gridItem"
111
- class="hidden md:grid grid-cols-2 gap-[1px] border-[1px] border-border-50 rounded-lg">
112
- <Radio v-model="preset.view"
113
- name="view"
114
- value="table"
115
- :custom="true"
116
- class="p-2 px-3 rounded-lg rounded-r-none"
117
- :class="preset.view === 'table' ? 'bg-primary-500' : 'bg-base-500'">
118
- <svg width="14" height="14" :class="preset.view === 'table' ? 'fill-white' : 'fill-text'" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--! Font Awesome Pro 6.0.0-alpha3 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) --><path d="M448 32H64.05C28.7 32 .0492 60.65 .0492 96v320c0 35.35 28.65 64 64 64h383.1c35.35 0 64-28.65 64-64V96C512 60.65 483.4 32 448 32zM224 416H64v-96h160V416zM224 256H64V160h160V256zM448 416h-160v-96h160V416zM448 256h-160V160h160V256z"/></svg>
119
- </Radio>
120
- <Radio v-model="preset.view"
121
- name="view"
122
- value="grid"
123
- :custom="true"
124
- class="p-2 px-3 bg-base-500 rounded-lg rounded-l-none"
125
- :class="preset.view === 'grid' ? 'bg-primary-500' : 'bg-base-500'">
126
- <svg width="14" height="14" :class="preset.view === 'grid' ? 'fill-white' : 'fill-text'" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 512"><!--! Font Awesome Pro 6.0.0-alpha3 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) --><path d="M144 288h-96c-17.67 0-32 14.33-32 32v96c0 17.67 14.33 32 32 32h96c17.67 0 32-14.33 32-32v-96C176 302.3 161.7 288 144 288zM368 288h-96c-17.67 0-32 14.33-32 32v96c0 17.67 14.33 32 32 32h96c17.67 0 32-14.33 32-32v-96C400 302.3 385.7 288 368 288zM592 288h-96c-17.67 0-32 14.33-32 32v96c0 17.67 14.33 32 32 32h96c17.67 0 32-14.33 32-32v-96C624 302.3 609.7 288 592 288zM144 64h-96c-17.67 0-32 14.33-32 32v96c0 17.67 14.33 32 32 32h96c17.67 0 32-14.33 32-32V96C176 78.33 161.7 64 144 64zM368 64h-96c-17.67 0-32 14.33-32 32v96c0 17.67 14.33 32 32 32h96c17.67 0 32-14.33 32-32V96C400 78.33 385.7 64 368 64zM592 64h-96c-17.67 0-32 14.33-32 32v96c0 17.67 14.33 32 32 32h96c17.67 0 32-14.33 32-32V96C624 78.33 609.7 64 592 64z"/></svg>
127
- </Radio>
128
- </div>
129
111
  </div>
130
112
  </slot>
131
113
  </div>
@@ -133,6 +115,27 @@
133
115
  </div>
134
116
  </slot>
135
117
 
118
+ <div class="md:hidden">
119
+ <slot name="mobile-toolbar">
120
+ <div class="p-3">
121
+ <Textbox v-if="canSearch"
122
+ placeholder="Search..."
123
+ class="bg-base-300"
124
+ clearable="1"
125
+ maxlength="100"
126
+ v-model="preset.search"
127
+ @clear="delete preset.search; load()"
128
+ @keyup.enter="load">
129
+ <template #start>
130
+ <div class="p-2 pr-0">
131
+ <svg width="14" height="14" class="fill-text-300" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--! Font Awesome Pro 6.0.0-alpha3 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) --><path d="M504.1 471l-134-134C399.1 301.5 415.1 256.8 415.1 208c0-114.9-93.13-208-208-208S-.0002 93.13-.0002 208S93.12 416 207.1 416c48.79 0 93.55-16.91 129-45.04l134 134C475.7 509.7 481.9 512 488 512s12.28-2.344 16.97-7.031C514.3 495.6 514.3 480.4 504.1 471zM48 208c0-88.22 71.78-160 160-160s160 71.78 160 160s-71.78 160-160 160S48 296.2 48 208z"/></svg>
132
+ </div>
133
+ </template>
134
+ </Textbox>
135
+ </div>
136
+ </slot>
137
+ </div>
138
+
136
139
  <div class="flex-1 flex" ref="content">
137
140
  <div v-if="readyState === 2" class="flex-1 flex items-center justify-center">
138
141
  <svg class="animate-spin" width="36" height="36" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"><circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle><path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path></svg>
@@ -288,7 +291,7 @@ import VirtualGrid from "./VirtualGrid.vue";
288
291
  import PresetSelector from "../widgets/PresetSelector.vue";
289
292
  import {generatePivotColumns, generateTotalColumns, setupConfig, sortsFn} from "../utils/preset-selector.mjs";
290
293
  import PresetBar from "../widgets/PresetBar.vue";
291
- import {groupBy, invokeAfterIdle, queueForLater} from "../utils/helpers.mjs";
294
+ import {invokeAfterIdle, queueForLater} from "../utils/helpers.mjs";
292
295
 
293
296
  export default{
294
297
 
@@ -899,40 +902,42 @@ export default{
899
902
  if(column){
900
903
  delete column.freeze
901
904
  }
902
- }
905
+ },
903
906
 
904
- },
907
+ onVisibleChange({ inViewport, entry }){
908
+ if(inViewport){
909
+ this.$addResizeListener(this.$el, () => this.compPrefix = this.$util.calculateMediaPrefix(this.$el.clientWidth))
910
+
911
+ this.loadPreset()
912
+ .finally(() => {
913
+ this.readyState = 1
914
+ this.$nextTick(() => {
915
+ this.calcItemsPerPage()
916
+ this.load()
917
+ this.loadExt()
918
+ })
919
+ })
905
920
 
906
- components: {PresetBar, PresetSelector, VirtualGrid, VirtualTable},
921
+ if(this.subscribeKey){
922
+ this.useSocket().send('user.subscribe', { name:this.subscribeKey })
923
+ this.useSocket().on(this.subscribeKey, this.onSignal)
924
+ }
907
925
 
908
- mounted(){
909
- this.$addResizeListener(this.$el, () => this.compPrefix = this.$util.calculateMediaPrefix(this.$el.clientWidth))
926
+ window.addEventListener('keydown', this.onKeyDown)
910
927
 
911
- this.loadPreset()
912
- .finally(() => {
913
- this.readyState = 1
914
- this.$nextTick(() => {
915
- this.calcItemsPerPage()
916
- this.load()
917
- this.loadExt()
918
- })
928
+ this.queue = queueForLater({
929
+ pop: this.loadQueued,
930
+ delay: this.updateInterval
919
931
  })
920
932
 
921
- if(this.subscribeKey){
922
- this.useSocket().send('user.subscribe', { name:this.subscribeKey })
923
- this.useSocket().on(this.subscribeKey, this.onSignal)
924
- }
925
-
926
- window.addEventListener('keydown', this.onKeyDown)
927
-
928
- this.queue = queueForLater({
929
- pop: this.loadQueued,
930
- delay: this.updateInterval
931
- })
933
+ this.useSocket().on('connect', this.onConnect)
934
+ }
935
+ },
932
936
 
933
- this.useSocket().on('connect', this.onConnect)
934
937
  },
935
938
 
939
+ components: {PresetBar, PresetSelector, VirtualGrid, VirtualTable},
940
+
936
941
  unmounted() {
937
942
  this.$removeResizeListener(this.$el)
938
943
  window.removeEventListener('keydown', this.onKeyDown)
@@ -1,6 +1,6 @@
1
1
  <template>
2
2
  <div :class="$style.dropdown">
3
- <div ref="btn" class="flex flex-row items-center w-[150px]"
3
+ <div ref="btn" class="flex-1 flex flex-row items-center w-[150px]"
4
4
  :class="containerClass"
5
5
  @click="open()">
6
6
  <div class="flex-1 p-2 text-ellipsis-nowrap">
@@ -12,9 +12,9 @@
12
12
  </button>
13
13
  </div>
14
14
 
15
- <ContextMenu ref="contextMenu" :dismiss="false">
15
+ <ContextMenu ref="contextMenu" class="mt-2">
16
16
  <div class="flex flex-col min-w-[200px]">
17
- <div class="p-2">
17
+ <div class="p-2" v-if="viewedItems.length > 10">
18
18
  <Textbox v-model="search" :clearable="true" @clear="search = ''">
19
19
  <template #start>
20
20
  <div class="pl-2">
@@ -23,7 +23,7 @@
23
23
  </template>
24
24
  </Textbox>
25
25
  </div>
26
- <div v-for="item in viewedItems" class="p-2">
26
+ <div v-for="item in viewedItems" class="p-2" @click.stop>
27
27
  <Checkbox :value="item[valueKey]" v-model="selectedItems">
28
28
  {{ item[textKey] }}
29
29
  </Checkbox>
@@ -40,7 +40,12 @@ export default{
40
40
  props: {
41
41
  containerClass: String,
42
42
 
43
- modelValue: Array,
43
+ modelValue: {
44
+ type: Array,
45
+ validator(value){
46
+ return Array.isArray(value)
47
+ }
48
+ },
44
49
 
45
50
  items: Array,
46
51
 
@@ -58,15 +63,15 @@ export default{
58
63
  computed: {
59
64
 
60
65
  selectedText(){
61
- if(this.modelValue.length > 0)
66
+ if((this.modelValue ?? []).length > 0)
62
67
  return this.modelValue.map(_ => _[this.textKey] ?? _[this.valueKey]).join(', ')
63
68
  return this.$t('(Not Selected)')
64
69
  },
65
70
 
66
71
  viewedItems(){
67
72
  if(this.search !== '')
68
- return this.items.filter(_ => `${_[this.textKey]}`.toLowerCase().indexOf(this.search.toLowerCase()) >= 0)
69
- return this.items
73
+ return (this.items ?? []).filter(_ => `${_[this.textKey]}`.toLowerCase().indexOf(this.search.toLowerCase()) >= 0)
74
+ return this.items ?? []
70
75
  }
71
76
 
72
77
  },
@@ -81,7 +86,7 @@ export default{
81
86
  methods: {
82
87
 
83
88
  open(){
84
- this.selectedItems = this.modelValue.map(_ => _[this.valueKey])
89
+ this.selectedItems = (this.modelValue ?? []).map(_ => _[this.valueKey])
85
90
  this.$refs.contextMenu.open(this.$refs.btn)
86
91
  }
87
92
 
@@ -92,8 +97,7 @@ export default{
92
97
  selectedItems: {
93
98
  deep: true,
94
99
  handler(_items){
95
- this.$emit('update:modelValue',
96
- this.items.filter(_ => _items.includes(_[this.valueKey]))
100
+ this.$emit('update:modelValue', (this.items ?? []).filter(_ => _items.includes(_[this.valueKey]))
97
101
  )
98
102
  }
99
103
  }
@@ -92,7 +92,7 @@ export default{
92
92
 
93
93
  .timepicker{
94
94
  @apply inline-flex flex-row items-center;
95
- @apply bg-base-300 border-[1px] border-border-200 rounded-lg;
95
+ @apply bg-base-400 border-[1px] border-border-200 rounded-lg;
96
96
  @apply divide-x divide-border-50;
97
97
  }
98
98
 
@@ -1,5 +1,5 @@
1
1
  <template>
2
- <div :class="$style.virtualGrid" @click="resize">
2
+ <div :class="$style.virtualGrid">
3
3
 
4
4
  <div ref="scroller" :class="$style.scroller" :style="scrollerStyle">
5
5
  <div :class="spacerClass" ref="spacer" :style="spacerStyle">
@@ -273,7 +273,7 @@ export default{
273
273
 
274
274
  .virtualGrid{
275
275
  @apply flex-1 overflow-y-auto;
276
- @apply border-[1px] border-border-50 bg-base-400;
276
+ @apply md:border-[1px] border-border-50 md:bg-base-400;
277
277
  }
278
278
 
279
279
  .scroller{
@@ -286,7 +286,7 @@ export default{
286
286
  .spacer{
287
287
  will-change: auto;
288
288
  position: relative;
289
- @apply grid;
289
+ @apply grid divide-y divide-border-50 border-t-[1px] border-border-50;
290
290
  }
291
291
 
292
292
  .calc{
@@ -207,7 +207,7 @@ const _DEFAULT_COLUMN_WIDTH = '100px'
207
207
 
208
208
  export default{
209
209
 
210
- inject: [ 'emitRoot', 'listStyle' ],
210
+ inject: [ 'emitRoot' ],
211
211
 
212
212
  emits: [ 'dragover', 'freeze', 'unfreeze', 'scroll-end', 'item-click' ],
213
213
 
@@ -685,8 +685,7 @@ export default{
685
685
 
686
686
  return [
687
687
  this.$style.tdDiv,
688
- this.$style['align-' + align],
689
- this.listStyle ? this.listStyle[appearanceClass] : ''
688
+ this.$style['align-' + align]
690
689
  ]
691
690
  .join(' ')
692
691
  },
@@ -0,0 +1,26 @@
1
+ // directives/intersect.js
2
+ export default {
3
+ mounted(el, binding) {
4
+ const callback = binding.value;
5
+
6
+ const observer = new IntersectionObserver(
7
+ (entries) => {
8
+ entries.forEach(entry => {
9
+ callback({
10
+ inViewport: entry.isIntersecting,
11
+ entry
12
+ });
13
+ });
14
+ },
15
+ {
16
+ threshold: 0.1, // adjust sensitivity
17
+ }
18
+ );
19
+
20
+ observer.observe(el);
21
+ el._observer = observer;
22
+ },
23
+ unmounted(el) {
24
+ if (el._observer) el._observer.disconnect();
25
+ }
26
+ };
package/src/index.js CHANGED
@@ -2,6 +2,8 @@ import {defineAsyncComponent, ref} from "vue"
2
2
  import {arrayPush, arrayRemove, arrayUnshift, downloadUrl, invokeAfterIdle, observeInit} from './utils/helpers.mjs'
3
3
  import dayjs from "dayjs";
4
4
  import {useDevice} from './hooks/device.js'
5
+ import IntersectDirective from './directives/intersect';
6
+
5
7
 
6
8
  let _UNIQID = 0
7
9
  const uniqid = (additionalKey = '') => {
@@ -425,6 +427,8 @@ export default{
425
427
  app.config.globalProperties.$mounted.value = true
426
428
  }
427
429
 
430
+ app.directive('intersect', IntersectDirective)
431
+
428
432
  app.component('Ahref', defineAsyncComponent(() => import("./components/Ahref.vue")))
429
433
  app.component('Paragraph', defineAsyncComponent(() => import("./components/Paragraph.vue")))
430
434
  app.component('Alert', defineAsyncComponent(() => import("./components/Alert.vue")))
@@ -366,34 +366,57 @@ const getValue = (filter, opt) => {
366
366
  return whereObj
367
367
  }
368
368
 
369
- const filtersToSequelizeWhere = async(filters, opt) => {
370
- if(!Array.isArray(filters) || filters.length < 1){
371
- return {
372
- where: {},
373
- replacements: []
374
- }
369
+ const getColumnTypeFromModelAttribute = (attribute, opt) => {
370
+ const { DataTypes } = opt?.Sequelize ?? Sequelize
371
+
372
+ switch(attribute.type.key){
373
+ case DataTypes.BOOLEAN.key:
374
+ return 'boolean'
375
+
376
+ case DataTypes.INTEGER.key:
377
+ case DataTypes.FLOAT.key:
378
+ case DataTypes.DOUBLE.key:
379
+ case DataTypes.DECIMAL.key:
380
+ return 'number'
381
+
382
+ case DataTypes.DATE.key:
383
+ case DataTypes.DATEONLY.key:
384
+ return 'date'
385
+
386
+ default:
387
+ return 'string'
375
388
  }
389
+ }
376
390
 
391
+ const filtersToSequelizeWhere = async(filters, opt) => {
377
392
  let whereArr = []
378
393
  let replacements = []
379
394
  const { literal, Op } = opt.Sequelize ?? Sequelize
395
+ const { columns = [] } = opt?.config ?? {}
396
+ const modelAttributes = opt.model.getAttributes()
397
+ const tableName = opt.model.getTableName()
398
+
399
+ if(columns.length < 1){
400
+ for(let key in modelAttributes){
401
+ columns.push({
402
+ key,
403
+ fieldName: modelAttributes[key]?.fieldName || key,
404
+ type: getColumnTypeFromModelAttribute(modelAttributes[key], opt)
405
+ })
406
+ }
407
+ }
380
408
 
381
- const getValue = (filter, opt) => {
409
+ const keyColumns = (columns ?? []).reduce((res, cur) => {
410
+ res[cur.key] = cur
411
+ cur.fieldName = modelAttributes[cur.key]?.fieldName || cur.key
412
+ return res
413
+ }, {})
382
414
 
383
- const { columns } = opt.config
384
- let { key, operator, value, fn:filterFn } = filter
385
- const keyColumns = groupBy(columns, 'key')
386
- const type = ((keyColumns[key] ?? [])[0] ?? {}).type
415
+ const getValue = (filter) => {
416
+ let { key, operator, value, fn:filterFn, fieldName } = filter
417
+ const type = (keyColumns[key] ?? {}).type ?? 'string'
387
418
 
388
419
  let whereObj = {}
389
- let Model, field
390
- if(opt.model){
391
- const modelAttributes = opt.model.getAttributes()
392
- if(modelAttributes[key]){
393
- Model = opt.model
394
- field = modelAttributes[key].field
395
- }
396
- }
397
420
 
398
421
  switch(type){
399
422
 
@@ -539,7 +562,7 @@ const filtersToSequelizeWhere = async(filters, opt) => {
539
562
  switch(filterFn) {
540
563
 
541
564
  case 'format_hour':
542
- whereObj = literal(`DATE_FORMAT(${Model.name}.${field}, "%H") < "${filter.value}"`)
565
+ whereObj = literal(`DATE_FORMAT(${tableName}.${fieldName}, "%H") < "${filter.value}"`)
543
566
  break
544
567
 
545
568
  default:
@@ -557,7 +580,7 @@ const filtersToSequelizeWhere = async(filters, opt) => {
557
580
  switch(filterFn) {
558
581
 
559
582
  case 'format_hour':
560
- whereObj = literal(`DATE_FORMAT(${Model.name}.${field}, "%H") <= "${filter.value}"`)
583
+ whereObj = literal(`DATE_FORMAT(${tableName}.${fieldName}, "%H") <= "${filter.value}"`)
561
584
  break
562
585
 
563
586
  default:
@@ -575,19 +598,19 @@ const filtersToSequelizeWhere = async(filters, opt) => {
575
598
  switch(filterFn) {
576
599
 
577
600
  case 'format_date':
578
- whereObj = literal(`DATE_FORMAT(${Model.name}.${field}, "%Y-%m-%d") = "${filter.value}"`)
601
+ whereObj = literal(`DATE_FORMAT(${tableName}.${fieldName}, "%Y-%m-%d") = "${filter.value}"`)
579
602
  break
580
603
 
581
604
  case 'format_hour':
582
- whereObj = literal(`DATE_FORMAT(${Model.name}.${field}, "%H") = "${filter.value}"`)
605
+ whereObj = literal(`DATE_FORMAT(${tableName}.${fieldName}, "%H") = "${filter.value}"`)
583
606
  break
584
607
 
585
608
  case 'format_month':
586
- whereObj = literal(`DATE_FORMAT(${Model.name}.${field}, "%m") = "${filter.value.split('-')[1]}"`)
609
+ whereObj = literal(`DATE_FORMAT(${tableName}.${fieldName}, "%m") = "${filter.value.split('-')[1]}"`)
587
610
  break
588
611
 
589
612
  case 'format_year':
590
- whereObj = literal(`DATE_FORMAT(${Model.name}.${field}, "%Y") = "${filter.value}"`)
613
+ whereObj = literal(`DATE_FORMAT(${tableName}.${fieldName}, "%Y") = "${filter.value}"`)
591
614
  break
592
615
 
593
616
  default:
@@ -606,7 +629,7 @@ const filtersToSequelizeWhere = async(filters, opt) => {
606
629
  switch(filterFn) {
607
630
 
608
631
  case 'format_hour':
609
- whereObj = literal(`DATE_FORMAT(${Model.name}.${field}, "%H") > "${filter.value}"`)
632
+ whereObj = literal(`DATE_FORMAT(${tableName}.${fieldName}, "%H") > "${filter.value}"`)
610
633
  break
611
634
 
612
635
  default:
@@ -624,7 +647,7 @@ const filtersToSequelizeWhere = async(filters, opt) => {
624
647
  switch(filterFn) {
625
648
 
626
649
  case 'format_hour':
627
- whereObj = literal(`DATE_FORMAT(${Model.name}.${field}, "%H") >= "${filter.value}"`)
650
+ whereObj = literal(`DATE_FORMAT(${tableName}.${fieldName}, "%H") >= "${filter.value}"`)
628
651
  break
629
652
 
630
653
  default:
@@ -736,41 +759,74 @@ const filtersToSequelizeWhere = async(filters, opt) => {
736
759
  return whereObj
737
760
  }
738
761
 
739
- for(let filter of filters) {
740
- if (filter.enabled === false) continue
762
+ if(Array.isArray(filters)){
763
+ for(let filter of filters) {
764
+ if (filter.enabled === false) continue
741
765
 
742
- const { key, value, modifier = 'and' } = filter
766
+ const { key, value, modifier = 'and' } = filter
743
767
 
744
- if(key.indexOf('.') >= 0) continue
768
+ if(!keyColumns[key]) continue
745
769
 
746
- if(Array.isArray(value)){
770
+ if(Array.isArray(value)){
747
771
 
748
- const modifierValue = []
772
+ const modifierValue = []
749
773
 
750
- for(let _value of value){
751
- modifierValue.push(getValue({
752
- ..._value,
753
- key
754
- }, opt))
755
- }
774
+ for(let _value of value){
775
+ modifierValue.push(getValue({
776
+ ..._value,
777
+ key
778
+ }, opt))
779
+ }
756
780
 
757
- switch(modifier){
758
- case 'or':
759
- whereArr.push({
760
- [Op.or]: modifierValue
761
- })
762
- break
781
+ switch(modifier){
782
+ case 'or':
783
+ whereArr.push({
784
+ [Op.or]: modifierValue
785
+ })
786
+ break
763
787
 
764
- default:
765
- whereArr.push({
766
- [Op.and]: modifierValue
767
- })
768
- break
788
+ default:
789
+ whereArr.push({
790
+ [Op.and]: modifierValue
791
+ })
792
+ break
793
+ }
794
+ }
795
+ else{
796
+ whereArr.push(getValue(filter, opt))
769
797
  }
770
798
  }
771
- else{
772
- whereArr.push(getValue(filter, opt))
799
+ }
800
+ else if(filters && typeof filters === 'object'){
801
+
802
+ for(let key in filters){
803
+ if(!keyColumns[key]) continue
804
+
805
+ const value = filters[key]
806
+
807
+ if(Array.isArray(value)){
808
+ const modifierValue = []
809
+
810
+ for(let _value of value){
811
+ modifierValue.push(getValue({
812
+ ..._value,
813
+ key
814
+ }, opt))
815
+ }
816
+
817
+ whereArr.push({
818
+ [Op.and]: modifierValue
819
+ })
820
+ }
821
+ else{
822
+ whereArr.push(getValue({
823
+ key,
824
+ operator: '=',
825
+ value
826
+ }, opt))
827
+ }
773
828
  }
829
+
774
830
  }
775
831
 
776
832
  return {
@@ -1305,7 +1361,8 @@ const presetToSequelizeList = async(preset, opt = {}) => {
1305
1361
  replacements: initialReplacements,
1306
1362
  include,
1307
1363
  order:initialOrder,
1308
- logging = false
1364
+ logging = false,
1365
+ debug = false
1309
1366
  } = opt
1310
1367
 
1311
1368
  if(!model) return {}
@@ -1349,7 +1406,8 @@ const presetToSequelizeList = async(preset, opt = {}) => {
1349
1406
  const { where:filterWhere, replacements:filterReplacements } = await filtersToSequelizeWhere(filters, {
1350
1407
  config,
1351
1408
  model,
1352
- Sequelize: opt.Sequelize
1409
+ Sequelize: opt.Sequelize,
1410
+ debug
1353
1411
  })
1354
1412
 
1355
1413
  let searchWhere = {}, searchReplacements = []