@open-mercato/core 0.4.2-canary-e6bf6a353e → 0.4.2-canary-49d47ff90e

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 (397) hide show
  1. package/dist/generated/entities/notification/index.js +57 -0
  2. package/dist/generated/entities/notification/index.js.map +7 -0
  3. package/dist/generated/entities.ids.generated.js +5 -1
  4. package/dist/generated/entities.ids.generated.js.map +2 -2
  5. package/dist/generated/entity-fields-registry.js +2 -0
  6. package/dist/generated/entity-fields-registry.js.map +2 -2
  7. package/dist/modules/api_docs/frontend/docs/api/page.js +3 -2
  8. package/dist/modules/api_docs/frontend/docs/api/page.js.map +2 -2
  9. package/dist/modules/api_keys/backend/api-keys/page.js +1 -1
  10. package/dist/modules/api_keys/backend/api-keys/page.js.map +2 -2
  11. package/dist/modules/attachments/components/AttachmentLibrary.js +4 -0
  12. package/dist/modules/attachments/components/AttachmentLibrary.js.map +2 -2
  13. package/dist/modules/attachments/components/AttachmentPartitionSettings.js +2 -0
  14. package/dist/modules/attachments/components/AttachmentPartitionSettings.js.map +2 -2
  15. package/dist/modules/auth/api/admin/nav.js +4 -3
  16. package/dist/modules/auth/api/admin/nav.js.map +2 -2
  17. package/dist/modules/auth/api/profile/route.js +157 -0
  18. package/dist/modules/auth/api/profile/route.js.map +7 -0
  19. package/dist/modules/auth/api/reset/confirm.js +25 -2
  20. package/dist/modules/auth/api/reset/confirm.js.map +2 -2
  21. package/dist/modules/auth/api/reset.js +23 -0
  22. package/dist/modules/auth/api/reset.js.map +2 -2
  23. package/dist/modules/auth/api/sidebar/preferences/route.js +14 -9
  24. package/dist/modules/auth/api/sidebar/preferences/route.js.map +2 -2
  25. package/dist/modules/auth/api/users/route.js +4 -2
  26. package/dist/modules/auth/api/users/route.js.map +2 -2
  27. package/dist/modules/auth/backend/auth/profile/page.js +141 -0
  28. package/dist/modules/auth/backend/auth/profile/page.js.map +7 -0
  29. package/dist/modules/auth/backend/auth/profile/page.meta.js +13 -0
  30. package/dist/modules/auth/backend/auth/profile/page.meta.js.map +7 -0
  31. package/dist/modules/auth/backend/roles/page.js +3 -3
  32. package/dist/modules/auth/backend/roles/page.js.map +2 -2
  33. package/dist/modules/auth/backend/users/[id]/edit/page.js +14 -2
  34. package/dist/modules/auth/backend/users/[id]/edit/page.js.map +2 -2
  35. package/dist/modules/auth/backend/users/create/page.js +15 -2
  36. package/dist/modules/auth/backend/users/create/page.js.map +2 -2
  37. package/dist/modules/auth/backend/users/page.js +3 -3
  38. package/dist/modules/auth/backend/users/page.js.map +2 -2
  39. package/dist/modules/auth/cli.js +13 -0
  40. package/dist/modules/auth/cli.js.map +2 -2
  41. package/dist/modules/auth/commands/users.js +59 -2
  42. package/dist/modules/auth/commands/users.js.map +2 -2
  43. package/dist/modules/auth/data/validators.js +4 -2
  44. package/dist/modules/auth/data/validators.js.map +2 -2
  45. package/dist/modules/auth/frontend/reset/[token]/page.js +20 -10
  46. package/dist/modules/auth/frontend/reset/[token]/page.js.map +2 -2
  47. package/dist/modules/auth/lib/setup-app.js +1 -0
  48. package/dist/modules/auth/lib/setup-app.js.map +2 -2
  49. package/dist/modules/auth/notifications.js +112 -0
  50. package/dist/modules/auth/notifications.js.map +7 -0
  51. package/dist/modules/auth/services/authService.js +3 -3
  52. package/dist/modules/auth/services/authService.js.map +2 -2
  53. package/dist/modules/business_rules/backend/rules/page.js +4 -0
  54. package/dist/modules/business_rules/backend/rules/page.js.map +2 -2
  55. package/dist/modules/business_rules/backend/sets/page.js +3 -0
  56. package/dist/modules/business_rules/backend/sets/page.js.map +2 -2
  57. package/dist/modules/business_rules/notifications.js +28 -0
  58. package/dist/modules/business_rules/notifications.js.map +7 -0
  59. package/dist/modules/business_rules/subscribers/rule-execution-failed-notification.js +37 -0
  60. package/dist/modules/business_rules/subscribers/rule-execution-failed-notification.js.map +7 -0
  61. package/dist/modules/catalog/components/PriceKindSettings.js +2 -0
  62. package/dist/modules/catalog/components/PriceKindSettings.js.map +2 -2
  63. package/dist/modules/catalog/components/categories/CategoriesDataTable.js +2 -2
  64. package/dist/modules/catalog/components/categories/CategoriesDataTable.js.map +2 -2
  65. package/dist/modules/catalog/components/products/ProductsDataTable.js +2 -0
  66. package/dist/modules/catalog/components/products/ProductsDataTable.js.map +2 -2
  67. package/dist/modules/catalog/notifications.js +28 -0
  68. package/dist/modules/catalog/notifications.js.map +7 -0
  69. package/dist/modules/catalog/subscribers/low-stock-notification.js +38 -0
  70. package/dist/modules/catalog/subscribers/low-stock-notification.js.map +7 -0
  71. package/dist/modules/configs/cli.js +6 -0
  72. package/dist/modules/configs/cli.js.map +2 -2
  73. package/dist/modules/configs/lib/upgrade-actions.js +18 -0
  74. package/dist/modules/configs/lib/upgrade-actions.js.map +2 -2
  75. package/dist/modules/currencies/backend/currencies/page.js +3 -0
  76. package/dist/modules/currencies/backend/currencies/page.js.map +2 -2
  77. package/dist/modules/currencies/backend/exchange-rates/page.js +2 -0
  78. package/dist/modules/currencies/backend/exchange-rates/page.js.map +2 -2
  79. package/dist/modules/customers/backend/customers/companies/page.js +3 -0
  80. package/dist/modules/customers/backend/customers/companies/page.js.map +2 -2
  81. package/dist/modules/customers/backend/customers/deals/page.js +3 -0
  82. package/dist/modules/customers/backend/customers/deals/page.js.map +2 -2
  83. package/dist/modules/customers/backend/customers/people/page.js +3 -0
  84. package/dist/modules/customers/backend/customers/people/page.js.map +2 -2
  85. package/dist/modules/customers/commands/deals.js +31 -0
  86. package/dist/modules/customers/commands/deals.js.map +2 -2
  87. package/dist/modules/customers/components/CustomerTodosTable.js +1 -0
  88. package/dist/modules/customers/components/CustomerTodosTable.js.map +2 -2
  89. package/dist/modules/customers/notifications.js +48 -0
  90. package/dist/modules/customers/notifications.js.map +7 -0
  91. package/dist/modules/dashboards/cli.js +32 -1
  92. package/dist/modules/dashboards/cli.js.map +2 -2
  93. package/dist/modules/dashboards/lib/role-widgets.js +58 -0
  94. package/dist/modules/dashboards/lib/role-widgets.js.map +7 -0
  95. package/dist/modules/dictionaries/components/DictionaryTable.js +2 -0
  96. package/dist/modules/dictionaries/components/DictionaryTable.js.map +2 -2
  97. package/dist/modules/directory/backend/directory/organizations/page.js +2 -2
  98. package/dist/modules/directory/backend/directory/organizations/page.js.map +2 -2
  99. package/dist/modules/directory/backend/directory/tenants/page.js +2 -2
  100. package/dist/modules/directory/backend/directory/tenants/page.js.map +2 -2
  101. package/dist/modules/entities/backend/entities/user/[entityId]/records/page.js +2 -2
  102. package/dist/modules/entities/backend/entities/user/[entityId]/records/page.js.map +2 -2
  103. package/dist/modules/entities/components/SystemEntitiesTable.js +1 -1
  104. package/dist/modules/entities/components/SystemEntitiesTable.js.map +2 -2
  105. package/dist/modules/entities/components/UserEntitiesTable.js +2 -2
  106. package/dist/modules/entities/components/UserEntitiesTable.js.map +2 -2
  107. package/dist/modules/feature_toggles/components/FeatureTogglesTable.js +3 -3
  108. package/dist/modules/feature_toggles/components/FeatureTogglesTable.js.map +2 -2
  109. package/dist/modules/feature_toggles/components/OverridesTable.js +1 -1
  110. package/dist/modules/feature_toggles/components/OverridesTable.js.map +2 -2
  111. package/dist/modules/notifications/acl.js +11 -0
  112. package/dist/modules/notifications/acl.js.map +7 -0
  113. package/dist/modules/notifications/api/[id]/action/route.js +74 -0
  114. package/dist/modules/notifications/api/[id]/action/route.js.map +7 -0
  115. package/dist/modules/notifications/api/[id]/dismiss/route.js +15 -0
  116. package/dist/modules/notifications/api/[id]/dismiss/route.js.map +7 -0
  117. package/dist/modules/notifications/api/[id]/read/route.js +15 -0
  118. package/dist/modules/notifications/api/[id]/read/route.js.map +7 -0
  119. package/dist/modules/notifications/api/[id]/restore/route.js +53 -0
  120. package/dist/modules/notifications/api/[id]/restore/route.js.map +7 -0
  121. package/dist/modules/notifications/api/batch/route.js +17 -0
  122. package/dist/modules/notifications/api/batch/route.js.map +7 -0
  123. package/dist/modules/notifications/api/feature/route.js +17 -0
  124. package/dist/modules/notifications/api/feature/route.js.map +7 -0
  125. package/dist/modules/notifications/api/mark-all-read/route.js +35 -0
  126. package/dist/modules/notifications/api/mark-all-read/route.js.map +7 -0
  127. package/dist/modules/notifications/api/openapi.js +76 -0
  128. package/dist/modules/notifications/api/openapi.js.map +7 -0
  129. package/dist/modules/notifications/api/role/route.js +17 -0
  130. package/dist/modules/notifications/api/role/route.js.map +7 -0
  131. package/dist/modules/notifications/api/route.js +85 -0
  132. package/dist/modules/notifications/api/route.js.map +7 -0
  133. package/dist/modules/notifications/api/settings/route.js +155 -0
  134. package/dist/modules/notifications/api/settings/route.js.map +7 -0
  135. package/dist/modules/notifications/api/unread-count/route.js +38 -0
  136. package/dist/modules/notifications/api/unread-count/route.js.map +7 -0
  137. package/dist/modules/notifications/backend/config/notifications/page.js +10 -0
  138. package/dist/modules/notifications/backend/config/notifications/page.js.map +7 -0
  139. package/dist/modules/notifications/backend/config/notifications/page.meta.js +24 -0
  140. package/dist/modules/notifications/backend/config/notifications/page.meta.js.map +7 -0
  141. package/dist/modules/notifications/cli.js +16 -0
  142. package/dist/modules/notifications/cli.js.map +7 -0
  143. package/dist/modules/notifications/data/entities.js +112 -0
  144. package/dist/modules/notifications/data/entities.js.map +7 -0
  145. package/dist/modules/notifications/data/validators.js +94 -0
  146. package/dist/modules/notifications/data/validators.js.map +7 -0
  147. package/dist/modules/notifications/di.js +13 -0
  148. package/dist/modules/notifications/di.js.map +7 -0
  149. package/dist/modules/notifications/emails/NotificationEmail.js +58 -0
  150. package/dist/modules/notifications/emails/NotificationEmail.js.map +7 -0
  151. package/dist/modules/notifications/frontend/NotificationInboxPageClient.js +44 -0
  152. package/dist/modules/notifications/frontend/NotificationInboxPageClient.js.map +7 -0
  153. package/dist/modules/notifications/frontend/NotificationSettingsPageClient.js +219 -0
  154. package/dist/modules/notifications/frontend/NotificationSettingsPageClient.js.map +7 -0
  155. package/dist/modules/notifications/index.js +14 -0
  156. package/dist/modules/notifications/index.js.map +7 -0
  157. package/dist/modules/notifications/lib/deliveryConfig.js +105 -0
  158. package/dist/modules/notifications/lib/deliveryConfig.js.map +7 -0
  159. package/dist/modules/notifications/lib/events.js +12 -0
  160. package/dist/modules/notifications/lib/events.js.map +7 -0
  161. package/dist/modules/notifications/lib/notificationBuilder.js +66 -0
  162. package/dist/modules/notifications/lib/notificationBuilder.js.map +7 -0
  163. package/dist/modules/notifications/lib/notificationFactory.js +54 -0
  164. package/dist/modules/notifications/lib/notificationFactory.js.map +7 -0
  165. package/dist/modules/notifications/lib/notificationMapper.js +34 -0
  166. package/dist/modules/notifications/lib/notificationMapper.js.map +7 -0
  167. package/dist/modules/notifications/lib/notificationRecipients.js +35 -0
  168. package/dist/modules/notifications/lib/notificationRecipients.js.map +7 -0
  169. package/dist/modules/notifications/lib/notificationService.js +279 -0
  170. package/dist/modules/notifications/lib/notificationService.js.map +7 -0
  171. package/dist/modules/notifications/lib/routeHelpers.js +101 -0
  172. package/dist/modules/notifications/lib/routeHelpers.js.map +7 -0
  173. package/dist/modules/notifications/lib/safeHref.js +24 -0
  174. package/dist/modules/notifications/lib/safeHref.js.map +7 -0
  175. package/dist/modules/notifications/migrations/Migration20260123000001.js +70 -0
  176. package/dist/modules/notifications/migrations/Migration20260123000001.js.map +7 -0
  177. package/dist/modules/notifications/migrations/Migration20260126150000.js +37 -0
  178. package/dist/modules/notifications/migrations/Migration20260126150000.js.map +7 -0
  179. package/dist/modules/notifications/subscribers/deliver-notification.js +139 -0
  180. package/dist/modules/notifications/subscribers/deliver-notification.js.map +7 -0
  181. package/dist/modules/notifications/workers/create-notification.worker.js +70 -0
  182. package/dist/modules/notifications/workers/create-notification.worker.js.map +7 -0
  183. package/dist/modules/planner/backend/planner/availability-rulesets/page.js +2 -2
  184. package/dist/modules/planner/backend/planner/availability-rulesets/page.js.map +2 -2
  185. package/dist/modules/query_index/components/QueryIndexesTable.js +7 -1
  186. package/dist/modules/query_index/components/QueryIndexesTable.js.map +2 -2
  187. package/dist/modules/resources/backend/resources/resource-types/page.js +2 -2
  188. package/dist/modules/resources/backend/resources/resource-types/page.js.map +2 -2
  189. package/dist/modules/resources/backend/resources/resources/page.js +2 -2
  190. package/dist/modules/resources/backend/resources/resources/page.js.map +2 -2
  191. package/dist/modules/sales/backend/sales/channels/offers/page.js +2 -0
  192. package/dist/modules/sales/backend/sales/channels/offers/page.js.map +2 -2
  193. package/dist/modules/sales/backend/sales/channels/page.js +2 -0
  194. package/dist/modules/sales/backend/sales/channels/page.js.map +2 -2
  195. package/dist/modules/sales/commands/documents.js +53 -0
  196. package/dist/modules/sales/commands/documents.js.map +2 -2
  197. package/dist/modules/sales/commands/payments.js +26 -0
  198. package/dist/modules/sales/commands/payments.js.map +2 -2
  199. package/dist/modules/sales/components/AdjustmentKindSettings.js +2 -2
  200. package/dist/modules/sales/components/AdjustmentKindSettings.js.map +2 -2
  201. package/dist/modules/sales/components/PaymentMethodsSettings.js +2 -2
  202. package/dist/modules/sales/components/PaymentMethodsSettings.js.map +2 -2
  203. package/dist/modules/sales/components/ShippingMethodsSettings.js +2 -2
  204. package/dist/modules/sales/components/ShippingMethodsSettings.js.map +2 -2
  205. package/dist/modules/sales/components/TaxRatesSettings.js +2 -2
  206. package/dist/modules/sales/components/TaxRatesSettings.js.map +2 -2
  207. package/dist/modules/sales/components/channels/SalesChannelOffersPanel.js +2 -0
  208. package/dist/modules/sales/components/channels/SalesChannelOffersPanel.js.map +2 -2
  209. package/dist/modules/sales/components/documents/AdjustmentsSection.js +2 -0
  210. package/dist/modules/sales/components/documents/AdjustmentsSection.js.map +2 -2
  211. package/dist/modules/sales/components/documents/PaymentsSection.js +2 -1
  212. package/dist/modules/sales/components/documents/PaymentsSection.js.map +2 -2
  213. package/dist/modules/sales/components/documents/SalesDocumentsTable.js +2 -0
  214. package/dist/modules/sales/components/documents/SalesDocumentsTable.js.map +2 -2
  215. package/dist/modules/sales/notifications.client.js +51 -0
  216. package/dist/modules/sales/notifications.client.js.map +7 -0
  217. package/dist/modules/sales/notifications.js +88 -0
  218. package/dist/modules/sales/notifications.js.map +7 -0
  219. package/dist/modules/sales/subscribers/quote-expiring-notification.js +38 -0
  220. package/dist/modules/sales/subscribers/quote-expiring-notification.js.map +7 -0
  221. package/dist/modules/sales/widgets/notifications/SalesOrderCreatedRenderer.js +137 -0
  222. package/dist/modules/sales/widgets/notifications/SalesOrderCreatedRenderer.js.map +7 -0
  223. package/dist/modules/sales/widgets/notifications/SalesQuoteCreatedRenderer.js +137 -0
  224. package/dist/modules/sales/widgets/notifications/SalesQuoteCreatedRenderer.js.map +7 -0
  225. package/dist/modules/sales/widgets/notifications/index.js +7 -0
  226. package/dist/modules/sales/widgets/notifications/index.js.map +7 -0
  227. package/dist/modules/sales/widgets/notifications/useSalesDocumentTotals.js +60 -0
  228. package/dist/modules/sales/widgets/notifications/useSalesDocumentTotals.js.map +7 -0
  229. package/dist/modules/staff/backend/staff/team-members/page.js +1 -1
  230. package/dist/modules/staff/backend/staff/team-members/page.js.map +2 -2
  231. package/dist/modules/staff/backend/staff/team-roles/page.js +2 -2
  232. package/dist/modules/staff/backend/staff/team-roles/page.js.map +2 -2
  233. package/dist/modules/staff/backend/staff/teams/[id]/edit/page.js +2 -2
  234. package/dist/modules/staff/backend/staff/teams/[id]/edit/page.js.map +2 -2
  235. package/dist/modules/staff/backend/staff/teams/page.js +2 -2
  236. package/dist/modules/staff/backend/staff/teams/page.js.map +2 -2
  237. package/dist/modules/staff/commands/leave-requests.js +79 -0
  238. package/dist/modules/staff/commands/leave-requests.js.map +2 -2
  239. package/dist/modules/staff/notifications.js +75 -0
  240. package/dist/modules/staff/notifications.js.map +7 -0
  241. package/dist/modules/workflows/backend/definitions/page.js +5 -0
  242. package/dist/modules/workflows/backend/definitions/page.js.map +2 -2
  243. package/dist/modules/workflows/backend/instances/page.js +3 -0
  244. package/dist/modules/workflows/backend/instances/page.js.map +2 -2
  245. package/dist/modules/workflows/backend/tasks/page.js +3 -0
  246. package/dist/modules/workflows/backend/tasks/page.js.map +2 -2
  247. package/dist/modules/workflows/notifications.js +28 -0
  248. package/dist/modules/workflows/notifications.js.map +7 -0
  249. package/dist/modules/workflows/subscribers/task-assigned-notification.js +38 -0
  250. package/dist/modules/workflows/subscribers/task-assigned-notification.js.map +7 -0
  251. package/generated/entities/notification/index.ts +27 -0
  252. package/generated/entities.ids.generated.ts +5 -1
  253. package/generated/entity-fields-registry.ts +2 -0
  254. package/package.json +2 -2
  255. package/src/modules/api_docs/frontend/docs/api/page.tsx +3 -2
  256. package/src/modules/api_keys/backend/api-keys/page.tsx +1 -1
  257. package/src/modules/attachments/components/AttachmentLibrary.tsx +4 -0
  258. package/src/modules/attachments/components/AttachmentPartitionSettings.tsx +2 -0
  259. package/src/modules/auth/api/admin/nav.ts +10 -6
  260. package/src/modules/auth/api/profile/route.ts +163 -0
  261. package/src/modules/auth/api/reset/confirm.ts +25 -2
  262. package/src/modules/auth/api/reset.ts +23 -0
  263. package/src/modules/auth/api/sidebar/preferences/route.ts +21 -12
  264. package/src/modules/auth/api/users/route.ts +5 -2
  265. package/src/modules/auth/backend/auth/profile/page.meta.ts +9 -0
  266. package/src/modules/auth/backend/auth/profile/page.tsx +174 -0
  267. package/src/modules/auth/backend/roles/page.tsx +3 -3
  268. package/src/modules/auth/backend/users/[id]/edit/page.tsx +18 -2
  269. package/src/modules/auth/backend/users/create/page.tsx +19 -2
  270. package/src/modules/auth/backend/users/page.tsx +3 -3
  271. package/src/modules/auth/cli.ts +14 -0
  272. package/src/modules/auth/commands/users.ts +73 -2
  273. package/src/modules/auth/data/validators.ts +5 -2
  274. package/src/modules/auth/frontend/reset/[token]/page.tsx +24 -11
  275. package/src/modules/auth/i18n/de.json +43 -1
  276. package/src/modules/auth/i18n/en.json +43 -1
  277. package/src/modules/auth/i18n/es.json +43 -1
  278. package/src/modules/auth/i18n/pl.json +43 -1
  279. package/src/modules/auth/lib/setup-app.ts +1 -0
  280. package/src/modules/auth/notifications.ts +109 -0
  281. package/src/modules/auth/services/authService.ts +4 -4
  282. package/src/modules/business_rules/backend/rules/page.tsx +4 -0
  283. package/src/modules/business_rules/backend/sets/page.tsx +3 -0
  284. package/src/modules/business_rules/i18n/en.json +3 -1
  285. package/src/modules/business_rules/notifications.ts +25 -0
  286. package/src/modules/business_rules/subscribers/rule-execution-failed-notification.ts +50 -0
  287. package/src/modules/catalog/components/PriceKindSettings.tsx +2 -0
  288. package/src/modules/catalog/components/categories/CategoriesDataTable.tsx +2 -2
  289. package/src/modules/catalog/components/products/ProductsDataTable.tsx +2 -0
  290. package/src/modules/catalog/i18n/en.json +3 -1
  291. package/src/modules/catalog/notifications.ts +25 -0
  292. package/src/modules/catalog/subscribers/low-stock-notification.ts +52 -0
  293. package/src/modules/configs/cli.ts +6 -0
  294. package/src/modules/configs/lib/upgrade-actions.ts +18 -0
  295. package/src/modules/currencies/backend/currencies/page.tsx +3 -0
  296. package/src/modules/currencies/backend/exchange-rates/page.tsx +2 -0
  297. package/src/modules/customers/backend/customers/companies/page.tsx +3 -0
  298. package/src/modules/customers/backend/customers/deals/page.tsx +3 -0
  299. package/src/modules/customers/backend/customers/people/page.tsx +3 -0
  300. package/src/modules/customers/commands/deals.ts +39 -0
  301. package/src/modules/customers/components/CustomerTodosTable.tsx +1 -0
  302. package/src/modules/customers/i18n/en.json +5 -1
  303. package/src/modules/customers/notifications.ts +44 -0
  304. package/src/modules/dashboards/cli.ts +41 -1
  305. package/src/modules/dashboards/lib/role-widgets.ts +80 -0
  306. package/src/modules/dictionaries/components/DictionaryTable.tsx +2 -0
  307. package/src/modules/directory/backend/directory/organizations/page.tsx +2 -2
  308. package/src/modules/directory/backend/directory/tenants/page.tsx +2 -2
  309. package/src/modules/entities/backend/entities/user/[entityId]/records/page.tsx +2 -2
  310. package/src/modules/entities/components/SystemEntitiesTable.tsx +1 -1
  311. package/src/modules/entities/components/UserEntitiesTable.tsx +2 -2
  312. package/src/modules/feature_toggles/components/FeatureTogglesTable.tsx +3 -4
  313. package/src/modules/feature_toggles/components/OverridesTable.tsx +1 -1
  314. package/src/modules/notifications/acl.ts +7 -0
  315. package/src/modules/notifications/api/[id]/action/route.ts +75 -0
  316. package/src/modules/notifications/api/[id]/dismiss/route.ts +12 -0
  317. package/src/modules/notifications/api/[id]/read/route.ts +12 -0
  318. package/src/modules/notifications/api/[id]/restore/route.ts +53 -0
  319. package/src/modules/notifications/api/batch/route.ts +14 -0
  320. package/src/modules/notifications/api/feature/route.ts +14 -0
  321. package/src/modules/notifications/api/mark-all-read/route.ts +34 -0
  322. package/src/modules/notifications/api/openapi.ts +76 -0
  323. package/src/modules/notifications/api/role/route.ts +14 -0
  324. package/src/modules/notifications/api/route.ts +92 -0
  325. package/src/modules/notifications/api/settings/route.ts +157 -0
  326. package/src/modules/notifications/api/unread-count/route.ts +38 -0
  327. package/src/modules/notifications/backend/config/notifications/page.meta.ts +22 -0
  328. package/src/modules/notifications/backend/config/notifications/page.tsx +12 -0
  329. package/src/modules/notifications/cli.ts +18 -0
  330. package/src/modules/notifications/data/entities.ts +99 -0
  331. package/src/modules/notifications/data/validators.ts +110 -0
  332. package/src/modules/notifications/di.ts +11 -0
  333. package/src/modules/notifications/emails/NotificationEmail.tsx +98 -0
  334. package/src/modules/notifications/frontend/NotificationInboxPageClient.tsx +42 -0
  335. package/src/modules/notifications/frontend/NotificationSettingsPageClient.tsx +231 -0
  336. package/src/modules/notifications/i18n/de.json +50 -0
  337. package/src/modules/notifications/i18n/en.json +50 -0
  338. package/src/modules/notifications/i18n/es.json +50 -0
  339. package/src/modules/notifications/i18n/pl.json +50 -0
  340. package/src/modules/notifications/index.ts +12 -0
  341. package/src/modules/notifications/lib/deliveryConfig.ts +145 -0
  342. package/src/modules/notifications/lib/events.ts +48 -0
  343. package/src/modules/notifications/lib/notificationBuilder.ts +121 -0
  344. package/src/modules/notifications/lib/notificationFactory.ts +76 -0
  345. package/src/modules/notifications/lib/notificationMapper.ts +33 -0
  346. package/src/modules/notifications/lib/notificationRecipients.ts +83 -0
  347. package/src/modules/notifications/lib/notificationService.ts +414 -0
  348. package/src/modules/notifications/lib/routeHelpers.ts +151 -0
  349. package/src/modules/notifications/lib/safeHref.ts +29 -0
  350. package/src/modules/notifications/migrations/.snapshot-open-mercato.json +300 -0
  351. package/src/modules/notifications/migrations/Migration20260123000001.ts +73 -0
  352. package/src/modules/notifications/migrations/Migration20260126150000.ts +39 -0
  353. package/src/modules/notifications/subscribers/deliver-notification.ts +175 -0
  354. package/src/modules/notifications/workers/create-notification.worker.ts +122 -0
  355. package/src/modules/planner/backend/planner/availability-rulesets/page.tsx +2 -2
  356. package/src/modules/query_index/components/QueryIndexesTable.tsx +8 -2
  357. package/src/modules/resources/backend/resources/resource-types/page.tsx +2 -2
  358. package/src/modules/resources/backend/resources/resources/page.tsx +2 -2
  359. package/src/modules/sales/backend/sales/channels/offers/page.tsx +2 -0
  360. package/src/modules/sales/backend/sales/channels/page.tsx +2 -0
  361. package/src/modules/sales/commands/documents.ts +65 -0
  362. package/src/modules/sales/commands/payments.ts +33 -0
  363. package/src/modules/sales/components/AdjustmentKindSettings.tsx +2 -2
  364. package/src/modules/sales/components/PaymentMethodsSettings.tsx +2 -2
  365. package/src/modules/sales/components/ShippingMethodsSettings.tsx +2 -2
  366. package/src/modules/sales/components/TaxRatesSettings.tsx +2 -2
  367. package/src/modules/sales/components/channels/SalesChannelOffersPanel.tsx +2 -0
  368. package/src/modules/sales/components/documents/AdjustmentsSection.tsx +2 -0
  369. package/src/modules/sales/components/documents/PaymentsSection.tsx +2 -1
  370. package/src/modules/sales/components/documents/SalesDocumentsTable.tsx +2 -0
  371. package/src/modules/sales/i18n/de.json +20 -0
  372. package/src/modules/sales/i18n/en.json +25 -1
  373. package/src/modules/sales/i18n/es.json +20 -0
  374. package/src/modules/sales/i18n/pl.json +20 -0
  375. package/src/modules/sales/notifications.client.ts +65 -0
  376. package/src/modules/sales/notifications.ts +82 -0
  377. package/src/modules/sales/subscribers/quote-expiring-notification.ts +53 -0
  378. package/src/modules/sales/widgets/notifications/SalesOrderCreatedRenderer.tsx +156 -0
  379. package/src/modules/sales/widgets/notifications/SalesQuoteCreatedRenderer.tsx +156 -0
  380. package/src/modules/sales/widgets/notifications/index.ts +2 -0
  381. package/src/modules/sales/widgets/notifications/useSalesDocumentTotals.ts +81 -0
  382. package/src/modules/staff/backend/staff/team-members/page.tsx +1 -1
  383. package/src/modules/staff/backend/staff/team-roles/page.tsx +2 -2
  384. package/src/modules/staff/backend/staff/teams/[id]/edit/page.tsx +2 -2
  385. package/src/modules/staff/backend/staff/teams/page.tsx +2 -2
  386. package/src/modules/staff/commands/leave-requests.ts +94 -0
  387. package/src/modules/staff/i18n/de.json +4 -0
  388. package/src/modules/staff/i18n/en.json +9 -1
  389. package/src/modules/staff/i18n/es.json +4 -0
  390. package/src/modules/staff/i18n/pl.json +4 -0
  391. package/src/modules/staff/notifications.ts +71 -0
  392. package/src/modules/workflows/backend/definitions/page.tsx +5 -0
  393. package/src/modules/workflows/backend/instances/page.tsx +4 -1
  394. package/src/modules/workflows/backend/tasks/page.tsx +4 -1
  395. package/src/modules/workflows/i18n/en.json +3 -1
  396. package/src/modules/workflows/notifications.ts +25 -0
  397. package/src/modules/workflows/subscribers/task-assigned-notification.ts +53 -0
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../src/modules/notifications/migrations/Migration20260126150000.ts"],
4
+ "sourcesContent": ["import { Migration } from '@mikro-orm/migrations'\n\nexport class Migration20260126150000 extends Migration {\n async up(): Promise<void> {\n // Add i18n support fields to notifications table\n this.addSql(`\n alter table \"notifications\"\n add column if not exists \"title_key\" text,\n add column if not exists \"body_key\" text,\n add column if not exists \"title_variables\" jsonb,\n add column if not exists \"body_variables\" jsonb;\n `)\n\n // Add comments for clarity\n this.addSql(`\n comment on column \"notifications\".\"title_key\" is 'i18n key for notification title';\n `)\n this.addSql(`\n comment on column \"notifications\".\"body_key\" is 'i18n key for notification body';\n `)\n this.addSql(`\n comment on column \"notifications\".\"title_variables\" is 'Variables for i18n interpolation in title';\n `)\n this.addSql(`\n comment on column \"notifications\".\"body_variables\" is 'Variables for i18n interpolation in body';\n `)\n }\n\n async down(): Promise<void> {\n // Remove i18n support fields\n this.addSql(`\n alter table \"notifications\"\n drop column if exists \"title_key\",\n drop column if exists \"body_key\",\n drop column if exists \"title_variables\",\n drop column if exists \"body_variables\";\n `)\n }\n}\n"],
5
+ "mappings": "AAAA,SAAS,iBAAiB;AAEnB,MAAM,gCAAgC,UAAU;AAAA,EACrD,MAAM,KAAoB;AAExB,SAAK,OAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,KAMX;AAGD,SAAK,OAAO;AAAA;AAAA,KAEX;AACD,SAAK,OAAO;AAAA;AAAA,KAEX;AACD,SAAK,OAAO;AAAA;AAAA,KAEX;AACD,SAAK,OAAO;AAAA;AAAA,KAEX;AAAA,EACH;AAAA,EAEA,MAAM,OAAsB;AAE1B,SAAK,OAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,KAMX;AAAA,EACH;AACF;",
6
+ "names": []
7
+ }
@@ -0,0 +1,139 @@
1
+ import { Notification } from "../data/entities.js";
2
+ import { NOTIFICATION_EVENTS } from "../lib/events.js";
3
+ import { DEFAULT_NOTIFICATION_DELIVERY_CONFIG, resolveNotificationDeliveryConfig, resolveNotificationPanelUrl } from "../lib/deliveryConfig.js";
4
+ import { sendEmail } from "@open-mercato/shared/lib/email/send";
5
+ import NotificationEmail from "../emails/NotificationEmail.js";
6
+ import { loadDictionary } from "@open-mercato/shared/lib/i18n/server";
7
+ import { createFallbackTranslator } from "@open-mercato/shared/lib/i18n/translate";
8
+ import { defaultLocale } from "@open-mercato/shared/lib/i18n/config";
9
+ import { findOneWithDecryption } from "@open-mercato/shared/lib/encryption/find";
10
+ import { User } from "../../auth/data/entities.js";
11
+ const metadata = {
12
+ event: NOTIFICATION_EVENTS.CREATED,
13
+ persistent: true,
14
+ id: "notifications:deliver"
15
+ };
16
+ const DEBUG = process.env.NOTIFICATIONS_DEBUG === "true";
17
+ function debug(...args) {
18
+ if (DEBUG) {
19
+ console.log("[notifications]", ...args);
20
+ }
21
+ }
22
+ const buildPanelLink = (panelUrl, notificationId) => {
23
+ if (panelUrl.startsWith("http://") || panelUrl.startsWith("https://")) {
24
+ const url = new URL(panelUrl);
25
+ url.searchParams.set("notificationId", notificationId);
26
+ return url.toString();
27
+ }
28
+ const separator = panelUrl.includes("?") ? "&" : "?";
29
+ return `${panelUrl}${separator}notificationId=${encodeURIComponent(notificationId)}`;
30
+ };
31
+ const resolveNotificationCopy = async (notification) => {
32
+ const dict = await loadDictionary(defaultLocale);
33
+ const t = createFallbackTranslator(dict);
34
+ const title = notification.titleKey ? t(notification.titleKey, notification.title ?? notification.titleKey, notification.titleVariables ?? void 0) : notification.title;
35
+ const body = notification.bodyKey ? t(notification.bodyKey, notification.body ?? notification.bodyKey ?? "", notification.bodyVariables ?? void 0) : notification.body ?? null;
36
+ return { title, body, t };
37
+ };
38
+ const resolveRecipient = async (em, notification, encryptionService) => {
39
+ const where = {
40
+ id: notification.recipientUserId,
41
+ tenantId: notification.tenantId,
42
+ deletedAt: null
43
+ };
44
+ if (notification.organizationId) {
45
+ where.organizationId = notification.organizationId;
46
+ }
47
+ const record = await findOneWithDecryption(
48
+ em,
49
+ User,
50
+ where,
51
+ void 0,
52
+ {
53
+ tenantId: notification.tenantId,
54
+ organizationId: notification.organizationId ?? null,
55
+ encryptionService: encryptionService ?? null
56
+ }
57
+ );
58
+ if (!record) return null;
59
+ return {
60
+ email: typeof record.email === "string" ? record.email : null,
61
+ name: typeof record.name === "string" ? record.name : null
62
+ };
63
+ };
64
+ async function handle(payload, ctx) {
65
+ debug("deliver notification event", payload);
66
+ const deliveryConfig = await resolveNotificationDeliveryConfig(ctx, { defaultValue: DEFAULT_NOTIFICATION_DELIVERY_CONFIG });
67
+ if (!deliveryConfig.strategies.email.enabled) {
68
+ debug("email delivery disabled");
69
+ return;
70
+ }
71
+ const em = ctx.resolve("em");
72
+ const notification = await em.findOne(Notification, {
73
+ id: payload.notificationId,
74
+ tenantId: payload.tenantId,
75
+ organizationId: payload.organizationId ?? null
76
+ });
77
+ if (!notification) {
78
+ debug("notification not found", payload.notificationId);
79
+ return;
80
+ }
81
+ let encryptionService = null;
82
+ try {
83
+ encryptionService = ctx.resolve("tenantEncryptionService");
84
+ } catch {
85
+ encryptionService = null;
86
+ }
87
+ const recipient = await resolveRecipient(em, notification, encryptionService);
88
+ if (!recipient?.email) {
89
+ debug("recipient has no email", notification.recipientUserId);
90
+ }
91
+ const { title, body, t } = await resolveNotificationCopy(notification);
92
+ const panelUrl = resolveNotificationPanelUrl(deliveryConfig);
93
+ if (!panelUrl) {
94
+ debug("missing panelUrl; check appUrl/panelPath settings");
95
+ return;
96
+ }
97
+ const panelLink = buildPanelLink(panelUrl, notification.id);
98
+ const actionLinks = (notification.actionData?.actions ?? []).map((action) => ({
99
+ id: action.id,
100
+ label: action.labelKey ? t(action.labelKey, action.label) : action.label,
101
+ href: panelLink
102
+ }));
103
+ if (deliveryConfig.strategies.email.enabled && recipient?.email) {
104
+ const subjectPrefix = deliveryConfig.strategies.email.subjectPrefix?.trim();
105
+ const subject = subjectPrefix ? `${subjectPrefix} ${title}` : title;
106
+ const copy = {
107
+ preview: t("notifications.delivery.email.preview", "New notification"),
108
+ heading: t("notifications.delivery.email.heading", "You have a new notification"),
109
+ bodyIntro: t("notifications.delivery.email.bodyIntro", "Review the notification details and take any required actions."),
110
+ actionNotice: t("notifications.delivery.email.actionNotice", "Actions are available in Open Mercato and are read-only in this email."),
111
+ openCta: t("notifications.delivery.email.openCta", "Open notification center"),
112
+ footer: t("notifications.delivery.email.footer", "Open Mercato notifications")
113
+ };
114
+ try {
115
+ debug("sending email", { to: recipient.email, from: deliveryConfig.strategies.email.from, subject });
116
+ await sendEmail({
117
+ to: recipient.email,
118
+ subject,
119
+ from: deliveryConfig.strategies.email.from,
120
+ replyTo: deliveryConfig.strategies.email.replyTo,
121
+ react: NotificationEmail({
122
+ title,
123
+ body,
124
+ actions: actionLinks,
125
+ panelUrl: panelLink,
126
+ copy
127
+ })
128
+ });
129
+ } catch (error) {
130
+ console.error("[notifications] email delivery failed", error);
131
+ }
132
+ }
133
+ return;
134
+ }
135
+ export {
136
+ handle as default,
137
+ metadata
138
+ };
139
+ //# sourceMappingURL=deliver-notification.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../src/modules/notifications/subscribers/deliver-notification.ts"],
4
+ "sourcesContent": ["import type { EntityManager } from '@mikro-orm/postgresql'\nimport { Notification } from '../data/entities'\nimport { NOTIFICATION_EVENTS } from '../lib/events'\nimport { DEFAULT_NOTIFICATION_DELIVERY_CONFIG, resolveNotificationDeliveryConfig, resolveNotificationPanelUrl } from '../lib/deliveryConfig'\nimport { sendEmail } from '@open-mercato/shared/lib/email/send'\nimport NotificationEmail from '../emails/NotificationEmail'\nimport { loadDictionary } from '@open-mercato/shared/lib/i18n/server'\nimport { createFallbackTranslator } from '@open-mercato/shared/lib/i18n/translate'\nimport { defaultLocale } from '@open-mercato/shared/lib/i18n/config'\nimport { findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport { User } from '../../auth/data/entities'\nimport type { TenantDataEncryptionService } from '@open-mercato/shared/lib/encryption/tenantDataEncryptionService'\n\nexport const metadata = {\n event: NOTIFICATION_EVENTS.CREATED,\n persistent: true,\n id: 'notifications:deliver',\n}\n\nconst DEBUG = process.env.NOTIFICATIONS_DEBUG === 'true'\n\nfunction debug(...args: unknown[]): void {\n if (DEBUG) {\n console.log('[notifications]', ...args)\n }\n}\n\ntype NotificationCreatedPayload = {\n notificationId: string\n recipientUserId: string\n tenantId: string\n organizationId?: string | null\n}\n\ntype ResolverContext = {\n resolve: <T = unknown>(name: string) => T\n}\n\nconst buildPanelLink = (panelUrl: string, notificationId: string) => {\n if (panelUrl.startsWith('http://') || panelUrl.startsWith('https://')) {\n const url = new URL(panelUrl)\n url.searchParams.set('notificationId', notificationId)\n return url.toString()\n }\n const separator = panelUrl.includes('?') ? '&' : '?'\n return `${panelUrl}${separator}notificationId=${encodeURIComponent(notificationId)}`\n}\n\nconst resolveNotificationCopy = async (\n notification: Notification\n) => {\n const dict = await loadDictionary(defaultLocale)\n const t = createFallbackTranslator(dict)\n\n const title = notification.titleKey\n ? t(notification.titleKey, notification.title ?? notification.titleKey, notification.titleVariables ?? undefined)\n : notification.title\n\n const body = notification.bodyKey\n ? t(notification.bodyKey, notification.body ?? notification.bodyKey ?? '', notification.bodyVariables ?? undefined)\n : notification.body ?? null\n\n return { title, body, t }\n}\n\nconst resolveRecipient = async (\n em: EntityManager,\n notification: Notification,\n encryptionService?: TenantDataEncryptionService | null,\n) => {\n const where: Partial<User> & { deletedAt?: null } = {\n id: notification.recipientUserId,\n tenantId: notification.tenantId,\n deletedAt: null,\n }\n if (notification.organizationId) {\n where.organizationId = notification.organizationId\n }\n const record = await findOneWithDecryption(\n em,\n User,\n where,\n undefined,\n {\n tenantId: notification.tenantId,\n organizationId: notification.organizationId ?? null,\n encryptionService: encryptionService ?? null,\n },\n )\n if (!record) return null\n return {\n email: typeof record.email === 'string' ? record.email : null,\n name: typeof record.name === 'string' ? record.name : null,\n }\n}\n\n\nexport default async function handle(payload: NotificationCreatedPayload, ctx: ResolverContext) {\n debug('deliver notification event', payload)\n const deliveryConfig = await resolveNotificationDeliveryConfig(ctx, { defaultValue: DEFAULT_NOTIFICATION_DELIVERY_CONFIG })\n if (!deliveryConfig.strategies.email.enabled) {\n debug('email delivery disabled')\n return\n }\n\n const em = ctx.resolve('em') as EntityManager\n const notification = await em.findOne(Notification, {\n id: payload.notificationId,\n tenantId: payload.tenantId,\n organizationId: payload.organizationId ?? null,\n })\n if (!notification) {\n debug('notification not found', payload.notificationId)\n return\n }\n\n let encryptionService: TenantDataEncryptionService | null = null\n try {\n encryptionService = ctx.resolve<TenantDataEncryptionService>('tenantEncryptionService')\n } catch {\n encryptionService = null\n }\n\n const recipient = await resolveRecipient(em, notification, encryptionService)\n if (!recipient?.email) {\n debug('recipient has no email', notification.recipientUserId)\n }\n const { title, body, t } = await resolveNotificationCopy(notification)\n const panelUrl = resolveNotificationPanelUrl(deliveryConfig)\n if (!panelUrl) {\n debug('missing panelUrl; check appUrl/panelPath settings')\n return\n }\n\n const panelLink = buildPanelLink(panelUrl, notification.id)\n const actionLinks = (notification.actionData?.actions ?? []).map((action) => ({\n id: action.id,\n label: action.labelKey ? t(action.labelKey, action.label) : action.label,\n href: panelLink,\n }))\n\n if (deliveryConfig.strategies.email.enabled && recipient?.email) {\n const subjectPrefix = deliveryConfig.strategies.email.subjectPrefix?.trim()\n const subject = subjectPrefix ? `${subjectPrefix} ${title}` : title\n const copy = {\n preview: t('notifications.delivery.email.preview', 'New notification'),\n heading: t('notifications.delivery.email.heading', 'You have a new notification'),\n bodyIntro: t('notifications.delivery.email.bodyIntro', 'Review the notification details and take any required actions.'),\n actionNotice: t('notifications.delivery.email.actionNotice', 'Actions are available in Open Mercato and are read-only in this email.'),\n openCta: t('notifications.delivery.email.openCta', 'Open notification center'),\n footer: t('notifications.delivery.email.footer', 'Open Mercato notifications'),\n }\n\n try {\n debug('sending email', { to: recipient.email, from: deliveryConfig.strategies.email.from, subject })\n await sendEmail({\n to: recipient.email,\n subject,\n from: deliveryConfig.strategies.email.from,\n replyTo: deliveryConfig.strategies.email.replyTo,\n react: NotificationEmail({\n title,\n body,\n actions: actionLinks,\n panelUrl: panelLink,\n copy,\n }),\n })\n } catch (error) {\n console.error('[notifications] email delivery failed', error)\n }\n }\n\n return\n}\n"],
5
+ "mappings": "AACA,SAAS,oBAAoB;AAC7B,SAAS,2BAA2B;AACpC,SAAS,sCAAsC,mCAAmC,mCAAmC;AACrH,SAAS,iBAAiB;AAC1B,OAAO,uBAAuB;AAC9B,SAAS,sBAAsB;AAC/B,SAAS,gCAAgC;AACzC,SAAS,qBAAqB;AAC9B,SAAS,6BAA6B;AACtC,SAAS,YAAY;AAGd,MAAM,WAAW;AAAA,EACtB,OAAO,oBAAoB;AAAA,EAC3B,YAAY;AAAA,EACZ,IAAI;AACN;AAEA,MAAM,QAAQ,QAAQ,IAAI,wBAAwB;AAElD,SAAS,SAAS,MAAuB;AACvC,MAAI,OAAO;AACT,YAAQ,IAAI,mBAAmB,GAAG,IAAI;AAAA,EACxC;AACF;AAaA,MAAM,iBAAiB,CAAC,UAAkB,mBAA2B;AACnE,MAAI,SAAS,WAAW,SAAS,KAAK,SAAS,WAAW,UAAU,GAAG;AACrE,UAAM,MAAM,IAAI,IAAI,QAAQ;AAC5B,QAAI,aAAa,IAAI,kBAAkB,cAAc;AACrD,WAAO,IAAI,SAAS;AAAA,EACtB;AACA,QAAM,YAAY,SAAS,SAAS,GAAG,IAAI,MAAM;AACjD,SAAO,GAAG,QAAQ,GAAG,SAAS,kBAAkB,mBAAmB,cAAc,CAAC;AACpF;AAEA,MAAM,0BAA0B,OAC9B,iBACG;AACH,QAAM,OAAO,MAAM,eAAe,aAAa;AAC/C,QAAM,IAAI,yBAAyB,IAAI;AAEvC,QAAM,QAAQ,aAAa,WACvB,EAAE,aAAa,UAAU,aAAa,SAAS,aAAa,UAAU,aAAa,kBAAkB,MAAS,IAC9G,aAAa;AAEjB,QAAM,OAAO,aAAa,UACtB,EAAE,aAAa,SAAS,aAAa,QAAQ,aAAa,WAAW,IAAI,aAAa,iBAAiB,MAAS,IAChH,aAAa,QAAQ;AAEzB,SAAO,EAAE,OAAO,MAAM,EAAE;AAC1B;AAEA,MAAM,mBAAmB,OACvB,IACA,cACA,sBACG;AACH,QAAM,QAA8C;AAAA,IAClD,IAAI,aAAa;AAAA,IACjB,UAAU,aAAa;AAAA,IACvB,WAAW;AAAA,EACb;AACA,MAAI,aAAa,gBAAgB;AAC/B,UAAM,iBAAiB,aAAa;AAAA,EACtC;AACA,QAAM,SAAS,MAAM;AAAA,IACnB;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,MACE,UAAU,aAAa;AAAA,MACvB,gBAAgB,aAAa,kBAAkB;AAAA,MAC/C,mBAAmB,qBAAqB;AAAA,IAC1C;AAAA,EACF;AACA,MAAI,CAAC,OAAQ,QAAO;AACpB,SAAO;AAAA,IACL,OAAO,OAAO,OAAO,UAAU,WAAW,OAAO,QAAQ;AAAA,IACzD,MAAM,OAAO,OAAO,SAAS,WAAW,OAAO,OAAO;AAAA,EACxD;AACF;AAGA,eAAO,OAA8B,SAAqC,KAAsB;AAC9F,QAAM,8BAA8B,OAAO;AAC3C,QAAM,iBAAiB,MAAM,kCAAkC,KAAK,EAAE,cAAc,qCAAqC,CAAC;AAC1H,MAAI,CAAC,eAAe,WAAW,MAAM,SAAS;AAC5C,UAAM,yBAAyB;AAC/B;AAAA,EACF;AAEA,QAAM,KAAK,IAAI,QAAQ,IAAI;AAC3B,QAAM,eAAe,MAAM,GAAG,QAAQ,cAAc;AAAA,IAClD,IAAI,QAAQ;AAAA,IACZ,UAAU,QAAQ;AAAA,IAClB,gBAAgB,QAAQ,kBAAkB;AAAA,EAC5C,CAAC;AACD,MAAI,CAAC,cAAc;AACjB,UAAM,0BAA0B,QAAQ,cAAc;AACtD;AAAA,EACF;AAEA,MAAI,oBAAwD;AAC5D,MAAI;AACF,wBAAoB,IAAI,QAAqC,yBAAyB;AAAA,EACxF,QAAQ;AACN,wBAAoB;AAAA,EACtB;AAEA,QAAM,YAAY,MAAM,iBAAiB,IAAI,cAAc,iBAAiB;AAC5E,MAAI,CAAC,WAAW,OAAO;AACrB,UAAM,0BAA0B,aAAa,eAAe;AAAA,EAC9D;AACA,QAAM,EAAE,OAAO,MAAM,EAAE,IAAI,MAAM,wBAAwB,YAAY;AACrE,QAAM,WAAW,4BAA4B,cAAc;AAC3D,MAAI,CAAC,UAAU;AACb,UAAM,mDAAmD;AACzD;AAAA,EACF;AAEA,QAAM,YAAY,eAAe,UAAU,aAAa,EAAE;AAC1D,QAAM,eAAe,aAAa,YAAY,WAAW,CAAC,GAAG,IAAI,CAAC,YAAY;AAAA,IAC5E,IAAI,OAAO;AAAA,IACX,OAAO,OAAO,WAAW,EAAE,OAAO,UAAU,OAAO,KAAK,IAAI,OAAO;AAAA,IACnE,MAAM;AAAA,EACR,EAAE;AAEF,MAAI,eAAe,WAAW,MAAM,WAAW,WAAW,OAAO;AAC/D,UAAM,gBAAgB,eAAe,WAAW,MAAM,eAAe,KAAK;AAC1E,UAAM,UAAU,gBAAgB,GAAG,aAAa,IAAI,KAAK,KAAK;AAC9D,UAAM,OAAO;AAAA,MACX,SAAS,EAAE,wCAAwC,kBAAkB;AAAA,MACrE,SAAS,EAAE,wCAAwC,6BAA6B;AAAA,MAChF,WAAW,EAAE,0CAA0C,gEAAgE;AAAA,MACvH,cAAc,EAAE,6CAA6C,wEAAwE;AAAA,MACrI,SAAS,EAAE,wCAAwC,0BAA0B;AAAA,MAC7E,QAAQ,EAAE,uCAAuC,4BAA4B;AAAA,IAC/E;AAEA,QAAI;AACF,YAAM,iBAAiB,EAAE,IAAI,UAAU,OAAO,MAAM,eAAe,WAAW,MAAM,MAAM,QAAQ,CAAC;AACnG,YAAM,UAAU;AAAA,QACd,IAAI,UAAU;AAAA,QACd;AAAA,QACA,MAAM,eAAe,WAAW,MAAM;AAAA,QACtC,SAAS,eAAe,WAAW,MAAM;AAAA,QACzC,OAAO,kBAAkB;AAAA,UACvB;AAAA,UACA;AAAA,UACA,SAAS;AAAA,UACT,UAAU;AAAA,UACV;AAAA,QACF,CAAC;AAAA,MACH,CAAC;AAAA,IACH,SAAS,OAAO;AACd,cAAQ,MAAM,yCAAyC,KAAK;AAAA,IAC9D;AAAA,EACF;AAEA;AACF;",
6
+ "names": []
7
+ }
@@ -0,0 +1,70 @@
1
+ import { buildNotificationEntity, emitNotificationCreated, emitNotificationCreatedBatch } from "../lib/notificationFactory.js";
2
+ import { getRecipientUserIdsForFeature, getRecipientUserIdsForRole } from "../lib/notificationRecipients.js";
3
+ function getKnex(em) {
4
+ return em.getConnection().getKnex();
5
+ }
6
+ const NOTIFICATIONS_QUEUE_NAME = "notifications";
7
+ const metadata = {
8
+ queue: NOTIFICATIONS_QUEUE_NAME,
9
+ id: "notifications:create",
10
+ concurrency: 5
11
+ };
12
+ async function handle(job, ctx) {
13
+ const { payload } = job;
14
+ if (payload.type === "create") {
15
+ const em = ctx.resolve("em").fork();
16
+ const eventBus = ctx.resolve("eventBus");
17
+ const { input, tenantId, organizationId } = payload;
18
+ const { recipientUserId, ...content } = input;
19
+ const notification = buildNotificationEntity(em, content, recipientUserId, { tenantId, organizationId });
20
+ await em.persistAndFlush(notification);
21
+ await emitNotificationCreated(eventBus, notification, { tenantId, organizationId });
22
+ } else if (payload.type === "create-role") {
23
+ const em = ctx.resolve("em").fork();
24
+ const eventBus = ctx.resolve("eventBus");
25
+ const { input, tenantId, organizationId } = payload;
26
+ const knex = getKnex(em);
27
+ const recipientUserIds = await getRecipientUserIdsForRole(knex, tenantId, input.roleId);
28
+ if (recipientUserIds.length === 0) {
29
+ return;
30
+ }
31
+ const { roleId: _roleId, ...content } = input;
32
+ const notifications = [];
33
+ for (const recipientUserId of recipientUserIds) {
34
+ const notification = buildNotificationEntity(em, content, recipientUserId, { tenantId, organizationId });
35
+ notifications.push(notification);
36
+ }
37
+ await em.persistAndFlush(notifications);
38
+ await emitNotificationCreatedBatch(eventBus, notifications, { tenantId, organizationId });
39
+ } else if (payload.type === "create-feature") {
40
+ const em = ctx.resolve("em").fork();
41
+ const eventBus = ctx.resolve("eventBus");
42
+ const { input, tenantId, organizationId } = payload;
43
+ const knex = getKnex(em);
44
+ const recipientUserIds = await getRecipientUserIdsForFeature(knex, tenantId, input.requiredFeature);
45
+ if (recipientUserIds.length === 0) {
46
+ return;
47
+ }
48
+ const notifications = [];
49
+ const { requiredFeature: _requiredFeature, ...content } = input;
50
+ for (const recipientUserId of recipientUserIds) {
51
+ const notification = buildNotificationEntity(em, content, recipientUserId, { tenantId, organizationId });
52
+ notifications.push(notification);
53
+ }
54
+ await em.persistAndFlush(notifications);
55
+ await emitNotificationCreatedBatch(eventBus, notifications, { tenantId, organizationId });
56
+ } else if (payload.type === "cleanup-expired") {
57
+ const em = ctx.resolve("em").fork();
58
+ const knex = getKnex(em);
59
+ await knex("notifications").where("expires_at", "<", knex.fn.now()).whereNotIn("status", ["actioned", "dismissed"]).update({
60
+ status: "dismissed",
61
+ dismissed_at: knex.fn.now()
62
+ });
63
+ }
64
+ }
65
+ export {
66
+ NOTIFICATIONS_QUEUE_NAME,
67
+ handle as default,
68
+ metadata
69
+ };
70
+ //# sourceMappingURL=create-notification.worker.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../src/modules/notifications/workers/create-notification.worker.ts"],
4
+ "sourcesContent": ["import type { EntityManager } from '@mikro-orm/core'\nimport type { Knex } from 'knex'\nimport { Notification } from '../data/entities'\nimport type { CreateNotificationInput, CreateRoleNotificationInput, CreateFeatureNotificationInput } from '../data/validators'\nimport { buildNotificationEntity, emitNotificationCreated, emitNotificationCreatedBatch } from '../lib/notificationFactory'\nimport { getRecipientUserIdsForFeature, getRecipientUserIdsForRole } from '../lib/notificationRecipients'\n\nfunction getKnex(em: EntityManager): Knex {\n return (em.getConnection() as unknown as { getKnex: () => Knex }).getKnex()\n}\n\nexport const NOTIFICATIONS_QUEUE_NAME = 'notifications'\n\nexport type CreateNotificationJob = {\n type: 'create'\n input: CreateNotificationInput\n tenantId: string\n organizationId?: string | null\n}\n\nexport type CreateRoleNotificationJob = {\n type: 'create-role'\n input: CreateRoleNotificationInput\n tenantId: string\n organizationId?: string | null\n}\n\nexport type CreateFeatureNotificationJob = {\n type: 'create-feature'\n input: CreateFeatureNotificationInput\n tenantId: string\n organizationId?: string | null\n}\n\nexport type CleanupExpiredJob = {\n type: 'cleanup-expired'\n}\n\nexport type NotificationJob = CreateNotificationJob | CreateRoleNotificationJob | CreateFeatureNotificationJob | CleanupExpiredJob\n\nexport const metadata = {\n queue: NOTIFICATIONS_QUEUE_NAME,\n id: 'notifications:create',\n concurrency: 5,\n}\n\ntype HandlerContext = {\n resolve: <T = unknown>(name: string) => T\n}\n\nexport default async function handle(\n job: { payload: NotificationJob },\n ctx: HandlerContext\n): Promise<void> {\n const { payload } = job\n\n if (payload.type === 'create') {\n const em = (ctx.resolve('em') as EntityManager).fork()\n const eventBus = ctx.resolve('eventBus') as { emit: (event: string, payload: unknown) => Promise<void> }\n const { input, tenantId, organizationId } = payload\n const { recipientUserId, ...content } = input\n const notification = buildNotificationEntity(em, content, recipientUserId, { tenantId, organizationId })\n\n await em.persistAndFlush(notification)\n\n await emitNotificationCreated(eventBus, notification, { tenantId, organizationId })\n } else if (payload.type === 'create-role') {\n const em = (ctx.resolve('em') as EntityManager).fork()\n const eventBus = ctx.resolve('eventBus') as { emit: (event: string, payload: unknown) => Promise<void> }\n const { input, tenantId, organizationId } = payload\n\n const knex = getKnex(em)\n const recipientUserIds = await getRecipientUserIdsForRole(knex, tenantId, input.roleId)\n if (recipientUserIds.length === 0) {\n return\n }\n\n const { roleId: _roleId, ...content } = input\n const notifications: Notification[] = []\n for (const recipientUserId of recipientUserIds) {\n const notification = buildNotificationEntity(em, content, recipientUserId, { tenantId, organizationId })\n notifications.push(notification)\n }\n\n await em.persistAndFlush(notifications)\n\n await emitNotificationCreatedBatch(eventBus, notifications, { tenantId, organizationId })\n } else if (payload.type === 'create-feature') {\n const em = (ctx.resolve('em') as EntityManager).fork()\n const eventBus = ctx.resolve('eventBus') as { emit: (event: string, payload: unknown) => Promise<void> }\n const { input, tenantId, organizationId } = payload\n\n const knex = getKnex(em)\n const recipientUserIds = await getRecipientUserIdsForFeature(knex, tenantId, input.requiredFeature)\n\n if (recipientUserIds.length === 0) {\n return\n }\n\n const notifications: Notification[] = []\n const { requiredFeature: _requiredFeature, ...content } = input\n for (const recipientUserId of recipientUserIds) {\n const notification = buildNotificationEntity(em, content, recipientUserId, { tenantId, organizationId })\n notifications.push(notification)\n }\n\n await em.persistAndFlush(notifications)\n\n await emitNotificationCreatedBatch(eventBus, notifications, { tenantId, organizationId })\n } else if (payload.type === 'cleanup-expired') {\n const em = (ctx.resolve('em') as EntityManager).fork()\n const knex = getKnex(em)\n\n await knex('notifications')\n .where('expires_at', '<', knex.fn.now())\n .whereNotIn('status', ['actioned', 'dismissed'])\n .update({\n status: 'dismissed',\n dismissed_at: knex.fn.now(),\n })\n }\n}\n"],
5
+ "mappings": "AAIA,SAAS,yBAAyB,yBAAyB,oCAAoC;AAC/F,SAAS,+BAA+B,kCAAkC;AAE1E,SAAS,QAAQ,IAAyB;AACxC,SAAQ,GAAG,cAAc,EAAyC,QAAQ;AAC5E;AAEO,MAAM,2BAA2B;AA6BjC,MAAM,WAAW;AAAA,EACtB,OAAO;AAAA,EACP,IAAI;AAAA,EACJ,aAAa;AACf;AAMA,eAAO,OACL,KACA,KACe;AACf,QAAM,EAAE,QAAQ,IAAI;AAEpB,MAAI,QAAQ,SAAS,UAAU;AAC7B,UAAM,KAAM,IAAI,QAAQ,IAAI,EAAoB,KAAK;AACrD,UAAM,WAAW,IAAI,QAAQ,UAAU;AACvC,UAAM,EAAE,OAAO,UAAU,eAAe,IAAI;AAC5C,UAAM,EAAE,iBAAiB,GAAG,QAAQ,IAAI;AACxC,UAAM,eAAe,wBAAwB,IAAI,SAAS,iBAAiB,EAAE,UAAU,eAAe,CAAC;AAEvG,UAAM,GAAG,gBAAgB,YAAY;AAErC,UAAM,wBAAwB,UAAU,cAAc,EAAE,UAAU,eAAe,CAAC;AAAA,EACpF,WAAW,QAAQ,SAAS,eAAe;AACzC,UAAM,KAAM,IAAI,QAAQ,IAAI,EAAoB,KAAK;AACrD,UAAM,WAAW,IAAI,QAAQ,UAAU;AACvC,UAAM,EAAE,OAAO,UAAU,eAAe,IAAI;AAE5C,UAAM,OAAO,QAAQ,EAAE;AACvB,UAAM,mBAAmB,MAAM,2BAA2B,MAAM,UAAU,MAAM,MAAM;AACtF,QAAI,iBAAiB,WAAW,GAAG;AACjC;AAAA,IACF;AAEA,UAAM,EAAE,QAAQ,SAAS,GAAG,QAAQ,IAAI;AACxC,UAAM,gBAAgC,CAAC;AACvC,eAAW,mBAAmB,kBAAkB;AAC9C,YAAM,eAAe,wBAAwB,IAAI,SAAS,iBAAiB,EAAE,UAAU,eAAe,CAAC;AACvG,oBAAc,KAAK,YAAY;AAAA,IACjC;AAEA,UAAM,GAAG,gBAAgB,aAAa;AAEtC,UAAM,6BAA6B,UAAU,eAAe,EAAE,UAAU,eAAe,CAAC;AAAA,EAC1F,WAAW,QAAQ,SAAS,kBAAkB;AAC5C,UAAM,KAAM,IAAI,QAAQ,IAAI,EAAoB,KAAK;AACrD,UAAM,WAAW,IAAI,QAAQ,UAAU;AACvC,UAAM,EAAE,OAAO,UAAU,eAAe,IAAI;AAE5C,UAAM,OAAO,QAAQ,EAAE;AACvB,UAAM,mBAAmB,MAAM,8BAA8B,MAAM,UAAU,MAAM,eAAe;AAElG,QAAI,iBAAiB,WAAW,GAAG;AACjC;AAAA,IACF;AAEA,UAAM,gBAAgC,CAAC;AACvC,UAAM,EAAE,iBAAiB,kBAAkB,GAAG,QAAQ,IAAI;AAC1D,eAAW,mBAAmB,kBAAkB;AAC9C,YAAM,eAAe,wBAAwB,IAAI,SAAS,iBAAiB,EAAE,UAAU,eAAe,CAAC;AACvG,oBAAc,KAAK,YAAY;AAAA,IACjC;AAEA,UAAM,GAAG,gBAAgB,aAAa;AAEtC,UAAM,6BAA6B,UAAU,eAAe,EAAE,UAAU,eAAe,CAAC;AAAA,EAC1F,WAAW,QAAQ,SAAS,mBAAmB;AAC7C,UAAM,KAAM,IAAI,QAAQ,IAAI,EAAoB,KAAK;AACrD,UAAM,OAAO,QAAQ,EAAE;AAEvB,UAAM,KAAK,eAAe,EACvB,MAAM,cAAc,KAAK,KAAK,GAAG,IAAI,CAAC,EACtC,WAAW,UAAU,CAAC,YAAY,WAAW,CAAC,EAC9C,OAAO;AAAA,MACN,QAAQ;AAAA,MACR,cAAc,KAAK,GAAG,IAAI;AAAA,IAC5B,CAAC;AAAA,EACL;AACF;",
6
+ "names": []
7
+ }
@@ -161,8 +161,8 @@ function PlannerAvailabilityRuleSetsPage() {
161
161
  RowActions,
162
162
  {
163
163
  items: [
164
- { label: labels.actions.edit, href: `/backend/planner/availability-rulesets/${row.id}` },
165
- { label: labels.actions.delete, destructive: true, onSelect: () => {
164
+ { id: "edit", label: labels.actions.edit, href: `/backend/planner/availability-rulesets/${row.id}` },
165
+ { id: "delete", label: labels.actions.delete, destructive: true, onSelect: () => {
166
166
  void handleDelete(row);
167
167
  } }
168
168
  ]
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../../src/modules/planner/backend/planner/availability-rulesets/page.tsx"],
4
- "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport Link from 'next/link'\nimport { useRouter } from 'next/navigation'\nimport type { ColumnDef, SortingState } from '@tanstack/react-table'\nimport type { PluggableList } from 'unified'\nimport ReactMarkdown from 'react-markdown'\nimport remarkGfm from 'remark-gfm'\nimport { Page, PageBody } from '@open-mercato/ui/backend/Page'\nimport { DataTable } from '@open-mercato/ui/backend/DataTable'\nimport { RowActions } from '@open-mercato/ui/backend/RowActions'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { readApiResultOrThrow } from '@open-mercato/ui/backend/utils/apiCall'\nimport { deleteCrud } from '@open-mercato/ui/backend/utils/crud'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { normalizeCrudServerError } from '@open-mercato/ui/backend/utils/serverErrors'\nimport { useOrganizationScopeVersion } from '@open-mercato/shared/lib/frontend/useOrganizationScope'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\n\nconst PAGE_SIZE = 50\nconst MARKDOWN_PLUGINS: PluggableList = [remarkGfm]\nconst MARKDOWN_SUBTEXT_CLASSNAME =\n 'line-clamp-2 text-xs text-muted-foreground [&>p]:m-0 [&_ul]:ml-4 [&_ul]:list-disc [&_ol]:ml-4 [&_ol]:list-decimal [&_code]:rounded [&_code]:bg-muted [&_code]:px-1 [&_code]:py-0.5'\n\ntype RuleSetRow = {\n id: string\n name: string\n description: string | null\n timezone: string\n updatedAt: string | null\n}\n\ntype RuleSetResponse = {\n items?: Array<Record<string, unknown>>\n total?: number\n totalPages?: number\n}\n\nexport default function PlannerAvailabilityRuleSetsPage() {\n const t = useT()\n const router = useRouter()\n const scopeVersion = useOrganizationScopeVersion()\n const [rows, setRows] = React.useState<RuleSetRow[]>([])\n const [page, setPage] = React.useState(1)\n const [total, setTotal] = React.useState(0)\n const [totalPages, setTotalPages] = React.useState(1)\n const [sorting, setSorting] = React.useState<SortingState>([{ id: 'name', desc: false }])\n const [search, setSearch] = React.useState('')\n const [isLoading, setIsLoading] = React.useState(true)\n const [reloadToken, setReloadToken] = React.useState(0)\n\n const labels = React.useMemo(() => ({\n title: t('planner.availabilityRuleSets.page.title', 'Availability schedules'),\n description: t('planner.availabilityRuleSets.page.description', 'Manage shared availability rulesets.'),\n table: {\n name: t('planner.availabilityRuleSets.table.name', 'Name'),\n timezone: t('planner.availabilityRuleSets.table.timezone', 'Timezone'),\n updatedAt: t('planner.availabilityRuleSets.table.updatedAt', 'Updated'),\n empty: t('planner.availabilityRuleSets.table.empty', 'No schedules yet.'),\n search: t('planner.availabilityRuleSets.table.search', 'Search schedules...'),\n },\n actions: {\n add: t('planner.availabilityRuleSets.actions.add', 'New schedule'),\n edit: t('planner.availabilityRuleSets.actions.edit', 'Edit'),\n delete: t('planner.availabilityRuleSets.actions.delete', 'Delete'),\n deleteConfirm: t('planner.availabilityRuleSets.actions.deleteConfirm', 'Delete schedule \"{{name}}\"?'),\n refresh: t('planner.availabilityRuleSets.actions.refresh', 'Refresh'),\n },\n messages: {\n deleted: t('planner.availabilityRuleSets.messages.deleted', 'Schedule deleted.'),\n },\n errors: {\n load: t('planner.availabilityRuleSets.errors.load', 'Failed to load schedules.'),\n delete: t('planner.availabilityRuleSets.errors.delete', 'Failed to delete schedule.'),\n },\n }), [t])\n\n const loadRuleSets = React.useCallback(async () => {\n setIsLoading(true)\n try {\n const params = new URLSearchParams({\n page: String(page),\n pageSize: String(PAGE_SIZE),\n })\n const sort = sorting[0]\n if (sort?.id) {\n params.set('sortField', sort.id)\n params.set('sortDir', sort.desc ? 'desc' : 'asc')\n }\n if (search.trim()) params.set('search', search.trim())\n const payload = await readApiResultOrThrow<RuleSetResponse>(\n `/api/planner/availability-rule-sets?${params.toString()}`,\n undefined,\n { errorMessage: labels.errors.load, fallback: { items: [], total: 0, totalPages: 1 } },\n )\n const items = Array.isArray(payload.items) ? payload.items : []\n setRows(items.map(mapRuleSet))\n setTotal(typeof payload.total === 'number' ? payload.total : items.length)\n setTotalPages(typeof payload.totalPages === 'number' ? payload.totalPages : Math.max(1, Math.ceil(items.length / PAGE_SIZE)))\n } catch (error) {\n console.error('planner.availability-rule-sets.list', error)\n flash(labels.errors.load, 'error')\n } finally {\n setIsLoading(false)\n }\n }, [labels.errors.load, page, search, sorting])\n\n React.useEffect(() => {\n void loadRuleSets()\n }, [loadRuleSets, scopeVersion, reloadToken])\n\n const handleSearchChange = React.useCallback((value: string) => {\n setSearch(value)\n setPage(1)\n }, [])\n\n const handleRefresh = React.useCallback(() => {\n setReloadToken((token) => token + 1)\n }, [])\n\n const handleDelete = React.useCallback(async (entry: RuleSetRow) => {\n const message = labels.actions.deleteConfirm.replace('{{name}}', entry.name)\n if (typeof window !== 'undefined' && !window.confirm(message)) return\n try {\n await deleteCrud('planner/availability-rule-sets', entry.id, { errorMessage: labels.errors.delete })\n flash(labels.messages.deleted, 'success')\n handleRefresh()\n } catch (error) {\n console.error('planner.availability-rule-sets.delete', error)\n const normalized = normalizeCrudServerError(error)\n flash(normalized.message ?? labels.errors.delete, 'error')\n }\n }, [handleRefresh, labels.actions.deleteConfirm, labels.errors.delete, labels.messages.deleted])\n\n const columns = React.useMemo<ColumnDef<RuleSetRow>[]>(() => [\n {\n accessorKey: 'name',\n header: labels.table.name,\n meta: { priority: 1, sticky: true },\n cell: ({ row }) => (\n <div className=\"flex flex-col\">\n <span className=\"font-medium\">{row.original.name}</span>\n {row.original.description ? (\n <ReactMarkdown remarkPlugins={MARKDOWN_PLUGINS} className={MARKDOWN_SUBTEXT_CLASSNAME}>\n {row.original.description}\n </ReactMarkdown>\n ) : null}\n </div>\n ),\n },\n {\n accessorKey: 'timezone',\n header: labels.table.timezone,\n meta: { priority: 2 },\n cell: ({ row }) => <span className=\"text-sm\">{row.original.timezone}</span>,\n },\n {\n accessorKey: 'updatedAt',\n header: labels.table.updatedAt,\n meta: { priority: 3 },\n cell: ({ row }) => row.original.updatedAt\n ? <span className=\"text-xs text-muted-foreground\">{formatDateTime(row.original.updatedAt)}</span>\n : <span className=\"text-xs text-muted-foreground\">-</span>,\n },\n ], [labels.table.name, labels.table.timezone, labels.table.updatedAt])\n\n return (\n <Page>\n <PageBody>\n <DataTable<RuleSetRow>\n title={labels.title}\n data={rows}\n columns={columns}\n isLoading={isLoading}\n searchValue={search}\n onSearchChange={handleSearchChange}\n searchPlaceholder={labels.table.search}\n emptyState={<p className=\"py-8 text-center text-sm text-muted-foreground\">{labels.table.empty}</p>}\n actions={(\n <Button asChild size=\"sm\">\n <Link href=\"/backend/planner/availability-rulesets/create\">\n {labels.actions.add}\n </Link>\n </Button>\n )}\n refreshButton={{\n label: labels.actions.refresh,\n onRefresh: handleRefresh,\n isRefreshing: isLoading,\n }}\n sortable\n sorting={sorting}\n onSortingChange={setSorting}\n pagination={{\n page,\n pageSize: PAGE_SIZE,\n total,\n totalPages,\n onPageChange: setPage,\n }}\n rowActions={(row) => (\n <RowActions\n items={[\n { label: labels.actions.edit, href: `/backend/planner/availability-rulesets/${row.id}` },\n { label: labels.actions.delete, destructive: true, onSelect: () => { void handleDelete(row) } },\n ]}\n />\n )}\n onRowClick={(row) => router.push(`/backend/planner/availability-rulesets/${row.id}`)}\n />\n </PageBody>\n </Page>\n )\n}\n\nfunction mapRuleSet(item: Record<string, unknown>): RuleSetRow {\n const id = typeof item.id === 'string' ? item.id : ''\n const name = typeof item.name === 'string' ? item.name : id\n const description = typeof item.description === 'string' ? item.description : null\n const timezone = typeof item.timezone === 'string' ? item.timezone : 'UTC'\n const updatedAt =\n typeof item.updatedAt === 'string'\n ? item.updatedAt\n : typeof item.updated_at === 'string'\n ? item.updated_at\n : null\n return {\n id,\n name,\n description,\n timezone,\n updatedAt,\n }\n}\n\nfunction formatDateTime(value: string): string {\n const parsed = new Date(value)\n if (Number.isNaN(parsed.getTime())) return value\n return parsed.toLocaleString()\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport Link from 'next/link'\nimport { useRouter } from 'next/navigation'\nimport type { ColumnDef, SortingState } from '@tanstack/react-table'\nimport type { PluggableList } from 'unified'\nimport ReactMarkdown from 'react-markdown'\nimport remarkGfm from 'remark-gfm'\nimport { Page, PageBody } from '@open-mercato/ui/backend/Page'\nimport { DataTable } from '@open-mercato/ui/backend/DataTable'\nimport { RowActions } from '@open-mercato/ui/backend/RowActions'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { readApiResultOrThrow } from '@open-mercato/ui/backend/utils/apiCall'\nimport { deleteCrud } from '@open-mercato/ui/backend/utils/crud'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { normalizeCrudServerError } from '@open-mercato/ui/backend/utils/serverErrors'\nimport { useOrganizationScopeVersion } from '@open-mercato/shared/lib/frontend/useOrganizationScope'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\n\nconst PAGE_SIZE = 50\nconst MARKDOWN_PLUGINS: PluggableList = [remarkGfm]\nconst MARKDOWN_SUBTEXT_CLASSNAME =\n 'line-clamp-2 text-xs text-muted-foreground [&>p]:m-0 [&_ul]:ml-4 [&_ul]:list-disc [&_ol]:ml-4 [&_ol]:list-decimal [&_code]:rounded [&_code]:bg-muted [&_code]:px-1 [&_code]:py-0.5'\n\ntype RuleSetRow = {\n id: string\n name: string\n description: string | null\n timezone: string\n updatedAt: string | null\n}\n\ntype RuleSetResponse = {\n items?: Array<Record<string, unknown>>\n total?: number\n totalPages?: number\n}\n\nexport default function PlannerAvailabilityRuleSetsPage() {\n const t = useT()\n const router = useRouter()\n const scopeVersion = useOrganizationScopeVersion()\n const [rows, setRows] = React.useState<RuleSetRow[]>([])\n const [page, setPage] = React.useState(1)\n const [total, setTotal] = React.useState(0)\n const [totalPages, setTotalPages] = React.useState(1)\n const [sorting, setSorting] = React.useState<SortingState>([{ id: 'name', desc: false }])\n const [search, setSearch] = React.useState('')\n const [isLoading, setIsLoading] = React.useState(true)\n const [reloadToken, setReloadToken] = React.useState(0)\n\n const labels = React.useMemo(() => ({\n title: t('planner.availabilityRuleSets.page.title', 'Availability schedules'),\n description: t('planner.availabilityRuleSets.page.description', 'Manage shared availability rulesets.'),\n table: {\n name: t('planner.availabilityRuleSets.table.name', 'Name'),\n timezone: t('planner.availabilityRuleSets.table.timezone', 'Timezone'),\n updatedAt: t('planner.availabilityRuleSets.table.updatedAt', 'Updated'),\n empty: t('planner.availabilityRuleSets.table.empty', 'No schedules yet.'),\n search: t('planner.availabilityRuleSets.table.search', 'Search schedules...'),\n },\n actions: {\n add: t('planner.availabilityRuleSets.actions.add', 'New schedule'),\n edit: t('planner.availabilityRuleSets.actions.edit', 'Edit'),\n delete: t('planner.availabilityRuleSets.actions.delete', 'Delete'),\n deleteConfirm: t('planner.availabilityRuleSets.actions.deleteConfirm', 'Delete schedule \"{{name}}\"?'),\n refresh: t('planner.availabilityRuleSets.actions.refresh', 'Refresh'),\n },\n messages: {\n deleted: t('planner.availabilityRuleSets.messages.deleted', 'Schedule deleted.'),\n },\n errors: {\n load: t('planner.availabilityRuleSets.errors.load', 'Failed to load schedules.'),\n delete: t('planner.availabilityRuleSets.errors.delete', 'Failed to delete schedule.'),\n },\n }), [t])\n\n const loadRuleSets = React.useCallback(async () => {\n setIsLoading(true)\n try {\n const params = new URLSearchParams({\n page: String(page),\n pageSize: String(PAGE_SIZE),\n })\n const sort = sorting[0]\n if (sort?.id) {\n params.set('sortField', sort.id)\n params.set('sortDir', sort.desc ? 'desc' : 'asc')\n }\n if (search.trim()) params.set('search', search.trim())\n const payload = await readApiResultOrThrow<RuleSetResponse>(\n `/api/planner/availability-rule-sets?${params.toString()}`,\n undefined,\n { errorMessage: labels.errors.load, fallback: { items: [], total: 0, totalPages: 1 } },\n )\n const items = Array.isArray(payload.items) ? payload.items : []\n setRows(items.map(mapRuleSet))\n setTotal(typeof payload.total === 'number' ? payload.total : items.length)\n setTotalPages(typeof payload.totalPages === 'number' ? payload.totalPages : Math.max(1, Math.ceil(items.length / PAGE_SIZE)))\n } catch (error) {\n console.error('planner.availability-rule-sets.list', error)\n flash(labels.errors.load, 'error')\n } finally {\n setIsLoading(false)\n }\n }, [labels.errors.load, page, search, sorting])\n\n React.useEffect(() => {\n void loadRuleSets()\n }, [loadRuleSets, scopeVersion, reloadToken])\n\n const handleSearchChange = React.useCallback((value: string) => {\n setSearch(value)\n setPage(1)\n }, [])\n\n const handleRefresh = React.useCallback(() => {\n setReloadToken((token) => token + 1)\n }, [])\n\n const handleDelete = React.useCallback(async (entry: RuleSetRow) => {\n const message = labels.actions.deleteConfirm.replace('{{name}}', entry.name)\n if (typeof window !== 'undefined' && !window.confirm(message)) return\n try {\n await deleteCrud('planner/availability-rule-sets', entry.id, { errorMessage: labels.errors.delete })\n flash(labels.messages.deleted, 'success')\n handleRefresh()\n } catch (error) {\n console.error('planner.availability-rule-sets.delete', error)\n const normalized = normalizeCrudServerError(error)\n flash(normalized.message ?? labels.errors.delete, 'error')\n }\n }, [handleRefresh, labels.actions.deleteConfirm, labels.errors.delete, labels.messages.deleted])\n\n const columns = React.useMemo<ColumnDef<RuleSetRow>[]>(() => [\n {\n accessorKey: 'name',\n header: labels.table.name,\n meta: { priority: 1, sticky: true },\n cell: ({ row }) => (\n <div className=\"flex flex-col\">\n <span className=\"font-medium\">{row.original.name}</span>\n {row.original.description ? (\n <ReactMarkdown remarkPlugins={MARKDOWN_PLUGINS} className={MARKDOWN_SUBTEXT_CLASSNAME}>\n {row.original.description}\n </ReactMarkdown>\n ) : null}\n </div>\n ),\n },\n {\n accessorKey: 'timezone',\n header: labels.table.timezone,\n meta: { priority: 2 },\n cell: ({ row }) => <span className=\"text-sm\">{row.original.timezone}</span>,\n },\n {\n accessorKey: 'updatedAt',\n header: labels.table.updatedAt,\n meta: { priority: 3 },\n cell: ({ row }) => row.original.updatedAt\n ? <span className=\"text-xs text-muted-foreground\">{formatDateTime(row.original.updatedAt)}</span>\n : <span className=\"text-xs text-muted-foreground\">-</span>,\n },\n ], [labels.table.name, labels.table.timezone, labels.table.updatedAt])\n\n return (\n <Page>\n <PageBody>\n <DataTable<RuleSetRow>\n title={labels.title}\n data={rows}\n columns={columns}\n isLoading={isLoading}\n searchValue={search}\n onSearchChange={handleSearchChange}\n searchPlaceholder={labels.table.search}\n emptyState={<p className=\"py-8 text-center text-sm text-muted-foreground\">{labels.table.empty}</p>}\n actions={(\n <Button asChild size=\"sm\">\n <Link href=\"/backend/planner/availability-rulesets/create\">\n {labels.actions.add}\n </Link>\n </Button>\n )}\n refreshButton={{\n label: labels.actions.refresh,\n onRefresh: handleRefresh,\n isRefreshing: isLoading,\n }}\n sortable\n sorting={sorting}\n onSortingChange={setSorting}\n pagination={{\n page,\n pageSize: PAGE_SIZE,\n total,\n totalPages,\n onPageChange: setPage,\n }}\n rowActions={(row) => (\n <RowActions\n items={[\n { id: 'edit', label: labels.actions.edit, href: `/backend/planner/availability-rulesets/${row.id}` },\n { id: 'delete', label: labels.actions.delete, destructive: true, onSelect: () => { void handleDelete(row) } },\n ]}\n />\n )}\n onRowClick={(row) => router.push(`/backend/planner/availability-rulesets/${row.id}`)}\n />\n </PageBody>\n </Page>\n )\n}\n\nfunction mapRuleSet(item: Record<string, unknown>): RuleSetRow {\n const id = typeof item.id === 'string' ? item.id : ''\n const name = typeof item.name === 'string' ? item.name : id\n const description = typeof item.description === 'string' ? item.description : null\n const timezone = typeof item.timezone === 'string' ? item.timezone : 'UTC'\n const updatedAt =\n typeof item.updatedAt === 'string'\n ? item.updatedAt\n : typeof item.updated_at === 'string'\n ? item.updated_at\n : null\n return {\n id,\n name,\n description,\n timezone,\n updatedAt,\n }\n}\n\nfunction formatDateTime(value: string): string {\n const parsed = new Date(value)\n if (Number.isNaN(parsed.getTime())) return value\n return parsed.toLocaleString()\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -265,12 +265,14 @@ function QueryIndexesTable() {
265
265
  perspective: { tableId: "query_index.status.list" },
266
266
  rowActions: (row) => {
267
267
  const items = [
268
- { label: t("query_index.table.actions.reindex"), onSelect: () => trigger("reindex", row.entityId) },
268
+ { id: "reindex", label: t("query_index.table.actions.reindex"), onSelect: () => trigger("reindex", row.entityId) },
269
269
  {
270
+ id: "reindex-force",
270
271
  label: t("query_index.table.actions.reindexForce"),
271
272
  onSelect: () => trigger("reindex", row.entityId, { force: true })
272
273
  },
273
274
  {
275
+ id: "purge",
274
276
  label: t("query_index.table.actions.purge"),
275
277
  destructive: true,
276
278
  onSelect: () => trigger("purge", row.entityId)
@@ -279,10 +281,12 @@ function QueryIndexesTable() {
279
281
  if (row.vectorEnabled) {
280
282
  items.push(
281
283
  {
284
+ id: "vector-reindex",
282
285
  label: t("query_index.table.actions.vectorReindex"),
283
286
  onSelect: () => triggerVector("reindex", row.entityId)
284
287
  },
285
288
  {
289
+ id: "vector-purge",
286
290
  label: t("query_index.table.actions.vectorPurge"),
287
291
  destructive: true,
288
292
  onSelect: () => triggerVector("purge", row.entityId)
@@ -292,10 +296,12 @@ function QueryIndexesTable() {
292
296
  if (row.fulltextEnabled) {
293
297
  items.push(
294
298
  {
299
+ id: "fulltext-reindex",
295
300
  label: t("query_index.table.actions.fulltextReindex"),
296
301
  onSelect: () => triggerFulltext("reindex", row.entityId)
297
302
  },
298
303
  {
304
+ id: "fulltext-purge",
299
305
  label: t("query_index.table.actions.fulltextPurge"),
300
306
  destructive: true,
301
307
  onSelect: () => triggerFulltext("purge", row.entityId)
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/modules/query_index/components/QueryIndexesTable.tsx"],
4
- "sourcesContent": ["\"use client\"\nimport * as React from 'react'\nimport { useQuery, useQueryClient } from '@tanstack/react-query'\nimport type { ColumnDef, SortingState } from '@tanstack/react-table'\nimport { DataTable } from '@open-mercato/ui/backend/DataTable'\nimport { RowActions } from '@open-mercato/ui/backend/RowActions'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { apiCallOrThrow, readApiResultOrThrow } from '@open-mercato/ui/backend/utils/apiCall'\nimport { useOrganizationScopeVersion } from '@open-mercato/shared/lib/frontend/useOrganizationScope'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\n\ntype Translator = (key: string, params?: Record<string, string | number>) => string\n\ntype PartitionStatus = {\n partitionIndex: number | null\n partitionCount: number | null\n status: 'reindexing' | 'purging' | 'stalled' | 'completed'\n processedCount?: number | null\n totalCount?: number | null\n heartbeatAt?: string | null\n startedAt?: string | null\n finishedAt?: string | null\n}\n\ntype JobStatus = {\n status: 'idle' | 'reindexing' | 'purging' | 'stalled'\n startedAt?: string | null\n finishedAt?: string | null\n heartbeatAt?: string | null\n processedCount?: number | null\n totalCount?: number | null\n partitions?: PartitionStatus[]\n scope?: {\n status?: 'reindexing' | 'purging' | 'stalled' | 'completed' | null\n processedCount?: number | null\n totalCount?: number | null\n } | null\n}\n\ntype Row = {\n entityId: string\n label: string\n baseCount: number | null\n indexCount: number | null\n vectorCount: number | null\n vectorEnabled: boolean\n fulltextCount: number | null\n fulltextEnabled: boolean\n ok: boolean\n job?: JobStatus\n}\n\ntype Resp = { items: Row[] }\n\nfunction formatCount(value: number | null): string {\n if (value == null) return '\u2014'\n return value.toLocaleString()\n}\n\nfunction formatNumeric(value: number | null | undefined): string | null {\n if (value == null) return null\n return Number(value).toLocaleString()\n}\n\nfunction formatProgressLabel(\n processed: number | null | undefined,\n total: number | null | undefined,\n t: Translator,\n): string | null {\n const processedText = formatNumeric(processed)\n if (!processedText) return null\n const totalText = formatNumeric(total)\n if (totalText) return t('query_index.table.status.progress', { processed: processedText, total: totalText })\n return t('query_index.table.status.progressSingle', { processed: processedText })\n}\n\nfunction translateJobStatus(t: Translator, status: JobStatus['status'] | undefined, ok: boolean): string {\n if (!status || status === 'idle') {\n return ok ? t('query_index.table.status.in_sync') : t('query_index.table.status.out_of_sync')\n }\n if (status === 'reindexing') return t('query_index.table.status.reindexing')\n if (status === 'purging') return t('query_index.table.status.purging')\n if (status === 'stalled') return t('query_index.table.status.stalled')\n return ok ? t('query_index.table.status.in_sync') : t('query_index.table.status.out_of_sync')\n}\n\nfunction translateScopeStatus(\n t: Translator,\n status: PartitionStatus['status'] | JobStatus['status'] | undefined | null,\n): string {\n if (status === 'reindexing') return t('query_index.table.status.scope.reindexing')\n if (status === 'purging') return t('query_index.table.status.scope.purging')\n if (status === 'stalled') return t('query_index.table.status.scope.stalled')\n return t('query_index.table.status.scope.completed')\n}\n\n\nfunction createColumns(t: Translator): ColumnDef<Row>[] {\n return [\n { id: 'entityId', header: () => t('query_index.table.columns.entity'), accessorKey: 'entityId', meta: { priority: 1 } },\n { id: 'label', header: () => t('query_index.table.columns.label'), accessorKey: 'label', meta: { priority: 2 } },\n {\n id: 'baseCount',\n header: () => t('query_index.table.columns.records'),\n accessorFn: (row) => row.baseCount ?? 0,\n cell: ({ row }) => <span>{formatCount(row.original.baseCount)}</span>,\n meta: { priority: 2 },\n },\n {\n id: 'indexCount',\n header: () => t('query_index.table.columns.indexed'),\n accessorFn: (row) => row.indexCount ?? 0,\n cell: ({ row }) => <span>{formatCount(row.original.indexCount)}</span>,\n meta: { priority: 2 },\n },\n {\n id: 'vectorCount',\n header: () => t('query_index.table.columns.vector'),\n accessorFn: (row) => (row.vectorEnabled ? row.vectorCount ?? 0 : -1),\n cell: ({ row }) => {\n const record = row.original\n if (!record.vectorEnabled) return <span>\u2014</span>\n const ok = record.vectorCount != null && record.baseCount != null && record.vectorCount === record.baseCount\n const display = formatCount(record.vectorCount)\n const className = ok ? 'text-green-600' : 'text-orange-600'\n return <span className={className}>{display}</span>\n },\n meta: { priority: 2 },\n },\n {\n id: 'fulltextCount',\n header: () => t('query_index.table.columns.fulltext'),\n accessorFn: (row) => (row.fulltextEnabled ? row.fulltextCount ?? 0 : -1),\n cell: ({ row }) => {\n const record = row.original\n if (!record.fulltextEnabled) return <span>\u2014</span>\n const ok = record.fulltextCount != null && record.baseCount != null && record.fulltextCount === record.baseCount\n const display = formatCount(record.fulltextCount)\n const className = ok ? 'text-green-600' : 'text-orange-600'\n return <span className={className}>{display}</span>\n },\n meta: { priority: 2 },\n },\n {\n id: 'status',\n header: () => t('query_index.table.columns.status'),\n cell: ({ row }) => {\n const record = row.original\n const job = record.job\n const partitions = job?.partitions ?? []\n const ok = record.ok && (!job || job.status === 'idle')\n const statusText = translateJobStatus(t, job?.status, ok)\n const jobProgress = job ? formatProgressLabel(job.processedCount ?? null, job.totalCount ?? null, t) : null\n const label = jobProgress\n ? t('query_index.table.status.withProgress', { status: statusText, progress: jobProgress })\n : statusText\n const className = job\n ? job.status === 'stalled'\n ? 'text-red-600'\n : job.status === 'reindexing' || job.status === 'purging'\n ? 'text-orange-600'\n : ok\n ? 'text-green-600'\n : 'text-muted-foreground'\n : ok\n ? 'text-green-600'\n : 'text-muted-foreground'\n\n const lines: string[] = []\n\n if (job?.scope && partitions.length <= 1) {\n const scopeStatus = translateScopeStatus(t, job.scope.status ?? null)\n const scopeProgress = formatProgressLabel(job.scope.processedCount ?? null, job.scope.totalCount ?? null, t)\n const scopeLabel = t('query_index.table.status.scopeLabel')\n lines.push(`${scopeLabel}: ${scopeStatus}${scopeProgress ? ` (${scopeProgress})` : ''}`)\n }\n\n if (partitions.length > 1) {\n for (const part of partitions) {\n const partitionLabel =\n part.partitionIndex != null\n ? t('query_index.table.status.partitionLabel', { index: Number(part.partitionIndex) + 1 })\n : t('query_index.table.status.scopeLabel')\n const partitionStatus = translateScopeStatus(t, part.status)\n const partitionProgress = formatProgressLabel(part.processedCount ?? null, part.totalCount ?? null, t)\n lines.push(`${partitionLabel}: ${partitionStatus}${partitionProgress ? ` (${partitionProgress})` : ''}`)\n }\n }\n\n if (record.vectorEnabled) {\n const vectorLabel = t('query_index.table.status.vectorLabel')\n const vectorCount = formatCount(record.vectorCount)\n const vectorTotal = record.baseCount != null ? formatCount(record.baseCount) : null\n const vectorValue = vectorTotal\n ? t('query_index.table.status.vectorValue', { count: vectorCount, total: vectorTotal })\n : vectorCount\n lines.push(`${vectorLabel}: ${vectorValue}`)\n }\n\n return (\n <div className=\"space-y-1\">\n <span className={className}>{label}</span>\n {lines.length > 0 && (\n <div className=\"text-xs text-muted-foreground\">\n {lines.map((line, idx) => (\n <div key={idx}>{line}</div>\n ))}\n </div>\n )}\n </div>\n )\n },\n meta: { priority: 1 },\n },\n ]\n}\n\nexport default function QueryIndexesTable() {\n const [sorting, setSorting] = React.useState<SortingState>([{ id: 'entityId', desc: false }])\n const [page, setPage] = React.useState(1)\n const [search, setSearch] = React.useState('')\n const qc = useQueryClient()\n const scopeVersion = useOrganizationScopeVersion()\n const [refreshSeq, setRefreshSeq] = React.useState(0)\n const t = useT()\n const columns = React.useMemo(() => createColumns(t), [t])\n\n const { data, isLoading } = useQuery<Resp>({\n queryKey: ['query-index-status', scopeVersion, refreshSeq],\n queryFn: async () => {\n const baseUrl = '/api/query_index/status'\n const url = refreshSeq > 0 ? `${baseUrl}?refresh=${refreshSeq}` : baseUrl\n return readApiResultOrThrow<Resp>(\n url,\n undefined,\n { errorMessage: t('query_index.table.errors.loadFailed') },\n )\n },\n refetchInterval: 4000,\n })\n\n const rowsAll = data?.items || []\n const rows = React.useMemo(() => {\n if (!search) return rowsAll\n const q = search.toLowerCase()\n return rowsAll.filter((r) => r.entityId.toLowerCase().includes(q) || r.label.toLowerCase().includes(q))\n }, [rowsAll, search])\n\n const trigger = React.useCallback(\n async (action: 'reindex' | 'purge', entityId: string, opts?: { force?: boolean }) => {\n const body: Record<string, unknown> = { entityType: entityId }\n if (opts?.force) body.force = true\n const actionLabel =\n action === 'purge' ? t('query_index.table.actions.purge') : t('query_index.table.actions.reindex')\n const errorMessage = t('query_index.table.errors.actionFailed', { action: actionLabel })\n try {\n await apiCallOrThrow(`/api/query_index/${action}`, {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify(body),\n }, { errorMessage })\n } catch (err) {\n console.error('query_index.table.trigger', err)\n if (typeof window !== 'undefined') {\n const message = err instanceof Error ? err.message : errorMessage\n window.alert(message)\n }\n }\n qc.invalidateQueries({ queryKey: ['query-index-status'] })\n },\n [qc, t],\n )\n\n const triggerVector = React.useCallback(\n async (action: 'reindex' | 'purge', entityId: string) => {\n if (action === 'purge' && typeof window !== 'undefined') {\n const confirmed = window.confirm(t('query_index.table.confirm.vectorPurge'))\n if (!confirmed) return\n }\n\n const actionLabel = action === 'purge'\n ? t('query_index.table.actions.vectorPurge')\n : t('query_index.table.actions.vectorReindex')\n const errorMessage = t('query_index.table.errors.actionFailed', { action: actionLabel })\n try {\n if (action === 'reindex') {\n await apiCallOrThrow('/api/vector/reindex', {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ entityId }),\n }, { errorMessage })\n } else {\n const url = `/api/vector/index?entityId=${encodeURIComponent(entityId)}`\n await apiCallOrThrow(url, { method: 'DELETE' }, { errorMessage })\n }\n } catch (err) {\n console.error('query_index.table.vectorAction', err)\n if (typeof window !== 'undefined') {\n const message = err instanceof Error ? err.message : errorMessage\n window.alert(message)\n }\n }\n qc.invalidateQueries({ queryKey: ['query-index-status'] })\n },\n [qc, t],\n )\n\n const triggerFulltext = React.useCallback(\n async (action: 'reindex' | 'purge', entityId: string) => {\n if (action === 'purge' && typeof window !== 'undefined') {\n const confirmed = window.confirm(t('query_index.table.confirm.fulltextPurge'))\n if (!confirmed) return\n }\n\n const actionLabel = action === 'purge'\n ? t('query_index.table.actions.fulltextPurge')\n : t('query_index.table.actions.fulltextReindex')\n const errorMessage = t('query_index.table.errors.actionFailed', { action: actionLabel })\n try {\n await apiCallOrThrow('/api/search/reindex', {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({\n action: action === 'purge' ? 'clear' : 'reindex',\n entityId,\n }),\n }, { errorMessage })\n } catch (err) {\n console.error('query_index.table.fulltextAction', err)\n if (typeof window !== 'undefined') {\n const message = err instanceof Error ? err.message : errorMessage\n window.alert(message)\n }\n }\n qc.invalidateQueries({ queryKey: ['query-index-status'] })\n },\n [qc, t],\n )\n\n return (\n <DataTable\n title={t('query_index.nav.queryIndexes')}\n actions={(\n <>\n <Button\n variant=\"outline\"\n onClick={() => {\n setRefreshSeq((v) => v + 1)\n qc.invalidateQueries({ queryKey: ['query-index-status'] })\n }}\n >\n {t('query_index.table.refresh')}\n </Button>\n </>\n )}\n columns={columns}\n data={rows}\n searchValue={search}\n searchPlaceholder={t('query_index.table.searchPlaceholder')}\n onSearchChange={(value) => {\n setSearch(value)\n setPage(1)\n }}\n sortable\n sorting={sorting}\n onSortingChange={setSorting}\n perspective={{ tableId: 'query_index.status.list' }}\n rowActions={(row) => {\n const items: Array<{ label: string; onSelect: () => void; destructive?: boolean }> = [\n { label: t('query_index.table.actions.reindex'), onSelect: () => trigger('reindex', row.entityId) },\n {\n label: t('query_index.table.actions.reindexForce'),\n onSelect: () => trigger('reindex', row.entityId, { force: true }),\n },\n {\n label: t('query_index.table.actions.purge'),\n destructive: true,\n onSelect: () => trigger('purge', row.entityId),\n },\n ]\n\n if (row.vectorEnabled) {\n items.push(\n {\n label: t('query_index.table.actions.vectorReindex'),\n onSelect: () => triggerVector('reindex', row.entityId),\n },\n {\n label: t('query_index.table.actions.vectorPurge'),\n destructive: true,\n onSelect: () => triggerVector('purge', row.entityId),\n },\n )\n }\n\n if (row.fulltextEnabled) {\n items.push(\n {\n label: t('query_index.table.actions.fulltextReindex'),\n onSelect: () => triggerFulltext('reindex', row.entityId),\n },\n {\n label: t('query_index.table.actions.fulltextPurge'),\n destructive: true,\n onSelect: () => triggerFulltext('purge', row.entityId),\n },\n )\n }\n\n return <RowActions items={items} />\n }}\n pagination={{ page, pageSize: 50, total: rows.length, totalPages: 1, onPageChange: setPage }}\n isLoading={isLoading}\n />\n )\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["\"use client\"\nimport * as React from 'react'\nimport { useQuery, useQueryClient } from '@tanstack/react-query'\nimport type { ColumnDef, SortingState } from '@tanstack/react-table'\nimport { DataTable } from '@open-mercato/ui/backend/DataTable'\nimport { RowActions } from '@open-mercato/ui/backend/RowActions'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { apiCallOrThrow, readApiResultOrThrow } from '@open-mercato/ui/backend/utils/apiCall'\nimport { useOrganizationScopeVersion } from '@open-mercato/shared/lib/frontend/useOrganizationScope'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\n\ntype Translator = (key: string, params?: Record<string, string | number>) => string\n\ntype PartitionStatus = {\n partitionIndex: number | null\n partitionCount: number | null\n status: 'reindexing' | 'purging' | 'stalled' | 'completed'\n processedCount?: number | null\n totalCount?: number | null\n heartbeatAt?: string | null\n startedAt?: string | null\n finishedAt?: string | null\n}\n\ntype JobStatus = {\n status: 'idle' | 'reindexing' | 'purging' | 'stalled'\n startedAt?: string | null\n finishedAt?: string | null\n heartbeatAt?: string | null\n processedCount?: number | null\n totalCount?: number | null\n partitions?: PartitionStatus[]\n scope?: {\n status?: 'reindexing' | 'purging' | 'stalled' | 'completed' | null\n processedCount?: number | null\n totalCount?: number | null\n } | null\n}\n\ntype Row = {\n entityId: string\n label: string\n baseCount: number | null\n indexCount: number | null\n vectorCount: number | null\n vectorEnabled: boolean\n fulltextCount: number | null\n fulltextEnabled: boolean\n ok: boolean\n job?: JobStatus\n}\n\ntype Resp = { items: Row[] }\n\nfunction formatCount(value: number | null): string {\n if (value == null) return '\u2014'\n return value.toLocaleString()\n}\n\nfunction formatNumeric(value: number | null | undefined): string | null {\n if (value == null) return null\n return Number(value).toLocaleString()\n}\n\nfunction formatProgressLabel(\n processed: number | null | undefined,\n total: number | null | undefined,\n t: Translator,\n): string | null {\n const processedText = formatNumeric(processed)\n if (!processedText) return null\n const totalText = formatNumeric(total)\n if (totalText) return t('query_index.table.status.progress', { processed: processedText, total: totalText })\n return t('query_index.table.status.progressSingle', { processed: processedText })\n}\n\nfunction translateJobStatus(t: Translator, status: JobStatus['status'] | undefined, ok: boolean): string {\n if (!status || status === 'idle') {\n return ok ? t('query_index.table.status.in_sync') : t('query_index.table.status.out_of_sync')\n }\n if (status === 'reindexing') return t('query_index.table.status.reindexing')\n if (status === 'purging') return t('query_index.table.status.purging')\n if (status === 'stalled') return t('query_index.table.status.stalled')\n return ok ? t('query_index.table.status.in_sync') : t('query_index.table.status.out_of_sync')\n}\n\nfunction translateScopeStatus(\n t: Translator,\n status: PartitionStatus['status'] | JobStatus['status'] | undefined | null,\n): string {\n if (status === 'reindexing') return t('query_index.table.status.scope.reindexing')\n if (status === 'purging') return t('query_index.table.status.scope.purging')\n if (status === 'stalled') return t('query_index.table.status.scope.stalled')\n return t('query_index.table.status.scope.completed')\n}\n\n\nfunction createColumns(t: Translator): ColumnDef<Row>[] {\n return [\n { id: 'entityId', header: () => t('query_index.table.columns.entity'), accessorKey: 'entityId', meta: { priority: 1 } },\n { id: 'label', header: () => t('query_index.table.columns.label'), accessorKey: 'label', meta: { priority: 2 } },\n {\n id: 'baseCount',\n header: () => t('query_index.table.columns.records'),\n accessorFn: (row) => row.baseCount ?? 0,\n cell: ({ row }) => <span>{formatCount(row.original.baseCount)}</span>,\n meta: { priority: 2 },\n },\n {\n id: 'indexCount',\n header: () => t('query_index.table.columns.indexed'),\n accessorFn: (row) => row.indexCount ?? 0,\n cell: ({ row }) => <span>{formatCount(row.original.indexCount)}</span>,\n meta: { priority: 2 },\n },\n {\n id: 'vectorCount',\n header: () => t('query_index.table.columns.vector'),\n accessorFn: (row) => (row.vectorEnabled ? row.vectorCount ?? 0 : -1),\n cell: ({ row }) => {\n const record = row.original\n if (!record.vectorEnabled) return <span>\u2014</span>\n const ok = record.vectorCount != null && record.baseCount != null && record.vectorCount === record.baseCount\n const display = formatCount(record.vectorCount)\n const className = ok ? 'text-green-600' : 'text-orange-600'\n return <span className={className}>{display}</span>\n },\n meta: { priority: 2 },\n },\n {\n id: 'fulltextCount',\n header: () => t('query_index.table.columns.fulltext'),\n accessorFn: (row) => (row.fulltextEnabled ? row.fulltextCount ?? 0 : -1),\n cell: ({ row }) => {\n const record = row.original\n if (!record.fulltextEnabled) return <span>\u2014</span>\n const ok = record.fulltextCount != null && record.baseCount != null && record.fulltextCount === record.baseCount\n const display = formatCount(record.fulltextCount)\n const className = ok ? 'text-green-600' : 'text-orange-600'\n return <span className={className}>{display}</span>\n },\n meta: { priority: 2 },\n },\n {\n id: 'status',\n header: () => t('query_index.table.columns.status'),\n cell: ({ row }) => {\n const record = row.original\n const job = record.job\n const partitions = job?.partitions ?? []\n const ok = record.ok && (!job || job.status === 'idle')\n const statusText = translateJobStatus(t, job?.status, ok)\n const jobProgress = job ? formatProgressLabel(job.processedCount ?? null, job.totalCount ?? null, t) : null\n const label = jobProgress\n ? t('query_index.table.status.withProgress', { status: statusText, progress: jobProgress })\n : statusText\n const className = job\n ? job.status === 'stalled'\n ? 'text-red-600'\n : job.status === 'reindexing' || job.status === 'purging'\n ? 'text-orange-600'\n : ok\n ? 'text-green-600'\n : 'text-muted-foreground'\n : ok\n ? 'text-green-600'\n : 'text-muted-foreground'\n\n const lines: string[] = []\n\n if (job?.scope && partitions.length <= 1) {\n const scopeStatus = translateScopeStatus(t, job.scope.status ?? null)\n const scopeProgress = formatProgressLabel(job.scope.processedCount ?? null, job.scope.totalCount ?? null, t)\n const scopeLabel = t('query_index.table.status.scopeLabel')\n lines.push(`${scopeLabel}: ${scopeStatus}${scopeProgress ? ` (${scopeProgress})` : ''}`)\n }\n\n if (partitions.length > 1) {\n for (const part of partitions) {\n const partitionLabel =\n part.partitionIndex != null\n ? t('query_index.table.status.partitionLabel', { index: Number(part.partitionIndex) + 1 })\n : t('query_index.table.status.scopeLabel')\n const partitionStatus = translateScopeStatus(t, part.status)\n const partitionProgress = formatProgressLabel(part.processedCount ?? null, part.totalCount ?? null, t)\n lines.push(`${partitionLabel}: ${partitionStatus}${partitionProgress ? ` (${partitionProgress})` : ''}`)\n }\n }\n\n if (record.vectorEnabled) {\n const vectorLabel = t('query_index.table.status.vectorLabel')\n const vectorCount = formatCount(record.vectorCount)\n const vectorTotal = record.baseCount != null ? formatCount(record.baseCount) : null\n const vectorValue = vectorTotal\n ? t('query_index.table.status.vectorValue', { count: vectorCount, total: vectorTotal })\n : vectorCount\n lines.push(`${vectorLabel}: ${vectorValue}`)\n }\n\n return (\n <div className=\"space-y-1\">\n <span className={className}>{label}</span>\n {lines.length > 0 && (\n <div className=\"text-xs text-muted-foreground\">\n {lines.map((line, idx) => (\n <div key={idx}>{line}</div>\n ))}\n </div>\n )}\n </div>\n )\n },\n meta: { priority: 1 },\n },\n ]\n}\n\nexport default function QueryIndexesTable() {\n const [sorting, setSorting] = React.useState<SortingState>([{ id: 'entityId', desc: false }])\n const [page, setPage] = React.useState(1)\n const [search, setSearch] = React.useState('')\n const qc = useQueryClient()\n const scopeVersion = useOrganizationScopeVersion()\n const [refreshSeq, setRefreshSeq] = React.useState(0)\n const t = useT()\n const columns = React.useMemo(() => createColumns(t), [t])\n\n const { data, isLoading } = useQuery<Resp>({\n queryKey: ['query-index-status', scopeVersion, refreshSeq],\n queryFn: async () => {\n const baseUrl = '/api/query_index/status'\n const url = refreshSeq > 0 ? `${baseUrl}?refresh=${refreshSeq}` : baseUrl\n return readApiResultOrThrow<Resp>(\n url,\n undefined,\n { errorMessage: t('query_index.table.errors.loadFailed') },\n )\n },\n refetchInterval: 4000,\n })\n\n const rowsAll = data?.items || []\n const rows = React.useMemo(() => {\n if (!search) return rowsAll\n const q = search.toLowerCase()\n return rowsAll.filter((r) => r.entityId.toLowerCase().includes(q) || r.label.toLowerCase().includes(q))\n }, [rowsAll, search])\n\n const trigger = React.useCallback(\n async (action: 'reindex' | 'purge', entityId: string, opts?: { force?: boolean }) => {\n const body: Record<string, unknown> = { entityType: entityId }\n if (opts?.force) body.force = true\n const actionLabel =\n action === 'purge' ? t('query_index.table.actions.purge') : t('query_index.table.actions.reindex')\n const errorMessage = t('query_index.table.errors.actionFailed', { action: actionLabel })\n try {\n await apiCallOrThrow(`/api/query_index/${action}`, {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify(body),\n }, { errorMessage })\n } catch (err) {\n console.error('query_index.table.trigger', err)\n if (typeof window !== 'undefined') {\n const message = err instanceof Error ? err.message : errorMessage\n window.alert(message)\n }\n }\n qc.invalidateQueries({ queryKey: ['query-index-status'] })\n },\n [qc, t],\n )\n\n const triggerVector = React.useCallback(\n async (action: 'reindex' | 'purge', entityId: string) => {\n if (action === 'purge' && typeof window !== 'undefined') {\n const confirmed = window.confirm(t('query_index.table.confirm.vectorPurge'))\n if (!confirmed) return\n }\n\n const actionLabel = action === 'purge'\n ? t('query_index.table.actions.vectorPurge')\n : t('query_index.table.actions.vectorReindex')\n const errorMessage = t('query_index.table.errors.actionFailed', { action: actionLabel })\n try {\n if (action === 'reindex') {\n await apiCallOrThrow('/api/vector/reindex', {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ entityId }),\n }, { errorMessage })\n } else {\n const url = `/api/vector/index?entityId=${encodeURIComponent(entityId)}`\n await apiCallOrThrow(url, { method: 'DELETE' }, { errorMessage })\n }\n } catch (err) {\n console.error('query_index.table.vectorAction', err)\n if (typeof window !== 'undefined') {\n const message = err instanceof Error ? err.message : errorMessage\n window.alert(message)\n }\n }\n qc.invalidateQueries({ queryKey: ['query-index-status'] })\n },\n [qc, t],\n )\n\n const triggerFulltext = React.useCallback(\n async (action: 'reindex' | 'purge', entityId: string) => {\n if (action === 'purge' && typeof window !== 'undefined') {\n const confirmed = window.confirm(t('query_index.table.confirm.fulltextPurge'))\n if (!confirmed) return\n }\n\n const actionLabel = action === 'purge'\n ? t('query_index.table.actions.fulltextPurge')\n : t('query_index.table.actions.fulltextReindex')\n const errorMessage = t('query_index.table.errors.actionFailed', { action: actionLabel })\n try {\n await apiCallOrThrow('/api/search/reindex', {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({\n action: action === 'purge' ? 'clear' : 'reindex',\n entityId,\n }),\n }, { errorMessage })\n } catch (err) {\n console.error('query_index.table.fulltextAction', err)\n if (typeof window !== 'undefined') {\n const message = err instanceof Error ? err.message : errorMessage\n window.alert(message)\n }\n }\n qc.invalidateQueries({ queryKey: ['query-index-status'] })\n },\n [qc, t],\n )\n\n return (\n <DataTable\n title={t('query_index.nav.queryIndexes')}\n actions={(\n <>\n <Button\n variant=\"outline\"\n onClick={() => {\n setRefreshSeq((v) => v + 1)\n qc.invalidateQueries({ queryKey: ['query-index-status'] })\n }}\n >\n {t('query_index.table.refresh')}\n </Button>\n </>\n )}\n columns={columns}\n data={rows}\n searchValue={search}\n searchPlaceholder={t('query_index.table.searchPlaceholder')}\n onSearchChange={(value) => {\n setSearch(value)\n setPage(1)\n }}\n sortable\n sorting={sorting}\n onSortingChange={setSorting}\n perspective={{ tableId: 'query_index.status.list' }}\n rowActions={(row) => {\n const items: Array<{ id: string; label: string; onSelect: () => void; destructive?: boolean }> = [\n { id: 'reindex', label: t('query_index.table.actions.reindex'), onSelect: () => trigger('reindex', row.entityId) },\n {\n id: 'reindex-force',\n label: t('query_index.table.actions.reindexForce'),\n onSelect: () => trigger('reindex', row.entityId, { force: true }),\n },\n {\n id: 'purge',\n label: t('query_index.table.actions.purge'),\n destructive: true,\n onSelect: () => trigger('purge', row.entityId),\n },\n ]\n\n if (row.vectorEnabled) {\n items.push(\n {\n id: 'vector-reindex',\n label: t('query_index.table.actions.vectorReindex'),\n onSelect: () => triggerVector('reindex', row.entityId),\n },\n {\n id: 'vector-purge',\n label: t('query_index.table.actions.vectorPurge'),\n destructive: true,\n onSelect: () => triggerVector('purge', row.entityId),\n },\n )\n }\n\n if (row.fulltextEnabled) {\n items.push(\n {\n id: 'fulltext-reindex',\n label: t('query_index.table.actions.fulltextReindex'),\n onSelect: () => triggerFulltext('reindex', row.entityId),\n },\n {\n id: 'fulltext-purge',\n label: t('query_index.table.actions.fulltextPurge'),\n destructive: true,\n onSelect: () => triggerFulltext('purge', row.entityId),\n },\n )\n }\n\n return <RowActions items={items} />\n }}\n pagination={{ page, pageSize: 50, total: rows.length, totalPages: 1, onPageChange: setPage }}\n isLoading={isLoading}\n />\n )\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -216,8 +216,8 @@ function ResourcesResourceTypesPage() {
216
216
  RowActions,
217
217
  {
218
218
  items: [
219
- { label: translations.actions.edit, href: `/backend/resources/resource-types/${row.id}/edit` },
220
- ...row.resourceCount > 0 ? [] : [{ label: translations.actions.delete, destructive: true, onSelect: () => handleDelete(row) }]
219
+ { id: "edit", label: translations.actions.edit, href: `/backend/resources/resource-types/${row.id}/edit` },
220
+ ...row.resourceCount > 0 ? [] : [{ id: "delete", label: translations.actions.delete, destructive: true, onSelect: () => handleDelete(row) }]
221
221
  ]
222
222
  }
223
223
  ),