@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
@@ -0,0 +1,469 @@
1
+ "use client"
2
+
3
+ import * as React from 'react'
4
+ import Link from 'next/link'
5
+ import { useRouter } from 'next/navigation'
6
+ import { useQuery } from '@tanstack/react-query'
7
+ import type { ColumnDef } from '@tanstack/react-table'
8
+ import { useT } from '@open-mercato/shared/lib/i18n/context'
9
+ import { useOrganizationScopeVersion } from '@open-mercato/shared/lib/frontend/useOrganizationScope'
10
+ import { DataTable } from '@open-mercato/ui/backend/DataTable'
11
+ import type { FilterDef, FilterValues } from '@open-mercato/ui/backend/FilterBar'
12
+ import { Button } from '@open-mercato/ui/primitives/button'
13
+ import { apiCall } from '@open-mercato/ui/backend/utils/apiCall'
14
+ import { flash } from '@open-mercato/ui/backend/FlashMessages'
15
+ import { Archive, ChevronDown, FilePenLine, Inbox, Layers, Send } from 'lucide-react'
16
+ import { getMessageUiComponentRegistry } from './utils/typeUiRegistry'
17
+ import { DefaultMessageListItem } from './defaults/DefaultMessageListItem'
18
+
19
+ type MessageFolder = 'inbox' | 'sent' | 'drafts' | 'archived' | 'all'
20
+
21
+ type MessageListItem = {
22
+ id: string
23
+ type: string
24
+ subject: string
25
+ bodyPreview: string
26
+ senderUserId: string
27
+ senderName?: string | null
28
+ senderEmail?: string | null
29
+ priority: string
30
+ status: string
31
+ hasObjects: boolean
32
+ objectCount: number
33
+ hasAttachments: boolean
34
+ attachmentCount: number
35
+ hasActions: boolean
36
+ actionTaken?: string | null
37
+ sentAt?: string | null
38
+ readAt?: string | null
39
+ threadId?: string | null
40
+ }
41
+
42
+ type MessageListResponse = {
43
+ items?: MessageListItem[]
44
+ total?: number
45
+ page?: number
46
+ pageSize?: number
47
+ totalPages?: number
48
+ }
49
+
50
+ type MessageTypeItem = {
51
+ type: string
52
+ module: string
53
+ labelKey: string
54
+ ui?: {
55
+ listItemComponent?: string | null
56
+ } | null
57
+ }
58
+
59
+ type UserListItem = {
60
+ id: string
61
+ email?: string | null
62
+ name?: string | null
63
+ }
64
+
65
+ function toErrorMessage(payload: unknown): string | null {
66
+ if (!payload) return null
67
+ if (typeof payload === 'string') return payload
68
+ if (Array.isArray(payload)) {
69
+ for (const item of payload) {
70
+ const nested = toErrorMessage(item)
71
+ if (nested) return nested
72
+ }
73
+ return null
74
+ }
75
+ if (typeof payload === 'object') {
76
+ const record = payload as Record<string, unknown>
77
+ return (
78
+ toErrorMessage(record.error)
79
+ ?? toErrorMessage(record.message)
80
+ ?? toErrorMessage(record.detail)
81
+ ?? toErrorMessage(record.details)
82
+ ?? null
83
+ )
84
+ }
85
+ return null
86
+ }
87
+
88
+ export function MessagesInboxPageClient() {
89
+ const router = useRouter()
90
+ const t = useT()
91
+ const scopeVersion = useOrganizationScopeVersion()
92
+
93
+ const [folder, setFolder] = React.useState<MessageFolder>('inbox')
94
+ const [folderMenuOpen, setFolderMenuOpen] = React.useState(false)
95
+ const [search, setSearch] = React.useState('')
96
+ const [filterValues, setFilterValues] = React.useState<FilterValues>({})
97
+ const [page, setPage] = React.useState(1)
98
+ const pageSize = 20
99
+ const folderMenuRef = React.useRef<HTMLDivElement | null>(null)
100
+ const messageUiRegistry = React.useMemo(() => getMessageUiComponentRegistry(), [])
101
+
102
+ const listQuery = useQuery({
103
+ queryKey: [
104
+ 'messages',
105
+ 'list',
106
+ folder,
107
+ search,
108
+ page,
109
+ pageSize,
110
+ JSON.stringify(filterValues),
111
+ scopeVersion,
112
+ ],
113
+ queryFn: async () => {
114
+ const params = new URLSearchParams()
115
+ params.set('folder', folder)
116
+ params.set('page', String(page))
117
+ params.set('pageSize', String(pageSize))
118
+
119
+ if (search.trim()) {
120
+ params.set('search', search.trim())
121
+ }
122
+
123
+ const status = typeof filterValues.status === 'string' ? filterValues.status.trim() : ''
124
+ const type = typeof filterValues.type === 'string' ? filterValues.type.trim() : ''
125
+ const hasObjects = typeof filterValues.hasObjects === 'string' ? filterValues.hasObjects.trim() : ''
126
+ const hasAttachments = typeof filterValues.hasAttachments === 'string' ? filterValues.hasAttachments.trim() : ''
127
+ const hasActions = typeof filterValues.hasActions === 'string' ? filterValues.hasActions.trim() : ''
128
+ const senderId = typeof filterValues.senderId === 'string' ? filterValues.senderId.trim() : ''
129
+ const since = typeof filterValues.since === 'string' ? filterValues.since.trim() : ''
130
+
131
+ if (status) params.set('status', status)
132
+ if (type) params.set('type', type)
133
+ if (hasObjects) params.set('hasObjects', hasObjects)
134
+ if (hasAttachments) params.set('hasAttachments', hasAttachments)
135
+ if (hasActions) params.set('hasActions', hasActions)
136
+ if (senderId) params.set('senderId', senderId)
137
+ if (since) params.set('since', since)
138
+
139
+ const call = await apiCall<MessageListResponse>(`/api/messages?${params.toString()}`)
140
+ if (!call.ok) {
141
+ throw new Error(
142
+ toErrorMessage(call.result)
143
+ ?? t('messages.errors.loadListFailed', 'Failed to load messages.'),
144
+ )
145
+ }
146
+
147
+ return {
148
+ items: Array.isArray(call.result?.items) ? call.result?.items ?? [] : [],
149
+ total: Number(call.result?.total ?? 0),
150
+ page: Number(call.result?.page ?? page),
151
+ pageSize: Number(call.result?.pageSize ?? pageSize),
152
+ totalPages: Number(call.result?.totalPages ?? 1),
153
+ }
154
+ },
155
+ })
156
+
157
+ const messageTypesQuery = useQuery({
158
+ queryKey: ['messages', 'types', scopeVersion],
159
+ queryFn: async () => {
160
+ const call = await apiCall<{ items?: MessageTypeItem[] }>('/api/messages/types')
161
+ if (!call.ok) {
162
+ throw new Error(
163
+ toErrorMessage(call.result)
164
+ ?? t('messages.errors.loadTypesFailed', 'Failed to load message types.'),
165
+ )
166
+ }
167
+ return Array.isArray(call.result?.items) ? call.result?.items ?? [] : []
168
+ },
169
+ })
170
+
171
+ React.useEffect(() => {
172
+ if (!listQuery.error) return
173
+ flash(
174
+ listQuery.error instanceof Error
175
+ ? listQuery.error.message
176
+ : t('messages.errors.loadListFailed', 'Failed to load messages.'),
177
+ 'error',
178
+ )
179
+ }, [listQuery.error, t])
180
+
181
+ React.useEffect(() => {
182
+ if (!messageTypesQuery.error) return
183
+ flash(
184
+ messageTypesQuery.error instanceof Error
185
+ ? messageTypesQuery.error.message
186
+ : t('messages.errors.loadTypesFailed', 'Failed to load message types.'),
187
+ 'error',
188
+ )
189
+ }, [messageTypesQuery.error, t])
190
+
191
+ const messageTypeLabelMap = React.useMemo(() => {
192
+ const map: Record<string, string> = {}
193
+ for (const item of messageTypesQuery.data ?? []) {
194
+ map[item.type] = t(item.labelKey, item.type)
195
+ }
196
+ return map
197
+ }, [messageTypesQuery.data, t])
198
+
199
+ const loadSenderOptions = React.useCallback(async (query?: string) => {
200
+ const params = new URLSearchParams()
201
+ params.set('page', '1')
202
+ params.set('pageSize', '20')
203
+ if (query && query.trim().length > 0) {
204
+ params.set('search', query.trim())
205
+ }
206
+
207
+ const call = await apiCall<{ items?: UserListItem[] }>(`/api/auth/users?${params.toString()}`)
208
+ if (!call.ok) return []
209
+
210
+ const items = Array.isArray(call.result?.items) ? call.result?.items ?? [] : []
211
+ return items.flatMap((item) => {
212
+ if (!item || typeof item.id !== 'string' || item.id.trim().length === 0) return []
213
+ const name = typeof item.name === 'string' && item.name.trim().length > 0 ? item.name.trim() : null
214
+ const email = typeof item.email === 'string' && item.email.trim().length > 0 ? item.email.trim() : null
215
+ const label = name ?? email ?? item.id
216
+ return [{
217
+ value: item.id,
218
+ label,
219
+ description: email && email !== label ? email : null,
220
+ }]
221
+ })
222
+ }, [])
223
+
224
+ const listItemComponentKeyByType = React.useMemo(() => {
225
+ const map: Record<string, string | null> = {}
226
+ for (const item of messageTypesQuery.data ?? []) {
227
+ map[item.type] = item.ui?.listItemComponent ?? null
228
+ }
229
+ return map
230
+ }, [messageTypesQuery.data])
231
+
232
+ const filters = React.useMemo<FilterDef[]>(() => {
233
+ const typeOptions = (messageTypesQuery.data ?? []).map((item) => ({
234
+ value: item.type,
235
+ label: t(item.labelKey, item.type),
236
+ }))
237
+
238
+ return [
239
+ {
240
+ id: 'status',
241
+ label: t('messages.filters.status', 'Status'),
242
+ type: 'select',
243
+ options: [
244
+ { value: '', label: t('messages.filters.all', 'All') },
245
+ { value: 'unread', label: t('messages.status.unread', 'Unread') },
246
+ { value: 'read', label: t('messages.status.read', 'Read') },
247
+ { value: 'archived', label: t('messages.status.archived', 'Archived') },
248
+ ],
249
+ },
250
+ {
251
+ id: 'type',
252
+ label: t('messages.filters.type', 'Type'),
253
+ type: 'select',
254
+ options: [{ value: '', label: t('messages.filters.all', 'All') }, ...typeOptions],
255
+ },
256
+ {
257
+ id: 'hasObjects',
258
+ label: t('messages.filters.hasObjects', 'Objects'),
259
+ type: 'select',
260
+ options: [
261
+ { value: '', label: t('messages.filters.all', 'All') },
262
+ { value: 'true', label: t('common.yes', 'Yes') },
263
+ { value: 'false', label: t('common.no', 'No') },
264
+ ],
265
+ },
266
+ {
267
+ id: 'hasAttachments',
268
+ label: t('messages.filters.hasAttachments', 'Attachments'),
269
+ type: 'select',
270
+ options: [
271
+ { value: '', label: t('messages.filters.all', 'All') },
272
+ { value: 'true', label: t('common.yes', 'Yes') },
273
+ { value: 'false', label: t('common.no', 'No') },
274
+ ],
275
+ },
276
+ {
277
+ id: 'hasActions',
278
+ label: t('messages.filters.hasActions', 'Actions'),
279
+ type: 'select',
280
+ options: [
281
+ { value: '', label: t('messages.filters.all', 'All') },
282
+ { value: 'true', label: t('common.yes', 'Yes') },
283
+ { value: 'false', label: t('common.no', 'No') },
284
+ ],
285
+ },
286
+ {
287
+ id: 'senderId',
288
+ label: t('messages.filters.sender', 'Sender'),
289
+ type: 'select',
290
+ options: [{ value: '', label: t('messages.filters.all', 'All') }],
291
+ loadOptions: loadSenderOptions,
292
+ },
293
+ {
294
+ id: 'since',
295
+ label: t('messages.filters.since', 'Sent after'),
296
+ type: 'text',
297
+ placeholder: t('messages.filters.sincePlaceholder', 'YYYY-MM-DDTHH:mm:ssZ'),
298
+ },
299
+ ]
300
+ }, [loadSenderOptions, messageTypesQuery.data, t])
301
+
302
+ const columns = React.useMemo<ColumnDef<MessageListItem>[]>(() => [
303
+ {
304
+ accessorKey: 'message',
305
+ header: t('messages.title', 'Messages'),
306
+ meta: {
307
+ truncate: false,
308
+ maxWidth: '100%',
309
+ },
310
+ cell: ({ row }) => {
311
+ const item = row.original
312
+ const listItemComponentKey = listItemComponentKeyByType[item.type]
313
+ const ListItemComponent = listItemComponentKey
314
+ ? messageUiRegistry.listItemComponents[listItemComponentKey] ?? null
315
+ : null
316
+ const ComponentToUse = ListItemComponent || DefaultMessageListItem
317
+
318
+ return (
319
+ <ComponentToUse
320
+ message={{
321
+ id: item.id,
322
+ type: item.type,
323
+ typeLabel: messageTypeLabelMap[item.type] ?? item.type,
324
+ subject: item.subject,
325
+ body: item.bodyPreview,
326
+ bodyFormat: 'text' as const,
327
+ priority: (item.priority as 'low' | 'normal' | 'high' | 'urgent') ?? 'normal',
328
+ sentAt: item.sentAt ? new Date(item.sentAt) : null,
329
+ senderName: item.senderName || item.senderEmail || item.senderUserId,
330
+ hasObjects: item.hasObjects,
331
+ objectCount: item.objectCount,
332
+ hasAttachments: item.hasAttachments,
333
+ attachmentCount: item.attachmentCount,
334
+ hasActions: item.hasActions,
335
+ actionTaken: item.actionTaken ?? null,
336
+ unread: item.status === 'unread',
337
+ }}
338
+ onClick={() => router.push(`/backend/messages/${item.id}`)}
339
+ />
340
+ )
341
+ },
342
+ },
343
+ ], [listItemComponentKeyByType, messageTypeLabelMap, messageUiRegistry, router, t])
344
+
345
+ const folderOptions = React.useMemo(() => [
346
+ { id: 'inbox' as const, label: t('messages.folder.inbox', 'Inbox'), icon: Inbox },
347
+ { id: 'sent' as const, label: t('messages.folder.sent', 'Sent'), icon: Send },
348
+ { id: 'drafts' as const, label: t('messages.folder.drafts', 'Drafts'), icon: FilePenLine },
349
+ { id: 'archived' as const, label: t('messages.folder.archived', 'Archived'), icon: Archive },
350
+ { id: 'all' as const, label: t('messages.folder.all', 'All'), icon: Layers },
351
+ ], [t])
352
+
353
+ const activeFolderOption = folderOptions.find((option) => option.id === folder) ?? folderOptions[0]
354
+ const ActiveFolderIcon = activeFolderOption.icon
355
+
356
+ React.useEffect(() => {
357
+ if (!folderMenuOpen) return
358
+ const handleClickOutside = (event: MouseEvent) => {
359
+ if (!folderMenuRef.current) return
360
+ const target = event.target
361
+ if (target instanceof Node && !folderMenuRef.current.contains(target)) {
362
+ setFolderMenuOpen(false)
363
+ }
364
+ }
365
+ const handleEscape = (event: KeyboardEvent) => {
366
+ if (event.key === 'Escape') setFolderMenuOpen(false)
367
+ }
368
+ document.addEventListener('mousedown', handleClickOutside)
369
+ document.addEventListener('keydown', handleEscape)
370
+ return () => {
371
+ document.removeEventListener('mousedown', handleClickOutside)
372
+ document.removeEventListener('keydown', handleEscape)
373
+ }
374
+ }, [folderMenuOpen])
375
+
376
+ const rows = listQuery.data?.items ?? []
377
+ const total = listQuery.data?.total ?? 0
378
+ const totalPages = listQuery.data?.totalPages ?? 1
379
+
380
+ return (
381
+ <div className="space-y-4">
382
+ <DataTable
383
+ title={t('messages.title', 'Messages')}
384
+ columns={columns}
385
+ data={rows}
386
+ searchValue={search}
387
+ onSearchChange={(value) => {
388
+ setSearch(value)
389
+ setPage(1)
390
+ }}
391
+ searchPlaceholder={t('messages.searchPlaceholder', 'Search messages')}
392
+ filters={filters}
393
+ filterValues={filterValues}
394
+ onFiltersApply={(value) => {
395
+ setFilterValues(value)
396
+ setPage(1)
397
+ }}
398
+ onFiltersClear={() => {
399
+ setFilterValues({})
400
+ setPage(1)
401
+ }}
402
+ isLoading={listQuery.isLoading || listQuery.isFetching}
403
+ pagination={{
404
+ page,
405
+ pageSize,
406
+ total,
407
+ totalPages,
408
+ onPageChange: setPage,
409
+ }}
410
+ actions={
411
+ <div className="flex flex-wrap items-center gap-2">
412
+ <div className="relative" ref={folderMenuRef}>
413
+ <Button
414
+ type="button"
415
+ variant="outline"
416
+ size="sm"
417
+ className="gap-2"
418
+ aria-expanded={folderMenuOpen}
419
+ aria-haspopup="menu"
420
+ onClick={() => setFolderMenuOpen((value) => !value)}
421
+ >
422
+ <ActiveFolderIcon className="h-4 w-4" aria-hidden />
423
+ <span>{t('messages.folder.selector', 'Folder')}:</span>
424
+ <span>{activeFolderOption.label}</span>
425
+ <ChevronDown className="h-4 w-4 opacity-70" aria-hidden />
426
+ </Button>
427
+ {folderMenuOpen ? (
428
+ <div
429
+ className="absolute right-0 z-20 mt-1 min-w-52 rounded-md border bg-background p-1 shadow"
430
+ role="menu"
431
+ >
432
+ {folderOptions.map((option) => {
433
+ const Icon = option.icon
434
+ const isActive = option.id === folder
435
+ return (
436
+ <button
437
+ key={option.id}
438
+ type="button"
439
+ role="menuitemradio"
440
+ aria-checked={isActive}
441
+ className={`flex w-full items-center gap-2 rounded px-2 py-1.5 text-left text-sm hover:bg-accent ${isActive ? 'bg-accent/60' : ''}`}
442
+ onClick={() => {
443
+ setFolder(option.id)
444
+ setPage(1)
445
+ setFolderMenuOpen(false)
446
+ }}
447
+ >
448
+ <Icon className="h-4 w-4" aria-hidden />
449
+ <span>{option.label}</span>
450
+ </button>
451
+ )
452
+ })}
453
+ </div>
454
+ ) : null}
455
+ </div>
456
+ <Button asChild>
457
+ <Link href="/backend/messages/compose">{t('messages.compose', 'Compose message')}</Link>
458
+ </Button>
459
+ </div>
460
+ }
461
+ onRowClick={(row) => {
462
+ router.push(`/backend/messages/${row.id}`)
463
+ }}
464
+ perspective={{ tableId: 'messages.inbox' }}
465
+ embedded
466
+ />
467
+ </div>
468
+ )
469
+ }
@@ -0,0 +1,35 @@
1
+ "use client"
2
+
3
+ import type { MessageActionsProps } from '@open-mercato/shared/modules/messages/types'
4
+ import { useT } from '@open-mercato/shared/lib/i18n/context'
5
+ import { Button } from '@open-mercato/ui/primitives/button'
6
+
7
+ export function MessageConfirmationActions({
8
+ message,
9
+ onExecuteAction,
10
+ isExecuting,
11
+ executingActionId,
12
+ }: MessageActionsProps) {
13
+ const t = useT()
14
+ const confirmationAction = (message.actionData?.actions ?? []).find((action) => action.id === 'confirmation')
15
+
16
+ if (!confirmationAction) return null
17
+
18
+ return (
19
+ <div className="flex flex-wrap gap-2">
20
+ <Button
21
+ type="button"
22
+ variant={confirmationAction.variant ?? 'default'}
23
+ size="sm"
24
+ onClick={() => onExecuteAction(confirmationAction.id)}
25
+ disabled={isExecuting || !!message.actionTaken}
26
+ >
27
+ {isExecuting && executingActionId === confirmationAction.id
28
+ ? t('messages.actions.executing', 'Executing...')
29
+ : t(confirmationAction.labelKey ?? 'messages.actions.confirmation', confirmationAction.label)}
30
+ </Button>
31
+ </div>
32
+ )
33
+ }
34
+
35
+ export default MessageConfirmationActions
@@ -0,0 +1,88 @@
1
+ "use client"
2
+
3
+ import { useQuery } from '@tanstack/react-query'
4
+ import type { MessageContentProps } from '@open-mercato/shared/modules/messages/types'
5
+ import { useT } from '@open-mercato/shared/lib/i18n/context'
6
+ import { apiCall } from '@open-mercato/ui/backend/utils/apiCall'
7
+ import { MarkdownContent } from '@open-mercato/ui/backend/markdown/MarkdownContent'
8
+
9
+ type MessageConfirmationResult = {
10
+ messageId: string
11
+ confirmed: boolean
12
+ confirmedAt: string | null
13
+ confirmedByUserId: string | null
14
+ }
15
+
16
+ function toErrorMessage(payload: unknown): string | null {
17
+ if (!payload) return null
18
+ if (typeof payload === 'string') return payload
19
+ if (Array.isArray(payload)) {
20
+ for (const item of payload) {
21
+ const nested = toErrorMessage(item)
22
+ if (nested) return nested
23
+ }
24
+ return null
25
+ }
26
+ if (typeof payload === 'object') {
27
+ const record = payload as Record<string, unknown>
28
+ return (
29
+ toErrorMessage(record.error)
30
+ ?? toErrorMessage(record.message)
31
+ ?? toErrorMessage(record.detail)
32
+ ?? null
33
+ )
34
+ }
35
+ return null
36
+ }
37
+
38
+ function formatDateTime(value: string | null): string {
39
+ if (!value) return '—'
40
+ const date = new Date(value)
41
+ if (Number.isNaN(date.getTime())) return '—'
42
+ return date.toLocaleString()
43
+ }
44
+
45
+ export function MessageConfirmationContent({ message }: MessageContentProps) {
46
+ const t = useT()
47
+
48
+ const confirmationQuery = useQuery({
49
+ queryKey: ['messages', 'confirmation', message.id],
50
+ queryFn: async () => {
51
+ const call = await apiCall<MessageConfirmationResult>(`/api/messages/${encodeURIComponent(message.id)}/confirmation`)
52
+ if (!call.ok || !call.result) {
53
+ throw new Error(
54
+ toErrorMessage(call.result)
55
+ ?? t('messages.errors.loadDetailFailed', 'Failed to load message details.'),
56
+ )
57
+ }
58
+ return call.result
59
+ },
60
+ enabled: Boolean(message.id),
61
+ })
62
+
63
+ const confirmation = confirmationQuery.data
64
+
65
+ return (
66
+ <div className="space-y-3">
67
+ <div className="max-h-[60vh] overflow-y-auto pr-1">
68
+ <MarkdownContent
69
+ body={message.body}
70
+ format={message.bodyFormat}
71
+ className="text-sm whitespace-pre-wrap [&>*]: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"
72
+ />
73
+ </div>
74
+ <div className="rounded border p-3 text-sm">
75
+ <p className="font-medium">
76
+ {confirmation?.confirmed
77
+ ? t('messages.confirmation.confirmed', 'Confirmed')
78
+ : t('messages.confirmation.notConfirmed', 'Not confirmed')}
79
+ </p>
80
+ <p className="text-xs text-muted-foreground">
81
+ {t('messages.confirmation.confirmedAt', 'Confirmed at')}: {formatDateTime(confirmation?.confirmedAt ?? null)}
82
+ </p>
83
+ </div>
84
+ </div>
85
+ )
86
+ }
87
+
88
+ export default MessageConfirmationContent
@@ -0,0 +1,37 @@
1
+ "use client"
2
+
3
+ import type { MessageActionsProps } from '@open-mercato/shared/modules/messages/types'
4
+ import { useT } from '@open-mercato/shared/lib/i18n/context'
5
+ import { Button } from '@open-mercato/ui/primitives/button'
6
+
7
+ export function DefaultMessageActions({
8
+ message,
9
+ onExecuteAction,
10
+ isExecuting,
11
+ executingActionId,
12
+ }: MessageActionsProps) {
13
+ const t = useT()
14
+ const actions = message.actionData?.actions ?? []
15
+
16
+ if (actions.length === 0) return null
17
+
18
+ return (
19
+ <div className="flex flex-wrap gap-2">
20
+ {actions.map((action) => (
21
+ <Button
22
+ key={action.id}
23
+ variant={action.variant ?? 'default'}
24
+ size="sm"
25
+ onClick={() => onExecuteAction(action.id)}
26
+ disabled={isExecuting || !!message.actionTaken}
27
+ >
28
+ {isExecuting && executingActionId === action.id
29
+ ? t('messages.actions.executing', 'Executing...')
30
+ : t(action.labelKey ?? action.label, action.label)}
31
+ </Button>
32
+ ))}
33
+ </div>
34
+ )
35
+ }
36
+
37
+ export default DefaultMessageActions
@@ -0,0 +1,21 @@
1
+ "use client"
2
+
3
+ import * as React from 'react'
4
+ import type { MessageContentProps } from '@open-mercato/shared/modules/messages/types'
5
+ import { MarkdownContent } from '@open-mercato/ui/backend/markdown/MarkdownContent'
6
+
7
+ export function DefaultMessageContent({ message }: MessageContentProps) {
8
+ return (
9
+ <section className="space-y-4">
10
+ <div>
11
+ <MarkdownContent
12
+ body={message.body}
13
+ format={message.bodyFormat}
14
+ className="text-sm text-foreground whitespace-pre-wrap [&>*]: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"
15
+ />
16
+ </div>
17
+ </section>
18
+ )
19
+ }
20
+
21
+ export default DefaultMessageContent