@open-mercato/core 0.4.2-canary-f075c3eb92 → 0.4.2-canary-8a04af8836

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 (507) 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/login.js +25 -6
  18. package/dist/modules/auth/api/login.js.map +2 -2
  19. package/dist/modules/auth/api/profile/route.js +157 -0
  20. package/dist/modules/auth/api/profile/route.js.map +7 -0
  21. package/dist/modules/auth/api/reset/confirm.js +25 -2
  22. package/dist/modules/auth/api/reset/confirm.js.map +2 -2
  23. package/dist/modules/auth/api/reset.js +23 -0
  24. package/dist/modules/auth/api/reset.js.map +2 -2
  25. package/dist/modules/auth/api/sidebar/preferences/route.js +14 -9
  26. package/dist/modules/auth/api/sidebar/preferences/route.js.map +2 -2
  27. package/dist/modules/auth/api/users/route.js +4 -2
  28. package/dist/modules/auth/api/users/route.js.map +2 -2
  29. package/dist/modules/auth/backend/auth/profile/page.js +141 -0
  30. package/dist/modules/auth/backend/auth/profile/page.js.map +7 -0
  31. package/dist/modules/auth/backend/auth/profile/page.meta.js +13 -0
  32. package/dist/modules/auth/backend/auth/profile/page.meta.js.map +7 -0
  33. package/dist/modules/auth/backend/roles/[id]/edit/page.js +4 -1
  34. package/dist/modules/auth/backend/roles/[id]/edit/page.js.map +2 -2
  35. package/dist/modules/auth/backend/roles/page.js +3 -3
  36. package/dist/modules/auth/backend/roles/page.js.map +2 -2
  37. package/dist/modules/auth/backend/users/[id]/edit/page.js +18 -3
  38. package/dist/modules/auth/backend/users/[id]/edit/page.js.map +2 -2
  39. package/dist/modules/auth/backend/users/create/page.js +15 -2
  40. package/dist/modules/auth/backend/users/create/page.js.map +2 -2
  41. package/dist/modules/auth/backend/users/page.js +3 -3
  42. package/dist/modules/auth/backend/users/page.js.map +2 -2
  43. package/dist/modules/auth/cli.js +25 -11
  44. package/dist/modules/auth/cli.js.map +2 -2
  45. package/dist/modules/auth/commands/users.js +59 -2
  46. package/dist/modules/auth/commands/users.js.map +2 -2
  47. package/dist/modules/auth/data/validators.js +6 -3
  48. package/dist/modules/auth/data/validators.js.map +2 -2
  49. package/dist/modules/auth/frontend/login.js +112 -3
  50. package/dist/modules/auth/frontend/login.js.map +2 -2
  51. package/dist/modules/auth/frontend/reset/[token]/page.js +20 -10
  52. package/dist/modules/auth/frontend/reset/[token]/page.js.map +2 -2
  53. package/dist/modules/auth/lib/setup-app.js +46 -8
  54. package/dist/modules/auth/lib/setup-app.js.map +2 -2
  55. package/dist/modules/auth/notifications.js +112 -0
  56. package/dist/modules/auth/notifications.js.map +7 -0
  57. package/dist/modules/auth/services/authService.js +24 -3
  58. package/dist/modules/auth/services/authService.js.map +2 -2
  59. package/dist/modules/business_rules/api/execute/route.js +7 -1
  60. package/dist/modules/business_rules/api/execute/route.js.map +2 -2
  61. package/dist/modules/business_rules/backend/rules/page.js +4 -0
  62. package/dist/modules/business_rules/backend/rules/page.js.map +2 -2
  63. package/dist/modules/business_rules/backend/sets/page.js +3 -0
  64. package/dist/modules/business_rules/backend/sets/page.js.map +2 -2
  65. package/dist/modules/business_rules/cli.js +2 -1
  66. package/dist/modules/business_rules/cli.js.map +2 -2
  67. package/dist/modules/business_rules/lib/rule-engine.js +33 -3
  68. package/dist/modules/business_rules/lib/rule-engine.js.map +2 -2
  69. package/dist/modules/business_rules/notifications.js +28 -0
  70. package/dist/modules/business_rules/notifications.js.map +7 -0
  71. package/dist/modules/business_rules/subscribers/rule-execution-failed-notification.js +37 -0
  72. package/dist/modules/business_rules/subscribers/rule-execution-failed-notification.js.map +7 -0
  73. package/dist/modules/catalog/components/PriceKindSettings.js +2 -0
  74. package/dist/modules/catalog/components/PriceKindSettings.js.map +2 -2
  75. package/dist/modules/catalog/components/categories/CategoriesDataTable.js +2 -2
  76. package/dist/modules/catalog/components/categories/CategoriesDataTable.js.map +2 -2
  77. package/dist/modules/catalog/components/products/ProductsDataTable.js +2 -0
  78. package/dist/modules/catalog/components/products/ProductsDataTable.js.map +2 -2
  79. package/dist/modules/catalog/notifications.js +28 -0
  80. package/dist/modules/catalog/notifications.js.map +7 -0
  81. package/dist/modules/catalog/subscribers/low-stock-notification.js +38 -0
  82. package/dist/modules/catalog/subscribers/low-stock-notification.js.map +7 -0
  83. package/dist/modules/configs/cli.js +6 -0
  84. package/dist/modules/configs/cli.js.map +2 -2
  85. package/dist/modules/configs/components/CachePanel.js +4 -4
  86. package/dist/modules/configs/components/CachePanel.js.map +2 -2
  87. package/dist/modules/configs/lib/system-status.js +48 -1
  88. package/dist/modules/configs/lib/system-status.js.map +2 -2
  89. package/dist/modules/configs/lib/upgrade-actions.js +138 -306
  90. package/dist/modules/configs/lib/upgrade-actions.js.map +2 -2
  91. package/dist/modules/currencies/backend/currencies/page.js +3 -0
  92. package/dist/modules/currencies/backend/currencies/page.js.map +2 -2
  93. package/dist/modules/currencies/backend/exchange-rates/page.js +2 -0
  94. package/dist/modules/currencies/backend/exchange-rates/page.js.map +2 -2
  95. package/dist/modules/customers/backend/customers/companies/page.js +3 -0
  96. package/dist/modules/customers/backend/customers/companies/page.js.map +2 -2
  97. package/dist/modules/customers/backend/customers/deals/page.js +3 -0
  98. package/dist/modules/customers/backend/customers/deals/page.js.map +2 -2
  99. package/dist/modules/customers/backend/customers/people/page.js +3 -0
  100. package/dist/modules/customers/backend/customers/people/page.js.map +2 -2
  101. package/dist/modules/customers/commands/deals.js +31 -0
  102. package/dist/modules/customers/commands/deals.js.map +2 -2
  103. package/dist/modules/customers/components/CustomerTodosTable.js +1 -0
  104. package/dist/modules/customers/components/CustomerTodosTable.js.map +2 -2
  105. package/dist/modules/customers/notifications.js +48 -0
  106. package/dist/modules/customers/notifications.js.map +7 -0
  107. package/dist/modules/customers/widgets/dashboard/customer-todos/widget.js +2 -1
  108. package/dist/modules/customers/widgets/dashboard/customer-todos/widget.js.map +2 -2
  109. package/dist/modules/customers/widgets/dashboard/new-customers/widget.js +2 -1
  110. package/dist/modules/customers/widgets/dashboard/new-customers/widget.js.map +2 -2
  111. package/dist/modules/customers/widgets/dashboard/new-deals/widget.js +2 -1
  112. package/dist/modules/customers/widgets/dashboard/new-deals/widget.js.map +2 -2
  113. package/dist/modules/customers/widgets/dashboard/next-interactions/widget.js +2 -1
  114. package/dist/modules/customers/widgets/dashboard/next-interactions/widget.js.map +2 -2
  115. package/dist/modules/dashboards/cli.js +44 -5
  116. package/dist/modules/dashboards/cli.js.map +2 -2
  117. package/dist/modules/dashboards/components/WidgetVisibilityEditor.js +16 -11
  118. package/dist/modules/dashboards/components/WidgetVisibilityEditor.js.map +3 -3
  119. package/dist/modules/dashboards/lib/role-widgets.js +58 -0
  120. package/dist/modules/dashboards/lib/role-widgets.js.map +7 -0
  121. package/dist/modules/dashboards/services/widgetDataService.js +139 -3
  122. package/dist/modules/dashboards/services/widgetDataService.js.map +2 -2
  123. package/dist/modules/dashboards/setup.js +15 -0
  124. package/dist/modules/dashboards/setup.js.map +2 -2
  125. package/dist/modules/dashboards/widgets/dashboard/aov-kpi/widget.js +2 -1
  126. package/dist/modules/dashboards/widgets/dashboard/aov-kpi/widget.js.map +2 -2
  127. package/dist/modules/dashboards/widgets/dashboard/new-customers-kpi/widget.js +2 -1
  128. package/dist/modules/dashboards/widgets/dashboard/new-customers-kpi/widget.js.map +2 -2
  129. package/dist/modules/dashboards/widgets/dashboard/orders-by-status/widget.js +2 -1
  130. package/dist/modules/dashboards/widgets/dashboard/orders-by-status/widget.js.map +2 -2
  131. package/dist/modules/dashboards/widgets/dashboard/orders-kpi/widget.js +2 -1
  132. package/dist/modules/dashboards/widgets/dashboard/orders-kpi/widget.js.map +2 -2
  133. package/dist/modules/dashboards/widgets/dashboard/pipeline-summary/widget.js +2 -1
  134. package/dist/modules/dashboards/widgets/dashboard/pipeline-summary/widget.js.map +2 -2
  135. package/dist/modules/dashboards/widgets/dashboard/revenue-kpi/widget.js +2 -1
  136. package/dist/modules/dashboards/widgets/dashboard/revenue-kpi/widget.js.map +2 -2
  137. package/dist/modules/dashboards/widgets/dashboard/revenue-trend/widget.js +2 -1
  138. package/dist/modules/dashboards/widgets/dashboard/revenue-trend/widget.js.map +2 -2
  139. package/dist/modules/dashboards/widgets/dashboard/sales-by-region/widget.js +2 -1
  140. package/dist/modules/dashboards/widgets/dashboard/sales-by-region/widget.js.map +2 -2
  141. package/dist/modules/dashboards/widgets/dashboard/top-customers/widget.js +2 -1
  142. package/dist/modules/dashboards/widgets/dashboard/top-customers/widget.js.map +2 -2
  143. package/dist/modules/dashboards/widgets/dashboard/top-products/widget.js +2 -1
  144. package/dist/modules/dashboards/widgets/dashboard/top-products/widget.js.map +2 -2
  145. package/dist/modules/dictionaries/components/DictionaryTable.js +2 -0
  146. package/dist/modules/dictionaries/components/DictionaryTable.js.map +2 -2
  147. package/dist/modules/directory/api/get/tenants/lookup.js +70 -0
  148. package/dist/modules/directory/api/get/tenants/lookup.js.map +7 -0
  149. package/dist/modules/directory/backend/directory/organizations/page.js +2 -2
  150. package/dist/modules/directory/backend/directory/organizations/page.js.map +2 -2
  151. package/dist/modules/directory/backend/directory/tenants/page.js +2 -2
  152. package/dist/modules/directory/backend/directory/tenants/page.js.map +2 -2
  153. package/dist/modules/entities/backend/entities/user/[entityId]/records/page.js +2 -2
  154. package/dist/modules/entities/backend/entities/user/[entityId]/records/page.js.map +2 -2
  155. package/dist/modules/entities/components/SystemEntitiesTable.js +1 -1
  156. package/dist/modules/entities/components/SystemEntitiesTable.js.map +2 -2
  157. package/dist/modules/entities/components/UserEntitiesTable.js +2 -2
  158. package/dist/modules/entities/components/UserEntitiesTable.js.map +2 -2
  159. package/dist/modules/feature_toggles/components/FeatureTogglesTable.js +3 -3
  160. package/dist/modules/feature_toggles/components/FeatureTogglesTable.js.map +2 -2
  161. package/dist/modules/feature_toggles/components/OverridesTable.js +1 -1
  162. package/dist/modules/feature_toggles/components/OverridesTable.js.map +2 -2
  163. package/dist/modules/notifications/acl.js +11 -0
  164. package/dist/modules/notifications/acl.js.map +7 -0
  165. package/dist/modules/notifications/api/[id]/action/route.js +74 -0
  166. package/dist/modules/notifications/api/[id]/action/route.js.map +7 -0
  167. package/dist/modules/notifications/api/[id]/dismiss/route.js +15 -0
  168. package/dist/modules/notifications/api/[id]/dismiss/route.js.map +7 -0
  169. package/dist/modules/notifications/api/[id]/read/route.js +15 -0
  170. package/dist/modules/notifications/api/[id]/read/route.js.map +7 -0
  171. package/dist/modules/notifications/api/[id]/restore/route.js +53 -0
  172. package/dist/modules/notifications/api/[id]/restore/route.js.map +7 -0
  173. package/dist/modules/notifications/api/batch/route.js +17 -0
  174. package/dist/modules/notifications/api/batch/route.js.map +7 -0
  175. package/dist/modules/notifications/api/feature/route.js +17 -0
  176. package/dist/modules/notifications/api/feature/route.js.map +7 -0
  177. package/dist/modules/notifications/api/mark-all-read/route.js +35 -0
  178. package/dist/modules/notifications/api/mark-all-read/route.js.map +7 -0
  179. package/dist/modules/notifications/api/openapi.js +76 -0
  180. package/dist/modules/notifications/api/openapi.js.map +7 -0
  181. package/dist/modules/notifications/api/role/route.js +17 -0
  182. package/dist/modules/notifications/api/role/route.js.map +7 -0
  183. package/dist/modules/notifications/api/route.js +85 -0
  184. package/dist/modules/notifications/api/route.js.map +7 -0
  185. package/dist/modules/notifications/api/settings/route.js +155 -0
  186. package/dist/modules/notifications/api/settings/route.js.map +7 -0
  187. package/dist/modules/notifications/api/unread-count/route.js +38 -0
  188. package/dist/modules/notifications/api/unread-count/route.js.map +7 -0
  189. package/dist/modules/notifications/backend/config/notifications/page.js +10 -0
  190. package/dist/modules/notifications/backend/config/notifications/page.js.map +7 -0
  191. package/dist/modules/notifications/backend/config/notifications/page.meta.js +24 -0
  192. package/dist/modules/notifications/backend/config/notifications/page.meta.js.map +7 -0
  193. package/dist/modules/notifications/cli.js +16 -0
  194. package/dist/modules/notifications/cli.js.map +7 -0
  195. package/dist/modules/notifications/data/entities.js +112 -0
  196. package/dist/modules/notifications/data/entities.js.map +7 -0
  197. package/dist/modules/notifications/data/validators.js +98 -0
  198. package/dist/modules/notifications/data/validators.js.map +7 -0
  199. package/dist/modules/notifications/di.js +13 -0
  200. package/dist/modules/notifications/di.js.map +7 -0
  201. package/dist/modules/notifications/emails/NotificationEmail.js +58 -0
  202. package/dist/modules/notifications/emails/NotificationEmail.js.map +7 -0
  203. package/dist/modules/notifications/frontend/NotificationInboxPageClient.js +44 -0
  204. package/dist/modules/notifications/frontend/NotificationInboxPageClient.js.map +7 -0
  205. package/dist/modules/notifications/frontend/NotificationSettingsPageClient.js +220 -0
  206. package/dist/modules/notifications/frontend/NotificationSettingsPageClient.js.map +7 -0
  207. package/dist/modules/notifications/index.js +14 -0
  208. package/dist/modules/notifications/index.js.map +7 -0
  209. package/dist/modules/notifications/lib/deliveryConfig.js +107 -0
  210. package/dist/modules/notifications/lib/deliveryConfig.js.map +7 -0
  211. package/dist/modules/notifications/lib/deliveryStrategies.js +14 -0
  212. package/dist/modules/notifications/lib/deliveryStrategies.js.map +7 -0
  213. package/dist/modules/notifications/lib/events.js +12 -0
  214. package/dist/modules/notifications/lib/events.js.map +7 -0
  215. package/dist/modules/notifications/lib/notificationBuilder.js +66 -0
  216. package/dist/modules/notifications/lib/notificationBuilder.js.map +7 -0
  217. package/dist/modules/notifications/lib/notificationFactory.js +54 -0
  218. package/dist/modules/notifications/lib/notificationFactory.js.map +7 -0
  219. package/dist/modules/notifications/lib/notificationMapper.js +34 -0
  220. package/dist/modules/notifications/lib/notificationMapper.js.map +7 -0
  221. package/dist/modules/notifications/lib/notificationRecipients.js +35 -0
  222. package/dist/modules/notifications/lib/notificationRecipients.js.map +7 -0
  223. package/dist/modules/notifications/lib/notificationService.js +279 -0
  224. package/dist/modules/notifications/lib/notificationService.js.map +7 -0
  225. package/dist/modules/notifications/lib/routeHelpers.js +101 -0
  226. package/dist/modules/notifications/lib/routeHelpers.js.map +7 -0
  227. package/dist/modules/notifications/lib/safeHref.js +24 -0
  228. package/dist/modules/notifications/lib/safeHref.js.map +7 -0
  229. package/dist/modules/notifications/migrations/Migration20260123000001.js +70 -0
  230. package/dist/modules/notifications/migrations/Migration20260123000001.js.map +7 -0
  231. package/dist/modules/notifications/migrations/Migration20260126150000.js +37 -0
  232. package/dist/modules/notifications/migrations/Migration20260126150000.js.map +7 -0
  233. package/dist/modules/notifications/migrations/Migration20260129082610.js +13 -0
  234. package/dist/modules/notifications/migrations/Migration20260129082610.js.map +7 -0
  235. package/dist/modules/notifications/subscribers/deliver-notification.js +165 -0
  236. package/dist/modules/notifications/subscribers/deliver-notification.js.map +7 -0
  237. package/dist/modules/notifications/workers/create-notification.worker.js +70 -0
  238. package/dist/modules/notifications/workers/create-notification.worker.js.map +7 -0
  239. package/dist/modules/planner/backend/planner/availability-rulesets/page.js +2 -2
  240. package/dist/modules/planner/backend/planner/availability-rulesets/page.js.map +2 -2
  241. package/dist/modules/query_index/cli.js +63 -7
  242. package/dist/modules/query_index/cli.js.map +2 -2
  243. package/dist/modules/query_index/components/QueryIndexesTable.js +7 -1
  244. package/dist/modules/query_index/components/QueryIndexesTable.js.map +2 -2
  245. package/dist/modules/resources/backend/resources/resource-types/page.js +2 -2
  246. package/dist/modules/resources/backend/resources/resource-types/page.js.map +2 -2
  247. package/dist/modules/resources/backend/resources/resources/page.js +2 -2
  248. package/dist/modules/resources/backend/resources/resources/page.js.map +2 -2
  249. package/dist/modules/sales/backend/sales/channels/offers/page.js +2 -0
  250. package/dist/modules/sales/backend/sales/channels/offers/page.js.map +2 -2
  251. package/dist/modules/sales/backend/sales/channels/page.js +2 -0
  252. package/dist/modules/sales/backend/sales/channels/page.js.map +2 -2
  253. package/dist/modules/sales/cli.js +2 -42
  254. package/dist/modules/sales/cli.js.map +2 -2
  255. package/dist/modules/sales/commands/documents.js +53 -0
  256. package/dist/modules/sales/commands/documents.js.map +2 -2
  257. package/dist/modules/sales/commands/payments.js +26 -0
  258. package/dist/modules/sales/commands/payments.js.map +2 -2
  259. package/dist/modules/sales/components/AdjustmentKindSettings.js +2 -2
  260. package/dist/modules/sales/components/AdjustmentKindSettings.js.map +2 -2
  261. package/dist/modules/sales/components/PaymentMethodsSettings.js +2 -2
  262. package/dist/modules/sales/components/PaymentMethodsSettings.js.map +2 -2
  263. package/dist/modules/sales/components/ShippingMethodsSettings.js +2 -2
  264. package/dist/modules/sales/components/ShippingMethodsSettings.js.map +2 -2
  265. package/dist/modules/sales/components/TaxRatesSettings.js +2 -2
  266. package/dist/modules/sales/components/TaxRatesSettings.js.map +2 -2
  267. package/dist/modules/sales/components/channels/SalesChannelOffersPanel.js +2 -0
  268. package/dist/modules/sales/components/channels/SalesChannelOffersPanel.js.map +2 -2
  269. package/dist/modules/sales/components/documents/AdjustmentsSection.js +2 -0
  270. package/dist/modules/sales/components/documents/AdjustmentsSection.js.map +2 -2
  271. package/dist/modules/sales/components/documents/PaymentsSection.js +2 -1
  272. package/dist/modules/sales/components/documents/PaymentsSection.js.map +2 -2
  273. package/dist/modules/sales/components/documents/SalesDocumentsTable.js +2 -0
  274. package/dist/modules/sales/components/documents/SalesDocumentsTable.js.map +2 -2
  275. package/dist/modules/sales/lib/seeds.js +48 -0
  276. package/dist/modules/sales/lib/seeds.js.map +7 -0
  277. package/dist/modules/sales/notifications.client.js +51 -0
  278. package/dist/modules/sales/notifications.client.js.map +7 -0
  279. package/dist/modules/sales/notifications.js +88 -0
  280. package/dist/modules/sales/notifications.js.map +7 -0
  281. package/dist/modules/sales/subscribers/quote-expiring-notification.js +38 -0
  282. package/dist/modules/sales/subscribers/quote-expiring-notification.js.map +7 -0
  283. package/dist/modules/sales/widgets/notifications/SalesOrderCreatedRenderer.js +137 -0
  284. package/dist/modules/sales/widgets/notifications/SalesOrderCreatedRenderer.js.map +7 -0
  285. package/dist/modules/sales/widgets/notifications/SalesQuoteCreatedRenderer.js +137 -0
  286. package/dist/modules/sales/widgets/notifications/SalesQuoteCreatedRenderer.js.map +7 -0
  287. package/dist/modules/sales/widgets/notifications/index.js +7 -0
  288. package/dist/modules/sales/widgets/notifications/index.js.map +7 -0
  289. package/dist/modules/sales/widgets/notifications/useSalesDocumentTotals.js +60 -0
  290. package/dist/modules/sales/widgets/notifications/useSalesDocumentTotals.js.map +7 -0
  291. package/dist/modules/staff/backend/staff/team-members/page.js +1 -1
  292. package/dist/modules/staff/backend/staff/team-members/page.js.map +2 -2
  293. package/dist/modules/staff/backend/staff/team-roles/page.js +2 -2
  294. package/dist/modules/staff/backend/staff/team-roles/page.js.map +2 -2
  295. package/dist/modules/staff/backend/staff/teams/[id]/edit/page.js +2 -2
  296. package/dist/modules/staff/backend/staff/teams/[id]/edit/page.js.map +2 -2
  297. package/dist/modules/staff/backend/staff/teams/page.js +2 -2
  298. package/dist/modules/staff/backend/staff/teams/page.js.map +2 -2
  299. package/dist/modules/staff/commands/leave-requests.js +79 -0
  300. package/dist/modules/staff/commands/leave-requests.js.map +2 -2
  301. package/dist/modules/staff/notifications.js +75 -0
  302. package/dist/modules/staff/notifications.js.map +7 -0
  303. package/dist/modules/workflows/backend/definitions/page.js +5 -0
  304. package/dist/modules/workflows/backend/definitions/page.js.map +2 -2
  305. package/dist/modules/workflows/backend/instances/page.js +3 -0
  306. package/dist/modules/workflows/backend/instances/page.js.map +2 -2
  307. package/dist/modules/workflows/backend/tasks/page.js +3 -0
  308. package/dist/modules/workflows/backend/tasks/page.js.map +2 -2
  309. package/dist/modules/workflows/cli.js +12 -12
  310. package/dist/modules/workflows/cli.js.map +2 -2
  311. package/dist/modules/workflows/lib/transition-handler.js +14 -6
  312. package/dist/modules/workflows/lib/transition-handler.js.map +2 -2
  313. package/dist/modules/workflows/notifications.js +28 -0
  314. package/dist/modules/workflows/notifications.js.map +7 -0
  315. package/dist/modules/workflows/subscribers/task-assigned-notification.js +38 -0
  316. package/dist/modules/workflows/subscribers/task-assigned-notification.js.map +7 -0
  317. package/generated/entities/notification/index.ts +27 -0
  318. package/generated/entities.ids.generated.ts +5 -1
  319. package/generated/entity-fields-registry.ts +2 -0
  320. package/package.json +2 -2
  321. package/src/modules/api_docs/frontend/docs/api/page.tsx +3 -2
  322. package/src/modules/api_keys/backend/api-keys/page.tsx +1 -1
  323. package/src/modules/attachments/components/AttachmentLibrary.tsx +4 -0
  324. package/src/modules/attachments/components/AttachmentPartitionSettings.tsx +2 -0
  325. package/src/modules/auth/README.md +1 -1
  326. package/src/modules/auth/__tests__/cli-setup-acl.test.ts +1 -1
  327. package/src/modules/auth/api/__tests__/login.test.ts +2 -0
  328. package/src/modules/auth/api/admin/nav.ts +10 -6
  329. package/src/modules/auth/api/login.ts +26 -7
  330. package/src/modules/auth/api/profile/route.ts +163 -0
  331. package/src/modules/auth/api/reset/confirm.ts +25 -2
  332. package/src/modules/auth/api/reset.ts +23 -0
  333. package/src/modules/auth/api/sidebar/preferences/route.ts +21 -12
  334. package/src/modules/auth/api/users/route.ts +5 -2
  335. package/src/modules/auth/backend/auth/profile/page.meta.ts +9 -0
  336. package/src/modules/auth/backend/auth/profile/page.tsx +174 -0
  337. package/src/modules/auth/backend/roles/[id]/edit/page.tsx +4 -1
  338. package/src/modules/auth/backend/roles/page.tsx +3 -3
  339. package/src/modules/auth/backend/users/[id]/edit/page.tsx +22 -3
  340. package/src/modules/auth/backend/users/create/page.tsx +19 -2
  341. package/src/modules/auth/backend/users/page.tsx +3 -3
  342. package/src/modules/auth/cli.ts +38 -11
  343. package/src/modules/auth/commands/users.ts +73 -2
  344. package/src/modules/auth/data/validators.ts +6 -2
  345. package/src/modules/auth/frontend/login.tsx +134 -5
  346. package/src/modules/auth/frontend/reset/[token]/page.tsx +24 -11
  347. package/src/modules/auth/i18n/de.json +48 -1
  348. package/src/modules/auth/i18n/en.json +48 -1
  349. package/src/modules/auth/i18n/es.json +48 -1
  350. package/src/modules/auth/i18n/pl.json +48 -1
  351. package/src/modules/auth/lib/setup-app.ts +63 -9
  352. package/src/modules/auth/notifications.ts +109 -0
  353. package/src/modules/auth/services/authService.ts +27 -4
  354. package/src/modules/business_rules/api/execute/route.ts +8 -1
  355. package/src/modules/business_rules/backend/rules/page.tsx +4 -0
  356. package/src/modules/business_rules/backend/sets/page.tsx +3 -0
  357. package/src/modules/business_rules/cli.ts +2 -1
  358. package/src/modules/business_rules/i18n/en.json +3 -1
  359. package/src/modules/business_rules/lib/__tests__/rule-engine.test.ts +51 -0
  360. package/src/modules/business_rules/lib/rule-engine.ts +57 -3
  361. package/src/modules/business_rules/notifications.ts +25 -0
  362. package/src/modules/business_rules/subscribers/rule-execution-failed-notification.ts +50 -0
  363. package/src/modules/catalog/components/PriceKindSettings.tsx +2 -0
  364. package/src/modules/catalog/components/categories/CategoriesDataTable.tsx +2 -2
  365. package/src/modules/catalog/components/products/ProductsDataTable.tsx +2 -0
  366. package/src/modules/catalog/i18n/en.json +3 -1
  367. package/src/modules/catalog/notifications.ts +25 -0
  368. package/src/modules/catalog/subscribers/low-stock-notification.ts +52 -0
  369. package/src/modules/configs/cli.ts +6 -0
  370. package/src/modules/configs/components/CachePanel.tsx +4 -4
  371. package/src/modules/configs/i18n/en.json +12 -2
  372. package/src/modules/configs/i18n/pl.json +12 -2
  373. package/src/modules/configs/lib/system-status.ts +48 -1
  374. package/src/modules/configs/lib/system-status.types.ts +1 -0
  375. package/src/modules/configs/lib/upgrade-actions.ts +157 -351
  376. package/src/modules/currencies/backend/currencies/page.tsx +3 -0
  377. package/src/modules/currencies/backend/exchange-rates/page.tsx +2 -0
  378. package/src/modules/customers/backend/customers/companies/page.tsx +3 -0
  379. package/src/modules/customers/backend/customers/deals/page.tsx +3 -0
  380. package/src/modules/customers/backend/customers/people/page.tsx +3 -0
  381. package/src/modules/customers/commands/deals.ts +39 -0
  382. package/src/modules/customers/components/CustomerTodosTable.tsx +1 -0
  383. package/src/modules/customers/i18n/en.json +5 -1
  384. package/src/modules/customers/notifications.ts +44 -0
  385. package/src/modules/customers/widgets/dashboard/customer-todos/widget.ts +2 -2
  386. package/src/modules/customers/widgets/dashboard/new-customers/widget.ts +2 -2
  387. package/src/modules/customers/widgets/dashboard/new-deals/widget.ts +2 -2
  388. package/src/modules/customers/widgets/dashboard/next-interactions/widget.ts +2 -2
  389. package/src/modules/dashboards/cli.ts +55 -5
  390. package/src/modules/dashboards/components/WidgetVisibilityEditor.tsx +22 -11
  391. package/src/modules/dashboards/lib/role-widgets.ts +80 -0
  392. package/src/modules/dashboards/services/widgetDataService.ts +164 -4
  393. package/src/modules/dashboards/setup.ts +16 -0
  394. package/src/modules/dashboards/widgets/dashboard/aov-kpi/widget.ts +2 -2
  395. package/src/modules/dashboards/widgets/dashboard/new-customers-kpi/widget.ts +2 -2
  396. package/src/modules/dashboards/widgets/dashboard/orders-by-status/widget.ts +2 -2
  397. package/src/modules/dashboards/widgets/dashboard/orders-kpi/widget.ts +2 -2
  398. package/src/modules/dashboards/widgets/dashboard/pipeline-summary/widget.ts +2 -2
  399. package/src/modules/dashboards/widgets/dashboard/revenue-kpi/widget.ts +2 -2
  400. package/src/modules/dashboards/widgets/dashboard/revenue-trend/widget.ts +2 -2
  401. package/src/modules/dashboards/widgets/dashboard/sales-by-region/widget.ts +2 -2
  402. package/src/modules/dashboards/widgets/dashboard/top-customers/widget.ts +2 -2
  403. package/src/modules/dashboards/widgets/dashboard/top-products/widget.ts +2 -2
  404. package/src/modules/dictionaries/components/DictionaryTable.tsx +2 -0
  405. package/src/modules/directory/api/get/tenants/lookup.ts +75 -0
  406. package/src/modules/directory/backend/directory/organizations/page.tsx +2 -2
  407. package/src/modules/directory/backend/directory/tenants/page.tsx +2 -2
  408. package/src/modules/entities/backend/entities/user/[entityId]/records/page.tsx +2 -2
  409. package/src/modules/entities/components/SystemEntitiesTable.tsx +1 -1
  410. package/src/modules/entities/components/UserEntitiesTable.tsx +2 -2
  411. package/src/modules/feature_toggles/components/FeatureTogglesTable.tsx +3 -4
  412. package/src/modules/feature_toggles/components/OverridesTable.tsx +1 -1
  413. package/src/modules/notifications/__tests__/deliver-notification.test.ts +195 -0
  414. package/src/modules/notifications/__tests__/deliveryStrategies.test.ts +19 -0
  415. package/src/modules/notifications/__tests__/notificationService.test.ts +208 -0
  416. package/src/modules/notifications/acl.ts +7 -0
  417. package/src/modules/notifications/api/[id]/action/route.ts +75 -0
  418. package/src/modules/notifications/api/[id]/dismiss/route.ts +12 -0
  419. package/src/modules/notifications/api/[id]/read/route.ts +12 -0
  420. package/src/modules/notifications/api/[id]/restore/route.ts +53 -0
  421. package/src/modules/notifications/api/batch/route.ts +14 -0
  422. package/src/modules/notifications/api/feature/route.ts +14 -0
  423. package/src/modules/notifications/api/mark-all-read/route.ts +34 -0
  424. package/src/modules/notifications/api/openapi.ts +76 -0
  425. package/src/modules/notifications/api/role/route.ts +14 -0
  426. package/src/modules/notifications/api/route.ts +92 -0
  427. package/src/modules/notifications/api/settings/route.ts +157 -0
  428. package/src/modules/notifications/api/unread-count/route.ts +38 -0
  429. package/src/modules/notifications/backend/config/notifications/page.meta.ts +22 -0
  430. package/src/modules/notifications/backend/config/notifications/page.tsx +12 -0
  431. package/src/modules/notifications/cli.ts +18 -0
  432. package/src/modules/notifications/data/entities.ts +99 -0
  433. package/src/modules/notifications/data/validators.ts +115 -0
  434. package/src/modules/notifications/di.ts +11 -0
  435. package/src/modules/notifications/emails/NotificationEmail.tsx +98 -0
  436. package/src/modules/notifications/frontend/NotificationInboxPageClient.tsx +42 -0
  437. package/src/modules/notifications/frontend/NotificationSettingsPageClient.tsx +233 -0
  438. package/src/modules/notifications/i18n/de.json +50 -0
  439. package/src/modules/notifications/i18n/en.json +50 -0
  440. package/src/modules/notifications/i18n/es.json +50 -0
  441. package/src/modules/notifications/i18n/pl.json +50 -0
  442. package/src/modules/notifications/index.ts +12 -0
  443. package/src/modules/notifications/lib/deliveryConfig.ts +153 -0
  444. package/src/modules/notifications/lib/deliveryStrategies.ts +50 -0
  445. package/src/modules/notifications/lib/events.ts +48 -0
  446. package/src/modules/notifications/lib/notificationBuilder.ts +121 -0
  447. package/src/modules/notifications/lib/notificationFactory.ts +76 -0
  448. package/src/modules/notifications/lib/notificationMapper.ts +33 -0
  449. package/src/modules/notifications/lib/notificationRecipients.ts +83 -0
  450. package/src/modules/notifications/lib/notificationService.ts +414 -0
  451. package/src/modules/notifications/lib/routeHelpers.ts +151 -0
  452. package/src/modules/notifications/lib/safeHref.ts +29 -0
  453. package/src/modules/notifications/migrations/.snapshot-open-mercato.json +336 -0
  454. package/src/modules/notifications/migrations/Migration20260123000001.ts +73 -0
  455. package/src/modules/notifications/migrations/Migration20260126150000.ts +39 -0
  456. package/src/modules/notifications/migrations/Migration20260129082610.ts +13 -0
  457. package/src/modules/notifications/subscribers/deliver-notification.ts +204 -0
  458. package/src/modules/notifications/workers/create-notification.worker.ts +122 -0
  459. package/src/modules/planner/backend/planner/availability-rulesets/page.tsx +2 -2
  460. package/src/modules/query_index/cli.ts +82 -13
  461. package/src/modules/query_index/components/QueryIndexesTable.tsx +8 -2
  462. package/src/modules/resources/backend/resources/resource-types/page.tsx +2 -2
  463. package/src/modules/resources/backend/resources/resources/page.tsx +2 -2
  464. package/src/modules/sales/backend/sales/channels/offers/page.tsx +2 -0
  465. package/src/modules/sales/backend/sales/channels/page.tsx +2 -0
  466. package/src/modules/sales/cli.ts +2 -43
  467. package/src/modules/sales/commands/documents.ts +65 -0
  468. package/src/modules/sales/commands/payments.ts +33 -0
  469. package/src/modules/sales/components/AdjustmentKindSettings.tsx +2 -2
  470. package/src/modules/sales/components/PaymentMethodsSettings.tsx +2 -2
  471. package/src/modules/sales/components/ShippingMethodsSettings.tsx +2 -2
  472. package/src/modules/sales/components/TaxRatesSettings.tsx +2 -2
  473. package/src/modules/sales/components/channels/SalesChannelOffersPanel.tsx +2 -0
  474. package/src/modules/sales/components/documents/AdjustmentsSection.tsx +2 -0
  475. package/src/modules/sales/components/documents/PaymentsSection.tsx +2 -1
  476. package/src/modules/sales/components/documents/SalesDocumentsTable.tsx +2 -0
  477. package/src/modules/sales/i18n/de.json +20 -0
  478. package/src/modules/sales/i18n/en.json +25 -1
  479. package/src/modules/sales/i18n/es.json +20 -0
  480. package/src/modules/sales/i18n/pl.json +20 -0
  481. package/src/modules/sales/lib/seeds.ts +53 -0
  482. package/src/modules/sales/notifications.client.ts +65 -0
  483. package/src/modules/sales/notifications.ts +82 -0
  484. package/src/modules/sales/subscribers/quote-expiring-notification.ts +53 -0
  485. package/src/modules/sales/widgets/notifications/SalesOrderCreatedRenderer.tsx +156 -0
  486. package/src/modules/sales/widgets/notifications/SalesQuoteCreatedRenderer.tsx +156 -0
  487. package/src/modules/sales/widgets/notifications/index.ts +2 -0
  488. package/src/modules/sales/widgets/notifications/useSalesDocumentTotals.ts +81 -0
  489. package/src/modules/staff/backend/staff/team-members/page.tsx +1 -1
  490. package/src/modules/staff/backend/staff/team-roles/page.tsx +2 -2
  491. package/src/modules/staff/backend/staff/teams/[id]/edit/page.tsx +2 -2
  492. package/src/modules/staff/backend/staff/teams/page.tsx +2 -2
  493. package/src/modules/staff/commands/leave-requests.ts +94 -0
  494. package/src/modules/staff/i18n/de.json +6 -0
  495. package/src/modules/staff/i18n/en.json +9 -1
  496. package/src/modules/staff/i18n/es.json +6 -0
  497. package/src/modules/staff/i18n/pl.json +6 -0
  498. package/src/modules/staff/notifications.ts +71 -0
  499. package/src/modules/workflows/backend/definitions/page.tsx +5 -0
  500. package/src/modules/workflows/backend/instances/page.tsx +4 -1
  501. package/src/modules/workflows/backend/tasks/page.tsx +4 -1
  502. package/src/modules/workflows/cli.ts +12 -12
  503. package/src/modules/workflows/i18n/en.json +3 -1
  504. package/src/modules/workflows/lib/__tests__/transition-handler.test.ts +6 -3
  505. package/src/modules/workflows/lib/transition-handler.ts +18 -6
  506. package/src/modules/workflows/notifications.ts +25 -0
  507. package/src/modules/workflows/subscribers/task-assigned-notification.ts +53 -0
@@ -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 }) => {
@@ -253,6 +253,7 @@ export function CustomerTodosTable(): React.JSX.Element {
253
253
  <RowActions
254
254
  items={[
255
255
  {
256
+ id: 'open-customer',
256
257
  label: t('customers.workPlan.customerTodos.table.actions.openCustomer'),
257
258
  href: customerLink,
258
259
  },
@@ -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
@@ -1,6 +1,6 @@
1
- import type { DashboardWidgetModule } from '@open-mercato/shared/modules/dashboard/widgets'
2
- import CustomerTodosWidget from './widget.client'
1
+ import { lazyDashboardWidget, type DashboardWidgetModule } from '@open-mercato/shared/modules/dashboard/widgets'
3
2
  import { DEFAULT_SETTINGS, hydrateCustomerTodoSettings, type CustomerTodoWidgetSettings } from './config'
3
+ const CustomerTodosWidget = lazyDashboardWidget(() => import('./widget.client'))
4
4
 
5
5
  const widget: DashboardWidgetModule<CustomerTodoWidgetSettings> = {
6
6
  metadata: {
@@ -1,6 +1,6 @@
1
- import type { DashboardWidgetModule } from '@open-mercato/shared/modules/dashboard/widgets'
2
- import CustomerNewCustomersWidget from './widget.client'
1
+ import { lazyDashboardWidget, type DashboardWidgetModule } from '@open-mercato/shared/modules/dashboard/widgets'
3
2
  import { DEFAULT_SETTINGS, hydrateNewCustomersSettings, type CustomerNewCustomersSettings } from './config'
3
+ const CustomerNewCustomersWidget = lazyDashboardWidget(() => import('./widget.client'))
4
4
 
5
5
  const widget: DashboardWidgetModule<CustomerNewCustomersSettings> = {
6
6
  metadata: {
@@ -1,6 +1,6 @@
1
- import type { DashboardWidgetModule } from '@open-mercato/shared/modules/dashboard/widgets'
2
- import CustomerNewDealsWidget from './widget.client'
1
+ import { lazyDashboardWidget, type DashboardWidgetModule } from '@open-mercato/shared/modules/dashboard/widgets'
3
2
  import { DEFAULT_SETTINGS, hydrateNewDealsSettings, type CustomerNewDealsSettings } from './config'
3
+ const CustomerNewDealsWidget = lazyDashboardWidget(() => import('./widget.client'))
4
4
 
5
5
  const widget: DashboardWidgetModule<CustomerNewDealsSettings> = {
6
6
  metadata: {
@@ -1,10 +1,10 @@
1
- import type { DashboardWidgetModule } from '@open-mercato/shared/modules/dashboard/widgets'
2
- import CustomerNextInteractionsWidget from './widget.client'
1
+ import { lazyDashboardWidget, type DashboardWidgetModule } from '@open-mercato/shared/modules/dashboard/widgets'
3
2
  import {
4
3
  DEFAULT_SETTINGS,
5
4
  hydrateNextInteractionsSettings,
6
5
  type CustomerNextInteractionsSettings,
7
6
  } from './config'
7
+ const CustomerNextInteractionsWidget = lazyDashboardWidget(() => import('./widget.client'))
8
8
 
9
9
  const widget: DashboardWidgetModule<CustomerNextInteractionsSettings> = {
10
10
  metadata: {
@@ -4,6 +4,7 @@ import type { EntityManager } from '@mikro-orm/postgresql'
4
4
  import { DashboardRoleWidgets } from '@open-mercato/core/modules/dashboards/data/entities'
5
5
  import { Role } from '@open-mercato/core/modules/auth/data/entities'
6
6
  import { loadAllWidgets } from '@open-mercato/core/modules/dashboards/lib/widgets'
7
+ import { appendWidgetsToRoles, resolveAnalyticsWidgetIds } from '@open-mercato/core/modules/dashboards/lib/role-widgets'
7
8
  import { seedAnalyticsData } from './seed/analytics'
8
9
 
9
10
  type Args = Record<string, string>
@@ -41,15 +42,25 @@ export async function seedDashboardDefaultsForTenant(
41
42
  const widgetMap = new Map(widgets.map((widget) => [widget.metadata.id, widget]))
42
43
  const resolvedWidgetIds = widgetIds && widgetIds.length
43
44
  ? widgetIds.filter((id) => widgetMap.has(id))
44
- : widgets.filter((widget) => widget.metadata.defaultEnabled).map((widget) => widget.metadata.id)
45
+ : null
46
+ const defaultWidgetIds = widgets
47
+ .filter((widget) => widget.metadata.defaultEnabled)
48
+ .map((widget) => widget.metadata.id)
49
+ const allWidgetIds = widgets.map((widget) => widget.metadata.id)
45
50
 
46
- if (!resolvedWidgetIds.length) {
51
+ if (resolvedWidgetIds && resolvedWidgetIds.length === 0) {
47
52
  log('No widgets resolved for dashboard seeding.')
48
53
  return false
49
54
  }
50
55
 
51
56
  await em.transactional(async (tem) => {
52
57
  for (const roleName of roleNames) {
58
+ const isAdminRole = roleName === 'admin' || roleName === 'superadmin'
59
+ const roleWidgetIds = resolvedWidgetIds ?? (isAdminRole ? allWidgetIds : defaultWidgetIds)
60
+ if (!roleWidgetIds.length) {
61
+ log(`No widgets resolved for role "${roleName}".`)
62
+ continue
63
+ }
53
64
  const role = await tem.findOne(Role, { name: roleName })
54
65
  if (!role) {
55
66
  log(`Skipping role "${roleName}" (not found)`)
@@ -62,7 +73,7 @@ export async function seedDashboardDefaultsForTenant(
62
73
  deletedAt: null,
63
74
  })
64
75
  if (existing) {
65
- existing.widgetIdsJson = resolvedWidgetIds
76
+ existing.widgetIdsJson = roleWidgetIds
66
77
  tem.persist(existing)
67
78
  log(`Updated dashboard widgets for role "${roleName}"`)
68
79
  } else {
@@ -70,7 +81,7 @@ export async function seedDashboardDefaultsForTenant(
70
81
  roleId: String(role.id),
71
82
  tenantId,
72
83
  organizationId,
73
- widgetIdsJson: resolvedWidgetIds,
84
+ widgetIdsJson: roleWidgetIds,
74
85
  createdAt: new Date(),
75
86
  updatedAt: null,
76
87
  deletedAt: null,
@@ -120,6 +131,45 @@ const seedDefaults: ModuleCli = {
120
131
  },
121
132
  }
122
133
 
134
+ const enableAnalyticsWidgets: ModuleCli = {
135
+ command: 'enable-analytics-widgets',
136
+ async run(rest) {
137
+ const args = parseArgs(rest)
138
+ const tenantId = args.tenant || args.tenantId || null
139
+ const organizationId = args.organization || args.organizationId || args.org || null
140
+ const roleCsv = args.roles || 'admin,employee'
141
+ if (!tenantId) {
142
+ console.error('Usage: mercato dashboards enable-analytics-widgets --tenant <tenantId> [--org <orgId>] [--roles admin,employee]')
143
+ return
144
+ }
145
+
146
+ const roleNames = roleCsv
147
+ .split(',')
148
+ .map((name) => name.trim())
149
+ .filter(Boolean)
150
+
151
+ if (!roleNames.length) {
152
+ console.log('No roles provided, nothing to update.')
153
+ return
154
+ }
155
+
156
+ const { resolve } = await createRequestContainer()
157
+ const em = resolve('em') as EntityManager
158
+ const widgetIds = await resolveAnalyticsWidgetIds()
159
+
160
+ const updated = await appendWidgetsToRoles(em, {
161
+ tenantId,
162
+ organizationId,
163
+ roleNames,
164
+ widgetIds,
165
+ })
166
+
167
+ if (!updated) {
168
+ console.log('No dashboard role widgets updated.')
169
+ }
170
+ },
171
+ }
172
+
123
173
  const seedAnalytics: ModuleCli = {
124
174
  command: 'seed-analytics',
125
175
  async run(rest) {
@@ -282,4 +332,4 @@ const debugAnalytics: ModuleCli = {
282
332
  },
283
333
  }
284
334
 
285
- export default [seedDefaults, seedAnalytics, debugAnalytics]
335
+ export default [seedDefaults, enableAnalyticsWidgets, seedAnalytics, debugAnalytics]
@@ -44,9 +44,13 @@ type UserProps = BaseProps & {
44
44
 
45
45
  type WidgetVisibilityEditorProps = RoleProps | UserProps
46
46
 
47
+ export type WidgetVisibilityEditorHandle = {
48
+ save: () => Promise<void>
49
+ }
50
+
47
51
  const EMPTY: string[] = []
48
52
 
49
- export function WidgetVisibilityEditor(props: WidgetVisibilityEditorProps) {
53
+ export const WidgetVisibilityEditor = React.forwardRef<WidgetVisibilityEditorHandle, WidgetVisibilityEditorProps>(function WidgetVisibilityEditor(props, ref) {
50
54
  const t = useT()
51
55
  const { kind, targetId, tenantId, organizationId } = props
52
56
  const [catalog, setCatalog] = React.useState<WidgetCatalogItem[]>([])
@@ -60,6 +64,15 @@ export function WidgetVisibilityEditor(props: WidgetVisibilityEditorProps) {
60
64
  const [originalMode, setOriginalMode] = React.useState<'inherit' | 'override'>('inherit')
61
65
  const [effective, setEffective] = React.useState<string[]>(EMPTY)
62
66
 
67
+ const dirty = React.useMemo(() => {
68
+ if (kind === 'user') {
69
+ if (mode !== originalMode) return true
70
+ if (mode === 'override') return selected.join('|') !== original.join('|')
71
+ return false
72
+ }
73
+ return selected.join('|') !== original.join('|')
74
+ }, [kind, mode, original, originalMode, selected])
75
+
63
76
  const loadCatalog = React.useCallback(async () => {
64
77
  const data = await readApiResultOrThrow<{ items?: unknown[] }>(
65
78
  '/api/dashboards/widgets/catalog',
@@ -149,6 +162,9 @@ export function WidgetVisibilityEditor(props: WidgetVisibilityEditorProps) {
149
162
  }, [original, originalMode])
150
163
 
151
164
  const save = React.useCallback(async () => {
165
+ if (loading) return
166
+ if (error && catalog.length === 0) return
167
+ if (!dirty) return
152
168
  setSaving(true)
153
169
  setError(null)
154
170
  try {
@@ -202,16 +218,9 @@ export function WidgetVisibilityEditor(props: WidgetVisibilityEditorProps) {
202
218
  } finally {
203
219
  setSaving(false)
204
220
  }
205
- }, [kind, mode, organizationId, selected, t, targetId, tenantId])
221
+ }, [catalog.length, dirty, error, kind, loading, mode, organizationId, selected, t, targetId, tenantId])
206
222
 
207
- const dirty = React.useMemo(() => {
208
- if (kind === 'user') {
209
- if (mode !== originalMode) return true
210
- if (mode === 'override') return selected.join('|') !== original.join('|')
211
- return false
212
- }
213
- return selected.join('|') !== original.join('|')
214
- }, [kind, mode, original, originalMode, selected])
223
+ React.useImperativeHandle(ref, () => ({ save }), [save])
215
224
 
216
225
  if (loading) {
217
226
  return (
@@ -297,4 +306,6 @@ export function WidgetVisibilityEditor(props: WidgetVisibilityEditorProps) {
297
306
  </div>
298
307
  </div>
299
308
  )
300
- }
309
+ })
310
+
311
+ WidgetVisibilityEditor.displayName = 'WidgetVisibilityEditor'
@@ -0,0 +1,80 @@
1
+ import type { EntityManager } from '@mikro-orm/postgresql'
2
+ import { Role } from '@open-mercato/core/modules/auth/data/entities'
3
+ import { DashboardRoleWidgets } from '../data/entities'
4
+ import { loadAllWidgets } from './widgets'
5
+
6
+ type RoleWidgetScope = {
7
+ tenantId: string
8
+ organizationId?: string | null
9
+ roleNames: string[]
10
+ widgetIds: string[]
11
+ }
12
+
13
+ async function findRoleByName(
14
+ em: EntityManager,
15
+ roleName: string,
16
+ tenantId: string,
17
+ ): Promise<Role | null> {
18
+ const tenantRole = await em.findOne(Role, { name: roleName, tenantId })
19
+ if (tenantRole) return tenantRole
20
+ return em.findOne(Role, { name: roleName, tenantId: null })
21
+ }
22
+
23
+ export async function resolveAnalyticsWidgetIds(): Promise<string[]> {
24
+ const widgets = await loadAllWidgets()
25
+ return widgets
26
+ .filter((widget) => widget.metadata.category === 'analytics' || widget.metadata.id.startsWith('dashboards.analytics.'))
27
+ .map((widget) => widget.metadata.id)
28
+ }
29
+
30
+ export async function appendWidgetsToRoles(
31
+ em: EntityManager,
32
+ { tenantId, organizationId = null, roleNames, widgetIds }: RoleWidgetScope,
33
+ ): Promise<boolean> {
34
+ const trimmedTenantId = tenantId.trim()
35
+ const widgets = await loadAllWidgets()
36
+ const validWidgetIds = new Set(widgets.map((widget) => widget.metadata.id))
37
+ const resolvedWidgetIds = widgetIds.filter((id) => validWidgetIds.has(id))
38
+ if (!resolvedWidgetIds.length) return false
39
+
40
+ let updated = false
41
+ await em.transactional(async (tem) => {
42
+ for (const roleName of roleNames) {
43
+ const role = await findRoleByName(tem, roleName, trimmedTenantId)
44
+ if (!role) continue
45
+
46
+ const record = await tem.findOne(DashboardRoleWidgets, {
47
+ roleId: String(role.id),
48
+ tenantId: trimmedTenantId,
49
+ organizationId,
50
+ deletedAt: null,
51
+ })
52
+ const roleRecord = record ?? (organizationId
53
+ ? await tem.findOne(DashboardRoleWidgets, {
54
+ roleId: String(role.id),
55
+ tenantId: trimmedTenantId,
56
+ organizationId: null,
57
+ deletedAt: null,
58
+ })
59
+ : null)
60
+ if (!roleRecord) continue
61
+
62
+ const current = Array.isArray(roleRecord.widgetIdsJson) ? roleRecord.widgetIdsJson : []
63
+ const next = [...current]
64
+ const existing = new Set(current)
65
+ for (const widgetId of resolvedWidgetIds) {
66
+ if (existing.has(widgetId)) continue
67
+ existing.add(widgetId)
68
+ next.push(widgetId)
69
+ }
70
+
71
+ if (next.length === current.length) continue
72
+ roleRecord.widgetIdsJson = next
73
+ roleRecord.updatedAt = new Date()
74
+ tem.persist(roleRecord)
75
+ updated = true
76
+ }
77
+ })
78
+
79
+ return updated
80
+ }
@@ -1,6 +1,10 @@
1
1
  import type { EntityManager } from '@mikro-orm/postgresql'
2
2
  import type { CacheStrategy } from '@open-mercato/cache'
3
3
  import { createHash } from 'node:crypto'
4
+ import { decryptWithAesGcm } from '@open-mercato/shared/lib/encryption/aes'
5
+ import { resolveTenantEncryptionService } from '@open-mercato/shared/lib/encryption/customFieldValues'
6
+ import { resolveEntityIdFromMetadata } from '@open-mercato/shared/lib/encryption/entityIds'
7
+ import { findWithDecryption } from '@open-mercato/shared/lib/encryption/find'
4
8
  import {
5
9
  type DateRangePreset,
6
10
  resolveDateRange,
@@ -17,6 +21,8 @@ import {
17
21
  import type { AnalyticsRegistry } from './analyticsRegistry'
18
22
 
19
23
  const WIDGET_DATA_CACHE_TTL = 120_000
24
+ const WIDGET_DATA_SEGMENT_TTL = 86_400_000
25
+ const WIDGET_DATA_SEGMENT_KEY = 'widget-data:__segment__'
20
26
 
21
27
  const SAFE_IDENTIFIER_PATTERN = /^[a-zA-Z_][a-zA-Z0-9_]*$/
22
28
 
@@ -168,6 +174,11 @@ export class WidgetDataService {
168
174
  const tags = this.getCacheTags(request.entityType)
169
175
  try {
170
176
  await this.cache.set(cacheKey, response, { ttl: WIDGET_DATA_CACHE_TTL, tags })
177
+ await this.cache.set(
178
+ WIDGET_DATA_SEGMENT_KEY,
179
+ { updatedAt: response.metadata.fetchedAt },
180
+ { ttl: WIDGET_DATA_SEGMENT_TTL, tags: ['widget-data'] },
181
+ )
171
182
  } catch {
172
183
  }
173
184
  }
@@ -282,6 +293,75 @@ export class WidgetDataService {
282
293
  assertSafeIdentifier(config.idColumn, 'id column')
283
294
  assertSafeIdentifier(config.labelColumn, 'label column')
284
295
 
296
+ const meta = this.resolveEntityMetadata(config.table)
297
+ const idProp = meta ? this.resolveEntityPropertyName(meta, config.idColumn) : null
298
+ const labelProp = meta ? this.resolveEntityPropertyName(meta, config.labelColumn) : null
299
+ const tenantProp = meta
300
+ ? (this.resolveEntityPropertyName(meta, 'tenant_id') ?? this.resolveEntityPropertyName(meta, 'tenantId'))
301
+ : null
302
+ const organizationProp = meta
303
+ ? (this.resolveEntityPropertyName(meta, 'organization_id') ?? this.resolveEntityPropertyName(meta, 'organizationId'))
304
+ : null
305
+ const entityName = meta ? ((meta as any).class ?? meta.className ?? meta.name) : null
306
+
307
+ if (meta && idProp && labelProp && tenantProp && entityName) {
308
+ const where: Record<string, unknown> = {
309
+ [idProp]: { $in: uniqueIds },
310
+ [tenantProp]: this.scope.tenantId,
311
+ }
312
+ if (organizationProp && this.scope.organizationIds && this.scope.organizationIds.length > 0) {
313
+ where[organizationProp] = { $in: this.scope.organizationIds }
314
+ }
315
+
316
+ try {
317
+ const records = await findWithDecryption(
318
+ this.em,
319
+ entityName,
320
+ where,
321
+ { fields: [idProp, labelProp, tenantProp, organizationProp].filter(Boolean) },
322
+ { tenantId: this.scope.tenantId, organizationId: this.resolveOrganizationId() },
323
+ )
324
+
325
+ const encryptionService = resolveTenantEncryptionService(this.em as any)
326
+ const dek = encryptionService?.isEnabled() ? await encryptionService.getDek(this.scope.tenantId) : null
327
+ let hasEncryptedLabels = false
328
+ let hasDecryptedLabels = false
329
+
330
+ const labelMap = new Map<string, string>()
331
+ for (const record of records as Array<Record<string, unknown>>) {
332
+ const id = record[idProp]
333
+ let labelValue = record[labelProp]
334
+ if (typeof labelValue === 'string' && this.isEncryptedPayload(labelValue)) {
335
+ hasEncryptedLabels = true
336
+ if (dek?.key) {
337
+ const decrypted = this.decryptWithDek(labelValue, dek.key)
338
+ if (decrypted !== null) {
339
+ labelValue = decrypted
340
+ hasDecryptedLabels = true
341
+ }
342
+ }
343
+ } else if (labelValue != null && labelValue !== '') {
344
+ hasDecryptedLabels = true
345
+ }
346
+
347
+ if (typeof id === 'string' && labelValue != null && labelValue !== '') {
348
+ labelMap.set(id, String(labelValue))
349
+ }
350
+ }
351
+
352
+ if (labelMap.size > 0 && (!hasEncryptedLabels || hasDecryptedLabels)) {
353
+ return data.map((item) => ({
354
+ ...item,
355
+ groupLabel: typeof item.groupKey === 'string' && labelMap.has(item.groupKey)
356
+ ? labelMap.get(item.groupKey)!
357
+ : undefined,
358
+ }))
359
+ }
360
+ } catch {
361
+ // fall through to SQL resolution
362
+ }
363
+ }
364
+
285
365
  const clauses = [`"${config.idColumn}" = ANY(?::uuid[])`, 'tenant_id = ?']
286
366
  const params: unknown[] = [`{${uniqueIds.join(',')}}`, this.scope.tenantId]
287
367
 
@@ -290,17 +370,43 @@ export class WidgetDataService {
290
370
  params.push(`{${this.scope.organizationIds.join(',')}}`)
291
371
  }
292
372
 
293
- const sql = `SELECT "${config.idColumn}" as id, "${config.labelColumn}" as label FROM "${config.table}" WHERE ${clauses.join(
373
+ const sql = `SELECT "${config.idColumn}" as id, "${config.labelColumn}" as label, tenant_id, organization_id FROM "${config.table}" WHERE ${clauses.join(
294
374
  ' AND ',
295
375
  )}`
296
376
 
297
377
  try {
298
378
  const labelRows = await this.em.getConnection().execute(sql, params)
379
+ const entityId = this.resolveEntityId(meta)
380
+ const encryptionService = resolveTenantEncryptionService(this.em as any)
381
+ const organizationId = this.resolveOrganizationId()
382
+ const dek = encryptionService?.isEnabled() ? await encryptionService.getDek(this.scope.tenantId) : null
299
383
 
300
384
  const labelMap = new Map<string, string>()
301
- for (const row of labelRows as Array<{ id: string; label: string | null }>) {
302
- if (row.id && row.label != null && row.label !== '') {
303
- labelMap.set(row.id, row.label)
385
+ for (const row of labelRows as Array<{ id: string; label: string | null; tenant_id?: string | null; organization_id?: string | null }>) {
386
+ let labelValue = row.label
387
+ if (entityId && encryptionService?.isEnabled() && labelValue != null) {
388
+ const rowOrgId = row.organization_id ?? organizationId ?? null
389
+ const decrypted = await encryptionService.decryptEntityPayload(
390
+ entityId,
391
+ { [config.labelColumn]: labelValue },
392
+ this.scope.tenantId,
393
+ rowOrgId,
394
+ )
395
+ const resolved = decrypted[config.labelColumn]
396
+ if (typeof resolved === 'string' || typeof resolved === 'number') {
397
+ labelValue = String(resolved)
398
+ }
399
+ }
400
+
401
+ if (labelValue && dek?.key && this.isEncryptedPayload(labelValue)) {
402
+ const decrypted = this.decryptWithDek(labelValue, dek.key)
403
+ if (decrypted !== null) {
404
+ labelValue = decrypted
405
+ }
406
+ }
407
+
408
+ if (row.id && labelValue != null && labelValue !== '') {
409
+ labelMap.set(row.id, labelValue)
304
410
  }
305
411
  }
306
412
 
@@ -317,6 +423,60 @@ export class WidgetDataService {
317
423
  }))
318
424
  }
319
425
  }
426
+
427
+ private resolveOrganizationId(): string | null {
428
+ if (!this.scope.organizationIds || this.scope.organizationIds.length !== 1) return null
429
+ return this.scope.organizationIds[0] ?? null
430
+ }
431
+
432
+ private resolveEntityMetadata(tableName: string): Record<string, any> | null {
433
+ const registry = (this.em as any)?.getMetadata?.()
434
+ if (!registry) return null
435
+ const entries =
436
+ (typeof registry.getAll === 'function' && registry.getAll()) ||
437
+ (Array.isArray(registry.metadata) ? registry.metadata : Object.values(registry.metadata ?? {}))
438
+ const metas = Array.isArray(entries) ? entries : Object.values(entries ?? {})
439
+ const match = metas.find((meta: any) => {
440
+ const table = meta?.tableName ?? meta?.collection
441
+ if (typeof table !== 'string') return false
442
+ if (table === tableName) return true
443
+ return table.split('.').pop() === tableName
444
+ })
445
+ return match ?? null
446
+ }
447
+
448
+ private resolveEntityPropertyName(meta: Record<string, any>, columnName: string): string | null {
449
+ const properties = meta?.properties ? Object.values(meta.properties) : []
450
+ for (const prop of properties as Array<Record<string, any>>) {
451
+ const fieldName = prop?.fieldName
452
+ const fieldNames = prop?.fieldNames
453
+ if (typeof fieldName === 'string' && fieldName === columnName) return prop?.name ?? null
454
+ if (Array.isArray(fieldNames) && fieldNames.includes(columnName)) return prop?.name ?? null
455
+ if (prop?.name === columnName) return prop?.name ?? null
456
+ }
457
+ return null
458
+ }
459
+
460
+ private resolveEntityId(meta: Record<string, any> | null): string | null {
461
+ if (!meta) return null
462
+ try {
463
+ return resolveEntityIdFromMetadata(meta as any)
464
+ } catch {
465
+ return null
466
+ }
467
+ }
468
+
469
+ private isEncryptedPayload(value: string): boolean {
470
+ const parts = value.split(':')
471
+ return parts.length === 4 && parts[3] === 'v1'
472
+ }
473
+
474
+ private decryptWithDek(value: string, dek: string): string | null {
475
+ const first = decryptWithAesGcm(value, dek)
476
+ if (first === null) return null
477
+ if (!this.isEncryptedPayload(first)) return first
478
+ return decryptWithAesGcm(first, dek) ?? first
479
+ }
320
480
  }
321
481
 
322
482
  export function createWidgetDataService(
@@ -1,10 +1,26 @@
1
1
  import type { ModuleSetupConfig } from '@open-mercato/shared/modules/setup'
2
+ import { seedDashboardDefaultsForTenant } from '@open-mercato/core/modules/dashboards/cli'
3
+ import { appendWidgetsToRoles, resolveAnalyticsWidgetIds } from '@open-mercato/core/modules/dashboards/lib/role-widgets'
2
4
 
3
5
  export const setup: ModuleSetupConfig = {
4
6
  defaultRoleFeatures: {
5
7
  admin: ['dashboards.*', 'dashboards.admin.assign-widgets', 'analytics.view'],
6
8
  employee: ['dashboards.view', 'dashboards.configure', 'analytics.view'],
7
9
  },
10
+
11
+ async onTenantCreated({ em, tenantId, organizationId }) {
12
+ await seedDashboardDefaultsForTenant(em, { tenantId, organizationId, logger: () => {} })
13
+ },
14
+
15
+ async seedDefaults({ em, tenantId, organizationId }) {
16
+ const analyticsWidgetIds = await resolveAnalyticsWidgetIds()
17
+ await appendWidgetsToRoles(em, {
18
+ tenantId,
19
+ organizationId,
20
+ roleNames: ['admin', 'employee'],
21
+ widgetIds: analyticsWidgetIds,
22
+ })
23
+ },
8
24
  }
9
25
 
10
26
  export default setup
@@ -1,6 +1,6 @@
1
- import type { DashboardWidgetModule } from '@open-mercato/shared/modules/dashboard/widgets'
2
- import AovKpiWidget from './widget.client'
1
+ import { lazyDashboardWidget, type DashboardWidgetModule } from '@open-mercato/shared/modules/dashboard/widgets'
3
2
  import { DEFAULT_SETTINGS, hydrateSettings, type AovKpiSettings } from './config'
3
+ const AovKpiWidget = lazyDashboardWidget(() => import('./widget.client'))
4
4
 
5
5
  const widget: DashboardWidgetModule<AovKpiSettings> = {
6
6
  metadata: {