@open-mercato/core 0.4.2-canary-da2b080494 → 0.4.2-canary-19353c5970

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 (433) 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/[id]/edit/page.js +4 -1
  32. package/dist/modules/auth/backend/roles/[id]/edit/page.js.map +2 -2
  33. package/dist/modules/auth/backend/roles/page.js +3 -3
  34. package/dist/modules/auth/backend/roles/page.js.map +2 -2
  35. package/dist/modules/auth/backend/users/[id]/edit/page.js +18 -3
  36. package/dist/modules/auth/backend/users/[id]/edit/page.js.map +2 -2
  37. package/dist/modules/auth/backend/users/create/page.js +15 -2
  38. package/dist/modules/auth/backend/users/create/page.js.map +2 -2
  39. package/dist/modules/auth/backend/users/page.js +3 -3
  40. package/dist/modules/auth/backend/users/page.js.map +2 -2
  41. package/dist/modules/auth/cli.js +25 -11
  42. package/dist/modules/auth/cli.js.map +2 -2
  43. package/dist/modules/auth/commands/users.js +59 -2
  44. package/dist/modules/auth/commands/users.js.map +2 -2
  45. package/dist/modules/auth/data/validators.js +4 -2
  46. package/dist/modules/auth/data/validators.js.map +2 -2
  47. package/dist/modules/auth/frontend/reset/[token]/page.js +20 -10
  48. package/dist/modules/auth/frontend/reset/[token]/page.js.map +2 -2
  49. package/dist/modules/auth/lib/setup-app.js +23 -2
  50. package/dist/modules/auth/lib/setup-app.js.map +2 -2
  51. package/dist/modules/auth/notifications.js +112 -0
  52. package/dist/modules/auth/notifications.js.map +7 -0
  53. package/dist/modules/auth/services/authService.js +3 -3
  54. package/dist/modules/auth/services/authService.js.map +2 -2
  55. package/dist/modules/business_rules/api/execute/route.js +7 -1
  56. package/dist/modules/business_rules/api/execute/route.js.map +2 -2
  57. package/dist/modules/business_rules/backend/rules/page.js +4 -0
  58. package/dist/modules/business_rules/backend/rules/page.js.map +2 -2
  59. package/dist/modules/business_rules/backend/sets/page.js +3 -0
  60. package/dist/modules/business_rules/backend/sets/page.js.map +2 -2
  61. package/dist/modules/business_rules/lib/rule-engine.js +33 -3
  62. package/dist/modules/business_rules/lib/rule-engine.js.map +2 -2
  63. package/dist/modules/business_rules/notifications.js +28 -0
  64. package/dist/modules/business_rules/notifications.js.map +7 -0
  65. package/dist/modules/business_rules/subscribers/rule-execution-failed-notification.js +37 -0
  66. package/dist/modules/business_rules/subscribers/rule-execution-failed-notification.js.map +7 -0
  67. package/dist/modules/catalog/components/PriceKindSettings.js +2 -0
  68. package/dist/modules/catalog/components/PriceKindSettings.js.map +2 -2
  69. package/dist/modules/catalog/components/categories/CategoriesDataTable.js +2 -2
  70. package/dist/modules/catalog/components/categories/CategoriesDataTable.js.map +2 -2
  71. package/dist/modules/catalog/components/products/ProductsDataTable.js +2 -0
  72. package/dist/modules/catalog/components/products/ProductsDataTable.js.map +2 -2
  73. package/dist/modules/catalog/notifications.js +28 -0
  74. package/dist/modules/catalog/notifications.js.map +7 -0
  75. package/dist/modules/catalog/subscribers/low-stock-notification.js +38 -0
  76. package/dist/modules/catalog/subscribers/low-stock-notification.js.map +7 -0
  77. package/dist/modules/configs/cli.js +6 -0
  78. package/dist/modules/configs/cli.js.map +2 -2
  79. package/dist/modules/configs/components/CachePanel.js +4 -4
  80. package/dist/modules/configs/components/CachePanel.js.map +2 -2
  81. package/dist/modules/configs/lib/system-status.js +48 -1
  82. package/dist/modules/configs/lib/system-status.js.map +2 -2
  83. package/dist/modules/configs/lib/upgrade-actions.js +18 -0
  84. package/dist/modules/configs/lib/upgrade-actions.js.map +2 -2
  85. package/dist/modules/currencies/backend/currencies/page.js +3 -0
  86. package/dist/modules/currencies/backend/currencies/page.js.map +2 -2
  87. package/dist/modules/currencies/backend/exchange-rates/page.js +2 -0
  88. package/dist/modules/currencies/backend/exchange-rates/page.js.map +2 -2
  89. package/dist/modules/customers/backend/customers/companies/page.js +3 -0
  90. package/dist/modules/customers/backend/customers/companies/page.js.map +2 -2
  91. package/dist/modules/customers/backend/customers/deals/page.js +3 -0
  92. package/dist/modules/customers/backend/customers/deals/page.js.map +2 -2
  93. package/dist/modules/customers/backend/customers/people/page.js +3 -0
  94. package/dist/modules/customers/backend/customers/people/page.js.map +2 -2
  95. package/dist/modules/customers/commands/deals.js +31 -0
  96. package/dist/modules/customers/commands/deals.js.map +2 -2
  97. package/dist/modules/customers/components/CustomerTodosTable.js +1 -0
  98. package/dist/modules/customers/components/CustomerTodosTable.js.map +2 -2
  99. package/dist/modules/customers/notifications.js +48 -0
  100. package/dist/modules/customers/notifications.js.map +7 -0
  101. package/dist/modules/dashboards/cli.js +44 -5
  102. package/dist/modules/dashboards/cli.js.map +2 -2
  103. package/dist/modules/dashboards/components/WidgetVisibilityEditor.js +16 -11
  104. package/dist/modules/dashboards/components/WidgetVisibilityEditor.js.map +3 -3
  105. package/dist/modules/dashboards/lib/role-widgets.js +58 -0
  106. package/dist/modules/dashboards/lib/role-widgets.js.map +7 -0
  107. package/dist/modules/dashboards/services/widgetDataService.js +139 -3
  108. package/dist/modules/dashboards/services/widgetDataService.js.map +2 -2
  109. package/dist/modules/dictionaries/components/DictionaryTable.js +2 -0
  110. package/dist/modules/dictionaries/components/DictionaryTable.js.map +2 -2
  111. package/dist/modules/directory/backend/directory/organizations/page.js +2 -2
  112. package/dist/modules/directory/backend/directory/organizations/page.js.map +2 -2
  113. package/dist/modules/directory/backend/directory/tenants/page.js +2 -2
  114. package/dist/modules/directory/backend/directory/tenants/page.js.map +2 -2
  115. package/dist/modules/entities/backend/entities/user/[entityId]/records/page.js +2 -2
  116. package/dist/modules/entities/backend/entities/user/[entityId]/records/page.js.map +2 -2
  117. package/dist/modules/entities/components/SystemEntitiesTable.js +1 -1
  118. package/dist/modules/entities/components/SystemEntitiesTable.js.map +2 -2
  119. package/dist/modules/entities/components/UserEntitiesTable.js +2 -2
  120. package/dist/modules/entities/components/UserEntitiesTable.js.map +2 -2
  121. package/dist/modules/feature_toggles/components/FeatureTogglesTable.js +3 -3
  122. package/dist/modules/feature_toggles/components/FeatureTogglesTable.js.map +2 -2
  123. package/dist/modules/feature_toggles/components/OverridesTable.js +1 -1
  124. package/dist/modules/feature_toggles/components/OverridesTable.js.map +2 -2
  125. package/dist/modules/notifications/acl.js +11 -0
  126. package/dist/modules/notifications/acl.js.map +7 -0
  127. package/dist/modules/notifications/api/[id]/action/route.js +74 -0
  128. package/dist/modules/notifications/api/[id]/action/route.js.map +7 -0
  129. package/dist/modules/notifications/api/[id]/dismiss/route.js +15 -0
  130. package/dist/modules/notifications/api/[id]/dismiss/route.js.map +7 -0
  131. package/dist/modules/notifications/api/[id]/read/route.js +15 -0
  132. package/dist/modules/notifications/api/[id]/read/route.js.map +7 -0
  133. package/dist/modules/notifications/api/[id]/restore/route.js +53 -0
  134. package/dist/modules/notifications/api/[id]/restore/route.js.map +7 -0
  135. package/dist/modules/notifications/api/batch/route.js +17 -0
  136. package/dist/modules/notifications/api/batch/route.js.map +7 -0
  137. package/dist/modules/notifications/api/feature/route.js +17 -0
  138. package/dist/modules/notifications/api/feature/route.js.map +7 -0
  139. package/dist/modules/notifications/api/mark-all-read/route.js +35 -0
  140. package/dist/modules/notifications/api/mark-all-read/route.js.map +7 -0
  141. package/dist/modules/notifications/api/openapi.js +76 -0
  142. package/dist/modules/notifications/api/openapi.js.map +7 -0
  143. package/dist/modules/notifications/api/role/route.js +17 -0
  144. package/dist/modules/notifications/api/role/route.js.map +7 -0
  145. package/dist/modules/notifications/api/route.js +85 -0
  146. package/dist/modules/notifications/api/route.js.map +7 -0
  147. package/dist/modules/notifications/api/settings/route.js +155 -0
  148. package/dist/modules/notifications/api/settings/route.js.map +7 -0
  149. package/dist/modules/notifications/api/unread-count/route.js +38 -0
  150. package/dist/modules/notifications/api/unread-count/route.js.map +7 -0
  151. package/dist/modules/notifications/backend/config/notifications/page.js +10 -0
  152. package/dist/modules/notifications/backend/config/notifications/page.js.map +7 -0
  153. package/dist/modules/notifications/backend/config/notifications/page.meta.js +24 -0
  154. package/dist/modules/notifications/backend/config/notifications/page.meta.js.map +7 -0
  155. package/dist/modules/notifications/cli.js +16 -0
  156. package/dist/modules/notifications/cli.js.map +7 -0
  157. package/dist/modules/notifications/data/entities.js +112 -0
  158. package/dist/modules/notifications/data/entities.js.map +7 -0
  159. package/dist/modules/notifications/data/validators.js +98 -0
  160. package/dist/modules/notifications/data/validators.js.map +7 -0
  161. package/dist/modules/notifications/di.js +13 -0
  162. package/dist/modules/notifications/di.js.map +7 -0
  163. package/dist/modules/notifications/emails/NotificationEmail.js +58 -0
  164. package/dist/modules/notifications/emails/NotificationEmail.js.map +7 -0
  165. package/dist/modules/notifications/frontend/NotificationInboxPageClient.js +44 -0
  166. package/dist/modules/notifications/frontend/NotificationInboxPageClient.js.map +7 -0
  167. package/dist/modules/notifications/frontend/NotificationSettingsPageClient.js +220 -0
  168. package/dist/modules/notifications/frontend/NotificationSettingsPageClient.js.map +7 -0
  169. package/dist/modules/notifications/index.js +14 -0
  170. package/dist/modules/notifications/index.js.map +7 -0
  171. package/dist/modules/notifications/lib/deliveryConfig.js +107 -0
  172. package/dist/modules/notifications/lib/deliveryConfig.js.map +7 -0
  173. package/dist/modules/notifications/lib/deliveryStrategies.js +14 -0
  174. package/dist/modules/notifications/lib/deliveryStrategies.js.map +7 -0
  175. package/dist/modules/notifications/lib/events.js +12 -0
  176. package/dist/modules/notifications/lib/events.js.map +7 -0
  177. package/dist/modules/notifications/lib/notificationBuilder.js +66 -0
  178. package/dist/modules/notifications/lib/notificationBuilder.js.map +7 -0
  179. package/dist/modules/notifications/lib/notificationFactory.js +54 -0
  180. package/dist/modules/notifications/lib/notificationFactory.js.map +7 -0
  181. package/dist/modules/notifications/lib/notificationMapper.js +34 -0
  182. package/dist/modules/notifications/lib/notificationMapper.js.map +7 -0
  183. package/dist/modules/notifications/lib/notificationRecipients.js +35 -0
  184. package/dist/modules/notifications/lib/notificationRecipients.js.map +7 -0
  185. package/dist/modules/notifications/lib/notificationService.js +279 -0
  186. package/dist/modules/notifications/lib/notificationService.js.map +7 -0
  187. package/dist/modules/notifications/lib/routeHelpers.js +101 -0
  188. package/dist/modules/notifications/lib/routeHelpers.js.map +7 -0
  189. package/dist/modules/notifications/lib/safeHref.js +24 -0
  190. package/dist/modules/notifications/lib/safeHref.js.map +7 -0
  191. package/dist/modules/notifications/migrations/Migration20260123000001.js +70 -0
  192. package/dist/modules/notifications/migrations/Migration20260123000001.js.map +7 -0
  193. package/dist/modules/notifications/migrations/Migration20260126150000.js +37 -0
  194. package/dist/modules/notifications/migrations/Migration20260126150000.js.map +7 -0
  195. package/dist/modules/notifications/subscribers/deliver-notification.js +165 -0
  196. package/dist/modules/notifications/subscribers/deliver-notification.js.map +7 -0
  197. package/dist/modules/notifications/workers/create-notification.worker.js +70 -0
  198. package/dist/modules/notifications/workers/create-notification.worker.js.map +7 -0
  199. package/dist/modules/planner/backend/planner/availability-rulesets/page.js +2 -2
  200. package/dist/modules/planner/backend/planner/availability-rulesets/page.js.map +2 -2
  201. package/dist/modules/query_index/components/QueryIndexesTable.js +7 -1
  202. package/dist/modules/query_index/components/QueryIndexesTable.js.map +2 -2
  203. package/dist/modules/resources/backend/resources/resource-types/page.js +2 -2
  204. package/dist/modules/resources/backend/resources/resource-types/page.js.map +2 -2
  205. package/dist/modules/resources/backend/resources/resources/page.js +2 -2
  206. package/dist/modules/resources/backend/resources/resources/page.js.map +2 -2
  207. package/dist/modules/sales/backend/sales/channels/offers/page.js +2 -0
  208. package/dist/modules/sales/backend/sales/channels/offers/page.js.map +2 -2
  209. package/dist/modules/sales/backend/sales/channels/page.js +2 -0
  210. package/dist/modules/sales/backend/sales/channels/page.js.map +2 -2
  211. package/dist/modules/sales/commands/documents.js +53 -0
  212. package/dist/modules/sales/commands/documents.js.map +2 -2
  213. package/dist/modules/sales/commands/payments.js +26 -0
  214. package/dist/modules/sales/commands/payments.js.map +2 -2
  215. package/dist/modules/sales/components/AdjustmentKindSettings.js +2 -2
  216. package/dist/modules/sales/components/AdjustmentKindSettings.js.map +2 -2
  217. package/dist/modules/sales/components/PaymentMethodsSettings.js +2 -2
  218. package/dist/modules/sales/components/PaymentMethodsSettings.js.map +2 -2
  219. package/dist/modules/sales/components/ShippingMethodsSettings.js +2 -2
  220. package/dist/modules/sales/components/ShippingMethodsSettings.js.map +2 -2
  221. package/dist/modules/sales/components/TaxRatesSettings.js +2 -2
  222. package/dist/modules/sales/components/TaxRatesSettings.js.map +2 -2
  223. package/dist/modules/sales/components/channels/SalesChannelOffersPanel.js +2 -0
  224. package/dist/modules/sales/components/channels/SalesChannelOffersPanel.js.map +2 -2
  225. package/dist/modules/sales/components/documents/AdjustmentsSection.js +2 -0
  226. package/dist/modules/sales/components/documents/AdjustmentsSection.js.map +2 -2
  227. package/dist/modules/sales/components/documents/PaymentsSection.js +2 -1
  228. package/dist/modules/sales/components/documents/PaymentsSection.js.map +2 -2
  229. package/dist/modules/sales/components/documents/SalesDocumentsTable.js +2 -0
  230. package/dist/modules/sales/components/documents/SalesDocumentsTable.js.map +2 -2
  231. package/dist/modules/sales/notifications.client.js +51 -0
  232. package/dist/modules/sales/notifications.client.js.map +7 -0
  233. package/dist/modules/sales/notifications.js +88 -0
  234. package/dist/modules/sales/notifications.js.map +7 -0
  235. package/dist/modules/sales/subscribers/quote-expiring-notification.js +38 -0
  236. package/dist/modules/sales/subscribers/quote-expiring-notification.js.map +7 -0
  237. package/dist/modules/sales/widgets/notifications/SalesOrderCreatedRenderer.js +137 -0
  238. package/dist/modules/sales/widgets/notifications/SalesOrderCreatedRenderer.js.map +7 -0
  239. package/dist/modules/sales/widgets/notifications/SalesQuoteCreatedRenderer.js +137 -0
  240. package/dist/modules/sales/widgets/notifications/SalesQuoteCreatedRenderer.js.map +7 -0
  241. package/dist/modules/sales/widgets/notifications/index.js +7 -0
  242. package/dist/modules/sales/widgets/notifications/index.js.map +7 -0
  243. package/dist/modules/sales/widgets/notifications/useSalesDocumentTotals.js +60 -0
  244. package/dist/modules/sales/widgets/notifications/useSalesDocumentTotals.js.map +7 -0
  245. package/dist/modules/staff/backend/staff/team-members/page.js +1 -1
  246. package/dist/modules/staff/backend/staff/team-members/page.js.map +2 -2
  247. package/dist/modules/staff/backend/staff/team-roles/page.js +2 -2
  248. package/dist/modules/staff/backend/staff/team-roles/page.js.map +2 -2
  249. package/dist/modules/staff/backend/staff/teams/[id]/edit/page.js +2 -2
  250. package/dist/modules/staff/backend/staff/teams/[id]/edit/page.js.map +2 -2
  251. package/dist/modules/staff/backend/staff/teams/page.js +2 -2
  252. package/dist/modules/staff/backend/staff/teams/page.js.map +2 -2
  253. package/dist/modules/staff/commands/leave-requests.js +79 -0
  254. package/dist/modules/staff/commands/leave-requests.js.map +2 -2
  255. package/dist/modules/staff/notifications.js +75 -0
  256. package/dist/modules/staff/notifications.js.map +7 -0
  257. package/dist/modules/workflows/backend/definitions/page.js +5 -0
  258. package/dist/modules/workflows/backend/definitions/page.js.map +2 -2
  259. package/dist/modules/workflows/backend/instances/page.js +3 -0
  260. package/dist/modules/workflows/backend/instances/page.js.map +2 -2
  261. package/dist/modules/workflows/backend/tasks/page.js +3 -0
  262. package/dist/modules/workflows/backend/tasks/page.js.map +2 -2
  263. package/dist/modules/workflows/lib/transition-handler.js +14 -6
  264. package/dist/modules/workflows/lib/transition-handler.js.map +2 -2
  265. package/dist/modules/workflows/notifications.js +28 -0
  266. package/dist/modules/workflows/notifications.js.map +7 -0
  267. package/dist/modules/workflows/subscribers/task-assigned-notification.js +38 -0
  268. package/dist/modules/workflows/subscribers/task-assigned-notification.js.map +7 -0
  269. package/generated/entities/notification/index.ts +27 -0
  270. package/generated/entities.ids.generated.ts +5 -1
  271. package/generated/entity-fields-registry.ts +2 -0
  272. package/package.json +2 -2
  273. package/src/modules/api_docs/frontend/docs/api/page.tsx +3 -2
  274. package/src/modules/api_keys/backend/api-keys/page.tsx +1 -1
  275. package/src/modules/attachments/components/AttachmentLibrary.tsx +4 -0
  276. package/src/modules/attachments/components/AttachmentPartitionSettings.tsx +2 -0
  277. package/src/modules/auth/README.md +1 -1
  278. package/src/modules/auth/__tests__/cli-setup-acl.test.ts +1 -1
  279. package/src/modules/auth/api/admin/nav.ts +10 -6
  280. package/src/modules/auth/api/profile/route.ts +163 -0
  281. package/src/modules/auth/api/reset/confirm.ts +25 -2
  282. package/src/modules/auth/api/reset.ts +23 -0
  283. package/src/modules/auth/api/sidebar/preferences/route.ts +21 -12
  284. package/src/modules/auth/api/users/route.ts +5 -2
  285. package/src/modules/auth/backend/auth/profile/page.meta.ts +9 -0
  286. package/src/modules/auth/backend/auth/profile/page.tsx +174 -0
  287. package/src/modules/auth/backend/roles/[id]/edit/page.tsx +4 -1
  288. package/src/modules/auth/backend/roles/page.tsx +3 -3
  289. package/src/modules/auth/backend/users/[id]/edit/page.tsx +22 -3
  290. package/src/modules/auth/backend/users/create/page.tsx +19 -2
  291. package/src/modules/auth/backend/users/page.tsx +3 -3
  292. package/src/modules/auth/cli.ts +38 -11
  293. package/src/modules/auth/commands/users.ts +73 -2
  294. package/src/modules/auth/data/validators.ts +5 -2
  295. package/src/modules/auth/frontend/reset/[token]/page.tsx +24 -11
  296. package/src/modules/auth/i18n/de.json +43 -1
  297. package/src/modules/auth/i18n/en.json +43 -1
  298. package/src/modules/auth/i18n/es.json +43 -1
  299. package/src/modules/auth/i18n/pl.json +43 -1
  300. package/src/modules/auth/lib/setup-app.ts +29 -2
  301. package/src/modules/auth/notifications.ts +109 -0
  302. package/src/modules/auth/services/authService.ts +4 -4
  303. package/src/modules/business_rules/api/execute/route.ts +8 -1
  304. package/src/modules/business_rules/backend/rules/page.tsx +4 -0
  305. package/src/modules/business_rules/backend/sets/page.tsx +3 -0
  306. package/src/modules/business_rules/i18n/en.json +3 -1
  307. package/src/modules/business_rules/lib/__tests__/rule-engine.test.ts +51 -0
  308. package/src/modules/business_rules/lib/rule-engine.ts +57 -3
  309. package/src/modules/business_rules/notifications.ts +25 -0
  310. package/src/modules/business_rules/subscribers/rule-execution-failed-notification.ts +50 -0
  311. package/src/modules/catalog/components/PriceKindSettings.tsx +2 -0
  312. package/src/modules/catalog/components/categories/CategoriesDataTable.tsx +2 -2
  313. package/src/modules/catalog/components/products/ProductsDataTable.tsx +2 -0
  314. package/src/modules/catalog/i18n/en.json +3 -1
  315. package/src/modules/catalog/notifications.ts +25 -0
  316. package/src/modules/catalog/subscribers/low-stock-notification.ts +52 -0
  317. package/src/modules/configs/cli.ts +6 -0
  318. package/src/modules/configs/components/CachePanel.tsx +4 -4
  319. package/src/modules/configs/i18n/en.json +12 -2
  320. package/src/modules/configs/i18n/pl.json +12 -2
  321. package/src/modules/configs/lib/system-status.ts +48 -1
  322. package/src/modules/configs/lib/system-status.types.ts +1 -0
  323. package/src/modules/configs/lib/upgrade-actions.ts +18 -0
  324. package/src/modules/currencies/backend/currencies/page.tsx +3 -0
  325. package/src/modules/currencies/backend/exchange-rates/page.tsx +2 -0
  326. package/src/modules/customers/backend/customers/companies/page.tsx +3 -0
  327. package/src/modules/customers/backend/customers/deals/page.tsx +3 -0
  328. package/src/modules/customers/backend/customers/people/page.tsx +3 -0
  329. package/src/modules/customers/commands/deals.ts +39 -0
  330. package/src/modules/customers/components/CustomerTodosTable.tsx +1 -0
  331. package/src/modules/customers/i18n/en.json +5 -1
  332. package/src/modules/customers/notifications.ts +44 -0
  333. package/src/modules/dashboards/cli.ts +55 -5
  334. package/src/modules/dashboards/components/WidgetVisibilityEditor.tsx +22 -11
  335. package/src/modules/dashboards/lib/role-widgets.ts +80 -0
  336. package/src/modules/dashboards/services/widgetDataService.ts +164 -4
  337. package/src/modules/dictionaries/components/DictionaryTable.tsx +2 -0
  338. package/src/modules/directory/backend/directory/organizations/page.tsx +2 -2
  339. package/src/modules/directory/backend/directory/tenants/page.tsx +2 -2
  340. package/src/modules/entities/backend/entities/user/[entityId]/records/page.tsx +2 -2
  341. package/src/modules/entities/components/SystemEntitiesTable.tsx +1 -1
  342. package/src/modules/entities/components/UserEntitiesTable.tsx +2 -2
  343. package/src/modules/feature_toggles/components/FeatureTogglesTable.tsx +3 -4
  344. package/src/modules/feature_toggles/components/OverridesTable.tsx +1 -1
  345. package/src/modules/notifications/__tests__/deliver-notification.test.ts +195 -0
  346. package/src/modules/notifications/__tests__/deliveryStrategies.test.ts +19 -0
  347. package/src/modules/notifications/__tests__/notificationService.test.ts +208 -0
  348. package/src/modules/notifications/acl.ts +7 -0
  349. package/src/modules/notifications/api/[id]/action/route.ts +75 -0
  350. package/src/modules/notifications/api/[id]/dismiss/route.ts +12 -0
  351. package/src/modules/notifications/api/[id]/read/route.ts +12 -0
  352. package/src/modules/notifications/api/[id]/restore/route.ts +53 -0
  353. package/src/modules/notifications/api/batch/route.ts +14 -0
  354. package/src/modules/notifications/api/feature/route.ts +14 -0
  355. package/src/modules/notifications/api/mark-all-read/route.ts +34 -0
  356. package/src/modules/notifications/api/openapi.ts +76 -0
  357. package/src/modules/notifications/api/role/route.ts +14 -0
  358. package/src/modules/notifications/api/route.ts +92 -0
  359. package/src/modules/notifications/api/settings/route.ts +157 -0
  360. package/src/modules/notifications/api/unread-count/route.ts +38 -0
  361. package/src/modules/notifications/backend/config/notifications/page.meta.ts +22 -0
  362. package/src/modules/notifications/backend/config/notifications/page.tsx +12 -0
  363. package/src/modules/notifications/cli.ts +18 -0
  364. package/src/modules/notifications/data/entities.ts +99 -0
  365. package/src/modules/notifications/data/validators.ts +115 -0
  366. package/src/modules/notifications/di.ts +11 -0
  367. package/src/modules/notifications/emails/NotificationEmail.tsx +98 -0
  368. package/src/modules/notifications/frontend/NotificationInboxPageClient.tsx +42 -0
  369. package/src/modules/notifications/frontend/NotificationSettingsPageClient.tsx +233 -0
  370. package/src/modules/notifications/i18n/de.json +50 -0
  371. package/src/modules/notifications/i18n/en.json +50 -0
  372. package/src/modules/notifications/i18n/es.json +50 -0
  373. package/src/modules/notifications/i18n/pl.json +50 -0
  374. package/src/modules/notifications/index.ts +12 -0
  375. package/src/modules/notifications/lib/deliveryConfig.ts +153 -0
  376. package/src/modules/notifications/lib/deliveryStrategies.ts +50 -0
  377. package/src/modules/notifications/lib/events.ts +48 -0
  378. package/src/modules/notifications/lib/notificationBuilder.ts +121 -0
  379. package/src/modules/notifications/lib/notificationFactory.ts +76 -0
  380. package/src/modules/notifications/lib/notificationMapper.ts +33 -0
  381. package/src/modules/notifications/lib/notificationRecipients.ts +83 -0
  382. package/src/modules/notifications/lib/notificationService.ts +414 -0
  383. package/src/modules/notifications/lib/routeHelpers.ts +151 -0
  384. package/src/modules/notifications/lib/safeHref.ts +29 -0
  385. package/src/modules/notifications/migrations/.snapshot-open-mercato.json +300 -0
  386. package/src/modules/notifications/migrations/Migration20260123000001.ts +73 -0
  387. package/src/modules/notifications/migrations/Migration20260126150000.ts +39 -0
  388. package/src/modules/notifications/subscribers/deliver-notification.ts +204 -0
  389. package/src/modules/notifications/workers/create-notification.worker.ts +122 -0
  390. package/src/modules/planner/backend/planner/availability-rulesets/page.tsx +2 -2
  391. package/src/modules/query_index/components/QueryIndexesTable.tsx +8 -2
  392. package/src/modules/resources/backend/resources/resource-types/page.tsx +2 -2
  393. package/src/modules/resources/backend/resources/resources/page.tsx +2 -2
  394. package/src/modules/sales/backend/sales/channels/offers/page.tsx +2 -0
  395. package/src/modules/sales/backend/sales/channels/page.tsx +2 -0
  396. package/src/modules/sales/commands/documents.ts +65 -0
  397. package/src/modules/sales/commands/payments.ts +33 -0
  398. package/src/modules/sales/components/AdjustmentKindSettings.tsx +2 -2
  399. package/src/modules/sales/components/PaymentMethodsSettings.tsx +2 -2
  400. package/src/modules/sales/components/ShippingMethodsSettings.tsx +2 -2
  401. package/src/modules/sales/components/TaxRatesSettings.tsx +2 -2
  402. package/src/modules/sales/components/channels/SalesChannelOffersPanel.tsx +2 -0
  403. package/src/modules/sales/components/documents/AdjustmentsSection.tsx +2 -0
  404. package/src/modules/sales/components/documents/PaymentsSection.tsx +2 -1
  405. package/src/modules/sales/components/documents/SalesDocumentsTable.tsx +2 -0
  406. package/src/modules/sales/i18n/de.json +20 -0
  407. package/src/modules/sales/i18n/en.json +25 -1
  408. package/src/modules/sales/i18n/es.json +20 -0
  409. package/src/modules/sales/i18n/pl.json +20 -0
  410. package/src/modules/sales/notifications.client.ts +65 -0
  411. package/src/modules/sales/notifications.ts +82 -0
  412. package/src/modules/sales/subscribers/quote-expiring-notification.ts +53 -0
  413. package/src/modules/sales/widgets/notifications/SalesOrderCreatedRenderer.tsx +156 -0
  414. package/src/modules/sales/widgets/notifications/SalesQuoteCreatedRenderer.tsx +156 -0
  415. package/src/modules/sales/widgets/notifications/index.ts +2 -0
  416. package/src/modules/sales/widgets/notifications/useSalesDocumentTotals.ts +81 -0
  417. package/src/modules/staff/backend/staff/team-members/page.tsx +1 -1
  418. package/src/modules/staff/backend/staff/team-roles/page.tsx +2 -2
  419. package/src/modules/staff/backend/staff/teams/[id]/edit/page.tsx +2 -2
  420. package/src/modules/staff/backend/staff/teams/page.tsx +2 -2
  421. package/src/modules/staff/commands/leave-requests.ts +94 -0
  422. package/src/modules/staff/i18n/de.json +4 -0
  423. package/src/modules/staff/i18n/en.json +9 -1
  424. package/src/modules/staff/i18n/es.json +4 -0
  425. package/src/modules/staff/i18n/pl.json +4 -0
  426. package/src/modules/staff/notifications.ts +71 -0
  427. package/src/modules/workflows/backend/definitions/page.tsx +5 -0
  428. package/src/modules/workflows/backend/instances/page.tsx +4 -1
  429. package/src/modules/workflows/backend/tasks/page.tsx +4 -1
  430. package/src/modules/workflows/i18n/en.json +3 -1
  431. package/src/modules/workflows/lib/transition-handler.ts +18 -6
  432. package/src/modules/workflows/notifications.ts +25 -0
  433. package/src/modules/workflows/subscribers/task-assigned-notification.ts +53 -0
@@ -2,8 +2,20 @@
2
2
  "auth.signIn": "Zaloguj się",
3
3
  "auth.email": "Email",
4
4
  "auth.password": "Hasło",
5
+ "auth.password.requirements.help": "Wymagania hasła: {requirements}",
6
+ "auth.password.requirements.minLength": "Co najmniej {min} znaków",
7
+ "auth.password.requirements.digit": "Jedna cyfra",
8
+ "auth.password.requirements.uppercase": "Jedna wielka litera",
9
+ "auth.password.requirements.special": "Jeden znak specjalny",
10
+ "auth.password.requirements.separator": ", ",
5
11
  "auth.sendResetLink": "Wyślij link resetujący",
6
12
  "auth.resetPassword": "Resetuj hasło",
13
+ "auth.reset.title": "Ustaw nowe hasło",
14
+ "auth.reset.subtitle": "Wybierz silne hasło dla swojego konta.",
15
+ "auth.reset.form.password": "Nowe hasło",
16
+ "auth.reset.form.loading": "...",
17
+ "auth.reset.form.submit": "Zaktualizuj hasło",
18
+ "auth.reset.errors.failed": "Nie udało się zresetować hasła",
7
19
  "auth.usersRoles": "Użytkownicy i role",
8
20
  "auth.manageAuthSettings": "Zarządzaj ustawieniami uwierzytelniania.",
9
21
  "auth.login.errors.permissionDenied": "Nie masz uprawnień do tego obszaru. Skontaktuj się z administratorem.",
@@ -80,6 +92,21 @@
80
92
  "auth.users.form.errors.load": "Nie udało się wczytać danych użytkownika",
81
93
  "auth.users.form.errors.aclUpdate": "Nie udało się zaktualizować uprawnień użytkownika",
82
94
  "auth.users.form.errors.delete": "Nie udało się usunąć użytkownika",
95
+ "auth.profile.title": "Profil",
96
+ "auth.profile.subtitle": "Zmiana hasła",
97
+ "auth.profile.form.email": "Email",
98
+ "auth.profile.form.password": "Nowe hasło",
99
+ "auth.profile.form.confirmPassword": "Potwierdź nowe hasło",
100
+ "auth.profile.form.save": "Zapisz zmiany",
101
+ "auth.profile.form.loading": "Ładowanie profilu...",
102
+ "auth.profile.form.errors.load": "Nie udało się wczytać profilu.",
103
+ "auth.profile.form.errors.save": "Nie udało się zaktualizować profilu.",
104
+ "auth.profile.form.errors.invalid": "Nieprawidłowa aktualizacja profilu.",
105
+ "auth.profile.form.errors.passwordMismatch": "Hasła nie są zgodne.",
106
+ "auth.profile.form.errors.passwordRequirements": "Hasło musi spełniać wymagania.",
107
+ "auth.profile.form.errors.noChanges": "Brak zmian do zapisania.",
108
+ "auth.profile.form.errors.emailRequired": "Email jest wymagany.",
109
+ "auth.profile.form.success": "Profil zaktualizowany.",
83
110
  "auth.users.list.error.load": "Nie udało się wczytać użytkowników",
84
111
  "auth.users.list.error.delete": "Nie udało się usunąć użytkownika",
85
112
  "auth.users.flash.created": "Użytkownik utworzony",
@@ -95,5 +122,20 @@
95
122
  "auth.email.resetPassword.title": "Zresetuj swoje hasło",
96
123
  "auth.email.resetPassword.body": "Kliknij poniższy link, aby ustawić nowe hasło. Link wygaśnie za 60 minut.",
97
124
  "auth.email.resetPassword.cta": "Ustaw nowe hasło",
98
- "auth.email.resetPassword.hint": "Jeśli nie prosiłeś o tę wiadomość, możesz ją bezpiecznie zignorować."
125
+ "auth.email.resetPassword.hint": "Jeśli nie prosiłeś o tę wiadomość, możesz ją bezpiecznie zignorować.",
126
+ "auth.notifications.passwordReset.requested.title": "Żądanie resetu hasła",
127
+ "auth.notifications.passwordReset.requested.body": "Link do resetu hasła został wysłany na Twój adres e-mail",
128
+ "auth.notifications.passwordReset.completed.title": "Hasło zmienione pomyślnie",
129
+ "auth.notifications.passwordReset.completed.body": "Twoje hasło zostało pomyślnie zaktualizowane",
130
+ "auth.notifications.account.locked.title": "Konto zablokowane",
131
+ "auth.notifications.account.locked.body": "Twoje konto zostało zablokowane z powodów bezpieczeństwa. Skontaktuj się z pomocą techniczną.",
132
+ "auth.notifications.login.newDevice.title": "Wykryto logowanie z nowego urządzenia",
133
+ "auth.notifications.login.newDevice.body": "Wykryto logowanie z nieznanego urządzenia na Twoim koncie",
134
+ "auth.notifications.role.assigned.title": "Przypisano nową rolę",
135
+ "auth.notifications.role.assigned.body": "Przypisano Ci nową rolę z dodatkowymi uprawnieniami",
136
+ "auth.notifications.role.revoked.title": "Rola usunięta",
137
+ "auth.notifications.role.revoked.body": "Z Twojego konta usunięto rolę",
138
+ "auth.actions.contactSupport": "Skontaktuj się z pomocą",
139
+ "auth.actions.viewSessions": "Zobacz sesje",
140
+ "auth.actions.viewPermissions": "Zobacz uprawnienia"
99
141
  }
@@ -87,6 +87,11 @@ type PrimaryUserInput = {
87
87
  confirm?: boolean
88
88
  }
89
89
 
90
+ const DERIVED_EMAIL_ENV = {
91
+ admin: 'OM_INIT_ADMIN_EMAIL',
92
+ employee: 'OM_INIT_EMPLOYEE_EMAIL',
93
+ } as const
94
+
90
95
  export type SetupInitialTenantOptions = {
91
96
  orgName: string
92
97
  primaryUser: PrimaryUserInput
@@ -177,8 +182,12 @@ export async function setupInitialTenant(
177
182
  const [local, domain] = String(primaryUser.email).split('@')
178
183
  const isSuperadminLocal = (local || '').toLowerCase() === 'superadmin' && !!domain
179
184
  if (isSuperadminLocal) {
180
- baseUsers.push({ email: `admin@${domain}`, roles: ['admin'] })
181
- baseUsers.push({ email: `employee@${domain}`, roles: ['employee'] })
185
+ const adminOverride = readEnvValue(DERIVED_EMAIL_ENV.admin)
186
+ const employeeOverride = readEnvValue(DERIVED_EMAIL_ENV.employee)
187
+ const adminEmail = adminOverride ?? `admin@${domain}`
188
+ const employeeEmail = employeeOverride ?? `employee@${domain}`
189
+ addUniqueBaseUser(baseUsers, { email: adminEmail, roles: ['admin'] })
190
+ addUniqueBaseUser(baseUsers, { email: employeeEmail, roles: ['employee'] })
182
191
  }
183
192
  }
184
193
  const passwordHash = await resolvePasswordHash(primaryUser)
@@ -340,6 +349,23 @@ function resolvePrimaryName(input: PrimaryUserInput): string | null {
340
349
  return null
341
350
  }
342
351
 
352
+ function readEnvValue(key: string): string | undefined {
353
+ const value = process.env[key]
354
+ if (typeof value !== 'string') return undefined
355
+ const trimmed = value.trim()
356
+ return trimmed.length > 0 ? trimmed : undefined
357
+ }
358
+
359
+ function addUniqueBaseUser(
360
+ baseUsers: Array<{ email: string; roles: string[]; name?: string | null }>,
361
+ entry: { email: string; roles: string[]; name?: string | null },
362
+ ) {
363
+ if (!entry.email) return
364
+ const normalized = entry.email.toLowerCase()
365
+ if (baseUsers.some((user) => user.email.toLowerCase() === normalized)) return
366
+ baseUsers.push(entry)
367
+ }
368
+
343
369
  async function resolvePasswordHash(input: PrimaryUserInput): Promise<string | null> {
344
370
  if (typeof input.hashedPassword === 'string') return input.hashedPassword
345
371
  if (input.password) return hash(input.password, 10)
@@ -396,6 +422,7 @@ async function ensureDefaultRoleAcls(
396
422
  'dashboards.admin.assign-widgets',
397
423
  'analytics.view',
398
424
  'api_keys.*',
425
+ 'notifications.manage',
399
426
  'perspectives.use',
400
427
  'perspectives.role_defaults',
401
428
  'business_rules.*',
@@ -0,0 +1,109 @@
1
+ import type { NotificationTypeDefinition } from '@open-mercato/shared/modules/notifications/types'
2
+
3
+ export const notificationTypes: NotificationTypeDefinition[] = [
4
+ {
5
+ type: 'auth.password_reset.requested',
6
+ module: 'auth',
7
+ titleKey: 'auth.notifications.passwordReset.requested.title',
8
+ bodyKey: 'auth.notifications.passwordReset.requested.body',
9
+ icon: 'key',
10
+ severity: 'info',
11
+ actions: [
12
+ {
13
+ id: 'view',
14
+ labelKey: 'common.view',
15
+ variant: 'outline',
16
+ href: '/backend/auth/profile',
17
+ icon: 'external-link',
18
+ },
19
+ ],
20
+ linkHref: '/backend/auth/profile',
21
+ expiresAfterHours: 24,
22
+ },
23
+ {
24
+ type: 'auth.password_reset.completed',
25
+ module: 'auth',
26
+ titleKey: 'auth.notifications.passwordReset.completed.title',
27
+ bodyKey: 'auth.notifications.passwordReset.completed.body',
28
+ icon: 'check-circle',
29
+ severity: 'success',
30
+ actions: [],
31
+ expiresAfterHours: 72,
32
+ },
33
+ {
34
+ type: 'auth.account.locked',
35
+ module: 'auth',
36
+ titleKey: 'auth.notifications.account.locked.title',
37
+ bodyKey: 'auth.notifications.account.locked.body',
38
+ icon: 'lock',
39
+ severity: 'warning',
40
+ actions: [
41
+ {
42
+ id: 'contact_support',
43
+ labelKey: 'auth.actions.contactSupport',
44
+ variant: 'default',
45
+ href: '/backend/support',
46
+ icon: 'mail',
47
+ },
48
+ ],
49
+ linkHref: '/backend/support',
50
+ },
51
+ {
52
+ type: 'auth.login.new_device',
53
+ module: 'auth',
54
+ titleKey: 'auth.notifications.login.newDevice.title',
55
+ bodyKey: 'auth.notifications.login.newDevice.body',
56
+ icon: 'smartphone',
57
+ severity: 'info',
58
+ actions: [
59
+ {
60
+ id: 'view_sessions',
61
+ labelKey: 'auth.actions.viewSessions',
62
+ variant: 'outline',
63
+ href: '/backend/auth/sessions',
64
+ icon: 'list',
65
+ },
66
+ ],
67
+ linkHref: '/backend/auth/sessions',
68
+ expiresAfterHours: 168, // 7 days
69
+ },
70
+ {
71
+ type: 'auth.role.assigned',
72
+ module: 'auth',
73
+ titleKey: 'auth.notifications.role.assigned.title',
74
+ bodyKey: 'auth.notifications.role.assigned.body',
75
+ icon: 'user-plus',
76
+ severity: 'success',
77
+ actions: [
78
+ {
79
+ id: 'view_permissions',
80
+ labelKey: 'auth.actions.viewPermissions',
81
+ variant: 'outline',
82
+ href: '/backend/auth/profile',
83
+ icon: 'shield',
84
+ },
85
+ ],
86
+ linkHref: '/backend/auth/profile',
87
+ expiresAfterHours: 168,
88
+ },
89
+ {
90
+ type: 'auth.role.revoked',
91
+ module: 'auth',
92
+ titleKey: 'auth.notifications.role.revoked.title',
93
+ bodyKey: 'auth.notifications.role.revoked.body',
94
+ icon: 'user-minus',
95
+ severity: 'warning',
96
+ actions: [
97
+ {
98
+ id: 'view_profile',
99
+ labelKey: 'common.view',
100
+ variant: 'outline',
101
+ href: '/backend/auth/profile',
102
+ },
103
+ ],
104
+ linkHref: '/backend/auth/profile',
105
+ expiresAfterHours: 168,
106
+ },
107
+ ]
108
+
109
+ export default notificationTypes
@@ -75,15 +75,15 @@ export class AuthService {
75
75
  return { user, token }
76
76
  }
77
77
 
78
- async confirmPasswordReset(token: string, newPassword: string) {
78
+ async confirmPasswordReset(token: string, newPassword: string): Promise<User | null> {
79
79
  const now = new Date()
80
80
  const row = await this.em.findOne(PasswordReset, { token })
81
- if (!row || (row.usedAt && row.usedAt <= now) || row.expiresAt <= now) return false
81
+ if (!row || (row.usedAt && row.usedAt <= now) || row.expiresAt <= now) return null
82
82
  const user = await this.em.findOne(User, { id: row.user.id })
83
- if (!user) return false
83
+ if (!user) return null
84
84
  user.passwordHash = await hash(newPassword, 10)
85
85
  row.usedAt = new Date()
86
86
  await this.em.flush()
87
- return true
87
+ return user
88
88
  }
89
89
  }
@@ -4,6 +4,7 @@ import type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'
4
4
  import { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'
5
5
  import { createRequestContainer } from '@open-mercato/shared/lib/di/container'
6
6
  import type { EntityManager } from '@mikro-orm/postgresql'
7
+ import type { EventBus } from '@open-mercato/events'
7
8
  import { ruleEngineContextSchema } from '../../data/validators'
8
9
  import * as ruleEngine from '../../lib/rule-engine'
9
10
 
@@ -46,6 +47,12 @@ export async function POST(req: Request) {
46
47
 
47
48
  const container = await createRequestContainer()
48
49
  const em = container.resolve('em') as EntityManager
50
+ let eventBus: EventBus | null = null
51
+ try {
52
+ eventBus = container.resolve('eventBus') as EventBus
53
+ } catch {
54
+ eventBus = null
55
+ }
49
56
 
50
57
  let body: any
51
58
  try {
@@ -85,7 +92,7 @@ export async function POST(req: Request) {
85
92
  }
86
93
 
87
94
  try {
88
- const result = await ruleEngine.executeRules(em, context)
95
+ const result = await ruleEngine.executeRules(em, context, { eventBus })
89
96
 
90
97
  const response = {
91
98
  allowed: result.allowed,
@@ -264,14 +264,17 @@ export default function RulesListPage() {
264
264
  <RowActions
265
265
  items={[
266
266
  {
267
+ id: 'edit',
267
268
  label: t('common.edit'),
268
269
  href: `/backend/rules/${row.original.id}`,
269
270
  },
270
271
  {
272
+ id: row.original.enabled ? 'disable' : 'enable',
271
273
  label: row.original.enabled ? t('common.disable') : t('common.enable'),
272
274
  onSelect: () => handleToggleEnabled(row.original.id, row.original.enabled),
273
275
  },
274
276
  {
277
+ id: 'duplicate',
275
278
  label: t('common.duplicate'),
276
279
  onSelect: () => {
277
280
  // TODO: Implement duplicate functionality in Step 5.2
@@ -279,6 +282,7 @@ export default function RulesListPage() {
279
282
  },
280
283
  },
281
284
  {
285
+ id: 'delete',
282
286
  label: t('common.delete'),
283
287
  onSelect: () => handleDelete(row.original.id, row.original.ruleName),
284
288
  destructive: true,
@@ -196,14 +196,17 @@ export default function RuleSetsListPage() {
196
196
  <RowActions
197
197
  items={[
198
198
  {
199
+ id: 'edit',
199
200
  label: t('common.edit'),
200
201
  href: `/backend/sets/${row.original.id}`,
201
202
  },
202
203
  {
204
+ id: row.original.enabled ? 'disable' : 'enable',
203
205
  label: row.original.enabled ? t('common.disable') : t('common.enable'),
204
206
  onSelect: () => handleToggleEnabled(row.original.id, row.original.enabled),
205
207
  },
206
208
  {
209
+ id: 'delete',
207
210
  label: t('common.delete'),
208
211
  onSelect: () => handleDelete(row.original.id, row.original.setName),
209
212
  destructive: true,
@@ -367,5 +367,7 @@
367
367
  "business_rules.components.conditionRow.field.comparisonPlaceholder": "e.g., user.role",
368
368
  "business_rules.components.conditionRow.value.help": "Use JSON for arrays: [\"a\",\"b\"]",
369
369
  "business_rules.components.conditionRow.field.comparisonHelp": "Field path to compare with",
370
- "business_rules.components.conditionRow.deleteCondition": "Delete condition"
370
+ "business_rules.components.conditionRow.deleteCondition": "Delete condition",
371
+ "businessRules.notifications.rule.executionFailed.title": "Business Rule Failed",
372
+ "businessRules.notifications.rule.executionFailed.body": "Rule \"{ruleName}\" failed to execute{entityType, select, other { on {entityType}}}: {errorMessage}"
371
373
  }
@@ -654,6 +654,57 @@ describe('Rule Engine (Unit Tests)', () => {
654
654
  expect(result.errors).toBeDefined()
655
655
  expect(result.errors![0]).toContain('Rule count limit exceeded')
656
656
  })
657
+
658
+ test('should emit execution_failed event when rule evaluation fails', async () => {
659
+ const mockRule: Partial<BusinessRule> = {
660
+ id: 'rule-1',
661
+ ruleId: 'TEST-001',
662
+ ruleName: 'Test Rule',
663
+ ruleType: 'ACTION',
664
+ entityType: 'WorkOrder',
665
+ conditionExpression: { field: 'status', operator: '=', value: 'RELEASED' },
666
+ enabled: true,
667
+ tenantId: testTenantId,
668
+ organizationId: testOrgId,
669
+ }
670
+
671
+ mockEm.find.mockResolvedValue([mockRule as BusinessRule])
672
+ mockEm.create.mockReturnValue({ id: 'log-1' } as any)
673
+ mockEm.persistAndFlush.mockResolvedValue(undefined)
674
+
675
+ jest.mocked(ruleEvaluator.evaluateSingleRule).mockResolvedValue({
676
+ rule: mockRule as BusinessRule,
677
+ conditionsPassed: false,
678
+ evaluationCompleted: false,
679
+ evaluationTime: 1,
680
+ error: 'Evaluation error',
681
+ })
682
+
683
+ const eventBus = { emitEvent: jest.fn().mockResolvedValue(undefined) }
684
+
685
+ const context: RuleEngineContext = {
686
+ entityType: 'WorkOrder',
687
+ entityId: testEntityId,
688
+ data: { status: 'RELEASED' },
689
+ tenantId: testTenantId,
690
+ organizationId: testOrgId,
691
+ dryRun: false,
692
+ }
693
+
694
+ await ruleEngine.executeRules(mockEm, context, { eventBus })
695
+
696
+ expect(eventBus.emitEvent).toHaveBeenCalledWith(
697
+ 'business_rules.rule.execution_failed',
698
+ expect.objectContaining({
699
+ ruleId: 'TEST-001',
700
+ ruleName: 'Test Rule',
701
+ entityType: 'WorkOrder',
702
+ errorMessage: 'Evaluation error',
703
+ tenantId: testTenantId,
704
+ organizationId: testOrgId,
705
+ })
706
+ )
707
+ })
657
708
  })
658
709
 
659
710
  describe('logRuleExecution', () => {
@@ -1,4 +1,5 @@
1
1
  import type { EntityManager } from '@mikro-orm/core'
2
+ import type { EventBus } from '@open-mercato/events'
2
3
  import { BusinessRule, RuleExecutionLog, type RuleType } from '../data/entities'
3
4
  import * as ruleEvaluator from './rule-evaluator'
4
5
  import * as actionExecutor from './action-executor'
@@ -85,6 +86,19 @@ export interface RuleDiscoveryOptions {
85
86
  ruleType?: RuleType
86
87
  }
87
88
 
89
+ export type RuleEngineExecutionOptions = {
90
+ eventBus?: Pick<EventBus, 'emitEvent'> | null
91
+ }
92
+
93
+ type RuleExecutionFailedPayload = {
94
+ ruleId: string
95
+ ruleName: string
96
+ entityType?: string | null
97
+ errorMessage?: string | null
98
+ tenantId: string
99
+ organizationId?: string | null
100
+ }
101
+
88
102
  /**
89
103
  * Execute a function with a timeout
90
104
  */
@@ -113,7 +127,8 @@ async function withTimeout<T>(
113
127
  */
114
128
  export async function executeRules(
115
129
  em: EntityManager,
116
- context: RuleEngineContext
130
+ context: RuleEngineContext,
131
+ options: RuleEngineExecutionOptions = {}
117
132
  ): Promise<RuleEngineResult> {
118
133
  // Validate input
119
134
  const validation = ruleEngineContextSchema.safeParse(context)
@@ -159,7 +174,7 @@ export async function executeRules(
159
174
  const executionPromise = (async () => {
160
175
  for (const rule of rules) {
161
176
  try {
162
- const ruleResult = await executeSingleRule(em, rule, context)
177
+ const ruleResult = await executeSingleRule(em, rule, context, options)
163
178
  executedRules.push(ruleResult)
164
179
 
165
180
  if (ruleResult.logId) {
@@ -177,6 +192,17 @@ export async function executeRules(
177
192
  `Unexpected error in rule execution [ruleId=${rule.ruleId}, type=${rule.ruleType}]: ${errorMessage}`
178
193
  )
179
194
 
195
+ if (!context.dryRun) {
196
+ await emitRuleExecutionFailed(options.eventBus, {
197
+ ruleId: rule.ruleId,
198
+ ruleName: rule.ruleName,
199
+ entityType: context.entityType ?? null,
200
+ errorMessage,
201
+ tenantId: context.tenantId,
202
+ organizationId: context.organizationId ?? null,
203
+ })
204
+ }
205
+
180
206
  executedRules.push({
181
207
  rule,
182
208
  conditionResult: false,
@@ -233,7 +259,8 @@ export async function executeRules(
233
259
  export async function executeSingleRule(
234
260
  em: EntityManager,
235
261
  rule: BusinessRule,
236
- context: RuleEngineContext
262
+ context: RuleEngineContext,
263
+ options: RuleEngineExecutionOptions = {}
237
264
  ): Promise<RuleExecutionResult> {
238
265
  const startTime = Date.now()
239
266
 
@@ -269,6 +296,15 @@ export async function executeSingleRule(
269
296
  executionTime,
270
297
  error: result.error,
271
298
  })
299
+
300
+ await emitRuleExecutionFailed(options.eventBus, {
301
+ ruleId: rule.ruleId,
302
+ ruleName: rule.ruleName,
303
+ entityType: context.entityType ?? null,
304
+ errorMessage: result.error ?? null,
305
+ tenantId: context.tenantId,
306
+ organizationId: context.organizationId ?? null,
307
+ })
272
308
  }
273
309
 
274
310
  return {
@@ -346,6 +382,15 @@ export async function executeSingleRule(
346
382
  executionTime,
347
383
  error: enhancedError,
348
384
  })
385
+
386
+ await emitRuleExecutionFailed(options.eventBus, {
387
+ ruleId: rule.ruleId,
388
+ ruleName: rule.ruleName,
389
+ entityType: context.entityType ?? null,
390
+ errorMessage: enhancedError,
391
+ tenantId: context.tenantId,
392
+ organizationId: context.organizationId ?? null,
393
+ })
349
394
  }
350
395
 
351
396
  return {
@@ -544,3 +589,12 @@ export async function logRuleExecution(
544
589
 
545
590
  return log.id
546
591
  }
592
+
593
+ async function emitRuleExecutionFailed(
594
+ eventBus: Pick<EventBus, 'emitEvent'> | null | undefined,
595
+ payload: RuleExecutionFailedPayload
596
+ ): Promise<void> {
597
+ if (!eventBus?.emitEvent) return
598
+
599
+ await eventBus.emitEvent('business_rules.rule.execution_failed', payload).catch(() => undefined)
600
+ }
@@ -0,0 +1,25 @@
1
+ import type { NotificationTypeDefinition } from '@open-mercato/shared/modules/notifications/types'
2
+
3
+ export const notificationTypes: NotificationTypeDefinition[] = [
4
+ {
5
+ type: 'business_rules.rule.execution_failed',
6
+ module: 'business_rules',
7
+ titleKey: 'businessRules.notifications.rule.executionFailed.title',
8
+ bodyKey: 'businessRules.notifications.rule.executionFailed.body',
9
+ icon: 'alert-triangle',
10
+ severity: 'error',
11
+ actions: [
12
+ {
13
+ id: 'view',
14
+ labelKey: 'common.view',
15
+ variant: 'outline',
16
+ href: '/backend/business-rules/{sourceEntityId}',
17
+ icon: 'external-link',
18
+ },
19
+ ],
20
+ linkHref: '/backend/business-rules/{sourceEntityId}',
21
+ expiresAfterHours: 168, // 7 days
22
+ },
23
+ ]
24
+
25
+ export default notificationTypes
@@ -0,0 +1,50 @@
1
+ import type { EntityManager } from '@mikro-orm/postgresql'
2
+ import { resolveNotificationService } from '../../notifications/lib/notificationService'
3
+ import { buildFeatureNotificationFromType } from '../../notifications/lib/notificationBuilder'
4
+ import { notificationTypes } from '../notifications'
5
+
6
+ export const metadata = {
7
+ event: 'business_rules.rule.execution_failed',
8
+ persistent: true,
9
+ id: 'business_rules:rule-execution-failed-notification',
10
+ }
11
+
12
+ type RuleExecutionFailedPayload = {
13
+ ruleId: string
14
+ ruleName: string
15
+ entityType?: string | null
16
+ errorMessage?: string | null
17
+ tenantId: string
18
+ organizationId?: string | null
19
+ }
20
+
21
+ type ResolverContext = {
22
+ resolve: <T = unknown>(name: string) => T
23
+ }
24
+
25
+ export default async function handle(payload: RuleExecutionFailedPayload, ctx: ResolverContext) {
26
+ try {
27
+ const notificationService = resolveNotificationService(ctx)
28
+ const typeDef = notificationTypes.find((type) => type.type === 'business_rules.rule.execution_failed')
29
+ if (!typeDef) return
30
+
31
+ const notificationInput = buildFeatureNotificationFromType(typeDef, {
32
+ requiredFeature: 'business_rules.manage',
33
+ bodyVariables: {
34
+ ruleName: payload.ruleName,
35
+ entityType: payload.entityType ?? '',
36
+ errorMessage: payload.errorMessage ?? 'Unknown error',
37
+ },
38
+ sourceEntityType: 'business_rules:rule',
39
+ sourceEntityId: payload.ruleId,
40
+ linkHref: `/backend/business-rules/${payload.ruleId}`,
41
+ })
42
+
43
+ await notificationService.createForFeature(notificationInput, {
44
+ tenantId: payload.tenantId,
45
+ organizationId: payload.organizationId ?? null,
46
+ })
47
+ } catch (err) {
48
+ console.error('[business_rules:rule-execution-failed-notification] Failed to create notification:', err)
49
+ }
50
+ }
@@ -420,10 +420,12 @@ export function PriceKindSettings() {
420
420
  <RowActions
421
421
  items={[
422
422
  {
423
+ id: 'edit',
423
424
  label: t('catalog.priceKinds.actions.edit', 'Edit'),
424
425
  onSelect: () => openDialog({ mode: 'edit', entry }),
425
426
  },
426
427
  {
428
+ id: 'delete',
427
429
  label: t('catalog.priceKinds.actions.delete', 'Delete'),
428
430
  destructive: true,
429
431
  onSelect: () => { void handleDelete(entry) },
@@ -221,8 +221,8 @@ export default function CategoriesDataTable() {
221
221
  canManage ? (
222
222
  <RowActions
223
223
  items={[
224
- { label: t('catalog.categories.list.actions.edit', 'Edit'), href: `/backend/catalog/categories/${row.id}/edit` },
225
- { label: t('catalog.categories.list.actions.delete', 'Delete'), destructive: true, onSelect: () => handleDelete(row) },
224
+ { id: 'edit', label: t('catalog.categories.list.actions.edit', 'Edit'), href: `/backend/catalog/categories/${row.id}/edit` },
225
+ { id: 'delete', label: t('catalog.categories.list.actions.delete', 'Delete'), destructive: true, onSelect: () => handleDelete(row) },
226
226
  ]}
227
227
  />
228
228
  ) : null
@@ -650,10 +650,12 @@ export default function ProductsDataTable() {
650
650
  <RowActions
651
651
  items={[
652
652
  {
653
+ id: 'edit',
653
654
  label: t('catalog.products.table.actions.edit', 'Edit'),
654
655
  href: `/backend/catalog/products/${row.id}`,
655
656
  },
656
657
  {
658
+ id: 'delete',
657
659
  label: t('catalog.products.table.actions.delete', 'Delete'),
658
660
  destructive: true,
659
661
  onSelect: () => {
@@ -681,5 +681,7 @@
681
681
  "deleteError": "Failed to delete variant."
682
682
  }
683
683
  }
684
- }
684
+ },
685
+ "catalog.notifications.product.lowStock.title": "Low Stock Alert",
686
+ "catalog.notifications.product.lowStock.body": "{productName}{sku, select, other { ({sku})}} is running low on stock ({currentStock} remaining, threshold: {threshold})"
685
687
  }
@@ -0,0 +1,25 @@
1
+ import type { NotificationTypeDefinition } from '@open-mercato/shared/modules/notifications/types'
2
+
3
+ export const notificationTypes: NotificationTypeDefinition[] = [
4
+ {
5
+ type: 'catalog.product.low_stock',
6
+ module: 'catalog',
7
+ titleKey: 'catalog.notifications.product.lowStock.title',
8
+ bodyKey: 'catalog.notifications.product.lowStock.body',
9
+ icon: 'package-x',
10
+ severity: 'warning',
11
+ actions: [
12
+ {
13
+ id: 'view',
14
+ labelKey: 'common.view',
15
+ variant: 'outline',
16
+ href: '/backend/catalog/products/{sourceEntityId}',
17
+ icon: 'external-link',
18
+ },
19
+ ],
20
+ linkHref: '/backend/catalog/products/{sourceEntityId}',
21
+ expiresAfterHours: 72, // 3 days
22
+ },
23
+ ]
24
+
25
+ export default notificationTypes