@open-mercato/core 0.4.5-develop-5191db4ef3 → 0.4.5-develop-033a719bf2

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 (385) hide show
  1. package/dist/generated/entities/message/index.js +65 -0
  2. package/dist/generated/entities/message/index.js.map +7 -0
  3. package/dist/generated/entities/message_access_token/index.js +19 -0
  4. package/dist/generated/entities/message_access_token/index.js.map +7 -0
  5. package/dist/generated/entities/message_confirmation/index.js +21 -0
  6. package/dist/generated/entities/message_confirmation/index.js.map +7 -0
  7. package/dist/generated/entities/message_object/index.js +23 -0
  8. package/dist/generated/entities/message_object/index.js.map +7 -0
  9. package/dist/generated/entities/message_recipient/index.js +31 -0
  10. package/dist/generated/entities/message_recipient/index.js.map +7 -0
  11. package/dist/generated/entities.ids.generated.js +8 -0
  12. package/dist/generated/entities.ids.generated.js.map +2 -2
  13. package/dist/generated/entity-fields-registry.js +10 -0
  14. package/dist/generated/entity-fields-registry.js.map +2 -2
  15. package/dist/modules/customers/backend/customers/deals/[id]/page.js +27 -8
  16. package/dist/modules/customers/backend/customers/deals/[id]/page.js.map +2 -2
  17. package/dist/modules/customers/lib/messageObjectPreviews.js +131 -0
  18. package/dist/modules/customers/lib/messageObjectPreviews.js.map +7 -0
  19. package/dist/modules/customers/message-objects.js +71 -0
  20. package/dist/modules/customers/message-objects.js.map +7 -0
  21. package/dist/modules/customers/widgets/messages/CustomerMessageObjectDetail.js +51 -0
  22. package/dist/modules/customers/widgets/messages/CustomerMessageObjectDetail.js.map +7 -0
  23. package/dist/modules/customers/widgets/messages/CustomerMessageObjectPreview.js +35 -0
  24. package/dist/modules/customers/widgets/messages/CustomerMessageObjectPreview.js.map +7 -0
  25. package/dist/modules/customers/widgets/messages/index.js +7 -0
  26. package/dist/modules/customers/widgets/messages/index.js.map +7 -0
  27. package/dist/modules/messages/acl.js +15 -0
  28. package/dist/modules/messages/acl.js.map +7 -0
  29. package/dist/modules/messages/api/[id]/actions/[actionId]/route.js +92 -0
  30. package/dist/modules/messages/api/[id]/actions/[actionId]/route.js.map +7 -0
  31. package/dist/modules/messages/api/[id]/archive/route.js +120 -0
  32. package/dist/modules/messages/api/[id]/archive/route.js.map +7 -0
  33. package/dist/modules/messages/api/[id]/attachments/route.js +195 -0
  34. package/dist/modules/messages/api/[id]/attachments/route.js.map +7 -0
  35. package/dist/modules/messages/api/[id]/confirmation/route.js +67 -0
  36. package/dist/modules/messages/api/[id]/confirmation/route.js.map +7 -0
  37. package/dist/modules/messages/api/[id]/conversation/archive/route.js +68 -0
  38. package/dist/modules/messages/api/[id]/conversation/archive/route.js.map +7 -0
  39. package/dist/modules/messages/api/[id]/conversation/read/route.js +68 -0
  40. package/dist/modules/messages/api/[id]/conversation/read/route.js.map +7 -0
  41. package/dist/modules/messages/api/[id]/conversation/route.js +68 -0
  42. package/dist/modules/messages/api/[id]/conversation/route.js.map +7 -0
  43. package/dist/modules/messages/api/[id]/forward/route.js +85 -0
  44. package/dist/modules/messages/api/[id]/forward/route.js.map +7 -0
  45. package/dist/modules/messages/api/[id]/forward-preview/route.js +70 -0
  46. package/dist/modules/messages/api/[id]/forward-preview/route.js.map +7 -0
  47. package/dist/modules/messages/api/[id]/read/route.js +120 -0
  48. package/dist/modules/messages/api/[id]/read/route.js.map +7 -0
  49. package/dist/modules/messages/api/[id]/reply/route.js +87 -0
  50. package/dist/modules/messages/api/[id]/reply/route.js.map +7 -0
  51. package/dist/modules/messages/api/[id]/route.js +350 -0
  52. package/dist/modules/messages/api/[id]/route.js.map +7 -0
  53. package/dist/modules/messages/api/object-types/route.js +54 -0
  54. package/dist/modules/messages/api/object-types/route.js.map +7 -0
  55. package/dist/modules/messages/api/openapi.js +261 -0
  56. package/dist/modules/messages/api/openapi.js.map +7 -0
  57. package/dist/modules/messages/api/route.js +262 -0
  58. package/dist/modules/messages/api/route.js.map +7 -0
  59. package/dist/modules/messages/api/token/[token]/route.js +99 -0
  60. package/dist/modules/messages/api/token/[token]/route.js.map +7 -0
  61. package/dist/modules/messages/api/types/route.js +40 -0
  62. package/dist/modules/messages/api/types/route.js.map +7 -0
  63. package/dist/modules/messages/api/unread-count/route.js +43 -0
  64. package/dist/modules/messages/api/unread-count/route.js.map +7 -0
  65. package/dist/modules/messages/backend/messages/[id]/page.js +10 -0
  66. package/dist/modules/messages/backend/messages/[id]/page.js.map +7 -0
  67. package/dist/modules/messages/backend/messages/[id]/page.meta.js +16 -0
  68. package/dist/modules/messages/backend/messages/[id]/page.meta.js.map +7 -0
  69. package/dist/modules/messages/backend/messages/compose/page.js +10 -0
  70. package/dist/modules/messages/backend/messages/compose/page.js.map +7 -0
  71. package/dist/modules/messages/backend/messages/compose/page.meta.js +17 -0
  72. package/dist/modules/messages/backend/messages/compose/page.meta.js.map +7 -0
  73. package/dist/modules/messages/backend/page.js +10 -0
  74. package/dist/modules/messages/backend/page.js.map +7 -0
  75. package/dist/modules/messages/backend/page.meta.js +33 -0
  76. package/dist/modules/messages/backend/page.meta.js.map +7 -0
  77. package/dist/modules/messages/commands/actions.js +265 -0
  78. package/dist/modules/messages/commands/actions.js.map +7 -0
  79. package/dist/modules/messages/commands/attachments.js +217 -0
  80. package/dist/modules/messages/commands/attachments.js.map +7 -0
  81. package/dist/modules/messages/commands/confirmations.js +151 -0
  82. package/dist/modules/messages/commands/confirmations.js.map +7 -0
  83. package/dist/modules/messages/commands/conversation.js +240 -0
  84. package/dist/modules/messages/commands/conversation.js.map +7 -0
  85. package/dist/modules/messages/commands/messages.js +748 -0
  86. package/dist/modules/messages/commands/messages.js.map +7 -0
  87. package/dist/modules/messages/commands/recipients.js +259 -0
  88. package/dist/modules/messages/commands/recipients.js.map +7 -0
  89. package/dist/modules/messages/commands/shared.js +258 -0
  90. package/dist/modules/messages/commands/shared.js.map +7 -0
  91. package/dist/modules/messages/commands/tokens.js +69 -0
  92. package/dist/modules/messages/commands/tokens.js.map +7 -0
  93. package/dist/modules/messages/components/ComposeMessagePageClient.js +24 -0
  94. package/dist/modules/messages/components/ComposeMessagePageClient.js.map +7 -0
  95. package/dist/modules/messages/components/MessageDetailPageClient.js +261 -0
  96. package/dist/modules/messages/components/MessageDetailPageClient.js.map +7 -0
  97. package/dist/modules/messages/components/MessagesInboxPageClient.js +390 -0
  98. package/dist/modules/messages/components/MessagesInboxPageClient.js.map +7 -0
  99. package/dist/modules/messages/components/confirmation/MessageConfirmationActions.js +31 -0
  100. package/dist/modules/messages/components/confirmation/MessageConfirmationActions.js.map +7 -0
  101. package/dist/modules/messages/components/confirmation/MessageConfirmationContent.js +69 -0
  102. package/dist/modules/messages/components/confirmation/MessageConfirmationContent.js.map +7 -0
  103. package/dist/modules/messages/components/defaults/DefaultMessageActions.js +31 -0
  104. package/dist/modules/messages/components/defaults/DefaultMessageActions.js.map +7 -0
  105. package/dist/modules/messages/components/defaults/DefaultMessageContent.js +19 -0
  106. package/dist/modules/messages/components/defaults/DefaultMessageContent.js.map +7 -0
  107. package/dist/modules/messages/components/defaults/DefaultMessageListItem.js +90 -0
  108. package/dist/modules/messages/components/defaults/DefaultMessageListItem.js.map +7 -0
  109. package/dist/modules/messages/components/defaults/MessageRecordObjectDetail.js +86 -0
  110. package/dist/modules/messages/components/defaults/MessageRecordObjectDetail.js.map +7 -0
  111. package/dist/modules/messages/components/defaults/MessageRecordObjectPreview.js +61 -0
  112. package/dist/modules/messages/components/defaults/MessageRecordObjectPreview.js.map +7 -0
  113. package/dist/modules/messages/components/message-detail/detail-panels.js +27 -0
  114. package/dist/modules/messages/components/message-detail/detail-panels.js.map +7 -0
  115. package/dist/modules/messages/components/message-detail/hooks/useMessageDetails.js +52 -0
  116. package/dist/modules/messages/components/message-detail/hooks/useMessageDetails.js.map +7 -0
  117. package/dist/modules/messages/components/message-detail/hooks/useMessageDetailsActions.js +289 -0
  118. package/dist/modules/messages/components/message-detail/hooks/useMessageDetailsActions.js.map +7 -0
  119. package/dist/modules/messages/components/message-detail/hooks/useMessageDetailsConversation.js +103 -0
  120. package/dist/modules/messages/components/message-detail/hooks/useMessageDetailsConversation.js.map +7 -0
  121. package/dist/modules/messages/components/message-detail/hooks/useMessageDetailsQueries.js +78 -0
  122. package/dist/modules/messages/components/message-detail/hooks/useMessageDetailsQueries.js.map +7 -0
  123. package/dist/modules/messages/components/message-detail/panels/MainMessageHeader.js +94 -0
  124. package/dist/modules/messages/components/message-detail/panels/MainMessageHeader.js.map +7 -0
  125. package/dist/modules/messages/components/message-detail/panels/MessageHeader.js +110 -0
  126. package/dist/modules/messages/components/message-detail/panels/MessageHeader.js.map +7 -0
  127. package/dist/modules/messages/components/message-detail/panels/MessageListComponent.js +58 -0
  128. package/dist/modules/messages/components/message-detail/panels/MessageListComponent.js.map +7 -0
  129. package/dist/modules/messages/components/message-detail/panels/actions-panel.js +51 -0
  130. package/dist/modules/messages/components/message-detail/panels/actions-panel.js.map +7 -0
  131. package/dist/modules/messages/components/message-detail/panels/attachments-panel.js +66 -0
  132. package/dist/modules/messages/components/message-detail/panels/attachments-panel.js.map +7 -0
  133. package/dist/modules/messages/components/message-detail/panels/body-panel.js +20 -0
  134. package/dist/modules/messages/components/message-detail/panels/body-panel.js.map +7 -0
  135. package/dist/modules/messages/components/message-detail/panels/composer-dialogs.js +36 -0
  136. package/dist/modules/messages/components/message-detail/panels/composer-dialogs.js.map +7 -0
  137. package/dist/modules/messages/components/message-detail/panels/dialogs.js +96 -0
  138. package/dist/modules/messages/components/message-detail/panels/dialogs.js.map +7 -0
  139. package/dist/modules/messages/components/message-detail/panels/index.js +25 -0
  140. package/dist/modules/messages/components/message-detail/panels/index.js.map +7 -0
  141. package/dist/modules/messages/components/message-detail/panels/meta-panel.js +14 -0
  142. package/dist/modules/messages/components/message-detail/panels/meta-panel.js.map +7 -0
  143. package/dist/modules/messages/components/message-detail/panels/objects-panel.js +51 -0
  144. package/dist/modules/messages/components/message-detail/panels/objects-panel.js.map +7 -0
  145. package/dist/modules/messages/components/message-detail/panels/thread-panel.js +54 -0
  146. package/dist/modules/messages/components/message-detail/panels/thread-panel.js.map +7 -0
  147. package/dist/modules/messages/components/message-detail/types.js +1 -0
  148. package/dist/modules/messages/components/message-detail/types.js.map +7 -0
  149. package/dist/modules/messages/components/message-detail/utils.js +54 -0
  150. package/dist/modules/messages/components/message-detail/utils.js.map +7 -0
  151. package/dist/modules/messages/components/utils/PriorityBadge.js +52 -0
  152. package/dist/modules/messages/components/utils/PriorityBadge.js.map +7 -0
  153. package/dist/modules/messages/components/utils/typeUiRegistry.js +77 -0
  154. package/dist/modules/messages/components/utils/typeUiRegistry.js.map +7 -0
  155. package/dist/modules/messages/data/entities.js +309 -0
  156. package/dist/modules/messages/data/entities.js.map +7 -0
  157. package/dist/modules/messages/data/validators.js +272 -0
  158. package/dist/modules/messages/data/validators.js.map +7 -0
  159. package/dist/modules/messages/emails/MessageEmail.js +108 -0
  160. package/dist/modules/messages/emails/MessageEmail.js.map +7 -0
  161. package/dist/modules/messages/events.js +24 -0
  162. package/dist/modules/messages/events.js.map +7 -0
  163. package/dist/modules/messages/frontend/messages/view/[token]/page.js +247 -0
  164. package/dist/modules/messages/frontend/messages/view/[token]/page.js.map +7 -0
  165. package/dist/modules/messages/frontend/messages/view/[token]/page.meta.js +9 -0
  166. package/dist/modules/messages/frontend/messages/view/[token]/page.meta.js.map +7 -0
  167. package/dist/modules/messages/index.js +21 -0
  168. package/dist/modules/messages/index.js.map +7 -0
  169. package/dist/modules/messages/lib/actions.js +141 -0
  170. package/dist/modules/messages/lib/actions.js.map +7 -0
  171. package/dist/modules/messages/lib/attachments.js +131 -0
  172. package/dist/modules/messages/lib/attachments.js.map +7 -0
  173. package/dist/modules/messages/lib/constants.js +7 -0
  174. package/dist/modules/messages/lib/constants.js.map +7 -0
  175. package/dist/modules/messages/lib/email-sender.js +201 -0
  176. package/dist/modules/messages/lib/email-sender.js.map +7 -0
  177. package/dist/modules/messages/lib/forwarding.js +179 -0
  178. package/dist/modules/messages/lib/forwarding.js.map +7 -0
  179. package/dist/modules/messages/lib/message-objects-registry.js +49 -0
  180. package/dist/modules/messages/lib/message-objects-registry.js.map +7 -0
  181. package/dist/modules/messages/lib/message-types-registry.js +41 -0
  182. package/dist/modules/messages/lib/message-types-registry.js.map +7 -0
  183. package/dist/modules/messages/lib/object-validation.js +20 -0
  184. package/dist/modules/messages/lib/object-validation.js.map +7 -0
  185. package/dist/modules/messages/lib/operationMetadata.js +21 -0
  186. package/dist/modules/messages/lib/operationMetadata.js.map +7 -0
  187. package/dist/modules/messages/lib/priorityUtils.js +61 -0
  188. package/dist/modules/messages/lib/priorityUtils.js.map +7 -0
  189. package/dist/modules/messages/lib/routeHelpers.js +44 -0
  190. package/dist/modules/messages/lib/routeHelpers.js.map +7 -0
  191. package/dist/modules/messages/message-objects.js +7 -0
  192. package/dist/modules/messages/message-objects.js.map +7 -0
  193. package/dist/modules/messages/message-types.js +67 -0
  194. package/dist/modules/messages/message-types.js.map +7 -0
  195. package/dist/modules/messages/migrations/Migration20260213181243.js +31 -0
  196. package/dist/modules/messages/migrations/Migration20260213181243.js.map +7 -0
  197. package/dist/modules/messages/migrations/Migration20260215165126.js +16 -0
  198. package/dist/modules/messages/migrations/Migration20260215165126.js.map +7 -0
  199. package/dist/modules/messages/notifications.js +27 -0
  200. package/dist/modules/messages/notifications.js.map +7 -0
  201. package/dist/modules/messages/setup.js +21 -0
  202. package/dist/modules/messages/setup.js.map +7 -0
  203. package/dist/modules/messages/subscribers/message-notification.js +108 -0
  204. package/dist/modules/messages/subscribers/message-notification.js.map +7 -0
  205. package/dist/modules/messages/workers/send-email.worker.js +253 -0
  206. package/dist/modules/messages/workers/send-email.worker.js.map +7 -0
  207. package/dist/modules/sales/backend/sales/documents/[id]/page.js +30 -11
  208. package/dist/modules/sales/backend/sales/documents/[id]/page.js.map +2 -2
  209. package/dist/modules/sales/commands/payments.js +12 -6
  210. package/dist/modules/sales/commands/payments.js.map +2 -2
  211. package/dist/modules/sales/lib/messageObjectPreviews.js +114 -0
  212. package/dist/modules/sales/lib/messageObjectPreviews.js.map +7 -0
  213. package/dist/modules/sales/message-objects.js +57 -0
  214. package/dist/modules/sales/message-objects.js.map +7 -0
  215. package/dist/modules/sales/widgets/messages/SalesDocumentMessageDetail.js +51 -0
  216. package/dist/modules/sales/widgets/messages/SalesDocumentMessageDetail.js.map +7 -0
  217. package/dist/modules/sales/widgets/messages/SalesDocumentMessagePreview.js +36 -0
  218. package/dist/modules/sales/widgets/messages/SalesDocumentMessagePreview.js.map +7 -0
  219. package/dist/modules/sales/widgets/messages/index.js +7 -0
  220. package/dist/modules/sales/widgets/messages/index.js.map +7 -0
  221. package/dist/modules/staff/backend/staff/leave-requests/[id]/page.js +55 -1
  222. package/dist/modules/staff/backend/staff/leave-requests/[id]/page.js.map +2 -2
  223. package/dist/modules/staff/backend/staff/my-leave-requests/[id]/page.js +60 -1
  224. package/dist/modules/staff/backend/staff/my-leave-requests/[id]/page.js.map +2 -2
  225. package/dist/modules/staff/backend/staff/team-members/[id]/page.js +2 -19
  226. package/dist/modules/staff/backend/staff/team-members/[id]/page.js.map +2 -2
  227. package/dist/modules/staff/components/LeaveRequestDetail.js +112 -0
  228. package/dist/modules/staff/components/LeaveRequestDetail.js.map +7 -0
  229. package/dist/modules/staff/components/LeaveRequestForm.js +3 -1
  230. package/dist/modules/staff/components/LeaveRequestForm.js.map +2 -2
  231. package/dist/modules/staff/components/LeaveRequestPreview.js +43 -0
  232. package/dist/modules/staff/components/LeaveRequestPreview.js.map +7 -0
  233. package/dist/modules/staff/lib/messageObjectPreviews.js +148 -0
  234. package/dist/modules/staff/lib/messageObjectPreviews.js.map +7 -0
  235. package/dist/modules/staff/message-objects.js +104 -0
  236. package/dist/modules/staff/message-objects.js.map +7 -0
  237. package/dist/modules/staff/message-types.js +23 -0
  238. package/dist/modules/staff/message-types.js.map +7 -0
  239. package/dist/modules/staff/widgets/messages/StaffMessageObjectDetail.js +51 -0
  240. package/dist/modules/staff/widgets/messages/StaffMessageObjectDetail.js.map +7 -0
  241. package/dist/modules/staff/widgets/messages/StaffMessageObjectPreview.js +34 -0
  242. package/dist/modules/staff/widgets/messages/StaffMessageObjectPreview.js.map +7 -0
  243. package/dist/modules/staff/widgets/messages/index.js +7 -0
  244. package/dist/modules/staff/widgets/messages/index.js.map +7 -0
  245. package/generated/entities/message/index.ts +31 -0
  246. package/generated/entities/message_access_token/index.ts +8 -0
  247. package/generated/entities/message_confirmation/index.ts +9 -0
  248. package/generated/entities/message_object/index.ts +10 -0
  249. package/generated/entities/message_recipient/index.ts +14 -0
  250. package/generated/entities.ids.generated.ts +8 -0
  251. package/generated/entity-fields-registry.ts +10 -0
  252. package/jest.setup.ts +5 -0
  253. package/package.json +2 -2
  254. package/src/modules/customers/backend/customers/deals/[id]/page.tsx +20 -4
  255. package/src/modules/customers/i18n/de.json +4 -0
  256. package/src/modules/customers/i18n/en.json +4 -0
  257. package/src/modules/customers/i18n/es.json +4 -0
  258. package/src/modules/customers/i18n/pl.json +4 -0
  259. package/src/modules/customers/lib/messageObjectPreviews.ts +154 -0
  260. package/src/modules/customers/message-objects.ts +70 -0
  261. package/src/modules/customers/widgets/messages/CustomerMessageObjectDetail.tsx +57 -0
  262. package/src/modules/customers/widgets/messages/CustomerMessageObjectPreview.tsx +49 -0
  263. package/src/modules/customers/widgets/messages/index.ts +2 -0
  264. package/src/modules/messages/acl.ts +11 -0
  265. package/src/modules/messages/api/[id]/actions/[actionId]/route.ts +103 -0
  266. package/src/modules/messages/api/[id]/archive/route.ts +138 -0
  267. package/src/modules/messages/api/[id]/attachments/route.ts +217 -0
  268. package/src/modules/messages/api/[id]/confirmation/route.ts +73 -0
  269. package/src/modules/messages/api/[id]/conversation/archive/route.ts +69 -0
  270. package/src/modules/messages/api/[id]/conversation/read/route.ts +69 -0
  271. package/src/modules/messages/api/[id]/conversation/route.ts +69 -0
  272. package/src/modules/messages/api/[id]/forward/route.ts +87 -0
  273. package/src/modules/messages/api/[id]/forward-preview/route.ts +75 -0
  274. package/src/modules/messages/api/[id]/read/route.ts +138 -0
  275. package/src/modules/messages/api/[id]/reply/route.ts +89 -0
  276. package/src/modules/messages/api/[id]/route.ts +401 -0
  277. package/src/modules/messages/api/object-types/route.ts +54 -0
  278. package/src/modules/messages/api/openapi.ts +261 -0
  279. package/src/modules/messages/api/route.ts +374 -0
  280. package/src/modules/messages/api/token/[token]/route.ts +103 -0
  281. package/src/modules/messages/api/types/route.ts +39 -0
  282. package/src/modules/messages/api/unread-count/route.ts +55 -0
  283. package/src/modules/messages/backend/messages/[id]/page.meta.ts +12 -0
  284. package/src/modules/messages/backend/messages/[id]/page.tsx +12 -0
  285. package/src/modules/messages/backend/messages/compose/page.meta.ts +13 -0
  286. package/src/modules/messages/backend/messages/compose/page.tsx +12 -0
  287. package/src/modules/messages/backend/page.meta.ts +31 -0
  288. package/src/modules/messages/backend/page.tsx +12 -0
  289. package/src/modules/messages/commands/actions.ts +307 -0
  290. package/src/modules/messages/commands/attachments.ts +227 -0
  291. package/src/modules/messages/commands/confirmations.ts +183 -0
  292. package/src/modules/messages/commands/conversation.ts +292 -0
  293. package/src/modules/messages/commands/messages.ts +845 -0
  294. package/src/modules/messages/commands/recipients.ts +281 -0
  295. package/src/modules/messages/commands/shared.ts +350 -0
  296. package/src/modules/messages/commands/tokens.ts +80 -0
  297. package/src/modules/messages/components/ComposeMessagePageClient.tsx +23 -0
  298. package/src/modules/messages/components/MessageDetailPageClient.tsx +287 -0
  299. package/src/modules/messages/components/MessagesInboxPageClient.tsx +469 -0
  300. package/src/modules/messages/components/confirmation/MessageConfirmationActions.tsx +35 -0
  301. package/src/modules/messages/components/confirmation/MessageConfirmationContent.tsx +88 -0
  302. package/src/modules/messages/components/defaults/DefaultMessageActions.tsx +37 -0
  303. package/src/modules/messages/components/defaults/DefaultMessageContent.tsx +21 -0
  304. package/src/modules/messages/components/defaults/DefaultMessageListItem.tsx +102 -0
  305. package/src/modules/messages/components/defaults/MessageRecordObjectDetail.tsx +114 -0
  306. package/src/modules/messages/components/defaults/MessageRecordObjectPreview.tsx +74 -0
  307. package/src/modules/messages/components/message-detail/detail-panels.ts +13 -0
  308. package/src/modules/messages/components/message-detail/hooks/useMessageDetails.ts +56 -0
  309. package/src/modules/messages/components/message-detail/hooks/useMessageDetailsActions.ts +367 -0
  310. package/src/modules/messages/components/message-detail/hooks/useMessageDetailsConversation.ts +134 -0
  311. package/src/modules/messages/components/message-detail/hooks/useMessageDetailsQueries.ts +102 -0
  312. package/src/modules/messages/components/message-detail/panels/MainMessageHeader.tsx +108 -0
  313. package/src/modules/messages/components/message-detail/panels/MessageHeader.tsx +144 -0
  314. package/src/modules/messages/components/message-detail/panels/MessageListComponent.tsx +63 -0
  315. package/src/modules/messages/components/message-detail/panels/actions-panel.tsx +66 -0
  316. package/src/modules/messages/components/message-detail/panels/attachments-panel.tsx +86 -0
  317. package/src/modules/messages/components/message-detail/panels/body-panel.tsx +32 -0
  318. package/src/modules/messages/components/message-detail/panels/composer-dialogs.tsx +42 -0
  319. package/src/modules/messages/components/message-detail/panels/dialogs.tsx +107 -0
  320. package/src/modules/messages/components/message-detail/panels/index.ts +11 -0
  321. package/src/modules/messages/components/message-detail/panels/meta-panel.tsx +19 -0
  322. package/src/modules/messages/components/message-detail/panels/objects-panel.tsx +65 -0
  323. package/src/modules/messages/components/message-detail/panels/thread-panel.tsx +65 -0
  324. package/src/modules/messages/components/message-detail/types.ts +114 -0
  325. package/src/modules/messages/components/message-detail/utils.ts +62 -0
  326. package/src/modules/messages/components/utils/PriorityBadge.tsx +63 -0
  327. package/src/modules/messages/components/utils/typeUiRegistry.ts +106 -0
  328. package/src/modules/messages/data/entities.ts +284 -0
  329. package/src/modules/messages/data/validators.ts +297 -0
  330. package/src/modules/messages/emails/MessageEmail.tsx +143 -0
  331. package/src/modules/messages/events.ts +24 -0
  332. package/src/modules/messages/frontend/messages/view/[token]/page.meta.ts +5 -0
  333. package/src/modules/messages/frontend/messages/view/[token]/page.tsx +389 -0
  334. package/src/modules/messages/i18n/de.json +240 -0
  335. package/src/modules/messages/i18n/en.json +240 -0
  336. package/src/modules/messages/i18n/es.json +240 -0
  337. package/src/modules/messages/i18n/pl.json +240 -0
  338. package/src/modules/messages/index.ts +19 -0
  339. package/src/modules/messages/lib/actions.ts +204 -0
  340. package/src/modules/messages/lib/attachments.ts +197 -0
  341. package/src/modules/messages/lib/constants.ts +2 -0
  342. package/src/modules/messages/lib/email-sender.ts +255 -0
  343. package/src/modules/messages/lib/forwarding.ts +240 -0
  344. package/src/modules/messages/lib/message-objects-registry.ts +60 -0
  345. package/src/modules/messages/lib/message-types-registry.ts +48 -0
  346. package/src/modules/messages/lib/object-validation.ts +26 -0
  347. package/src/modules/messages/lib/operationMetadata.ts +43 -0
  348. package/src/modules/messages/lib/priorityUtils.ts +76 -0
  349. package/src/modules/messages/lib/routeHelpers.ts +65 -0
  350. package/src/modules/messages/message-objects.ts +5 -0
  351. package/src/modules/messages/message-types.ts +65 -0
  352. package/src/modules/messages/migrations/.snapshot-open-mercato.json +957 -0
  353. package/src/modules/messages/migrations/Migration20260213181243.ts +34 -0
  354. package/src/modules/messages/migrations/Migration20260215165126.ts +16 -0
  355. package/src/modules/messages/notifications.ts +25 -0
  356. package/src/modules/messages/setup.ts +19 -0
  357. package/src/modules/messages/subscribers/message-notification.ts +138 -0
  358. package/src/modules/messages/workers/send-email.worker.ts +321 -0
  359. package/src/modules/sales/backend/sales/documents/[id]/page.tsx +23 -7
  360. package/src/modules/sales/commands/payments.ts +12 -6
  361. package/src/modules/sales/i18n/de.json +3 -0
  362. package/src/modules/sales/i18n/en.json +3 -0
  363. package/src/modules/sales/i18n/es.json +3 -0
  364. package/src/modules/sales/i18n/pl.json +3 -0
  365. package/src/modules/sales/lib/messageObjectPreviews.ts +150 -0
  366. package/src/modules/sales/message-objects.ts +56 -0
  367. package/src/modules/sales/widgets/messages/SalesDocumentMessageDetail.tsx +57 -0
  368. package/src/modules/sales/widgets/messages/SalesDocumentMessagePreview.tsx +46 -0
  369. package/src/modules/sales/widgets/messages/index.ts +2 -0
  370. package/src/modules/staff/backend/staff/leave-requests/[id]/page.tsx +54 -0
  371. package/src/modules/staff/backend/staff/my-leave-requests/[id]/page.tsx +58 -0
  372. package/src/modules/staff/backend/staff/team-members/[id]/page.tsx +2 -32
  373. package/src/modules/staff/components/LeaveRequestDetail.tsx +135 -0
  374. package/src/modules/staff/components/LeaveRequestForm.tsx +3 -0
  375. package/src/modules/staff/components/LeaveRequestPreview.tsx +74 -0
  376. package/src/modules/staff/i18n/de.json +8 -0
  377. package/src/modules/staff/i18n/en.json +8 -0
  378. package/src/modules/staff/i18n/es.json +8 -0
  379. package/src/modules/staff/i18n/pl.json +8 -0
  380. package/src/modules/staff/lib/messageObjectPreviews.ts +182 -0
  381. package/src/modules/staff/message-objects.ts +102 -0
  382. package/src/modules/staff/message-types.ts +21 -0
  383. package/src/modules/staff/widgets/messages/StaffMessageObjectDetail.tsx +57 -0
  384. package/src/modules/staff/widgets/messages/StaffMessageObjectPreview.tsx +44 -0
  385. package/src/modules/staff/widgets/messages/index.ts +2 -0
@@ -0,0 +1,34 @@
1
+ import { Migration } from '@mikro-orm/migrations';
2
+
3
+ export class Migration20260213181243 extends Migration {
4
+
5
+ override async up(): Promise<void> {
6
+ this.addSql(`create table "messages" ("id" uuid not null default gen_random_uuid(), "type" text not null default 'default', "thread_id" uuid null, "parent_message_id" uuid null, "sender_user_id" uuid not null, "subject" text not null, "body" text not null, "body_format" text not null default 'text', "priority" text not null default 'normal', "status" text not null default 'draft', "is_draft" boolean not null default true, "sent_at" timestamptz null, "action_data" jsonb null, "action_result" jsonb null, "action_taken" text null, "action_taken_by_user_id" uuid null, "action_taken_at" timestamptz null, "send_via_email" boolean not null default false, "tenant_id" uuid not null, "organization_id" uuid null, "created_at" timestamptz not null, "updated_at" timestamptz not null, "deleted_at" timestamptz null, "visibility" text null, "source_entity_type" text null, "source_entity_id" uuid null, "external_email" text null, "external_name" text null, "external_email_sent_at" timestamptz null, "external_email_failed_at" timestamptz null, "external_email_error" text null, constraint "messages_pkey" primary key ("id"));`);
7
+ this.addSql(`create index "messages_tenant_idx" on "messages" ("tenant_id", "organization_id");`);
8
+ this.addSql(`create index "messages_type_idx" on "messages" ("type", "tenant_id");`);
9
+ this.addSql(`create index "messages_thread_idx" on "messages" ("thread_id");`);
10
+ this.addSql(`create index "messages_sender_idx" on "messages" ("sender_user_id", "sent_at");`);
11
+
12
+ this.addSql(`create table "message_access_tokens" ("id" uuid not null default gen_random_uuid(), "message_id" uuid not null, "recipient_user_id" uuid not null, "token" text not null, "expires_at" timestamptz not null, "used_at" timestamptz null, "use_count" int not null default 0, "created_at" timestamptz not null, constraint "message_access_tokens_pkey" primary key ("id"));`);
13
+ this.addSql(`alter table "message_access_tokens" add constraint "message_access_tokens_token_unique" unique ("token");`);
14
+ this.addSql(`create index "message_access_tokens_message_idx" on "message_access_tokens" ("message_id");`);
15
+ this.addSql(`create index "message_access_tokens_token_idx" on "message_access_tokens" ("token");`);
16
+
17
+ this.addSql(`create table "message_objects" ("id" uuid not null default gen_random_uuid(), "message_id" uuid not null, "entity_module" text not null, "entity_type" text not null, "entity_id" uuid not null, "action_required" boolean not null default false, "action_type" text null, "action_label" text null, "entity_snapshot" jsonb null, "created_at" timestamptz not null, constraint "message_objects_pkey" primary key ("id"));`);
18
+ this.addSql(`create index "message_objects_entity_idx" on "message_objects" ("entity_type", "entity_id");`);
19
+ this.addSql(`create index "message_objects_message_idx" on "message_objects" ("message_id");`);
20
+
21
+ this.addSql(`create table "message_recipients" ("id" uuid not null default gen_random_uuid(), "message_id" uuid not null, "recipient_user_id" uuid not null, "recipient_type" text not null default 'to', "status" text not null default 'unread', "read_at" timestamptz null, "archived_at" timestamptz null, "deleted_at" timestamptz null, "email_sent_at" timestamptz null, "email_delivered_at" timestamptz null, "email_opened_at" timestamptz null, "email_failed_at" timestamptz null, "email_error" text null, "created_at" timestamptz not null, constraint "message_recipients_pkey" primary key ("id"));`);
22
+ this.addSql(`alter table "message_recipients" add constraint "message_recipients_message_user_unique" unique ("message_id", "recipient_user_id");`);
23
+ this.addSql(`create index "message_recipients_message_idx" on "message_recipients" ("message_id");`);
24
+ this.addSql(`create index "message_recipients_user_idx" on "message_recipients" ("recipient_user_id", "status");`);
25
+ }
26
+
27
+ override async down(): Promise<void> {
28
+ this.addSql(`drop table if exists "message_recipients" cascade;`);
29
+ this.addSql(`drop table if exists "message_objects" cascade;`);
30
+ this.addSql(`drop table if exists "message_access_tokens" cascade;`);
31
+ this.addSql(`drop table if exists "messages" cascade;`);
32
+ }
33
+
34
+ }
@@ -0,0 +1,16 @@
1
+ import { Migration } from '@mikro-orm/migrations';
2
+
3
+ export class Migration20260215165126 extends Migration {
4
+
5
+ override async up(): Promise<void> {
6
+ this.addSql(`create table "message_confirmations" ("id" uuid not null default gen_random_uuid(), "message_id" uuid not null, "tenant_id" uuid not null, "organization_id" uuid null, "confirmed" boolean not null default true, "confirmed_by_user_id" uuid null, "confirmed_at" timestamptz null, "created_at" timestamptz not null, "updated_at" timestamptz not null, constraint "message_confirmations_pkey" primary key ("id"));`);
7
+ this.addSql(`create index "message_confirmations_scope_idx" on "message_confirmations" ("tenant_id", "organization_id");`);
8
+ this.addSql(`create index "message_confirmations_message_idx" on "message_confirmations" ("message_id");`);
9
+ this.addSql(`alter table "message_confirmations" add constraint "message_confirmations_message_unique" unique ("message_id");`);
10
+ }
11
+
12
+ override async down(): Promise<void> {
13
+ this.addSql(`drop table if exists "message_confirmations" cascade;`);
14
+ }
15
+
16
+ }
@@ -0,0 +1,25 @@
1
+ import type { NotificationTypeDefinition } from '@open-mercato/shared/modules/notifications/types'
2
+
3
+ export const notificationTypes: NotificationTypeDefinition[] = [
4
+ {
5
+ type: 'messages.new',
6
+ module: 'messages',
7
+ titleKey: 'messages.notifications.new.title',
8
+ bodyKey: 'messages.notifications.new.body',
9
+ icon: 'mail',
10
+ severity: 'info',
11
+ actions: [
12
+ {
13
+ id: 'view',
14
+ labelKey: 'common.view',
15
+ variant: 'outline',
16
+ href: '/backend/messages/{sourceEntityId}',
17
+ icon: 'external-link',
18
+ },
19
+ ],
20
+ linkHref: '/backend/messages/{sourceEntityId}',
21
+ expiresAfterHours: 168,
22
+ },
23
+ ]
24
+
25
+ export default notificationTypes
@@ -0,0 +1,19 @@
1
+ import type { ModuleSetupConfig } from '@open-mercato/shared/modules/setup'
2
+
3
+ export const setup: ModuleSetupConfig = {
4
+ defaultRoleFeatures: {
5
+ superadmin: ['messages.*'],
6
+ admin: [
7
+ 'messages.*',
8
+ ],
9
+ employee: [
10
+ 'messages.view',
11
+ 'messages.compose',
12
+ 'messages.attach',
13
+ 'messages.attach_files',
14
+ 'messages.actions',
15
+ ],
16
+ },
17
+ }
18
+
19
+ export default setup
@@ -0,0 +1,138 @@
1
+ import type { EntityManager } from '@mikro-orm/postgresql'
2
+ import { findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'
3
+ import { createQueue } from '@open-mercato/queue'
4
+ import { buildBatchNotificationFromType } from '../../notifications/lib/notificationBuilder'
5
+ import { resolveNotificationService } from '../../notifications/lib/notificationService'
6
+ import {
7
+ MESSAGES_EMAIL_QUEUE_NAME,
8
+ type SendMessageEmailJob,
9
+ } from '../workers/send-email.worker'
10
+ import { User } from '../../auth/data/entities'
11
+ import { Message } from '../data/entities'
12
+ import { notificationTypes } from '../notifications'
13
+
14
+ export const metadata = {
15
+ event: 'messages.message.sent',
16
+ persistent: true,
17
+ id: 'messages:queue-email-delivery',
18
+ }
19
+
20
+ type MessageSentPayload = {
21
+ messageId: string
22
+ senderUserId: string
23
+ recipientUserIds: string[]
24
+ sendViaEmail: boolean
25
+ externalEmail?: string | null
26
+ forwardedFrom?: string
27
+ replyTo?: string
28
+ tenantId: string
29
+ organizationId?: string | null
30
+ }
31
+
32
+ type ResolverContext = {
33
+ resolve: <T = unknown>(name: string) => T
34
+ }
35
+
36
+ async function resolveNotificationVariables(payload: MessageSentPayload, ctx: ResolverContext): Promise<{ title: string; from: string }> {
37
+ try {
38
+ const em = ctx.resolve<EntityManager>('em')?.fork()
39
+ if (!em) return { title: '', from: '' }
40
+
41
+ const scope = {
42
+ tenantId: payload.tenantId,
43
+ organizationId: payload.organizationId ?? null,
44
+ }
45
+ const whereUser: { id: string; tenantId: string; deletedAt: null; organizationId?: string | null } = {
46
+ id: payload.senderUserId,
47
+ tenantId: payload.tenantId,
48
+ deletedAt: null,
49
+ }
50
+ if (payload.organizationId !== undefined) {
51
+ whereUser.organizationId = payload.organizationId
52
+ }
53
+
54
+ const [message, sender] = await Promise.all([
55
+ findOneWithDecryption(
56
+ em,
57
+ Message,
58
+ {
59
+ id: payload.messageId,
60
+ tenantId: payload.tenantId,
61
+ deletedAt: null,
62
+ },
63
+ undefined,
64
+ scope,
65
+ ),
66
+ findOneWithDecryption(
67
+ em,
68
+ User,
69
+ whereUser,
70
+ undefined,
71
+ scope,
72
+ ),
73
+ ])
74
+
75
+ return {
76
+ title: typeof message?.subject === 'string' ? message.subject : '',
77
+ from: typeof sender?.name === 'string' && sender.name.trim().length > 0
78
+ ? sender.name
79
+ : typeof sender?.email === 'string'
80
+ ? sender.email
81
+ : '',
82
+ }
83
+ } catch {
84
+ return { title: '', from: '' }
85
+ }
86
+ }
87
+
88
+
89
+ export default async function handle(payload: MessageSentPayload, ctx: ResolverContext): Promise<void> {
90
+ const uniqueRecipientUserIds = Array.from(new Set(payload.recipientUserIds))
91
+
92
+ const typeDef = notificationTypes.find((type) => type.type === 'messages.new')
93
+ if (typeDef && uniqueRecipientUserIds.length > 0) {
94
+ const variables = await resolveNotificationVariables(payload, ctx)
95
+ const notificationService = resolveNotificationService(ctx)
96
+ const notificationInput = buildBatchNotificationFromType(typeDef, {
97
+ recipientUserIds: uniqueRecipientUserIds,
98
+ titleVariables: variables,
99
+ bodyVariables: variables,
100
+ sourceEntityType: 'message',
101
+ sourceEntityId: payload.messageId,
102
+ linkHref: `/backend/messages/${payload.messageId}`,
103
+ })
104
+ await notificationService.createBatch(notificationInput, {
105
+ tenantId: payload.tenantId,
106
+ organizationId: payload.organizationId ?? null,
107
+ })
108
+ }
109
+
110
+ if (!payload.sendViaEmail) {
111
+ return
112
+ }
113
+
114
+ const strategy = process.env.QUEUE_STRATEGY === 'async' ? 'async' : 'local'
115
+
116
+ const emailQueue = createQueue<SendMessageEmailJob>(MESSAGES_EMAIL_QUEUE_NAME, strategy)
117
+
118
+ for (const recipientUserId of uniqueRecipientUserIds) {
119
+ await emailQueue.enqueue({
120
+ type: 'recipient',
121
+ messageId: payload.messageId,
122
+ recipientUserId,
123
+ tenantId: payload.tenantId,
124
+ organizationId: payload.organizationId ?? null,
125
+ })
126
+ }
127
+
128
+ const externalEmail = payload.externalEmail?.trim()
129
+ if (externalEmail) {
130
+ await emailQueue.enqueue({
131
+ type: 'external',
132
+ messageId: payload.messageId,
133
+ email: externalEmail,
134
+ tenantId: payload.tenantId,
135
+ organizationId: payload.organizationId ?? null,
136
+ })
137
+ }
138
+ }
@@ -0,0 +1,321 @@
1
+ import type { EntityManager } from '@mikro-orm/postgresql'
2
+ import type { JobContext, QueuedJob, WorkerMeta } from '@open-mercato/queue'
3
+ import { findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'
4
+ import { User } from '../../auth/data/entities'
5
+ import { Message, MessageObject, MessageRecipient } from '../data/entities'
6
+ import { emitMessagesEvent } from '../events'
7
+ import { getMessageEmailAttachments } from '../lib/attachments'
8
+ import {
9
+ sendMessageEmailToExternal,
10
+ sendMessageEmailToRecipient,
11
+ } from '../lib/email-sender'
12
+
13
+ export const MESSAGES_EMAIL_QUEUE_NAME = 'messages-email'
14
+
15
+ export type SendMessageEmailToRecipientJob = {
16
+ type: 'recipient'
17
+ messageId: string
18
+ recipientUserId: string
19
+ tenantId: string
20
+ organizationId?: string | null
21
+ }
22
+
23
+ export type SendMessageEmailToExternalJob = {
24
+ type: 'external'
25
+ messageId: string
26
+ email: string
27
+ tenantId: string
28
+ organizationId?: string | null
29
+ }
30
+
31
+ export type SendMessageEmailJob = SendMessageEmailToRecipientJob | SendMessageEmailToExternalJob
32
+
33
+ export const metadata: WorkerMeta = {
34
+ queue: MESSAGES_EMAIL_QUEUE_NAME,
35
+ id: 'messages:send-email',
36
+ concurrency: 10,
37
+ }
38
+
39
+ type HandlerContext = {
40
+ resolve: <T = unknown>(name: string) => T
41
+ }
42
+
43
+ async function emitEmailDeliveryEvent(
44
+ ctx: HandlerContext,
45
+ eventId: 'messages.message.email_sent' | 'messages.message.email_failed',
46
+ payload: {
47
+ messageId: string
48
+ target: 'recipient' | 'external'
49
+ recipientUserId?: string
50
+ email?: string
51
+ error?: string
52
+ tenantId: string
53
+ organizationId?: string | null
54
+ }
55
+ ) {
56
+ const eventBus = ctx.resolve<{ emit?: unknown } | null>('eventBus')
57
+ if (!eventBus || typeof eventBus !== 'object' || typeof (eventBus as { emit?: unknown }).emit !== 'function') {
58
+ return
59
+ }
60
+ await emitMessagesEvent(eventId, payload, { persistent: true })
61
+ }
62
+
63
+ async function resolveSender(em: EntityManager, message: Message) {
64
+ const sender = await findOneWithDecryption(
65
+ em,
66
+ User,
67
+ {
68
+ id: message.senderUserId,
69
+ tenantId: message.tenantId,
70
+ deletedAt: null,
71
+ },
72
+ undefined,
73
+ { tenantId: message.tenantId, organizationId: message.organizationId ?? null }
74
+ )
75
+ return {
76
+ name: sender?.name ?? null,
77
+ email: sender?.email ?? null,
78
+ }
79
+ }
80
+
81
+ async function resolveMessageScope(
82
+ em: EntityManager,
83
+ payload: SendMessageEmailJob
84
+ ) {
85
+ return await em.findOne(Message, {
86
+ id: payload.messageId,
87
+ tenantId: payload.tenantId,
88
+ organizationId: payload.organizationId ?? null,
89
+ deletedAt: null,
90
+ })
91
+ }
92
+
93
+ export async function claimRecipientDelivery(
94
+ em: EntityManager,
95
+ payload: SendMessageEmailToRecipientJob,
96
+ ): Promise<Date | null> {
97
+ const claimTimestamp = new Date()
98
+ const updatedRows = await em.nativeUpdate(
99
+ MessageRecipient,
100
+ {
101
+ messageId: payload.messageId,
102
+ recipientUserId: payload.recipientUserId,
103
+ emailSentAt: null,
104
+ },
105
+ {
106
+ emailSentAt: claimTimestamp,
107
+ emailFailedAt: null,
108
+ emailError: null,
109
+ },
110
+ )
111
+ return updatedRows > 0 ? claimTimestamp : null
112
+ }
113
+
114
+ export async function releaseRecipientClaim(
115
+ em: EntityManager,
116
+ payload: SendMessageEmailToRecipientJob,
117
+ claimTimestamp: Date,
118
+ errorMessage: string,
119
+ ): Promise<void> {
120
+ await em.nativeUpdate(
121
+ MessageRecipient,
122
+ {
123
+ messageId: payload.messageId,
124
+ recipientUserId: payload.recipientUserId,
125
+ emailSentAt: claimTimestamp,
126
+ },
127
+ {
128
+ emailSentAt: null,
129
+ emailFailedAt: new Date(),
130
+ emailError: errorMessage,
131
+ },
132
+ )
133
+ }
134
+
135
+ export async function claimExternalDelivery(
136
+ em: EntityManager,
137
+ payload: SendMessageEmailToExternalJob,
138
+ ): Promise<Date | null> {
139
+ const claimTimestamp = new Date()
140
+ const updatedRows = await em.nativeUpdate(
141
+ Message,
142
+ {
143
+ id: payload.messageId,
144
+ tenantId: payload.tenantId,
145
+ organizationId: payload.organizationId ?? null,
146
+ externalEmailSentAt: null,
147
+ },
148
+ {
149
+ externalEmailSentAt: claimTimestamp,
150
+ externalEmailFailedAt: null,
151
+ externalEmailError: null,
152
+ },
153
+ )
154
+ return updatedRows > 0 ? claimTimestamp : null
155
+ }
156
+
157
+ export async function releaseExternalClaim(
158
+ em: EntityManager,
159
+ payload: SendMessageEmailToExternalJob,
160
+ claimTimestamp: Date,
161
+ errorMessage: string,
162
+ ): Promise<void> {
163
+ await em.nativeUpdate(
164
+ Message,
165
+ {
166
+ id: payload.messageId,
167
+ tenantId: payload.tenantId,
168
+ organizationId: payload.organizationId ?? null,
169
+ externalEmailSentAt: claimTimestamp,
170
+ },
171
+ {
172
+ externalEmailSentAt: null,
173
+ externalEmailFailedAt: new Date(),
174
+ externalEmailError: errorMessage,
175
+ },
176
+ )
177
+ }
178
+
179
+ export default async function handle(
180
+ job: QueuedJob<SendMessageEmailJob>,
181
+ ctx: JobContext & HandlerContext
182
+ ): Promise<void> {
183
+ const { payload } = job
184
+ const em = (ctx.resolve('em') as EntityManager).fork()
185
+
186
+ const message = await resolveMessageScope(em, payload)
187
+ if (!message) {
188
+ console.error('[messages:send-email] Message not found', payload.messageId)
189
+ return
190
+ }
191
+
192
+ const objects = await em.find(MessageObject, { messageId: message.id })
193
+ const attachments = await getMessageEmailAttachments(
194
+ em,
195
+ message.id,
196
+ message.organizationId ?? null,
197
+ message.tenantId
198
+ )
199
+ const sender = await resolveSender(em, message)
200
+
201
+ if (payload.type === 'external') {
202
+ const externalClaimTimestamp = await claimExternalDelivery(em, payload)
203
+ if (!externalClaimTimestamp) {
204
+ return
205
+ }
206
+
207
+ try {
208
+ await sendMessageEmailToExternal({
209
+ message,
210
+ email: payload.email,
211
+ sender,
212
+ objects,
213
+ attachments,
214
+ })
215
+ await emitEmailDeliveryEvent(ctx, 'messages.message.email_sent', {
216
+ messageId: message.id,
217
+ target: 'external',
218
+ email: payload.email,
219
+ tenantId: message.tenantId,
220
+ organizationId: message.organizationId ?? null,
221
+ })
222
+ } catch (error) {
223
+ const errorMessage = error instanceof Error ? error.message : 'Unknown error'
224
+ await releaseExternalClaim(
225
+ em,
226
+ payload,
227
+ externalClaimTimestamp,
228
+ errorMessage,
229
+ )
230
+ await emitEmailDeliveryEvent(ctx, 'messages.message.email_failed', {
231
+ messageId: message.id,
232
+ target: 'external',
233
+ email: payload.email,
234
+ error: errorMessage,
235
+ tenantId: message.tenantId,
236
+ organizationId: message.organizationId ?? null,
237
+ })
238
+ console.error('[messages:send-email] External email send failed', error)
239
+ }
240
+ return
241
+ }
242
+
243
+ const recipientRecord = await em.findOne(MessageRecipient, {
244
+ messageId: payload.messageId,
245
+ recipientUserId: payload.recipientUserId,
246
+ })
247
+ if (!recipientRecord) {
248
+ console.error('[messages:send-email] Recipient row not found', payload)
249
+ return
250
+ }
251
+
252
+ if (recipientRecord.emailSentAt) {
253
+ return
254
+ }
255
+
256
+ const recipientClaimTimestamp = await claimRecipientDelivery(em, payload)
257
+ if (!recipientClaimTimestamp) {
258
+ return
259
+ }
260
+
261
+ const recipientUser = await findOneWithDecryption(
262
+ em,
263
+ User,
264
+ {
265
+ id: payload.recipientUserId,
266
+ tenantId: message.tenantId,
267
+ deletedAt: null,
268
+ },
269
+ undefined,
270
+ { tenantId: message.tenantId, organizationId: message.organizationId ?? null }
271
+ )
272
+
273
+ const recipientEmail = recipientUser?.email?.trim()
274
+ if (!recipientEmail) {
275
+ await releaseRecipientClaim(
276
+ em,
277
+ payload,
278
+ recipientClaimTimestamp,
279
+ 'Recipient has no email address',
280
+ )
281
+ return
282
+ }
283
+
284
+ try {
285
+ await sendMessageEmailToRecipient({
286
+ em,
287
+ message,
288
+ recipientUserId: payload.recipientUserId,
289
+ recipientEmail,
290
+ sender,
291
+ objects,
292
+ attachments,
293
+ })
294
+ await emitEmailDeliveryEvent(ctx, 'messages.message.email_sent', {
295
+ messageId: message.id,
296
+ target: 'recipient',
297
+ recipientUserId: payload.recipientUserId,
298
+ email: recipientEmail,
299
+ tenantId: message.tenantId,
300
+ organizationId: message.organizationId ?? null,
301
+ })
302
+ } catch (error) {
303
+ const errorMessage = error instanceof Error ? error.message : 'Unknown error'
304
+ await releaseRecipientClaim(
305
+ em,
306
+ payload,
307
+ recipientClaimTimestamp,
308
+ errorMessage,
309
+ )
310
+ await emitEmailDeliveryEvent(ctx, 'messages.message.email_failed', {
311
+ messageId: message.id,
312
+ target: 'recipient',
313
+ recipientUserId: payload.recipientUserId,
314
+ email: recipientEmail,
315
+ error: errorMessage,
316
+ tenantId: message.tenantId,
317
+ organizationId: message.organizationId ?? null,
318
+ })
319
+ console.error('[messages:send-email] Recipient email send failed', error)
320
+ }
321
+ }
@@ -24,6 +24,7 @@ import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, D
24
24
  import { ArrowRightLeft, Building2, CreditCard, Mail, Pencil, Plus, Send, Store, Truck, UserRound, Wand2, X } from 'lucide-react'
25
25
  import { FormHeader, type ActionItem } from '@open-mercato/ui/backend/forms'
26
26
  import { VersionHistoryAction } from '@open-mercato/ui/backend/version-history'
27
+ import { SendObjectMessageDialog } from '@open-mercato/ui/backend/messages'
27
28
  import Link from 'next/link'
28
29
  import { flash } from '@open-mercato/ui/backend/FlashMessages'
29
30
  import { apiCall, apiCallOrThrow, readApiResultOrThrow } from '@open-mercato/ui/backend/utils/apiCall'
@@ -4425,13 +4426,28 @@ export default function SalesDocumentDetailPage({
4425
4426
  backHref={kind === 'order' ? '/backend/sales/orders' : '/backend/sales/quotes'}
4426
4427
  backLabel={t('sales.documents.detail.back', 'Back to documents')}
4427
4428
  utilityActions={record ? (
4428
- <VersionHistoryAction
4429
- config={{
4430
- resourceKind: kind === 'order' ? 'sales.order' : 'sales.quote',
4431
- resourceId: record.id,
4432
- }}
4433
- t={t}
4434
- />
4429
+ <>
4430
+ <SendObjectMessageDialog
4431
+ object={{
4432
+ entityModule: 'sales',
4433
+ entityType: kind,
4434
+ entityId: record.id,
4435
+ sourceEntityType: kind === 'order' ? 'sales.order' : 'sales.quote',
4436
+ sourceEntityId: record.id,
4437
+ }}
4438
+ defaultValues={{
4439
+ sourceEntityType: kind === 'order' ? 'sales.order' : 'sales.quote',
4440
+ sourceEntityId: record.id,
4441
+ }}
4442
+ />
4443
+ <VersionHistoryAction
4444
+ config={{
4445
+ resourceKind: kind === 'order' ? 'sales.order' : 'sales.quote',
4446
+ resourceId: record.id,
4447
+ }}
4448
+ t={t}
4449
+ />
4450
+ </>
4435
4451
  ) : null}
4436
4452
  entityTypeLabel={kind === 'order'
4437
4453
  ? t('sales.documents.detail.order', 'Sales order')
@@ -564,21 +564,27 @@ const updatePaymentCommand: CommandHandler<
564
564
  },
565
565
  async execute(rawInput, ctx) {
566
566
  const input = paymentUpdateSchema.parse(rawInput ?? {})
567
- ensureTenantScope(ctx, input.tenantId)
568
- ensureOrganizationScope(ctx, input.organizationId)
569
567
  const em = (ctx.container.resolve('em') as EntityManager).fork()
570
568
  const { translate } = await resolveTranslations()
569
+ const scopeSeed = assertFound(
570
+ await em.findOne(SalesPayment, { id: input.id }),
571
+ 'sales.payments.not_found'
572
+ )
573
+ const resolvedTenantId = input.tenantId ?? scopeSeed.tenantId
574
+ const resolvedOrganizationId = input.organizationId ?? scopeSeed.organizationId
575
+ ensureTenantScope(ctx, resolvedTenantId)
576
+ ensureOrganizationScope(ctx, resolvedOrganizationId)
571
577
  const payment = assertFound(
572
578
  await findOneWithDecryption(
573
579
  em,
574
580
  SalesPayment,
575
581
  { id: input.id },
576
582
  { populate: ['order'] },
577
- { tenantId: input.tenantId, organizationId: input.organizationId },
583
+ { tenantId: resolvedTenantId, organizationId: resolvedOrganizationId },
578
584
  ),
579
585
  'sales.payments.not_found'
580
586
  )
581
- ensureSameScope(payment, input.organizationId, input.tenantId)
587
+ ensureSameScope(payment, resolvedOrganizationId, resolvedTenantId)
582
588
  const previousOrder = payment.order as SalesOrder | null
583
589
  if (input.orderId !== undefined) {
584
590
  if (!input.orderId) {
@@ -588,7 +594,7 @@ const updatePaymentCommand: CommandHandler<
588
594
  await em.findOne(SalesOrder, { id: input.orderId }),
589
595
  'sales.payments.order_not_found'
590
596
  )
591
- ensureSameScope(order, input.organizationId, input.tenantId)
597
+ ensureSameScope(order, resolvedOrganizationId, resolvedTenantId)
592
598
  if (
593
599
  order.currencyCode &&
594
600
  input.currencyCode &&
@@ -609,7 +615,7 @@ const updatePaymentCommand: CommandHandler<
609
615
  await em.findOne(SalesPaymentMethod, { id: input.paymentMethodId }),
610
616
  'sales.payments.method_not_found'
611
617
  )
612
- ensureSameScope(method, input.organizationId, input.tenantId)
618
+ ensureSameScope(method, resolvedOrganizationId, resolvedTenantId)
613
619
  payment.paymentMethod = method
614
620
  }
615
621
  }
@@ -1106,6 +1106,9 @@
1106
1106
  "sales.errors.tag_required": "Tag ist erforderlich.",
1107
1107
  "sales.errors.tenant_required": "Mandantenkontext ist erforderlich",
1108
1108
  "sales.errors.unauthorized": "Nicht autorisiert",
1109
+ "sales.messageObjects.notFound": "Nicht gefunden",
1110
+ "sales.messageObjects.order.title": "Bestellung",
1111
+ "sales.messageObjects.quote.title": "Angebot",
1109
1112
  "sales.module.description": "Angebote, Aufträge, Fulfillment, Rechnungsstellung und Zahlungen verbunden mit dem Produktkatalog.",
1110
1113
  "sales.module.title": "Vertriebsmanagement",
1111
1114
  "sales.nav.channels": "Vertriebskanäle",
@@ -1106,6 +1106,9 @@
1106
1106
  "sales.errors.tag_required": "Tag is required.",
1107
1107
  "sales.errors.tenant_required": "Tenant context is required",
1108
1108
  "sales.errors.unauthorized": "Unauthorized",
1109
+ "sales.messageObjects.notFound": "Not found",
1110
+ "sales.messageObjects.order.title": "Sales order",
1111
+ "sales.messageObjects.quote.title": "Sales quote",
1109
1112
  "sales.module.description": "Quotes, orders, fulfillment, invoicing, and payments connected to the product catalog.",
1110
1113
  "sales.module.title": "Sales Management",
1111
1114
  "sales.nav.channels": "Channels",