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