adminizer 4.1.0-commit.d8a06d3 → 4.1.1

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 (253) hide show
  1. package/Readme.md +46 -0
  2. package/assets/{VanillaJSONEditor-D-Lx670g.js → VanillaJSONEditor-DfRefswH.js} +104 -104
  3. package/assets/add-CJ1-zeA1.js +1 -0
  4. package/assets/add-form-CbNl0wdS.js +1 -0
  5. package/assets/add-group-form-Bt_Dx9wm.js +1 -0
  6. package/assets/add-group-pIFJV5qL.js +1 -0
  7. package/assets/add-user-D2US8hL_.js +1 -0
  8. package/assets/add-user-form-Bt8Jy6S2.js +1 -0
  9. package/assets/{app-WTooYWC3.css → app-B6cCPfWk.css} +1 -1
  10. package/assets/app-layout-B86jl9bH.js +1 -0
  11. package/assets/app-shell-CiGyzQ0C.js +1 -0
  12. package/assets/app-sidebar-Bz-SRwBg.js +1 -0
  13. package/assets/app-sidebar-header-7CNCx7Rs.js +11 -0
  14. package/assets/app.js +45 -37
  15. package/assets/breadcrumbs-_jhtTcOi.js +1 -0
  16. package/assets/catalog-CON6moaA.js +16 -0
  17. package/assets/catalog-Cc6Mk9sX.css +1 -0
  18. package/assets/checkbox-CQdZw5mb.js +1 -0
  19. package/assets/dashboard-es9ZT8qk.js +3 -0
  20. package/assets/{date-picker-DI1cCO7K.js → date-picker-Dz-d-YWX.js} +2 -2
  21. package/assets/debounce-qdyXyOeG.js +1 -0
  22. package/assets/del-modal-D0mrZvki.js +11 -0
  23. package/assets/dialog-stack-BiMbfm58.js +1 -0
  24. package/assets/dropdown-menu-BSBcRiXx.js +1 -0
  25. package/assets/{field-renderer-DVHZF6HS.js → field-renderer-B6hFAeiU.js} +3 -3
  26. package/assets/form-Buslty7y.js +1 -0
  27. package/assets/form-aqiRmPix.js +1 -0
  28. package/assets/{geo-json-BjiyTtMu.js → geo-json-BKmYmu3d.js} +2 -2
  29. package/assets/{handsontable-4mjU_b0n.js → handsontable-B8xVeMAQ.js} +5 -5
  30. package/assets/home-Dr72Jk-w.js +2 -0
  31. package/assets/icon-B-a7xg54.js +1 -0
  32. package/assets/index-B68_akQe.js +1 -0
  33. package/assets/index-B7-xTDyy.js +1 -0
  34. package/assets/index-BJFFlCSo.js +1 -0
  35. package/assets/{index-Cw5o84pO.js → index-BOuMo_nl.js} +3 -3
  36. package/assets/index-BzOcGsBq.js +1 -0
  37. package/assets/index-CFbQ2Rv0.js +1 -0
  38. package/assets/{index-D5ir9iUV.js → index-Cq31rrfZ.js} +1 -1
  39. package/assets/{index-Cgp8OfuI.js → index-D5UDXTFs.js} +1 -1
  40. package/assets/index-oxIQWAdI.js +41 -0
  41. package/assets/init-user-5H689LP0.js +1 -0
  42. package/assets/input-CSJjrf2i.js +1 -0
  43. package/assets/input-error-CK92Shhm.js +1 -0
  44. package/assets/label-Muj0-Efy.js +1 -0
  45. package/assets/list-Cvh7zbyG.js +1 -0
  46. package/assets/list-table-Bs_vbQiV.js +52 -0
  47. package/assets/login-DliJrjMQ.js +6 -0
  48. package/assets/manifest.json +575 -673
  49. package/assets/material-icon-BRS58Qpk.js +1 -0
  50. package/assets/module-CjG21VCT.js +1 -0
  51. package/assets/{monaco-editor-CJs0_GlD.js → monaco-editor-9fDiyfuW.js} +2 -2
  52. package/assets/{multi-select-BvRpAW8r.js → multi-select-CMQIJ0JT.js} +2 -2
  53. package/assets/nav-footer-IGMn9NiE.js +1 -0
  54. package/assets/nav-main-DBE4rMiZ.js +1 -0
  55. package/assets/nav-user-CjyLRyq-.js +1 -0
  56. package/assets/pagination-render-DUAq2klc.js +1 -0
  57. package/assets/popover-C_cww-4_.js +1 -0
  58. package/assets/register-UzQn3J0g.js +1 -0
  59. package/assets/{select-BWepUw-9.js → select-DzBno-dc.js} +2 -2
  60. package/assets/{sidebar-CAHsHdjM.js → sidebar-Dc4iqfu-.js} +2 -2
  61. package/assets/skeleton-Dkmj2o2Z.js +1 -0
  62. package/assets/sonner-De1t71Gj.js +1 -0
  63. package/assets/{toast-editor-CMK7QtF1.js → toast-editor-1Pul3oMs.js} +2 -2
  64. package/assets/tooltip-CwPrC-HD.js +1 -0
  65. package/assets/use-appearance-J8J2w43p.js +1 -0
  66. package/assets/use-window-size-DLSEoR6e.js +1 -0
  67. package/assets/{user-info-DFrmpJ4y.js → user-info-8RyDk7qh.js} +2 -2
  68. package/assets/user-menu-content-CpxTLH56.js +6 -0
  69. package/assets/welcome-BYKI6yO-.js +1 -0
  70. package/controllers/add.js +8 -8
  71. package/controllers/catalog/Catalog.d.ts +1 -1
  72. package/controllers/catalog/Catalog.js +40 -35
  73. package/controllers/catalog/FrontentCatalogAdapter.d.ts +36 -26
  74. package/controllers/catalog/FrontentCatalogAdapter.js +71 -72
  75. package/controllers/edit.js +8 -11
  76. package/controllers/list.js +2 -2
  77. package/controllers/login.js +16 -7
  78. package/controllers/navigation/edit.js +1 -1
  79. package/controllers/remove.js +1 -1
  80. package/controllers/view.js +1 -1
  81. package/helpers/JsUtils.d.ts +1 -0
  82. package/helpers/JsUtils.js +3 -0
  83. package/helpers/accessRightsHelper.d.ts +3 -0
  84. package/helpers/accessRightsHelper.js +27 -4
  85. package/helpers/controllerHelper.js +39 -18
  86. package/helpers/fieldsHelper.d.ts +3 -2
  87. package/helpers/fieldsHelper.js +5 -1
  88. package/helpers/inertiaAddHelper.d.ts +4 -5
  89. package/helpers/inertiaAddHelper.js +25 -7
  90. package/helpers/inertiaFromHelper.js +1 -1
  91. package/helpers/inertiaMenuHelper.d.ts +1 -1
  92. package/helpers/inertiaMenuHelper.js +1 -1
  93. package/helpers/menuHelper.d.ts +8 -3
  94. package/helpers/menuHelper.js +26 -17
  95. package/index.d.ts +2 -6
  96. package/index.js +5 -6
  97. package/interfaces/adminpanelConfig.d.ts +35 -30
  98. package/interfaces/global.d.ts +5 -2
  99. package/interfaces/types.d.ts +1 -1
  100. package/lib/Adminizer.d.ts +9 -3
  101. package/lib/Adminizer.js +32 -16
  102. package/lib/media-manager/Items.js +0 -3
  103. package/lib/requestProcessor.js +10 -3
  104. package/lib/v4/DataAccessor.d.ts +3 -1
  105. package/lib/v4/DataAccessor.js +43 -18
  106. package/lib/v4/I18n.js +2 -0
  107. package/lib/{catalog → v4/catalog}/AbstractCatalog.d.ts +89 -60
  108. package/lib/{catalog → v4/catalog}/AbstractCatalog.js +59 -54
  109. package/lib/v4/catalog/Navigation.d.ts +43 -0
  110. package/lib/v4/catalog/Navigation.js +457 -0
  111. package/lib/{controls → v4/controls}/AbstractControls.d.ts +1 -1
  112. package/lib/{controls → v4/controls}/codeEditor/MonacoEditor.d.ts +1 -1
  113. package/lib/{controls → v4/controls}/geojsoneditor/GeoEditor.d.ts +1 -1
  114. package/lib/{controls → v4/controls}/jsoneditor/JsonEditor.d.ts +1 -1
  115. package/lib/{controls → v4/controls}/markdown/ToastUiEditor.d.ts +1 -1
  116. package/lib/{controls → v4/controls}/table/Handsontable.d.ts +1 -1
  117. package/lib/{controls → v4/controls}/wysiwyg/CKeditor.d.ts +1 -1
  118. package/lib/{datatable → v4/datatable}/NodeTable.d.ts +3 -3
  119. package/lib/{datatable → v4/datatable}/NodeTable.js +8 -5
  120. package/lib/v4/model/AbstractModel.d.ts +5 -1
  121. package/lib/v4/model/AbstractModel.js +2 -1
  122. package/lib/v4/model/ModelHandler.js +1 -3
  123. package/lib/v4/model/adapter/sequelize.d.ts +8 -0
  124. package/lib/v4/model/adapter/sequelize.js +274 -133
  125. package/lib/v4/model/adapter/waterline.js +5 -2
  126. package/lib/{widgets → v4/widgets}/abstractLink.d.ts +1 -1
  127. package/lib/{widgets → v4/widgets}/abstractWidgetBase.d.ts +1 -1
  128. package/lib/{widgets → v4/widgets}/widgetHandler.d.ts +11 -4
  129. package/lib/{widgets → v4/widgets}/widgetHandler.js +1 -1
  130. package/models/NavigationAP.d.ts +2 -3
  131. package/models/NavigationAP.js +5 -6
  132. package/models/UserAP.d.ts +2 -3
  133. package/package.json +17 -6
  134. package/system/Router.js +8 -11
  135. package/system/bindAccessRights.js +0 -4
  136. package/system/bindControls.js +6 -6
  137. package/system/bindDev.js +1 -0
  138. package/system/bindInertia.js +9 -4
  139. package/system/bindMediaManager.js +0 -1
  140. package/system/bindModels.js +9 -6
  141. package/system/bindNavigation.d.ts +2 -0
  142. package/system/bindNavigation.js +42 -42
  143. package/system/bindReqFunctions.js +0 -1
  144. package/system/defaults.js +13 -6
  145. package/translations/ar.json +70 -1
  146. package/translations/cn.json +70 -1
  147. package/translations/de.json +71 -3
  148. package/translations/en.json +70 -102
  149. package/translations/es.json +70 -1
  150. package/translations/fr.json +70 -1
  151. package/translations/hi.json +70 -1
  152. package/translations/jp.json +70 -1
  153. package/translations/ko.json +70 -1
  154. package/translations/pt.json +70 -1
  155. package/translations/ru.json +71 -103
  156. package/translations/ua.json +70 -1
  157. package/translations/uz.json +72 -0
  158. package/translations/vi.json +70 -1
  159. package/assets/add-BP_u3Tjl.js +0 -1
  160. package/assets/add-form-nffmiYTh.js +0 -1
  161. package/assets/add-group-D4vcHlm-.js +0 -1
  162. package/assets/add-group-form-gNJW11-q.js +0 -1
  163. package/assets/add-user-DWh3Nas0.js +0 -1
  164. package/assets/add-user-form-BJ0FsZYJ.js +0 -1
  165. package/assets/app-layout-DYsRFUuP.js +0 -1
  166. package/assets/app-shell-Dd9Eknik.js +0 -1
  167. package/assets/app-sidebar-BOmu6-Sl.js +0 -1
  168. package/assets/app-sidebar-header-CoXSzWxk.js +0 -11
  169. package/assets/breadcrumbs-BmAq4fHT.js +0 -1
  170. package/assets/button-C9B_5Jf_.js +0 -1
  171. package/assets/checkbox-Fg27sL_1.js +0 -1
  172. package/assets/compiler-runtime-hB6dAw2d.js +0 -9
  173. package/assets/dashboard-BACj996r.js +0 -3
  174. package/assets/dropdown-menu-Bhj17ENm.js +0 -1
  175. package/assets/form-BgWdwvz1.js +0 -1
  176. package/assets/form-DgtErfln.js +0 -1
  177. package/assets/home-CHntBHJa.js +0 -2
  178. package/assets/icon-Dt0vBgDO.js +0 -1
  179. package/assets/index-B8BS9wMC.js +0 -1
  180. package/assets/index-BC0yfOHo.js +0 -1
  181. package/assets/index-BKCgnGj_.js +0 -1
  182. package/assets/index-BbXEMx9n.js +0 -1
  183. package/assets/index-BykbU6Ee.js +0 -41
  184. package/assets/index-CaJgc5EZ.js +0 -1
  185. package/assets/index-DrWfGwtf.js +0 -1
  186. package/assets/init-user-JfCKJ5Ce.js +0 -1
  187. package/assets/input-CvRbIbk4.js +0 -1
  188. package/assets/input-error-DMK7cAt7.js +0 -1
  189. package/assets/label-CSLFe9Bz.js +0 -1
  190. package/assets/list-B2Oa5O_Q.js +0 -1
  191. package/assets/list-table-DA9MhywL.js +0 -62
  192. package/assets/login-DQwGtdis.js +0 -6
  193. package/assets/material-icon-BJg8o_fs.js +0 -1
  194. package/assets/module-DWXp59Vg.js +0 -1
  195. package/assets/nav-footer-Bs4dmN1d.js +0 -1
  196. package/assets/nav-main-Bq3IMk4C.js +0 -1
  197. package/assets/nav-user-CbB22J5O.js +0 -1
  198. package/assets/pagination-render-DuXDDbC_.js +0 -1
  199. package/assets/popover-DtoGI2b1.js +0 -1
  200. package/assets/register-BSogVF0h.js +0 -1
  201. package/assets/skeleton-COOcAA8f.js +0 -1
  202. package/assets/sonner-BI0IFOQN.js +0 -1
  203. package/assets/tooltip-DHwpLKOI.js +0 -1
  204. package/assets/use-appearance-DRfIqTgw.js +0 -1
  205. package/assets/use-window-size-h0Sh_NvX.js +0 -1
  206. package/assets/user-menu-content-DPXQdERj.js +0 -6
  207. package/assets/utils-DjqsqOe8.js +0 -1
  208. package/assets/welcome-B4WCIXKf.js +0 -1
  209. package/controllers/processInstallFinalize.d.ts +0 -1
  210. package/controllers/processInstallFinalize.js +0 -16
  211. package/controllers/processInstallStep.d.ts +0 -0
  212. package/controllers/processInstallStep.js +0 -122
  213. package/lib/catalog/Navigation.d.ts +0 -0
  214. package/lib/catalog/Navigation.js +0 -532
  215. package/lib/installStepper/InstallStepAbstract.d.ts +0 -52
  216. package/lib/installStepper/InstallStepAbstract.js +0 -69
  217. package/lib/installStepper/finalizeStep.d.ts +0 -21
  218. package/lib/installStepper/finalizeStep.js +0 -48
  219. package/lib/installStepper/installStepper.d.ts +0 -40
  220. package/lib/installStepper/installStepper.js +0 -195
  221. package/models/MediaManagerAP.d.ts +0 -62
  222. package/models/MediaManagerAP.js +0 -45
  223. package/models/MediaManagerAssociationsAP.d.ts +0 -38
  224. package/models/MediaManagerAssociationsAP.js +0 -27
  225. package/models/MediaManagerMetaAP.d.ts +0 -30
  226. package/models/MediaManagerMetaAP.js +0 -21
  227. package/system/bindInstallStepper.d.ts +0 -2
  228. package/system/bindInstallStepper.js +0 -23
  229. /package/lib/{catalog → v4/catalog}/CatalogHandler.d.ts +0 -0
  230. /package/lib/{catalog → v4/catalog}/CatalogHandler.js +0 -0
  231. /package/lib/{controls → v4/controls}/AbstractControls.js +0 -0
  232. /package/lib/{controls → v4/controls}/ControlsHandler.d.ts +0 -0
  233. /package/lib/{controls → v4/controls}/ControlsHandler.js +0 -0
  234. /package/lib/{controls → v4/controls}/codeEditor/MonacoEditor.js +0 -0
  235. /package/lib/{controls → v4/controls}/geojsoneditor/GeoEditor.js +0 -0
  236. /package/lib/{controls → v4/controls}/jsoneditor/JsonEditor.js +0 -0
  237. /package/lib/{controls → v4/controls}/markdown/ToastUiEditor.js +0 -0
  238. /package/lib/{controls → v4/controls}/table/Handsontable.js +0 -0
  239. /package/lib/{controls → v4/controls}/wysiwyg/CKeditor.js +0 -0
  240. /package/lib/{inertia → v4/inertia}/flash.d.ts +0 -0
  241. /package/lib/{inertia → v4/inertia}/flash.js +0 -0
  242. /package/lib/{inertia → v4/inertia}/inertiaAdapter.d.ts +0 -0
  243. /package/lib/{inertia → v4/inertia}/inertiaAdapter.js +0 -0
  244. /package/lib/{widgets → v4/widgets}/abstractAction.d.ts +0 -0
  245. /package/lib/{widgets → v4/widgets}/abstractAction.js +0 -0
  246. /package/lib/{widgets → v4/widgets}/abstractCustom.d.ts +0 -0
  247. /package/lib/{widgets → v4/widgets}/abstractCustom.js +0 -0
  248. /package/lib/{widgets → v4/widgets}/abstractInfo.d.ts +0 -0
  249. /package/lib/{widgets → v4/widgets}/abstractInfo.js +0 -0
  250. /package/lib/{widgets → v4/widgets}/abstractLink.js +0 -0
  251. /package/lib/{widgets → v4/widgets}/abstractSwitch.d.ts +0 -0
  252. /package/lib/{widgets → v4/widgets}/abstractSwitch.js +0 -0
  253. /package/lib/{widgets → v4/widgets}/abstractWidgetBase.js +0 -0
@@ -3,6 +3,64 @@ import { AbstractAdapter, AbstractModel } from "../AbstractModel";
3
3
  import path from "path";
4
4
  import fs from "fs";
5
5
  import { pathToFileURL } from "url";
6
+ function generateAssociationsFromSchema(models, schemas) {
7
+ for (const modelName in schemas) {
8
+ const schema = schemas[modelName];
9
+ const model = models[modelName];
10
+ if (!model)
11
+ continue;
12
+ for (const fieldName in schema) {
13
+ const field = schema[fieldName];
14
+ if (field.collection && field.via) {
15
+ const targetModel = models[field.collection];
16
+ const targetSchema = schemas[field.collection];
17
+ const inverseField = targetSchema?.[field.via];
18
+ const throughTableName = [modelName, field.collection].sort().join("");
19
+ // 💡 M:N связь
20
+ if (inverseField && inverseField.collection === modelName) {
21
+ model.belongsToMany(targetModel, {
22
+ through: throughTableName,
23
+ as: fieldName,
24
+ foreignKey: `${modelName}Id`,
25
+ otherKey: `${field.collection}Id`
26
+ });
27
+ }
28
+ // 💡 O:M связь (один ко многим)
29
+ else {
30
+ model.hasMany(targetModel, {
31
+ as: fieldName,
32
+ foreignKey: `${modelName}Id`,
33
+ });
34
+ targetModel.belongsTo(model, {
35
+ as: field.via,
36
+ foreignKey: `${modelName}Id`,
37
+ });
38
+ }
39
+ }
40
+ // 💡 O:1 или 1:1 (belongsTo)
41
+ if (field.model) {
42
+ const targetModel = models[field.model];
43
+ if (!targetModel)
44
+ continue;
45
+ // Avoid naming collision by making FK explicit: `${fieldName}Id`
46
+ const foreignKey = `${fieldName}Id`;
47
+ const alias = fieldName;
48
+ // Если поле уже существует — избегаем конфликта
49
+ if (model.rawAttributes[alias]) {
50
+ model.belongsTo(targetModel, {
51
+ as: alias,
52
+ foreignKey,
53
+ });
54
+ }
55
+ else {
56
+ model.belongsTo(targetModel, {
57
+ foreignKey: alias, // если нет конфликта, можно использовать alias как FK
58
+ });
59
+ }
60
+ }
61
+ }
62
+ }
63
+ }
6
64
  function resolveType(type) {
7
65
  const sqlType = typeof type.toString === "function"
8
66
  ? type.toString().toLowerCase()
@@ -38,18 +96,20 @@ export function mapSequelizeToWaterline(model) {
38
96
  switch (assoc.associationType) {
39
97
  case "BelongsTo": {
40
98
  const a = assoc;
99
+ result[a.foreignKey]["primaryKeyForAssociation"] = true;
41
100
  result[alias] = {
42
101
  type: "association",
43
- model: a.target.name.toLowerCase(),
102
+ model: a.target.name,
44
103
  via: a.foreignKey,
45
104
  };
46
105
  break;
47
106
  }
48
107
  case "HasOne": {
49
108
  const a = assoc;
109
+ result[a.foreignKey]["primaryKeyForAssociation"] = true;
50
110
  result[alias] = {
51
111
  type: "association",
52
- model: a.target.name.toLowerCase(),
112
+ model: a.target.name,
53
113
  via: a.foreignKey,
54
114
  };
55
115
  break;
@@ -58,7 +118,7 @@ export function mapSequelizeToWaterline(model) {
58
118
  const a = assoc;
59
119
  result[alias] = {
60
120
  type: "association-many",
61
- collection: a.target.name.toLowerCase(),
121
+ collection: a.target.name,
62
122
  via: a.foreignKey,
63
123
  };
64
124
  break;
@@ -67,7 +127,7 @@ export function mapSequelizeToWaterline(model) {
67
127
  const a = assoc;
68
128
  result[alias] = {
69
129
  type: "association-many",
70
- collection: a.target.name.toLowerCase(),
130
+ collection: a.target.name,
71
131
  via: a.otherKey,
72
132
  };
73
133
  break;
@@ -78,113 +138,109 @@ export function mapSequelizeToWaterline(model) {
78
138
  }
79
139
  return result;
80
140
  }
81
- // function resolveType(type: any): AbstractFieldType {
82
- // try {
83
- // const sqlType = typeof type.toString === "function"
84
- // ? type.toString().toLowerCase()
85
- // : "";
86
- // if (sqlType.includes("string")) return "string";
87
- // if (sqlType.includes("uuid")) return "string";
88
- // if (sqlType.includes("int") || sqlType.includes("float") || sqlType.includes("decimal")) return "number";
89
- // if (sqlType.includes("bool")) return "boolean";
90
- // if (sqlType.includes("json")) return "json";
91
- // if (sqlType.includes("date")) return "string";
92
- // return "ref";
93
- // } catch {
94
- // return "ref";
95
- // }
96
- // }
97
- /** SequelizeModel — реализация модели, совместимая с AbstractModel */
98
141
  function capitalize(str) {
99
142
  return str.charAt(0).toUpperCase() + str.slice(1);
100
143
  }
101
- function convertCriteriaToSequelize(criteria) {
102
- // console.debug("waterline criteria", criteria)
103
- const result = {};
104
- for (const key in criteria) {
105
- const value = criteria[key];
106
- if (value === undefined ||
107
- value === null ||
108
- (typeof value === "object" && Object.keys(value).length === 0)) {
109
- continue;
110
- }
111
- if (typeof value === "object" && !Array.isArray(value)) {
112
- const operatorEntries = Object.entries(value)
113
- .filter(([_, v]) => v !== undefined && v !== null)
114
- .map(([op, val]) => {
115
- switch (op) {
116
- case "contains": return [Op.like, `%${val}%`];
117
- case "startsWith": return [Op.startsWith, val];
118
- case "endsWith": return [Op.endsWith, val];
119
- case ">": return [Op.gt, val];
120
- case ">=": return [Op.gte, val];
121
- case "<": return [Op.lt, val];
122
- case "<=": return [Op.lte, val];
123
- case "!=": return [Op.ne, val];
124
- case "in": return [Op.in, val];
125
- case "nin": return [Op.notIn, val];
126
- default: return [Op.eq, val];
127
- }
128
- });
129
- if (operatorEntries.length > 0) {
130
- result[key] = Object.fromEntries(operatorEntries);
131
- }
132
- }
133
- else {
134
- result[key] = value;
135
- }
136
- }
137
- return result;
138
- }
139
- function convertWaterlineCriteriaToSequelizeOptions(criteria) {
140
- // console.debug("WATERLINE CRITERIA (raw):", criteria);
141
- const { where: nestedWhere, skip, limit, sort, ...rest } = criteria;
142
- const rawWhere = (nestedWhere && Object.keys(nestedWhere).length > 0)
143
- ? nestedWhere
144
- : rest;
145
- // console.debug("WATERLINE CRITERIA: using rawWhere =", rawWhere);
146
- const where = convertCriteriaToSequelize(rawWhere);
147
- // console.debug("convertCriteriaToSequelize →", where);
148
- const result = { where };
149
- if (typeof skip === "number") {
150
- result.offset = skip;
151
- // console.debug("→ offset =", skip);
152
- }
153
- if (typeof limit === "number") {
154
- result.limit = limit;
155
- // console.debug("→ limit =", limit);
156
- }
157
- if (typeof sort === "string") {
158
- const [field, dir] = sort.trim().split(/\s+/);
159
- result.order = [[field, dir?.toUpperCase() === "DESC" ? "DESC" : "ASC"]];
160
- // console.debug("→ order =", result.order);
161
- }
162
- return result;
163
- }
164
- function convertWaterlineCriteriaToSequelizeOptions____OLD(criteria) {
165
- // console.debug("WATERLINE CRITERIA", criteria)
166
- const { where = {}, skip, limit, sort } = criteria;
167
- const result = {
168
- where: convertCriteriaToSequelize(where),
169
- };
170
- if (typeof skip === "number") {
171
- result.offset = skip;
172
- }
173
- if (typeof limit === "number") {
174
- result.limit = limit;
175
- }
176
- if (typeof sort === "string") {
177
- const [field, direction] = sort.trim().split(/\s+/);
178
- result.order = [[field, direction?.toUpperCase() === "DESC" ? "DESC" : "ASC"]];
179
- }
180
- return result;
181
- }
182
144
  export class SequelizeModel extends AbstractModel {
183
145
  model;
184
146
  constructor(modelName, model) {
185
147
  super(modelName, mapSequelizeToWaterline(model), model.primaryKeyAttribute, model.name);
186
148
  this.model = model;
187
149
  }
150
+ _convertCriteriaToSequelize(criteria) {
151
+ const result = {};
152
+ for (const key in criteria) {
153
+ const value = criteria[key];
154
+ if (value === undefined ||
155
+ value === null ||
156
+ (typeof value === "object" && Object.keys(value).length === 0)) {
157
+ continue;
158
+ }
159
+ // 🧠 Заменяем ключ на `via`, если это ассоциация
160
+ const attr = this.attributes?.[key];
161
+ let targetKey = key;
162
+ if (attr?.type === "association" && attr.via) {
163
+ targetKey = attr.via;
164
+ }
165
+ if (typeof value === "object" && !Array.isArray(value)) {
166
+ const operatorEntries = Object.entries(value)
167
+ .filter(([_, v]) => v !== undefined && v !== null)
168
+ .map(([op, val]) => {
169
+ switch (op) {
170
+ case "contains": return [Op.like, `%${val}%`];
171
+ case "startsWith": return [Op.startsWith, val];
172
+ case "endsWith": return [Op.endsWith, val];
173
+ case ">": return [Op.gt, val];
174
+ case ">=": return [Op.gte, val];
175
+ case "<": return [Op.lt, val];
176
+ case "<=": return [Op.lte, val];
177
+ case "!=": return [Op.ne, val];
178
+ case "in": return [Op.in, val];
179
+ case "nin": return [Op.notIn, val];
180
+ default: return [Op.eq, val];
181
+ }
182
+ });
183
+ if (operatorEntries.length > 0) {
184
+ result[targetKey] = Object.fromEntries(operatorEntries);
185
+ }
186
+ }
187
+ else {
188
+ result[targetKey] = value;
189
+ }
190
+ }
191
+ return result;
192
+ }
193
+ _convertWaterlineCriteriaToSequelizeOptions(criteria) {
194
+ // console.debug("WATERLINE CRITERIA (raw):", criteria);
195
+ const { where: nestedWhere, skip, limit, sort, ...rest } = criteria;
196
+ const rawWhere = (nestedWhere && Object.keys(nestedWhere).length > 0)
197
+ ? nestedWhere
198
+ : rest;
199
+ // console.debug("WATERLINE CRITERIA: using rawWhere =", rawWhere);
200
+ const where = this._convertCriteriaToSequelize(rawWhere);
201
+ const result = { where };
202
+ if (typeof skip === "number") {
203
+ result.offset = skip;
204
+ // console.debug("→ offset =", skip);
205
+ }
206
+ if (typeof limit === "number") {
207
+ result.limit = limit;
208
+ // console.debug("→ limit =", limit);
209
+ }
210
+ if (typeof sort === "string") {
211
+ const [field, dir] = sort.trim().split(/\s+/);
212
+ result.order = [[field, dir?.toUpperCase() === "DESC" ? "DESC" : "ASC"]];
213
+ // console.debug("→ order =", result.order);
214
+ }
215
+ return result;
216
+ }
217
+ async _assignAssociations(instance, assocData) {
218
+ for (const [alias, ids] of Object.entries(assocData)) {
219
+ const assoc = this.model.associations[alias];
220
+ if (!assoc)
221
+ continue;
222
+ //@ts-ignore
223
+ const { set: setAccessor, add: addAccessor } = assoc.accessors;
224
+ if (Array.isArray(ids)) {
225
+ if (typeof instance[setAccessor] === 'function') {
226
+ await instance[setAccessor](ids);
227
+ continue;
228
+ }
229
+ if (typeof instance[addAccessor] === 'function') {
230
+ for (const id of ids) {
231
+ await instance[addAccessor](id);
232
+ }
233
+ continue;
234
+ }
235
+ }
236
+ if (typeof instance[setAccessor] === 'function') {
237
+ await instance[setAccessor](ids);
238
+ }
239
+ else if (typeof instance[addAccessor] === 'function') {
240
+ await instance[addAccessor](ids);
241
+ }
242
+ }
243
+ }
188
244
  // --- CREATE ---
189
245
  async _create(data) {
190
246
  // console.clear()
@@ -253,7 +309,7 @@ export class SequelizeModel extends AbstractModel {
253
309
  // --- FIND ONE ---
254
310
  async _findOne(criteria) {
255
311
  // console.debug(">> _findOne: входные критерии:", criteria);
256
- const { where } = convertWaterlineCriteriaToSequelizeOptions(criteria);
312
+ const { where } = this._convertWaterlineCriteriaToSequelizeOptions(criteria);
257
313
  const includes = this._buildIncludes();
258
314
  // console.debug(">> _findOne: преобразованные where:", where);
259
315
  // console.debug(">> _findOne: includes:", includes);
@@ -278,7 +334,7 @@ export class SequelizeModel extends AbstractModel {
278
334
  async _find(criteria = {}, options = {}) {
279
335
  const assocNames = Object.keys(this.model.associations);
280
336
  // console.debug(">> _find: входные criteria:", criteria, "options:", options);
281
- const { where, limit, offset, order } = convertWaterlineCriteriaToSequelizeOptions(criteria);
337
+ const { where, limit, offset, order } = this._convertWaterlineCriteriaToSequelizeOptions(criteria);
282
338
  const includes = options.populate
283
339
  ? options.populate.map(([field, opts]) => ({ association: field, ...opts }))
284
340
  : assocNames.map(a => ({ association: a }));
@@ -290,20 +346,13 @@ export class SequelizeModel extends AbstractModel {
290
346
  // includes,
291
347
  // });
292
348
  let instances;
293
- try {
294
- instances = await this.model.findAll({
295
- where,
296
- limit,
297
- offset,
298
- order,
299
- include: includes
300
- });
301
- // console.debug(">> _find: получено моделей:", instances.length);
302
- }
303
- catch (err) {
304
- // console.error("!! _find: ошибка в findAll:", err);
305
- throw err;
306
- }
349
+ instances = await this.model.findAll({
350
+ where,
351
+ limit,
352
+ offset,
353
+ order,
354
+ include: includes
355
+ });
307
356
  for (const inst of instances) {
308
357
  //For each association, we call getxxx () once again
309
358
  for (const alias of assocNames) {
@@ -329,28 +378,81 @@ export class SequelizeModel extends AbstractModel {
329
378
  }
330
379
  // --- UPDATE ONE ---
331
380
  async _updateOne(criteria, data) {
332
- const { where } = convertWaterlineCriteriaToSequelizeOptions(criteria);
381
+ const { where } = this._convertWaterlineCriteriaToSequelizeOptions(criteria);
333
382
  const record = await this.model.findOne({ where });
334
- if (!record) {
383
+ if (!record)
335
384
  return null;
385
+ const assocNames = Object.keys(this.model.associations);
386
+ const plainData = {};
387
+ const assocData = {};
388
+ for (const [key, val] of Object.entries(data)) {
389
+ if (assocNames.includes(key)) {
390
+ assocData[key] = val;
391
+ }
392
+ else {
393
+ plainData[key] = val;
394
+ }
336
395
  }
337
- await record.update(data);
338
- const result = await this.model.findByPk(record.get(this.primaryKey), { raw: true });
339
- return result;
396
+ await record.update(plainData);
397
+ await this._assignAssociations(record, assocData);
398
+ await record.reload({ include: Object.values(this.model.associations) });
399
+ return record.get({ plain: true });
340
400
  }
341
401
  // --- UPDATE MANY ---
342
402
  async _update(criteria, data) {
343
- const { where } = convertWaterlineCriteriaToSequelizeOptions(criteria);
344
- await this.model.update(data, { where });
345
- const result = await this.model.findAll({ where, raw: true });
346
- return result;
403
+ const { where } = this._convertWaterlineCriteriaToSequelizeOptions(criteria);
404
+ const assocNames = Object.keys(this.model.associations);
405
+ const plainData = {};
406
+ const assocData = {};
407
+ for (const [key, val] of Object.entries(data)) {
408
+ if (assocNames.includes(key)) {
409
+ assocData[key] = val;
410
+ }
411
+ else {
412
+ plainData[key] = val;
413
+ }
414
+ }
415
+ const records = await this.model.findAll({ where });
416
+ for (const record of records) {
417
+ await record.update(plainData);
418
+ await this._assignAssociations(record, assocData);
419
+ }
420
+ const reloaded = await this.model.findAll({
421
+ where,
422
+ include: Object.values(this.model.associations)
423
+ });
424
+ return reloaded.map(r => r.get({ plain: true }));
347
425
  }
348
426
  // --- DESTROY ONE ---
349
427
  async _destroyOne(criteria) {
350
- const { where } = convertWaterlineCriteriaToSequelizeOptions(criteria);
428
+ const { where } = this._convertWaterlineCriteriaToSequelizeOptions(criteria);
351
429
  const record = await this.model.findOne({ where });
352
- if (!record) {
430
+ if (!record)
353
431
  return null;
432
+ const assocNames = Object.keys(this.model.associations);
433
+ for (const alias of assocNames) {
434
+ const assoc = this.model.associations[alias];
435
+ // @ts-ignore: accessors should exist
436
+ const getAccessor = assoc.accessors?.get;
437
+ if (typeof record[getAccessor] === "function") {
438
+ try {
439
+ const related = await record[getAccessor]();
440
+ // 🧹 Удаляем связанные записи
441
+ if (Array.isArray(related)) {
442
+ for (const r of related) {
443
+ if (typeof r.destroy === "function") {
444
+ await r.destroy();
445
+ }
446
+ }
447
+ }
448
+ else if (related && typeof related.destroy === "function") {
449
+ await related.destroy();
450
+ }
451
+ }
452
+ catch (e) {
453
+ // console.warn(`Failed to fetch/delete relation "${alias}":`, e);
454
+ }
455
+ }
354
456
  }
355
457
  const raw = record.get({ plain: true });
356
458
  await record.destroy();
@@ -358,15 +460,44 @@ export class SequelizeModel extends AbstractModel {
358
460
  }
359
461
  // --- DESTROY MANY ---
360
462
  async _destroy(criteria) {
361
- const { where } = convertWaterlineCriteriaToSequelizeOptions(criteria);
463
+ const { where } = this._convertWaterlineCriteriaToSequelizeOptions(criteria);
362
464
  const records = await this.model.findAll({ where });
465
+ const assocNames = Object.keys(this.model.associations);
466
+ for (const record of records) {
467
+ for (const alias of assocNames) {
468
+ const assoc = this.model.associations[alias];
469
+ // 🛑 Не трогаем родительские связи
470
+ if (assoc.associationType === "BelongsTo")
471
+ continue;
472
+ // @ts-ignore accessor exists
473
+ const getAccessor = assoc.accessors?.get;
474
+ if (typeof record[getAccessor] === "function") {
475
+ try {
476
+ const related = await record[getAccessor]();
477
+ if (Array.isArray(related)) {
478
+ for (const rel of related) {
479
+ if (typeof rel.destroy === "function") {
480
+ await rel.destroy();
481
+ }
482
+ }
483
+ }
484
+ else if (related && typeof related.destroy === "function") {
485
+ await related.destroy();
486
+ }
487
+ }
488
+ catch (e) {
489
+ // console.warn(`Failed to delete relation ${alias}:`, e);
490
+ }
491
+ }
492
+ }
493
+ }
363
494
  const raw = records.map(r => r.get({ plain: true }));
364
495
  await this.model.destroy({ where });
365
496
  return raw;
366
497
  }
367
498
  // --- COUNT ---
368
499
  async _count(criteria = {}) {
369
- const { where } = convertWaterlineCriteriaToSequelizeOptions(criteria);
500
+ const { where } = this._convertWaterlineCriteriaToSequelizeOptions(criteria);
370
501
  const result = await this.model.count({ where });
371
502
  return result;
372
503
  }
@@ -400,13 +531,23 @@ export class SequelizeAdapter extends AbstractAdapter {
400
531
  /**Registration of system models*/
401
532
  static async registerSystemModels(sequelize) {
402
533
  const modelsDir = path.resolve(import.meta.dirname, "../../../../models");
403
- const files = fs.readdirSync(modelsDir).filter(f => f.endsWith(".js"));
534
+ let files = fs.readdirSync(modelsDir).filter(f => f.endsWith(".js"));
535
+ if (!files.length) {
536
+ files = fs.readdirSync(modelsDir).filter(f => f.endsWith(".ts") && !f.endsWith(".d.ts"));
537
+ }
538
+ if (!files.length) {
539
+ throw `Model files not found in dir ${modelsDir}`;
540
+ }
541
+ const schemas = {};
404
542
  for (const file of files) {
405
543
  const modelName = path.basename(file, path.extname(file));
406
544
  const filePath = path.resolve(modelsDir, file);
407
- const definition = (await import(pathToFileURL(filePath).href)).default;
545
+ const mod = await import(pathToFileURL(filePath).href);
546
+ const definition = mod.default;
547
+ schemas[modelName] = definition;
408
548
  generateSequelizeModel(sequelize, modelName, definition);
409
549
  }
550
+ generateAssociationsFromSchema(sequelize.models, schemas);
410
551
  await sequelize.sync();
411
552
  }
412
553
  }
@@ -90,10 +90,13 @@ export class WaterlineAdapter extends AbstractAdapter {
90
90
  /** Method that processes custom waterline model creation. Is used for system models. Replaces beforeCreate method in waterline */
91
91
  static async registerSystemModels(waterlineORM) {
92
92
  const systemModelsDir = path.resolve(import.meta.dirname, "../../../../models");
93
- const systemModelsFiles = fs.readdirSync(systemModelsDir).filter(file => file.endsWith(".js"));
93
+ let systemModelsFiles = fs.readdirSync(systemModelsDir).filter(file => file.endsWith(".js"));
94
+ if (!systemModelsFiles.length) {
95
+ systemModelsFiles = fs.readdirSync(systemModelsDir).filter(file => file.endsWith(".ts") && !file.endsWith(".d.ts"));
96
+ }
94
97
  // Register adminizer system models
95
98
  await Promise.all(systemModelsFiles.map(async (file) => {
96
- const modelName = path.basename(file, ".js");
99
+ const modelName = path.basename(file).replace(/\.(ts|js)$/, "");
97
100
  const systemModelPath = path.resolve(systemModelsDir, file);
98
101
  const adminizerModel = (await import(pathToFileURL(systemModelPath).href)).default;
99
102
  const waterlineLikeModel = generateWaterlineModel(modelName, adminizerModel);
@@ -1,5 +1,5 @@
1
1
  import BaseWidget from "./abstractWidgetBase";
2
- import { MaterialIcon } from "../../interfaces/MaaterialIcons";
2
+ import { MaterialIcon } from "../../../interfaces/MaaterialIcons";
3
3
  export interface Links {
4
4
  name: string;
5
5
  description: string;
@@ -1,4 +1,4 @@
1
- import { MaterialIcon } from "../../interfaces/MaaterialIcons";
1
+ import { MaterialIcon } from "../../../interfaces/MaaterialIcons";
2
2
  export default abstract class BaseWidget {
3
3
  /** Widget unique id */
4
4
  abstract readonly id: string;
@@ -3,10 +3,10 @@ import InfoBase from "./abstractInfo";
3
3
  import ActionBase from "./abstractAction";
4
4
  import LinkBase from "./abstractLink";
5
5
  import CustomBase from "./abstractCustom";
6
- import { AdminpanelIcon } from "../../interfaces/adminpanelConfig";
7
- import { Adminizer } from "../Adminizer";
6
+ import { AdminpanelIcon } from "../../../interfaces/adminpanelConfig";
7
+ import { Adminizer } from "../../Adminizer";
8
8
  import { UserAP } from "models/UserAP";
9
- import { I18n } from "../v4/I18n";
9
+ import { I18n } from "../I18n";
10
10
  export type WidgetType = (SwitcherBase | InfoBase | ActionBase | LinkBase | CustomBase);
11
11
  export interface WidgetConfig {
12
12
  id: string;
@@ -36,6 +36,13 @@ export interface WidgetLayoutItem {
36
36
  i: string;
37
37
  id: string;
38
38
  }
39
+ export interface WidgetsLayouts {
40
+ lg: WidgetLayoutItem[];
41
+ md: WidgetLayoutItem[];
42
+ sm: WidgetLayoutItem[];
43
+ xs: WidgetLayoutItem[];
44
+ xxs: WidgetLayoutItem[];
45
+ }
39
46
  export declare class WidgetHandler {
40
47
  private widgets;
41
48
  adminizer: Adminizer;
@@ -46,7 +53,7 @@ export declare class WidgetHandler {
46
53
  getAll(user: UserAP, i18n: I18n): Promise<WidgetConfig[]>;
47
54
  getWidgetsDB(id: number, auth: boolean, i18n: I18n): Promise<{
48
55
  widgets: WidgetConfig[];
49
- layout: WidgetLayoutItem[];
56
+ layout: WidgetsLayouts;
50
57
  }>;
51
58
  setWidgetsDB(id: number, body: {
52
59
  widgets: WidgetConfig[];
@@ -124,7 +124,7 @@ export class WidgetHandler {
124
124
  }
125
125
  async getWidgetsDB(id, auth, i18n) {
126
126
  let user;
127
- let result = { widgets: [], layout: [] };
127
+ let result = { widgets: [], layout: { lg: [], md: [], sm: [], xs: [], xxs: [] } };
128
128
  if (!auth) {
129
129
  // TODO refactor CRUD functions for DataAccessor usage
130
130
  user = await this.adminizer.modelHandler.model.get("UserAP")?.["_findOne"]({ login: this.adminizer.config.administrator?.login ?? 'admin' });
@@ -1,14 +1,13 @@
1
1
  declare const _default: {
2
2
  id: {
3
3
  type: string;
4
- allowNull: boolean;
5
- uuid: boolean;
4
+ autoIncrement: boolean;
6
5
  primaryKey: boolean;
7
- required: boolean;
8
6
  };
9
7
  label: {
10
8
  type: string;
11
9
  required: boolean;
10
+ unique: boolean;
12
11
  };
13
12
  tree: {
14
13
  type: string;
@@ -1,14 +1,13 @@
1
1
  export default {
2
2
  id: {
3
- type: "string",
4
- allowNull: false,
5
- uuid: true,
6
- primaryKey: true,
7
- required: true
3
+ type: "number",
4
+ autoIncrement: true,
5
+ primaryKey: true
8
6
  },
9
7
  label: {
10
8
  type: "string",
11
- required: true
9
+ required: true,
10
+ unique: true
12
11
  },
13
12
  tree: {
14
13
  type: "json",
@@ -1,6 +1,5 @@
1
- import { WidgetConfig } from "lib/widgets/widgetHandler";
1
+ import { WidgetConfig, WidgetsLayouts } from "lib/v4/widgets/widgetHandler";
2
2
  import { GroupAP } from "./GroupAP";
3
- import { WidgetLayoutItem } from "../assets/js/types";
4
3
  declare const _default: {
5
4
  id: {
6
5
  type: string;
@@ -71,7 +70,7 @@ export interface UserAP {
71
70
  groups?: GroupAP[];
72
71
  widgets?: {
73
72
  widgets: WidgetConfig[];
74
- layout: WidgetLayoutItem[];
73
+ layout: WidgetsLayouts;
75
74
  };
76
75
  isConfirmed?: boolean;
77
76
  }