@open-mercato/core 0.4.2-canary-10c7a8bf2a → 0.4.2-canary-c84cff7ed5

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 (526) hide show
  1. package/dist/generated/entities/notification/index.js +57 -0
  2. package/dist/generated/entities/notification/index.js.map +7 -0
  3. package/dist/generated/entities.ids.generated.js +5 -1
  4. package/dist/generated/entities.ids.generated.js.map +2 -2
  5. package/dist/generated/entity-fields-registry.js +2 -0
  6. package/dist/generated/entity-fields-registry.js.map +2 -2
  7. package/dist/modules/api_docs/frontend/docs/api/page.js +3 -2
  8. package/dist/modules/api_docs/frontend/docs/api/page.js.map +2 -2
  9. package/dist/modules/api_keys/backend/api-keys/page.js +1 -1
  10. package/dist/modules/api_keys/backend/api-keys/page.js.map +2 -2
  11. package/dist/modules/attachments/components/AttachmentLibrary.js +4 -0
  12. package/dist/modules/attachments/components/AttachmentLibrary.js.map +2 -2
  13. package/dist/modules/attachments/components/AttachmentPartitionSettings.js +2 -0
  14. package/dist/modules/attachments/components/AttachmentPartitionSettings.js.map +2 -2
  15. package/dist/modules/auth/api/admin/nav.js +4 -3
  16. package/dist/modules/auth/api/admin/nav.js.map +2 -2
  17. package/dist/modules/auth/api/profile/route.js +157 -0
  18. package/dist/modules/auth/api/profile/route.js.map +7 -0
  19. package/dist/modules/auth/api/reset/confirm.js +25 -2
  20. package/dist/modules/auth/api/reset/confirm.js.map +2 -2
  21. package/dist/modules/auth/api/reset.js +23 -0
  22. package/dist/modules/auth/api/reset.js.map +2 -2
  23. package/dist/modules/auth/api/sidebar/preferences/route.js +14 -9
  24. package/dist/modules/auth/api/sidebar/preferences/route.js.map +2 -2
  25. package/dist/modules/auth/api/users/route.js +4 -2
  26. package/dist/modules/auth/api/users/route.js.map +2 -2
  27. package/dist/modules/auth/backend/auth/profile/page.js +141 -0
  28. package/dist/modules/auth/backend/auth/profile/page.js.map +7 -0
  29. package/dist/modules/auth/backend/auth/profile/page.meta.js +13 -0
  30. package/dist/modules/auth/backend/auth/profile/page.meta.js.map +7 -0
  31. package/dist/modules/auth/backend/roles/page.js +3 -3
  32. package/dist/modules/auth/backend/roles/page.js.map +2 -2
  33. package/dist/modules/auth/backend/users/[id]/edit/page.js +14 -2
  34. package/dist/modules/auth/backend/users/[id]/edit/page.js.map +2 -2
  35. package/dist/modules/auth/backend/users/create/page.js +15 -2
  36. package/dist/modules/auth/backend/users/create/page.js.map +2 -2
  37. package/dist/modules/auth/backend/users/page.js +3 -3
  38. package/dist/modules/auth/backend/users/page.js.map +2 -2
  39. package/dist/modules/auth/cli.js +13 -0
  40. package/dist/modules/auth/cli.js.map +2 -2
  41. package/dist/modules/auth/commands/users.js +59 -2
  42. package/dist/modules/auth/commands/users.js.map +2 -2
  43. package/dist/modules/auth/data/validators.js +4 -2
  44. package/dist/modules/auth/data/validators.js.map +2 -2
  45. package/dist/modules/auth/frontend/reset/[token]/page.js +20 -10
  46. package/dist/modules/auth/frontend/reset/[token]/page.js.map +2 -2
  47. package/dist/modules/auth/lib/setup-app.js +3 -0
  48. package/dist/modules/auth/lib/setup-app.js.map +2 -2
  49. package/dist/modules/auth/notifications.js +112 -0
  50. package/dist/modules/auth/notifications.js.map +7 -0
  51. package/dist/modules/auth/services/authService.js +3 -3
  52. package/dist/modules/auth/services/authService.js.map +2 -2
  53. package/dist/modules/business_rules/backend/rules/page.js +4 -0
  54. package/dist/modules/business_rules/backend/rules/page.js.map +2 -2
  55. package/dist/modules/business_rules/backend/sets/page.js +3 -0
  56. package/dist/modules/business_rules/backend/sets/page.js.map +2 -2
  57. package/dist/modules/business_rules/notifications.js +28 -0
  58. package/dist/modules/business_rules/notifications.js.map +7 -0
  59. package/dist/modules/business_rules/subscribers/rule-execution-failed-notification.js +37 -0
  60. package/dist/modules/business_rules/subscribers/rule-execution-failed-notification.js.map +7 -0
  61. package/dist/modules/catalog/analytics.js +27 -0
  62. package/dist/modules/catalog/analytics.js.map +7 -0
  63. package/dist/modules/catalog/components/PriceKindSettings.js +2 -0
  64. package/dist/modules/catalog/components/PriceKindSettings.js.map +2 -2
  65. package/dist/modules/catalog/components/categories/CategoriesDataTable.js +2 -2
  66. package/dist/modules/catalog/components/categories/CategoriesDataTable.js.map +2 -2
  67. package/dist/modules/catalog/components/products/ProductsDataTable.js +2 -0
  68. package/dist/modules/catalog/components/products/ProductsDataTable.js.map +2 -2
  69. package/dist/modules/catalog/notifications.js +28 -0
  70. package/dist/modules/catalog/notifications.js.map +7 -0
  71. package/dist/modules/catalog/subscribers/low-stock-notification.js +38 -0
  72. package/dist/modules/catalog/subscribers/low-stock-notification.js.map +7 -0
  73. package/dist/modules/configs/cli.js +6 -0
  74. package/dist/modules/configs/cli.js.map +2 -2
  75. package/dist/modules/configs/lib/upgrade-actions.js +18 -0
  76. package/dist/modules/configs/lib/upgrade-actions.js.map +2 -2
  77. package/dist/modules/currencies/backend/currencies/page.js +3 -0
  78. package/dist/modules/currencies/backend/currencies/page.js.map +2 -2
  79. package/dist/modules/currencies/backend/exchange-rates/page.js +2 -0
  80. package/dist/modules/currencies/backend/exchange-rates/page.js.map +2 -2
  81. package/dist/modules/customers/analytics.js +50 -0
  82. package/dist/modules/customers/analytics.js.map +7 -0
  83. package/dist/modules/customers/backend/customers/companies/page.js +3 -0
  84. package/dist/modules/customers/backend/customers/companies/page.js.map +2 -2
  85. package/dist/modules/customers/backend/customers/deals/page.js +3 -0
  86. package/dist/modules/customers/backend/customers/deals/page.js.map +2 -2
  87. package/dist/modules/customers/backend/customers/people/page.js +3 -0
  88. package/dist/modules/customers/backend/customers/people/page.js.map +2 -2
  89. package/dist/modules/customers/commands/deals.js +31 -0
  90. package/dist/modules/customers/commands/deals.js.map +2 -2
  91. package/dist/modules/customers/components/CustomerTodosTable.js +1 -0
  92. package/dist/modules/customers/components/CustomerTodosTable.js.map +2 -2
  93. package/dist/modules/customers/notifications.js +48 -0
  94. package/dist/modules/customers/notifications.js.map +7 -0
  95. package/dist/modules/dashboards/acl.js +2 -1
  96. package/dist/modules/dashboards/acl.js.map +2 -2
  97. package/dist/modules/dashboards/api/widgets/data/route.js +187 -0
  98. package/dist/modules/dashboards/api/widgets/data/route.js.map +7 -0
  99. package/dist/modules/dashboards/cli.js +173 -1
  100. package/dist/modules/dashboards/cli.js.map +2 -2
  101. package/dist/modules/dashboards/di.js +11 -0
  102. package/dist/modules/dashboards/di.js.map +7 -0
  103. package/dist/modules/dashboards/lib/aggregations.js +162 -0
  104. package/dist/modules/dashboards/lib/aggregations.js.map +7 -0
  105. package/dist/modules/dashboards/lib/formatters.js +34 -0
  106. package/dist/modules/dashboards/lib/formatters.js.map +7 -0
  107. package/dist/modules/dashboards/lib/role-widgets.js +58 -0
  108. package/dist/modules/dashboards/lib/role-widgets.js.map +7 -0
  109. package/dist/modules/dashboards/seed/analytics.js +383 -0
  110. package/dist/modules/dashboards/seed/analytics.js.map +7 -0
  111. package/dist/modules/dashboards/services/analyticsRegistry.js +52 -0
  112. package/dist/modules/dashboards/services/analyticsRegistry.js.map +7 -0
  113. package/dist/modules/dashboards/services/widgetDataService.js +207 -0
  114. package/dist/modules/dashboards/services/widgetDataService.js.map +7 -0
  115. package/dist/modules/dashboards/widgets/dashboard/aov-kpi/config.js +18 -0
  116. package/dist/modules/dashboards/widgets/dashboard/aov-kpi/config.js.map +7 -0
  117. package/dist/modules/dashboards/widgets/dashboard/aov-kpi/widget.client.js +128 -0
  118. package/dist/modules/dashboards/widgets/dashboard/aov-kpi/widget.client.js.map +7 -0
  119. package/dist/modules/dashboards/widgets/dashboard/aov-kpi/widget.js +25 -0
  120. package/dist/modules/dashboards/widgets/dashboard/aov-kpi/widget.js.map +7 -0
  121. package/dist/modules/dashboards/widgets/dashboard/new-customers-kpi/config.js +18 -0
  122. package/dist/modules/dashboards/widgets/dashboard/new-customers-kpi/config.js.map +7 -0
  123. package/dist/modules/dashboards/widgets/dashboard/new-customers-kpi/widget.client.js +126 -0
  124. package/dist/modules/dashboards/widgets/dashboard/new-customers-kpi/widget.client.js.map +7 -0
  125. package/dist/modules/dashboards/widgets/dashboard/new-customers-kpi/widget.js +25 -0
  126. package/dist/modules/dashboards/widgets/dashboard/new-customers-kpi/widget.js.map +7 -0
  127. package/dist/modules/dashboards/widgets/dashboard/orders-by-status/config.js +18 -0
  128. package/dist/modules/dashboards/widgets/dashboard/orders-by-status/config.js.map +7 -0
  129. package/dist/modules/dashboards/widgets/dashboard/orders-by-status/widget.client.js +151 -0
  130. package/dist/modules/dashboards/widgets/dashboard/orders-by-status/widget.client.js.map +7 -0
  131. package/dist/modules/dashboards/widgets/dashboard/orders-by-status/widget.js +25 -0
  132. package/dist/modules/dashboards/widgets/dashboard/orders-by-status/widget.js.map +7 -0
  133. package/dist/modules/dashboards/widgets/dashboard/orders-kpi/config.js +18 -0
  134. package/dist/modules/dashboards/widgets/dashboard/orders-kpi/config.js.map +7 -0
  135. package/dist/modules/dashboards/widgets/dashboard/orders-kpi/widget.client.js +126 -0
  136. package/dist/modules/dashboards/widgets/dashboard/orders-kpi/widget.client.js.map +7 -0
  137. package/dist/modules/dashboards/widgets/dashboard/orders-kpi/widget.js +25 -0
  138. package/dist/modules/dashboards/widgets/dashboard/orders-kpi/widget.js.map +7 -0
  139. package/dist/modules/dashboards/widgets/dashboard/pipeline-summary/config.js +16 -0
  140. package/dist/modules/dashboards/widgets/dashboard/pipeline-summary/config.js.map +7 -0
  141. package/dist/modules/dashboards/widgets/dashboard/pipeline-summary/widget.client.js +123 -0
  142. package/dist/modules/dashboards/widgets/dashboard/pipeline-summary/widget.client.js.map +7 -0
  143. package/dist/modules/dashboards/widgets/dashboard/pipeline-summary/widget.js +25 -0
  144. package/dist/modules/dashboards/widgets/dashboard/pipeline-summary/widget.js.map +7 -0
  145. package/dist/modules/dashboards/widgets/dashboard/revenue-kpi/config.js +18 -0
  146. package/dist/modules/dashboards/widgets/dashboard/revenue-kpi/config.js.map +7 -0
  147. package/dist/modules/dashboards/widgets/dashboard/revenue-kpi/widget.client.js +128 -0
  148. package/dist/modules/dashboards/widgets/dashboard/revenue-kpi/widget.client.js.map +7 -0
  149. package/dist/modules/dashboards/widgets/dashboard/revenue-kpi/widget.js +25 -0
  150. package/dist/modules/dashboards/widgets/dashboard/revenue-kpi/widget.js.map +7 -0
  151. package/dist/modules/dashboards/widgets/dashboard/revenue-trend/config.js +21 -0
  152. package/dist/modules/dashboards/widgets/dashboard/revenue-trend/config.js.map +7 -0
  153. package/dist/modules/dashboards/widgets/dashboard/revenue-trend/widget.client.js +211 -0
  154. package/dist/modules/dashboards/widgets/dashboard/revenue-trend/widget.client.js.map +7 -0
  155. package/dist/modules/dashboards/widgets/dashboard/revenue-trend/widget.js +25 -0
  156. package/dist/modules/dashboards/widgets/dashboard/revenue-trend/widget.js.map +7 -0
  157. package/dist/modules/dashboards/widgets/dashboard/sales-by-region/config.js +19 -0
  158. package/dist/modules/dashboards/widgets/dashboard/sales-by-region/config.js.map +7 -0
  159. package/dist/modules/dashboards/widgets/dashboard/sales-by-region/widget.client.js +131 -0
  160. package/dist/modules/dashboards/widgets/dashboard/sales-by-region/widget.client.js.map +7 -0
  161. package/dist/modules/dashboards/widgets/dashboard/sales-by-region/widget.js +25 -0
  162. package/dist/modules/dashboards/widgets/dashboard/sales-by-region/widget.js.map +7 -0
  163. package/dist/modules/dashboards/widgets/dashboard/top-customers/config.js +19 -0
  164. package/dist/modules/dashboards/widgets/dashboard/top-customers/config.js.map +7 -0
  165. package/dist/modules/dashboards/widgets/dashboard/top-customers/widget.client.js +153 -0
  166. package/dist/modules/dashboards/widgets/dashboard/top-customers/widget.client.js.map +7 -0
  167. package/dist/modules/dashboards/widgets/dashboard/top-customers/widget.js +25 -0
  168. package/dist/modules/dashboards/widgets/dashboard/top-customers/widget.js.map +7 -0
  169. package/dist/modules/dashboards/widgets/dashboard/top-products/config.js +22 -0
  170. package/dist/modules/dashboards/widgets/dashboard/top-products/config.js.map +7 -0
  171. package/dist/modules/dashboards/widgets/dashboard/top-products/widget.client.js +180 -0
  172. package/dist/modules/dashboards/widgets/dashboard/top-products/widget.client.js.map +7 -0
  173. package/dist/modules/dashboards/widgets/dashboard/top-products/widget.js +25 -0
  174. package/dist/modules/dashboards/widgets/dashboard/top-products/widget.js.map +7 -0
  175. package/dist/modules/dictionaries/components/DictionaryTable.js +2 -0
  176. package/dist/modules/dictionaries/components/DictionaryTable.js.map +2 -2
  177. package/dist/modules/directory/backend/directory/organizations/page.js +2 -2
  178. package/dist/modules/directory/backend/directory/organizations/page.js.map +2 -2
  179. package/dist/modules/directory/backend/directory/tenants/page.js +2 -2
  180. package/dist/modules/directory/backend/directory/tenants/page.js.map +2 -2
  181. package/dist/modules/entities/backend/entities/user/[entityId]/records/page.js +2 -2
  182. package/dist/modules/entities/backend/entities/user/[entityId]/records/page.js.map +2 -2
  183. package/dist/modules/entities/components/SystemEntitiesTable.js +1 -1
  184. package/dist/modules/entities/components/SystemEntitiesTable.js.map +2 -2
  185. package/dist/modules/entities/components/UserEntitiesTable.js +2 -2
  186. package/dist/modules/entities/components/UserEntitiesTable.js.map +2 -2
  187. package/dist/modules/feature_toggles/components/FeatureTogglesTable.js +3 -3
  188. package/dist/modules/feature_toggles/components/FeatureTogglesTable.js.map +2 -2
  189. package/dist/modules/feature_toggles/components/OverridesTable.js +1 -1
  190. package/dist/modules/feature_toggles/components/OverridesTable.js.map +2 -2
  191. package/dist/modules/notifications/acl.js +11 -0
  192. package/dist/modules/notifications/acl.js.map +7 -0
  193. package/dist/modules/notifications/api/[id]/action/route.js +74 -0
  194. package/dist/modules/notifications/api/[id]/action/route.js.map +7 -0
  195. package/dist/modules/notifications/api/[id]/dismiss/route.js +15 -0
  196. package/dist/modules/notifications/api/[id]/dismiss/route.js.map +7 -0
  197. package/dist/modules/notifications/api/[id]/read/route.js +15 -0
  198. package/dist/modules/notifications/api/[id]/read/route.js.map +7 -0
  199. package/dist/modules/notifications/api/[id]/restore/route.js +53 -0
  200. package/dist/modules/notifications/api/[id]/restore/route.js.map +7 -0
  201. package/dist/modules/notifications/api/batch/route.js +17 -0
  202. package/dist/modules/notifications/api/batch/route.js.map +7 -0
  203. package/dist/modules/notifications/api/feature/route.js +17 -0
  204. package/dist/modules/notifications/api/feature/route.js.map +7 -0
  205. package/dist/modules/notifications/api/mark-all-read/route.js +35 -0
  206. package/dist/modules/notifications/api/mark-all-read/route.js.map +7 -0
  207. package/dist/modules/notifications/api/openapi.js +76 -0
  208. package/dist/modules/notifications/api/openapi.js.map +7 -0
  209. package/dist/modules/notifications/api/role/route.js +17 -0
  210. package/dist/modules/notifications/api/role/route.js.map +7 -0
  211. package/dist/modules/notifications/api/route.js +85 -0
  212. package/dist/modules/notifications/api/route.js.map +7 -0
  213. package/dist/modules/notifications/api/settings/route.js +155 -0
  214. package/dist/modules/notifications/api/settings/route.js.map +7 -0
  215. package/dist/modules/notifications/api/unread-count/route.js +38 -0
  216. package/dist/modules/notifications/api/unread-count/route.js.map +7 -0
  217. package/dist/modules/notifications/backend/config/notifications/page.js +10 -0
  218. package/dist/modules/notifications/backend/config/notifications/page.js.map +7 -0
  219. package/dist/modules/notifications/backend/config/notifications/page.meta.js +24 -0
  220. package/dist/modules/notifications/backend/config/notifications/page.meta.js.map +7 -0
  221. package/dist/modules/notifications/cli.js +16 -0
  222. package/dist/modules/notifications/cli.js.map +7 -0
  223. package/dist/modules/notifications/data/entities.js +112 -0
  224. package/dist/modules/notifications/data/entities.js.map +7 -0
  225. package/dist/modules/notifications/data/validators.js +94 -0
  226. package/dist/modules/notifications/data/validators.js.map +7 -0
  227. package/dist/modules/notifications/di.js +13 -0
  228. package/dist/modules/notifications/di.js.map +7 -0
  229. package/dist/modules/notifications/emails/NotificationEmail.js +58 -0
  230. package/dist/modules/notifications/emails/NotificationEmail.js.map +7 -0
  231. package/dist/modules/notifications/frontend/NotificationInboxPageClient.js +44 -0
  232. package/dist/modules/notifications/frontend/NotificationInboxPageClient.js.map +7 -0
  233. package/dist/modules/notifications/frontend/NotificationSettingsPageClient.js +219 -0
  234. package/dist/modules/notifications/frontend/NotificationSettingsPageClient.js.map +7 -0
  235. package/dist/modules/notifications/index.js +14 -0
  236. package/dist/modules/notifications/index.js.map +7 -0
  237. package/dist/modules/notifications/lib/deliveryConfig.js +105 -0
  238. package/dist/modules/notifications/lib/deliveryConfig.js.map +7 -0
  239. package/dist/modules/notifications/lib/events.js +12 -0
  240. package/dist/modules/notifications/lib/events.js.map +7 -0
  241. package/dist/modules/notifications/lib/notificationBuilder.js +66 -0
  242. package/dist/modules/notifications/lib/notificationBuilder.js.map +7 -0
  243. package/dist/modules/notifications/lib/notificationFactory.js +54 -0
  244. package/dist/modules/notifications/lib/notificationFactory.js.map +7 -0
  245. package/dist/modules/notifications/lib/notificationMapper.js +34 -0
  246. package/dist/modules/notifications/lib/notificationMapper.js.map +7 -0
  247. package/dist/modules/notifications/lib/notificationRecipients.js +35 -0
  248. package/dist/modules/notifications/lib/notificationRecipients.js.map +7 -0
  249. package/dist/modules/notifications/lib/notificationService.js +279 -0
  250. package/dist/modules/notifications/lib/notificationService.js.map +7 -0
  251. package/dist/modules/notifications/lib/routeHelpers.js +101 -0
  252. package/dist/modules/notifications/lib/routeHelpers.js.map +7 -0
  253. package/dist/modules/notifications/lib/safeHref.js +24 -0
  254. package/dist/modules/notifications/lib/safeHref.js.map +7 -0
  255. package/dist/modules/notifications/migrations/Migration20260123000001.js +70 -0
  256. package/dist/modules/notifications/migrations/Migration20260123000001.js.map +7 -0
  257. package/dist/modules/notifications/migrations/Migration20260126150000.js +37 -0
  258. package/dist/modules/notifications/migrations/Migration20260126150000.js.map +7 -0
  259. package/dist/modules/notifications/subscribers/deliver-notification.js +139 -0
  260. package/dist/modules/notifications/subscribers/deliver-notification.js.map +7 -0
  261. package/dist/modules/notifications/workers/create-notification.worker.js +70 -0
  262. package/dist/modules/notifications/workers/create-notification.worker.js.map +7 -0
  263. package/dist/modules/planner/backend/planner/availability-rulesets/page.js +2 -2
  264. package/dist/modules/planner/backend/planner/availability-rulesets/page.js.map +2 -2
  265. package/dist/modules/query_index/components/QueryIndexesTable.js +7 -1
  266. package/dist/modules/query_index/components/QueryIndexesTable.js.map +2 -2
  267. package/dist/modules/resources/backend/resources/resource-types/page.js +2 -2
  268. package/dist/modules/resources/backend/resources/resource-types/page.js.map +2 -2
  269. package/dist/modules/resources/backend/resources/resources/page.js +2 -2
  270. package/dist/modules/resources/backend/resources/resources/page.js.map +2 -2
  271. package/dist/modules/sales/analytics.js +67 -0
  272. package/dist/modules/sales/analytics.js.map +7 -0
  273. package/dist/modules/sales/backend/sales/channels/offers/page.js +2 -0
  274. package/dist/modules/sales/backend/sales/channels/offers/page.js.map +2 -2
  275. package/dist/modules/sales/backend/sales/channels/page.js +2 -0
  276. package/dist/modules/sales/backend/sales/channels/page.js.map +2 -2
  277. package/dist/modules/sales/commands/documents.js +53 -0
  278. package/dist/modules/sales/commands/documents.js.map +2 -2
  279. package/dist/modules/sales/commands/payments.js +26 -0
  280. package/dist/modules/sales/commands/payments.js.map +2 -2
  281. package/dist/modules/sales/components/AdjustmentKindSettings.js +2 -2
  282. package/dist/modules/sales/components/AdjustmentKindSettings.js.map +2 -2
  283. package/dist/modules/sales/components/PaymentMethodsSettings.js +2 -2
  284. package/dist/modules/sales/components/PaymentMethodsSettings.js.map +2 -2
  285. package/dist/modules/sales/components/ShippingMethodsSettings.js +2 -2
  286. package/dist/modules/sales/components/ShippingMethodsSettings.js.map +2 -2
  287. package/dist/modules/sales/components/TaxRatesSettings.js +2 -2
  288. package/dist/modules/sales/components/TaxRatesSettings.js.map +2 -2
  289. package/dist/modules/sales/components/channels/SalesChannelOffersPanel.js +2 -0
  290. package/dist/modules/sales/components/channels/SalesChannelOffersPanel.js.map +2 -2
  291. package/dist/modules/sales/components/documents/AdjustmentsSection.js +2 -0
  292. package/dist/modules/sales/components/documents/AdjustmentsSection.js.map +2 -2
  293. package/dist/modules/sales/components/documents/PaymentsSection.js +2 -1
  294. package/dist/modules/sales/components/documents/PaymentsSection.js.map +2 -2
  295. package/dist/modules/sales/components/documents/SalesDocumentsTable.js +2 -0
  296. package/dist/modules/sales/components/documents/SalesDocumentsTable.js.map +2 -2
  297. package/dist/modules/sales/notifications.client.js +51 -0
  298. package/dist/modules/sales/notifications.client.js.map +7 -0
  299. package/dist/modules/sales/notifications.js +88 -0
  300. package/dist/modules/sales/notifications.js.map +7 -0
  301. package/dist/modules/sales/subscribers/quote-expiring-notification.js +38 -0
  302. package/dist/modules/sales/subscribers/quote-expiring-notification.js.map +7 -0
  303. package/dist/modules/sales/widgets/notifications/SalesOrderCreatedRenderer.js +137 -0
  304. package/dist/modules/sales/widgets/notifications/SalesOrderCreatedRenderer.js.map +7 -0
  305. package/dist/modules/sales/widgets/notifications/SalesQuoteCreatedRenderer.js +137 -0
  306. package/dist/modules/sales/widgets/notifications/SalesQuoteCreatedRenderer.js.map +7 -0
  307. package/dist/modules/sales/widgets/notifications/index.js +7 -0
  308. package/dist/modules/sales/widgets/notifications/index.js.map +7 -0
  309. package/dist/modules/sales/widgets/notifications/useSalesDocumentTotals.js +60 -0
  310. package/dist/modules/sales/widgets/notifications/useSalesDocumentTotals.js.map +7 -0
  311. package/dist/modules/staff/backend/staff/team-members/page.js +1 -1
  312. package/dist/modules/staff/backend/staff/team-members/page.js.map +2 -2
  313. package/dist/modules/staff/backend/staff/team-roles/page.js +2 -2
  314. package/dist/modules/staff/backend/staff/team-roles/page.js.map +2 -2
  315. package/dist/modules/staff/backend/staff/teams/[id]/edit/page.js +2 -2
  316. package/dist/modules/staff/backend/staff/teams/[id]/edit/page.js.map +2 -2
  317. package/dist/modules/staff/backend/staff/teams/page.js +2 -2
  318. package/dist/modules/staff/backend/staff/teams/page.js.map +2 -2
  319. package/dist/modules/staff/commands/leave-requests.js +79 -0
  320. package/dist/modules/staff/commands/leave-requests.js.map +2 -2
  321. package/dist/modules/staff/notifications.js +75 -0
  322. package/dist/modules/staff/notifications.js.map +7 -0
  323. package/dist/modules/workflows/backend/definitions/page.js +5 -0
  324. package/dist/modules/workflows/backend/definitions/page.js.map +2 -2
  325. package/dist/modules/workflows/backend/instances/page.js +3 -0
  326. package/dist/modules/workflows/backend/instances/page.js.map +2 -2
  327. package/dist/modules/workflows/backend/tasks/page.js +3 -0
  328. package/dist/modules/workflows/backend/tasks/page.js.map +2 -2
  329. package/dist/modules/workflows/notifications.js +28 -0
  330. package/dist/modules/workflows/notifications.js.map +7 -0
  331. package/dist/modules/workflows/subscribers/task-assigned-notification.js +38 -0
  332. package/dist/modules/workflows/subscribers/task-assigned-notification.js.map +7 -0
  333. package/generated/entities/notification/index.ts +27 -0
  334. package/generated/entities.ids.generated.ts +5 -1
  335. package/generated/entity-fields-registry.ts +2 -0
  336. package/package.json +2 -2
  337. package/src/modules/api_docs/frontend/docs/api/page.tsx +3 -2
  338. package/src/modules/api_keys/backend/api-keys/page.tsx +1 -1
  339. package/src/modules/attachments/components/AttachmentLibrary.tsx +4 -0
  340. package/src/modules/attachments/components/AttachmentPartitionSettings.tsx +2 -0
  341. package/src/modules/auth/api/admin/nav.ts +10 -6
  342. package/src/modules/auth/api/profile/route.ts +163 -0
  343. package/src/modules/auth/api/reset/confirm.ts +25 -2
  344. package/src/modules/auth/api/reset.ts +23 -0
  345. package/src/modules/auth/api/sidebar/preferences/route.ts +21 -12
  346. package/src/modules/auth/api/users/route.ts +5 -2
  347. package/src/modules/auth/backend/auth/profile/page.meta.ts +9 -0
  348. package/src/modules/auth/backend/auth/profile/page.tsx +174 -0
  349. package/src/modules/auth/backend/roles/page.tsx +3 -3
  350. package/src/modules/auth/backend/users/[id]/edit/page.tsx +18 -2
  351. package/src/modules/auth/backend/users/create/page.tsx +19 -2
  352. package/src/modules/auth/backend/users/page.tsx +3 -3
  353. package/src/modules/auth/cli.ts +14 -0
  354. package/src/modules/auth/commands/users.ts +73 -2
  355. package/src/modules/auth/data/validators.ts +5 -2
  356. package/src/modules/auth/frontend/reset/[token]/page.tsx +24 -11
  357. package/src/modules/auth/i18n/de.json +43 -1
  358. package/src/modules/auth/i18n/en.json +43 -1
  359. package/src/modules/auth/i18n/es.json +43 -1
  360. package/src/modules/auth/i18n/pl.json +43 -1
  361. package/src/modules/auth/lib/setup-app.ts +3 -0
  362. package/src/modules/auth/notifications.ts +109 -0
  363. package/src/modules/auth/services/authService.ts +4 -4
  364. package/src/modules/business_rules/backend/rules/page.tsx +4 -0
  365. package/src/modules/business_rules/backend/sets/page.tsx +3 -0
  366. package/src/modules/business_rules/i18n/en.json +3 -1
  367. package/src/modules/business_rules/notifications.ts +25 -0
  368. package/src/modules/business_rules/subscribers/rule-execution-failed-notification.ts +50 -0
  369. package/src/modules/catalog/analytics.ts +24 -0
  370. package/src/modules/catalog/components/PriceKindSettings.tsx +2 -0
  371. package/src/modules/catalog/components/categories/CategoriesDataTable.tsx +2 -2
  372. package/src/modules/catalog/components/products/ProductsDataTable.tsx +2 -0
  373. package/src/modules/catalog/i18n/en.json +3 -1
  374. package/src/modules/catalog/notifications.ts +25 -0
  375. package/src/modules/catalog/subscribers/low-stock-notification.ts +52 -0
  376. package/src/modules/configs/cli.ts +6 -0
  377. package/src/modules/configs/lib/upgrade-actions.ts +18 -0
  378. package/src/modules/currencies/backend/currencies/page.tsx +3 -0
  379. package/src/modules/currencies/backend/exchange-rates/page.tsx +2 -0
  380. package/src/modules/customers/analytics.ts +47 -0
  381. package/src/modules/customers/backend/customers/companies/page.tsx +3 -0
  382. package/src/modules/customers/backend/customers/deals/page.tsx +3 -0
  383. package/src/modules/customers/backend/customers/people/page.tsx +3 -0
  384. package/src/modules/customers/commands/deals.ts +39 -0
  385. package/src/modules/customers/components/CustomerTodosTable.tsx +1 -0
  386. package/src/modules/customers/i18n/en.json +5 -1
  387. package/src/modules/customers/notifications.ts +44 -0
  388. package/src/modules/dashboards/acl.ts +1 -0
  389. package/src/modules/dashboards/api/widgets/data/route.ts +221 -0
  390. package/src/modules/dashboards/cli.ts +204 -1
  391. package/src/modules/dashboards/di.ts +9 -0
  392. package/src/modules/dashboards/i18n/de.json +115 -1
  393. package/src/modules/dashboards/i18n/en.json +115 -1
  394. package/src/modules/dashboards/i18n/es.json +115 -1
  395. package/src/modules/dashboards/i18n/pl.json +115 -1
  396. package/src/modules/dashboards/lib/__tests__/aggregations.test.ts +327 -0
  397. package/src/modules/dashboards/lib/__tests__/formatters.test.ts +128 -0
  398. package/src/modules/dashboards/lib/aggregations.ts +225 -0
  399. package/src/modules/dashboards/lib/formatters.ts +36 -0
  400. package/src/modules/dashboards/lib/role-widgets.ts +80 -0
  401. package/src/modules/dashboards/seed/analytics.ts +405 -0
  402. package/src/modules/dashboards/services/analyticsRegistry.ts +79 -0
  403. package/src/modules/dashboards/services/widgetDataService.ts +329 -0
  404. package/src/modules/dashboards/widgets/dashboard/aov-kpi/config.ts +20 -0
  405. package/src/modules/dashboards/widgets/dashboard/aov-kpi/widget.client.tsx +135 -0
  406. package/src/modules/dashboards/widgets/dashboard/aov-kpi/widget.ts +24 -0
  407. package/src/modules/dashboards/widgets/dashboard/new-customers-kpi/config.ts +20 -0
  408. package/src/modules/dashboards/widgets/dashboard/new-customers-kpi/widget.client.tsx +133 -0
  409. package/src/modules/dashboards/widgets/dashboard/new-customers-kpi/widget.ts +24 -0
  410. package/src/modules/dashboards/widgets/dashboard/orders-by-status/config.ts +20 -0
  411. package/src/modules/dashboards/widgets/dashboard/orders-by-status/widget.client.tsx +154 -0
  412. package/src/modules/dashboards/widgets/dashboard/orders-by-status/widget.ts +24 -0
  413. package/src/modules/dashboards/widgets/dashboard/orders-kpi/config.ts +20 -0
  414. package/src/modules/dashboards/widgets/dashboard/orders-kpi/widget.client.tsx +133 -0
  415. package/src/modules/dashboards/widgets/dashboard/orders-kpi/widget.ts +24 -0
  416. package/src/modules/dashboards/widgets/dashboard/pipeline-summary/config.ts +17 -0
  417. package/src/modules/dashboards/widgets/dashboard/pipeline-summary/widget.client.tsx +137 -0
  418. package/src/modules/dashboards/widgets/dashboard/pipeline-summary/widget.ts +24 -0
  419. package/src/modules/dashboards/widgets/dashboard/revenue-kpi/config.ts +20 -0
  420. package/src/modules/dashboards/widgets/dashboard/revenue-kpi/widget.client.tsx +135 -0
  421. package/src/modules/dashboards/widgets/dashboard/revenue-kpi/widget.ts +24 -0
  422. package/src/modules/dashboards/widgets/dashboard/revenue-trend/config.ts +24 -0
  423. package/src/modules/dashboards/widgets/dashboard/revenue-trend/widget.client.tsx +220 -0
  424. package/src/modules/dashboards/widgets/dashboard/revenue-trend/widget.ts +24 -0
  425. package/src/modules/dashboards/widgets/dashboard/sales-by-region/config.ts +21 -0
  426. package/src/modules/dashboards/widgets/dashboard/sales-by-region/widget.client.tsx +131 -0
  427. package/src/modules/dashboards/widgets/dashboard/sales-by-region/widget.ts +24 -0
  428. package/src/modules/dashboards/widgets/dashboard/top-customers/config.ts +21 -0
  429. package/src/modules/dashboards/widgets/dashboard/top-customers/widget.client.tsx +161 -0
  430. package/src/modules/dashboards/widgets/dashboard/top-customers/widget.ts +24 -0
  431. package/src/modules/dashboards/widgets/dashboard/top-products/config.ts +27 -0
  432. package/src/modules/dashboards/widgets/dashboard/top-products/widget.client.tsx +181 -0
  433. package/src/modules/dashboards/widgets/dashboard/top-products/widget.ts +24 -0
  434. package/src/modules/dictionaries/components/DictionaryTable.tsx +2 -0
  435. package/src/modules/directory/backend/directory/organizations/page.tsx +2 -2
  436. package/src/modules/directory/backend/directory/tenants/page.tsx +2 -2
  437. package/src/modules/entities/backend/entities/user/[entityId]/records/page.tsx +2 -2
  438. package/src/modules/entities/components/SystemEntitiesTable.tsx +1 -1
  439. package/src/modules/entities/components/UserEntitiesTable.tsx +2 -2
  440. package/src/modules/feature_toggles/components/FeatureTogglesTable.tsx +3 -4
  441. package/src/modules/feature_toggles/components/OverridesTable.tsx +1 -1
  442. package/src/modules/notifications/acl.ts +7 -0
  443. package/src/modules/notifications/api/[id]/action/route.ts +75 -0
  444. package/src/modules/notifications/api/[id]/dismiss/route.ts +12 -0
  445. package/src/modules/notifications/api/[id]/read/route.ts +12 -0
  446. package/src/modules/notifications/api/[id]/restore/route.ts +53 -0
  447. package/src/modules/notifications/api/batch/route.ts +14 -0
  448. package/src/modules/notifications/api/feature/route.ts +14 -0
  449. package/src/modules/notifications/api/mark-all-read/route.ts +34 -0
  450. package/src/modules/notifications/api/openapi.ts +76 -0
  451. package/src/modules/notifications/api/role/route.ts +14 -0
  452. package/src/modules/notifications/api/route.ts +92 -0
  453. package/src/modules/notifications/api/settings/route.ts +157 -0
  454. package/src/modules/notifications/api/unread-count/route.ts +38 -0
  455. package/src/modules/notifications/backend/config/notifications/page.meta.ts +22 -0
  456. package/src/modules/notifications/backend/config/notifications/page.tsx +12 -0
  457. package/src/modules/notifications/cli.ts +18 -0
  458. package/src/modules/notifications/data/entities.ts +99 -0
  459. package/src/modules/notifications/data/validators.ts +110 -0
  460. package/src/modules/notifications/di.ts +11 -0
  461. package/src/modules/notifications/emails/NotificationEmail.tsx +98 -0
  462. package/src/modules/notifications/frontend/NotificationInboxPageClient.tsx +42 -0
  463. package/src/modules/notifications/frontend/NotificationSettingsPageClient.tsx +231 -0
  464. package/src/modules/notifications/i18n/de.json +50 -0
  465. package/src/modules/notifications/i18n/en.json +50 -0
  466. package/src/modules/notifications/i18n/es.json +50 -0
  467. package/src/modules/notifications/i18n/pl.json +50 -0
  468. package/src/modules/notifications/index.ts +12 -0
  469. package/src/modules/notifications/lib/deliveryConfig.ts +145 -0
  470. package/src/modules/notifications/lib/events.ts +48 -0
  471. package/src/modules/notifications/lib/notificationBuilder.ts +121 -0
  472. package/src/modules/notifications/lib/notificationFactory.ts +76 -0
  473. package/src/modules/notifications/lib/notificationMapper.ts +33 -0
  474. package/src/modules/notifications/lib/notificationRecipients.ts +83 -0
  475. package/src/modules/notifications/lib/notificationService.ts +414 -0
  476. package/src/modules/notifications/lib/routeHelpers.ts +151 -0
  477. package/src/modules/notifications/lib/safeHref.ts +29 -0
  478. package/src/modules/notifications/migrations/.snapshot-open-mercato.json +300 -0
  479. package/src/modules/notifications/migrations/Migration20260123000001.ts +73 -0
  480. package/src/modules/notifications/migrations/Migration20260126150000.ts +39 -0
  481. package/src/modules/notifications/subscribers/deliver-notification.ts +175 -0
  482. package/src/modules/notifications/workers/create-notification.worker.ts +122 -0
  483. package/src/modules/planner/backend/planner/availability-rulesets/page.tsx +2 -2
  484. package/src/modules/query_index/components/QueryIndexesTable.tsx +8 -2
  485. package/src/modules/resources/backend/resources/resource-types/page.tsx +2 -2
  486. package/src/modules/resources/backend/resources/resources/page.tsx +2 -2
  487. package/src/modules/sales/analytics.ts +64 -0
  488. package/src/modules/sales/backend/sales/channels/offers/page.tsx +2 -0
  489. package/src/modules/sales/backend/sales/channels/page.tsx +2 -0
  490. package/src/modules/sales/commands/documents.ts +65 -0
  491. package/src/modules/sales/commands/payments.ts +33 -0
  492. package/src/modules/sales/components/AdjustmentKindSettings.tsx +2 -2
  493. package/src/modules/sales/components/PaymentMethodsSettings.tsx +2 -2
  494. package/src/modules/sales/components/ShippingMethodsSettings.tsx +2 -2
  495. package/src/modules/sales/components/TaxRatesSettings.tsx +2 -2
  496. package/src/modules/sales/components/channels/SalesChannelOffersPanel.tsx +2 -0
  497. package/src/modules/sales/components/documents/AdjustmentsSection.tsx +2 -0
  498. package/src/modules/sales/components/documents/PaymentsSection.tsx +2 -1
  499. package/src/modules/sales/components/documents/SalesDocumentsTable.tsx +2 -0
  500. package/src/modules/sales/i18n/de.json +20 -0
  501. package/src/modules/sales/i18n/en.json +25 -1
  502. package/src/modules/sales/i18n/es.json +20 -0
  503. package/src/modules/sales/i18n/pl.json +20 -0
  504. package/src/modules/sales/notifications.client.ts +65 -0
  505. package/src/modules/sales/notifications.ts +82 -0
  506. package/src/modules/sales/subscribers/quote-expiring-notification.ts +53 -0
  507. package/src/modules/sales/widgets/notifications/SalesOrderCreatedRenderer.tsx +156 -0
  508. package/src/modules/sales/widgets/notifications/SalesQuoteCreatedRenderer.tsx +156 -0
  509. package/src/modules/sales/widgets/notifications/index.ts +2 -0
  510. package/src/modules/sales/widgets/notifications/useSalesDocumentTotals.ts +81 -0
  511. package/src/modules/staff/backend/staff/team-members/page.tsx +1 -1
  512. package/src/modules/staff/backend/staff/team-roles/page.tsx +2 -2
  513. package/src/modules/staff/backend/staff/teams/[id]/edit/page.tsx +2 -2
  514. package/src/modules/staff/backend/staff/teams/page.tsx +2 -2
  515. package/src/modules/staff/commands/leave-requests.ts +94 -0
  516. package/src/modules/staff/i18n/de.json +4 -0
  517. package/src/modules/staff/i18n/en.json +9 -1
  518. package/src/modules/staff/i18n/es.json +4 -0
  519. package/src/modules/staff/i18n/pl.json +4 -0
  520. package/src/modules/staff/notifications.ts +71 -0
  521. package/src/modules/workflows/backend/definitions/page.tsx +5 -0
  522. package/src/modules/workflows/backend/instances/page.tsx +4 -1
  523. package/src/modules/workflows/backend/tasks/page.tsx +4 -1
  524. package/src/modules/workflows/i18n/en.json +3 -1
  525. package/src/modules/workflows/notifications.ts +25 -0
  526. package/src/modules/workflows/subscribers/task-assigned-notification.ts +53 -0
@@ -64,12 +64,16 @@ export async function GET(req: Request) {
64
64
  { tenantId: auth.tenantId ?? null, organizationId: auth.orgId ?? null },
65
65
  ) ?? false
66
66
 
67
- const settings = await loadSidebarPreference(em, {
68
- userId: auth.sub,
69
- tenantId: auth.tenantId ?? null,
70
- organizationId: auth.orgId ?? null,
71
- locale,
72
- })
67
+ // For API key auth, use userId (the actual user) if available
68
+ const effectiveUserId = auth.isApiKey ? auth.userId : auth.sub
69
+ const settings = effectiveUserId
70
+ ? await loadSidebarPreference(em, {
71
+ userId: effectiveUserId,
72
+ tenantId: auth.tenantId ?? null,
73
+ organizationId: auth.orgId ?? null,
74
+ locale,
75
+ })
76
+ : null
73
77
 
74
78
  let rolesPayload: Array<{ id: string; name: string; hasPreference: boolean }> = []
75
79
  if (canApplyToRoles) {
@@ -92,11 +96,11 @@ export async function GET(req: Request) {
92
96
  return NextResponse.json({
93
97
  locale,
94
98
  settings: {
95
- version: settings.version ?? SIDEBAR_PREFERENCES_VERSION,
96
- groupOrder: settings.groupOrder ?? [],
97
- groupLabels: settings.groupLabels ?? {},
98
- itemLabels: settings.itemLabels ?? {},
99
- hiddenItems: settings.hiddenItems ?? [],
99
+ version: settings?.version ?? SIDEBAR_PREFERENCES_VERSION,
100
+ groupOrder: settings?.groupOrder ?? [],
101
+ groupLabels: settings?.groupLabels ?? {},
102
+ itemLabels: settings?.itemLabels ?? {},
103
+ hiddenItems: settings?.hiddenItems ?? [],
100
104
  },
101
105
  canApplyToRoles,
102
106
  roles: rolesPayload,
@@ -106,6 +110,11 @@ export async function GET(req: Request) {
106
110
  export async function PUT(req: Request) {
107
111
  const auth = await getAuthFromRequest(req)
108
112
  if (!auth) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
113
+ // For API key auth, use userId (the actual user) if available
114
+ const effectiveUserId = auth.isApiKey ? auth.userId : auth.sub
115
+ if (!effectiveUserId) {
116
+ return NextResponse.json({ error: 'Cannot save preferences: no user associated with this API key' }, { status: 403 })
117
+ }
109
118
 
110
119
  let parsedBody: unknown
111
120
  try {
@@ -182,7 +191,7 @@ export async function PUT(req: Request) {
182
191
  }
183
192
 
184
193
  const settings = await saveSidebarPreference(em, {
185
- userId: auth.sub,
194
+ userId: effectiveUserId,
186
195
  tenantId: auth.tenantId ?? null,
187
196
  organizationId: auth.orgId ?? null,
188
197
  locale,
@@ -15,6 +15,7 @@ import type { EntityManager } from '@mikro-orm/postgresql'
15
15
  import { userCrudEvents, userCrudIndexer } from '@open-mercato/core/modules/auth/commands/users'
16
16
  import { findWithDecryption } from '@open-mercato/shared/lib/encryption/find'
17
17
  import { escapeLikePattern } from '@open-mercato/shared/lib/db/escapeLikePattern'
18
+ import { buildPasswordSchema } from '@open-mercato/shared/lib/auth/passwordPolicy'
18
19
 
19
20
  const querySchema = z.object({
20
21
  id: z.string().uuid().optional(),
@@ -27,9 +28,11 @@ const querySchema = z.object({
27
28
 
28
29
  const rawBodySchema = z.object({}).passthrough()
29
30
 
31
+ const passwordSchema = buildPasswordSchema()
32
+
30
33
  const userCreateSchema = z.object({
31
34
  email: z.string().email(),
32
- password: z.string().min(6),
35
+ password: passwordSchema,
33
36
  organizationId: z.string().uuid(),
34
37
  roles: z.array(z.string()).optional(),
35
38
  })
@@ -37,7 +40,7 @@ const userCreateSchema = z.object({
37
40
  const userUpdateSchema = z.object({
38
41
  id: z.string().uuid(),
39
42
  email: z.string().email().optional(),
40
- password: z.string().min(6).optional(),
43
+ password: passwordSchema.optional(),
41
44
  organizationId: z.string().uuid().optional(),
42
45
  roles: z.array(z.string()).optional(),
43
46
  })
@@ -0,0 +1,9 @@
1
+ export const metadata = {
2
+ requireAuth: true,
3
+ navHidden: true,
4
+ pageTitle: 'Profile',
5
+ pageTitleKey: 'auth.profile.title',
6
+ breadcrumb: [
7
+ { label: 'Profile', labelKey: 'auth.profile.title' },
8
+ ],
9
+ }
@@ -0,0 +1,174 @@
1
+ "use client"
2
+ import * as React from 'react'
3
+ import { useRouter } from 'next/navigation'
4
+ import { z } from 'zod'
5
+ import { Save } from 'lucide-react'
6
+ import { Page, PageBody } from '@open-mercato/ui/backend/Page'
7
+ import { CrudForm, type CrudField } from '@open-mercato/ui/backend/CrudForm'
8
+ import { apiCall, readApiResultOrThrow } from '@open-mercato/ui/backend/utils/apiCall'
9
+ import { createCrudFormError } from '@open-mercato/ui/backend/utils/serverErrors'
10
+ import { flash } from '@open-mercato/ui/backend/FlashMessages'
11
+ import { LoadingMessage, ErrorMessage } from '@open-mercato/ui/backend/detail'
12
+ import { Button } from '@open-mercato/ui/primitives/button'
13
+ import { useT } from '@open-mercato/shared/lib/i18n/context'
14
+ import { buildPasswordSchema, formatPasswordRequirements, getPasswordPolicy } from '@open-mercato/shared/lib/auth/passwordPolicy'
15
+
16
+ type ProfileResponse = {
17
+ email?: string | null
18
+ }
19
+
20
+ type ProfileUpdateResponse = {
21
+ ok?: boolean
22
+ email?: string | null
23
+ }
24
+
25
+ type ProfileFormValues = {
26
+ email: string
27
+ password: string
28
+ confirmPassword: string
29
+ }
30
+
31
+ export default function AuthProfilePage() {
32
+ const t = useT()
33
+ const router = useRouter()
34
+ const [loading, setLoading] = React.useState(true)
35
+ const [error, setError] = React.useState<string | null>(null)
36
+ const [email, setEmail] = React.useState('')
37
+ const [formKey, setFormKey] = React.useState(0)
38
+ const formId = React.useId()
39
+ const passwordPolicy = React.useMemo(() => getPasswordPolicy(), [])
40
+ const passwordRequirements = React.useMemo(
41
+ () => formatPasswordRequirements(passwordPolicy, t),
42
+ [passwordPolicy, t],
43
+ )
44
+ const passwordDescription = React.useMemo(() => (
45
+ passwordRequirements
46
+ ? t('auth.password.requirements.help', 'Password requirements: {requirements}', { requirements: passwordRequirements })
47
+ : undefined
48
+ ), [passwordRequirements, t])
49
+
50
+ React.useEffect(() => {
51
+ let cancelled = false
52
+ async function load() {
53
+ setLoading(true)
54
+ setError(null)
55
+ try {
56
+ const { ok, result } = await apiCall<ProfileResponse>('/api/auth/profile')
57
+ if (!ok) throw new Error('load_failed')
58
+ const resolvedEmail = typeof result?.email === 'string' ? result.email : ''
59
+ if (!cancelled) setEmail(resolvedEmail)
60
+ } catch (err) {
61
+ console.error('Failed to load auth profile', err)
62
+ if (!cancelled) setError(t('auth.profile.form.errors.load', 'Failed to load profile.'))
63
+ } finally {
64
+ if (!cancelled) setLoading(false)
65
+ }
66
+ }
67
+ load()
68
+ return () => { cancelled = true }
69
+ }, [t])
70
+
71
+ const fields = React.useMemo<CrudField[]>(() => [
72
+ { id: 'email', label: t('auth.profile.form.email', 'Email'), type: 'text', required: true },
73
+ {
74
+ id: 'password',
75
+ label: t('auth.profile.form.password', 'New password'),
76
+ type: 'text',
77
+ description: passwordDescription,
78
+ },
79
+ { id: 'confirmPassword', label: t('auth.profile.form.confirmPassword', 'Confirm new password'), type: 'text' },
80
+ ], [passwordDescription, t])
81
+
82
+ const schema = React.useMemo(() => {
83
+ const passwordSchema = buildPasswordSchema({
84
+ policy: passwordPolicy,
85
+ message: t('auth.profile.form.errors.passwordRequirements', 'Password must meet the requirements.'),
86
+ })
87
+ const optionalPasswordSchema = z.union([z.literal(''), passwordSchema]).optional()
88
+ return z.object({
89
+ email: z.string().trim().min(1, t('auth.profile.form.errors.emailRequired', 'Email is required.')),
90
+ password: optionalPasswordSchema,
91
+ confirmPassword: z.string().optional(),
92
+ }).superRefine((values, ctx) => {
93
+ const password = values.password?.trim() ?? ''
94
+ const confirmPassword = values.confirmPassword?.trim() ?? ''
95
+ if ((password || confirmPassword) && password !== confirmPassword) {
96
+ ctx.addIssue({
97
+ code: z.ZodIssueCode.custom,
98
+ message: t('auth.profile.form.errors.passwordMismatch', 'Passwords do not match.'),
99
+ path: ['confirmPassword'],
100
+ })
101
+ }
102
+ })
103
+ }, [passwordPolicy, t])
104
+
105
+ const handleSubmit = React.useCallback(async (values: ProfileFormValues) => {
106
+ const nextEmail = values.email?.trim() ?? ''
107
+ const password = values.password?.trim() ?? ''
108
+
109
+ if (!password && nextEmail === email) {
110
+ throw createCrudFormError(t('auth.profile.form.errors.noChanges', 'No changes to save.'))
111
+ }
112
+
113
+ const payload: { email: string; password?: string } = { email: nextEmail }
114
+ if (password) payload.password = password
115
+
116
+ const result = await readApiResultOrThrow<ProfileUpdateResponse>(
117
+ '/api/auth/profile',
118
+ {
119
+ method: 'PUT',
120
+ headers: { 'content-type': 'application/json' },
121
+ body: JSON.stringify(payload),
122
+ },
123
+ { errorMessage: t('auth.profile.form.errors.save', 'Failed to update profile.') },
124
+ )
125
+
126
+ const resolvedEmail = typeof result?.email === 'string' ? result.email : nextEmail
127
+ setEmail(resolvedEmail)
128
+ setFormKey((prev) => prev + 1)
129
+ flash(t('auth.profile.form.success', 'Profile updated.'), 'success')
130
+ router.refresh()
131
+ }, [email, router, t])
132
+
133
+ return (
134
+ <Page>
135
+ <PageBody>
136
+ {loading ? (
137
+ <LoadingMessage label={t('auth.profile.form.loading', 'Loading profile...')} />
138
+ ) : error ? (
139
+ <ErrorMessage label={error} />
140
+ ) : (
141
+ <section className="space-y-6 rounded-lg border bg-background p-6">
142
+ <header className="flex flex-col gap-4 sm:flex-row sm:items-start sm:justify-between">
143
+ <div className="space-y-1">
144
+ <h2 className="text-lg font-semibold">{t('auth.profile.title', 'Profile')}</h2>
145
+ <p className="text-sm text-muted-foreground">
146
+ {t('auth.profile.subtitle', 'Change password')}
147
+ </p>
148
+ </div>
149
+ <Button type="submit" form={formId}>
150
+ <Save className="size-4 mr-2" />
151
+ {t('auth.profile.form.save', 'Save changes')}
152
+ </Button>
153
+ </header>
154
+ <CrudForm<ProfileFormValues>
155
+ key={formKey}
156
+ formId={formId}
157
+ schema={schema}
158
+ fields={fields}
159
+ initialValues={{
160
+ email,
161
+ password: '',
162
+ confirmPassword: '',
163
+ }}
164
+ submitLabel={t('auth.profile.form.save', 'Save changes')}
165
+ onSubmit={handleSubmit}
166
+ embedded
167
+ hideFooterActions
168
+ />
169
+ </section>
170
+ )}
171
+ </PageBody>
172
+ </Page>
173
+ )
174
+ }
@@ -117,9 +117,9 @@ export default function RolesListPage() {
117
117
  onSearchChange={(v) => { setSearch(v); setPage(1) }}
118
118
  rowActions={(row) => (
119
119
  <RowActions items={[
120
- { label: t('common.edit', 'Edit'), href: `/backend/roles/${row.id}/edit` },
121
- { label: t('auth.roles.list.actions.showUsers', 'Show users'), href: `/backend/users?roleId=${encodeURIComponent(row.id)}` },
122
- { label: t('common.delete', 'Delete'), destructive: true, onSelect: () => { void handleDelete(row) } },
120
+ { id: 'edit', label: t('common.edit', 'Edit'), href: `/backend/roles/${row.id}/edit` },
121
+ { id: 'show-users', label: t('auth.roles.list.actions.showUsers', 'Show users'), href: `/backend/users?roleId=${encodeURIComponent(row.id)}` },
122
+ { id: 'delete', label: t('common.delete', 'Delete'), destructive: true, onSelect: () => { void handleDelete(row) } },
123
123
  ]} />
124
124
  )}
125
125
  sortable
@@ -12,6 +12,7 @@ import { TenantSelect } from '@open-mercato/core/modules/directory/components/Te
12
12
  import { fetchRoleOptions } from '@open-mercato/core/modules/auth/backend/users/roleOptions'
13
13
  import { WidgetVisibilityEditor } from '@open-mercato/core/modules/dashboards/components/WidgetVisibilityEditor'
14
14
  import { useT } from '@open-mercato/shared/lib/i18n/context'
15
+ import { formatPasswordRequirements, getPasswordPolicy } from '@open-mercato/shared/lib/auth/passwordPolicy'
15
16
 
16
17
  type EditUserFormValues = {
17
18
  email: string
@@ -108,6 +109,16 @@ export default function EditUserPage({ params }: { params?: { id?: string } }) {
108
109
  const [aclData, setAclData] = React.useState<AclData>({ isSuperAdmin: false, features: [], organizations: null })
109
110
  const [customFieldValues, setCustomFieldValues] = React.useState<Record<string, unknown>>({})
110
111
  const [actorIsSuperAdmin, setActorIsSuperAdmin] = React.useState(false)
112
+ const passwordPolicy = React.useMemo(() => getPasswordPolicy(), [])
113
+ const passwordRequirements = React.useMemo(
114
+ () => formatPasswordRequirements(passwordPolicy, t),
115
+ [passwordPolicy, t],
116
+ )
117
+ const passwordDescription = React.useMemo(() => (
118
+ passwordRequirements
119
+ ? t('auth.password.requirements.help', 'Password requirements: {requirements}', { requirements: passwordRequirements })
120
+ : undefined
121
+ ), [passwordRequirements, t])
111
122
 
112
123
  React.useEffect(() => {
113
124
  if (!id) {
@@ -201,7 +212,12 @@ export default function EditUserPage({ params }: { params?: { id?: string } }) {
201
212
  const fields: CrudField[] = React.useMemo(() => {
202
213
  const items: CrudField[] = [
203
214
  { id: 'email', label: t('auth.users.form.field.email', 'Email'), type: 'text', required: true },
204
- { id: 'password', label: t('auth.users.form.field.password', 'Password'), type: 'text' },
215
+ {
216
+ id: 'password',
217
+ label: t('auth.users.form.field.password', 'Password'),
218
+ type: 'text',
219
+ description: passwordDescription,
220
+ },
205
221
  ]
206
222
  if (actorIsSuperAdmin) {
207
223
  items.push({
@@ -251,7 +267,7 @@ export default function EditUserPage({ params }: { params?: { id?: string } }) {
251
267
  })
252
268
  items.push({ id: 'roles', label: t('auth.users.form.field.roles', 'Roles'), type: 'tags', loadOptions: loadRoleOptions })
253
269
  return items
254
- }, [actorIsSuperAdmin, loadRoleOptions, preloadedTenants, selectedOrgId, selectedTenantId, t])
270
+ }, [actorIsSuperAdmin, loadRoleOptions, passwordDescription, preloadedTenants, selectedOrgId, selectedTenantId, t])
255
271
 
256
272
  const detailFieldIds = React.useMemo(() => {
257
273
  const base: string[] = ['email', 'password', 'organizationId', 'roles']
@@ -11,6 +11,7 @@ import { TenantSelect } from '@open-mercato/core/modules/directory/components/Te
11
11
  import { fetchRoleOptions } from '@open-mercato/core/modules/auth/backend/users/roleOptions'
12
12
  import { Spinner } from '@open-mercato/ui/primitives/spinner'
13
13
  import { useT } from '@open-mercato/shared/lib/i18n/context'
14
+ import { formatPasswordRequirements, getPasswordPolicy } from '@open-mercato/shared/lib/auth/passwordPolicy'
14
15
 
15
16
  type CreateUserFormValues = {
16
17
  email: string
@@ -84,6 +85,16 @@ export default function CreateUserPage() {
84
85
  const [selectedWidgets, setSelectedWidgets] = React.useState<string[]>([])
85
86
  const [selectedTenantId, setSelectedTenantId] = React.useState<string | null>(null)
86
87
  const [actorIsSuperAdmin, setActorIsSuperAdmin] = React.useState(false)
88
+ const passwordPolicy = React.useMemo(() => getPasswordPolicy(), [])
89
+ const passwordRequirements = React.useMemo(
90
+ () => formatPasswordRequirements(passwordPolicy, t),
91
+ [passwordPolicy, t],
92
+ )
93
+ const passwordDescription = React.useMemo(() => (
94
+ passwordRequirements
95
+ ? t('auth.password.requirements.help', 'Password requirements: {requirements}', { requirements: passwordRequirements })
96
+ : undefined
97
+ ), [passwordRequirements, t])
87
98
 
88
99
  React.useEffect(() => {
89
100
  let cancelled = false
@@ -156,7 +167,13 @@ export default function CreateUserPage() {
156
167
  const fields: CrudField[] = React.useMemo(() => {
157
168
  const items: CrudField[] = [
158
169
  { id: 'email', label: t('auth.users.form.field.email', 'Email'), type: 'text', required: true },
159
- { id: 'password', label: t('auth.users.form.field.password', 'Password'), type: 'text', required: true },
170
+ {
171
+ id: 'password',
172
+ label: t('auth.users.form.field.password', 'Password'),
173
+ type: 'text',
174
+ required: true,
175
+ description: passwordDescription,
176
+ },
160
177
  ]
161
178
  if (actorIsSuperAdmin) {
162
179
  items.push({
@@ -203,7 +220,7 @@ export default function CreateUserPage() {
203
220
  })
204
221
  items.push({ id: 'roles', label: t('auth.users.form.field.roles', 'Roles'), type: 'tags', loadOptions: loadRoleOptions })
205
222
  return items
206
- }, [actorIsSuperAdmin, loadRoleOptions, selectedTenantId, t])
223
+ }, [actorIsSuperAdmin, loadRoleOptions, passwordDescription, selectedTenantId, t])
207
224
 
208
225
  const detailFieldIds = React.useMemo(() => {
209
226
  const base: string[] = ['email', 'password', 'organizationId', 'roles']
@@ -383,9 +383,9 @@ export default function UsersListPage() {
383
383
  perspective={{ tableId: 'auth.users.list' }}
384
384
  rowActions={(row) => (
385
385
  <RowActions items={[
386
- { label: t('common.edit', 'Edit'), href: `/backend/users/${row.id}/edit` },
387
- { label: t('auth.users.list.actions.showRoles', 'Show roles'), href: `/backend/roles?userId=${encodeURIComponent(row.id)}` },
388
- { label: t('common.delete', 'Delete'), destructive: true, onSelect: () => { void handleDelete(row) } },
386
+ { id: 'edit', label: t('common.edit', 'Edit'), href: `/backend/users/${row.id}/edit` },
387
+ { id: 'show-roles', label: t('auth.users.list.actions.showRoles', 'Show roles'), href: `/backend/roles?userId=${encodeURIComponent(row.id)}` },
388
+ { id: 'delete', label: t('common.delete', 'Delete'), destructive: true, onSelect: () => { void handleDelete(row) } },
389
389
  ]} />
390
390
  )}
391
391
  pagination={{ page, pageSize: 50, total, totalPages, onPageChange: setPage }}
@@ -16,6 +16,7 @@ import { decryptWithAesGcm } from '@open-mercato/shared/lib/encryption/aes'
16
16
  import { env } from 'process'
17
17
  import type { KmsService, TenantDek } from '@open-mercato/shared/lib/encryption/kms'
18
18
  import crypto from 'node:crypto'
19
+ import { formatPasswordRequirements, getPasswordPolicy, validatePassword } from '@open-mercato/shared/lib/auth/passwordPolicy'
19
20
 
20
21
  const addUser: ModuleCli = {
21
22
  command: 'add-user',
@@ -34,6 +35,7 @@ const addUser: ModuleCli = {
34
35
  console.error('Usage: mercato auth add-user --email <email> --password <password> --organizationId <id> [--roles customer,employee]')
35
36
  return
36
37
  }
38
+ if (!ensurePasswordPolicy(password)) return
37
39
  const { resolve } = await createRequestContainer()
38
40
  const em = resolve('em') as any
39
41
  const org =
@@ -102,6 +104,16 @@ function hashSecret(value: string | null | undefined): string | null {
102
104
  return crypto.createHash('sha256').update(normalizeKeyInput(value)).digest('hex').slice(0, 12)
103
105
  }
104
106
 
107
+ function ensurePasswordPolicy(password: string): boolean {
108
+ const policy = getPasswordPolicy()
109
+ const result = validatePassword(password, policy)
110
+ if (result.ok) return true
111
+ const requirements = formatPasswordRequirements(policy, (_key, fallback) => fallback)
112
+ const suffix = requirements ? `: ${requirements}` : ''
113
+ console.error(`Password does not meet the requirements${suffix}.`)
114
+ return false
115
+ }
116
+
105
117
  async function withEncryptionDebugDisabled<T>(fn: () => Promise<T>): Promise<T> {
106
118
  const previous = process.env.TENANT_DATA_ENCRYPTION_DEBUG
107
119
  process.env.TENANT_DATA_ENCRYPTION_DEBUG = 'no'
@@ -406,6 +418,7 @@ const setupApp: ModuleCli = {
406
418
  console.error('Usage: mercato auth setup --orgName <name> --email <email> --password <password> [--roles superadmin,admin,employee]')
407
419
  return
408
420
  }
421
+ if (!ensurePasswordPolicy(password)) return
409
422
  const { resolve } = await createRequestContainer()
410
423
  const em = resolve<EntityManager>('em')
411
424
  const roleNames = rolesCsv
@@ -595,6 +608,7 @@ const setPassword: ModuleCli = {
595
608
  console.error('Usage: mercato auth set-password --email <email> --password <newPassword>')
596
609
  return
597
610
  }
611
+ if (!ensurePasswordPolicy(password)) return
598
612
 
599
613
  const { resolve } = await createRequestContainer()
600
614
  const em = resolve('em') as any
@@ -27,6 +27,10 @@ import {
27
27
  import { normalizeTenantId } from '@open-mercato/core/modules/auth/lib/tenantAccess'
28
28
  import { computeEmailHash } from '@open-mercato/core/modules/auth/lib/emailHash'
29
29
  import { findOneWithDecryption, findWithDecryption } from '@open-mercato/shared/lib/encryption/find'
30
+ import { buildNotificationFromType } from '@open-mercato/core/modules/notifications/lib/notificationBuilder'
31
+ import { resolveNotificationService } from '@open-mercato/core/modules/notifications/lib/notificationService'
32
+ import notificationTypes from '@open-mercato/core/modules/auth/notifications'
33
+ import { buildPasswordSchema } from '@open-mercato/shared/lib/auth/passwordPolicy'
30
34
 
31
35
  type SerializedUser = {
32
36
  email: string
@@ -63,9 +67,11 @@ type UserSnapshots = {
63
67
  undo: UserUndoSnapshot
64
68
  }
65
69
 
70
+ const passwordSchema = buildPasswordSchema()
71
+
66
72
  const createSchema = z.object({
67
73
  email: z.string().email(),
68
- password: z.string().min(6),
74
+ password: passwordSchema,
69
75
  organizationId: z.string().uuid(),
70
76
  roles: z.array(z.string()).optional(),
71
77
  })
@@ -73,7 +79,7 @@ const createSchema = z.object({
73
79
  const updateSchema = z.object({
74
80
  id: z.string().uuid(),
75
81
  email: z.string().email().optional(),
76
- password: z.string().min(6).optional(),
82
+ password: passwordSchema.optional(),
77
83
  organizationId: z.string().uuid().optional(),
78
84
  roles: z.array(z.string()).optional(),
79
85
  })
@@ -105,6 +111,46 @@ export const userCrudIndexer: CrudIndexerConfig = {
105
111
  }),
106
112
  }
107
113
 
114
+ async function notifyRoleChanges(
115
+ ctx: CommandRuntimeContext,
116
+ user: User,
117
+ assignedRoles: string[],
118
+ revokedRoles: string[],
119
+ ): Promise<void> {
120
+ const tenantId = user.tenantId ? String(user.tenantId) : null
121
+ if (!tenantId) return
122
+ const organizationId = user.organizationId ? String(user.organizationId) : null
123
+
124
+ try {
125
+ const notificationService = resolveNotificationService(ctx.container)
126
+ if (assignedRoles.length) {
127
+ const assignedType = notificationTypes.find((type) => type.type === 'auth.role.assigned')
128
+ if (assignedType) {
129
+ const notificationInput = buildNotificationFromType(assignedType, {
130
+ recipientUserId: String(user.id),
131
+ sourceEntityType: 'auth:user',
132
+ sourceEntityId: String(user.id),
133
+ })
134
+ await notificationService.create(notificationInput, { tenantId, organizationId })
135
+ }
136
+ }
137
+
138
+ if (revokedRoles.length) {
139
+ const revokedType = notificationTypes.find((type) => type.type === 'auth.role.revoked')
140
+ if (revokedType) {
141
+ const notificationInput = buildNotificationFromType(revokedType, {
142
+ recipientUserId: String(user.id),
143
+ sourceEntityType: 'auth:user',
144
+ sourceEntityId: String(user.id),
145
+ })
146
+ await notificationService.create(notificationInput, { tenantId, organizationId })
147
+ }
148
+ }
149
+ } catch (err) {
150
+ console.error('[auth.users.roles] Failed to create notification:', err)
151
+ }
152
+ }
153
+
108
154
  const createUserCommand: CommandHandler<Record<string, unknown>, User> = {
109
155
  id: 'auth.users.create',
110
156
  async execute(rawInput, ctx) {
@@ -147,8 +193,10 @@ const createUserCommand: CommandHandler<Record<string, unknown>, User> = {
147
193
  throw error
148
194
  }
149
195
 
196
+ let assignedRoles: string[] = []
150
197
  if (Array.isArray(parsed.roles) && parsed.roles.length) {
151
198
  await syncUserRoles(em, user, parsed.roles, tenantId)
199
+ assignedRoles = await loadUserRoleNames(em, String(user.id))
152
200
  }
153
201
 
154
202
  await setCustomFieldsIfAny({
@@ -173,6 +221,10 @@ const createUserCommand: CommandHandler<Record<string, unknown>, User> = {
173
221
  indexer: userCrudIndexer,
174
222
  })
175
223
 
224
+ if (assignedRoles.length) {
225
+ await notifyRoleChanges(ctx, user, assignedRoles, [])
226
+ }
227
+
176
228
  return user
177
229
  },
178
230
  captureAfter: async (_input, result, ctx) => {
@@ -288,6 +340,9 @@ const updateUserCommand: CommandHandler<Record<string, unknown>, User> = {
288
340
  async execute(rawInput, ctx) {
289
341
  const { parsed, custom } = parseWithCustomFields(updateSchema, rawInput)
290
342
  const em = (ctx.container.resolve('em') as EntityManager)
343
+ const rolesBefore = Array.isArray(parsed.roles)
344
+ ? await loadUserRoleNames(em, parsed.id)
345
+ : null
291
346
 
292
347
  if (parsed.email !== undefined) {
293
348
  const emailHash = computeEmailHash(parsed.email)
@@ -377,6 +432,14 @@ const updateUserCommand: CommandHandler<Record<string, unknown>, User> = {
377
432
  indexer: userCrudIndexer,
378
433
  })
379
434
 
435
+ if (Array.isArray(parsed.roles) && rolesBefore) {
436
+ const rolesAfter = await loadUserRoleNames(em, String(user.id))
437
+ const { assigned, revoked } = diffRoleChanges(rolesBefore, rolesAfter)
438
+ if (assigned.length || revoked.length) {
439
+ await notifyRoleChanges(ctx, user, assigned, revoked)
440
+ }
441
+ }
442
+
380
443
  await invalidateUserCache(ctx, parsed.id)
381
444
 
382
445
  return user
@@ -772,6 +835,14 @@ async function invalidateUserCache(ctx: CommandRuntimeContext, userId: string) {
772
835
  }
773
836
  }
774
837
 
838
+ function diffRoleChanges(before: string[], after: string[]) {
839
+ const beforeSet = new Set(before)
840
+ const afterSet = new Set(after)
841
+ const assigned = after.filter((role) => !beforeSet.has(role))
842
+ const revoked = before.filter((role) => !afterSet.has(role))
843
+ return { assigned, revoked }
844
+ }
845
+
775
846
  function arrayEquals(left: string[] | undefined, right: string[]): boolean {
776
847
  if (!left) return false
777
848
  if (left.length !== right.length) return false
@@ -1,4 +1,7 @@
1
1
  import { z } from 'zod'
2
+ import { buildPasswordSchema } from '@open-mercato/shared/lib/auth/passwordPolicy'
3
+
4
+ const passwordSchema = buildPasswordSchema()
2
5
 
3
6
  // Core auth validators
4
7
  export const userLoginSchema = z.object({
@@ -13,7 +16,7 @@ export const requestPasswordResetSchema = z.object({
13
16
 
14
17
  export const confirmPasswordResetSchema = z.object({
15
18
  token: z.string().min(10),
16
- password: z.string().min(6),
19
+ password: passwordSchema,
17
20
  })
18
21
 
19
22
  export const sidebarPreferencesInputSchema = z.object({
@@ -29,7 +32,7 @@ export const sidebarPreferencesInputSchema = z.object({
29
32
  // Optional helpers for CLI or admin forms
30
33
  export const userCreateSchema = z.object({
31
34
  email: z.string().email(),
32
- password: z.string().min(6),
35
+ password: passwordSchema,
33
36
  tenantId: z.string().uuid().optional(),
34
37
  organizationId: z.string().uuid(),
35
38
  rolesCsv: z.string().optional(),