@open-mercato/core 0.4.5-develop-5191db4ef3 → 0.4.5-develop-9f9549ebc8

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
@@ -1106,6 +1106,9 @@
1106
1106
  "sales.errors.tag_required": "La etiqueta es obligatoria.",
1107
1107
  "sales.errors.tenant_required": "Se requiere el contexto del inquilino",
1108
1108
  "sales.errors.unauthorized": "No autorizado",
1109
+ "sales.messageObjects.notFound": "No encontrado",
1110
+ "sales.messageObjects.order.title": "Pedido",
1111
+ "sales.messageObjects.quote.title": "Cotización",
1109
1112
  "sales.module.description": "Cotizaciones, pedidos, cumplimiento, facturación y pagos conectados al catálogo de productos.",
1110
1113
  "sales.module.title": "Gestión de ventas",
1111
1114
  "sales.nav.channels": "Canales de ventas",
@@ -1106,6 +1106,9 @@
1106
1106
  "sales.errors.tag_required": "Tag jest wymagany.",
1107
1107
  "sales.errors.tenant_required": "Wymagany jest kontekst najemcy",
1108
1108
  "sales.errors.unauthorized": "Brak autoryzacji",
1109
+ "sales.messageObjects.notFound": "Nie znaleziono",
1110
+ "sales.messageObjects.order.title": "Zamówienie",
1111
+ "sales.messageObjects.quote.title": "Oferta",
1109
1112
  "sales.module.description": "Oferty, zamówienia, realizacja, fakturowanie i płatności powiązane z katalogiem produktów.",
1110
1113
  "sales.module.title": "Sprzedaż",
1111
1114
  "sales.nav.channels": "Kanały sprzedaży",
@@ -0,0 +1,150 @@
1
+ import { createRequestContainer } from '@open-mercato/shared/lib/di/container'
2
+ import { findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'
3
+ import { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'
4
+ import type { ObjectPreviewData } from '@open-mercato/shared/modules/messages/types'
5
+ import type { EntityManager } from '@mikro-orm/postgresql'
6
+ import { SalesOrder, SalesQuote } from '../data/entities'
7
+
8
+ type PreviewContext = {
9
+ tenantId: string
10
+ organizationId?: string | null
11
+ }
12
+
13
+ type DocumentKind = 'order' | 'quote'
14
+
15
+ type SalesDocumentPreviewRecord = {
16
+ id: string
17
+ status?: string | null
18
+ customerSnapshot?: Record<string, unknown> | null
19
+ currencyCode?: string | null
20
+ grandTotalGrossAmount?: string | null
21
+ orderNumber?: string
22
+ quoteNumber?: string
23
+ }
24
+
25
+ function resolveCustomerName(snapshot: Record<string, unknown> | null): string | null {
26
+ if (!snapshot) return null
27
+ const customer = snapshot.customer as Record<string, unknown> | undefined
28
+ const contact = snapshot.contact as Record<string, unknown> | undefined
29
+ const displayName = typeof customer?.displayName === 'string' ? customer.displayName : null
30
+ if (displayName && displayName.trim().length > 0) return displayName.trim()
31
+ const first = typeof contact?.firstName === 'string' ? contact.firstName : null
32
+ const last = typeof contact?.lastName === 'string' ? contact.lastName : null
33
+ const preferred = typeof contact?.preferredName === 'string' ? contact.preferredName : null
34
+ const parts = [preferred ?? first, last]
35
+ .map((part) => (typeof part === 'string' ? part.trim() : ''))
36
+ .filter((part) => part.length > 0)
37
+ return parts.length > 0 ? parts.join(' ') : null
38
+ }
39
+
40
+ function formatTotal(amount: string | null | undefined, currency: string | null | undefined): string | null {
41
+ if (!amount) return null
42
+ const value = Number(amount)
43
+ if (!Number.isFinite(value)) return currency ? `${amount} ${currency}` : amount
44
+ if (!currency) return value.toLocaleString()
45
+ try {
46
+ return new Intl.NumberFormat(undefined, { style: 'currency', currency }).format(value)
47
+ } catch {
48
+ return `${value.toLocaleString()} ${currency}`
49
+ }
50
+ }
51
+
52
+ function statusColor(status: string | null | undefined): string | undefined {
53
+ if (!status) return undefined
54
+ const value = status.toLowerCase()
55
+ if (value.includes('approved') || value.includes('paid') || value.includes('fulfilled') || value.includes('accepted')) {
56
+ return 'green'
57
+ }
58
+ if (value.includes('cancel') || value.includes('rejected') || value.includes('void')) {
59
+ return 'red'
60
+ }
61
+ if (value.includes('draft') || value.includes('pending') || value.includes('open') || value.includes('sent')) {
62
+ return 'amber'
63
+ }
64
+ return 'blue'
65
+ }
66
+
67
+ async function buildPreview(kind: DocumentKind, entityId: string, record: SalesDocumentPreviewRecord | null): Promise<ObjectPreviewData> {
68
+ const { t } = await resolveTranslations()
69
+ const defaultTitle = kind === 'quote' ? t('sales.messageObjects.quote.title') : t('sales.messageObjects.order.title')
70
+ if (!record) {
71
+ return {
72
+ title: defaultTitle,
73
+ subtitle: entityId,
74
+ status: t('sales.messageObjects.notFound'),
75
+ statusColor: 'gray',
76
+ }
77
+ }
78
+
79
+ const number = kind === 'quote' ? record.quoteNumber : record.orderNumber
80
+ const customerName = resolveCustomerName(record.customerSnapshot ?? null)
81
+ const total = formatTotal(record.grandTotalGrossAmount, record.currencyCode)
82
+
83
+ const subtitleParts = [customerName, total].filter((part): part is string => Boolean(part && part.trim().length > 0))
84
+ const subtitle = subtitleParts.length > 0 ? subtitleParts.join(' • ') : entityId
85
+
86
+ const metadata: Record<string, string> = {}
87
+ if (number) metadata.Number = number
88
+ if (customerName) metadata.Customer = customerName
89
+ if (total) metadata.Total = total
90
+
91
+ return {
92
+ title: number && number.trim().length > 0 ? number : defaultTitle,
93
+ subtitle,
94
+ status: record.status ?? undefined,
95
+ statusColor: statusColor(record.status),
96
+ metadata: Object.keys(metadata).length > 0 ? metadata : undefined,
97
+ }
98
+ }
99
+
100
+ async function loadDocumentRecord(
101
+ kind: DocumentKind,
102
+ entityId: string,
103
+ ctx: PreviewContext,
104
+ ): Promise<SalesDocumentPreviewRecord | null> {
105
+ if (!ctx.organizationId) return null
106
+
107
+ const { resolve } = await createRequestContainer()
108
+ const em = resolve('em') as EntityManager
109
+ const scope = { tenantId: ctx.tenantId, organizationId: ctx.organizationId }
110
+
111
+ if (kind === 'quote') {
112
+ return await findOneWithDecryption(
113
+ em,
114
+ SalesQuote,
115
+ {
116
+ id: entityId,
117
+ tenantId: ctx.tenantId,
118
+ organizationId: ctx.organizationId,
119
+ deletedAt: null,
120
+ },
121
+ undefined,
122
+ scope,
123
+ )
124
+ }
125
+
126
+ return await findOneWithDecryption(
127
+ em,
128
+ SalesOrder,
129
+ {
130
+ id: entityId,
131
+ tenantId: ctx.tenantId,
132
+ organizationId: ctx.organizationId,
133
+ deletedAt: null,
134
+ },
135
+ undefined,
136
+ scope,
137
+ )
138
+ }
139
+
140
+ export async function loadSalesQuotePreview(entityId: string, ctx: PreviewContext): Promise<ObjectPreviewData> {
141
+ const record = await loadDocumentRecord('quote', entityId, ctx)
142
+ return await buildPreview('quote', entityId, record)
143
+ }
144
+
145
+ export async function loadSalesOrderPreview(entityId: string, ctx: PreviewContext): Promise<ObjectPreviewData> {
146
+ const record = await loadDocumentRecord('order', entityId, ctx)
147
+ return await buildPreview('order', entityId, record)
148
+ }
149
+
150
+
@@ -0,0 +1,56 @@
1
+ import type { MessageObjectTypeDefinition } from '@open-mercato/shared/modules/messages/types'
2
+ import { SalesDocumentMessageDetail } from './widgets/messages/SalesDocumentMessageDetail'
3
+ import { SalesDocumentMessagePreview } from './widgets/messages/SalesDocumentMessagePreview'
4
+
5
+ const objectMessageTypes = ['default', 'messages.defaultWithObjects']
6
+
7
+ export const messageObjectTypes: MessageObjectTypeDefinition[] = [
8
+ {
9
+ module: 'sales',
10
+ entityType: 'order',
11
+ messageTypes: objectMessageTypes,
12
+ entityId: 'sales:sales_order',
13
+ optionLabelField: 'number',
14
+ optionSubtitleField: 'status',
15
+ labelKey: 'sales.documents.detail.order',
16
+ icon: 'receipt-text',
17
+ PreviewComponent: SalesDocumentMessagePreview,
18
+ DetailComponent: SalesDocumentMessageDetail,
19
+ actions: [],
20
+ loadPreview: async (entityId, ctx) => {
21
+ if (typeof window !== 'undefined') {
22
+ return {
23
+ title: 'Sales order',
24
+ subtitle: entityId,
25
+ }
26
+ }
27
+ const { loadSalesOrderPreview } = await import('./lib/messageObjectPreviews')
28
+ return loadSalesOrderPreview(entityId, ctx)
29
+ },
30
+ },
31
+ {
32
+ module: 'sales',
33
+ entityType: 'quote',
34
+ messageTypes: objectMessageTypes,
35
+ entityId: 'sales:sales_quote',
36
+ optionLabelField: 'number',
37
+ optionSubtitleField: 'status',
38
+ labelKey: 'sales.documents.detail.quote',
39
+ icon: 'file-text',
40
+ PreviewComponent: SalesDocumentMessagePreview,
41
+ DetailComponent: SalesDocumentMessageDetail,
42
+ actions: [],
43
+ loadPreview: async (entityId, ctx) => {
44
+ if (typeof window !== 'undefined') {
45
+ return {
46
+ title: 'Sales quote',
47
+ subtitle: entityId,
48
+ }
49
+ }
50
+ const { loadSalesQuotePreview } = await import('./lib/messageObjectPreviews')
51
+ return loadSalesQuotePreview(entityId, ctx)
52
+ },
53
+ },
54
+ ]
55
+
56
+ export default messageObjectTypes
@@ -0,0 +1,57 @@
1
+ "use client"
2
+
3
+ import * as React from 'react'
4
+ import { useT } from '@open-mercato/shared/lib/i18n/context'
5
+ import type { ObjectDetailProps } from '@open-mercato/shared/modules/messages/types'
6
+ import { Button } from '@open-mercato/ui/primitives/button'
7
+ import { SalesDocumentMessagePreview } from './SalesDocumentMessagePreview'
8
+
9
+ export function SalesDocumentMessageDetail(props: ObjectDetailProps) {
10
+ const t = useT()
11
+ const [executingActionId, setExecutingActionId] = React.useState<string | null>(null)
12
+
13
+ return (
14
+ <div className="space-y-3 rounded border p-3">
15
+ <SalesDocumentMessagePreview
16
+ entityId={props.entityId}
17
+ entityModule={props.entityModule}
18
+ entityType={props.entityType}
19
+ snapshot={props.snapshot}
20
+ previewData={props.previewData}
21
+ actionRequired={props.actionRequired}
22
+ actionType={props.actionType}
23
+ actionLabel={props.actionLabel}
24
+ />
25
+
26
+ {props.actions.length > 0 ? (
27
+ <div className="flex flex-wrap gap-2">
28
+ {props.actions.map((action) => (
29
+ <Button
30
+ key={action.id}
31
+ type="button"
32
+ size="sm"
33
+ variant={action.variant ?? 'default'}
34
+ disabled={executingActionId !== null}
35
+ onClick={async () => {
36
+ if (executingActionId) return
37
+ setExecutingActionId(action.id)
38
+ try {
39
+ await props.onAction(action.id, { id: props.entityId })
40
+ } finally {
41
+ setExecutingActionId(null)
42
+ }
43
+ }}
44
+ >
45
+ {executingActionId === action.id
46
+ ? t('messages.actions.executing', 'Executing...')
47
+ : t(action.labelKey ?? action.id, action.id)}
48
+ </Button>
49
+ ))}
50
+ </div>
51
+ ) : null}
52
+ </div>
53
+ )
54
+ }
55
+
56
+ export default SalesDocumentMessageDetail
57
+
@@ -0,0 +1,46 @@
1
+ "use client"
2
+
3
+ import { FileText, ReceiptText } from 'lucide-react'
4
+ import { useT } from '@open-mercato/shared/lib/i18n/context'
5
+ import type { ObjectPreviewProps } from '@open-mercato/shared/modules/messages/types'
6
+ import { Badge } from '@open-mercato/ui/primitives/badge'
7
+
8
+ export function SalesDocumentMessagePreview({
9
+ entityType,
10
+ entityId,
11
+ previewData,
12
+ actionRequired,
13
+ actionLabel,
14
+ }: ObjectPreviewProps) {
15
+ const t = useT()
16
+ const isQuote = entityType === 'quote'
17
+ const Icon = isQuote ? FileText : ReceiptText
18
+ const fallbackTitle = isQuote
19
+ ? t('sales.documents.detail.quote', 'Sales quote')
20
+ : t('sales.documents.detail.order', 'Sales order')
21
+ const title = previewData?.title || fallbackTitle
22
+ const subtitle = previewData?.subtitle || entityId
23
+
24
+ return (
25
+ <div className="flex items-start gap-3 rounded-md border bg-muted/20 p-3">
26
+ <Icon className="mt-0.5 h-4 w-4 text-muted-foreground" />
27
+ <div className="min-w-0 flex-1 space-y-1">
28
+ <div className="flex items-center gap-2">
29
+ <p className="truncate text-sm font-medium">{title}</p>
30
+ {actionRequired ? (
31
+ <Badge variant="secondary" className="text-xs">
32
+ {actionLabel || t('messages.composer.objectActionRequired', 'Action required')}
33
+ </Badge>
34
+ ) : null}
35
+ </div>
36
+ <p className="truncate text-xs text-muted-foreground">{subtitle}</p>
37
+ {previewData?.status ? (
38
+ <Badge variant="outline" className="text-xs">{previewData.status}</Badge>
39
+ ) : null}
40
+ </div>
41
+ </div>
42
+ )
43
+ }
44
+
45
+ export default SalesDocumentMessagePreview
46
+
@@ -0,0 +1,2 @@
1
+ export { SalesDocumentMessageDetail } from './SalesDocumentMessageDetail'
2
+ export { SalesDocumentMessagePreview } from './SalesDocumentMessagePreview'
@@ -2,11 +2,13 @@
2
2
 
3
3
  import * as React from 'react'
4
4
  import { useRouter } from 'next/navigation'
5
+ import { Send } from 'lucide-react'
5
6
  import { Page, PageBody } from '@open-mercato/ui/backend/Page'
6
7
  import { Badge } from '@open-mercato/ui/primitives/badge'
7
8
  import { Button } from '@open-mercato/ui/primitives/button'
8
9
  import { Textarea } from '@open-mercato/ui/primitives/textarea'
9
10
  import { LoadingMessage, ErrorMessage } from '@open-mercato/ui/backend/detail'
11
+ import { SendObjectMessageDialog } from '@open-mercato/ui/backend/messages'
10
12
  import { apiCallOrThrow, readApiResultOrThrow } from '@open-mercato/ui/backend/utils/apiCall'
11
13
  import { updateCrud } from '@open-mercato/ui/backend/utils/crud'
12
14
  import { flash } from '@open-mercato/ui/backend/FlashMessages'
@@ -110,6 +112,20 @@ export default function StaffLeaveRequestDetailPage({ params }: { params?: { id?
110
112
  note: record?.note ?? null,
111
113
  }), [record, memberLabel])
112
114
 
115
+ const messageContextPreview = React.useMemo(() => (
116
+ <div className="space-y-1">
117
+ <p className="font-medium">{t('staff.leaveRequests.messages.contextTitle', 'Linked leave request')}</p>
118
+ {memberLabel ? (
119
+ <p className="text-xs text-muted-foreground">
120
+ {t('staff.leaveRequests.detail.member', 'Team member')}: {memberLabel}
121
+ </p>
122
+ ) : null}
123
+ <p className="text-xs text-muted-foreground">
124
+ {t('staff.leaveRequests.detail.dates', 'Dates')}: {dateSummary}
125
+ </p>
126
+ </div>
127
+ ), [dateSummary, memberLabel, t])
128
+
113
129
  const handleSubmit = React.useCallback(async (values: LeaveRequestFormValues) => {
114
130
  if (!record?.id) return
115
131
  const payload = buildLeaveRequestPayload(values, { id: record.id })
@@ -215,6 +231,44 @@ export default function StaffLeaveRequestDetailPage({ params }: { params?: { id?
215
231
  onSubmit={handleSubmit}
216
232
  allowMemberSelect
217
233
  memberLabel={memberLabel}
234
+ extraActions={record.id ? (
235
+ <SendObjectMessageDialog
236
+ object={{
237
+ entityModule: 'staff',
238
+ entityType: 'leave_request',
239
+ entityId: record.id,
240
+ sourceEntityType: 'staff:leave_request',
241
+ sourceEntityId: record.id,
242
+ }}
243
+ lockedType="staff.leave_request_approval"
244
+ requiredActionConfig={{
245
+ mode: 'required',
246
+ options: [
247
+ { id: 'approve', label: t('staff.notifications.leaveRequest.actions.approve', 'Approve') },
248
+ { id: 'reject', label: t('staff.notifications.leaveRequest.actions.reject', 'Reject') },
249
+ ],
250
+ }}
251
+ defaultValues={{
252
+ type: 'staff.leave_request_approval',
253
+ subject: t('staff.leaveRequests.messages.compose.subject', 'Leave request approval needed'),
254
+ body: t('staff.leaveRequests.messages.compose.body', 'Please review this leave request and take action.'),
255
+ }}
256
+ contextPreview={messageContextPreview}
257
+ renderTrigger={({ openComposer, disabled }) => (
258
+ <Button
259
+ type="button"
260
+ size="icon"
261
+ variant="outline"
262
+ onClick={openComposer}
263
+ disabled={disabled}
264
+ aria-label={t('staff.leaveRequests.messages.compose.action', 'Send for review')}
265
+ title={t('staff.leaveRequests.messages.compose.action', 'Send for review')}
266
+ >
267
+ <Send className="h-4 w-4" />
268
+ </Button>
269
+ )}
270
+ />
271
+ ) : null}
218
272
  />
219
273
  </PageBody>
220
274
  </Page>
@@ -2,9 +2,12 @@
2
2
 
3
3
  import * as React from 'react'
4
4
  import { useRouter } from 'next/navigation'
5
+ import { Leaf } from 'lucide-react'
5
6
  import { Page, PageBody } from '@open-mercato/ui/backend/Page'
6
7
  import { Badge } from '@open-mercato/ui/primitives/badge'
8
+ import { Button } from '@open-mercato/ui/primitives/button'
7
9
  import { LoadingMessage, ErrorMessage } from '@open-mercato/ui/backend/detail'
10
+ import { SendObjectMessageDialog } from '@open-mercato/ui/backend/messages'
8
11
  import { readApiResultOrThrow } from '@open-mercato/ui/backend/utils/apiCall'
9
12
  import { updateCrud } from '@open-mercato/ui/backend/utils/crud'
10
13
  import { flash } from '@open-mercato/ui/backend/FlashMessages'
@@ -95,6 +98,23 @@ export default function StaffMyLeaveRequestDetailPage({ params }: { params?: { i
95
98
  unavailabilityReasonValue: record?.unavailabilityReasonValue ?? record?.unavailability_reason_value ?? null,
96
99
  note: record?.note ?? null,
97
100
  }), [record, memberLabel])
101
+ const dateSummary = formatDateRange(
102
+ record?.startDate ?? record?.start_date ?? null,
103
+ record?.endDate ?? record?.end_date ?? null,
104
+ )
105
+ const messageContextPreview = React.useMemo(() => (
106
+ <div className="space-y-1">
107
+ <p className="font-medium">{t('staff.leaveRequests.messages.contextTitle', 'Linked leave request')}</p>
108
+ {memberLabel ? (
109
+ <p className="text-xs text-muted-foreground">
110
+ {t('staff.leaveRequests.detail.member', 'Team member')}: {memberLabel}
111
+ </p>
112
+ ) : null}
113
+ <p className="text-xs text-muted-foreground">
114
+ {t('staff.leaveRequests.detail.dates', 'Dates')}: {dateSummary}
115
+ </p>
116
+ </div>
117
+ ), [dateSummary, memberLabel, t])
98
118
 
99
119
  const handleSubmit = React.useCallback(async (values: LeaveRequestFormValues) => {
100
120
  if (!record?.id) return
@@ -158,6 +178,44 @@ export default function StaffMyLeaveRequestDetailPage({ params }: { params?: { i
158
178
  onSubmit={handleSubmit}
159
179
  allowMemberSelect={false}
160
180
  memberLabel={memberLabel}
181
+ extraActions={record.id ? (
182
+ <SendObjectMessageDialog
183
+ object={{
184
+ entityModule: 'staff',
185
+ entityType: 'leave_request',
186
+ entityId: record.id,
187
+ sourceEntityType: 'staff:leave_request',
188
+ sourceEntityId: record.id,
189
+ }}
190
+ lockedType="staff.leave_request_approval"
191
+ requiredActionConfig={{
192
+ mode: 'required',
193
+ options: [
194
+ { id: 'approve', label: t('staff.notifications.leaveRequest.actions.approve', 'Approve') },
195
+ { id: 'reject', label: t('staff.notifications.leaveRequest.actions.reject', 'Reject') },
196
+ ],
197
+ }}
198
+ defaultValues={{
199
+ type: 'staff.leave_request_approval',
200
+ subject: t('staff.leaveRequests.messages.compose.subject', 'Leave request approval needed'),
201
+ body: t('staff.leaveRequests.messages.compose.body', 'Please review this leave request and take action.'),
202
+ }}
203
+ contextPreview={messageContextPreview}
204
+ renderTrigger={({ openComposer, disabled }) => (
205
+ <Button
206
+ type="button"
207
+ size="icon"
208
+ variant="outline"
209
+ onClick={openComposer}
210
+ disabled={disabled}
211
+ aria-label={t('staff.leaveRequests.messages.compose.action', 'Send for review')}
212
+ title={t('staff.leaveRequests.messages.compose.action', 'Send for review')}
213
+ >
214
+ <Leaf className="h-4 w-4" />
215
+ </Button>
216
+ )}
217
+ />
218
+ ) : null}
161
219
  />
162
220
  ) : (
163
221
  <div className="rounded-lg border bg-card p-4 text-sm text-muted-foreground">
@@ -2,9 +2,7 @@
2
2
 
3
3
  import * as React from 'react'
4
4
  import Link from 'next/link'
5
- import dynamic from 'next/dynamic'
6
5
  import { useRouter, useSearchParams } from 'next/navigation'
7
- import type { PluggableList } from 'unified'
8
6
  import { Page, PageBody } from '@open-mercato/ui/backend/Page'
9
7
  import { Button } from '@open-mercato/ui/primitives/button'
10
8
  import { readApiResultOrThrow } from '@open-mercato/ui/backend/utils/apiCall'
@@ -23,6 +21,7 @@ import { renderDictionaryColor, renderDictionaryIcon, ICON_SUGGESTIONS } from '@
23
21
  import { createStaffNotesAdapter } from '@open-mercato/core/modules/staff/components/detail/notesAdapter'
24
22
  import { createStaffActivitiesAdapter } from '@open-mercato/core/modules/staff/components/detail/activitiesAdapter'
25
23
  import { createStaffAddressAdapter, createStaffAddressTypesAdapter } from '@open-mercato/core/modules/staff/components/detail/addressesAdapter'
24
+ import { MarkdownContent } from '@open-mercato/ui/backend/markdown/MarkdownContent'
26
25
  import {
27
26
  createStaffDictionaryEntry,
28
27
  loadStaffDictionary,
@@ -32,31 +31,9 @@ import { JobHistorySection } from '@open-mercato/core/modules/staff/components/d
32
31
  import type { DictionarySelectLabels } from '@open-mercato/core/modules/dictionaries/components/DictionaryEntrySelect'
33
32
  import { Plus } from 'lucide-react'
34
33
 
35
- const isTestEnv = typeof process !== 'undefined' && process.env.NODE_ENV === 'test'
36
34
  const MARKDOWN_CLASSNAME =
37
35
  'text-sm text-muted-foreground break-words [&>*]:mb-2 [&>*:last-child]:mb-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 [&_pre]:rounded-md [&_pre]:bg-muted [&_pre]:p-3 [&_pre]:text-xs'
38
36
 
39
- type MarkdownPreviewProps = { children: string; className?: string; remarkPlugins?: PluggableList }
40
-
41
- const MarkdownPreview: React.ComponentType<MarkdownPreviewProps> = isTestEnv
42
- ? ({ children, className }) => <div className={className}>{children}</div>
43
- : (dynamic(() => import('react-markdown').then((mod) => mod.default as React.ComponentType<MarkdownPreviewProps>), {
44
- ssr: false,
45
- loading: () => null,
46
- }) as unknown as React.ComponentType<MarkdownPreviewProps>)
47
-
48
- let markdownPluginsPromise: Promise<PluggableList> | null = null
49
-
50
- async function loadMarkdownPlugins(): Promise<PluggableList> {
51
- if (isTestEnv) return []
52
- if (!markdownPluginsPromise) {
53
- markdownPluginsPromise = import('remark-gfm')
54
- .then((mod) => [mod.default ?? mod] as PluggableList)
55
- .catch(() => [])
56
- }
57
- return markdownPluginsPromise
58
- }
59
-
60
37
  type TeamMemberRecord = {
61
38
  id: string
62
39
  teamId?: string | null
@@ -97,7 +74,6 @@ export default function StaffTeamMemberDetailPage({ params }: { params?: { id?:
97
74
  const [sectionAction, setSectionAction] = React.useState<SectionAction | null>(null)
98
75
  const [activityDictionaryId, setActivityDictionaryId] = React.useState<string | null>(null)
99
76
  const [activityTypeEntries, setActivityTypeEntries] = React.useState<DictionaryEntryOption[]>([])
100
- const [markdownPlugins, setMarkdownPlugins] = React.useState<PluggableList>([])
101
77
  const flashShownRef = React.useRef(false)
102
78
 
103
79
  const notesAdapter = React.useMemo(() => createStaffNotesAdapter(detailTranslator), [detailTranslator])
@@ -105,10 +81,6 @@ export default function StaffTeamMemberDetailPage({ params }: { params?: { id?:
105
81
  const addressesAdapter = React.useMemo(() => createStaffAddressAdapter(detailTranslator), [detailTranslator])
106
82
  const addressTypesAdapter = React.useMemo(() => createStaffAddressTypesAdapter(detailTranslator), [detailTranslator])
107
83
 
108
- React.useEffect(() => {
109
- void loadMarkdownPlugins().then((plugins) => setMarkdownPlugins(plugins))
110
- }, [])
111
-
112
84
  const activityTypeLabels = React.useMemo<DictionarySelectLabels>(() => ({
113
85
  placeholder: t('staff.teamMembers.detail.activities.dictionary.placeholder', 'Select an activity type'),
114
86
  addLabel: t('staff.teamMembers.detail.activities.dictionary.add', 'Add type'),
@@ -519,9 +491,7 @@ export default function StaffTeamMemberDetailPage({ params }: { params?: { id?:
519
491
  </h2>
520
492
  <div className="space-y-2">
521
493
  {memberRecord?.description ? (
522
- <MarkdownPreview remarkPlugins={markdownPlugins} className={MARKDOWN_CLASSNAME}>
523
- {memberRecord.description}
524
- </MarkdownPreview>
494
+ <MarkdownContent body={memberRecord.description} format="markdown" className={MARKDOWN_CLASSNAME} />
525
495
  ) : (
526
496
  <p className="text-sm text-muted-foreground">
527
497
  {t('staff.teamMembers.detail.descriptionEmpty', 'No description provided.')}