@open-mercato/core 0.4.2-canary-f6b7824b47 → 0.4.2-canary-70c8402224

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 (235) 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/auth/api/admin/nav.js +4 -3
  10. package/dist/modules/auth/api/admin/nav.js.map +2 -2
  11. package/dist/modules/auth/api/profile/route.js +155 -0
  12. package/dist/modules/auth/api/profile/route.js.map +7 -0
  13. package/dist/modules/auth/api/reset/confirm.js +25 -2
  14. package/dist/modules/auth/api/reset/confirm.js.map +2 -2
  15. package/dist/modules/auth/api/reset.js +23 -0
  16. package/dist/modules/auth/api/reset.js.map +2 -2
  17. package/dist/modules/auth/api/sidebar/preferences/route.js +14 -9
  18. package/dist/modules/auth/api/sidebar/preferences/route.js.map +2 -2
  19. package/dist/modules/auth/backend/auth/profile/page.js +99 -0
  20. package/dist/modules/auth/backend/auth/profile/page.js.map +7 -0
  21. package/dist/modules/auth/backend/auth/profile/page.meta.js +12 -0
  22. package/dist/modules/auth/backend/auth/profile/page.meta.js.map +7 -0
  23. package/dist/modules/auth/commands/users.js +55 -0
  24. package/dist/modules/auth/commands/users.js.map +2 -2
  25. package/dist/modules/auth/lib/setup-app.js +1 -0
  26. package/dist/modules/auth/lib/setup-app.js.map +2 -2
  27. package/dist/modules/auth/notifications.js +112 -0
  28. package/dist/modules/auth/notifications.js.map +7 -0
  29. package/dist/modules/auth/services/authService.js +3 -3
  30. package/dist/modules/auth/services/authService.js.map +2 -2
  31. package/dist/modules/business_rules/notifications.js +28 -0
  32. package/dist/modules/business_rules/notifications.js.map +7 -0
  33. package/dist/modules/business_rules/subscribers/rule-execution-failed-notification.js +37 -0
  34. package/dist/modules/business_rules/subscribers/rule-execution-failed-notification.js.map +7 -0
  35. package/dist/modules/catalog/notifications.js +28 -0
  36. package/dist/modules/catalog/notifications.js.map +7 -0
  37. package/dist/modules/catalog/subscribers/low-stock-notification.js +38 -0
  38. package/dist/modules/catalog/subscribers/low-stock-notification.js.map +7 -0
  39. package/dist/modules/configs/cli.js +6 -0
  40. package/dist/modules/configs/cli.js.map +2 -2
  41. package/dist/modules/customers/commands/deals.js +31 -0
  42. package/dist/modules/customers/commands/deals.js.map +2 -2
  43. package/dist/modules/customers/notifications.js +48 -0
  44. package/dist/modules/customers/notifications.js.map +7 -0
  45. package/dist/modules/notifications/acl.js +11 -0
  46. package/dist/modules/notifications/acl.js.map +7 -0
  47. package/dist/modules/notifications/api/[id]/action/route.js +69 -0
  48. package/dist/modules/notifications/api/[id]/action/route.js.map +7 -0
  49. package/dist/modules/notifications/api/[id]/dismiss/route.js +15 -0
  50. package/dist/modules/notifications/api/[id]/dismiss/route.js.map +7 -0
  51. package/dist/modules/notifications/api/[id]/read/route.js +15 -0
  52. package/dist/modules/notifications/api/[id]/read/route.js.map +7 -0
  53. package/dist/modules/notifications/api/[id]/restore/route.js +53 -0
  54. package/dist/modules/notifications/api/[id]/restore/route.js.map +7 -0
  55. package/dist/modules/notifications/api/batch/route.js +17 -0
  56. package/dist/modules/notifications/api/batch/route.js.map +7 -0
  57. package/dist/modules/notifications/api/feature/route.js +17 -0
  58. package/dist/modules/notifications/api/feature/route.js.map +7 -0
  59. package/dist/modules/notifications/api/mark-all-read/route.js +35 -0
  60. package/dist/modules/notifications/api/mark-all-read/route.js.map +7 -0
  61. package/dist/modules/notifications/api/openapi.js +57 -0
  62. package/dist/modules/notifications/api/openapi.js.map +7 -0
  63. package/dist/modules/notifications/api/role/route.js +17 -0
  64. package/dist/modules/notifications/api/role/route.js.map +7 -0
  65. package/dist/modules/notifications/api/route.js +85 -0
  66. package/dist/modules/notifications/api/route.js.map +7 -0
  67. package/dist/modules/notifications/api/settings/route.js +96 -0
  68. package/dist/modules/notifications/api/settings/route.js.map +7 -0
  69. package/dist/modules/notifications/api/unread-count/route.js +38 -0
  70. package/dist/modules/notifications/api/unread-count/route.js.map +7 -0
  71. package/dist/modules/notifications/backend/config/notifications/page.js +10 -0
  72. package/dist/modules/notifications/backend/config/notifications/page.js.map +7 -0
  73. package/dist/modules/notifications/backend/config/notifications/page.meta.js +24 -0
  74. package/dist/modules/notifications/backend/config/notifications/page.meta.js.map +7 -0
  75. package/dist/modules/notifications/cli.js +16 -0
  76. package/dist/modules/notifications/cli.js.map +7 -0
  77. package/dist/modules/notifications/data/entities.js +112 -0
  78. package/dist/modules/notifications/data/entities.js.map +7 -0
  79. package/dist/modules/notifications/data/validators.js +94 -0
  80. package/dist/modules/notifications/data/validators.js.map +7 -0
  81. package/dist/modules/notifications/di.js +13 -0
  82. package/dist/modules/notifications/di.js.map +7 -0
  83. package/dist/modules/notifications/emails/NotificationEmail.js +58 -0
  84. package/dist/modules/notifications/emails/NotificationEmail.js.map +7 -0
  85. package/dist/modules/notifications/frontend/NotificationInboxPageClient.js +44 -0
  86. package/dist/modules/notifications/frontend/NotificationInboxPageClient.js.map +7 -0
  87. package/dist/modules/notifications/frontend/NotificationSettingsPageClient.js +219 -0
  88. package/dist/modules/notifications/frontend/NotificationSettingsPageClient.js.map +7 -0
  89. package/dist/modules/notifications/index.js +14 -0
  90. package/dist/modules/notifications/index.js.map +7 -0
  91. package/dist/modules/notifications/lib/deliveryConfig.js +105 -0
  92. package/dist/modules/notifications/lib/deliveryConfig.js.map +7 -0
  93. package/dist/modules/notifications/lib/events.js +12 -0
  94. package/dist/modules/notifications/lib/events.js.map +7 -0
  95. package/dist/modules/notifications/lib/notificationBuilder.js +66 -0
  96. package/dist/modules/notifications/lib/notificationBuilder.js.map +7 -0
  97. package/dist/modules/notifications/lib/notificationFactory.js +54 -0
  98. package/dist/modules/notifications/lib/notificationFactory.js.map +7 -0
  99. package/dist/modules/notifications/lib/notificationMapper.js +34 -0
  100. package/dist/modules/notifications/lib/notificationMapper.js.map +7 -0
  101. package/dist/modules/notifications/lib/notificationRecipients.js +35 -0
  102. package/dist/modules/notifications/lib/notificationRecipients.js.map +7 -0
  103. package/dist/modules/notifications/lib/notificationService.js +279 -0
  104. package/dist/modules/notifications/lib/notificationService.js.map +7 -0
  105. package/dist/modules/notifications/lib/routeHelpers.js +101 -0
  106. package/dist/modules/notifications/lib/routeHelpers.js.map +7 -0
  107. package/dist/modules/notifications/lib/safeHref.js +24 -0
  108. package/dist/modules/notifications/lib/safeHref.js.map +7 -0
  109. package/dist/modules/notifications/migrations/Migration20260123000001.js +70 -0
  110. package/dist/modules/notifications/migrations/Migration20260123000001.js.map +7 -0
  111. package/dist/modules/notifications/migrations/Migration20260126150000.js +37 -0
  112. package/dist/modules/notifications/migrations/Migration20260126150000.js.map +7 -0
  113. package/dist/modules/notifications/subscribers/deliver-notification.js +139 -0
  114. package/dist/modules/notifications/subscribers/deliver-notification.js.map +7 -0
  115. package/dist/modules/notifications/workers/create-notification.worker.js +70 -0
  116. package/dist/modules/notifications/workers/create-notification.worker.js.map +7 -0
  117. package/dist/modules/sales/commands/documents.js +53 -0
  118. package/dist/modules/sales/commands/documents.js.map +2 -2
  119. package/dist/modules/sales/commands/payments.js +26 -0
  120. package/dist/modules/sales/commands/payments.js.map +2 -2
  121. package/dist/modules/sales/notifications.client.js +51 -0
  122. package/dist/modules/sales/notifications.client.js.map +7 -0
  123. package/dist/modules/sales/notifications.js +88 -0
  124. package/dist/modules/sales/notifications.js.map +7 -0
  125. package/dist/modules/sales/subscribers/quote-expiring-notification.js +38 -0
  126. package/dist/modules/sales/subscribers/quote-expiring-notification.js.map +7 -0
  127. package/dist/modules/sales/widgets/notifications/SalesOrderCreatedRenderer.js +137 -0
  128. package/dist/modules/sales/widgets/notifications/SalesOrderCreatedRenderer.js.map +7 -0
  129. package/dist/modules/sales/widgets/notifications/SalesQuoteCreatedRenderer.js +137 -0
  130. package/dist/modules/sales/widgets/notifications/SalesQuoteCreatedRenderer.js.map +7 -0
  131. package/dist/modules/sales/widgets/notifications/index.js +7 -0
  132. package/dist/modules/sales/widgets/notifications/index.js.map +7 -0
  133. package/dist/modules/sales/widgets/notifications/useSalesDocumentTotals.js +60 -0
  134. package/dist/modules/sales/widgets/notifications/useSalesDocumentTotals.js.map +7 -0
  135. package/dist/modules/staff/commands/leave-requests.js +79 -0
  136. package/dist/modules/staff/commands/leave-requests.js.map +2 -2
  137. package/dist/modules/staff/notifications.js +75 -0
  138. package/dist/modules/staff/notifications.js.map +7 -0
  139. package/dist/modules/workflows/notifications.js +28 -0
  140. package/dist/modules/workflows/notifications.js.map +7 -0
  141. package/dist/modules/workflows/subscribers/task-assigned-notification.js +38 -0
  142. package/dist/modules/workflows/subscribers/task-assigned-notification.js.map +7 -0
  143. package/generated/entities/notification/index.ts +27 -0
  144. package/generated/entities.ids.generated.ts +5 -1
  145. package/generated/entity-fields-registry.ts +2 -0
  146. package/package.json +2 -2
  147. package/src/modules/api_docs/frontend/docs/api/page.tsx +3 -2
  148. package/src/modules/auth/api/admin/nav.ts +10 -6
  149. package/src/modules/auth/api/profile/route.ts +160 -0
  150. package/src/modules/auth/api/reset/confirm.ts +25 -2
  151. package/src/modules/auth/api/reset.ts +23 -0
  152. package/src/modules/auth/api/sidebar/preferences/route.ts +21 -12
  153. package/src/modules/auth/backend/auth/profile/page.meta.ts +8 -0
  154. package/src/modules/auth/backend/auth/profile/page.tsx +127 -0
  155. package/src/modules/auth/commands/users.ts +68 -0
  156. package/src/modules/auth/i18n/de.json +29 -1
  157. package/src/modules/auth/i18n/en.json +29 -1
  158. package/src/modules/auth/i18n/es.json +29 -1
  159. package/src/modules/auth/i18n/pl.json +29 -1
  160. package/src/modules/auth/lib/setup-app.ts +1 -0
  161. package/src/modules/auth/notifications.ts +109 -0
  162. package/src/modules/auth/services/authService.ts +4 -4
  163. package/src/modules/business_rules/i18n/en.json +3 -1
  164. package/src/modules/business_rules/notifications.ts +25 -0
  165. package/src/modules/business_rules/subscribers/rule-execution-failed-notification.ts +50 -0
  166. package/src/modules/catalog/i18n/en.json +3 -1
  167. package/src/modules/catalog/notifications.ts +25 -0
  168. package/src/modules/catalog/subscribers/low-stock-notification.ts +52 -0
  169. package/src/modules/configs/cli.ts +6 -0
  170. package/src/modules/customers/commands/deals.ts +39 -0
  171. package/src/modules/customers/i18n/en.json +5 -1
  172. package/src/modules/customers/notifications.ts +44 -0
  173. package/src/modules/notifications/acl.ts +7 -0
  174. package/src/modules/notifications/api/[id]/action/route.ts +70 -0
  175. package/src/modules/notifications/api/[id]/dismiss/route.ts +12 -0
  176. package/src/modules/notifications/api/[id]/read/route.ts +12 -0
  177. package/src/modules/notifications/api/[id]/restore/route.ts +53 -0
  178. package/src/modules/notifications/api/batch/route.ts +14 -0
  179. package/src/modules/notifications/api/feature/route.ts +14 -0
  180. package/src/modules/notifications/api/mark-all-read/route.ts +34 -0
  181. package/src/modules/notifications/api/openapi.ts +52 -0
  182. package/src/modules/notifications/api/role/route.ts +14 -0
  183. package/src/modules/notifications/api/route.ts +92 -0
  184. package/src/modules/notifications/api/settings/route.ts +98 -0
  185. package/src/modules/notifications/api/unread-count/route.ts +38 -0
  186. package/src/modules/notifications/backend/config/notifications/page.meta.ts +22 -0
  187. package/src/modules/notifications/backend/config/notifications/page.tsx +12 -0
  188. package/src/modules/notifications/cli.ts +18 -0
  189. package/src/modules/notifications/data/entities.ts +99 -0
  190. package/src/modules/notifications/data/validators.ts +110 -0
  191. package/src/modules/notifications/di.ts +11 -0
  192. package/src/modules/notifications/emails/NotificationEmail.tsx +98 -0
  193. package/src/modules/notifications/frontend/NotificationInboxPageClient.tsx +42 -0
  194. package/src/modules/notifications/frontend/NotificationSettingsPageClient.tsx +231 -0
  195. package/src/modules/notifications/i18n/de.json +50 -0
  196. package/src/modules/notifications/i18n/en.json +50 -0
  197. package/src/modules/notifications/i18n/es.json +50 -0
  198. package/src/modules/notifications/i18n/pl.json +50 -0
  199. package/src/modules/notifications/index.ts +12 -0
  200. package/src/modules/notifications/lib/deliveryConfig.ts +145 -0
  201. package/src/modules/notifications/lib/events.ts +48 -0
  202. package/src/modules/notifications/lib/notificationBuilder.ts +121 -0
  203. package/src/modules/notifications/lib/notificationFactory.ts +76 -0
  204. package/src/modules/notifications/lib/notificationMapper.ts +33 -0
  205. package/src/modules/notifications/lib/notificationRecipients.ts +83 -0
  206. package/src/modules/notifications/lib/notificationService.ts +414 -0
  207. package/src/modules/notifications/lib/routeHelpers.ts +151 -0
  208. package/src/modules/notifications/lib/safeHref.ts +29 -0
  209. package/src/modules/notifications/migrations/.snapshot-open-mercato.json +300 -0
  210. package/src/modules/notifications/migrations/Migration20260123000001.ts +73 -0
  211. package/src/modules/notifications/migrations/Migration20260126150000.ts +39 -0
  212. package/src/modules/notifications/subscribers/deliver-notification.ts +175 -0
  213. package/src/modules/notifications/workers/create-notification.worker.ts +122 -0
  214. package/src/modules/sales/commands/documents.ts +65 -0
  215. package/src/modules/sales/commands/payments.ts +33 -0
  216. package/src/modules/sales/i18n/de.json +20 -0
  217. package/src/modules/sales/i18n/en.json +25 -1
  218. package/src/modules/sales/i18n/es.json +20 -0
  219. package/src/modules/sales/i18n/pl.json +20 -0
  220. package/src/modules/sales/notifications.client.ts +65 -0
  221. package/src/modules/sales/notifications.ts +82 -0
  222. package/src/modules/sales/subscribers/quote-expiring-notification.ts +53 -0
  223. package/src/modules/sales/widgets/notifications/SalesOrderCreatedRenderer.tsx +156 -0
  224. package/src/modules/sales/widgets/notifications/SalesQuoteCreatedRenderer.tsx +156 -0
  225. package/src/modules/sales/widgets/notifications/index.ts +2 -0
  226. package/src/modules/sales/widgets/notifications/useSalesDocumentTotals.ts +81 -0
  227. package/src/modules/staff/commands/leave-requests.ts +94 -0
  228. package/src/modules/staff/i18n/de.json +4 -0
  229. package/src/modules/staff/i18n/en.json +9 -1
  230. package/src/modules/staff/i18n/es.json +4 -0
  231. package/src/modules/staff/i18n/pl.json +4 -0
  232. package/src/modules/staff/notifications.ts +71 -0
  233. package/src/modules/workflows/i18n/en.json +3 -1
  234. package/src/modules/workflows/notifications.ts +25 -0
  235. package/src/modules/workflows/subscribers/task-assigned-notification.ts +53 -0
@@ -80,6 +80,19 @@
80
80
  "auth.users.form.errors.load": "No se pudieron cargar los datos del usuario",
81
81
  "auth.users.form.errors.aclUpdate": "No se pudo actualizar el control de acceso del usuario",
82
82
  "auth.users.form.errors.delete": "No se pudo eliminar el usuario",
83
+ "auth.profile.title": "Perfil",
84
+ "auth.profile.form.email": "Correo electrónico",
85
+ "auth.profile.form.password": "Nueva contraseña",
86
+ "auth.profile.form.confirmPassword": "Confirmar nueva contraseña",
87
+ "auth.profile.form.save": "Guardar cambios",
88
+ "auth.profile.form.loading": "Cargando perfil...",
89
+ "auth.profile.form.errors.load": "No se pudo cargar el perfil.",
90
+ "auth.profile.form.errors.save": "No se pudo actualizar el perfil.",
91
+ "auth.profile.form.errors.invalid": "Actualización de perfil inválida.",
92
+ "auth.profile.form.errors.passwordMismatch": "Las contraseñas no coinciden.",
93
+ "auth.profile.form.errors.noChanges": "No hay cambios para guardar.",
94
+ "auth.profile.form.errors.emailRequired": "El correo electrónico es obligatorio.",
95
+ "auth.profile.form.success": "Perfil actualizado.",
83
96
  "auth.users.list.error.load": "No se pudieron cargar los usuarios",
84
97
  "auth.users.list.error.delete": "No se pudo eliminar el usuario",
85
98
  "auth.users.flash.created": "Usuario creado",
@@ -95,5 +108,20 @@
95
108
  "auth.email.resetPassword.title": "Restablecer tu contraseña",
96
109
  "auth.email.resetPassword.body": "Haz clic en el siguiente enlace para establecer una nueva contraseña. Este enlace caducará en 60 minutos.",
97
110
  "auth.email.resetPassword.cta": "Establecer nueva contraseña",
98
- "auth.email.resetPassword.hint": "Si no solicitaste esto, puedes ignorar este correo de forma segura."
111
+ "auth.email.resetPassword.hint": "Si no solicitaste esto, puedes ignorar este correo de forma segura.",
112
+ "auth.notifications.passwordReset.requested.title": "Solicitud de restablecimiento de contraseña",
113
+ "auth.notifications.passwordReset.requested.body": "Se ha enviado un enlace de restablecimiento de contraseña a tu correo electrónico",
114
+ "auth.notifications.passwordReset.completed.title": "Contraseña cambiada correctamente",
115
+ "auth.notifications.passwordReset.completed.body": "Tu contraseña se actualizó correctamente",
116
+ "auth.notifications.account.locked.title": "Cuenta bloqueada",
117
+ "auth.notifications.account.locked.body": "Tu cuenta ha sido bloqueada por razones de seguridad. Ponte en contacto con soporte.",
118
+ "auth.notifications.login.newDevice.title": "Nuevo inicio de sesión detectado",
119
+ "auth.notifications.login.newDevice.body": "Se detectó un inicio de sesión desde un dispositivo no reconocido en tu cuenta",
120
+ "auth.notifications.role.assigned.title": "Nuevo rol asignado",
121
+ "auth.notifications.role.assigned.body": "Se te ha asignado un nuevo rol con permisos adicionales",
122
+ "auth.notifications.role.revoked.title": "Rol eliminado",
123
+ "auth.notifications.role.revoked.body": "Se ha eliminado un rol de tu cuenta",
124
+ "auth.actions.contactSupport": "Contactar soporte",
125
+ "auth.actions.viewSessions": "Ver sesiones",
126
+ "auth.actions.viewPermissions": "Ver permisos"
99
127
  }
@@ -80,6 +80,19 @@
80
80
  "auth.users.form.errors.load": "Nie udało się wczytać danych użytkownika",
81
81
  "auth.users.form.errors.aclUpdate": "Nie udało się zaktualizować uprawnień użytkownika",
82
82
  "auth.users.form.errors.delete": "Nie udało się usunąć użytkownika",
83
+ "auth.profile.title": "Profil",
84
+ "auth.profile.form.email": "Email",
85
+ "auth.profile.form.password": "Nowe hasło",
86
+ "auth.profile.form.confirmPassword": "Potwierdź nowe hasło",
87
+ "auth.profile.form.save": "Zapisz zmiany",
88
+ "auth.profile.form.loading": "Ładowanie profilu...",
89
+ "auth.profile.form.errors.load": "Nie udało się wczytać profilu.",
90
+ "auth.profile.form.errors.save": "Nie udało się zaktualizować profilu.",
91
+ "auth.profile.form.errors.invalid": "Nieprawidłowa aktualizacja profilu.",
92
+ "auth.profile.form.errors.passwordMismatch": "Hasła nie są zgodne.",
93
+ "auth.profile.form.errors.noChanges": "Brak zmian do zapisania.",
94
+ "auth.profile.form.errors.emailRequired": "Email jest wymagany.",
95
+ "auth.profile.form.success": "Profil zaktualizowany.",
83
96
  "auth.users.list.error.load": "Nie udało się wczytać użytkowników",
84
97
  "auth.users.list.error.delete": "Nie udało się usunąć użytkownika",
85
98
  "auth.users.flash.created": "Użytkownik utworzony",
@@ -95,5 +108,20 @@
95
108
  "auth.email.resetPassword.title": "Zresetuj swoje hasło",
96
109
  "auth.email.resetPassword.body": "Kliknij poniższy link, aby ustawić nowe hasło. Link wygaśnie za 60 minut.",
97
110
  "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ć."
111
+ "auth.email.resetPassword.hint": "Jeśli nie prosiłeś o tę wiadomość, możesz ją bezpiecznie zignorować.",
112
+ "auth.notifications.passwordReset.requested.title": "Żądanie resetu hasła",
113
+ "auth.notifications.passwordReset.requested.body": "Link do resetu hasła został wysłany na Twój adres e-mail",
114
+ "auth.notifications.passwordReset.completed.title": "Hasło zmienione pomyślnie",
115
+ "auth.notifications.passwordReset.completed.body": "Twoje hasło zostało pomyślnie zaktualizowane",
116
+ "auth.notifications.account.locked.title": "Konto zablokowane",
117
+ "auth.notifications.account.locked.body": "Twoje konto zostało zablokowane z powodów bezpieczeństwa. Skontaktuj się z pomocą techniczną.",
118
+ "auth.notifications.login.newDevice.title": "Wykryto logowanie z nowego urządzenia",
119
+ "auth.notifications.login.newDevice.body": "Wykryto logowanie z nieznanego urządzenia na Twoim koncie",
120
+ "auth.notifications.role.assigned.title": "Przypisano nową rolę",
121
+ "auth.notifications.role.assigned.body": "Przypisano Ci nową rolę z dodatkowymi uprawnieniami",
122
+ "auth.notifications.role.revoked.title": "Rola usunięta",
123
+ "auth.notifications.role.revoked.body": "Z Twojego konta usunięto rolę",
124
+ "auth.actions.contactSupport": "Skontaktuj się z pomocą",
125
+ "auth.actions.viewSessions": "Zobacz sesje",
126
+ "auth.actions.viewPermissions": "Zobacz uprawnienia"
99
127
  }
@@ -395,6 +395,7 @@ async function ensureDefaultRoleAcls(
395
395
  'dashboards.*',
396
396
  'dashboards.admin.assign-widgets',
397
397
  'api_keys.*',
398
+ 'notifications.manage',
398
399
  'perspectives.use',
399
400
  'perspectives.role_defaults',
400
401
  '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
  }
@@ -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
  }
@@ -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
+ }
@@ -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
@@ -0,0 +1,52 @@
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: 'catalog.product.stock_low',
8
+ persistent: true,
9
+ id: 'catalog:low-stock-notification',
10
+ }
11
+
12
+ type LowStockPayload = {
13
+ productId: string
14
+ productName: string
15
+ sku?: string | null
16
+ currentStock: number
17
+ threshold: number
18
+ tenantId: string
19
+ organizationId?: string | null
20
+ }
21
+
22
+ type ResolverContext = {
23
+ resolve: <T = unknown>(name: string) => T
24
+ }
25
+
26
+ export default async function handle(payload: LowStockPayload, ctx: ResolverContext) {
27
+ try {
28
+ const notificationService = resolveNotificationService(ctx)
29
+ const typeDef = notificationTypes.find((type) => type.type === 'catalog.product.low_stock')
30
+ if (!typeDef) return
31
+
32
+ const notificationInput = buildFeatureNotificationFromType(typeDef, {
33
+ requiredFeature: 'catalog.products.manage',
34
+ bodyVariables: {
35
+ productName: payload.productName,
36
+ sku: payload.sku ?? '',
37
+ currentStock: String(payload.currentStock),
38
+ threshold: String(payload.threshold),
39
+ },
40
+ sourceEntityType: 'catalog:product',
41
+ sourceEntityId: payload.productId,
42
+ linkHref: `/backend/catalog/products/${payload.productId}`,
43
+ })
44
+
45
+ await notificationService.createForFeature(notificationInput, {
46
+ tenantId: payload.tenantId,
47
+ organizationId: payload.organizationId ?? null,
48
+ })
49
+ } catch (err) {
50
+ console.error('[catalog:low-stock-notification] Failed to create notification:', err)
51
+ }
52
+ }
@@ -2,6 +2,7 @@ import type { ModuleCli } from '@open-mercato/shared/modules/registry'
2
2
  import { createRequestContainer } from '@open-mercato/shared/lib/di/container'
3
3
  import type { ModuleConfigService } from './lib/module-config-service'
4
4
  import { parseBooleanToken } from '@open-mercato/shared/lib/boolean'
5
+ import { DEFAULT_NOTIFICATION_DELIVERY_CONFIG, NOTIFICATIONS_DELIVERY_CONFIG_KEY } from '../notifications/lib/deliveryConfig'
5
6
 
6
7
  function envDisablesAutoIndexing(): boolean {
7
8
  const raw = process.env.DISABLE_VECTOR_SEARCH_AUTOINDEXING
@@ -31,6 +32,11 @@ const restoreDefaults: ModuleCli = {
31
32
  name: 'auto_index_enabled',
32
33
  value: defaultEnabled,
33
34
  },
35
+ {
36
+ moduleId: 'notifications',
37
+ name: NOTIFICATIONS_DELIVERY_CONFIG_KEY,
38
+ value: DEFAULT_NOTIFICATION_DELIVERY_CONFIG,
39
+ },
34
40
  ],
35
41
  { force: true },
36
42
  )
@@ -35,6 +35,9 @@ import { CrudHttpError } from '@open-mercato/shared/lib/crud/errors'
35
35
  import type { CrudIndexerConfig } from '@open-mercato/shared/lib/crud/types'
36
36
  import { E } from '#generated/entities.ids.generated'
37
37
  import { findWithDecryption } from '@open-mercato/shared/lib/encryption/find'
38
+ import { resolveNotificationService } from '../../notifications/lib/notificationService'
39
+ import { buildNotificationFromType } from '../../notifications/lib/notificationBuilder'
40
+ import { notificationTypes } from '../notifications'
38
41
 
39
42
  const DEAL_ENTITY_ID = 'customers:customer_deal'
40
43
  const dealCrudIndexer: CrudIndexerConfig<CustomerDeal> = {
@@ -281,6 +284,8 @@ const updateDealCommand: CommandHandler<DealUpdateInput, { dealId: string }> = {
281
284
  ensureTenantScope(ctx, record.tenantId)
282
285
  ensureOrganizationScope(ctx, record.organizationId)
283
286
 
287
+ const previousStatus = record.status
288
+
284
289
  if (parsed.title !== undefined) record.title = parsed.title
285
290
  if (parsed.description !== undefined) record.description = parsed.description ?? null
286
291
  if (parsed.status !== undefined) record.status = parsed.status ?? record.status
@@ -319,6 +324,40 @@ const updateDealCommand: CommandHandler<DealUpdateInput, { dealId: string }> = {
319
324
  indexer: dealCrudIndexer,
320
325
  })
321
326
 
327
+ // Send notifications for deal won/lost status changes
328
+ const newStatus = record.status
329
+ const normalizedStatus = newStatus === 'win' ? 'won' : newStatus === 'loose' ? 'lost' : newStatus
330
+ if (previousStatus !== newStatus && (normalizedStatus === 'won' || normalizedStatus === 'lost') && record.ownerUserId) {
331
+ try {
332
+ const notificationService = resolveNotificationService(ctx.container)
333
+ const notificationType = normalizedStatus === 'won' ? 'customers.deal.won' : 'customers.deal.lost'
334
+ const typeDef = notificationTypes.find((type) => type.type === notificationType)
335
+ if (typeDef) {
336
+ const valueDisplay = record.valueAmount && record.valueCurrency
337
+ ? `${record.valueCurrency} ${record.valueAmount}`
338
+ : ''
339
+
340
+ const notificationInput = buildNotificationFromType(typeDef, {
341
+ recipientUserId: record.ownerUserId,
342
+ bodyVariables: {
343
+ dealTitle: record.title,
344
+ dealValue: valueDisplay,
345
+ },
346
+ sourceEntityType: 'customers:customer_deal',
347
+ sourceEntityId: record.id,
348
+ linkHref: `/backend/customers/deals/${record.id}`,
349
+ })
350
+
351
+ await notificationService.create(notificationInput, {
352
+ tenantId: record.tenantId,
353
+ organizationId: record.organizationId,
354
+ })
355
+ }
356
+ } catch {
357
+ // Notification creation is non-critical, don't fail the command
358
+ }
359
+ }
360
+
322
361
  return { dealId: record.id }
323
362
  },
324
363
  buildLog: async ({ snapshots, ctx }) => {
@@ -943,5 +943,9 @@
943
943
  "customers.workPlan.customerTodos.table.state.empty": "No customer tasks yet.",
944
944
  "customers.workPlan.customerTodos.table.error.load": "Failed to load customer tasks.",
945
945
  "customers.workPlan.customerTodos.table.export.view": "Exports the current list with filters and visible columns.",
946
- "customers.workPlan.customerTodos.table.export.full": "Exports every linked task field, including hidden attributes."
946
+ "customers.workPlan.customerTodos.table.export.full": "Exports every linked task field, including hidden attributes.",
947
+ "customers.notifications.deal.won.title": "Deal Won",
948
+ "customers.notifications.deal.won.body": "{dealTitle} has been marked as won{dealValue, select, other { ({dealValue})}}",
949
+ "customers.notifications.deal.lost.title": "Deal Lost",
950
+ "customers.notifications.deal.lost.body": "{dealTitle} has been marked as lost"
947
951
  }
@@ -0,0 +1,44 @@
1
+ import type { NotificationTypeDefinition } from '@open-mercato/shared/modules/notifications/types'
2
+
3
+ export const notificationTypes: NotificationTypeDefinition[] = [
4
+ {
5
+ type: 'customers.deal.won',
6
+ module: 'customers',
7
+ titleKey: 'customers.notifications.deal.won.title',
8
+ bodyKey: 'customers.notifications.deal.won.body',
9
+ icon: 'trophy',
10
+ severity: 'success',
11
+ actions: [
12
+ {
13
+ id: 'view',
14
+ labelKey: 'common.view',
15
+ variant: 'outline',
16
+ href: '/backend/customers/deals/{sourceEntityId}',
17
+ icon: 'external-link',
18
+ },
19
+ ],
20
+ linkHref: '/backend/customers/deals/{sourceEntityId}',
21
+ expiresAfterHours: 168, // 7 days
22
+ },
23
+ {
24
+ type: 'customers.deal.lost',
25
+ module: 'customers',
26
+ titleKey: 'customers.notifications.deal.lost.title',
27
+ bodyKey: 'customers.notifications.deal.lost.body',
28
+ icon: 'x-circle',
29
+ severity: 'warning',
30
+ actions: [
31
+ {
32
+ id: 'view',
33
+ labelKey: 'common.view',
34
+ variant: 'outline',
35
+ href: '/backend/customers/deals/{sourceEntityId}',
36
+ icon: 'external-link',
37
+ },
38
+ ],
39
+ linkHref: '/backend/customers/deals/{sourceEntityId}',
40
+ expiresAfterHours: 168, // 7 days
41
+ },
42
+ ]
43
+
44
+ export default notificationTypes
@@ -0,0 +1,7 @@
1
+ export const features = [
2
+ { id: 'notifications.view', title: 'View own notifications', module: 'notifications' },
3
+ { id: 'notifications.create', title: 'Create notifications for others', module: 'notifications' },
4
+ { id: 'notifications.manage', title: 'Manage all notifications', module: 'notifications' },
5
+ ]
6
+
7
+ export default features
@@ -0,0 +1,70 @@
1
+ import { executeActionSchema } from '../../../data/validators'
2
+ import { actionResultResponseSchema } from '../../openapi'
3
+ import { resolveNotificationContext } from '../../../lib/routeHelpers'
4
+ import { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'
5
+
6
+ export const metadata = {
7
+ POST: { requireAuth: true },
8
+ }
9
+
10
+ export async function POST(req: Request, { params }: { params: Promise<{ id: string }> }) {
11
+ const { id } = await params
12
+ const { service, scope } = await resolveNotificationContext(req)
13
+
14
+ const body = await req.json().catch(() => ({}))
15
+ const input = executeActionSchema.parse(body)
16
+
17
+ try {
18
+ const { notification, result } = await service.executeAction(id, input, scope)
19
+
20
+ const action = notification.actionData?.actions?.find((a) => a.id === input.actionId)
21
+ const href = action?.href?.replace('{sourceEntityId}', notification.sourceEntityId ?? '')
22
+
23
+ return Response.json({
24
+ ok: true,
25
+ result,
26
+ href,
27
+ })
28
+ } catch (error) {
29
+ const { t } = await resolveTranslations()
30
+ const fallback = t('notifications.error.action', 'Failed to execute action')
31
+ const message = error instanceof Error && error.message ? error.message : fallback
32
+ return Response.json({ error: message }, { status: 400 })
33
+ }
34
+ }
35
+
36
+ export const openApi = {
37
+ POST: {
38
+ summary: 'Execute notification action',
39
+ tags: ['Notifications'],
40
+ parameters: [
41
+ {
42
+ name: 'id',
43
+ in: 'path',
44
+ required: true,
45
+ schema: { type: 'string', format: 'uuid' },
46
+ },
47
+ ],
48
+ requestBody: {
49
+ required: true,
50
+ content: {
51
+ 'application/json': {
52
+ schema: executeActionSchema,
53
+ },
54
+ },
55
+ },
56
+ responses: {
57
+ 200: {
58
+ description: 'Action executed successfully',
59
+ content: {
60
+ 'application/json': {
61
+ schema: actionResultResponseSchema,
62
+ },
63
+ },
64
+ },
65
+ 400: {
66
+ description: 'Action not found or failed',
67
+ },
68
+ },
69
+ },
70
+ }
@@ -0,0 +1,12 @@
1
+ import { createSingleNotificationActionRoute, createSingleNotificationActionOpenApi } from '../../../lib/routeHelpers'
2
+
3
+ export const metadata = {
4
+ PUT: { requireAuth: true },
5
+ }
6
+
7
+ export const PUT = createSingleNotificationActionRoute('dismiss')
8
+
9
+ export const openApi = createSingleNotificationActionOpenApi(
10
+ 'Dismiss notification',
11
+ 'Notification dismissed'
12
+ )
@@ -0,0 +1,12 @@
1
+ import { createSingleNotificationActionRoute, createSingleNotificationActionOpenApi } from '../../../lib/routeHelpers'
2
+
3
+ export const metadata = {
4
+ PUT: { requireAuth: true },
5
+ }
6
+
7
+ export const PUT = createSingleNotificationActionRoute('markAsRead')
8
+
9
+ export const openApi = createSingleNotificationActionOpenApi(
10
+ 'Mark notification as read',
11
+ 'Notification marked as read'
12
+ )