adminizer 4.6.0 → 4.7.0-build.221

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 (335) hide show
  1. package/Readme.md +12 -0
  2. package/assets/RelationDialogStack-CnHFOYyR.css +1 -0
  3. package/assets/RelationDialogStack-DI2bZxQX.js +20 -0
  4. package/assets/add-4213XScp.js +1 -0
  5. package/assets/add-group--F1XxfJ1.js +1 -0
  6. package/assets/add-group-form-DKRXC2n-.js +1 -0
  7. package/assets/add-user-b9HfMq7z.js +1 -0
  8. package/assets/add-user-form-UXAToWQd.js +1 -0
  9. package/assets/app-Clx8f5hd.css +1 -0
  10. package/assets/app-layout-C02ja4OI.js +1 -0
  11. package/assets/app-shell-D8wtkECO.js +1 -0
  12. package/assets/app-sidebar-BR5_zwxj.js +1 -0
  13. package/assets/app-sidebar-header-BZbIBQjq.js +1 -0
  14. package/assets/app.js +59 -7990
  15. package/assets/breadcrumbs-CODaqVkB.js +1 -0
  16. package/assets/catalog-6ZZjWdFs.js +6 -0
  17. package/assets/dashboard-CftAdKiH.js +3 -0
  18. package/assets/date-picker-DSOkNiNo.js +1 -0
  19. package/assets/del-modal-Cd4M7af8.js +1 -0
  20. package/assets/dynamic-controls-BsrJhPDY.js +1 -0
  21. package/assets/geo-json-CzR5JBXL.js +1 -0
  22. package/assets/handsontable-BI98OF5R.js +336 -0
  23. package/assets/history-DLHSU7Ak.js +1 -0
  24. package/assets/home-NcQ7nnU0.js +2 -0
  25. package/assets/icon-C2JNaLxH.js +1 -0
  26. package/assets/init-user-D2HSyXup.js +1 -0
  27. package/assets/input-error-CH2caTtO.js +1 -0
  28. package/assets/jsoneditor-DVNQmA6G.js +3869 -0
  29. package/assets/list-CFU0-TgN.js +4 -0
  30. package/assets/login-DFSLEO17.js +1 -0
  31. package/assets/manifest.json +246 -273
  32. package/assets/material-icon-Bx9pGQiQ.js +1 -0
  33. package/assets/media-manager-WcKe1EvD.js +6 -0
  34. package/assets/module-CT7PC6pc.js +1 -0
  35. package/assets/monaco-Bl4zdWCn.js +11 -0
  36. package/assets/nav-footer-BiSQkPt4.js +1 -0
  37. package/assets/nav-main-rTH0PfDw.js +1 -0
  38. package/assets/nav-user-Bj6cWs0a.js +1 -0
  39. package/assets/notification-Bi-PX87D.js +20 -0
  40. package/assets/pagination-render-AKJvTIRA.js +1 -0
  41. package/assets/register-BTP4uxsK.js +1 -0
  42. package/assets/ru-eW0WKkga.js +1 -0
  43. package/assets/theme-switcher-Dm8EOGzx.js +1 -0
  44. package/assets/toast-editor-DOg_gvSp.js +668 -0
  45. package/assets/user-filters-list-YUfR4XMI.js +1 -0
  46. package/assets/user-info-BgSbT02v.js +1 -0
  47. package/assets/user-menu-content-TQTXHIv6.js +1 -0
  48. package/assets/{vanilla-picker-l5rcX3cq.js → vanilla-picker-B3LgXoKV.js} +1 -7
  49. package/assets/welcome-vLwTu1R6.js +1 -0
  50. package/controllers/add.d.ts +1 -1
  51. package/controllers/add.js +20 -26
  52. package/controllers/addGroup.d.ts +1 -1
  53. package/controllers/addGroup.js +7 -15
  54. package/controllers/addUser.d.ts +1 -1
  55. package/controllers/addUser.js +10 -16
  56. package/controllers/catalog/Catalog.d.ts +1 -1
  57. package/controllers/catalog/Catalog.js +1 -10
  58. package/controllers/catalog/FrontendCatalogAdapter.d.ts +3 -3
  59. package/controllers/catalog/FrontendCatalogAdapter.js +0 -6
  60. package/controllers/catalog/FrontentCatalogAdapter.d.ts +3 -3
  61. package/controllers/catalog/FrontentCatalogAdapter.js +0 -4
  62. package/controllers/ckeditorUpload.js +2 -18
  63. package/controllers/dashboard.d.ts +1 -1
  64. package/controllers/dashboard.js +2 -5
  65. package/controllers/edit.js +24 -37
  66. package/controllers/editGroup.d.ts +1 -1
  67. package/controllers/editGroup.js +10 -21
  68. package/controllers/editUser.d.ts +1 -1
  69. package/controllers/editUser.js +11 -23
  70. package/controllers/exportData.d.ts +6 -0
  71. package/controllers/exportData.js +315 -0
  72. package/controllers/feed.d.ts +6 -0
  73. package/controllers/feed.js +73 -0
  74. package/controllers/feedback/feedbackController.d.ts +1 -1
  75. package/controllers/feedback/feedbackController.js +5 -7
  76. package/controllers/filter-fields/columns.d.ts +11 -0
  77. package/controllers/filter-fields/columns.js +106 -0
  78. package/controllers/filter-fields/filterFields.d.ts +10 -0
  79. package/controllers/filter-fields/filterFields.js +141 -0
  80. package/controllers/filter-fields/getAllUserFilters.d.ts +6 -0
  81. package/controllers/filter-fields/getAllUserFilters.js +115 -0
  82. package/controllers/filter-fields/groups.d.ts +5 -0
  83. package/controllers/filter-fields/groups.js +26 -0
  84. package/controllers/filter-fields/savedFilters.d.ts +25 -0
  85. package/controllers/filter-fields/savedFilters.js +317 -0
  86. package/controllers/getAllWidgets.d.ts +1 -1
  87. package/controllers/getAllWidgets.js +1 -9
  88. package/controllers/history-actions/HistoryController.js +6 -31
  89. package/controllers/initUser.js +6 -5
  90. package/controllers/inlineUpdate.d.ts +7 -0
  91. package/controllers/inlineUpdate.js +148 -0
  92. package/controllers/list.d.ts +1 -1
  93. package/controllers/list.js +383 -70
  94. package/controllers/listUserFilters.d.ts +5 -0
  95. package/controllers/listUserFilters.js +26 -0
  96. package/controllers/login.js +2 -2
  97. package/controllers/media-manager/mediaManagerApi.js +0 -9
  98. package/controllers/navigation/edit.js +3 -9
  99. package/controllers/notifications/NotificationController.d.ts +1 -1
  100. package/controllers/notifications/NotificationController.js +12 -36
  101. package/controllers/register.js +10 -9
  102. package/controllers/remove.js +10 -19
  103. package/controllers/timezones.d.ts +1 -1
  104. package/controllers/timezones.js +0 -6
  105. package/controllers/userApiKey.d.ts +7 -0
  106. package/controllers/userApiKey.js +36 -0
  107. package/controllers/view.js +14 -26
  108. package/controllers/welcome.d.ts +1 -1
  109. package/controllers/welcome.js +0 -4
  110. package/controllers/widgets/Action.d.ts +1 -1
  111. package/controllers/widgets/Action.js +1 -9
  112. package/controllers/widgets/Custom.d.ts +1 -1
  113. package/controllers/widgets/Custom.js +1 -9
  114. package/controllers/widgets/Info.d.ts +1 -1
  115. package/controllers/widgets/Info.js +1 -9
  116. package/controllers/widgets/filterInfo.d.ts +1 -0
  117. package/controllers/widgets/filterInfo.js +44 -0
  118. package/controllers/widgets/switch.d.ts +1 -1
  119. package/controllers/widgets/switch.js +1 -9
  120. package/controllers/widgetsDB.d.ts +1 -1
  121. package/controllers/widgetsDB.js +2 -8
  122. package/helpers/accessRightsHelper.d.ts +1 -1
  123. package/helpers/accessRightsHelper.js +5 -4
  124. package/helpers/apiKeyHelper.d.ts +5 -0
  125. package/helpers/apiKeyHelper.js +8 -0
  126. package/helpers/configHelper.js +12 -15
  127. package/helpers/controllerHelper.d.ts +14 -21
  128. package/helpers/controllerHelper.js +50 -83
  129. package/helpers/fieldsHelper.js +3 -3
  130. package/helpers/filterDatetimeHelper.d.ts +5 -0
  131. package/helpers/filterDatetimeHelper.js +84 -0
  132. package/helpers/inertiaActionsHelper.d.ts +2 -2
  133. package/helpers/inertiaActionsHelper.js +5 -4
  134. package/helpers/inertiaAddHelper.d.ts +3 -414
  135. package/helpers/inertiaAddHelper.js +39 -15
  136. package/helpers/inertiaGroupHelper.d.ts +2 -2
  137. package/helpers/inertiaGroupHelper.js +3 -3
  138. package/helpers/inertiaListHelper.d.ts +7 -3
  139. package/helpers/inertiaListHelper.js +26 -12
  140. package/helpers/inertiaMenuHelper.d.ts +1 -0
  141. package/helpers/inertiaMenuHelper.js +15 -3
  142. package/helpers/inertiaUserHelper.d.ts +3 -2
  143. package/helpers/inertiaUserHelper.js +7 -3
  144. package/helpers/menuHelper.d.ts +5 -2
  145. package/helpers/menuHelper.js +64 -12
  146. package/helpers/viteHelper.js +1 -2
  147. package/index.d.ts +6 -2
  148. package/index.js +6 -2
  149. package/interfaces/adminpanelConfig.d.ts +126 -39
  150. package/interfaces/internalModelAccess.d.ts +37 -0
  151. package/interfaces/internalModelAccess.js +1 -0
  152. package/interfaces/listQueryBuilder.d.ts +22 -0
  153. package/interfaces/listQueryBuilder.js +1 -0
  154. package/interfaces/queryCriteria.d.ts +54 -0
  155. package/interfaces/queryCriteria.js +1 -0
  156. package/interfaces/types.d.ts +1 -13
  157. package/lib/Adminizer.d.ts +4 -2
  158. package/lib/Adminizer.js +29 -10
  159. package/lib/DataAccessor.d.ts +8 -5
  160. package/lib/DataAccessor.js +89 -42
  161. package/lib/I18n.d.ts +5 -1
  162. package/lib/I18n.js +45 -19
  163. package/lib/MiddlewareManager.d.ts +8 -0
  164. package/lib/MiddlewareManager.js +53 -0
  165. package/lib/ai-assistant/OpenAiModelService.d.ts +3 -3
  166. package/lib/ai-assistant/OpenAiModelService.js +23 -24
  167. package/lib/catalog/Navigation.d.ts +1 -0
  168. package/lib/catalog/Navigation.js +12 -11
  169. package/lib/controls/jsoneditor/JsonEditor.d.ts +1 -2
  170. package/lib/controls/markdown/ToastUiEditor.d.ts +1 -2
  171. package/lib/feedback/FeedbackHandler.js +2 -2
  172. package/lib/filters/ConditionValidator.d.ts +107 -0
  173. package/lib/filters/ConditionValidator.js +483 -0
  174. package/lib/filters/CustomFilterHandler.d.ts +122 -0
  175. package/lib/filters/CustomFilterHandler.js +131 -0
  176. package/lib/filters/FilterCustomFieldHandler.d.ts +2 -0
  177. package/lib/filters/FilterCustomFieldHandler.js +2 -0
  178. package/lib/filters/FilterService.d.ts +82 -0
  179. package/lib/filters/FilterService.js +323 -0
  180. package/lib/filters/index.d.ts +6 -0
  181. package/lib/filters/index.js +4 -0
  182. package/lib/history-actions/AbstractHistoryAdapter.d.ts +5 -4
  183. package/lib/history-actions/AbstractHistoryAdapter.js +45 -30
  184. package/lib/history-actions/DefaultHistoryAdapter.d.ts +2 -0
  185. package/lib/history-actions/DefaultHistoryAdapter.js +12 -6
  186. package/lib/history-actions/HistoryHandler.js +1 -1
  187. package/lib/list-query-builder/ListQueryBuilder.d.ts +117 -0
  188. package/lib/list-query-builder/ListQueryBuilder.js +707 -0
  189. package/lib/media-manager/AbstractMediaManager.d.ts +9 -8
  190. package/lib/media-manager/DefaultMediaManager.d.ts +2 -0
  191. package/lib/media-manager/DefaultMediaManager.js +27 -15
  192. package/lib/media-manager/Items.d.ts +4 -1
  193. package/lib/media-manager/Items.js +62 -59
  194. package/lib/media-manager/MediaManagerHandler.js +1 -1
  195. package/lib/media-manager/helpers/MediaManagerHelper.d.ts +5 -1
  196. package/lib/media-manager/helpers/MediaManagerHelper.js +38 -4
  197. package/lib/model/AbstractModel.d.ts +33 -25
  198. package/lib/model/AbstractModel.js +54 -2
  199. package/lib/model/InternalModelAccessFactory.d.ts +18 -0
  200. package/lib/model/InternalModelAccessFactory.js +82 -0
  201. package/lib/model/ModelHandler.d.ts +6 -0
  202. package/lib/model/ModelHandler.js +14 -0
  203. package/lib/model/adapter/sequelize.d.ts +32 -10
  204. package/lib/model/adapter/sequelize.js +409 -62
  205. package/lib/model/adapter/typeorm.d.ts +62 -0
  206. package/lib/model/adapter/typeorm.js +777 -0
  207. package/lib/model/internalModelAccessToken.d.ts +1 -0
  208. package/lib/model/internalModelAccessToken.js +1 -0
  209. package/lib/model/internalModelRepository.d.ts +3 -0
  210. package/lib/model/internalModelRepository.js +4 -0
  211. package/lib/notifications/AbstractNotificationService.d.ts +3 -0
  212. package/lib/notifications/AbstractNotificationService.js +42 -36
  213. package/lib/notifications/GeneralNotificationService.js +37 -40
  214. package/lib/notifications/SystemNotificationService.js +33 -36
  215. package/lib/requestProcessor.js +0 -3
  216. package/lib/ui-i18n/getUiTranslations.d.ts +1 -0
  217. package/lib/ui-i18n/getUiTranslations.js +3 -0
  218. package/lib/ui-i18n/uiTranslationKeys.d.ts +6 -0
  219. package/lib/ui-i18n/uiTranslationKeys.js +268 -0
  220. package/lib/widgets/widgetHandler.d.ts +7 -2
  221. package/lib/widgets/widgetHandler.js +98 -18
  222. package/models/FilterAP.d.ts +116 -0
  223. package/models/FilterAP.js +68 -0
  224. package/models/FilterColumnAP.d.ts +32 -0
  225. package/models/FilterColumnAP.js +21 -0
  226. package/models/GroupAP.d.ts +1 -1
  227. package/models/HistoryActionsAP.d.ts +3 -3
  228. package/models/MediaManagerMetaAP.d.ts +1 -1
  229. package/models/NavigationAP.d.ts +1 -1
  230. package/models/UserAP.d.ts +6 -1
  231. package/models/UserAP.js +4 -0
  232. package/package.json +39 -32
  233. package/policies/authPolicies.d.ts +12 -0
  234. package/policies/authPolicies.js +104 -0
  235. package/policies/permissionResolvers.d.ts +12 -0
  236. package/policies/permissionResolvers.js +52 -0
  237. package/services/FeedService.d.ts +70 -0
  238. package/services/FeedService.js +334 -0
  239. package/system/Router.d.ts +1 -0
  240. package/system/Router.js +136 -59
  241. package/system/bindAccessRights.js +11 -30
  242. package/system/bindAuthorization.js +18 -18
  243. package/system/bindCors.js +16 -1
  244. package/system/bindCustomFilterHandlers.d.ts +2 -0
  245. package/system/bindCustomFilterHandlers.js +4 -0
  246. package/system/bindInertia.js +18 -61
  247. package/system/bindReqFunctions.js +10 -2
  248. package/system/bindTranslations.js +7 -3
  249. package/system/buildInternalModelAccess.d.ts +4 -0
  250. package/system/buildInternalModelAccess.js +59 -0
  251. package/system/defaults.js +18 -14
  252. package/system/routePolicies.d.ts +2 -0
  253. package/system/routePolicies.js +29 -0
  254. package/translations/en.json +151 -39
  255. package/translations/ru.json +211 -64
  256. package/ui/components/handsontable.js +13 -27
  257. package/ui/components/ui/dialog-stack.js +1 -3
  258. package/ui/components/ui/popover.js +1 -1
  259. package/ui/components/ui/sidebar.js +1 -1
  260. package/assets/RelationDialogStack-D9UVEF9s.js +0 -407
  261. package/assets/RelationDialogStack-DV8OITPR.css +0 -4
  262. package/assets/add-BlvzWQKF.js +0 -1
  263. package/assets/add-group-1Uoq9NWe.js +0 -1
  264. package/assets/add-group-form-B01gwDGu.js +0 -1
  265. package/assets/add-user-BQCAsaRy.js +0 -1
  266. package/assets/add-user-form-Cgwc9JkI.js +0 -1
  267. package/assets/app-DaOMy1nl.css +0 -72
  268. package/assets/app-layout-CAm60qoE.js +0 -1
  269. package/assets/app-shell-QWCtn9ye.js +0 -1
  270. package/assets/app-sidebar-CfaYbnSy.js +0 -1
  271. package/assets/app-sidebar-header-BsPFDhZ8.js +0 -1
  272. package/assets/breadcrumbs-CZZirDj8.js +0 -1
  273. package/assets/catalog-k5myk7fk.js +0 -6
  274. package/assets/dashboard-BkL9ybBw.js +0 -3
  275. package/assets/date-picker-B5AZ_7Q9.js +0 -1
  276. package/assets/del-modal-BoyXZloG.js +0 -1
  277. package/assets/dynamic-controls-k61T9W2P.js +0 -1
  278. package/assets/form-1WGY0YgG.js +0 -1
  279. package/assets/form-DJapG3qF.js +0 -1
  280. package/assets/geo-json-DsMUHo0l.js +0 -4
  281. package/assets/handsontable-PxYdjN8c.js +0 -298
  282. package/assets/history-CluyR-yJ.js +0 -1
  283. package/assets/home-Dug9zBzM.js +0 -2
  284. package/assets/icon-DfDLMG5f.js +0 -1
  285. package/assets/init-user-CF80MEnT.js +0 -1
  286. package/assets/input-error-DuuqFgZa.js +0 -1
  287. package/assets/jsoneditor-De1FvcYu.js +0 -3882
  288. package/assets/list-CuexshNt.js +0 -1
  289. package/assets/list-table-BsEWjTKY.js +0 -22
  290. package/assets/login-eDemYzN-.js +0 -1
  291. package/assets/material-icon-DMT8KdmE.js +0 -1
  292. package/assets/media-manager-DfYa_mT_.js +0 -14
  293. package/assets/module-Cd4Tm6uq.js +0 -1
  294. package/assets/monaco-BhBYtg3d.js +0 -11
  295. package/assets/nav-footer-D3olYxtz.js +0 -1
  296. package/assets/nav-main-BctqyzeV.js +0 -1
  297. package/assets/nav-user-Bn1Lt-BZ.js +0 -1
  298. package/assets/notification-D64qvni-.js +0 -27
  299. package/assets/pagination-render-DxD78ahJ.js +0 -1
  300. package/assets/register-349PwYbe.js +0 -1
  301. package/assets/theme-switcher-DfExsHW2.js +0 -1
  302. package/assets/toast-editor-ESK5iWPx.js +0 -845
  303. package/assets/user-info-DtBK5Kh8.js +0 -1
  304. package/assets/user-menu-content-0XYOCMaA.js +0 -1
  305. package/assets/welcome-PDp-gUei.js +0 -1
  306. package/controllers/form.d.ts +0 -1
  307. package/controllers/form.js +0 -86
  308. package/helpers/formHelper.d.ts +0 -6
  309. package/helpers/formHelper.js +0 -38
  310. package/helpers/inertiaFromHelper.d.ts +0 -11
  311. package/helpers/inertiaFromHelper.js +0 -98
  312. package/helpers/runtimeMode.d.ts +0 -1
  313. package/helpers/runtimeMode.js +0 -3
  314. package/lib/PolicyManager.d.ts +0 -8
  315. package/lib/PolicyManager.js +0 -53
  316. package/lib/datatable/NodeTable.d.ts +0 -49
  317. package/lib/datatable/NodeTable.js +0 -207
  318. package/lib/model/adapter/waterline.d.ts +0 -32
  319. package/lib/model/adapter/waterline.js +0 -180
  320. package/migrations/index.d.ts +0 -3
  321. package/migrations/index.js +0 -4
  322. package/migrations/knex/20250106000001_adminizer_init.d.ts +0 -8
  323. package/migrations/knex/20250106000001_adminizer_init.js +0 -164
  324. package/migrations/umzug/0001.d.ts +0 -7
  325. package/migrations/umzug/0001.js +0 -121
  326. package/migrations/umzugExports.d.ts +0 -2
  327. package/migrations/umzugExports.js +0 -9
  328. package/system/bindForms.d.ts +0 -2
  329. package/system/bindForms.js +0 -20
  330. /package/{policies → middlewares}/addMessages.d.ts +0 -0
  331. /package/{policies → middlewares}/addMessages.js +0 -0
  332. /package/{policies → middlewares}/checkAuth.d.ts +0 -0
  333. /package/{policies → middlewares}/checkAuth.js +0 -0
  334. /package/{policies → middlewares}/setLocale.d.ts +0 -0
  335. /package/{policies → middlewares}/setLocale.js +0 -0
@@ -0,0 +1,131 @@
1
+ /**
2
+ * Base custom filter handler contract.
3
+ * Stores model and field separately and builds a full handler ID as "Model.field".
4
+ */
5
+ export class AbstractCustomFilter {
6
+ modelName;
7
+ fieldName;
8
+ constructor(modelName, fieldName) {
9
+ if (!modelName?.trim()) {
10
+ throw new Error("Custom filter handler requires non-empty model name.");
11
+ }
12
+ if (!fieldName?.trim()) {
13
+ throw new Error("Custom filter handler requires non-empty field name.");
14
+ }
15
+ this.modelName = modelName;
16
+ this.fieldName = fieldName;
17
+ }
18
+ get id() {
19
+ return `${this.modelName}.${this.fieldName}`;
20
+ }
21
+ /**
22
+ * Handler description
23
+ */
24
+ description;
25
+ /**
26
+ * Optional input config for UI (up to 3 inputs).
27
+ * Example:
28
+ * {
29
+ * from: { placeholder: "From", type: "number" },
30
+ * to: { placeholder: "To", type: "number" }
31
+ * }
32
+ */
33
+ inputConfig;
34
+ getInputDefinitions() {
35
+ if (!this.inputConfig) {
36
+ return [];
37
+ }
38
+ return Object.entries(this.inputConfig).map(([id, config]) => ({
39
+ id,
40
+ placeholder: config?.placeholder || id,
41
+ type: config?.type === "number" ? "number" : "text",
42
+ }));
43
+ }
44
+ }
45
+ /**
46
+ * CustomFilterHandler - registry for custom field handlers
47
+ */
48
+ export class CustomFilterHandler {
49
+ handlers = new Map();
50
+ validateInputConfig(handler) {
51
+ const definitions = handler.getInputDefinitions();
52
+ if (definitions.length > 3) {
53
+ throw new Error(`Custom filter handler '${handler.id}' supports at most 3 inputs.`);
54
+ }
55
+ for (const definition of definitions) {
56
+ if (!definition.id.trim()) {
57
+ throw new Error(`Custom filter handler '${handler.id}' has an input with empty id.`);
58
+ }
59
+ }
60
+ }
61
+ /**
62
+ * Register a custom handler
63
+ * @param handler - Handler instance
64
+ * @param options - Registration options
65
+ */
66
+ add(handler, options) {
67
+ this.validateInputConfig(handler);
68
+ if (this.handlers.has(handler.id) && !options?.force) {
69
+ throw new Error(`Handler '${handler.id}' is already registered. Use force: true to overwrite.`);
70
+ }
71
+ this.handlers.set(handler.id, handler);
72
+ }
73
+ /**
74
+ * Alias for add()
75
+ */
76
+ register(handler, options) {
77
+ this.add(handler, options);
78
+ }
79
+ /**
80
+ * Get handler by ID
81
+ */
82
+ get(id) {
83
+ return this.handlers.get(id);
84
+ }
85
+ /**
86
+ * Check if handler exists
87
+ */
88
+ has(id) {
89
+ return this.handlers.has(id);
90
+ }
91
+ /**
92
+ * Get all handlers
93
+ */
94
+ getAll() {
95
+ return new Map(this.handlers);
96
+ }
97
+ /**
98
+ * Get handlers for a specific model
99
+ * @param modelName - Model name (e.g., "Order")
100
+ * @returns Map of field handlers for the model
101
+ */
102
+ getForModel(modelName) {
103
+ const modelHandlers = new Map();
104
+ const prefix = `${modelName}.`;
105
+ for (const [id, handler] of this.handlers.entries()) {
106
+ if (id.startsWith(prefix)) {
107
+ modelHandlers.set(id, handler);
108
+ }
109
+ }
110
+ return modelHandlers;
111
+ }
112
+ /**
113
+ * Unregister a handler
114
+ */
115
+ unregister(id) {
116
+ return this.handlers.delete(id);
117
+ }
118
+ /**
119
+ * Clear all handlers (useful for testing)
120
+ */
121
+ clear() {
122
+ this.handlers.clear();
123
+ }
124
+ /**
125
+ * Get count of registered handlers
126
+ */
127
+ count() {
128
+ return this.handlers.size;
129
+ }
130
+ }
131
+ export default CustomFilterHandler;
@@ -0,0 +1,2 @@
1
+ export * from './CustomFilterHandler';
2
+ export { default } from './CustomFilterHandler';
@@ -0,0 +1,2 @@
1
+ export * from './CustomFilterHandler.js';
2
+ export { default } from './CustomFilterHandler.js';
@@ -0,0 +1,82 @@
1
+ import { Adminizer } from '../Adminizer';
2
+ import { FilterAP } from '../../models/FilterAP';
3
+ import { FilterColumnAP } from '../../models/FilterColumnAP';
4
+ import { UserAP } from '../../models/UserAP';
5
+ /**
6
+ * FilterService - manages filter operations
7
+ *
8
+ * Key responsibilities:
9
+ * - Check if filters are enabled for a model
10
+ * - Apply saved filters to queries
11
+ * - Integrate with ListQueryBuilder
12
+ * - Handle filter access control
13
+ */
14
+ export declare class FilterService {
15
+ private adminizer;
16
+ constructor(adminizer: Adminizer);
17
+ /**
18
+ * Get ModelResource object by model name
19
+ * Similar to ControllerHelper.findModelResource but without req dependency
20
+ */
21
+ private getModelResourceByModelName;
22
+ /**
23
+ * Get filter by ID with access control
24
+ * Returns null when filter is not found or user has no access.
25
+ */
26
+ getFilterById(filterId: string, user: UserAP): Promise<FilterAP | null>;
27
+ /**
28
+ * Get filters for a model accessible by user
29
+ */
30
+ getFiltersForModel(modelName: string, user: UserAP, options?: {
31
+ includePublic?: boolean;
32
+ includeSystem?: boolean;
33
+ }): Promise<FilterAP[]>;
34
+ /**
35
+ * Check if user can view a filter
36
+ */
37
+ canViewFilter(filter: FilterAP, user: UserAP): boolean;
38
+ /**
39
+ * Check if user can edit a filter
40
+ */
41
+ canEditFilter(filter: FilterAP, user: UserAP): boolean;
42
+ /**
43
+ * Check if user can delete a filter
44
+ */
45
+ canDeleteFilter(filter: FilterAP, user: UserAP): boolean;
46
+ /**
47
+ * Count records matching a filter's conditions
48
+ *
49
+ * @param filter - Filter to count results for
50
+ * @param user - User requesting the count (for access control)
51
+ * @returns Number of matching records, or -1 on error
52
+ */
53
+ countFilterResults(filter: FilterAP, user: UserAP): Promise<number>;
54
+ /**
55
+ * Create a new filter
56
+ */
57
+ createFilter(data: Partial<FilterAP>, user: UserAP): Promise<FilterAP>;
58
+ /**
59
+ * Update a filter
60
+ */
61
+ updateFilter(filterId: string, data: Partial<FilterAP>, user: UserAP): Promise<FilterAP>;
62
+ /**
63
+ * Delete a filter
64
+ */
65
+ deleteFilter(filterId: string, user: UserAP): Promise<void>;
66
+ /**
67
+ * Get columns for a filter
68
+ */
69
+ getFilterColumns(filterId: string): Promise<FilterColumnAP[]>;
70
+ /**
71
+ * Update columns for a filter (replace all)
72
+ */
73
+ updateFilterColumns(filterId: string, columns: Partial<FilterColumnAP>[], user: UserAP): Promise<FilterColumnAP[]>;
74
+ /**
75
+ * Get filter with columns loaded
76
+ */
77
+ getFilterWithColumns(filterId: string, user: UserAP): Promise<{
78
+ filter: FilterAP | null;
79
+ columns: FilterColumnAP[];
80
+ }>;
81
+ }
82
+ export default FilterService;
@@ -0,0 +1,323 @@
1
+ import { Adminizer } from '../Adminizer.js';
2
+ import { DataAccessor } from '../DataAccessor.js';
3
+ import { ListQueryBuilder } from '../list-query-builder/ListQueryBuilder.js';
4
+ import { convertDatetimeConditions } from '../../helpers/filterDatetimeHelper.js';
5
+ /**
6
+ * FilterService - manages filter operations
7
+ *
8
+ * Key responsibilities:
9
+ * - Check if filters are enabled for a model
10
+ * - Apply saved filters to queries
11
+ * - Integrate with ListQueryBuilder
12
+ * - Handle filter access control
13
+ */
14
+ export class FilterService {
15
+ adminizer;
16
+ constructor(adminizer) {
17
+ this.adminizer = adminizer;
18
+ }
19
+ /**
20
+ * Get ModelResource object by model name
21
+ * Similar to ControllerHelper.findModelResource but without req dependency
22
+ */
23
+ getModelResourceByModelName(modelName) {
24
+ const models = this.adminizer.config.models;
25
+ if (!models) {
26
+ return null;
27
+ }
28
+ // Find config (case-insensitive)
29
+ const foundKey = Object.keys(models).find(key => key.toLowerCase() === modelName.toLowerCase());
30
+ if (!foundKey) {
31
+ return null;
32
+ }
33
+ const config = models[foundKey];
34
+ if (!config || typeof config === 'boolean') {
35
+ return null;
36
+ }
37
+ // Get AbstractModel
38
+ const model = this.adminizer.modelHandler.model.get(config.model);
39
+ if (!model) {
40
+ return null;
41
+ }
42
+ return {
43
+ name: foundKey,
44
+ config,
45
+ model,
46
+ uri: `${this.adminizer.config.routePrefix}/model/${foundKey}`
47
+ };
48
+ }
49
+ /**
50
+ * Get filter by ID with access control
51
+ * Returns null when filter is not found or user has no access.
52
+ */
53
+ async getFilterById(filterId, user) {
54
+ const filterModel = this.adminizer.modelHandler.internal('filters').get('FilterAP');
55
+ const filter = await filterModel.findOne({ where: { id: filterId } });
56
+ if (!filter) {
57
+ return null;
58
+ }
59
+ const filterAP = filter;
60
+ if (!this.canViewFilter(filterAP, user)) {
61
+ return null;
62
+ }
63
+ return filterAP;
64
+ }
65
+ /**
66
+ * Get filters for a model accessible by user
67
+ */
68
+ async getFiltersForModel(modelName, user, options) {
69
+ const filterModel = this.adminizer.modelHandler.internal('filters').get('FilterAP');
70
+ const filters = [];
71
+ // Build base criteria
72
+ const baseCriteria = { modelName };
73
+ // 1. Get user's own filters
74
+ const ownFilters = await filterModel.find({
75
+ where: {
76
+ ...baseCriteria,
77
+ ownerId: user.id
78
+ }
79
+ });
80
+ filters.push(...ownFilters);
81
+ // 2. Get public filters (if requested)
82
+ if (options?.includePublic !== false) {
83
+ const publicFilters = await filterModel.find({
84
+ where: {
85
+ ...baseCriteria,
86
+ visibility: 'public'
87
+ }
88
+ });
89
+ // Deduplicate
90
+ for (const filter of publicFilters) {
91
+ if (!filters.find(f => f.id === filter.id)) {
92
+ filters.push(filter);
93
+ }
94
+ }
95
+ }
96
+ // 3. Get group filters if user has groups
97
+ if (user.groups && user.groups.length > 0) {
98
+ const groupIds = user.groups.map(g => g.id);
99
+ const groupFilters = await filterModel.find({
100
+ where: {
101
+ ...baseCriteria,
102
+ visibility: 'groups'
103
+ }
104
+ });
105
+ // Check if user's groups intersect with filter's groups
106
+ for (const filter of groupFilters) {
107
+ if (filter.groupIds && filter.groupIds.some(gid => groupIds.includes(gid))) {
108
+ if (!filters.find(f => f.id === filter.id)) {
109
+ filters.push(filter);
110
+ }
111
+ }
112
+ }
113
+ }
114
+ // 4. Admin sees all filters
115
+ if (user.isAdministrator) {
116
+ const allFilters = await filterModel.find({ where: baseCriteria });
117
+ for (const filter of allFilters) {
118
+ if (!filters.find(f => f.id === filter.id)) {
119
+ filters.push(filter);
120
+ }
121
+ }
122
+ }
123
+ if (!options?.includeSystem) {
124
+ return filters.filter(f => f.visibility !== 'system');
125
+ }
126
+ return filters;
127
+ }
128
+ /**
129
+ * Check if user can view a filter
130
+ */
131
+ canViewFilter(filter, user) {
132
+ // Admin can view all
133
+ if (user.isAdministrator) {
134
+ return true;
135
+ }
136
+ // Owner can view
137
+ if (filter.ownerId === user.id) {
138
+ return true;
139
+ }
140
+ // Public filters
141
+ if (filter.visibility === 'public') {
142
+ return true;
143
+ }
144
+ // System filters (accessible via API)
145
+ if (filter.visibility === 'system') {
146
+ return true;
147
+ }
148
+ // Group filters
149
+ if (filter.visibility === 'groups' && filter.groupIds && user.groups) {
150
+ const userGroupIds = user.groups.map(g => g.id);
151
+ return filter.groupIds.some(gid => userGroupIds.includes(gid));
152
+ }
153
+ return false;
154
+ }
155
+ /**
156
+ * Check if user can edit a filter
157
+ */
158
+ canEditFilter(filter, user) {
159
+ // Admin can edit all
160
+ if (user.isAdministrator) {
161
+ return true;
162
+ }
163
+ // Only owner can edit
164
+ return filter.ownerId === user.id;
165
+ }
166
+ /**
167
+ * Check if user can delete a filter
168
+ */
169
+ canDeleteFilter(filter, user) {
170
+ return this.canEditFilter(filter, user);
171
+ }
172
+ /**
173
+ * Count records matching a filter's conditions
174
+ *
175
+ * @param filter - Filter to count results for
176
+ * @param user - User requesting the count (for access control)
177
+ * @returns Number of matching records, or -1 on error
178
+ */
179
+ async countFilterResults(filter, user) {
180
+ try {
181
+ const modelResource = this.getModelResourceByModelName(filter.modelName);
182
+ if (!modelResource || !modelResource.model) {
183
+ Adminizer.log.warn(`FilterService.countFilterResults: Model '${filter.modelName}' not found`);
184
+ return -1;
185
+ }
186
+ const dataAccessor = new DataAccessor(this.adminizer, user, modelResource, 'list');
187
+ const fields = dataAccessor.getFieldsConfig();
188
+ // Convert datetime conditions to date ranges
189
+ const convertedConditions = convertDatetimeConditions(filter.conditions || []);
190
+ const queryParams = {
191
+ page: 1,
192
+ limit: 1, // We only need count
193
+ filters: convertedConditions
194
+ };
195
+ const listQueryBuilder = new ListQueryBuilder(modelResource.model, fields, dataAccessor, this.adminizer.customFilterHandler);
196
+ const result = await listQueryBuilder.execute(queryParams);
197
+ return result.filtered;
198
+ }
199
+ catch (error) {
200
+ Adminizer.log.error(`FilterService.countFilterResults error for filter '${filter.id}':`, error);
201
+ return -1;
202
+ }
203
+ }
204
+ /**
205
+ * Create a new filter
206
+ */
207
+ async createFilter(data, user) {
208
+ const filterModel = this.adminizer.modelHandler.internal('filters').get('FilterAP');
209
+ // Generate UUID if not provided
210
+ if (!data.id) {
211
+ data.id = crypto.randomUUID();
212
+ }
213
+ // Set owner
214
+ data.ownerId = user.id;
215
+ // Set defaults
216
+ data.visibility = data.visibility || 'private';
217
+ data.version = data.version || 1;
218
+ data.apiEnabled = data.apiEnabled || false;
219
+ data.conditions = data.conditions || [];
220
+ return await filterModel.create(data);
221
+ }
222
+ /**
223
+ * Update a filter
224
+ */
225
+ async updateFilter(filterId, data, user) {
226
+ const filter = await this.getFilterById(filterId, user);
227
+ if (!filter) {
228
+ throw new Error(`Filter '${filterId}' not found`);
229
+ }
230
+ if (!this.canEditFilter(filter, user)) {
231
+ throw new Error('Access denied');
232
+ }
233
+ const filterModel = this.adminizer.modelHandler.internal('filters').get('FilterAP');
234
+ // Don't allow changing owner
235
+ delete data.ownerId;
236
+ delete data.id;
237
+ await filterModel.updateOne({ where: { id: filterId } }, data);
238
+ const updated = await this.getFilterById(filterId, user);
239
+ if (!updated) {
240
+ throw new Error(`Filter '${filterId}' not found after update`);
241
+ }
242
+ return updated;
243
+ }
244
+ /**
245
+ * Delete a filter
246
+ */
247
+ async deleteFilter(filterId, user) {
248
+ const filter = await this.getFilterById(filterId, user);
249
+ if (!filter) {
250
+ throw new Error(`Filter '${filterId}' not found`);
251
+ }
252
+ if (!this.canDeleteFilter(filter, user)) {
253
+ throw new Error('Access denied');
254
+ }
255
+ // Delete columns first - find by filter association and delete by ID
256
+ const columnModel = this.adminizer.modelHandler.internal('filters').get('FilterColumnAP');
257
+ if (columnModel) {
258
+ // Find columns for this filter
259
+ const columns = await columnModel.find({ where: { filter: filterId } });
260
+ // Delete each column by ID to avoid cascade issues
261
+ for (const column of columns) {
262
+ await columnModel.destroyOne({ where: { id: column.id } });
263
+ }
264
+ }
265
+ // Delete filter by ID only
266
+ const filterModel = this.adminizer.modelHandler.internal('filters').get('FilterAP');
267
+ if (filterModel) {
268
+ // Use destroyOne with explicit ID to avoid cascade
269
+ await filterModel.destroyOne({ where: { id: filterId } });
270
+ }
271
+ }
272
+ /**
273
+ * Get columns for a filter
274
+ */
275
+ async getFilterColumns(filterId) {
276
+ const columnModel = this.adminizer.modelHandler.internal('filters').get('FilterColumnAP');
277
+ const columns = await columnModel.find({ where: { filter: filterId } });
278
+ // Sort by order
279
+ return columns.sort((a, b) => (a.order || 0) - (b.order || 0));
280
+ }
281
+ /**
282
+ * Update columns for a filter (replace all)
283
+ */
284
+ async updateFilterColumns(filterId, columns, user) {
285
+ const filter = await this.getFilterById(filterId, user);
286
+ if (!filter) {
287
+ throw new Error(`Filter '${filterId}' not found`);
288
+ }
289
+ if (!this.canEditFilter(filter, user)) {
290
+ throw new Error('Access denied');
291
+ }
292
+ const columnModel = this.adminizer.modelHandler.internal('filters').get('FilterColumnAP');
293
+ // Delete existing columns
294
+ await columnModel.destroy({ where: { filter: filterId } });
295
+ // Create new columns
296
+ if (columns.length > 0) {
297
+ for (let i = 0; i < columns.length; i++) {
298
+ const col = columns[i];
299
+ await columnModel.create({
300
+ filter: filterId,
301
+ fieldName: col.fieldName,
302
+ order: col.order ?? i
303
+ });
304
+ }
305
+ }
306
+ return this.getFilterColumns(filterId);
307
+ }
308
+ /**
309
+ * Get filter with columns loaded
310
+ */
311
+ async getFilterWithColumns(filterId, user) {
312
+ const filter = await this.getFilterById(filterId, user);
313
+ if (!filter) {
314
+ return { filter: null, columns: [] };
315
+ }
316
+ if (!this.canViewFilter(filter, user)) {
317
+ return { filter: null, columns: [] };
318
+ }
319
+ const columns = await this.getFilterColumns(filterId);
320
+ return { filter, columns };
321
+ }
322
+ }
323
+ export default FilterService;
@@ -0,0 +1,6 @@
1
+ export { FilterService, default as FilterServiceDefault } from './FilterService';
2
+ export { CustomFilterHandler, default as CustomFilterHandlerDefault } from './CustomFilterHandler';
3
+ export { AbstractCustomFilter } from './CustomFilterHandler';
4
+ export type { FilterCustomFieldCondition, RegisterOptions, FilterCustomFieldInputConfig, FilterCustomFieldInputDefinition, FilterCustomFieldInputType } from './CustomFilterHandler';
5
+ export { ConditionValidator, default as ConditionValidatorDefault } from './ConditionValidator';
6
+ export type { ValidationResult, ValidationError, FieldConfig, SecurityEvent, SecurityEventType } from './ConditionValidator';
@@ -0,0 +1,4 @@
1
+ export { FilterService, default as FilterServiceDefault } from './FilterService.js';
2
+ export { CustomFilterHandler, default as CustomFilterHandlerDefault } from './CustomFilterHandler.js';
3
+ export { AbstractCustomFilter } from './CustomFilterHandler.js';
4
+ export { ConditionValidator, default as ConditionValidatorDefault } from './ConditionValidator.js';
@@ -3,7 +3,7 @@ import { UserAP } from "../../models/UserAP";
3
3
  import { Adminizer } from "../Adminizer";
4
4
  import { Fields } from "../../helpers/fieldsHelper";
5
5
  import { ActionType } from "../../interfaces/adminpanelConfig";
6
- import { Entity } from "../../interfaces/types";
6
+ import { ModelResource } from "../../interfaces/types";
7
7
  /**
8
8
  * Abstract base class for handling history operations in the AdminPanel.
9
9
  * Provides common methods and structure for retrieving, filtering, and formatting historical data.
@@ -33,6 +33,7 @@ export declare abstract class AbstractHistoryAdapter {
33
33
  * @param adminizer - The main Adminizer instance.
34
34
  */
35
35
  protected constructor(adminizer: Adminizer);
36
+ private internalModel;
36
37
  /**
37
38
  * Registers access rights for this history adapter.
38
39
  * Called during construction with a slight delay to ensure Adminizer is ready.
@@ -139,14 +140,14 @@ export declare abstract class AbstractHistoryAdapter {
139
140
  */
140
141
  protected getModelRelationsHistory<T extends string | number>(model: string, ids: T[]): Promise<T[]>;
141
142
  /**
142
- * Constructs an Entity object from a history record.
143
+ * Constructs an ModelResource object from a history record.
143
144
  * Used to access model configuration and instance.
144
145
  *
145
146
  * @param history - The history record.
146
- * @returns Entity object with name, URI, type, model instance, and config.
147
+ * @returns ModelResource object with name, URI, model instance, and config.
147
148
  * @protected
148
149
  */
149
- protected findEntityObject(history: HistoryActionsAP): Entity;
150
+ protected findModelResource(history: HistoryActionsAP): ModelResource;
150
151
  /**
151
152
  * Loads associated records for association-type fields.
152
153
  * Populates `records` array in field config for widget rendering.