@mixd-id/web-scaffold 0.2.240706 → 0.2.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.
Files changed (220) hide show
  1. package/docs/components/Dashboard.md +56 -0
  2. package/log.txt +7 -0
  3. package/package.json +27 -19
  4. package/src/components/404.vue +61 -0
  5. package/src/components/AccountIcon.vue +19 -0
  6. package/src/components/Ahref.vue +1 -1
  7. package/src/components/Alert.vue +4 -13
  8. package/src/components/ArrayList.vue +49 -0
  9. package/src/components/Article.vue +24 -30
  10. package/src/components/Button.vue +79 -167
  11. package/src/components/Card.vue +235 -0
  12. package/src/components/Carousel.vue +61 -60
  13. package/src/components/Cart.vue +192 -0
  14. package/src/components/CartIcon.vue +89 -0
  15. package/src/components/ChartBar.vue +2 -3
  16. package/src/components/Checkbox.vue +20 -11
  17. package/src/components/Checkout.vue +373 -0
  18. package/src/components/CheckoutDelivery.vue +267 -0
  19. package/src/components/CodeEditor.vue +5 -16
  20. package/src/components/CollapsiblePanel.vue +70 -0
  21. package/src/components/ColorPicker.vue +8 -3
  22. package/src/components/ColorPicker2.vue +41 -19
  23. package/src/components/ColorPicker3.vue +100 -0
  24. package/src/components/Confirm.vue +9 -7
  25. package/src/components/ContextMenu.vue +122 -206
  26. package/src/components/ContextMenuItem.vue +53 -0
  27. package/src/components/Dashboard.vue +243 -0
  28. package/src/components/Dashboard2.vue +118 -0
  29. package/src/components/DashboardComponentSelector.vue +96 -0
  30. package/src/components/DashboardConfigs.vue +202 -0
  31. package/src/components/Datepicker.vue +102 -41
  32. package/src/components/DayTimeRange.vue +3 -2
  33. package/src/components/Dropdown.vue +7 -4
  34. package/src/components/Flex.vue +14 -40
  35. package/src/components/GHeatMaps.vue +2 -2
  36. package/src/components/Grid.vue +6 -6
  37. package/src/components/HTMLEditor.vue +27 -14
  38. package/src/components/Image.vue +62 -108
  39. package/src/components/ImagePreview.vue +14 -4
  40. package/src/components/ImageUploader.vue +114 -0
  41. package/src/components/ImportModal.vue +3 -3
  42. package/src/components/Link.vue +62 -6
  43. package/src/components/List.vue +524 -403
  44. package/src/components/ListContextMenu.vue +88 -0
  45. package/src/components/ListItem.vue +5 -3
  46. package/src/components/ListPage1.vue +14 -15
  47. package/src/components/ListView.vue +5 -6
  48. package/src/components/ListViewSettings.vue +2 -2
  49. package/src/components/LogViewerItem.vue +1 -1
  50. package/src/components/MarkdownEdit.vue +128 -0
  51. package/src/components/MarkdownPreview.vue +102 -0
  52. package/src/components/MenuItem1.vue +36 -0
  53. package/src/components/Modal.vue +95 -43
  54. package/src/components/MultiDropdown.vue +124 -0
  55. package/src/components/MultilineText.vue +1 -4
  56. package/src/components/OTPField.vue +11 -17
  57. package/src/components/ObjectTree.vue +1 -1
  58. package/src/components/PageBuilder.vue +3 -3
  59. package/src/components/Paragraph.vue +1 -2
  60. package/src/components/PresetSelectorFilterItem.vue +107 -95
  61. package/src/components/Radio.vue +1 -1
  62. package/src/components/SearchModal.vue +153 -0
  63. package/src/components/Slider.vue +1 -1
  64. package/src/components/Svg.vue +1 -1
  65. package/src/components/SvgEditor.vue +173 -0
  66. package/src/components/Switch.vue +4 -5
  67. package/src/components/Table.vue +2 -2
  68. package/src/components/TableView.vue +2 -3
  69. package/src/components/TableViewHead.vue +2 -2
  70. package/src/components/Tabs.vue +1 -1
  71. package/src/components/Testimonial.vue +2 -2
  72. package/src/components/Text.vue +7 -22
  73. package/src/components/TextEditor.vue +3 -3
  74. package/src/components/TextWithTag.vue +61 -30
  75. package/src/components/Textarea.vue +11 -16
  76. package/src/components/Textbox.vue +9 -19
  77. package/src/components/Timepicker.vue +25 -15
  78. package/src/components/Toast.vue +5 -3
  79. package/src/components/TreeMenu.vue +122 -0
  80. package/src/components/TreeView.vue +15 -10
  81. package/src/components/TreeView2.vue +38 -0
  82. package/src/components/TreeViewItem.vue +58 -29
  83. package/src/components/TreeViewItem2.vue +55 -0
  84. package/src/components/Uploader.vue +45 -0
  85. package/src/components/Video.vue +119 -0
  86. package/src/components/VirtualGrid.vue +24 -7
  87. package/src/components/VirtualTable.vue +363 -128
  88. package/src/configs/dashboard/data-table.js +9 -0
  89. package/src/configs/web-page-builder.js +118 -0
  90. package/src/directives/intersect.js +26 -0
  91. package/src/hooks/device.js +14 -0
  92. package/src/index.js +62 -107
  93. package/src/mixin/component.js +147 -67
  94. package/src/themes/default/index.js +83 -155
  95. package/src/utils/dashboard.js +22 -962
  96. package/src/utils/helpers.cjs +635 -0
  97. package/src/utils/helpers.js +91 -60
  98. package/src/utils/helpers.mjs +245 -12
  99. package/src/utils/importer.js +22 -3
  100. package/src/utils/list.mjs +1509 -0
  101. package/src/utils/preset-selector.cjs +1455 -0
  102. package/src/utils/preset-selector.js +489 -95
  103. package/src/utils/preset-selector.mjs +59 -20
  104. package/src/utils/queue.js +63 -0
  105. package/src/utils/web.mjs +120 -0
  106. package/src/utils/wss.js +37 -29
  107. package/src/utils/wss.mjs +24 -19
  108. package/src/widgets/AhrefSetting.vue +16 -13
  109. package/src/widgets/ArticleSetting.vue +15 -27
  110. package/src/widgets/BackgroundColorSetting.vue +153 -0
  111. package/src/widgets/BorderColorSetting.vue +57 -0
  112. package/src/widgets/BotEditor/BotEditorActions.vue +3 -2
  113. package/src/widgets/BotEditor/BotEditorSettings.vue +21 -0
  114. package/src/widgets/BotEditor.vue +35 -15
  115. package/src/widgets/ButtonSetting.vue +12 -13
  116. package/src/widgets/CarouselSetting.vue +33 -45
  117. package/src/widgets/CartSetting.vue +46 -0
  118. package/src/widgets/CheckoutSetting.vue +46 -0
  119. package/src/widgets/CollapsiblePanelSetting.vue +46 -0
  120. package/src/widgets/ColumnSelector.vue +29 -5
  121. package/src/widgets/ComponentSetting.vue +1 -1
  122. package/src/widgets/ComponentSetting2.vue +112 -234
  123. package/src/widgets/ComponentSetting3.vue +1 -1
  124. package/src/widgets/ContactForm.vue +3 -3
  125. package/src/widgets/ContactFormSetting.vue +41 -30
  126. package/src/widgets/Dashboard/BarChart.vue +47 -11
  127. package/src/widgets/Dashboard/BarChartSetting.vue +1 -1
  128. package/src/widgets/Dashboard/DataTable.vue +125 -0
  129. package/src/widgets/Dashboard/DataTableSetting.vue +243 -0
  130. package/src/widgets/Dashboard/DatasourceSelector.vue +1 -1
  131. package/src/widgets/Dashboard/Doughnut.vue +49 -7
  132. package/src/widgets/Dashboard/DoughnutSetting.vue +2 -2
  133. package/src/widgets/Dashboard/Metric.vue +78 -19
  134. package/src/widgets/Dashboard/MetricSetting.vue +81 -28
  135. package/src/widgets/Dashboard/Pie.vue +55 -6
  136. package/src/widgets/Dashboard/PieSetting.vue +1 -1
  137. package/src/widgets/Dashboard/PolarArea.vue +49 -7
  138. package/src/widgets/Dashboard/PolarAreaSetting.vue +1 -1
  139. package/src/widgets/Dashboard/SharingModal.vue +4 -5
  140. package/src/widgets/Dashboard/ViewSelector.vue +2 -2
  141. package/src/widgets/Dashboard/VirtualTableSetting.vue +121 -184
  142. package/src/widgets/{Dashboard.vue → Dashboard0.vue} +426 -343
  143. package/src/widgets/EmbeddedVideoSetting.vue +7 -5
  144. package/src/widgets/FAQ.vue +16 -3
  145. package/src/widgets/FAQSetting.vue +53 -47
  146. package/src/widgets/FeatureList.vue +3 -0
  147. package/src/widgets/FeatureListSetting.vue +112 -102
  148. package/src/widgets/FlexSetting.vue +83 -106
  149. package/src/widgets/GridSetting.vue +71 -196
  150. package/src/widgets/Header2.vue +34 -71
  151. package/src/widgets/Header2Setting.vue +95 -179
  152. package/src/widgets/HeaderSetting.vue +16 -18
  153. package/src/widgets/IconListSetting.vue +69 -65
  154. package/src/widgets/ImageSetting.vue +33 -60
  155. package/src/widgets/LinkSetting.vue +60 -37
  156. package/src/widgets/LinkSettingModal.vue +173 -0
  157. package/src/widgets/LogViewer.vue +1 -1
  158. package/src/widgets/MarginSetting.vue +2 -2
  159. package/src/widgets/MenuEditor.vue +1 -1
  160. package/src/widgets/MenuItem1Setting.vue +78 -0
  161. package/src/widgets/ModalSetting.vue +42 -44
  162. package/src/widgets/MultiValueSetting.vue +2 -2
  163. package/src/widgets/MultiValueSetting2.vue +78 -45
  164. package/src/widgets/OGSettingModal.vue +103 -0
  165. package/src/widgets/PaddingSetting.vue +2 -2
  166. package/src/widgets/ParagraphSetting.vue +16 -13
  167. package/src/widgets/PositionSetting.vue +209 -0
  168. package/src/widgets/PresetBar.vue +359 -210
  169. package/src/widgets/PresetBarPivot.vue +31 -19
  170. package/src/widgets/PresetSelector.vue +29 -17
  171. package/src/widgets/SearchModalSetting.vue +70 -0
  172. package/src/widgets/Share.vue +1 -2
  173. package/src/widgets/ShareSetting.vue +67 -60
  174. package/src/widgets/StyleSetting.vue +227 -116
  175. package/src/widgets/TestimonialSetting.vue +97 -88
  176. package/src/widgets/TextBlockSetting.vue +16 -13
  177. package/src/widgets/UserActionBuilder/UserActionConsole.vue +30 -10
  178. package/src/widgets/UserActionBuilder/UserActionOutput.vue +2 -2
  179. package/src/widgets/UserActionBuilder/UserActionOutputReply.vue +64 -87
  180. package/src/widgets/UserActionBuilder/UserActionProps.vue +3 -3
  181. package/src/widgets/UserActionBuilder.vue +4 -16
  182. package/src/widgets/WebComponentSelector.vue +15 -11
  183. package/src/widgets/WebLayoutSelector.vue +41 -270
  184. package/src/widgets/WebPageBuilder.vue +693 -704
  185. package/src/widgets/WebPageBuilder2.vue +7 -7
  186. package/src/widgets/WebPageBuilder4/ButtonSetting.vue +0 -8
  187. package/src/widgets/WebPageBuilder4/CarouselSetting.vue +63 -7
  188. package/src/widgets/WebPageBuilder4/FlexAlignSetting.vue +3 -3
  189. package/src/widgets/WebPageBuilder4/FlexSetting.vue +1 -10
  190. package/src/widgets/WebPageBuilder4/MultiValueSetting.vue +2 -2
  191. package/src/widgets/WebPageBuilder4/PropertySetting.vue +0 -7
  192. package/src/widgets/WebPageBuilder4/WebPageComponentSelector.vue +1 -7
  193. package/src/widgets/WebPageBuilder4.vue +289 -575
  194. package/src/widgets/WebPageSelector.vue +1 -1
  195. package/src/widgets/YoutubeVideoSetting.vue +16 -13
  196. package/tailwind.config.js +3 -35
  197. package/docs/schema/user-action.json +0 -266
  198. package/src/App.vue +0 -25
  199. package/src/components/SearchButton.vue +0 -57
  200. package/src/entry-client.js +0 -27
  201. package/src/entry-server.js +0 -73
  202. package/src/events/event.js +0 -2
  203. package/src/main.js +0 -29
  204. package/src/mixin/website.js +0 -121
  205. package/src/router.js +0 -57
  206. package/src/widgets/MobileMenu.vue +0 -182
  207. package/src/widgets/WebPageBuilder4/ActionSetting.vue +0 -158
  208. package/src/widgets/WebPageBuilder4/ColorSetting.vue +0 -63
  209. package/src/widgets/WebPageBuilder4/DataSetting.vue +0 -92
  210. package/src/widgets/WebPageBuilder4/FontSizeSetting.vue +0 -76
  211. package/src/widgets/WebPageBuilder4/LinkSetting.vue +0 -68
  212. package/src/widgets/WebPageBuilder4/MobileMenuSetting.vue +0 -106
  213. package/src/widgets/WebPageBuilder4/Setting.vue +0 -73
  214. package/src/widgets/WebPageBuilder4/StyleSetting.vue +0 -77
  215. package/src/widgets/WebPageBuilder4/SvgSetting.vue +0 -207
  216. package/src/widgets/WebPageBuilder4/TextTransformSetting.vue +0 -70
  217. package/src/widgets/WebPageBuilder4/WebPageDataEdit.vue +0 -121
  218. package/test.json +0 -22
  219. /package/src/widgets/{Header1.vue → Header0.vue} +0 -0
  220. /package/src/widgets/{Header1Setting.vue → Header0Setting.vue} +0 -0
@@ -0,0 +1,1509 @@
1
+ import dayjs from "dayjs";
2
+ import Sequelize from 'sequelize'
3
+ import {groupBy} from './helpers.mjs'
4
+
5
+ const ftWildcard = (key) => {
6
+ if(key.indexOf('/') >= 0) return key
7
+
8
+ const term = key.replace(/[\-\+\*\<\>\@\(\)\~]/, ' ', 'gi')
9
+
10
+ const searchTerms = []
11
+ term.split(' ').forEach((txt) => {
12
+ if(txt.length >= 3){
13
+ searchTerms.push(`+${txt}*`)
14
+ }
15
+ })
16
+ return searchTerms.join(' ')
17
+ }
18
+
19
+ const getValue = (filter, opt) => {
20
+
21
+ const { columns, withoutKey = false } = opt
22
+ const { key, operator } = filter
23
+ const keyColumns = groupBy(columns, 'key')
24
+ const type = ((keyColumns[key] ?? [])[0] ?? {}).type
25
+ const { Op } = opt.Sequelize ?? Sequelize
26
+
27
+ let whereObj = {}
28
+
29
+ switch(type){
30
+
31
+ case 'boolean':
32
+ withoutKey ? whereObj = { [Op.eq]:filter.value } : whereObj[key] = filter.value
33
+ break
34
+
35
+ case 'number':
36
+ switch(operator) {
37
+ case '=':
38
+ withoutKey ? whereObj = { [Op.eq]:filter.value } : whereObj[key] = filter.value
39
+ break
40
+
41
+ case '>':
42
+ case '>=':
43
+ case '<':
44
+ case '<=':
45
+ withoutKey ?
46
+ whereObj = { [Op[operator]]:filter.value } :
47
+ whereObj[key] = {
48
+ [Op[operator]]: filter.value
49
+ }
50
+ break
51
+
52
+ case 'in':
53
+ withoutKey ?
54
+ whereObj = { [Op.in]: (Array.isArray(filter.value) ? filter.value : filter.value.split(','))
55
+ .map(_ => parseInt(_))
56
+ .filter(_ => !isNaN(_)) } :
57
+ whereObj[key] = {
58
+ [Op.in]: filter.value.split(',')
59
+ .map(_ => parseInt(_))
60
+ .filter(_ => !isNaN(_))
61
+ }
62
+ break
63
+
64
+ case 'notIn':
65
+ withoutKey ?
66
+ whereObj = { [Op.notIn]: filter.value.split(',')
67
+ .map(_ => parseInt(_))
68
+ .filter(_ => !isNaN(_)) } :
69
+ whereObj[key] = {
70
+ [Op.notIn]: filter.value.split(',')
71
+ .map(_ => parseInt(_))
72
+ .filter(_ => !isNaN(_))
73
+ }
74
+ break
75
+ }
76
+ break
77
+
78
+ case 'date':
79
+ switch(operator) {
80
+
81
+ case 'thisWeek':
82
+ withoutKey ?
83
+ whereObj = {
84
+ [Op.between]: [
85
+ dayjs().startOf('week').format('YYYY-MM-DD 00:00:00'),
86
+ dayjs().endOf('week').format('YYYY-MM-DD 23:59:59'),
87
+ ]
88
+ } :
89
+ whereObj[key] = {
90
+ [Op.between]: [
91
+ dayjs().startOf('week').format('YYYY-MM-DD 00:00:00'),
92
+ dayjs().endOf('week').format('YYYY-MM-DD 23:59:59'),
93
+ ]
94
+ }
95
+ break
96
+
97
+ case 'thisMonth':
98
+ withoutKey ?
99
+ whereObj = {
100
+ [Op.between]: [
101
+ dayjs().startOf('month').format('YYYY-MM-DD 00:00:00'),
102
+ dayjs().endOf('month').format('YYYY-MM-DD 23:59:59'),
103
+ ]
104
+ } :
105
+ whereObj[key] = {
106
+ [Op.between]: [
107
+ dayjs().startOf('month').format('YYYY-MM-DD 00:00:00'),
108
+ dayjs().endOf('month').format('YYYY-MM-DD 23:59:59'),
109
+ ]
110
+ }
111
+ break
112
+
113
+ case 'thisYear':
114
+ withoutKey ?
115
+ whereObj = {
116
+ [Op.between]: [
117
+ dayjs().startOf('year').format('YYYY-MM-DD 00:00:00'),
118
+ dayjs().endOf('year').format('YYYY-MM-DD 23:59:59'),
119
+ ]
120
+ } :
121
+ whereObj[key] = {
122
+ [Op.between]: [
123
+ dayjs().startOf('year').format('YYYY-MM-DD 00:00:00'),
124
+ dayjs().endOf('year').format('YYYY-MM-DD 23:59:59'),
125
+ ]
126
+ }
127
+ break
128
+
129
+ case 'null':
130
+ withoutKey ?
131
+ whereObj = {
132
+ [Op.eq]: null
133
+ } :
134
+ whereObj[key] = {
135
+ [Op.eq]: null
136
+ }
137
+ break
138
+
139
+ case 'between':
140
+ withoutKey ?
141
+ whereObj = {
142
+ [Op.between]: [
143
+ dayjs(filter.value).format('YYYY-MM-DD 00:00:00'),
144
+ dayjs(filter.value2).format('YYYY-MM-DD 23:59:59'),
145
+ ]
146
+ } :
147
+ whereObj[key] = {
148
+ [Op.between]: [
149
+ dayjs(filter.value).format('YYYY-MM-DD 00:00:00'),
150
+ dayjs(filter.value2).format('YYYY-MM-DD 23:59:59'),
151
+ ]
152
+ }
153
+ break
154
+
155
+ case 'yesterday':
156
+ withoutKey ?
157
+ whereObj = {
158
+ [Op.between]: [
159
+ dayjs().subtract(1, 'day').startOf('day').format('YYYY-MM-DD 00:00:00'),
160
+ dayjs().subtract(1, 'day').endOf('day').format('YYYY-MM-DD 23:59:59'),
161
+ ]
162
+ } :
163
+ whereObj[key] = {
164
+ [Op.between]: [
165
+ dayjs().subtract(1, 'day').startOf('day').format('YYYY-MM-DD 00:00:00'),
166
+ dayjs().subtract(1, 'day').endOf('day').format('YYYY-MM-DD 23:59:59'),
167
+ ]
168
+ }
169
+ break
170
+
171
+ case 'today':
172
+ withoutKey ?
173
+ whereObj = {
174
+ [Op.between]: [
175
+ dayjs().startOf('day').format('YYYY-MM-DD 00:00:00'),
176
+ dayjs().endOf('day').format('YYYY-MM-DD 23:59:59'),
177
+ ]
178
+ } :
179
+ whereObj[key] = {
180
+ [Op.between]: [
181
+ dayjs().startOf('day').format('YYYY-MM-DD 00:00:00'),
182
+ dayjs().endOf('day').format('YYYY-MM-DD 23:59:59'),
183
+ ]
184
+ }
185
+ break
186
+
187
+ case '<':
188
+ withoutKey ?
189
+ whereObj = {
190
+ [Op.lt]: [
191
+ dayjs(filter.value).format('YYYY-MM-DD 00:00:00'),
192
+ ]
193
+ } :
194
+ whereObj[key] = {
195
+ [Op.lt]: [
196
+ dayjs(filter.value).format('YYYY-MM-DD 00:00:00'),
197
+ ]
198
+ }
199
+ break
200
+
201
+ case '<=':
202
+ withoutKey ?
203
+ whereObj = {
204
+ [Op.lte]: [
205
+ dayjs(filter.value).format('YYYY-MM-DD 23:59:59'),
206
+ ]
207
+ } :
208
+ whereObj[key] = {
209
+ [Op.lte]: [
210
+ dayjs(filter.value).format('YYYY-MM-DD 23:59:59'),
211
+ ]
212
+ }
213
+ break
214
+
215
+ case '=':
216
+ withoutKey ?
217
+ whereObj = {
218
+ [Op.between]: [
219
+ dayjs(filter.value).format('YYYY-MM-DD 00:00:00'),
220
+ dayjs(filter.value).format('YYYY-MM-DD 23:59:59'),
221
+ ]
222
+ } :
223
+ whereObj[key] = {
224
+ [Op.between]: [
225
+ dayjs(filter.value).format('YYYY-MM-DD 00:00:00'),
226
+ dayjs(filter.value).format('YYYY-MM-DD 23:59:59'),
227
+ ]
228
+ }
229
+ break
230
+
231
+ case '>':
232
+ withoutKey ?
233
+ whereObj = {
234
+ [Op.gt]: [
235
+ dayjs(filter.value).format('YYYY-MM-DD 23:59:59'),
236
+ ]
237
+ } :
238
+ whereObj[key] = {
239
+ [Op.gt]: [
240
+ dayjs(filter.value).format('YYYY-MM-DD 23:59:59'),
241
+ ]
242
+ }
243
+ break
244
+
245
+ case '>=':
246
+ withoutKey ?
247
+ whereObj = {
248
+ [Op.gte]: [
249
+ dayjs(filter.value).format('YYYY-MM-DD 00:00:00'),
250
+ ]
251
+ } :
252
+ whereObj[key] = {
253
+ [Op.gte]: [
254
+ dayjs(filter.value).format('YYYY-MM-DD 00:00:00'),
255
+ ]
256
+ }
257
+ break
258
+
259
+ }
260
+ break
261
+
262
+ default:
263
+ switch(operator) {
264
+ case '=':
265
+ withoutKey ?
266
+ whereObj = {
267
+ [Op.eq]: filter.value
268
+ } :
269
+ whereObj[key] = {
270
+ [Op.eq]: filter.value
271
+ }
272
+ break
273
+
274
+ case 'eq':
275
+ case 'not':
276
+ withoutKey ?
277
+ whereObj = {
278
+ [Op[operator]]: filter.value
279
+ } :
280
+ whereObj[key] = {
281
+ [Op[operator]]: filter.value
282
+ }
283
+ break
284
+
285
+ case 'startsWith':
286
+ withoutKey ?
287
+ whereObj = {
288
+ [Op.like]: `${filter.value}%`
289
+ } :
290
+ whereObj[key] = {
291
+ [Op.like]: `${filter.value}%`
292
+ }
293
+ break
294
+
295
+ case 'endsWith':
296
+ withoutKey ?
297
+ whereObj = {
298
+ [Op.like]: `%${filter.value}`
299
+ } :
300
+ whereObj[key] = {
301
+ [Op.like]: `%${filter.value}`
302
+ }
303
+ break
304
+
305
+ case 'contains':
306
+ withoutKey ?
307
+ whereObj = {
308
+ [Op.like]: `%${filter.value}%`
309
+ } :
310
+ whereObj[key] = {
311
+ [Op.like]: `%${filter.value}%`
312
+ }
313
+ break
314
+
315
+ case 'notContains':
316
+ withoutKey ?
317
+ whereObj = {
318
+ [Op.notLike]: `%${filter.value}%`
319
+ } :
320
+ whereObj[key] = {
321
+ [Op.notLike]: `%${filter.value}%`
322
+ }
323
+ break
324
+
325
+ case 'in':
326
+ withoutKey ?
327
+ whereObj = {
328
+ [Op.in]: (!Array.isArray(filter.value) ? filter.value.split(',') : filter.value)
329
+ .map(_ => parseInt(_))
330
+ .filter(_ => !isNaN(_))
331
+ } :
332
+ whereObj[key] = {
333
+ [Op.in]: (!Array.isArray(filter.value) ? filter.value.split(',') : filter.value)
334
+ .map(_ => parseInt(_))
335
+ .filter(_ => !isNaN(_))
336
+ }
337
+ break
338
+
339
+ case 'notIn':
340
+ withoutKey ?
341
+ whereObj = {
342
+ [Op.notIn]: filter.value.split(',')
343
+ .map(_ => parseInt(_))
344
+ .filter(_ => !isNaN(_))
345
+ } :
346
+ whereObj[key] = {
347
+ [Op.notIn]: filter.value.split(',')
348
+ .map(_ => parseInt(_))
349
+ .filter(_ => !isNaN(_))
350
+ }
351
+ break
352
+
353
+ case 'regex':
354
+ withoutKey ?
355
+ whereObj = {
356
+ [Op.regexp]: filter.value
357
+ } :
358
+ whereObj[key] = {
359
+ [Op.regexp]: filter.value
360
+ }
361
+ break
362
+ }
363
+ break
364
+ }
365
+
366
+ return whereObj
367
+ }
368
+
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'
388
+ }
389
+ }
390
+
391
+ const filtersToSequelizeWhere = async(filters, opt) => {
392
+ let whereArr = []
393
+ let replacements = []
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
+ }
408
+
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
+ }, {})
414
+
415
+ const getValue = (filter) => {
416
+ let { key, operator, value, fn:filterFn, fieldName } = filter
417
+ const type = (keyColumns[key] ?? {}).type ?? 'string'
418
+
419
+ let whereObj = {}
420
+
421
+ switch(type){
422
+
423
+ case 'boolean':
424
+ value = !!value
425
+ whereObj = { [key]: value }
426
+ break
427
+
428
+ case 'number':
429
+ case 'currency':
430
+ value = parseFloat(value)
431
+
432
+ switch(operator) {
433
+ case '=':
434
+ whereObj = { [key]:value }
435
+ break
436
+
437
+ case '>':
438
+ whereObj = { [key]:{ [Op.gt]:value } }
439
+ break
440
+
441
+ case '>=':
442
+ whereObj = { [key]:{ [Op.gte]:value } }
443
+ break
444
+
445
+ case '<':
446
+ whereObj = { [key]:{ [Op.lt]:value } }
447
+ break
448
+
449
+ case '<=':
450
+ whereObj = { [key]:{ [Op.lte]:value } }
451
+ break
452
+
453
+ case 'in':
454
+ whereObj = {
455
+ [key]:{
456
+ [Op.in]: (!Array.isArray(filter.value) ? filter.value.split(',') : filter.value)
457
+ .map(_ => parseInt(_))
458
+ .filter(_ => !isNaN(_))
459
+ }
460
+ }
461
+ break
462
+
463
+ case 'notIn':
464
+ whereObj = {
465
+ [key]:{
466
+ [Op.notIn]: (!Array.isArray(filter.value) ? filter.value.split(',') : filter.value)
467
+ .map(_ => parseInt(_))
468
+ .filter(_ => !isNaN(_))
469
+ }
470
+ }
471
+ break
472
+ }
473
+ break
474
+
475
+ case 'date':
476
+ switch(operator) {
477
+
478
+ case 'thisWeek':
479
+ whereObj = {
480
+ [key]:{
481
+ [Op.between]: [
482
+ dayjs().startOf('week').format('YYYY-MM-DD 00:00:00'),
483
+ dayjs().endOf('week').format('YYYY-MM-DD 23:59:59'),
484
+ ]
485
+ }
486
+ }
487
+ break
488
+
489
+ case 'thisMonth':
490
+ whereObj = {
491
+ [key]:{
492
+ [Op.between]: [
493
+ dayjs().startOf('month').format('YYYY-MM-DD 00:00:00'),
494
+ dayjs().endOf('month').format('YYYY-MM-DD 23:59:59'),
495
+ ]
496
+ }
497
+ }
498
+ break
499
+
500
+ case 'lastMonth':
501
+ whereObj = {
502
+ [key]:{
503
+ [Op.between]: [
504
+ dayjs().subtract(1, 'month').startOf('month').format('YYYY-MM-DD 00:00:00'),
505
+ dayjs().subtract(1, 'month').endOf('month').format('YYYY-MM-DD 23:59:59'),
506
+ ]
507
+ }
508
+ }
509
+ break
510
+
511
+ case 'thisYear':
512
+ whereObj = {
513
+ [key]:{
514
+ [Op.between]: [
515
+ dayjs().startOf('year').format('YYYY-MM-DD 00:00:00'),
516
+ dayjs().endOf('year').format('YYYY-MM-DD 23:59:59'),
517
+ ]
518
+ }
519
+ }
520
+ break
521
+
522
+ case 'null':
523
+ whereObj = {
524
+ [key]: null
525
+ }
526
+ break
527
+
528
+ case 'between':
529
+ whereObj = {
530
+ [key]:{
531
+ [Op.between]: [
532
+ dayjs(filter.value).format('YYYY-MM-DD 00:00:00'),
533
+ dayjs(filter.value2).format('YYYY-MM-DD 23:59:59'),
534
+ ]
535
+ }
536
+ }
537
+ break
538
+
539
+ case 'yesterday':
540
+ whereObj = {
541
+ [key]:{
542
+ [Op.between]: [
543
+ dayjs().subtract(1, 'day').startOf('day').format('YYYY-MM-DD 00:00:00'),
544
+ dayjs().subtract(1, 'day').endOf('day').format('YYYY-MM-DD 23:59:59'),
545
+ ]
546
+ }
547
+ }
548
+ break
549
+
550
+ case 'today':
551
+ whereObj = {
552
+ [key]:{
553
+ [Op.between]: [
554
+ dayjs().startOf('day').format('YYYY-MM-DD 00:00:00'),
555
+ dayjs().endOf('day').format('YYYY-MM-DD 23:59:59'),
556
+ ]
557
+ }
558
+ }
559
+ break
560
+
561
+ case '<':
562
+ switch(filterFn) {
563
+
564
+ case 'format_hour':
565
+ whereObj = literal(`DATE_FORMAT(${tableName}.${fieldName}, "%H") < "${filter.value}"`)
566
+ break
567
+
568
+ default:
569
+ whereObj = {
570
+ [key]: {
571
+ [Op.lt]: [
572
+ dayjs(filter.value).format('YYYY-MM-DD 00:00:00'),
573
+ ]
574
+ }
575
+ }
576
+ }
577
+ break
578
+
579
+ case '<=':
580
+ switch(filterFn) {
581
+
582
+ case 'format_hour':
583
+ whereObj = literal(`DATE_FORMAT(${tableName}.${fieldName}, "%H") <= "${filter.value}"`)
584
+ break
585
+
586
+ default:
587
+ whereObj = {
588
+ [key]: {
589
+ [Op.lte]: [
590
+ dayjs(filter.value).format('YYYY-MM-DD 23:59:59'),
591
+ ]
592
+ }
593
+ }
594
+ }
595
+ break
596
+
597
+ case '=':
598
+ switch(filterFn) {
599
+
600
+ case 'format_date':
601
+ whereObj = literal(`DATE_FORMAT(${tableName}.${fieldName}, "%Y-%m-%d") = "${filter.value}"`)
602
+ break
603
+
604
+ case 'format_hour':
605
+ whereObj = literal(`DATE_FORMAT(${tableName}.${fieldName}, "%H") = "${filter.value}"`)
606
+ break
607
+
608
+ case 'format_month':
609
+ whereObj = literal(`DATE_FORMAT(${tableName}.${fieldName}, "%m") = "${filter.value.split('-')[1]}"`)
610
+ break
611
+
612
+ case 'format_year':
613
+ whereObj = literal(`DATE_FORMAT(${tableName}.${fieldName}, "%Y") = "${filter.value}"`)
614
+ break
615
+
616
+ default:
617
+ whereObj = {
618
+ [key]: {
619
+ [Op.between]: [
620
+ dayjs(filter.value).format('YYYY-MM-DD 00:00:00'),
621
+ dayjs(filter.value).format('YYYY-MM-DD 23:59:59'),
622
+ ]
623
+ }
624
+ }
625
+ }
626
+ break
627
+
628
+ case '>':
629
+ switch(filterFn) {
630
+
631
+ case 'format_hour':
632
+ whereObj = literal(`DATE_FORMAT(${tableName}.${fieldName}, "%H") > "${filter.value}"`)
633
+ break
634
+
635
+ default:
636
+ whereObj = {
637
+ [key]: {
638
+ [Op.gt]: [
639
+ dayjs(filter.value).format('YYYY-MM-DD 23:59:59'),
640
+ ]
641
+ }
642
+ }
643
+ }
644
+ break
645
+
646
+ case '>=':
647
+ switch(filterFn) {
648
+
649
+ case 'format_hour':
650
+ whereObj = literal(`DATE_FORMAT(${tableName}.${fieldName}, "%H") >= "${filter.value}"`)
651
+ break
652
+
653
+ default:
654
+ whereObj = {
655
+ [key]: {
656
+ [Op.gte]: [
657
+ dayjs(filter.value).format('YYYY-MM-DD 00:00:00'),
658
+ ]
659
+ }
660
+ }
661
+ }
662
+ break
663
+
664
+ }
665
+ break
666
+
667
+ default:
668
+ switch(operator) {
669
+ case '=':
670
+ case 'eq':
671
+ whereObj = {
672
+ [key]:{
673
+ [Op.eq]: filter.value
674
+ }
675
+ }
676
+ break
677
+
678
+ case 'not':
679
+ whereObj = {
680
+ [key]:{
681
+ [Op[operator]]: filter.value
682
+ }
683
+ }
684
+ break
685
+
686
+ case 'startsWith':
687
+ whereObj = {
688
+ [key]:{
689
+ [Op.like]: `${filter.value}%`
690
+ }
691
+ }
692
+ break
693
+
694
+ case 'endsWith':
695
+ whereObj = {
696
+ [key]:{
697
+ [Op.like]: `%${filter.value}`
698
+ }
699
+ }
700
+ break
701
+
702
+ case 'contains':
703
+ whereObj = {
704
+ [key]:{
705
+ [Op.like]: `%${filter.value}%`
706
+ }
707
+ }
708
+ break
709
+
710
+ case 'notContains':
711
+ whereObj = {
712
+ [key]:{
713
+ [Op.notLike]: `%${filter.value}%`
714
+ }
715
+ }
716
+ break
717
+
718
+ case 'in':
719
+ whereObj = {
720
+ [key]:{
721
+ [Op.in]: (!Array.isArray(filter.value) ? filter.value.split(',') : filter.value)
722
+ .filter(_ => _)
723
+ }
724
+ }
725
+ break
726
+
727
+ case 'notIn':
728
+ whereObj = {
729
+ [key]:{
730
+ [Op.notIn]: filter.value.split(',')
731
+ .map(_ => parseInt(_))
732
+ .filter(_ => !isNaN(_))
733
+ }
734
+ }
735
+ break
736
+
737
+ case 'regex':
738
+ whereObj = {
739
+ [key]:{
740
+ [Op.regexp]: filter.value
741
+ }
742
+ }
743
+ break
744
+
745
+ case 'notEmpty':
746
+ whereObj = {
747
+ [key]:{
748
+ [Op.or]: [
749
+ { [Op.ne]: null },
750
+ { [Op.ne]: '' }
751
+ ]
752
+ }
753
+ }
754
+ break
755
+ }
756
+ break
757
+ }
758
+
759
+ return whereObj
760
+ }
761
+
762
+ if(Array.isArray(filters)){
763
+ for(let filter of filters) {
764
+ if (filter.enabled === false) continue
765
+
766
+ const { key, value, modifier = 'and' } = filter
767
+
768
+ if(!keyColumns[key]) continue
769
+
770
+ if(Array.isArray(value)){
771
+
772
+ const modifierValue = []
773
+
774
+ for(let _value of value){
775
+ modifierValue.push(getValue({
776
+ ..._value,
777
+ key
778
+ }, opt))
779
+ }
780
+
781
+ switch(modifier){
782
+ case 'or':
783
+ whereArr.push({
784
+ [Op.or]: modifierValue
785
+ })
786
+ break
787
+
788
+ default:
789
+ whereArr.push({
790
+ [Op.and]: modifierValue
791
+ })
792
+ break
793
+ }
794
+ }
795
+ else{
796
+ whereArr.push(getValue(filter, opt))
797
+ }
798
+ }
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
+ }
828
+ }
829
+
830
+ }
831
+
832
+ return {
833
+ where: {
834
+ [Op.and]: whereArr
835
+ },
836
+ replacements
837
+ }
838
+ }
839
+
840
+ const afterItemToSequelizeWhere = async (order, afterItem, opt = {}) => {
841
+ if(!afterItem){
842
+ return { where:{}, replacements:[] }
843
+ }
844
+
845
+ const { Op } = opt.Sequelize ?? Sequelize
846
+
847
+ if(order.filter((_) => _[0] === 'id').length <= 0){
848
+ order.push([ 'id', 'desc' ])
849
+ }
850
+
851
+ const opOr = []
852
+ for(let i = 0 ; i < order.length ; i++){
853
+
854
+ const obj = order[i]
855
+
856
+ if(obj.length !== 2) continue
857
+
858
+ const exp = {}
859
+ for(let j = 0 ; j < i ; j++){
860
+ exp[order[j][0]] = afterItem[order[j][0]]
861
+ }
862
+ exp[obj[0]] = obj[1] === 'desc' ? { [Op.lt]:afterItem[obj[0]] } : { [Op.gt]:afterItem[obj[0]] }
863
+
864
+ if(Object.keys(exp).length === 1){
865
+ opOr.push(exp)
866
+ }
867
+ else{
868
+ opOr.push({
869
+ [Op.and]: exp
870
+ })
871
+ }
872
+ }
873
+
874
+ let where = {}
875
+ const replacements = []
876
+ if(opOr.length > 0){
877
+ where = {
878
+ [Op.or]: opOr
879
+ }
880
+ }
881
+
882
+ return {
883
+ where,
884
+ replacements
885
+ }
886
+ }
887
+
888
+ const searchToSequelizeWhere = async (search, params) => {
889
+ if(!search && (!Array.isArray(params) || params.length < 1)){
890
+ return {
891
+ where: {},
892
+ replacements: []
893
+ }
894
+ }
895
+
896
+ const { match } = params
897
+ const { literal } = params.Sequelize ?? Sequelize
898
+
899
+ let where = {}
900
+ let replacements = []
901
+
902
+ if(match){
903
+
904
+ if(Array.isArray(match) && match.length > 0){
905
+ where = literal(`MATCH(${match.join(',')}) AGAINST (? IN BOOLEAN MODE)`)
906
+ replacements.push(ftWildcard(search))
907
+ }
908
+
909
+ }
910
+
911
+ return {
912
+ where,
913
+ replacements
914
+ }
915
+ }
916
+
917
+ const chartToSequelizeWhere = async (chart, opt) => {
918
+
919
+ const { model, where, replacements } = opt
920
+ const { type } = chart
921
+ const { xAxis, yAxis } = chart[type] ?? {}
922
+ const { literal, Op } = opt.Sequelize ?? Sequelize
923
+
924
+ let attributes = []
925
+ let group = []
926
+ let include = []
927
+ let order
928
+
929
+ switch(type){
930
+
931
+ case 'doughnut':
932
+ case 'pie':
933
+ if(yAxis.length > 0){
934
+ const field = model.rawAttributes[yAxis[0].key].field
935
+
936
+ for(let xAxe of xAxis){
937
+ if(!xAxe.key) continue
938
+
939
+ const field = model.rawAttributes[xAxe.key].field
940
+
941
+ switch(xAxe.aggregrate){
942
+
943
+ case 'date':
944
+ attributes.push([literal(`DATE_FORMAT(${field}, "%Y-%m-%d")`), xAxis[0].key])
945
+ group.push(literal(`DATE_FORMAT(${field}, "%Y-%m-%d")`))
946
+ order = literal(`DATE_FORMAT(${field}, \"%Y-%m-%d\")`)
947
+ break
948
+
949
+ default:
950
+ attributes.push([literal(`${field}`), xAxe.key])
951
+ group.push(literal(`${field}`))
952
+ order = literal(`${field}`)
953
+ break
954
+ }
955
+ }
956
+
957
+ if(yAxis.length > 0){
958
+ for(let yAxe of yAxis){
959
+ if(!yAxe.key) continue
960
+
961
+ const groups = await model.findAll({
962
+ attributes: [ yAxe.key ],
963
+ where: {
964
+ [Op.and]: [
965
+ where
966
+ ]
967
+ },
968
+ group: [ yAxe.key ],
969
+ replacements
970
+ })
971
+
972
+ if(groups.length > 50){
973
+ throw new Error('Chart value is too large, max 50 items allowed.')
974
+ }
975
+
976
+ groups.forEach(group => {
977
+ if(group[yAxis[0].key] === null){
978
+ attributes.push([literal(`SUM(CASE WHEN ${field} is null THEN 1 ELSE 0 END)`), `${yAxe.key}:NULL`])
979
+ }
980
+ else if(group[yAxis[0].key] === ''){
981
+ attributes.push([literal(`SUM(CASE WHEN ${field} = '' THEN 1 ELSE 0 END)`), `${yAxe.key}:EMPTY`])
982
+ }
983
+ else{
984
+ attributes.push([literal(`SUM(CASE WHEN ${field} = '${group[yAxe.key]}' THEN 1 ELSE 0 END)`), `${yAxe.key}:${group[yAxe.key]}` ])
985
+ }
986
+ })
987
+ }
988
+ }
989
+ else{
990
+ attributes.push([literal(`COUNT(*)`), 'count'])
991
+ }
992
+
993
+ }
994
+ break
995
+
996
+ case 'bar':
997
+ case 'line':
998
+ for(let xAxe of xAxis){
999
+ if(!xAxe.key) continue
1000
+
1001
+ const field = model.rawAttributes[xAxe.key].field
1002
+
1003
+ switch(xAxe.aggregrate){
1004
+
1005
+ case 'date':
1006
+ attributes.push([literal(`DATE_FORMAT(${model.name}.${field}, "%Y-%m-%d")`), xAxis[0].key])
1007
+ group.push(literal(`DATE_FORMAT(${model.name}.${field}, "%Y-%m-%d")`))
1008
+ order = literal(`DATE_FORMAT(${model.name}.${field}, \"%Y-%m-%d\")`)
1009
+ break
1010
+
1011
+ case 'hour':
1012
+ attributes.push([literal(`DATE_FORMAT(${model.name}.${field}, "%H")`), xAxis[0].key])
1013
+ group.push(literal(`DATE_FORMAT(${model.name}.${field}, "%H")`))
1014
+ order = literal(`DATE_FORMAT(${model.name}.${field}, \"%H\")`)
1015
+ break
1016
+
1017
+ default:
1018
+ attributes.push([literal(`${model.name}.${field}`), xAxe.key])
1019
+ group.push(literal(`${model.name}.${field}`))
1020
+ order = literal(`${model.name}.${field}`)
1021
+ break
1022
+ }
1023
+ }
1024
+
1025
+ if(yAxis.length < 1){
1026
+ attributes.push([literal(`COUNT(*)`), 'count'])
1027
+ }
1028
+ else{
1029
+ for(let yAxe of yAxis){
1030
+ switch(yAxe.aggregrate){
1031
+
1032
+ case 'count':
1033
+ attributes.push([literal(`COUNT(*)`), `${yAxe.key}:count`])
1034
+ break
1035
+
1036
+ default:
1037
+ const field = model.rawAttributes[yAxe.key].field
1038
+
1039
+ const groups = await model.findAll({
1040
+ attributes: [ yAxe.key ],
1041
+ where: {
1042
+ [Op.and]: [
1043
+ where
1044
+ ]
1045
+ },
1046
+ group: [ yAxe.key ],
1047
+ replacements
1048
+ })
1049
+
1050
+ if(groups.length > 50){
1051
+ throw new Error('Chart value is too large, max 50 items allowed.')
1052
+ }
1053
+
1054
+ groups.forEach(group => {
1055
+ if(group[yAxe.key] === null){
1056
+ attributes.push([literal(`SUM(CASE WHEN ${model.name}.${field} is null THEN 1 ELSE 0 END)`), `${yAxe.key}:NULL`])
1057
+ }
1058
+ else if(group[yAxe.key] === ''){
1059
+ attributes.push([literal(`SUM(CASE WHEN ${model.name}.${field} = '' THEN 1 ELSE 0 END)`), `${yAxe.key}:EMPTY`])
1060
+ }
1061
+ else{
1062
+ attributes.push([literal(`SUM(CASE WHEN ${model.name}.${field} = '${group[yAxe.key]}' THEN 1 ELSE 0 END)`), `${yAxe.key}:${group[yAxe.key]}` ])
1063
+ }
1064
+ })
1065
+
1066
+ break
1067
+ }
1068
+ }
1069
+ }
1070
+
1071
+ break
1072
+ }
1073
+
1074
+ return {
1075
+ attributes,
1076
+ group,
1077
+ order,
1078
+ include
1079
+ }
1080
+ }
1081
+
1082
+ const pivotToSequelizeWhere = async (pivot, opt) => {
1083
+ const { model, sorts = [] } = opt
1084
+ const { fn, literal, Op, DataTypes } = opt.Sequelize ?? Sequelize
1085
+
1086
+ const attributes = []
1087
+ const group = []
1088
+ const order = []
1089
+ const sortExists = sorts.length > 0
1090
+
1091
+ if(Array.isArray(pivot.rows)){
1092
+ const groupedSorts = groupBy(sorts, 'key')
1093
+
1094
+ for(let row of pivot.rows){
1095
+ const field = model.rawAttributes[row.key].field
1096
+
1097
+ switch(row.aggregrate){
1098
+
1099
+ case 'date':
1100
+ attributes.push([ fn('DATE_FORMAT', literal(`${model.name}.${field}`), '%Y-%m-%d'), row.key ])
1101
+ group.push([ fn('DATE_FORMAT', literal(`${model.name}.${field}`), '%Y-%m-%d'), row.key ])
1102
+ if(!sortExists) order.push([ fn('DATE_FORMAT', literal(`${model.name}.${field}`), '%Y-%m-%d'), 'asc' ])
1103
+ break
1104
+
1105
+ case 'month':
1106
+ attributes.push([ fn('DATE_FORMAT', literal(`${model.name}.${field}`), '%Y-%m'), row.key ])
1107
+ group.push([ fn('DATE_FORMAT', literal(`${model.name}.${field}`), '%Y-%m'), row.key ])
1108
+ if(!sortExists) order.push([ fn('DATE_FORMAT', literal(`${model.name}.${field}`), '%Y-%m'), 'asc' ])
1109
+ break
1110
+
1111
+ case 'year':
1112
+ attributes.push([ fn('DATE_FORMAT', literal(`${model.name}.${field}`), '%Y'), row.key ])
1113
+ group.push([ fn('DATE_FORMAT', literal(`${model.name}.${field}`), '%Y'), row.key ])
1114
+ if(!sortExists) order.push([ fn('DATE_FORMAT', literal(`${model.name}.${field}`), '%Y'), 'asc' ])
1115
+ break
1116
+
1117
+ case 'hour':
1118
+ attributes.push([ fn('DATE_FORMAT', literal(`${model.name}.${field}`), '%H:00-%H:59'), row.key ])
1119
+ group.push([ fn('DATE_FORMAT', literal(`${model.name}.${field}`), '%H:00-%H:59'), row.key ])
1120
+ if(!sortExists) order.push([ fn('DATE_FORMAT', literal(`${model.name}.${field}`), '%H:00-%H:59'), 'asc' ])
1121
+ break
1122
+
1123
+ default:
1124
+ attributes.push([ literal(`${model.name}.${field}`), row.key ])
1125
+ group.push(`${model.name}.${field}`)
1126
+ if(!sortExists) order.push([ row.key, groupedSorts[row.key] ? groupedSorts[row.key][0].type : 'asc' ])
1127
+ break
1128
+ }
1129
+ }
1130
+ }
1131
+
1132
+ const sortKeyToOrders = {} // Helper to determine group ordering
1133
+ if(Array.isArray(pivot.values)){
1134
+ for(let value of pivot.values){
1135
+ const valueKey = model.rawAttributes[value.key].field
1136
+
1137
+ switch(value.aggregrate){
1138
+
1139
+ case 'sum':
1140
+ attributes.push([ fn('SUM', literal(`${model.name}.${valueKey}`)), `_${value.key}-${value.aggregrate}` ])
1141
+ break
1142
+
1143
+ case 'avg':
1144
+ attributes.push([ fn('AVG', literal(`${model.name}.${valueKey}`)), `_${value.key}-${value.aggregrate}` ])
1145
+ break
1146
+
1147
+ case 'min':
1148
+ attributes.push([ fn('MIN', literal(`${model.name}.${valueKey}`)), `_${value.key}-${value.aggregrate}` ])
1149
+ break
1150
+
1151
+ case 'max':
1152
+ attributes.push([ fn('MAX', literal(`${model.name}.${valueKey}`)), `_${value.key}-${value.aggregrate}` ])
1153
+ break
1154
+
1155
+ case 'countDistinct':
1156
+ attributes.push([ literal(`COUNT(DISTINCT ${model.name}.${valueKey})`), `_${value.key}-${value.aggregrate}` ])
1157
+ sortKeyToOrders[`_${value.key}-${value.aggregrate}`] = literal(`COUNT(DISTINCT ${model.name}.${valueKey})`)
1158
+ break
1159
+
1160
+ case 'first':
1161
+ attributes.push([ literal(`(SELECT ${valueKey} FROM ${model.tableName} WHERE id = MIN(${model.name}.id))`), `_${value.key}-${value.aggregrate}` ])
1162
+ break
1163
+
1164
+ case 'last':
1165
+ attributes.push([ literal(`(SELECT ${valueKey} FROM ${model.tableName} WHERE id = MAX(${model.name}.id))`), `_${value.key}-${value.aggregrate}` ])
1166
+ break
1167
+
1168
+ case 'count':
1169
+ attributes.push([ fn('COUNT', literal(`${model.name}.${valueKey}`)), `_${value.key}-${value.aggregrate}` ])
1170
+ sortKeyToOrders[`_${value.key}-${value.aggregrate}`] = fn('COUNT', literal(`${model.name}.${valueKey}`))
1171
+ break
1172
+
1173
+ default:
1174
+ const { where, replacements } = opt
1175
+ const groups = await model.findAll({
1176
+ attributes: [ value.key ],
1177
+ where: {
1178
+ [Op.and]: [
1179
+ where
1180
+ ]
1181
+ },
1182
+ group: [ value.key ],
1183
+ replacements
1184
+ })
1185
+
1186
+ if(groups.length > 50){
1187
+ throw new Error('Pivot value group is too large, max 50 items allowed.')
1188
+ }
1189
+
1190
+ const field = model.rawAttributes[value.key].field
1191
+
1192
+
1193
+ groups.forEach(group => {
1194
+
1195
+ let groupValue
1196
+ if(group[value.key] === null){
1197
+ attributes.push([ literal(`SUM(CASE WHEN ${model.name}.${field} is null THEN 1 ELSE 0 END)`), `_${value.key}-(null)` ])
1198
+ }
1199
+ else if(group[value.key] === ''){
1200
+ attributes.push([ literal(`SUM(CASE WHEN ${model.name}.${field} = '' THEN 1 ELSE 0 END)`), `_${value.key}-(empty)` ])
1201
+ }
1202
+ else{
1203
+ if(model.rawAttributes[value.key].type instanceof DataTypes.BOOLEAN){
1204
+ groupValue = group[value.key]
1205
+ attributes.push([ literal(`SUM(CASE WHEN ${model.name}.${field} = ${groupValue === true ? 1 : 0} THEN 1 ELSE 0 END)`), `_${value.key}-${groupValue}` ])
1206
+ }
1207
+ else{
1208
+ groupValue = (group[value.key] ?? '').toString().replace(/\s/g, '_')
1209
+ attributes.push([ literal(`SUM(CASE WHEN ${model.name}.${field} = '${group[value.key]}' THEN 1 ELSE 0 END)`), `_${value.key}-${groupValue}` ])
1210
+ }
1211
+ }
1212
+ })
1213
+
1214
+ break
1215
+ }
1216
+ }
1217
+ }
1218
+
1219
+ if(sortExists){
1220
+ for(let sort of sorts){
1221
+ if(sortKeyToOrders[sort.key]){
1222
+ order.push([ sortKeyToOrders[sort.key], sort.type ?? 'asc' ])
1223
+ }
1224
+ else{
1225
+ order.push([ sort.key, sort.type ?? 'asc' ])
1226
+ }
1227
+ }
1228
+ }
1229
+
1230
+ attributes.push([ literal(`1`), 'isPivot' ])
1231
+
1232
+ return {
1233
+ attributes,
1234
+ group,
1235
+ order
1236
+ }
1237
+ }
1238
+
1239
+ const sortsToSequelizeWhere = async (sorts, opt) => {
1240
+
1241
+ let { order, model } = opt ?? {}
1242
+
1243
+ if(Array.isArray(sorts) && sorts.length > 0){
1244
+
1245
+ if(sorts.filter(_ => !_.key).length > 0){
1246
+ throw new Error('Sorts must have a key')
1247
+ }
1248
+
1249
+ order = []
1250
+ for(let sort of sorts){
1251
+ order.push([ sort.key, sort.type ?? 'asc' ])
1252
+ }
1253
+ }
1254
+
1255
+ // Make sure the last sort is always by id
1256
+ if(order.length > 0 && order[order.length - 1][0] !== 'id' &&
1257
+ model && model.rawAttributes && model.rawAttributes.id && model.rawAttributes.id.field){
1258
+ order.push([ 'id', (order[0] ?? [])[1] ?? 'desc' ])
1259
+ }
1260
+
1261
+ return {
1262
+ order
1263
+ }
1264
+ }
1265
+
1266
+ const filtersToSequelizeInclude = async(filters, opt, includes) => {
1267
+ if(!Array.isArray(filters) || filters.length < 1){
1268
+ return includes
1269
+ }
1270
+
1271
+ const { Op } = opt.Sequelize ?? Sequelize
1272
+
1273
+ let whereObj = {}
1274
+ for(let filter of filters) {
1275
+ if (filter.enabled === false) continue
1276
+
1277
+ const {key, value, operator} = filter
1278
+
1279
+ if(key.indexOf('.') < 0) continue
1280
+
1281
+ if(Array.isArray(value) && !operator){
1282
+ const { modifier = 'and' } = filter
1283
+
1284
+ if(![ 'and', 'or' ].includes(modifier)) continue
1285
+
1286
+ const opModifierValue = []
1287
+ for(let v of value){
1288
+ opModifierValue.push(getValue({ ...v, key }, {
1289
+ ...opt,
1290
+ withoutKey: true
1291
+ }))
1292
+ }
1293
+
1294
+ whereObj = {
1295
+ ...whereObj,
1296
+ [key]: {
1297
+ [Op[modifier]]: opModifierValue
1298
+ }
1299
+ }
1300
+ }
1301
+ else{
1302
+ whereObj = {
1303
+ ...whereObj,
1304
+ ...getValue(filter, opt)
1305
+ }
1306
+ }
1307
+ }
1308
+
1309
+ for(let key in whereObj){
1310
+
1311
+ const keyParts = key.split('.')
1312
+ if(keyParts.length < 2) continue
1313
+
1314
+ // Walk through the includes to find the correct include
1315
+ let include
1316
+ for(let i = 0 ; i < keyParts.length - 1 ; i++){
1317
+ const as = keyParts[i]
1318
+ include = !include ? includes.find(_ => _.as === as) : include.includes ?? []
1319
+ }
1320
+
1321
+ // If include is not found, skip
1322
+ if(!include){
1323
+ continue
1324
+ }
1325
+
1326
+ // Generate where object for the correct include
1327
+ const currentKey = keyParts[keyParts.length - 1]
1328
+ if(!include.where){
1329
+ include.where = { [Op.and]: [] }
1330
+ }
1331
+ include.where[Op.and].push({ [currentKey]: whereObj[key] })
1332
+
1333
+ include.required = true
1334
+ }
1335
+
1336
+ return includes
1337
+ }
1338
+
1339
+ const syntaxSearchToSequelizeOpt = async(search, opt) => {
1340
+
1341
+ let where = {}
1342
+ let replacements = []
1343
+
1344
+ const { config } = opt
1345
+
1346
+ let syntaxKey = search[1]
1347
+ let syntaxVal = search[2].trim()
1348
+
1349
+ return {
1350
+ where,
1351
+ replacements
1352
+ }
1353
+ }
1354
+
1355
+ const presetToSequelizeList = async(preset, opt = {}) => {
1356
+ let {
1357
+ conn,
1358
+ model,
1359
+ config,
1360
+ where:initialWhere,
1361
+ replacements: initialReplacements,
1362
+ include,
1363
+ order:initialOrder,
1364
+ logging = false,
1365
+ debug = false
1366
+ } = opt
1367
+
1368
+ if(!model) return {}
1369
+
1370
+ const updatedAtField = model.rawAttributes['updatedAt'] ?? model.rawAttributes['updated_at']
1371
+
1372
+ if(!initialOrder){
1373
+ initialOrder = [
1374
+ ...(updatedAtField ? [[ updatedAtField.fieldName, 'desc' ]] : []),
1375
+ [ 'id', 'desc' ],
1376
+ ]
1377
+ }
1378
+
1379
+ const { itemsPerPage = 20, filters = [], sorts, pivot, afterItem, search, id, uid } = preset;
1380
+ const { Op } = opt.Sequelize ?? Sequelize
1381
+
1382
+ let where = {}
1383
+ const replacements = []
1384
+ const { order } = await sortsToSequelizeWhere(sorts, {
1385
+ order: initialOrder
1386
+ })
1387
+
1388
+ if(Array.isArray(id)){
1389
+ where = {
1390
+ ...where,
1391
+ id: {
1392
+ [Op.in]: id
1393
+ }
1394
+ }
1395
+ }
1396
+
1397
+ if(Array.isArray(uid)){
1398
+ where = {
1399
+ ...where,
1400
+ uid: {
1401
+ [Op.in]: uid
1402
+ }
1403
+ }
1404
+ }
1405
+
1406
+ const { where:filterWhere, replacements:filterReplacements } = await filtersToSequelizeWhere(filters, {
1407
+ config,
1408
+ model,
1409
+ Sequelize: opt.Sequelize,
1410
+ debug
1411
+ })
1412
+
1413
+ let searchWhere = {}, searchReplacements = []
1414
+ if(search){
1415
+ const syntaxSearch = (search ?? '').match(/^(\w+)\:(.*)/)
1416
+ if(syntaxSearch !== null){
1417
+ const syntaxSearchOpt = syntaxSearchToSequelizeOpt(syntaxSearch, { config })
1418
+ searchWhere = syntaxSearchOpt.where
1419
+ searchReplacements = syntaxSearchOpt.replacements
1420
+ }
1421
+ else{
1422
+ const match = (config.columns ?? [])
1423
+ .filter(_ => (_.search && _.key))
1424
+ .map(_ => typeof _.search === 'string' ? _.search : _.key)
1425
+
1426
+ if(match !== null){
1427
+ const searchOpt = await searchToSequelizeWhere(search, {
1428
+ match,
1429
+ Sequelize: opt.Sequelize
1430
+ })
1431
+ searchWhere = searchOpt.where
1432
+ searchReplacements = searchOpt.replacements
1433
+ }
1434
+ }
1435
+ }
1436
+
1437
+ if(include){
1438
+ include = await filtersToSequelizeInclude(filters, config, include)
1439
+ }
1440
+
1441
+ if(pivot && pivot.enabled){
1442
+ const { attributes, group, order } = await pivotToSequelizeWhere(pivot, {
1443
+ model,
1444
+ conn,
1445
+ Sequelize: opt.Sequelize
1446
+ })
1447
+
1448
+ return {
1449
+ attributes,
1450
+ where: {
1451
+ [Op.and]: [
1452
+ initialWhere ?? {},
1453
+ where,
1454
+ filterWhere,
1455
+ searchWhere
1456
+ ]
1457
+ },
1458
+ replacements: [
1459
+ ...(initialReplacements ?? []),
1460
+ ...filterReplacements,
1461
+ ...searchReplacements
1462
+ ],
1463
+ group,
1464
+ order,
1465
+ logging
1466
+ }
1467
+ }
1468
+ else{
1469
+ const { where:afterItemWhere } = await afterItemToSequelizeWhere(order, afterItem, {
1470
+ Sequelize: opt.Sequelize
1471
+ })
1472
+
1473
+ return {
1474
+ where: {
1475
+ [Op.and]: [
1476
+ initialWhere ?? {},
1477
+ where,
1478
+ afterItemWhere,
1479
+ filterWhere,
1480
+ searchWhere,
1481
+ ]
1482
+ },
1483
+ limit: itemsPerPage + 1,
1484
+ order,
1485
+ include,
1486
+ replacements: [
1487
+ ...(initialReplacements ?? []),
1488
+ ...replacements,
1489
+ ...filterReplacements,
1490
+ ...searchReplacements
1491
+ ],
1492
+ itemsPerPage,
1493
+ logging
1494
+ }
1495
+ }
1496
+ }
1497
+
1498
+
1499
+ export {
1500
+ filtersToSequelizeWhere,
1501
+ afterItemToSequelizeWhere,
1502
+ searchToSequelizeWhere,
1503
+ chartToSequelizeWhere,
1504
+ pivotToSequelizeWhere,
1505
+ sortsToSequelizeWhere,
1506
+ filtersToSequelizeInclude,
1507
+
1508
+ presetToSequelizeList
1509
+ }