@open-mercato/core 0.4.2-canary-da2b080494 → 0.4.2-canary-19703ca707

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 (449) 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 +85 -1
  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 +42 -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 +18 -0
  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/dashboards/cli.js +44 -5
  108. package/dist/modules/dashboards/cli.js.map +2 -2
  109. package/dist/modules/dashboards/components/WidgetVisibilityEditor.js +16 -11
  110. package/dist/modules/dashboards/components/WidgetVisibilityEditor.js.map +3 -3
  111. package/dist/modules/dashboards/lib/role-widgets.js +58 -0
  112. package/dist/modules/dashboards/lib/role-widgets.js.map +7 -0
  113. package/dist/modules/dashboards/services/widgetDataService.js +139 -3
  114. package/dist/modules/dashboards/services/widgetDataService.js.map +2 -2
  115. package/dist/modules/dictionaries/components/DictionaryTable.js +2 -0
  116. package/dist/modules/dictionaries/components/DictionaryTable.js.map +2 -2
  117. package/dist/modules/directory/api/get/tenants/lookup.js +68 -0
  118. package/dist/modules/directory/api/get/tenants/lookup.js.map +7 -0
  119. package/dist/modules/directory/backend/directory/organizations/page.js +2 -2
  120. package/dist/modules/directory/backend/directory/organizations/page.js.map +2 -2
  121. package/dist/modules/directory/backend/directory/tenants/page.js +2 -2
  122. package/dist/modules/directory/backend/directory/tenants/page.js.map +2 -2
  123. package/dist/modules/entities/backend/entities/user/[entityId]/records/page.js +2 -2
  124. package/dist/modules/entities/backend/entities/user/[entityId]/records/page.js.map +2 -2
  125. package/dist/modules/entities/components/SystemEntitiesTable.js +1 -1
  126. package/dist/modules/entities/components/SystemEntitiesTable.js.map +2 -2
  127. package/dist/modules/entities/components/UserEntitiesTable.js +2 -2
  128. package/dist/modules/entities/components/UserEntitiesTable.js.map +2 -2
  129. package/dist/modules/feature_toggles/components/FeatureTogglesTable.js +3 -3
  130. package/dist/modules/feature_toggles/components/FeatureTogglesTable.js.map +2 -2
  131. package/dist/modules/feature_toggles/components/OverridesTable.js +1 -1
  132. package/dist/modules/feature_toggles/components/OverridesTable.js.map +2 -2
  133. package/dist/modules/notifications/acl.js +11 -0
  134. package/dist/modules/notifications/acl.js.map +7 -0
  135. package/dist/modules/notifications/api/[id]/action/route.js +74 -0
  136. package/dist/modules/notifications/api/[id]/action/route.js.map +7 -0
  137. package/dist/modules/notifications/api/[id]/dismiss/route.js +15 -0
  138. package/dist/modules/notifications/api/[id]/dismiss/route.js.map +7 -0
  139. package/dist/modules/notifications/api/[id]/read/route.js +15 -0
  140. package/dist/modules/notifications/api/[id]/read/route.js.map +7 -0
  141. package/dist/modules/notifications/api/[id]/restore/route.js +53 -0
  142. package/dist/modules/notifications/api/[id]/restore/route.js.map +7 -0
  143. package/dist/modules/notifications/api/batch/route.js +17 -0
  144. package/dist/modules/notifications/api/batch/route.js.map +7 -0
  145. package/dist/modules/notifications/api/feature/route.js +17 -0
  146. package/dist/modules/notifications/api/feature/route.js.map +7 -0
  147. package/dist/modules/notifications/api/mark-all-read/route.js +35 -0
  148. package/dist/modules/notifications/api/mark-all-read/route.js.map +7 -0
  149. package/dist/modules/notifications/api/openapi.js +76 -0
  150. package/dist/modules/notifications/api/openapi.js.map +7 -0
  151. package/dist/modules/notifications/api/role/route.js +17 -0
  152. package/dist/modules/notifications/api/role/route.js.map +7 -0
  153. package/dist/modules/notifications/api/route.js +85 -0
  154. package/dist/modules/notifications/api/route.js.map +7 -0
  155. package/dist/modules/notifications/api/settings/route.js +155 -0
  156. package/dist/modules/notifications/api/settings/route.js.map +7 -0
  157. package/dist/modules/notifications/api/unread-count/route.js +38 -0
  158. package/dist/modules/notifications/api/unread-count/route.js.map +7 -0
  159. package/dist/modules/notifications/backend/config/notifications/page.js +10 -0
  160. package/dist/modules/notifications/backend/config/notifications/page.js.map +7 -0
  161. package/dist/modules/notifications/backend/config/notifications/page.meta.js +24 -0
  162. package/dist/modules/notifications/backend/config/notifications/page.meta.js.map +7 -0
  163. package/dist/modules/notifications/cli.js +16 -0
  164. package/dist/modules/notifications/cli.js.map +7 -0
  165. package/dist/modules/notifications/data/entities.js +112 -0
  166. package/dist/modules/notifications/data/entities.js.map +7 -0
  167. package/dist/modules/notifications/data/validators.js +98 -0
  168. package/dist/modules/notifications/data/validators.js.map +7 -0
  169. package/dist/modules/notifications/di.js +13 -0
  170. package/dist/modules/notifications/di.js.map +7 -0
  171. package/dist/modules/notifications/emails/NotificationEmail.js +58 -0
  172. package/dist/modules/notifications/emails/NotificationEmail.js.map +7 -0
  173. package/dist/modules/notifications/frontend/NotificationInboxPageClient.js +44 -0
  174. package/dist/modules/notifications/frontend/NotificationInboxPageClient.js.map +7 -0
  175. package/dist/modules/notifications/frontend/NotificationSettingsPageClient.js +220 -0
  176. package/dist/modules/notifications/frontend/NotificationSettingsPageClient.js.map +7 -0
  177. package/dist/modules/notifications/index.js +14 -0
  178. package/dist/modules/notifications/index.js.map +7 -0
  179. package/dist/modules/notifications/lib/deliveryConfig.js +107 -0
  180. package/dist/modules/notifications/lib/deliveryConfig.js.map +7 -0
  181. package/dist/modules/notifications/lib/deliveryStrategies.js +14 -0
  182. package/dist/modules/notifications/lib/deliveryStrategies.js.map +7 -0
  183. package/dist/modules/notifications/lib/events.js +12 -0
  184. package/dist/modules/notifications/lib/events.js.map +7 -0
  185. package/dist/modules/notifications/lib/notificationBuilder.js +66 -0
  186. package/dist/modules/notifications/lib/notificationBuilder.js.map +7 -0
  187. package/dist/modules/notifications/lib/notificationFactory.js +54 -0
  188. package/dist/modules/notifications/lib/notificationFactory.js.map +7 -0
  189. package/dist/modules/notifications/lib/notificationMapper.js +34 -0
  190. package/dist/modules/notifications/lib/notificationMapper.js.map +7 -0
  191. package/dist/modules/notifications/lib/notificationRecipients.js +35 -0
  192. package/dist/modules/notifications/lib/notificationRecipients.js.map +7 -0
  193. package/dist/modules/notifications/lib/notificationService.js +279 -0
  194. package/dist/modules/notifications/lib/notificationService.js.map +7 -0
  195. package/dist/modules/notifications/lib/routeHelpers.js +101 -0
  196. package/dist/modules/notifications/lib/routeHelpers.js.map +7 -0
  197. package/dist/modules/notifications/lib/safeHref.js +24 -0
  198. package/dist/modules/notifications/lib/safeHref.js.map +7 -0
  199. package/dist/modules/notifications/migrations/Migration20260123000001.js +70 -0
  200. package/dist/modules/notifications/migrations/Migration20260123000001.js.map +7 -0
  201. package/dist/modules/notifications/migrations/Migration20260126150000.js +37 -0
  202. package/dist/modules/notifications/migrations/Migration20260126150000.js.map +7 -0
  203. package/dist/modules/notifications/subscribers/deliver-notification.js +165 -0
  204. package/dist/modules/notifications/subscribers/deliver-notification.js.map +7 -0
  205. package/dist/modules/notifications/workers/create-notification.worker.js +70 -0
  206. package/dist/modules/notifications/workers/create-notification.worker.js.map +7 -0
  207. package/dist/modules/planner/backend/planner/availability-rulesets/page.js +2 -2
  208. package/dist/modules/planner/backend/planner/availability-rulesets/page.js.map +2 -2
  209. package/dist/modules/query_index/components/QueryIndexesTable.js +7 -1
  210. package/dist/modules/query_index/components/QueryIndexesTable.js.map +2 -2
  211. package/dist/modules/resources/backend/resources/resource-types/page.js +2 -2
  212. package/dist/modules/resources/backend/resources/resource-types/page.js.map +2 -2
  213. package/dist/modules/resources/backend/resources/resources/page.js +2 -2
  214. package/dist/modules/resources/backend/resources/resources/page.js.map +2 -2
  215. package/dist/modules/sales/backend/sales/channels/offers/page.js +2 -0
  216. package/dist/modules/sales/backend/sales/channels/offers/page.js.map +2 -2
  217. package/dist/modules/sales/backend/sales/channels/page.js +2 -0
  218. package/dist/modules/sales/backend/sales/channels/page.js.map +2 -2
  219. package/dist/modules/sales/commands/documents.js +53 -0
  220. package/dist/modules/sales/commands/documents.js.map +2 -2
  221. package/dist/modules/sales/commands/payments.js +26 -0
  222. package/dist/modules/sales/commands/payments.js.map +2 -2
  223. package/dist/modules/sales/components/AdjustmentKindSettings.js +2 -2
  224. package/dist/modules/sales/components/AdjustmentKindSettings.js.map +2 -2
  225. package/dist/modules/sales/components/PaymentMethodsSettings.js +2 -2
  226. package/dist/modules/sales/components/PaymentMethodsSettings.js.map +2 -2
  227. package/dist/modules/sales/components/ShippingMethodsSettings.js +2 -2
  228. package/dist/modules/sales/components/ShippingMethodsSettings.js.map +2 -2
  229. package/dist/modules/sales/components/TaxRatesSettings.js +2 -2
  230. package/dist/modules/sales/components/TaxRatesSettings.js.map +2 -2
  231. package/dist/modules/sales/components/channels/SalesChannelOffersPanel.js +2 -0
  232. package/dist/modules/sales/components/channels/SalesChannelOffersPanel.js.map +2 -2
  233. package/dist/modules/sales/components/documents/AdjustmentsSection.js +2 -0
  234. package/dist/modules/sales/components/documents/AdjustmentsSection.js.map +2 -2
  235. package/dist/modules/sales/components/documents/PaymentsSection.js +2 -1
  236. package/dist/modules/sales/components/documents/PaymentsSection.js.map +2 -2
  237. package/dist/modules/sales/components/documents/SalesDocumentsTable.js +2 -0
  238. package/dist/modules/sales/components/documents/SalesDocumentsTable.js.map +2 -2
  239. package/dist/modules/sales/notifications.client.js +51 -0
  240. package/dist/modules/sales/notifications.client.js.map +7 -0
  241. package/dist/modules/sales/notifications.js +88 -0
  242. package/dist/modules/sales/notifications.js.map +7 -0
  243. package/dist/modules/sales/subscribers/quote-expiring-notification.js +38 -0
  244. package/dist/modules/sales/subscribers/quote-expiring-notification.js.map +7 -0
  245. package/dist/modules/sales/widgets/notifications/SalesOrderCreatedRenderer.js +137 -0
  246. package/dist/modules/sales/widgets/notifications/SalesOrderCreatedRenderer.js.map +7 -0
  247. package/dist/modules/sales/widgets/notifications/SalesQuoteCreatedRenderer.js +137 -0
  248. package/dist/modules/sales/widgets/notifications/SalesQuoteCreatedRenderer.js.map +7 -0
  249. package/dist/modules/sales/widgets/notifications/index.js +7 -0
  250. package/dist/modules/sales/widgets/notifications/index.js.map +7 -0
  251. package/dist/modules/sales/widgets/notifications/useSalesDocumentTotals.js +60 -0
  252. package/dist/modules/sales/widgets/notifications/useSalesDocumentTotals.js.map +7 -0
  253. package/dist/modules/staff/backend/staff/team-members/page.js +1 -1
  254. package/dist/modules/staff/backend/staff/team-members/page.js.map +2 -2
  255. package/dist/modules/staff/backend/staff/team-roles/page.js +2 -2
  256. package/dist/modules/staff/backend/staff/team-roles/page.js.map +2 -2
  257. package/dist/modules/staff/backend/staff/teams/[id]/edit/page.js +2 -2
  258. package/dist/modules/staff/backend/staff/teams/[id]/edit/page.js.map +2 -2
  259. package/dist/modules/staff/backend/staff/teams/page.js +2 -2
  260. package/dist/modules/staff/backend/staff/teams/page.js.map +2 -2
  261. package/dist/modules/staff/commands/leave-requests.js +79 -0
  262. package/dist/modules/staff/commands/leave-requests.js.map +2 -2
  263. package/dist/modules/staff/notifications.js +75 -0
  264. package/dist/modules/staff/notifications.js.map +7 -0
  265. package/dist/modules/workflows/backend/definitions/page.js +5 -0
  266. package/dist/modules/workflows/backend/definitions/page.js.map +2 -2
  267. package/dist/modules/workflows/backend/instances/page.js +3 -0
  268. package/dist/modules/workflows/backend/instances/page.js.map +2 -2
  269. package/dist/modules/workflows/backend/tasks/page.js +3 -0
  270. package/dist/modules/workflows/backend/tasks/page.js.map +2 -2
  271. package/dist/modules/workflows/cli.js +12 -12
  272. package/dist/modules/workflows/cli.js.map +2 -2
  273. package/dist/modules/workflows/lib/transition-handler.js +14 -6
  274. package/dist/modules/workflows/lib/transition-handler.js.map +2 -2
  275. package/dist/modules/workflows/notifications.js +28 -0
  276. package/dist/modules/workflows/notifications.js.map +7 -0
  277. package/dist/modules/workflows/subscribers/task-assigned-notification.js +38 -0
  278. package/dist/modules/workflows/subscribers/task-assigned-notification.js.map +7 -0
  279. package/generated/entities/notification/index.ts +27 -0
  280. package/generated/entities.ids.generated.ts +5 -1
  281. package/generated/entity-fields-registry.ts +2 -0
  282. package/package.json +2 -2
  283. package/src/modules/api_docs/frontend/docs/api/page.tsx +3 -2
  284. package/src/modules/api_keys/backend/api-keys/page.tsx +1 -1
  285. package/src/modules/attachments/components/AttachmentLibrary.tsx +4 -0
  286. package/src/modules/attachments/components/AttachmentPartitionSettings.tsx +2 -0
  287. package/src/modules/auth/README.md +1 -1
  288. package/src/modules/auth/__tests__/cli-setup-acl.test.ts +1 -1
  289. package/src/modules/auth/api/__tests__/login.test.ts +2 -0
  290. package/src/modules/auth/api/admin/nav.ts +10 -6
  291. package/src/modules/auth/api/login.ts +26 -7
  292. package/src/modules/auth/api/profile/route.ts +163 -0
  293. package/src/modules/auth/api/reset/confirm.ts +25 -2
  294. package/src/modules/auth/api/reset.ts +23 -0
  295. package/src/modules/auth/api/sidebar/preferences/route.ts +21 -12
  296. package/src/modules/auth/api/users/route.ts +5 -2
  297. package/src/modules/auth/backend/auth/profile/page.meta.ts +9 -0
  298. package/src/modules/auth/backend/auth/profile/page.tsx +174 -0
  299. package/src/modules/auth/backend/roles/[id]/edit/page.tsx +4 -1
  300. package/src/modules/auth/backend/roles/page.tsx +3 -3
  301. package/src/modules/auth/backend/users/[id]/edit/page.tsx +22 -3
  302. package/src/modules/auth/backend/users/create/page.tsx +19 -2
  303. package/src/modules/auth/backend/users/page.tsx +3 -3
  304. package/src/modules/auth/cli.ts +38 -11
  305. package/src/modules/auth/commands/users.ts +73 -2
  306. package/src/modules/auth/data/validators.ts +6 -2
  307. package/src/modules/auth/frontend/login.tsx +106 -2
  308. package/src/modules/auth/frontend/reset/[token]/page.tsx +24 -11
  309. package/src/modules/auth/i18n/de.json +48 -1
  310. package/src/modules/auth/i18n/en.json +48 -1
  311. package/src/modules/auth/i18n/es.json +48 -1
  312. package/src/modules/auth/i18n/pl.json +48 -1
  313. package/src/modules/auth/lib/setup-app.ts +58 -9
  314. package/src/modules/auth/notifications.ts +109 -0
  315. package/src/modules/auth/services/authService.ts +27 -4
  316. package/src/modules/business_rules/api/execute/route.ts +8 -1
  317. package/src/modules/business_rules/backend/rules/page.tsx +4 -0
  318. package/src/modules/business_rules/backend/sets/page.tsx +3 -0
  319. package/src/modules/business_rules/cli.ts +2 -1
  320. package/src/modules/business_rules/i18n/en.json +3 -1
  321. package/src/modules/business_rules/lib/__tests__/rule-engine.test.ts +51 -0
  322. package/src/modules/business_rules/lib/rule-engine.ts +57 -3
  323. package/src/modules/business_rules/notifications.ts +25 -0
  324. package/src/modules/business_rules/subscribers/rule-execution-failed-notification.ts +50 -0
  325. package/src/modules/catalog/components/PriceKindSettings.tsx +2 -0
  326. package/src/modules/catalog/components/categories/CategoriesDataTable.tsx +2 -2
  327. package/src/modules/catalog/components/products/ProductsDataTable.tsx +2 -0
  328. package/src/modules/catalog/i18n/en.json +3 -1
  329. package/src/modules/catalog/notifications.ts +25 -0
  330. package/src/modules/catalog/subscribers/low-stock-notification.ts +52 -0
  331. package/src/modules/configs/cli.ts +6 -0
  332. package/src/modules/configs/components/CachePanel.tsx +4 -4
  333. package/src/modules/configs/i18n/en.json +12 -2
  334. package/src/modules/configs/i18n/pl.json +12 -2
  335. package/src/modules/configs/lib/system-status.ts +48 -1
  336. package/src/modules/configs/lib/system-status.types.ts +1 -0
  337. package/src/modules/configs/lib/upgrade-actions.ts +18 -0
  338. package/src/modules/currencies/backend/currencies/page.tsx +3 -0
  339. package/src/modules/currencies/backend/exchange-rates/page.tsx +2 -0
  340. package/src/modules/customers/backend/customers/companies/page.tsx +3 -0
  341. package/src/modules/customers/backend/customers/deals/page.tsx +3 -0
  342. package/src/modules/customers/backend/customers/people/page.tsx +3 -0
  343. package/src/modules/customers/commands/deals.ts +39 -0
  344. package/src/modules/customers/components/CustomerTodosTable.tsx +1 -0
  345. package/src/modules/customers/i18n/en.json +5 -1
  346. package/src/modules/customers/notifications.ts +44 -0
  347. package/src/modules/dashboards/cli.ts +55 -5
  348. package/src/modules/dashboards/components/WidgetVisibilityEditor.tsx +22 -11
  349. package/src/modules/dashboards/lib/role-widgets.ts +80 -0
  350. package/src/modules/dashboards/services/widgetDataService.ts +164 -4
  351. package/src/modules/dictionaries/components/DictionaryTable.tsx +2 -0
  352. package/src/modules/directory/api/get/tenants/lookup.ts +73 -0
  353. package/src/modules/directory/backend/directory/organizations/page.tsx +2 -2
  354. package/src/modules/directory/backend/directory/tenants/page.tsx +2 -2
  355. package/src/modules/entities/backend/entities/user/[entityId]/records/page.tsx +2 -2
  356. package/src/modules/entities/components/SystemEntitiesTable.tsx +1 -1
  357. package/src/modules/entities/components/UserEntitiesTable.tsx +2 -2
  358. package/src/modules/feature_toggles/components/FeatureTogglesTable.tsx +3 -4
  359. package/src/modules/feature_toggles/components/OverridesTable.tsx +1 -1
  360. package/src/modules/notifications/__tests__/deliver-notification.test.ts +195 -0
  361. package/src/modules/notifications/__tests__/deliveryStrategies.test.ts +19 -0
  362. package/src/modules/notifications/__tests__/notificationService.test.ts +208 -0
  363. package/src/modules/notifications/acl.ts +7 -0
  364. package/src/modules/notifications/api/[id]/action/route.ts +75 -0
  365. package/src/modules/notifications/api/[id]/dismiss/route.ts +12 -0
  366. package/src/modules/notifications/api/[id]/read/route.ts +12 -0
  367. package/src/modules/notifications/api/[id]/restore/route.ts +53 -0
  368. package/src/modules/notifications/api/batch/route.ts +14 -0
  369. package/src/modules/notifications/api/feature/route.ts +14 -0
  370. package/src/modules/notifications/api/mark-all-read/route.ts +34 -0
  371. package/src/modules/notifications/api/openapi.ts +76 -0
  372. package/src/modules/notifications/api/role/route.ts +14 -0
  373. package/src/modules/notifications/api/route.ts +92 -0
  374. package/src/modules/notifications/api/settings/route.ts +157 -0
  375. package/src/modules/notifications/api/unread-count/route.ts +38 -0
  376. package/src/modules/notifications/backend/config/notifications/page.meta.ts +22 -0
  377. package/src/modules/notifications/backend/config/notifications/page.tsx +12 -0
  378. package/src/modules/notifications/cli.ts +18 -0
  379. package/src/modules/notifications/data/entities.ts +99 -0
  380. package/src/modules/notifications/data/validators.ts +115 -0
  381. package/src/modules/notifications/di.ts +11 -0
  382. package/src/modules/notifications/emails/NotificationEmail.tsx +98 -0
  383. package/src/modules/notifications/frontend/NotificationInboxPageClient.tsx +42 -0
  384. package/src/modules/notifications/frontend/NotificationSettingsPageClient.tsx +233 -0
  385. package/src/modules/notifications/i18n/de.json +50 -0
  386. package/src/modules/notifications/i18n/en.json +50 -0
  387. package/src/modules/notifications/i18n/es.json +50 -0
  388. package/src/modules/notifications/i18n/pl.json +50 -0
  389. package/src/modules/notifications/index.ts +12 -0
  390. package/src/modules/notifications/lib/deliveryConfig.ts +153 -0
  391. package/src/modules/notifications/lib/deliveryStrategies.ts +50 -0
  392. package/src/modules/notifications/lib/events.ts +48 -0
  393. package/src/modules/notifications/lib/notificationBuilder.ts +121 -0
  394. package/src/modules/notifications/lib/notificationFactory.ts +76 -0
  395. package/src/modules/notifications/lib/notificationMapper.ts +33 -0
  396. package/src/modules/notifications/lib/notificationRecipients.ts +83 -0
  397. package/src/modules/notifications/lib/notificationService.ts +414 -0
  398. package/src/modules/notifications/lib/routeHelpers.ts +151 -0
  399. package/src/modules/notifications/lib/safeHref.ts +29 -0
  400. package/src/modules/notifications/migrations/.snapshot-open-mercato.json +300 -0
  401. package/src/modules/notifications/migrations/Migration20260123000001.ts +73 -0
  402. package/src/modules/notifications/migrations/Migration20260126150000.ts +39 -0
  403. package/src/modules/notifications/subscribers/deliver-notification.ts +204 -0
  404. package/src/modules/notifications/workers/create-notification.worker.ts +122 -0
  405. package/src/modules/planner/backend/planner/availability-rulesets/page.tsx +2 -2
  406. package/src/modules/query_index/components/QueryIndexesTable.tsx +8 -2
  407. package/src/modules/resources/backend/resources/resource-types/page.tsx +2 -2
  408. package/src/modules/resources/backend/resources/resources/page.tsx +2 -2
  409. package/src/modules/sales/backend/sales/channels/offers/page.tsx +2 -0
  410. package/src/modules/sales/backend/sales/channels/page.tsx +2 -0
  411. package/src/modules/sales/commands/documents.ts +65 -0
  412. package/src/modules/sales/commands/payments.ts +33 -0
  413. package/src/modules/sales/components/AdjustmentKindSettings.tsx +2 -2
  414. package/src/modules/sales/components/PaymentMethodsSettings.tsx +2 -2
  415. package/src/modules/sales/components/ShippingMethodsSettings.tsx +2 -2
  416. package/src/modules/sales/components/TaxRatesSettings.tsx +2 -2
  417. package/src/modules/sales/components/channels/SalesChannelOffersPanel.tsx +2 -0
  418. package/src/modules/sales/components/documents/AdjustmentsSection.tsx +2 -0
  419. package/src/modules/sales/components/documents/PaymentsSection.tsx +2 -1
  420. package/src/modules/sales/components/documents/SalesDocumentsTable.tsx +2 -0
  421. package/src/modules/sales/i18n/de.json +20 -0
  422. package/src/modules/sales/i18n/en.json +25 -1
  423. package/src/modules/sales/i18n/es.json +20 -0
  424. package/src/modules/sales/i18n/pl.json +20 -0
  425. package/src/modules/sales/notifications.client.ts +65 -0
  426. package/src/modules/sales/notifications.ts +82 -0
  427. package/src/modules/sales/subscribers/quote-expiring-notification.ts +53 -0
  428. package/src/modules/sales/widgets/notifications/SalesOrderCreatedRenderer.tsx +156 -0
  429. package/src/modules/sales/widgets/notifications/SalesQuoteCreatedRenderer.tsx +156 -0
  430. package/src/modules/sales/widgets/notifications/index.ts +2 -0
  431. package/src/modules/sales/widgets/notifications/useSalesDocumentTotals.ts +81 -0
  432. package/src/modules/staff/backend/staff/team-members/page.tsx +1 -1
  433. package/src/modules/staff/backend/staff/team-roles/page.tsx +2 -2
  434. package/src/modules/staff/backend/staff/teams/[id]/edit/page.tsx +2 -2
  435. package/src/modules/staff/backend/staff/teams/page.tsx +2 -2
  436. package/src/modules/staff/commands/leave-requests.ts +94 -0
  437. package/src/modules/staff/i18n/de.json +4 -0
  438. package/src/modules/staff/i18n/en.json +9 -1
  439. package/src/modules/staff/i18n/es.json +4 -0
  440. package/src/modules/staff/i18n/pl.json +4 -0
  441. package/src/modules/staff/notifications.ts +71 -0
  442. package/src/modules/workflows/backend/definitions/page.tsx +5 -0
  443. package/src/modules/workflows/backend/instances/page.tsx +4 -1
  444. package/src/modules/workflows/backend/tasks/page.tsx +4 -1
  445. package/src/modules/workflows/cli.ts +12 -12
  446. package/src/modules/workflows/i18n/en.json +3 -1
  447. package/src/modules/workflows/lib/transition-handler.ts +18 -6
  448. package/src/modules/workflows/notifications.ts +25 -0
  449. package/src/modules/workflows/subscribers/task-assigned-notification.ts +53 -0
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/modules/dashboards/components/WidgetVisibilityEditor.tsx"],
4
- "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { Spinner } from '@open-mercato/ui/primitives/spinner'\nimport { apiCallOrThrow, readApiResultOrThrow } from '@open-mercato/ui/backend/utils/apiCall'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\n\ntype WidgetCatalogItem = {\n id: string\n title: string\n description: string | null\n}\n\ntype RoleResponse = {\n widgetIds: string[]\n hasCustom: boolean\n scope: { tenantId: string | null; organizationId: string | null }\n}\n\ntype UserResponse = {\n mode: 'inherit' | 'override'\n widgetIds: string[]\n hasCustom: boolean\n effectiveWidgetIds: string[]\n scope: { tenantId: string | null; organizationId: string | null }\n}\n\ntype BaseProps = {\n tenantId?: string | null\n organizationId?: string | null\n}\n\ntype RoleProps = BaseProps & {\n kind: 'role'\n targetId: string\n}\n\ntype UserProps = BaseProps & {\n kind: 'user'\n targetId: string\n}\n\ntype WidgetVisibilityEditorProps = RoleProps | UserProps\n\nconst EMPTY: string[] = []\n\nexport function WidgetVisibilityEditor(props: WidgetVisibilityEditorProps) {\n const t = useT()\n const { kind, targetId, tenantId, organizationId } = props\n const [catalog, setCatalog] = React.useState<WidgetCatalogItem[]>([])\n const [loading, setLoading] = React.useState(true)\n const [saving, setSaving] = React.useState(false)\n const [error, setError] = React.useState<string | null>(null)\n\n const [selected, setSelected] = React.useState<string[]>(EMPTY)\n const [original, setOriginal] = React.useState<string[]>(EMPTY)\n const [mode, setMode] = React.useState<'inherit' | 'override'>('inherit')\n const [originalMode, setOriginalMode] = React.useState<'inherit' | 'override'>('inherit')\n const [effective, setEffective] = React.useState<string[]>(EMPTY)\n\n const loadCatalog = React.useCallback(async () => {\n const data = await readApiResultOrThrow<{ items?: unknown[] }>(\n '/api/dashboards/widgets/catalog',\n undefined,\n { errorMessage: t('dashboards.widgets.error.load', 'Unable to load widget configuration.') },\n )\n const items = Array.isArray(data?.items) ? data.items : []\n const mapped = items\n .map((item: unknown): WidgetCatalogItem | null => {\n if (!item || typeof item !== 'object') return null\n const entry = item as Record<string, unknown>\n const id = typeof entry.id === 'string' ? entry.id : null\n if (!id || !id.length) return null\n const title =\n typeof entry.title === 'string' && entry.title.length ? entry.title : id\n const description =\n typeof entry.description === 'string' && entry.description.length ? entry.description : null\n return { id, title, description }\n })\n .filter((item: WidgetCatalogItem | null): item is WidgetCatalogItem => item !== null)\n setCatalog(mapped)\n }, [t])\n\n const loadRoleData = React.useCallback(async () => {\n const params = new URLSearchParams({ roleId: targetId })\n if (tenantId) params.set('tenantId', tenantId)\n if (organizationId) params.set('organizationId', organizationId)\n const data = await readApiResultOrThrow<RoleResponse>(\n `/api/dashboards/roles/widgets?${params.toString()}`,\n undefined,\n { errorMessage: t('dashboards.widgets.error.load', 'Unable to load widget configuration.') },\n )\n const ids = Array.isArray(data.widgetIds) ? data.widgetIds : []\n setSelected(ids)\n setOriginal(ids)\n setMode('override')\n setOriginalMode('override')\n setEffective(ids)\n }, [organizationId, targetId, tenantId, t])\n\n const loadUserData = React.useCallback(async () => {\n const params = new URLSearchParams({ userId: targetId })\n if (tenantId) params.set('tenantId', tenantId)\n if (organizationId) params.set('organizationId', organizationId)\n const data = await readApiResultOrThrow<UserResponse>(\n `/api/dashboards/users/widgets?${params.toString()}`,\n undefined,\n { errorMessage: t('dashboards.widgets.error.load', 'Unable to load widget configuration.') },\n )\n const ids = Array.isArray(data.widgetIds) ? data.widgetIds : []\n setSelected(ids)\n setOriginal(ids)\n setMode(data.mode || 'inherit')\n setOriginalMode(data.mode || 'inherit')\n setEffective(Array.isArray(data.effectiveWidgetIds) ? data.effectiveWidgetIds : [])\n }, [organizationId, targetId, tenantId, t])\n\n React.useEffect(() => {\n let cancelled = false\n async function load() {\n setLoading(true)\n setError(null)\n try {\n await loadCatalog()\n if (kind === 'role') await loadRoleData()\n else await loadUserData()\n } catch (err) {\n console.error('Failed to load widget visibility data', err)\n if (!cancelled) {\n setError(t('dashboards.widgets.error.load', 'Unable to load widget configuration.'))\n }\n } finally {\n if (!cancelled) setLoading(false)\n }\n }\n load()\n return () => { cancelled = true }\n }, [kind, loadCatalog, loadRoleData, loadUserData, t])\n\n const toggle = React.useCallback((id: string) => {\n setSelected((prev) => (prev.includes(id) ? prev.filter((value) => value !== id) : [...prev, id]))\n }, [])\n\n const resetSelections = React.useCallback(() => {\n setSelected(original)\n setMode(originalMode)\n }, [original, originalMode])\n\n const save = React.useCallback(async () => {\n setSaving(true)\n setError(null)\n try {\n const saveError = t('dashboards.widgets.error.save', 'Unable to save dashboard widget preferences.')\n if (kind === 'role') {\n const payload = {\n roleId: targetId,\n tenantId: tenantId ?? null,\n organizationId: organizationId ?? null,\n widgetIds: selected,\n }\n await apiCallOrThrow('/api/dashboards/roles/widgets', {\n method: 'PUT',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify(payload),\n }, { errorMessage: saveError })\n setOriginal(selected)\n setOriginalMode('override')\n setEffective(selected)\n } else {\n const payload = {\n userId: targetId,\n tenantId: tenantId ?? null,\n organizationId: organizationId ?? null,\n mode,\n widgetIds: selected,\n }\n await apiCallOrThrow('/api/dashboards/users/widgets', {\n method: 'PUT',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify(payload),\n }, { errorMessage: saveError })\n setOriginal(selected)\n if (mode === 'inherit') {\n const refreshed = await readApiResultOrThrow<UserResponse>(\n `/api/dashboards/users/widgets?userId=${encodeURIComponent(targetId)}`,\n undefined,\n { errorMessage: saveError },\n )\n setEffective(Array.isArray(refreshed.effectiveWidgetIds) ? refreshed.effectiveWidgetIds : [])\n } else {\n setEffective(selected)\n }\n setOriginal(selected)\n setOriginalMode(mode)\n }\n try { flash(t('dashboards.widgets.flash.saved', 'Dashboard widgets updated'), 'success') } catch {}\n } catch (err) {\n console.error('Failed to save widget visibility', err)\n setError(t('dashboards.widgets.error.save', 'Unable to save dashboard widget preferences.'))\n } finally {\n setSaving(false)\n }\n }, [kind, mode, organizationId, selected, t, targetId, tenantId])\n\n const dirty = React.useMemo(() => {\n if (kind === 'user') {\n if (mode !== originalMode) return true\n if (mode === 'override') return selected.join('|') !== original.join('|')\n return false\n }\n return selected.join('|') !== original.join('|')\n }, [kind, mode, original, originalMode, selected])\n\n if (loading) {\n return (\n <div className=\"flex items-center gap-2 text-sm text-muted-foreground\">\n <Spinner size=\"sm\" /> {t('dashboards.widgets.loading', 'Loading widget options\u2026')}\n </div>\n )\n }\n\n if (error && catalog.length === 0) {\n return <div className=\"rounded-md border border-destructive/40 bg-destructive/10 p-3 text-sm text-destructive\">{error}</div>\n }\n\n return (\n <div className=\"space-y-4\">\n {error && (\n <div className=\"rounded-md border border-destructive/40 bg-destructive/10 p-3 text-sm text-destructive\">{error}</div>\n )}\n\n {kind === 'user' && (\n <div className=\"flex items-center gap-3 rounded-md border bg-muted/30 px-3 py-2\">\n <label className=\"flex items-center gap-2 text-sm\">\n <input\n type=\"radio\"\n name=\"widgetOverride\"\n value=\"inherit\"\n checked={mode === 'inherit'}\n onChange={() => setMode('inherit')}\n />\n {t('dashboards.widgets.mode.inherit', 'Inherit from roles')}\n </label>\n <label className=\"flex items-center gap-2 text-sm\">\n <input\n type=\"radio\"\n name=\"widgetOverride\"\n value=\"override\"\n checked={mode === 'override'}\n onChange={() => setMode('override')}\n />\n {t('dashboards.widgets.mode.override', 'Override for this user')}\n </label>\n </div>\n )}\n\n {kind === 'user' && mode === 'inherit' && (\n <div className=\"rounded-md border border-muted bg-muted/20 px-3 py-2 text-xs text-muted-foreground\">\n {t('dashboards.widgets.mode.hint', 'This user currently inherits widgets from their assigned roles. Switch to override to customize.')}\n </div>\n )}\n\n {(kind === 'role' || mode === 'override') && (\n <div className=\"space-y-3\">\n {catalog.map((widget) => (\n <label key={widget.id} className=\"flex items-start gap-3 rounded-md border px-3 py-2 hover:border-primary/40\">\n <input\n type=\"checkbox\"\n className=\"mt-1 size-4\"\n checked={selected.includes(widget.id)}\n onChange={() => toggle(widget.id)}\n />\n <div>\n <div className=\"text-sm font-medium leading-none\">{widget.title}</div>\n {widget.description ? <div className=\"mt-1 text-xs text-muted-foreground\">{widget.description}</div> : null}\n </div>\n </label>\n ))}\n </div>\n )}\n\n {kind === 'user' && effective.length > 0 && (\n <div className=\"rounded-md border bg-muted/30 px-3 py-2 text-xs text-muted-foreground\">\n Effective widgets: {effective.map((id) => catalog.find((meta) => meta.id === id)?.title || id).join(', ')}\n </div>\n )}\n\n <div className=\"flex items-center gap-2\">\n <Button type=\"button\" onClick={save} disabled={saving || !dirty}>\n {saving ? 'Saving\u2026' : 'Save widgets'}\n </Button>\n <Button type=\"button\" variant=\"ghost\" onClick={resetSelections} disabled={!dirty}>\n Reset\n </Button>\n </div>\n </div>\n )\n}\n"],
5
- "mappings": "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",
6
- "names": []
4
+ "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { Spinner } from '@open-mercato/ui/primitives/spinner'\nimport { apiCallOrThrow, readApiResultOrThrow } from '@open-mercato/ui/backend/utils/apiCall'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\n\ntype WidgetCatalogItem = {\n id: string\n title: string\n description: string | null\n}\n\ntype RoleResponse = {\n widgetIds: string[]\n hasCustom: boolean\n scope: { tenantId: string | null; organizationId: string | null }\n}\n\ntype UserResponse = {\n mode: 'inherit' | 'override'\n widgetIds: string[]\n hasCustom: boolean\n effectiveWidgetIds: string[]\n scope: { tenantId: string | null; organizationId: string | null }\n}\n\ntype BaseProps = {\n tenantId?: string | null\n organizationId?: string | null\n}\n\ntype RoleProps = BaseProps & {\n kind: 'role'\n targetId: string\n}\n\ntype UserProps = BaseProps & {\n kind: 'user'\n targetId: string\n}\n\ntype WidgetVisibilityEditorProps = RoleProps | UserProps\n\nexport type WidgetVisibilityEditorHandle = {\n save: () => Promise<void>\n}\n\nconst EMPTY: string[] = []\n\nexport const WidgetVisibilityEditor = React.forwardRef<WidgetVisibilityEditorHandle, WidgetVisibilityEditorProps>(function WidgetVisibilityEditor(props, ref) {\n const t = useT()\n const { kind, targetId, tenantId, organizationId } = props\n const [catalog, setCatalog] = React.useState<WidgetCatalogItem[]>([])\n const [loading, setLoading] = React.useState(true)\n const [saving, setSaving] = React.useState(false)\n const [error, setError] = React.useState<string | null>(null)\n\n const [selected, setSelected] = React.useState<string[]>(EMPTY)\n const [original, setOriginal] = React.useState<string[]>(EMPTY)\n const [mode, setMode] = React.useState<'inherit' | 'override'>('inherit')\n const [originalMode, setOriginalMode] = React.useState<'inherit' | 'override'>('inherit')\n const [effective, setEffective] = React.useState<string[]>(EMPTY)\n\n const dirty = React.useMemo(() => {\n if (kind === 'user') {\n if (mode !== originalMode) return true\n if (mode === 'override') return selected.join('|') !== original.join('|')\n return false\n }\n return selected.join('|') !== original.join('|')\n }, [kind, mode, original, originalMode, selected])\n\n const loadCatalog = React.useCallback(async () => {\n const data = await readApiResultOrThrow<{ items?: unknown[] }>(\n '/api/dashboards/widgets/catalog',\n undefined,\n { errorMessage: t('dashboards.widgets.error.load', 'Unable to load widget configuration.') },\n )\n const items = Array.isArray(data?.items) ? data.items : []\n const mapped = items\n .map((item: unknown): WidgetCatalogItem | null => {\n if (!item || typeof item !== 'object') return null\n const entry = item as Record<string, unknown>\n const id = typeof entry.id === 'string' ? entry.id : null\n if (!id || !id.length) return null\n const title =\n typeof entry.title === 'string' && entry.title.length ? entry.title : id\n const description =\n typeof entry.description === 'string' && entry.description.length ? entry.description : null\n return { id, title, description }\n })\n .filter((item: WidgetCatalogItem | null): item is WidgetCatalogItem => item !== null)\n setCatalog(mapped)\n }, [t])\n\n const loadRoleData = React.useCallback(async () => {\n const params = new URLSearchParams({ roleId: targetId })\n if (tenantId) params.set('tenantId', tenantId)\n if (organizationId) params.set('organizationId', organizationId)\n const data = await readApiResultOrThrow<RoleResponse>(\n `/api/dashboards/roles/widgets?${params.toString()}`,\n undefined,\n { errorMessage: t('dashboards.widgets.error.load', 'Unable to load widget configuration.') },\n )\n const ids = Array.isArray(data.widgetIds) ? data.widgetIds : []\n setSelected(ids)\n setOriginal(ids)\n setMode('override')\n setOriginalMode('override')\n setEffective(ids)\n }, [organizationId, targetId, tenantId, t])\n\n const loadUserData = React.useCallback(async () => {\n const params = new URLSearchParams({ userId: targetId })\n if (tenantId) params.set('tenantId', tenantId)\n if (organizationId) params.set('organizationId', organizationId)\n const data = await readApiResultOrThrow<UserResponse>(\n `/api/dashboards/users/widgets?${params.toString()}`,\n undefined,\n { errorMessage: t('dashboards.widgets.error.load', 'Unable to load widget configuration.') },\n )\n const ids = Array.isArray(data.widgetIds) ? data.widgetIds : []\n setSelected(ids)\n setOriginal(ids)\n setMode(data.mode || 'inherit')\n setOriginalMode(data.mode || 'inherit')\n setEffective(Array.isArray(data.effectiveWidgetIds) ? data.effectiveWidgetIds : [])\n }, [organizationId, targetId, tenantId, t])\n\n React.useEffect(() => {\n let cancelled = false\n async function load() {\n setLoading(true)\n setError(null)\n try {\n await loadCatalog()\n if (kind === 'role') await loadRoleData()\n else await loadUserData()\n } catch (err) {\n console.error('Failed to load widget visibility data', err)\n if (!cancelled) {\n setError(t('dashboards.widgets.error.load', 'Unable to load widget configuration.'))\n }\n } finally {\n if (!cancelled) setLoading(false)\n }\n }\n load()\n return () => { cancelled = true }\n }, [kind, loadCatalog, loadRoleData, loadUserData, t])\n\n const toggle = React.useCallback((id: string) => {\n setSelected((prev) => (prev.includes(id) ? prev.filter((value) => value !== id) : [...prev, id]))\n }, [])\n\n const resetSelections = React.useCallback(() => {\n setSelected(original)\n setMode(originalMode)\n }, [original, originalMode])\n\n const save = React.useCallback(async () => {\n if (loading) return\n if (error && catalog.length === 0) return\n if (!dirty) return\n setSaving(true)\n setError(null)\n try {\n const saveError = t('dashboards.widgets.error.save', 'Unable to save dashboard widget preferences.')\n if (kind === 'role') {\n const payload = {\n roleId: targetId,\n tenantId: tenantId ?? null,\n organizationId: organizationId ?? null,\n widgetIds: selected,\n }\n await apiCallOrThrow('/api/dashboards/roles/widgets', {\n method: 'PUT',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify(payload),\n }, { errorMessage: saveError })\n setOriginal(selected)\n setOriginalMode('override')\n setEffective(selected)\n } else {\n const payload = {\n userId: targetId,\n tenantId: tenantId ?? null,\n organizationId: organizationId ?? null,\n mode,\n widgetIds: selected,\n }\n await apiCallOrThrow('/api/dashboards/users/widgets', {\n method: 'PUT',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify(payload),\n }, { errorMessage: saveError })\n setOriginal(selected)\n if (mode === 'inherit') {\n const refreshed = await readApiResultOrThrow<UserResponse>(\n `/api/dashboards/users/widgets?userId=${encodeURIComponent(targetId)}`,\n undefined,\n { errorMessage: saveError },\n )\n setEffective(Array.isArray(refreshed.effectiveWidgetIds) ? refreshed.effectiveWidgetIds : [])\n } else {\n setEffective(selected)\n }\n setOriginal(selected)\n setOriginalMode(mode)\n }\n try { flash(t('dashboards.widgets.flash.saved', 'Dashboard widgets updated'), 'success') } catch {}\n } catch (err) {\n console.error('Failed to save widget visibility', err)\n setError(t('dashboards.widgets.error.save', 'Unable to save dashboard widget preferences.'))\n } finally {\n setSaving(false)\n }\n }, [catalog.length, dirty, error, kind, loading, mode, organizationId, selected, t, targetId, tenantId])\n\n React.useImperativeHandle(ref, () => ({ save }), [save])\n\n if (loading) {\n return (\n <div className=\"flex items-center gap-2 text-sm text-muted-foreground\">\n <Spinner size=\"sm\" /> {t('dashboards.widgets.loading', 'Loading widget options\u2026')}\n </div>\n )\n }\n\n if (error && catalog.length === 0) {\n return <div className=\"rounded-md border border-destructive/40 bg-destructive/10 p-3 text-sm text-destructive\">{error}</div>\n }\n\n return (\n <div className=\"space-y-4\">\n {error && (\n <div className=\"rounded-md border border-destructive/40 bg-destructive/10 p-3 text-sm text-destructive\">{error}</div>\n )}\n\n {kind === 'user' && (\n <div className=\"flex items-center gap-3 rounded-md border bg-muted/30 px-3 py-2\">\n <label className=\"flex items-center gap-2 text-sm\">\n <input\n type=\"radio\"\n name=\"widgetOverride\"\n value=\"inherit\"\n checked={mode === 'inherit'}\n onChange={() => setMode('inherit')}\n />\n {t('dashboards.widgets.mode.inherit', 'Inherit from roles')}\n </label>\n <label className=\"flex items-center gap-2 text-sm\">\n <input\n type=\"radio\"\n name=\"widgetOverride\"\n value=\"override\"\n checked={mode === 'override'}\n onChange={() => setMode('override')}\n />\n {t('dashboards.widgets.mode.override', 'Override for this user')}\n </label>\n </div>\n )}\n\n {kind === 'user' && mode === 'inherit' && (\n <div className=\"rounded-md border border-muted bg-muted/20 px-3 py-2 text-xs text-muted-foreground\">\n {t('dashboards.widgets.mode.hint', 'This user currently inherits widgets from their assigned roles. Switch to override to customize.')}\n </div>\n )}\n\n {(kind === 'role' || mode === 'override') && (\n <div className=\"space-y-3\">\n {catalog.map((widget) => (\n <label key={widget.id} className=\"flex items-start gap-3 rounded-md border px-3 py-2 hover:border-primary/40\">\n <input\n type=\"checkbox\"\n className=\"mt-1 size-4\"\n checked={selected.includes(widget.id)}\n onChange={() => toggle(widget.id)}\n />\n <div>\n <div className=\"text-sm font-medium leading-none\">{widget.title}</div>\n {widget.description ? <div className=\"mt-1 text-xs text-muted-foreground\">{widget.description}</div> : null}\n </div>\n </label>\n ))}\n </div>\n )}\n\n {kind === 'user' && effective.length > 0 && (\n <div className=\"rounded-md border bg-muted/30 px-3 py-2 text-xs text-muted-foreground\">\n Effective widgets: {effective.map((id) => catalog.find((meta) => meta.id === id)?.title || id).join(', ')}\n </div>\n )}\n\n <div className=\"flex items-center gap-2\">\n <Button type=\"button\" onClick={save} disabled={saving || !dirty}>\n {saving ? 'Saving\u2026' : 'Save widgets'}\n </Button>\n <Button type=\"button\" variant=\"ghost\" onClick={resetSelections} disabled={!dirty}>\n Reset\n </Button>\n </div>\n </div>\n )\n})\n\nWidgetVisibilityEditor.displayName = 'WidgetVisibilityEditor'\n"],
5
+ "mappings": "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",
6
+ "names": ["WidgetVisibilityEditor"]
7
7
  }
@@ -0,0 +1,58 @@
1
+ import { Role } from "@open-mercato/core/modules/auth/data/entities";
2
+ import { DashboardRoleWidgets } from "../data/entities.js";
3
+ import { loadAllWidgets } from "./widgets.js";
4
+ async function findRoleByName(em, roleName, tenantId) {
5
+ const tenantRole = await em.findOne(Role, { name: roleName, tenantId });
6
+ if (tenantRole) return tenantRole;
7
+ return em.findOne(Role, { name: roleName, tenantId: null });
8
+ }
9
+ async function resolveAnalyticsWidgetIds() {
10
+ const widgets = await loadAllWidgets();
11
+ return widgets.filter((widget) => widget.metadata.category === "analytics" || widget.metadata.id.startsWith("dashboards.analytics.")).map((widget) => widget.metadata.id);
12
+ }
13
+ async function appendWidgetsToRoles(em, { tenantId, organizationId = null, roleNames, widgetIds }) {
14
+ const trimmedTenantId = tenantId.trim();
15
+ const widgets = await loadAllWidgets();
16
+ const validWidgetIds = new Set(widgets.map((widget) => widget.metadata.id));
17
+ const resolvedWidgetIds = widgetIds.filter((id) => validWidgetIds.has(id));
18
+ if (!resolvedWidgetIds.length) return false;
19
+ let updated = false;
20
+ await em.transactional(async (tem) => {
21
+ for (const roleName of roleNames) {
22
+ const role = await findRoleByName(tem, roleName, trimmedTenantId);
23
+ if (!role) continue;
24
+ const record = await tem.findOne(DashboardRoleWidgets, {
25
+ roleId: String(role.id),
26
+ tenantId: trimmedTenantId,
27
+ organizationId,
28
+ deletedAt: null
29
+ });
30
+ const roleRecord = record ?? (organizationId ? await tem.findOne(DashboardRoleWidgets, {
31
+ roleId: String(role.id),
32
+ tenantId: trimmedTenantId,
33
+ organizationId: null,
34
+ deletedAt: null
35
+ }) : null);
36
+ if (!roleRecord) continue;
37
+ const current = Array.isArray(roleRecord.widgetIdsJson) ? roleRecord.widgetIdsJson : [];
38
+ const next = [...current];
39
+ const existing = new Set(current);
40
+ for (const widgetId of resolvedWidgetIds) {
41
+ if (existing.has(widgetId)) continue;
42
+ existing.add(widgetId);
43
+ next.push(widgetId);
44
+ }
45
+ if (next.length === current.length) continue;
46
+ roleRecord.widgetIdsJson = next;
47
+ roleRecord.updatedAt = /* @__PURE__ */ new Date();
48
+ tem.persist(roleRecord);
49
+ updated = true;
50
+ }
51
+ });
52
+ return updated;
53
+ }
54
+ export {
55
+ appendWidgetsToRoles,
56
+ resolveAnalyticsWidgetIds
57
+ };
58
+ //# sourceMappingURL=role-widgets.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../src/modules/dashboards/lib/role-widgets.ts"],
4
+ "sourcesContent": ["import type { EntityManager } from '@mikro-orm/postgresql'\nimport { Role } from '@open-mercato/core/modules/auth/data/entities'\nimport { DashboardRoleWidgets } from '../data/entities'\nimport { loadAllWidgets } from './widgets'\n\ntype RoleWidgetScope = {\n tenantId: string\n organizationId?: string | null\n roleNames: string[]\n widgetIds: string[]\n}\n\nasync function findRoleByName(\n em: EntityManager,\n roleName: string,\n tenantId: string,\n): Promise<Role | null> {\n const tenantRole = await em.findOne(Role, { name: roleName, tenantId })\n if (tenantRole) return tenantRole\n return em.findOne(Role, { name: roleName, tenantId: null })\n}\n\nexport async function resolveAnalyticsWidgetIds(): Promise<string[]> {\n const widgets = await loadAllWidgets()\n return widgets\n .filter((widget) => widget.metadata.category === 'analytics' || widget.metadata.id.startsWith('dashboards.analytics.'))\n .map((widget) => widget.metadata.id)\n}\n\nexport async function appendWidgetsToRoles(\n em: EntityManager,\n { tenantId, organizationId = null, roleNames, widgetIds }: RoleWidgetScope,\n): Promise<boolean> {\n const trimmedTenantId = tenantId.trim()\n const widgets = await loadAllWidgets()\n const validWidgetIds = new Set(widgets.map((widget) => widget.metadata.id))\n const resolvedWidgetIds = widgetIds.filter((id) => validWidgetIds.has(id))\n if (!resolvedWidgetIds.length) return false\n\n let updated = false\n await em.transactional(async (tem) => {\n for (const roleName of roleNames) {\n const role = await findRoleByName(tem, roleName, trimmedTenantId)\n if (!role) continue\n\n const record = await tem.findOne(DashboardRoleWidgets, {\n roleId: String(role.id),\n tenantId: trimmedTenantId,\n organizationId,\n deletedAt: null,\n })\n const roleRecord = record ?? (organizationId\n ? await tem.findOne(DashboardRoleWidgets, {\n roleId: String(role.id),\n tenantId: trimmedTenantId,\n organizationId: null,\n deletedAt: null,\n })\n : null)\n if (!roleRecord) continue\n\n const current = Array.isArray(roleRecord.widgetIdsJson) ? roleRecord.widgetIdsJson : []\n const next = [...current]\n const existing = new Set(current)\n for (const widgetId of resolvedWidgetIds) {\n if (existing.has(widgetId)) continue\n existing.add(widgetId)\n next.push(widgetId)\n }\n\n if (next.length === current.length) continue\n roleRecord.widgetIdsJson = next\n roleRecord.updatedAt = new Date()\n tem.persist(roleRecord)\n updated = true\n }\n })\n\n return updated\n}\n"],
5
+ "mappings": "AACA,SAAS,YAAY;AACrB,SAAS,4BAA4B;AACrC,SAAS,sBAAsB;AAS/B,eAAe,eACb,IACA,UACA,UACsB;AACtB,QAAM,aAAa,MAAM,GAAG,QAAQ,MAAM,EAAE,MAAM,UAAU,SAAS,CAAC;AACtE,MAAI,WAAY,QAAO;AACvB,SAAO,GAAG,QAAQ,MAAM,EAAE,MAAM,UAAU,UAAU,KAAK,CAAC;AAC5D;AAEA,eAAsB,4BAA+C;AACnE,QAAM,UAAU,MAAM,eAAe;AACrC,SAAO,QACJ,OAAO,CAAC,WAAW,OAAO,SAAS,aAAa,eAAe,OAAO,SAAS,GAAG,WAAW,uBAAuB,CAAC,EACrH,IAAI,CAAC,WAAW,OAAO,SAAS,EAAE;AACvC;AAEA,eAAsB,qBACpB,IACA,EAAE,UAAU,iBAAiB,MAAM,WAAW,UAAU,GACtC;AAClB,QAAM,kBAAkB,SAAS,KAAK;AACtC,QAAM,UAAU,MAAM,eAAe;AACrC,QAAM,iBAAiB,IAAI,IAAI,QAAQ,IAAI,CAAC,WAAW,OAAO,SAAS,EAAE,CAAC;AAC1E,QAAM,oBAAoB,UAAU,OAAO,CAAC,OAAO,eAAe,IAAI,EAAE,CAAC;AACzE,MAAI,CAAC,kBAAkB,OAAQ,QAAO;AAEtC,MAAI,UAAU;AACd,QAAM,GAAG,cAAc,OAAO,QAAQ;AACpC,eAAW,YAAY,WAAW;AAChC,YAAM,OAAO,MAAM,eAAe,KAAK,UAAU,eAAe;AAChE,UAAI,CAAC,KAAM;AAEX,YAAM,SAAS,MAAM,IAAI,QAAQ,sBAAsB;AAAA,QACrD,QAAQ,OAAO,KAAK,EAAE;AAAA,QACtB,UAAU;AAAA,QACV;AAAA,QACA,WAAW;AAAA,MACb,CAAC;AACD,YAAM,aAAa,WAAW,iBAC1B,MAAM,IAAI,QAAQ,sBAAsB;AAAA,QACxC,QAAQ,OAAO,KAAK,EAAE;AAAA,QACtB,UAAU;AAAA,QACV,gBAAgB;AAAA,QAChB,WAAW;AAAA,MACb,CAAC,IACC;AACJ,UAAI,CAAC,WAAY;AAEjB,YAAM,UAAU,MAAM,QAAQ,WAAW,aAAa,IAAI,WAAW,gBAAgB,CAAC;AACtF,YAAM,OAAO,CAAC,GAAG,OAAO;AACxB,YAAM,WAAW,IAAI,IAAI,OAAO;AAChC,iBAAW,YAAY,mBAAmB;AACxC,YAAI,SAAS,IAAI,QAAQ,EAAG;AAC5B,iBAAS,IAAI,QAAQ;AACrB,aAAK,KAAK,QAAQ;AAAA,MACpB;AAEA,UAAI,KAAK,WAAW,QAAQ,OAAQ;AACpC,iBAAW,gBAAgB;AAC3B,iBAAW,YAAY,oBAAI,KAAK;AAChC,UAAI,QAAQ,UAAU;AACtB,gBAAU;AAAA,IACZ;AAAA,EACF,CAAC;AAED,SAAO;AACT;",
6
+ "names": []
7
+ }
@@ -1,4 +1,8 @@
1
1
  import { createHash } from "node:crypto";
2
+ import { decryptWithAesGcm } from "@open-mercato/shared/lib/encryption/aes";
3
+ import { resolveTenantEncryptionService } from "@open-mercato/shared/lib/encryption/customFieldValues";
4
+ import { resolveEntityIdFromMetadata } from "@open-mercato/shared/lib/encryption/entityIds";
5
+ import { findWithDecryption } from "@open-mercato/shared/lib/encryption/find";
2
6
  import {
3
7
  resolveDateRange,
4
8
  getPreviousPeriod,
@@ -10,6 +14,8 @@ import {
10
14
  buildAggregationQuery
11
15
  } from "../lib/aggregations.js";
12
16
  const WIDGET_DATA_CACHE_TTL = 12e4;
17
+ const WIDGET_DATA_SEGMENT_TTL = 864e5;
18
+ const WIDGET_DATA_SEGMENT_KEY = "widget-data:__segment__";
13
19
  const SAFE_IDENTIFIER_PATTERN = /^[a-zA-Z_][a-zA-Z0-9_]*$/;
14
20
  class WidgetDataValidationError extends Error {
15
21
  constructor(message) {
@@ -83,6 +89,11 @@ class WidgetDataService {
83
89
  const tags = this.getCacheTags(request.entityType);
84
90
  try {
85
91
  await this.cache.set(cacheKey, response, { ttl: WIDGET_DATA_CACHE_TTL, tags });
92
+ await this.cache.set(
93
+ WIDGET_DATA_SEGMENT_KEY,
94
+ { updatedAt: response.metadata.fetchedAt },
95
+ { ttl: WIDGET_DATA_SEGMENT_TTL, tags: ["widget-data"] }
96
+ );
86
97
  } catch {
87
98
  }
88
99
  }
@@ -167,21 +178,100 @@ class WidgetDataService {
167
178
  assertSafeIdentifier(config.table, "table name");
168
179
  assertSafeIdentifier(config.idColumn, "id column");
169
180
  assertSafeIdentifier(config.labelColumn, "label column");
181
+ const meta = this.resolveEntityMetadata(config.table);
182
+ const idProp = meta ? this.resolveEntityPropertyName(meta, config.idColumn) : null;
183
+ const labelProp = meta ? this.resolveEntityPropertyName(meta, config.labelColumn) : null;
184
+ const tenantProp = meta ? this.resolveEntityPropertyName(meta, "tenant_id") ?? this.resolveEntityPropertyName(meta, "tenantId") : null;
185
+ const organizationProp = meta ? this.resolveEntityPropertyName(meta, "organization_id") ?? this.resolveEntityPropertyName(meta, "organizationId") : null;
186
+ const entityName = meta ? meta.class ?? meta.className ?? meta.name : null;
187
+ if (meta && idProp && labelProp && tenantProp && entityName) {
188
+ const where = {
189
+ [idProp]: { $in: uniqueIds },
190
+ [tenantProp]: this.scope.tenantId
191
+ };
192
+ if (organizationProp && this.scope.organizationIds && this.scope.organizationIds.length > 0) {
193
+ where[organizationProp] = { $in: this.scope.organizationIds };
194
+ }
195
+ try {
196
+ const records = await findWithDecryption(
197
+ this.em,
198
+ entityName,
199
+ where,
200
+ { fields: [idProp, labelProp, tenantProp, organizationProp].filter(Boolean) },
201
+ { tenantId: this.scope.tenantId, organizationId: this.resolveOrganizationId() }
202
+ );
203
+ const encryptionService = resolveTenantEncryptionService(this.em);
204
+ const dek = encryptionService?.isEnabled() ? await encryptionService.getDek(this.scope.tenantId) : null;
205
+ let hasEncryptedLabels = false;
206
+ let hasDecryptedLabels = false;
207
+ const labelMap = /* @__PURE__ */ new Map();
208
+ for (const record of records) {
209
+ const id = record[idProp];
210
+ let labelValue = record[labelProp];
211
+ if (typeof labelValue === "string" && this.isEncryptedPayload(labelValue)) {
212
+ hasEncryptedLabels = true;
213
+ if (dek?.key) {
214
+ const decrypted = this.decryptWithDek(labelValue, dek.key);
215
+ if (decrypted !== null) {
216
+ labelValue = decrypted;
217
+ hasDecryptedLabels = true;
218
+ }
219
+ }
220
+ } else if (labelValue != null && labelValue !== "") {
221
+ hasDecryptedLabels = true;
222
+ }
223
+ if (typeof id === "string" && labelValue != null && labelValue !== "") {
224
+ labelMap.set(id, String(labelValue));
225
+ }
226
+ }
227
+ if (labelMap.size > 0 && (!hasEncryptedLabels || hasDecryptedLabels)) {
228
+ return data.map((item) => ({
229
+ ...item,
230
+ groupLabel: typeof item.groupKey === "string" && labelMap.has(item.groupKey) ? labelMap.get(item.groupKey) : void 0
231
+ }));
232
+ }
233
+ } catch {
234
+ }
235
+ }
170
236
  const clauses = [`"${config.idColumn}" = ANY(?::uuid[])`, "tenant_id = ?"];
171
237
  const params = [`{${uniqueIds.join(",")}}`, this.scope.tenantId];
172
238
  if (this.scope.organizationIds && this.scope.organizationIds.length > 0) {
173
239
  clauses.push("organization_id = ANY(?::uuid[])");
174
240
  params.push(`{${this.scope.organizationIds.join(",")}}`);
175
241
  }
176
- const sql = `SELECT "${config.idColumn}" as id, "${config.labelColumn}" as label FROM "${config.table}" WHERE ${clauses.join(
242
+ const sql = `SELECT "${config.idColumn}" as id, "${config.labelColumn}" as label, tenant_id, organization_id FROM "${config.table}" WHERE ${clauses.join(
177
243
  " AND "
178
244
  )}`;
179
245
  try {
180
246
  const labelRows = await this.em.getConnection().execute(sql, params);
247
+ const entityId = this.resolveEntityId(meta);
248
+ const encryptionService = resolveTenantEncryptionService(this.em);
249
+ const organizationId = this.resolveOrganizationId();
250
+ const dek = encryptionService?.isEnabled() ? await encryptionService.getDek(this.scope.tenantId) : null;
181
251
  const labelMap = /* @__PURE__ */ new Map();
182
252
  for (const row of labelRows) {
183
- if (row.id && row.label != null && row.label !== "") {
184
- labelMap.set(row.id, row.label);
253
+ let labelValue = row.label;
254
+ if (entityId && encryptionService?.isEnabled() && labelValue != null) {
255
+ const rowOrgId = row.organization_id ?? organizationId ?? null;
256
+ const decrypted = await encryptionService.decryptEntityPayload(
257
+ entityId,
258
+ { [config.labelColumn]: labelValue },
259
+ this.scope.tenantId,
260
+ rowOrgId
261
+ );
262
+ const resolved = decrypted[config.labelColumn];
263
+ if (typeof resolved === "string" || typeof resolved === "number") {
264
+ labelValue = String(resolved);
265
+ }
266
+ }
267
+ if (labelValue && dek?.key && this.isEncryptedPayload(labelValue)) {
268
+ const decrypted = this.decryptWithDek(labelValue, dek.key);
269
+ if (decrypted !== null) {
270
+ labelValue = decrypted;
271
+ }
272
+ }
273
+ if (row.id && labelValue != null && labelValue !== "") {
274
+ labelMap.set(row.id, labelValue);
185
275
  }
186
276
  }
187
277
  return data.map((item) => ({
@@ -195,6 +285,52 @@ class WidgetDataService {
195
285
  }));
196
286
  }
197
287
  }
288
+ resolveOrganizationId() {
289
+ if (!this.scope.organizationIds || this.scope.organizationIds.length !== 1) return null;
290
+ return this.scope.organizationIds[0] ?? null;
291
+ }
292
+ resolveEntityMetadata(tableName) {
293
+ const registry = this.em?.getMetadata?.();
294
+ if (!registry) return null;
295
+ const entries = typeof registry.getAll === "function" && registry.getAll() || (Array.isArray(registry.metadata) ? registry.metadata : Object.values(registry.metadata ?? {}));
296
+ const metas = Array.isArray(entries) ? entries : Object.values(entries ?? {});
297
+ const match = metas.find((meta) => {
298
+ const table = meta?.tableName ?? meta?.collection;
299
+ if (typeof table !== "string") return false;
300
+ if (table === tableName) return true;
301
+ return table.split(".").pop() === tableName;
302
+ });
303
+ return match ?? null;
304
+ }
305
+ resolveEntityPropertyName(meta, columnName) {
306
+ const properties = meta?.properties ? Object.values(meta.properties) : [];
307
+ for (const prop of properties) {
308
+ const fieldName = prop?.fieldName;
309
+ const fieldNames = prop?.fieldNames;
310
+ if (typeof fieldName === "string" && fieldName === columnName) return prop?.name ?? null;
311
+ if (Array.isArray(fieldNames) && fieldNames.includes(columnName)) return prop?.name ?? null;
312
+ if (prop?.name === columnName) return prop?.name ?? null;
313
+ }
314
+ return null;
315
+ }
316
+ resolveEntityId(meta) {
317
+ if (!meta) return null;
318
+ try {
319
+ return resolveEntityIdFromMetadata(meta);
320
+ } catch {
321
+ return null;
322
+ }
323
+ }
324
+ isEncryptedPayload(value) {
325
+ const parts = value.split(":");
326
+ return parts.length === 4 && parts[3] === "v1";
327
+ }
328
+ decryptWithDek(value, dek) {
329
+ const first = decryptWithAesGcm(value, dek);
330
+ if (first === null) return null;
331
+ if (!this.isEncryptedPayload(first)) return first;
332
+ return decryptWithAesGcm(first, dek) ?? first;
333
+ }
198
334
  }
199
335
  function createWidgetDataService(em, scope, registry, cache) {
200
336
  return new WidgetDataService({ em, scope, registry, cache });
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/modules/dashboards/services/widgetDataService.ts"],
4
- "sourcesContent": ["import type { EntityManager } from '@mikro-orm/postgresql'\nimport type { CacheStrategy } from '@open-mercato/cache'\nimport { createHash } from 'node:crypto'\nimport {\n type DateRangePreset,\n resolveDateRange,\n getPreviousPeriod,\n calculatePercentageChange,\n determineChangeDirection,\n isValidDateRangePreset,\n} from '@open-mercato/ui/backend/date-range'\nimport {\n type AggregateFunction,\n type DateGranularity,\n buildAggregationQuery,\n} from '../lib/aggregations'\nimport type { AnalyticsRegistry } from './analyticsRegistry'\n\nconst WIDGET_DATA_CACHE_TTL = 120_000\n\nconst SAFE_IDENTIFIER_PATTERN = /^[a-zA-Z_][a-zA-Z0-9_]*$/\n\nexport class WidgetDataValidationError extends Error {\n constructor(message: string) {\n super(message)\n this.name = 'WidgetDataValidationError'\n }\n}\n\nfunction assertSafeIdentifier(value: string, name: string): void {\n if (!SAFE_IDENTIFIER_PATTERN.test(value)) {\n throw new Error(`Invalid ${name}: ${value}`)\n }\n}\n\nexport type WidgetDataRequest = {\n entityType: string\n metric: {\n field: string\n aggregate: AggregateFunction\n }\n groupBy?: {\n field: string\n granularity?: DateGranularity\n limit?: number\n resolveLabels?: boolean\n }\n filters?: Array<{\n field: string\n operator: 'eq' | 'neq' | 'gt' | 'gte' | 'lt' | 'lte' | 'in' | 'not_in' | 'is_null' | 'is_not_null'\n value?: unknown\n }>\n dateRange?: {\n field: string\n preset: DateRangePreset\n }\n comparison?: {\n type: 'previous_period' | 'previous_year'\n }\n}\n\nexport type WidgetDataItem = {\n groupKey: unknown\n groupLabel?: string\n value: number | null\n}\n\nexport type WidgetDataResponse = {\n value: number | null\n data: WidgetDataItem[]\n comparison?: {\n value: number | null\n change: number\n direction: 'up' | 'down' | 'unchanged'\n }\n metadata: {\n fetchedAt: string\n recordCount: number\n }\n}\n\nexport type WidgetDataScope = {\n tenantId: string\n organizationIds?: string[]\n}\n\nexport type WidgetDataServiceOptions = {\n em: EntityManager\n scope: WidgetDataScope\n registry: AnalyticsRegistry\n cache?: CacheStrategy\n}\n\nexport class WidgetDataService {\n private em: EntityManager\n private scope: WidgetDataScope\n private registry: AnalyticsRegistry\n private cache?: CacheStrategy\n\n constructor(options: WidgetDataServiceOptions) {\n this.em = options.em\n this.scope = options.scope\n this.registry = options.registry\n this.cache = options.cache\n }\n\n private buildCacheKey(request: WidgetDataRequest): string {\n const hash = createHash('sha256')\n hash.update(JSON.stringify({ request, scope: this.scope }))\n return `widget-data:${hash.digest('hex').slice(0, 16)}`\n }\n\n private getCacheTags(entityType: string): string[] {\n return ['widget-data', `widget-data:${entityType}`]\n }\n\n async fetchWidgetData(request: WidgetDataRequest): Promise<WidgetDataResponse> {\n this.validateRequest(request)\n\n if (this.cache) {\n const cacheKey = this.buildCacheKey(request)\n try {\n const cached = await this.cache.get(cacheKey)\n if (cached && typeof cached === 'object' && 'value' in (cached as object)) {\n return cached as WidgetDataResponse\n }\n } catch {\n }\n }\n\n const now = new Date()\n let dateRangeResolved: { start: Date; end: Date } | undefined\n let comparisonRange: { start: Date; end: Date } | undefined\n\n if (request.dateRange) {\n dateRangeResolved = resolveDateRange(request.dateRange.preset, now)\n if (request.comparison) {\n comparisonRange = getPreviousPeriod(dateRangeResolved, request.dateRange.preset)\n }\n }\n\n const mainResult = await this.executeQuery(request, dateRangeResolved)\n\n let comparisonResult: { value: number | null; data: WidgetDataItem[] } | undefined\n if (comparisonRange && request.dateRange) {\n comparisonResult = await this.executeQuery(request, comparisonRange)\n }\n\n const response: WidgetDataResponse = {\n value: mainResult.value,\n data: mainResult.data,\n metadata: {\n fetchedAt: now.toISOString(),\n recordCount: mainResult.data.length || (mainResult.value !== null ? 1 : 0),\n },\n }\n\n if (comparisonResult && mainResult.value !== null && comparisonResult.value !== null) {\n response.comparison = {\n value: comparisonResult.value,\n change: calculatePercentageChange(mainResult.value, comparisonResult.value),\n direction: determineChangeDirection(mainResult.value, comparisonResult.value),\n }\n }\n\n if (this.cache) {\n const cacheKey = this.buildCacheKey(request)\n const tags = this.getCacheTags(request.entityType)\n try {\n await this.cache.set(cacheKey, response, { ttl: WIDGET_DATA_CACHE_TTL, tags })\n } catch {\n }\n }\n\n return response\n }\n\n private validateRequest(request: WidgetDataRequest): void {\n if (!this.registry.isValidEntityType(request.entityType)) {\n throw new WidgetDataValidationError(`Invalid entity type: ${request.entityType}`)\n }\n\n if (!request.metric?.field || !request.metric?.aggregate) {\n throw new WidgetDataValidationError('Metric field and aggregate are required')\n }\n\n const metricMapping = this.registry.getFieldMapping(request.entityType, request.metric.field)\n if (!metricMapping) {\n throw new WidgetDataValidationError(\n `Invalid metric field: ${request.metric.field} for entity type: ${request.entityType}`\n )\n }\n\n const validAggregates: AggregateFunction[] = ['count', 'sum', 'avg', 'min', 'max']\n if (!validAggregates.includes(request.metric.aggregate)) {\n throw new WidgetDataValidationError(`Invalid aggregate function: ${request.metric.aggregate}`)\n }\n\n if (request.dateRange && !isValidDateRangePreset(request.dateRange.preset)) {\n throw new WidgetDataValidationError(`Invalid date range preset: ${request.dateRange.preset}`)\n }\n\n if (request.groupBy) {\n const groupMapping = this.registry.getFieldMapping(request.entityType, request.groupBy.field)\n if (!groupMapping) {\n const [baseField] = request.groupBy.field.split('.')\n const baseMapping = this.registry.getFieldMapping(request.entityType, baseField)\n if (!baseMapping || baseMapping.type !== 'jsonb') {\n throw new WidgetDataValidationError(`Invalid groupBy field: ${request.groupBy.field}`)\n }\n }\n }\n }\n\n private async executeQuery(\n request: WidgetDataRequest,\n dateRange?: { start: Date; end: Date },\n ): Promise<{ value: number | null; data: WidgetDataItem[] }> {\n const query = buildAggregationQuery({\n entityType: request.entityType,\n metric: request.metric,\n groupBy: request.groupBy,\n dateRange: dateRange && request.dateRange ? { field: request.dateRange.field, ...dateRange } : undefined,\n filters: request.filters,\n scope: this.scope,\n registry: this.registry,\n })\n\n if (!query) {\n throw new Error('Failed to build aggregation query')\n }\n\n const rows = await this.em.getConnection().execute(query.sql, query.params)\n const results = Array.isArray(rows) ? rows : []\n\n if (request.groupBy) {\n let data: WidgetDataItem[] = results.map((row: Record<string, unknown>) => ({\n groupKey: row.group_key,\n value: row.value !== null ? Number(row.value) : null,\n }))\n\n if (request.groupBy.resolveLabels) {\n data = await this.resolveGroupLabels(data, request.entityType, request.groupBy.field)\n }\n\n const totalValue = data.reduce((sum: number, item: WidgetDataItem) => sum + (item.value ?? 0), 0)\n return { value: totalValue, data }\n }\n\n const singleValue = results[0]?.value !== undefined ? Number(results[0].value) : null\n return { value: singleValue, data: [] }\n }\n\n private async resolveGroupLabels(\n data: WidgetDataItem[],\n entityType: string,\n groupByField: string,\n ): Promise<WidgetDataItem[]> {\n const config = this.registry.getLabelResolverConfig(entityType, groupByField)\n\n if (!config) {\n return data.map((item) => ({\n ...item,\n groupLabel: item.groupKey != null && item.groupKey !== '' ? String(item.groupKey) : undefined,\n }))\n }\n\n const ids = data\n .map((item) => item.groupKey)\n .filter((id): id is string => {\n if (typeof id !== 'string' || id.length === 0) return false\n return /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(id)\n })\n\n if (ids.length === 0) {\n return data.map((item) => ({ ...item, groupLabel: undefined }))\n }\n\n const uniqueIds = [...new Set(ids)]\n\n assertSafeIdentifier(config.table, 'table name')\n assertSafeIdentifier(config.idColumn, 'id column')\n assertSafeIdentifier(config.labelColumn, 'label column')\n\n const clauses = [`\"${config.idColumn}\" = ANY(?::uuid[])`, 'tenant_id = ?']\n const params: unknown[] = [`{${uniqueIds.join(',')}}`, this.scope.tenantId]\n\n if (this.scope.organizationIds && this.scope.organizationIds.length > 0) {\n clauses.push('organization_id = ANY(?::uuid[])')\n params.push(`{${this.scope.organizationIds.join(',')}}`)\n }\n\n const sql = `SELECT \"${config.idColumn}\" as id, \"${config.labelColumn}\" as label FROM \"${config.table}\" WHERE ${clauses.join(\n ' AND ',\n )}`\n\n try {\n const labelRows = await this.em.getConnection().execute(sql, params)\n\n const labelMap = new Map<string, string>()\n for (const row of labelRows as Array<{ id: string; label: string | null }>) {\n if (row.id && row.label != null && row.label !== '') {\n labelMap.set(row.id, row.label)\n }\n }\n\n return data.map((item) => ({\n ...item,\n groupLabel: typeof item.groupKey === 'string' && labelMap.has(item.groupKey)\n ? labelMap.get(item.groupKey)!\n : undefined,\n }))\n } catch {\n return data.map((item) => ({\n ...item,\n groupLabel: undefined,\n }))\n }\n }\n}\n\nexport function createWidgetDataService(\n em: EntityManager,\n scope: WidgetDataScope,\n registry: AnalyticsRegistry,\n cache?: CacheStrategy,\n): WidgetDataService {\n return new WidgetDataService({ em, scope, registry, cache })\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import type { EntityManager } from '@mikro-orm/postgresql'\nimport type { CacheStrategy } from '@open-mercato/cache'\nimport { createHash } from 'node:crypto'\nimport { decryptWithAesGcm } from '@open-mercato/shared/lib/encryption/aes'\nimport { resolveTenantEncryptionService } from '@open-mercato/shared/lib/encryption/customFieldValues'\nimport { resolveEntityIdFromMetadata } from '@open-mercato/shared/lib/encryption/entityIds'\nimport { findWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport {\n type DateRangePreset,\n resolveDateRange,\n getPreviousPeriod,\n calculatePercentageChange,\n determineChangeDirection,\n isValidDateRangePreset,\n} from '@open-mercato/ui/backend/date-range'\nimport {\n type AggregateFunction,\n type DateGranularity,\n buildAggregationQuery,\n} from '../lib/aggregations'\nimport type { AnalyticsRegistry } from './analyticsRegistry'\n\nconst WIDGET_DATA_CACHE_TTL = 120_000\nconst WIDGET_DATA_SEGMENT_TTL = 86_400_000\nconst WIDGET_DATA_SEGMENT_KEY = 'widget-data:__segment__'\n\nconst SAFE_IDENTIFIER_PATTERN = /^[a-zA-Z_][a-zA-Z0-9_]*$/\n\nexport class WidgetDataValidationError extends Error {\n constructor(message: string) {\n super(message)\n this.name = 'WidgetDataValidationError'\n }\n}\n\nfunction assertSafeIdentifier(value: string, name: string): void {\n if (!SAFE_IDENTIFIER_PATTERN.test(value)) {\n throw new Error(`Invalid ${name}: ${value}`)\n }\n}\n\nexport type WidgetDataRequest = {\n entityType: string\n metric: {\n field: string\n aggregate: AggregateFunction\n }\n groupBy?: {\n field: string\n granularity?: DateGranularity\n limit?: number\n resolveLabels?: boolean\n }\n filters?: Array<{\n field: string\n operator: 'eq' | 'neq' | 'gt' | 'gte' | 'lt' | 'lte' | 'in' | 'not_in' | 'is_null' | 'is_not_null'\n value?: unknown\n }>\n dateRange?: {\n field: string\n preset: DateRangePreset\n }\n comparison?: {\n type: 'previous_period' | 'previous_year'\n }\n}\n\nexport type WidgetDataItem = {\n groupKey: unknown\n groupLabel?: string\n value: number | null\n}\n\nexport type WidgetDataResponse = {\n value: number | null\n data: WidgetDataItem[]\n comparison?: {\n value: number | null\n change: number\n direction: 'up' | 'down' | 'unchanged'\n }\n metadata: {\n fetchedAt: string\n recordCount: number\n }\n}\n\nexport type WidgetDataScope = {\n tenantId: string\n organizationIds?: string[]\n}\n\nexport type WidgetDataServiceOptions = {\n em: EntityManager\n scope: WidgetDataScope\n registry: AnalyticsRegistry\n cache?: CacheStrategy\n}\n\nexport class WidgetDataService {\n private em: EntityManager\n private scope: WidgetDataScope\n private registry: AnalyticsRegistry\n private cache?: CacheStrategy\n\n constructor(options: WidgetDataServiceOptions) {\n this.em = options.em\n this.scope = options.scope\n this.registry = options.registry\n this.cache = options.cache\n }\n\n private buildCacheKey(request: WidgetDataRequest): string {\n const hash = createHash('sha256')\n hash.update(JSON.stringify({ request, scope: this.scope }))\n return `widget-data:${hash.digest('hex').slice(0, 16)}`\n }\n\n private getCacheTags(entityType: string): string[] {\n return ['widget-data', `widget-data:${entityType}`]\n }\n\n async fetchWidgetData(request: WidgetDataRequest): Promise<WidgetDataResponse> {\n this.validateRequest(request)\n\n if (this.cache) {\n const cacheKey = this.buildCacheKey(request)\n try {\n const cached = await this.cache.get(cacheKey)\n if (cached && typeof cached === 'object' && 'value' in (cached as object)) {\n return cached as WidgetDataResponse\n }\n } catch {\n }\n }\n\n const now = new Date()\n let dateRangeResolved: { start: Date; end: Date } | undefined\n let comparisonRange: { start: Date; end: Date } | undefined\n\n if (request.dateRange) {\n dateRangeResolved = resolveDateRange(request.dateRange.preset, now)\n if (request.comparison) {\n comparisonRange = getPreviousPeriod(dateRangeResolved, request.dateRange.preset)\n }\n }\n\n const mainResult = await this.executeQuery(request, dateRangeResolved)\n\n let comparisonResult: { value: number | null; data: WidgetDataItem[] } | undefined\n if (comparisonRange && request.dateRange) {\n comparisonResult = await this.executeQuery(request, comparisonRange)\n }\n\n const response: WidgetDataResponse = {\n value: mainResult.value,\n data: mainResult.data,\n metadata: {\n fetchedAt: now.toISOString(),\n recordCount: mainResult.data.length || (mainResult.value !== null ? 1 : 0),\n },\n }\n\n if (comparisonResult && mainResult.value !== null && comparisonResult.value !== null) {\n response.comparison = {\n value: comparisonResult.value,\n change: calculatePercentageChange(mainResult.value, comparisonResult.value),\n direction: determineChangeDirection(mainResult.value, comparisonResult.value),\n }\n }\n\n if (this.cache) {\n const cacheKey = this.buildCacheKey(request)\n const tags = this.getCacheTags(request.entityType)\n try {\n await this.cache.set(cacheKey, response, { ttl: WIDGET_DATA_CACHE_TTL, tags })\n await this.cache.set(\n WIDGET_DATA_SEGMENT_KEY,\n { updatedAt: response.metadata.fetchedAt },\n { ttl: WIDGET_DATA_SEGMENT_TTL, tags: ['widget-data'] },\n )\n } catch {\n }\n }\n\n return response\n }\n\n private validateRequest(request: WidgetDataRequest): void {\n if (!this.registry.isValidEntityType(request.entityType)) {\n throw new WidgetDataValidationError(`Invalid entity type: ${request.entityType}`)\n }\n\n if (!request.metric?.field || !request.metric?.aggregate) {\n throw new WidgetDataValidationError('Metric field and aggregate are required')\n }\n\n const metricMapping = this.registry.getFieldMapping(request.entityType, request.metric.field)\n if (!metricMapping) {\n throw new WidgetDataValidationError(\n `Invalid metric field: ${request.metric.field} for entity type: ${request.entityType}`\n )\n }\n\n const validAggregates: AggregateFunction[] = ['count', 'sum', 'avg', 'min', 'max']\n if (!validAggregates.includes(request.metric.aggregate)) {\n throw new WidgetDataValidationError(`Invalid aggregate function: ${request.metric.aggregate}`)\n }\n\n if (request.dateRange && !isValidDateRangePreset(request.dateRange.preset)) {\n throw new WidgetDataValidationError(`Invalid date range preset: ${request.dateRange.preset}`)\n }\n\n if (request.groupBy) {\n const groupMapping = this.registry.getFieldMapping(request.entityType, request.groupBy.field)\n if (!groupMapping) {\n const [baseField] = request.groupBy.field.split('.')\n const baseMapping = this.registry.getFieldMapping(request.entityType, baseField)\n if (!baseMapping || baseMapping.type !== 'jsonb') {\n throw new WidgetDataValidationError(`Invalid groupBy field: ${request.groupBy.field}`)\n }\n }\n }\n }\n\n private async executeQuery(\n request: WidgetDataRequest,\n dateRange?: { start: Date; end: Date },\n ): Promise<{ value: number | null; data: WidgetDataItem[] }> {\n const query = buildAggregationQuery({\n entityType: request.entityType,\n metric: request.metric,\n groupBy: request.groupBy,\n dateRange: dateRange && request.dateRange ? { field: request.dateRange.field, ...dateRange } : undefined,\n filters: request.filters,\n scope: this.scope,\n registry: this.registry,\n })\n\n if (!query) {\n throw new Error('Failed to build aggregation query')\n }\n\n const rows = await this.em.getConnection().execute(query.sql, query.params)\n const results = Array.isArray(rows) ? rows : []\n\n if (request.groupBy) {\n let data: WidgetDataItem[] = results.map((row: Record<string, unknown>) => ({\n groupKey: row.group_key,\n value: row.value !== null ? Number(row.value) : null,\n }))\n\n if (request.groupBy.resolveLabels) {\n data = await this.resolveGroupLabels(data, request.entityType, request.groupBy.field)\n }\n\n const totalValue = data.reduce((sum: number, item: WidgetDataItem) => sum + (item.value ?? 0), 0)\n return { value: totalValue, data }\n }\n\n const singleValue = results[0]?.value !== undefined ? Number(results[0].value) : null\n return { value: singleValue, data: [] }\n }\n\n private async resolveGroupLabels(\n data: WidgetDataItem[],\n entityType: string,\n groupByField: string,\n ): Promise<WidgetDataItem[]> {\n const config = this.registry.getLabelResolverConfig(entityType, groupByField)\n\n if (!config) {\n return data.map((item) => ({\n ...item,\n groupLabel: item.groupKey != null && item.groupKey !== '' ? String(item.groupKey) : undefined,\n }))\n }\n\n const ids = data\n .map((item) => item.groupKey)\n .filter((id): id is string => {\n if (typeof id !== 'string' || id.length === 0) return false\n return /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(id)\n })\n\n if (ids.length === 0) {\n return data.map((item) => ({ ...item, groupLabel: undefined }))\n }\n\n const uniqueIds = [...new Set(ids)]\n\n assertSafeIdentifier(config.table, 'table name')\n assertSafeIdentifier(config.idColumn, 'id column')\n assertSafeIdentifier(config.labelColumn, 'label column')\n\n const meta = this.resolveEntityMetadata(config.table)\n const idProp = meta ? this.resolveEntityPropertyName(meta, config.idColumn) : null\n const labelProp = meta ? this.resolveEntityPropertyName(meta, config.labelColumn) : null\n const tenantProp = meta\n ? (this.resolveEntityPropertyName(meta, 'tenant_id') ?? this.resolveEntityPropertyName(meta, 'tenantId'))\n : null\n const organizationProp = meta\n ? (this.resolveEntityPropertyName(meta, 'organization_id') ?? this.resolveEntityPropertyName(meta, 'organizationId'))\n : null\n const entityName = meta ? ((meta as any).class ?? meta.className ?? meta.name) : null\n\n if (meta && idProp && labelProp && tenantProp && entityName) {\n const where: Record<string, unknown> = {\n [idProp]: { $in: uniqueIds },\n [tenantProp]: this.scope.tenantId,\n }\n if (organizationProp && this.scope.organizationIds && this.scope.organizationIds.length > 0) {\n where[organizationProp] = { $in: this.scope.organizationIds }\n }\n\n try {\n const records = await findWithDecryption(\n this.em,\n entityName,\n where,\n { fields: [idProp, labelProp, tenantProp, organizationProp].filter(Boolean) },\n { tenantId: this.scope.tenantId, organizationId: this.resolveOrganizationId() },\n )\n\n const encryptionService = resolveTenantEncryptionService(this.em as any)\n const dek = encryptionService?.isEnabled() ? await encryptionService.getDek(this.scope.tenantId) : null\n let hasEncryptedLabels = false\n let hasDecryptedLabels = false\n\n const labelMap = new Map<string, string>()\n for (const record of records as Array<Record<string, unknown>>) {\n const id = record[idProp]\n let labelValue = record[labelProp]\n if (typeof labelValue === 'string' && this.isEncryptedPayload(labelValue)) {\n hasEncryptedLabels = true\n if (dek?.key) {\n const decrypted = this.decryptWithDek(labelValue, dek.key)\n if (decrypted !== null) {\n labelValue = decrypted\n hasDecryptedLabels = true\n }\n }\n } else if (labelValue != null && labelValue !== '') {\n hasDecryptedLabels = true\n }\n\n if (typeof id === 'string' && labelValue != null && labelValue !== '') {\n labelMap.set(id, String(labelValue))\n }\n }\n\n if (labelMap.size > 0 && (!hasEncryptedLabels || hasDecryptedLabels)) {\n return data.map((item) => ({\n ...item,\n groupLabel: typeof item.groupKey === 'string' && labelMap.has(item.groupKey)\n ? labelMap.get(item.groupKey)!\n : undefined,\n }))\n }\n } catch {\n // fall through to SQL resolution\n }\n }\n\n const clauses = [`\"${config.idColumn}\" = ANY(?::uuid[])`, 'tenant_id = ?']\n const params: unknown[] = [`{${uniqueIds.join(',')}}`, this.scope.tenantId]\n\n if (this.scope.organizationIds && this.scope.organizationIds.length > 0) {\n clauses.push('organization_id = ANY(?::uuid[])')\n params.push(`{${this.scope.organizationIds.join(',')}}`)\n }\n\n const sql = `SELECT \"${config.idColumn}\" as id, \"${config.labelColumn}\" as label, tenant_id, organization_id FROM \"${config.table}\" WHERE ${clauses.join(\n ' AND ',\n )}`\n\n try {\n const labelRows = await this.em.getConnection().execute(sql, params)\n const entityId = this.resolveEntityId(meta)\n const encryptionService = resolveTenantEncryptionService(this.em as any)\n const organizationId = this.resolveOrganizationId()\n const dek = encryptionService?.isEnabled() ? await encryptionService.getDek(this.scope.tenantId) : null\n\n const labelMap = new Map<string, string>()\n for (const row of labelRows as Array<{ id: string; label: string | null; tenant_id?: string | null; organization_id?: string | null }>) {\n let labelValue = row.label\n if (entityId && encryptionService?.isEnabled() && labelValue != null) {\n const rowOrgId = row.organization_id ?? organizationId ?? null\n const decrypted = await encryptionService.decryptEntityPayload(\n entityId,\n { [config.labelColumn]: labelValue },\n this.scope.tenantId,\n rowOrgId,\n )\n const resolved = decrypted[config.labelColumn]\n if (typeof resolved === 'string' || typeof resolved === 'number') {\n labelValue = String(resolved)\n }\n }\n\n if (labelValue && dek?.key && this.isEncryptedPayload(labelValue)) {\n const decrypted = this.decryptWithDek(labelValue, dek.key)\n if (decrypted !== null) {\n labelValue = decrypted\n }\n }\n\n if (row.id && labelValue != null && labelValue !== '') {\n labelMap.set(row.id, labelValue)\n }\n }\n\n return data.map((item) => ({\n ...item,\n groupLabel: typeof item.groupKey === 'string' && labelMap.has(item.groupKey)\n ? labelMap.get(item.groupKey)!\n : undefined,\n }))\n } catch {\n return data.map((item) => ({\n ...item,\n groupLabel: undefined,\n }))\n }\n }\n\n private resolveOrganizationId(): string | null {\n if (!this.scope.organizationIds || this.scope.organizationIds.length !== 1) return null\n return this.scope.organizationIds[0] ?? null\n }\n\n private resolveEntityMetadata(tableName: string): Record<string, any> | null {\n const registry = (this.em as any)?.getMetadata?.()\n if (!registry) return null\n const entries =\n (typeof registry.getAll === 'function' && registry.getAll()) ||\n (Array.isArray(registry.metadata) ? registry.metadata : Object.values(registry.metadata ?? {}))\n const metas = Array.isArray(entries) ? entries : Object.values(entries ?? {})\n const match = metas.find((meta: any) => {\n const table = meta?.tableName ?? meta?.collection\n if (typeof table !== 'string') return false\n if (table === tableName) return true\n return table.split('.').pop() === tableName\n })\n return match ?? null\n }\n\n private resolveEntityPropertyName(meta: Record<string, any>, columnName: string): string | null {\n const properties = meta?.properties ? Object.values(meta.properties) : []\n for (const prop of properties as Array<Record<string, any>>) {\n const fieldName = prop?.fieldName\n const fieldNames = prop?.fieldNames\n if (typeof fieldName === 'string' && fieldName === columnName) return prop?.name ?? null\n if (Array.isArray(fieldNames) && fieldNames.includes(columnName)) return prop?.name ?? null\n if (prop?.name === columnName) return prop?.name ?? null\n }\n return null\n }\n\n private resolveEntityId(meta: Record<string, any> | null): string | null {\n if (!meta) return null\n try {\n return resolveEntityIdFromMetadata(meta as any)\n } catch {\n return null\n }\n }\n\n private isEncryptedPayload(value: string): boolean {\n const parts = value.split(':')\n return parts.length === 4 && parts[3] === 'v1'\n }\n\n private decryptWithDek(value: string, dek: string): string | null {\n const first = decryptWithAesGcm(value, dek)\n if (first === null) return null\n if (!this.isEncryptedPayload(first)) return first\n return decryptWithAesGcm(first, dek) ?? first\n }\n}\n\nexport function createWidgetDataService(\n em: EntityManager,\n scope: WidgetDataScope,\n registry: AnalyticsRegistry,\n cache?: CacheStrategy,\n): WidgetDataService {\n return new WidgetDataService({ em, scope, registry, cache })\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -106,12 +106,14 @@ function DictionaryTable({
106
106
  const items = [];
107
107
  if (onEdit) {
108
108
  items.push({
109
+ id: "edit",
109
110
  label: translations.editLabel,
110
111
  onSelect: () => onEdit(entry)
111
112
  });
112
113
  }
113
114
  if (onDelete) {
114
115
  items.push({
116
+ id: "delete",
115
117
  label: translations.deleteLabel,
116
118
  onSelect: () => onDelete(entry),
117
119
  destructive: true
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/modules/dictionaries/components/DictionaryTable.tsx"],
4
- "sourcesContent": ["import * as React from 'react'\nimport type { ColumnDef, SortingState } from '@tanstack/react-table'\nimport { DataTable } from '@open-mercato/ui/backend/DataTable'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { RowActions, type RowActionItem } from '@open-mercato/ui/backend/RowActions'\nimport { renderDictionaryColor, renderDictionaryIcon } from './dictionaryAppearance'\n\nexport type DictionaryTableEntry = {\n id: string\n value: string\n label: string\n color: string | null\n icon: string | null\n organizationId?: string | null\n tenantId?: string | null\n isInherited?: boolean\n createdAt?: string | null\n updatedAt?: string | null\n}\n\nexport type DictionaryTableTranslations = {\n title: string\n valueColumn: string\n labelColumn: string\n appearanceColumn: string\n addLabel: string\n editLabel: string\n deleteLabel: string\n refreshLabel: string\n inheritedLabel: string\n inheritedTooltip: string\n emptyLabel: string\n searchPlaceholder?: string\n}\n\ntype DictionaryTableProps = {\n entries: DictionaryTableEntry[]\n loading?: boolean\n canManage?: boolean\n onCreate?: () => void\n onEdit?: (entry: DictionaryTableEntry) => void\n onDelete?: (entry: DictionaryTableEntry) => void\n onRefresh?: () => void\n translations: DictionaryTableTranslations\n}\n\nexport function DictionaryTable({\n entries,\n loading = false,\n canManage = false,\n onCreate,\n onEdit,\n onDelete,\n onRefresh,\n translations,\n}: DictionaryTableProps) {\n const [search, setSearch] = React.useState('')\n const [sorting, setSorting] = React.useState<SortingState>([{ id: 'value', desc: false }])\n const [page, setPage] = React.useState(1)\n const pageSize = 50\n\n const filtered = React.useMemo(() => {\n if (!search.trim()) return entries\n const term = search.trim().toLowerCase()\n return entries.filter((entry) => {\n return (\n entry.value.toLowerCase().includes(term) ||\n (entry.label ?? '').toLowerCase().includes(term)\n )\n })\n }, [entries, search])\n\n const paginated = React.useMemo(() => {\n const start = (page - 1) * pageSize\n return filtered.slice(start, start + pageSize)\n }, [filtered, page])\n\n const totalPages = Math.max(1, Math.ceil(filtered.length / pageSize))\n\n const columns = React.useMemo<ColumnDef<DictionaryTableEntry>[]>(() => [\n {\n accessorKey: 'value',\n header: translations.valueColumn,\n meta: { priority: 1 },\n cell: ({ getValue, row }) => (\n <div className=\"flex items-center gap-2\">\n <span className=\"font-medium\">{String(getValue())}</span>\n {row.original.isInherited ? (\n <span className=\"rounded bg-muted px-2 py-0.5 text-xs text-muted-foreground\" title={translations.inheritedTooltip}>\n {translations.inheritedLabel}\n </span>\n ) : null}\n </div>\n ),\n },\n {\n accessorKey: 'label',\n header: translations.labelColumn,\n meta: { priority: 2 },\n cell: ({ getValue }) => <span>{String(getValue() ?? '')}</span>,\n },\n {\n id: 'appearance',\n header: translations.appearanceColumn,\n meta: { priority: 3 },\n cell: ({ row }) => {\n const { color, icon } = row.original\n if (!color && !icon) return <span className=\"text-muted-foreground\">\u2014</span>\n return (\n <div className=\"flex items-center gap-2\">\n {color ? renderDictionaryColor(color, 'h-4 w-4 rounded-full border border-border') : null}\n {icon ? renderDictionaryIcon(icon, 'h-4 w-4') : null}\n </div>\n )\n },\n },\n ], [translations.appearanceColumn, translations.inheritedLabel, translations.inheritedTooltip, translations.labelColumn, translations.valueColumn])\n\n const actions = React.useMemo(() => {\n if (!canManage || !onCreate) return null\n return (\n <Button size=\"sm\" onClick={onCreate}>\n {translations.addLabel}\n </Button>\n )\n }, [canManage, onCreate, translations.addLabel])\n\n const handleRowClick = canManage && onEdit\n ? (entry: DictionaryTableEntry) => {\n if (entry.isInherited) return\n onEdit(entry)\n }\n : undefined\n\n return (\n <DataTable<DictionaryTableEntry>\n title={translations.title}\n actions={actions}\n columns={columns}\n data={paginated}\n embedded\n sortable\n sorting={sorting}\n onSortingChange={setSorting}\n searchValue={search}\n onSearchChange={(value) => {\n setSearch(value)\n setPage(1)\n }}\n searchPlaceholder={translations.searchPlaceholder}\n isLoading={loading}\n emptyState={<p className=\"py-10 text-center text-sm text-muted-foreground\">{translations.emptyLabel}</p>}\n pagination={{\n page,\n pageSize,\n total: filtered.length,\n totalPages,\n onPageChange: setPage,\n }}\n refreshButton={onRefresh ? {\n label: translations.refreshLabel,\n onRefresh,\n isRefreshing: loading,\n } : undefined}\n onRowClick={handleRowClick}\n rowActions={\n canManage\n ? (entry) => {\n if (!entry) return null\n if (entry.isInherited) return null\n const items: RowActionItem[] = []\n if (onEdit) {\n items.push({\n label: translations.editLabel,\n onSelect: () => onEdit(entry),\n })\n }\n if (onDelete) {\n items.push({\n label: translations.deleteLabel,\n onSelect: () => onDelete(entry),\n destructive: true,\n })\n }\n return items.length ? <RowActions items={items} /> : null\n }\n : undefined\n }\n />\n )\n}\n"],
5
- "mappings": "AAqFQ,SACE,KADF;AArFR,YAAY,WAAW;AAEvB,SAAS,iBAAiB;AAC1B,SAAS,cAAc;AACvB,SAAS,kBAAsC;AAC/C,SAAS,uBAAuB,4BAA4B;AAyCrD,SAAS,gBAAgB;AAAA,EAC9B;AAAA,EACA,UAAU;AAAA,EACV,YAAY;AAAA,EACZ;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAAyB;AACvB,QAAM,CAAC,QAAQ,SAAS,IAAI,MAAM,SAAS,EAAE;AAC7C,QAAM,CAAC,SAAS,UAAU,IAAI,MAAM,SAAuB,CAAC,EAAE,IAAI,SAAS,MAAM,MAAM,CAAC,CAAC;AACzF,QAAM,CAAC,MAAM,OAAO,IAAI,MAAM,SAAS,CAAC;AACxC,QAAM,WAAW;AAEjB,QAAM,WAAW,MAAM,QAAQ,MAAM;AACnC,QAAI,CAAC,OAAO,KAAK,EAAG,QAAO;AAC3B,UAAM,OAAO,OAAO,KAAK,EAAE,YAAY;AACvC,WAAO,QAAQ,OAAO,CAAC,UAAU;AAC/B,aACE,MAAM,MAAM,YAAY,EAAE,SAAS,IAAI,MACtC,MAAM,SAAS,IAAI,YAAY,EAAE,SAAS,IAAI;AAAA,IAEnD,CAAC;AAAA,EACH,GAAG,CAAC,SAAS,MAAM,CAAC;AAEpB,QAAM,YAAY,MAAM,QAAQ,MAAM;AACpC,UAAM,SAAS,OAAO,KAAK;AAC3B,WAAO,SAAS,MAAM,OAAO,QAAQ,QAAQ;AAAA,EAC/C,GAAG,CAAC,UAAU,IAAI,CAAC;AAEnB,QAAM,aAAa,KAAK,IAAI,GAAG,KAAK,KAAK,SAAS,SAAS,QAAQ,CAAC;AAEpE,QAAM,UAAU,MAAM,QAA2C,MAAM;AAAA,IACrE;AAAA,MACE,aAAa;AAAA,MACb,QAAQ,aAAa;AAAA,MACrB,MAAM,EAAE,UAAU,EAAE;AAAA,MACpB,MAAM,CAAC,EAAE,UAAU,IAAI,MACrB,qBAAC,SAAI,WAAU,2BACb;AAAA,4BAAC,UAAK,WAAU,eAAe,iBAAO,SAAS,CAAC,GAAE;AAAA,QACjD,IAAI,SAAS,cACZ,oBAAC,UAAK,WAAU,8DAA6D,OAAO,aAAa,kBAC9F,uBAAa,gBAChB,IACE;AAAA,SACN;AAAA,IAEJ;AAAA,IACA;AAAA,MACE,aAAa;AAAA,MACb,QAAQ,aAAa;AAAA,MACrB,MAAM,EAAE,UAAU,EAAE;AAAA,MACpB,MAAM,CAAC,EAAE,SAAS,MAAM,oBAAC,UAAM,iBAAO,SAAS,KAAK,EAAE,GAAE;AAAA,IAC1D;AAAA,IACA;AAAA,MACE,IAAI;AAAA,MACJ,QAAQ,aAAa;AAAA,MACrB,MAAM,EAAE,UAAU,EAAE;AAAA,MACpB,MAAM,CAAC,EAAE,IAAI,MAAM;AACjB,cAAM,EAAE,OAAO,KAAK,IAAI,IAAI;AAC5B,YAAI,CAAC,SAAS,CAAC,KAAM,QAAO,oBAAC,UAAK,WAAU,yBAAwB,oBAAC;AACrE,eACE,qBAAC,SAAI,WAAU,2BACZ;AAAA,kBAAQ,sBAAsB,OAAO,2CAA2C,IAAI;AAAA,UACpF,OAAO,qBAAqB,MAAM,SAAS,IAAI;AAAA,WAClD;AAAA,MAEJ;AAAA,IACF;AAAA,EACF,GAAG,CAAC,aAAa,kBAAkB,aAAa,gBAAgB,aAAa,kBAAkB,aAAa,aAAa,aAAa,WAAW,CAAC;AAElJ,QAAM,UAAU,MAAM,QAAQ,MAAM;AAClC,QAAI,CAAC,aAAa,CAAC,SAAU,QAAO;AACpC,WACE,oBAAC,UAAO,MAAK,MAAK,SAAS,UACxB,uBAAa,UAChB;AAAA,EAEJ,GAAG,CAAC,WAAW,UAAU,aAAa,QAAQ,CAAC;AAE/C,QAAM,iBAAiB,aAAa,SAChC,CAAC,UAAgC;AAC/B,QAAI,MAAM,YAAa;AACvB,WAAO,KAAK;AAAA,EACd,IACA;AAEJ,SACE;AAAA,IAAC;AAAA;AAAA,MACC,OAAO,aAAa;AAAA,MACpB;AAAA,MACA;AAAA,MACA,MAAM;AAAA,MACN,UAAQ;AAAA,MACR,UAAQ;AAAA,MACR;AAAA,MACA,iBAAiB;AAAA,MACjB,aAAa;AAAA,MACb,gBAAgB,CAAC,UAAU;AACzB,kBAAU,KAAK;AACf,gBAAQ,CAAC;AAAA,MACX;AAAA,MACA,mBAAmB,aAAa;AAAA,MAChC,WAAW;AAAA,MACX,YAAY,oBAAC,OAAE,WAAU,mDAAmD,uBAAa,YAAW;AAAA,MACpG,YAAY;AAAA,QACV;AAAA,QACA;AAAA,QACA,OAAO,SAAS;AAAA,QAChB;AAAA,QACA,cAAc;AAAA,MAChB;AAAA,MACA,eAAe,YAAY;AAAA,QACzB,OAAO,aAAa;AAAA,QACpB;AAAA,QACA,cAAc;AAAA,MAChB,IAAI;AAAA,MACJ,YAAY;AAAA,MACZ,YACE,YACI,CAAC,UAAU;AACT,YAAI,CAAC,MAAO,QAAO;AACnB,YAAI,MAAM,YAAa,QAAO;AAC9B,cAAM,QAAyB,CAAC;AAChC,YAAI,QAAQ;AACV,gBAAM,KAAK;AAAA,YACT,OAAO,aAAa;AAAA,YACpB,UAAU,MAAM,OAAO,KAAK;AAAA,UAC9B,CAAC;AAAA,QACH;AACA,YAAI,UAAU;AACZ,gBAAM,KAAK;AAAA,YACT,OAAO,aAAa;AAAA,YACpB,UAAU,MAAM,SAAS,KAAK;AAAA,YAC9B,aAAa;AAAA,UACf,CAAC;AAAA,QACH;AACA,eAAO,MAAM,SAAS,oBAAC,cAAW,OAAc,IAAK;AAAA,MACvD,IACA;AAAA;AAAA,EAER;AAEJ;",
4
+ "sourcesContent": ["import * as React from 'react'\nimport type { ColumnDef, SortingState } from '@tanstack/react-table'\nimport { DataTable } from '@open-mercato/ui/backend/DataTable'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { RowActions, type RowActionItem } from '@open-mercato/ui/backend/RowActions'\nimport { renderDictionaryColor, renderDictionaryIcon } from './dictionaryAppearance'\n\nexport type DictionaryTableEntry = {\n id: string\n value: string\n label: string\n color: string | null\n icon: string | null\n organizationId?: string | null\n tenantId?: string | null\n isInherited?: boolean\n createdAt?: string | null\n updatedAt?: string | null\n}\n\nexport type DictionaryTableTranslations = {\n title: string\n valueColumn: string\n labelColumn: string\n appearanceColumn: string\n addLabel: string\n editLabel: string\n deleteLabel: string\n refreshLabel: string\n inheritedLabel: string\n inheritedTooltip: string\n emptyLabel: string\n searchPlaceholder?: string\n}\n\ntype DictionaryTableProps = {\n entries: DictionaryTableEntry[]\n loading?: boolean\n canManage?: boolean\n onCreate?: () => void\n onEdit?: (entry: DictionaryTableEntry) => void\n onDelete?: (entry: DictionaryTableEntry) => void\n onRefresh?: () => void\n translations: DictionaryTableTranslations\n}\n\nexport function DictionaryTable({\n entries,\n loading = false,\n canManage = false,\n onCreate,\n onEdit,\n onDelete,\n onRefresh,\n translations,\n}: DictionaryTableProps) {\n const [search, setSearch] = React.useState('')\n const [sorting, setSorting] = React.useState<SortingState>([{ id: 'value', desc: false }])\n const [page, setPage] = React.useState(1)\n const pageSize = 50\n\n const filtered = React.useMemo(() => {\n if (!search.trim()) return entries\n const term = search.trim().toLowerCase()\n return entries.filter((entry) => {\n return (\n entry.value.toLowerCase().includes(term) ||\n (entry.label ?? '').toLowerCase().includes(term)\n )\n })\n }, [entries, search])\n\n const paginated = React.useMemo(() => {\n const start = (page - 1) * pageSize\n return filtered.slice(start, start + pageSize)\n }, [filtered, page])\n\n const totalPages = Math.max(1, Math.ceil(filtered.length / pageSize))\n\n const columns = React.useMemo<ColumnDef<DictionaryTableEntry>[]>(() => [\n {\n accessorKey: 'value',\n header: translations.valueColumn,\n meta: { priority: 1 },\n cell: ({ getValue, row }) => (\n <div className=\"flex items-center gap-2\">\n <span className=\"font-medium\">{String(getValue())}</span>\n {row.original.isInherited ? (\n <span className=\"rounded bg-muted px-2 py-0.5 text-xs text-muted-foreground\" title={translations.inheritedTooltip}>\n {translations.inheritedLabel}\n </span>\n ) : null}\n </div>\n ),\n },\n {\n accessorKey: 'label',\n header: translations.labelColumn,\n meta: { priority: 2 },\n cell: ({ getValue }) => <span>{String(getValue() ?? '')}</span>,\n },\n {\n id: 'appearance',\n header: translations.appearanceColumn,\n meta: { priority: 3 },\n cell: ({ row }) => {\n const { color, icon } = row.original\n if (!color && !icon) return <span className=\"text-muted-foreground\">\u2014</span>\n return (\n <div className=\"flex items-center gap-2\">\n {color ? renderDictionaryColor(color, 'h-4 w-4 rounded-full border border-border') : null}\n {icon ? renderDictionaryIcon(icon, 'h-4 w-4') : null}\n </div>\n )\n },\n },\n ], [translations.appearanceColumn, translations.inheritedLabel, translations.inheritedTooltip, translations.labelColumn, translations.valueColumn])\n\n const actions = React.useMemo(() => {\n if (!canManage || !onCreate) return null\n return (\n <Button size=\"sm\" onClick={onCreate}>\n {translations.addLabel}\n </Button>\n )\n }, [canManage, onCreate, translations.addLabel])\n\n const handleRowClick = canManage && onEdit\n ? (entry: DictionaryTableEntry) => {\n if (entry.isInherited) return\n onEdit(entry)\n }\n : undefined\n\n return (\n <DataTable<DictionaryTableEntry>\n title={translations.title}\n actions={actions}\n columns={columns}\n data={paginated}\n embedded\n sortable\n sorting={sorting}\n onSortingChange={setSorting}\n searchValue={search}\n onSearchChange={(value) => {\n setSearch(value)\n setPage(1)\n }}\n searchPlaceholder={translations.searchPlaceholder}\n isLoading={loading}\n emptyState={<p className=\"py-10 text-center text-sm text-muted-foreground\">{translations.emptyLabel}</p>}\n pagination={{\n page,\n pageSize,\n total: filtered.length,\n totalPages,\n onPageChange: setPage,\n }}\n refreshButton={onRefresh ? {\n label: translations.refreshLabel,\n onRefresh,\n isRefreshing: loading,\n } : undefined}\n onRowClick={handleRowClick}\n rowActions={\n canManage\n ? (entry) => {\n if (!entry) return null\n if (entry.isInherited) return null\n const items: RowActionItem[] = []\n if (onEdit) {\n items.push({\n id: 'edit',\n label: translations.editLabel,\n onSelect: () => onEdit(entry),\n })\n }\n if (onDelete) {\n items.push({\n id: 'delete',\n label: translations.deleteLabel,\n onSelect: () => onDelete(entry),\n destructive: true,\n })\n }\n return items.length ? <RowActions items={items} /> : null\n }\n : undefined\n }\n />\n )\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -0,0 +1,68 @@
1
+ import { NextResponse } from "next/server";
2
+ import { z } from "zod";
3
+ import { createRequestContainer } from "@open-mercato/shared/lib/di/container";
4
+ import { Tenant } from "@open-mercato/core/modules/directory/data/entities";
5
+ const metadata = {
6
+ GET: {
7
+ requireAuth: false
8
+ }
9
+ };
10
+ const tenantLookupQuerySchema = z.object({
11
+ tenantId: z.string().uuid()
12
+ });
13
+ async function GET(req) {
14
+ const url = new URL(req.url);
15
+ const tenantId = url.searchParams.get("tenantId") || url.searchParams.get("tenant") || "";
16
+ const parsed = tenantLookupQuerySchema.safeParse({ tenantId });
17
+ if (!parsed.success) {
18
+ return NextResponse.json({ ok: false, error: "Invalid tenant id." }, { status: 400 });
19
+ }
20
+ const container = await createRequestContainer();
21
+ const em = container.resolve("em");
22
+ const tenant = await em.findOne(Tenant, { id: parsed.data.tenantId, deletedAt: null });
23
+ if (!tenant) {
24
+ return NextResponse.json({ ok: false, error: "Tenant not found." }, { status: 404 });
25
+ }
26
+ return NextResponse.json({
27
+ ok: true,
28
+ tenant: { id: String(tenant.id), name: tenant.name }
29
+ });
30
+ }
31
+ const lookupTag = "Directory";
32
+ const tenantLookupSuccessSchema = z.object({
33
+ ok: z.literal(true),
34
+ tenant: z.object({
35
+ id: z.string().uuid(),
36
+ name: z.string()
37
+ })
38
+ });
39
+ const tenantLookupErrorSchema = z.object({
40
+ ok: z.literal(false),
41
+ error: z.string()
42
+ });
43
+ const tenantLookupDoc = {
44
+ summary: "Public tenant lookup",
45
+ description: "Resolves tenant metadata for login/activation flows.",
46
+ tags: [lookupTag],
47
+ query: tenantLookupQuerySchema,
48
+ responses: [
49
+ { status: 200, description: "Tenant resolved.", schema: tenantLookupSuccessSchema }
50
+ ],
51
+ errors: [
52
+ { status: 400, description: "Invalid tenant id", schema: tenantLookupErrorSchema },
53
+ { status: 404, description: "Tenant not found", schema: tenantLookupErrorSchema }
54
+ ]
55
+ };
56
+ const openApi = {
57
+ tag: lookupTag,
58
+ summary: "Public tenant lookup",
59
+ methods: {
60
+ GET: tenantLookupDoc
61
+ }
62
+ };
63
+ export {
64
+ GET,
65
+ metadata,
66
+ openApi
67
+ };
68
+ //# sourceMappingURL=lookup.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../../src/modules/directory/api/get/tenants/lookup.ts"],
4
+ "sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { Tenant } from '@open-mercato/core/modules/directory/data/entities'\nimport type { OpenApiMethodDoc, OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\n\nexport const metadata = {\n GET: {\n requireAuth: false,\n },\n}\n\nconst tenantLookupQuerySchema = z.object({\n tenantId: z.string().uuid(),\n})\n\nexport async function GET(req: Request) {\n const url = new URL(req.url)\n const tenantId = url.searchParams.get('tenantId') || url.searchParams.get('tenant') || ''\n const parsed = tenantLookupQuerySchema.safeParse({ tenantId })\n if (!parsed.success) {\n return NextResponse.json({ ok: false, error: 'Invalid tenant id.' }, { status: 400 })\n }\n\n const container = await createRequestContainer()\n const em = (container.resolve('em') as EntityManager)\n const tenant = await em.findOne(Tenant, { id: parsed.data.tenantId, deletedAt: null })\n if (!tenant) {\n return NextResponse.json({ ok: false, error: 'Tenant not found.' }, { status: 404 })\n }\n return NextResponse.json({\n ok: true,\n tenant: { id: String(tenant.id), name: tenant.name },\n })\n}\n\nconst lookupTag = 'Directory'\n\nconst tenantLookupSuccessSchema = z.object({\n ok: z.literal(true),\n tenant: z.object({\n id: z.string().uuid(),\n name: z.string(),\n }),\n})\n\nconst tenantLookupErrorSchema = z.object({\n ok: z.literal(false),\n error: z.string(),\n})\n\nconst tenantLookupDoc: OpenApiMethodDoc = {\n summary: 'Public tenant lookup',\n description: 'Resolves tenant metadata for login/activation flows.',\n tags: [lookupTag],\n query: tenantLookupQuerySchema,\n responses: [\n { status: 200, description: 'Tenant resolved.', schema: tenantLookupSuccessSchema },\n ],\n errors: [\n { status: 400, description: 'Invalid tenant id', schema: tenantLookupErrorSchema },\n { status: 404, description: 'Tenant not found', schema: tenantLookupErrorSchema },\n ],\n}\n\nexport const openApi: OpenApiRouteDoc = {\n tag: lookupTag,\n summary: 'Public tenant lookup',\n methods: {\n GET: tenantLookupDoc,\n },\n}\n"],
5
+ "mappings": "AAAA,SAAS,oBAAoB;AAC7B,SAAS,SAAS;AAElB,SAAS,8BAA8B;AACvC,SAAS,cAAc;AAGhB,MAAM,WAAW;AAAA,EACtB,KAAK;AAAA,IACH,aAAa;AAAA,EACf;AACF;AAEA,MAAM,0BAA0B,EAAE,OAAO;AAAA,EACvC,UAAU,EAAE,OAAO,EAAE,KAAK;AAC5B,CAAC;AAED,eAAsB,IAAI,KAAc;AACtC,QAAM,MAAM,IAAI,IAAI,IAAI,GAAG;AAC3B,QAAM,WAAW,IAAI,aAAa,IAAI,UAAU,KAAK,IAAI,aAAa,IAAI,QAAQ,KAAK;AACvF,QAAM,SAAS,wBAAwB,UAAU,EAAE,SAAS,CAAC;AAC7D,MAAI,CAAC,OAAO,SAAS;AACnB,WAAO,aAAa,KAAK,EAAE,IAAI,OAAO,OAAO,qBAAqB,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,EACtF;AAEA,QAAM,YAAY,MAAM,uBAAuB;AAC/C,QAAM,KAAM,UAAU,QAAQ,IAAI;AAClC,QAAM,SAAS,MAAM,GAAG,QAAQ,QAAQ,EAAE,IAAI,OAAO,KAAK,UAAU,WAAW,KAAK,CAAC;AACrF,MAAI,CAAC,QAAQ;AACX,WAAO,aAAa,KAAK,EAAE,IAAI,OAAO,OAAO,oBAAoB,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,EACrF;AACA,SAAO,aAAa,KAAK;AAAA,IACvB,IAAI;AAAA,IACJ,QAAQ,EAAE,IAAI,OAAO,OAAO,EAAE,GAAG,MAAM,OAAO,KAAK;AAAA,EACrD,CAAC;AACH;AAEA,MAAM,YAAY;AAElB,MAAM,4BAA4B,EAAE,OAAO;AAAA,EACzC,IAAI,EAAE,QAAQ,IAAI;AAAA,EAClB,QAAQ,EAAE,OAAO;AAAA,IACf,IAAI,EAAE,OAAO,EAAE,KAAK;AAAA,IACpB,MAAM,EAAE,OAAO;AAAA,EACjB,CAAC;AACH,CAAC;AAED,MAAM,0BAA0B,EAAE,OAAO;AAAA,EACvC,IAAI,EAAE,QAAQ,KAAK;AAAA,EACnB,OAAO,EAAE,OAAO;AAClB,CAAC;AAED,MAAM,kBAAoC;AAAA,EACxC,SAAS;AAAA,EACT,aAAa;AAAA,EACb,MAAM,CAAC,SAAS;AAAA,EAChB,OAAO;AAAA,EACP,WAAW;AAAA,IACT,EAAE,QAAQ,KAAK,aAAa,oBAAoB,QAAQ,0BAA0B;AAAA,EACpF;AAAA,EACA,QAAQ;AAAA,IACN,EAAE,QAAQ,KAAK,aAAa,qBAAqB,QAAQ,wBAAwB;AAAA,IACjF,EAAE,QAAQ,KAAK,aAAa,oBAAoB,QAAQ,wBAAwB;AAAA,EAClF;AACF;AAEO,MAAM,UAA2B;AAAA,EACtC,KAAK;AAAA,EACL,SAAS;AAAA,EACT,SAAS;AAAA,IACP,KAAK;AAAA,EACP;AACF;",
6
+ "names": []
7
+ }