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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (385) hide show
  1. package/dist/generated/entities/message/index.js +65 -0
  2. package/dist/generated/entities/message/index.js.map +7 -0
  3. package/dist/generated/entities/message_access_token/index.js +19 -0
  4. package/dist/generated/entities/message_access_token/index.js.map +7 -0
  5. package/dist/generated/entities/message_confirmation/index.js +21 -0
  6. package/dist/generated/entities/message_confirmation/index.js.map +7 -0
  7. package/dist/generated/entities/message_object/index.js +23 -0
  8. package/dist/generated/entities/message_object/index.js.map +7 -0
  9. package/dist/generated/entities/message_recipient/index.js +31 -0
  10. package/dist/generated/entities/message_recipient/index.js.map +7 -0
  11. package/dist/generated/entities.ids.generated.js +8 -0
  12. package/dist/generated/entities.ids.generated.js.map +2 -2
  13. package/dist/generated/entity-fields-registry.js +10 -0
  14. package/dist/generated/entity-fields-registry.js.map +2 -2
  15. package/dist/modules/customers/backend/customers/deals/[id]/page.js +27 -8
  16. package/dist/modules/customers/backend/customers/deals/[id]/page.js.map +2 -2
  17. package/dist/modules/customers/lib/messageObjectPreviews.js +131 -0
  18. package/dist/modules/customers/lib/messageObjectPreviews.js.map +7 -0
  19. package/dist/modules/customers/message-objects.js +71 -0
  20. package/dist/modules/customers/message-objects.js.map +7 -0
  21. package/dist/modules/customers/widgets/messages/CustomerMessageObjectDetail.js +51 -0
  22. package/dist/modules/customers/widgets/messages/CustomerMessageObjectDetail.js.map +7 -0
  23. package/dist/modules/customers/widgets/messages/CustomerMessageObjectPreview.js +35 -0
  24. package/dist/modules/customers/widgets/messages/CustomerMessageObjectPreview.js.map +7 -0
  25. package/dist/modules/customers/widgets/messages/index.js +7 -0
  26. package/dist/modules/customers/widgets/messages/index.js.map +7 -0
  27. package/dist/modules/messages/acl.js +15 -0
  28. package/dist/modules/messages/acl.js.map +7 -0
  29. package/dist/modules/messages/api/[id]/actions/[actionId]/route.js +92 -0
  30. package/dist/modules/messages/api/[id]/actions/[actionId]/route.js.map +7 -0
  31. package/dist/modules/messages/api/[id]/archive/route.js +120 -0
  32. package/dist/modules/messages/api/[id]/archive/route.js.map +7 -0
  33. package/dist/modules/messages/api/[id]/attachments/route.js +195 -0
  34. package/dist/modules/messages/api/[id]/attachments/route.js.map +7 -0
  35. package/dist/modules/messages/api/[id]/confirmation/route.js +67 -0
  36. package/dist/modules/messages/api/[id]/confirmation/route.js.map +7 -0
  37. package/dist/modules/messages/api/[id]/conversation/archive/route.js +68 -0
  38. package/dist/modules/messages/api/[id]/conversation/archive/route.js.map +7 -0
  39. package/dist/modules/messages/api/[id]/conversation/read/route.js +68 -0
  40. package/dist/modules/messages/api/[id]/conversation/read/route.js.map +7 -0
  41. package/dist/modules/messages/api/[id]/conversation/route.js +68 -0
  42. package/dist/modules/messages/api/[id]/conversation/route.js.map +7 -0
  43. package/dist/modules/messages/api/[id]/forward/route.js +85 -0
  44. package/dist/modules/messages/api/[id]/forward/route.js.map +7 -0
  45. package/dist/modules/messages/api/[id]/forward-preview/route.js +70 -0
  46. package/dist/modules/messages/api/[id]/forward-preview/route.js.map +7 -0
  47. package/dist/modules/messages/api/[id]/read/route.js +120 -0
  48. package/dist/modules/messages/api/[id]/read/route.js.map +7 -0
  49. package/dist/modules/messages/api/[id]/reply/route.js +87 -0
  50. package/dist/modules/messages/api/[id]/reply/route.js.map +7 -0
  51. package/dist/modules/messages/api/[id]/route.js +350 -0
  52. package/dist/modules/messages/api/[id]/route.js.map +7 -0
  53. package/dist/modules/messages/api/object-types/route.js +54 -0
  54. package/dist/modules/messages/api/object-types/route.js.map +7 -0
  55. package/dist/modules/messages/api/openapi.js +261 -0
  56. package/dist/modules/messages/api/openapi.js.map +7 -0
  57. package/dist/modules/messages/api/route.js +262 -0
  58. package/dist/modules/messages/api/route.js.map +7 -0
  59. package/dist/modules/messages/api/token/[token]/route.js +99 -0
  60. package/dist/modules/messages/api/token/[token]/route.js.map +7 -0
  61. package/dist/modules/messages/api/types/route.js +40 -0
  62. package/dist/modules/messages/api/types/route.js.map +7 -0
  63. package/dist/modules/messages/api/unread-count/route.js +43 -0
  64. package/dist/modules/messages/api/unread-count/route.js.map +7 -0
  65. package/dist/modules/messages/backend/messages/[id]/page.js +10 -0
  66. package/dist/modules/messages/backend/messages/[id]/page.js.map +7 -0
  67. package/dist/modules/messages/backend/messages/[id]/page.meta.js +16 -0
  68. package/dist/modules/messages/backend/messages/[id]/page.meta.js.map +7 -0
  69. package/dist/modules/messages/backend/messages/compose/page.js +10 -0
  70. package/dist/modules/messages/backend/messages/compose/page.js.map +7 -0
  71. package/dist/modules/messages/backend/messages/compose/page.meta.js +17 -0
  72. package/dist/modules/messages/backend/messages/compose/page.meta.js.map +7 -0
  73. package/dist/modules/messages/backend/page.js +10 -0
  74. package/dist/modules/messages/backend/page.js.map +7 -0
  75. package/dist/modules/messages/backend/page.meta.js +33 -0
  76. package/dist/modules/messages/backend/page.meta.js.map +7 -0
  77. package/dist/modules/messages/commands/actions.js +265 -0
  78. package/dist/modules/messages/commands/actions.js.map +7 -0
  79. package/dist/modules/messages/commands/attachments.js +217 -0
  80. package/dist/modules/messages/commands/attachments.js.map +7 -0
  81. package/dist/modules/messages/commands/confirmations.js +151 -0
  82. package/dist/modules/messages/commands/confirmations.js.map +7 -0
  83. package/dist/modules/messages/commands/conversation.js +240 -0
  84. package/dist/modules/messages/commands/conversation.js.map +7 -0
  85. package/dist/modules/messages/commands/messages.js +748 -0
  86. package/dist/modules/messages/commands/messages.js.map +7 -0
  87. package/dist/modules/messages/commands/recipients.js +259 -0
  88. package/dist/modules/messages/commands/recipients.js.map +7 -0
  89. package/dist/modules/messages/commands/shared.js +258 -0
  90. package/dist/modules/messages/commands/shared.js.map +7 -0
  91. package/dist/modules/messages/commands/tokens.js +69 -0
  92. package/dist/modules/messages/commands/tokens.js.map +7 -0
  93. package/dist/modules/messages/components/ComposeMessagePageClient.js +24 -0
  94. package/dist/modules/messages/components/ComposeMessagePageClient.js.map +7 -0
  95. package/dist/modules/messages/components/MessageDetailPageClient.js +261 -0
  96. package/dist/modules/messages/components/MessageDetailPageClient.js.map +7 -0
  97. package/dist/modules/messages/components/MessagesInboxPageClient.js +390 -0
  98. package/dist/modules/messages/components/MessagesInboxPageClient.js.map +7 -0
  99. package/dist/modules/messages/components/confirmation/MessageConfirmationActions.js +31 -0
  100. package/dist/modules/messages/components/confirmation/MessageConfirmationActions.js.map +7 -0
  101. package/dist/modules/messages/components/confirmation/MessageConfirmationContent.js +69 -0
  102. package/dist/modules/messages/components/confirmation/MessageConfirmationContent.js.map +7 -0
  103. package/dist/modules/messages/components/defaults/DefaultMessageActions.js +31 -0
  104. package/dist/modules/messages/components/defaults/DefaultMessageActions.js.map +7 -0
  105. package/dist/modules/messages/components/defaults/DefaultMessageContent.js +19 -0
  106. package/dist/modules/messages/components/defaults/DefaultMessageContent.js.map +7 -0
  107. package/dist/modules/messages/components/defaults/DefaultMessageListItem.js +90 -0
  108. package/dist/modules/messages/components/defaults/DefaultMessageListItem.js.map +7 -0
  109. package/dist/modules/messages/components/defaults/MessageRecordObjectDetail.js +86 -0
  110. package/dist/modules/messages/components/defaults/MessageRecordObjectDetail.js.map +7 -0
  111. package/dist/modules/messages/components/defaults/MessageRecordObjectPreview.js +61 -0
  112. package/dist/modules/messages/components/defaults/MessageRecordObjectPreview.js.map +7 -0
  113. package/dist/modules/messages/components/message-detail/detail-panels.js +27 -0
  114. package/dist/modules/messages/components/message-detail/detail-panels.js.map +7 -0
  115. package/dist/modules/messages/components/message-detail/hooks/useMessageDetails.js +52 -0
  116. package/dist/modules/messages/components/message-detail/hooks/useMessageDetails.js.map +7 -0
  117. package/dist/modules/messages/components/message-detail/hooks/useMessageDetailsActions.js +289 -0
  118. package/dist/modules/messages/components/message-detail/hooks/useMessageDetailsActions.js.map +7 -0
  119. package/dist/modules/messages/components/message-detail/hooks/useMessageDetailsConversation.js +103 -0
  120. package/dist/modules/messages/components/message-detail/hooks/useMessageDetailsConversation.js.map +7 -0
  121. package/dist/modules/messages/components/message-detail/hooks/useMessageDetailsQueries.js +78 -0
  122. package/dist/modules/messages/components/message-detail/hooks/useMessageDetailsQueries.js.map +7 -0
  123. package/dist/modules/messages/components/message-detail/panels/MainMessageHeader.js +94 -0
  124. package/dist/modules/messages/components/message-detail/panels/MainMessageHeader.js.map +7 -0
  125. package/dist/modules/messages/components/message-detail/panels/MessageHeader.js +110 -0
  126. package/dist/modules/messages/components/message-detail/panels/MessageHeader.js.map +7 -0
  127. package/dist/modules/messages/components/message-detail/panels/MessageListComponent.js +58 -0
  128. package/dist/modules/messages/components/message-detail/panels/MessageListComponent.js.map +7 -0
  129. package/dist/modules/messages/components/message-detail/panels/actions-panel.js +51 -0
  130. package/dist/modules/messages/components/message-detail/panels/actions-panel.js.map +7 -0
  131. package/dist/modules/messages/components/message-detail/panels/attachments-panel.js +66 -0
  132. package/dist/modules/messages/components/message-detail/panels/attachments-panel.js.map +7 -0
  133. package/dist/modules/messages/components/message-detail/panels/body-panel.js +20 -0
  134. package/dist/modules/messages/components/message-detail/panels/body-panel.js.map +7 -0
  135. package/dist/modules/messages/components/message-detail/panels/composer-dialogs.js +36 -0
  136. package/dist/modules/messages/components/message-detail/panels/composer-dialogs.js.map +7 -0
  137. package/dist/modules/messages/components/message-detail/panels/dialogs.js +96 -0
  138. package/dist/modules/messages/components/message-detail/panels/dialogs.js.map +7 -0
  139. package/dist/modules/messages/components/message-detail/panels/index.js +25 -0
  140. package/dist/modules/messages/components/message-detail/panels/index.js.map +7 -0
  141. package/dist/modules/messages/components/message-detail/panels/meta-panel.js +14 -0
  142. package/dist/modules/messages/components/message-detail/panels/meta-panel.js.map +7 -0
  143. package/dist/modules/messages/components/message-detail/panels/objects-panel.js +51 -0
  144. package/dist/modules/messages/components/message-detail/panels/objects-panel.js.map +7 -0
  145. package/dist/modules/messages/components/message-detail/panels/thread-panel.js +54 -0
  146. package/dist/modules/messages/components/message-detail/panels/thread-panel.js.map +7 -0
  147. package/dist/modules/messages/components/message-detail/types.js +1 -0
  148. package/dist/modules/messages/components/message-detail/types.js.map +7 -0
  149. package/dist/modules/messages/components/message-detail/utils.js +54 -0
  150. package/dist/modules/messages/components/message-detail/utils.js.map +7 -0
  151. package/dist/modules/messages/components/utils/PriorityBadge.js +52 -0
  152. package/dist/modules/messages/components/utils/PriorityBadge.js.map +7 -0
  153. package/dist/modules/messages/components/utils/typeUiRegistry.js +77 -0
  154. package/dist/modules/messages/components/utils/typeUiRegistry.js.map +7 -0
  155. package/dist/modules/messages/data/entities.js +309 -0
  156. package/dist/modules/messages/data/entities.js.map +7 -0
  157. package/dist/modules/messages/data/validators.js +272 -0
  158. package/dist/modules/messages/data/validators.js.map +7 -0
  159. package/dist/modules/messages/emails/MessageEmail.js +108 -0
  160. package/dist/modules/messages/emails/MessageEmail.js.map +7 -0
  161. package/dist/modules/messages/events.js +24 -0
  162. package/dist/modules/messages/events.js.map +7 -0
  163. package/dist/modules/messages/frontend/messages/view/[token]/page.js +247 -0
  164. package/dist/modules/messages/frontend/messages/view/[token]/page.js.map +7 -0
  165. package/dist/modules/messages/frontend/messages/view/[token]/page.meta.js +9 -0
  166. package/dist/modules/messages/frontend/messages/view/[token]/page.meta.js.map +7 -0
  167. package/dist/modules/messages/index.js +21 -0
  168. package/dist/modules/messages/index.js.map +7 -0
  169. package/dist/modules/messages/lib/actions.js +141 -0
  170. package/dist/modules/messages/lib/actions.js.map +7 -0
  171. package/dist/modules/messages/lib/attachments.js +131 -0
  172. package/dist/modules/messages/lib/attachments.js.map +7 -0
  173. package/dist/modules/messages/lib/constants.js +7 -0
  174. package/dist/modules/messages/lib/constants.js.map +7 -0
  175. package/dist/modules/messages/lib/email-sender.js +201 -0
  176. package/dist/modules/messages/lib/email-sender.js.map +7 -0
  177. package/dist/modules/messages/lib/forwarding.js +179 -0
  178. package/dist/modules/messages/lib/forwarding.js.map +7 -0
  179. package/dist/modules/messages/lib/message-objects-registry.js +49 -0
  180. package/dist/modules/messages/lib/message-objects-registry.js.map +7 -0
  181. package/dist/modules/messages/lib/message-types-registry.js +41 -0
  182. package/dist/modules/messages/lib/message-types-registry.js.map +7 -0
  183. package/dist/modules/messages/lib/object-validation.js +20 -0
  184. package/dist/modules/messages/lib/object-validation.js.map +7 -0
  185. package/dist/modules/messages/lib/operationMetadata.js +21 -0
  186. package/dist/modules/messages/lib/operationMetadata.js.map +7 -0
  187. package/dist/modules/messages/lib/priorityUtils.js +61 -0
  188. package/dist/modules/messages/lib/priorityUtils.js.map +7 -0
  189. package/dist/modules/messages/lib/routeHelpers.js +44 -0
  190. package/dist/modules/messages/lib/routeHelpers.js.map +7 -0
  191. package/dist/modules/messages/message-objects.js +7 -0
  192. package/dist/modules/messages/message-objects.js.map +7 -0
  193. package/dist/modules/messages/message-types.js +67 -0
  194. package/dist/modules/messages/message-types.js.map +7 -0
  195. package/dist/modules/messages/migrations/Migration20260213181243.js +31 -0
  196. package/dist/modules/messages/migrations/Migration20260213181243.js.map +7 -0
  197. package/dist/modules/messages/migrations/Migration20260215165126.js +16 -0
  198. package/dist/modules/messages/migrations/Migration20260215165126.js.map +7 -0
  199. package/dist/modules/messages/notifications.js +27 -0
  200. package/dist/modules/messages/notifications.js.map +7 -0
  201. package/dist/modules/messages/setup.js +21 -0
  202. package/dist/modules/messages/setup.js.map +7 -0
  203. package/dist/modules/messages/subscribers/message-notification.js +108 -0
  204. package/dist/modules/messages/subscribers/message-notification.js.map +7 -0
  205. package/dist/modules/messages/workers/send-email.worker.js +253 -0
  206. package/dist/modules/messages/workers/send-email.worker.js.map +7 -0
  207. package/dist/modules/sales/backend/sales/documents/[id]/page.js +30 -11
  208. package/dist/modules/sales/backend/sales/documents/[id]/page.js.map +2 -2
  209. package/dist/modules/sales/commands/payments.js +12 -6
  210. package/dist/modules/sales/commands/payments.js.map +2 -2
  211. package/dist/modules/sales/lib/messageObjectPreviews.js +114 -0
  212. package/dist/modules/sales/lib/messageObjectPreviews.js.map +7 -0
  213. package/dist/modules/sales/message-objects.js +57 -0
  214. package/dist/modules/sales/message-objects.js.map +7 -0
  215. package/dist/modules/sales/widgets/messages/SalesDocumentMessageDetail.js +51 -0
  216. package/dist/modules/sales/widgets/messages/SalesDocumentMessageDetail.js.map +7 -0
  217. package/dist/modules/sales/widgets/messages/SalesDocumentMessagePreview.js +36 -0
  218. package/dist/modules/sales/widgets/messages/SalesDocumentMessagePreview.js.map +7 -0
  219. package/dist/modules/sales/widgets/messages/index.js +7 -0
  220. package/dist/modules/sales/widgets/messages/index.js.map +7 -0
  221. package/dist/modules/staff/backend/staff/leave-requests/[id]/page.js +55 -1
  222. package/dist/modules/staff/backend/staff/leave-requests/[id]/page.js.map +2 -2
  223. package/dist/modules/staff/backend/staff/my-leave-requests/[id]/page.js +60 -1
  224. package/dist/modules/staff/backend/staff/my-leave-requests/[id]/page.js.map +2 -2
  225. package/dist/modules/staff/backend/staff/team-members/[id]/page.js +2 -19
  226. package/dist/modules/staff/backend/staff/team-members/[id]/page.js.map +2 -2
  227. package/dist/modules/staff/components/LeaveRequestDetail.js +112 -0
  228. package/dist/modules/staff/components/LeaveRequestDetail.js.map +7 -0
  229. package/dist/modules/staff/components/LeaveRequestForm.js +3 -1
  230. package/dist/modules/staff/components/LeaveRequestForm.js.map +2 -2
  231. package/dist/modules/staff/components/LeaveRequestPreview.js +43 -0
  232. package/dist/modules/staff/components/LeaveRequestPreview.js.map +7 -0
  233. package/dist/modules/staff/lib/messageObjectPreviews.js +148 -0
  234. package/dist/modules/staff/lib/messageObjectPreviews.js.map +7 -0
  235. package/dist/modules/staff/message-objects.js +104 -0
  236. package/dist/modules/staff/message-objects.js.map +7 -0
  237. package/dist/modules/staff/message-types.js +23 -0
  238. package/dist/modules/staff/message-types.js.map +7 -0
  239. package/dist/modules/staff/widgets/messages/StaffMessageObjectDetail.js +51 -0
  240. package/dist/modules/staff/widgets/messages/StaffMessageObjectDetail.js.map +7 -0
  241. package/dist/modules/staff/widgets/messages/StaffMessageObjectPreview.js +34 -0
  242. package/dist/modules/staff/widgets/messages/StaffMessageObjectPreview.js.map +7 -0
  243. package/dist/modules/staff/widgets/messages/index.js +7 -0
  244. package/dist/modules/staff/widgets/messages/index.js.map +7 -0
  245. package/generated/entities/message/index.ts +31 -0
  246. package/generated/entities/message_access_token/index.ts +8 -0
  247. package/generated/entities/message_confirmation/index.ts +9 -0
  248. package/generated/entities/message_object/index.ts +10 -0
  249. package/generated/entities/message_recipient/index.ts +14 -0
  250. package/generated/entities.ids.generated.ts +8 -0
  251. package/generated/entity-fields-registry.ts +10 -0
  252. package/jest.setup.ts +5 -0
  253. package/package.json +2 -2
  254. package/src/modules/customers/backend/customers/deals/[id]/page.tsx +20 -4
  255. package/src/modules/customers/i18n/de.json +4 -0
  256. package/src/modules/customers/i18n/en.json +4 -0
  257. package/src/modules/customers/i18n/es.json +4 -0
  258. package/src/modules/customers/i18n/pl.json +4 -0
  259. package/src/modules/customers/lib/messageObjectPreviews.ts +154 -0
  260. package/src/modules/customers/message-objects.ts +70 -0
  261. package/src/modules/customers/widgets/messages/CustomerMessageObjectDetail.tsx +57 -0
  262. package/src/modules/customers/widgets/messages/CustomerMessageObjectPreview.tsx +49 -0
  263. package/src/modules/customers/widgets/messages/index.ts +2 -0
  264. package/src/modules/messages/acl.ts +11 -0
  265. package/src/modules/messages/api/[id]/actions/[actionId]/route.ts +103 -0
  266. package/src/modules/messages/api/[id]/archive/route.ts +138 -0
  267. package/src/modules/messages/api/[id]/attachments/route.ts +217 -0
  268. package/src/modules/messages/api/[id]/confirmation/route.ts +73 -0
  269. package/src/modules/messages/api/[id]/conversation/archive/route.ts +69 -0
  270. package/src/modules/messages/api/[id]/conversation/read/route.ts +69 -0
  271. package/src/modules/messages/api/[id]/conversation/route.ts +69 -0
  272. package/src/modules/messages/api/[id]/forward/route.ts +87 -0
  273. package/src/modules/messages/api/[id]/forward-preview/route.ts +75 -0
  274. package/src/modules/messages/api/[id]/read/route.ts +138 -0
  275. package/src/modules/messages/api/[id]/reply/route.ts +89 -0
  276. package/src/modules/messages/api/[id]/route.ts +401 -0
  277. package/src/modules/messages/api/object-types/route.ts +54 -0
  278. package/src/modules/messages/api/openapi.ts +261 -0
  279. package/src/modules/messages/api/route.ts +374 -0
  280. package/src/modules/messages/api/token/[token]/route.ts +103 -0
  281. package/src/modules/messages/api/types/route.ts +39 -0
  282. package/src/modules/messages/api/unread-count/route.ts +55 -0
  283. package/src/modules/messages/backend/messages/[id]/page.meta.ts +12 -0
  284. package/src/modules/messages/backend/messages/[id]/page.tsx +12 -0
  285. package/src/modules/messages/backend/messages/compose/page.meta.ts +13 -0
  286. package/src/modules/messages/backend/messages/compose/page.tsx +12 -0
  287. package/src/modules/messages/backend/page.meta.ts +31 -0
  288. package/src/modules/messages/backend/page.tsx +12 -0
  289. package/src/modules/messages/commands/actions.ts +307 -0
  290. package/src/modules/messages/commands/attachments.ts +227 -0
  291. package/src/modules/messages/commands/confirmations.ts +183 -0
  292. package/src/modules/messages/commands/conversation.ts +292 -0
  293. package/src/modules/messages/commands/messages.ts +845 -0
  294. package/src/modules/messages/commands/recipients.ts +281 -0
  295. package/src/modules/messages/commands/shared.ts +350 -0
  296. package/src/modules/messages/commands/tokens.ts +80 -0
  297. package/src/modules/messages/components/ComposeMessagePageClient.tsx +23 -0
  298. package/src/modules/messages/components/MessageDetailPageClient.tsx +287 -0
  299. package/src/modules/messages/components/MessagesInboxPageClient.tsx +469 -0
  300. package/src/modules/messages/components/confirmation/MessageConfirmationActions.tsx +35 -0
  301. package/src/modules/messages/components/confirmation/MessageConfirmationContent.tsx +88 -0
  302. package/src/modules/messages/components/defaults/DefaultMessageActions.tsx +37 -0
  303. package/src/modules/messages/components/defaults/DefaultMessageContent.tsx +21 -0
  304. package/src/modules/messages/components/defaults/DefaultMessageListItem.tsx +102 -0
  305. package/src/modules/messages/components/defaults/MessageRecordObjectDetail.tsx +114 -0
  306. package/src/modules/messages/components/defaults/MessageRecordObjectPreview.tsx +74 -0
  307. package/src/modules/messages/components/message-detail/detail-panels.ts +13 -0
  308. package/src/modules/messages/components/message-detail/hooks/useMessageDetails.ts +56 -0
  309. package/src/modules/messages/components/message-detail/hooks/useMessageDetailsActions.ts +367 -0
  310. package/src/modules/messages/components/message-detail/hooks/useMessageDetailsConversation.ts +134 -0
  311. package/src/modules/messages/components/message-detail/hooks/useMessageDetailsQueries.ts +102 -0
  312. package/src/modules/messages/components/message-detail/panels/MainMessageHeader.tsx +108 -0
  313. package/src/modules/messages/components/message-detail/panels/MessageHeader.tsx +144 -0
  314. package/src/modules/messages/components/message-detail/panels/MessageListComponent.tsx +63 -0
  315. package/src/modules/messages/components/message-detail/panels/actions-panel.tsx +66 -0
  316. package/src/modules/messages/components/message-detail/panels/attachments-panel.tsx +86 -0
  317. package/src/modules/messages/components/message-detail/panels/body-panel.tsx +32 -0
  318. package/src/modules/messages/components/message-detail/panels/composer-dialogs.tsx +42 -0
  319. package/src/modules/messages/components/message-detail/panels/dialogs.tsx +107 -0
  320. package/src/modules/messages/components/message-detail/panels/index.ts +11 -0
  321. package/src/modules/messages/components/message-detail/panels/meta-panel.tsx +19 -0
  322. package/src/modules/messages/components/message-detail/panels/objects-panel.tsx +65 -0
  323. package/src/modules/messages/components/message-detail/panels/thread-panel.tsx +65 -0
  324. package/src/modules/messages/components/message-detail/types.ts +114 -0
  325. package/src/modules/messages/components/message-detail/utils.ts +62 -0
  326. package/src/modules/messages/components/utils/PriorityBadge.tsx +63 -0
  327. package/src/modules/messages/components/utils/typeUiRegistry.ts +106 -0
  328. package/src/modules/messages/data/entities.ts +284 -0
  329. package/src/modules/messages/data/validators.ts +297 -0
  330. package/src/modules/messages/emails/MessageEmail.tsx +143 -0
  331. package/src/modules/messages/events.ts +24 -0
  332. package/src/modules/messages/frontend/messages/view/[token]/page.meta.ts +5 -0
  333. package/src/modules/messages/frontend/messages/view/[token]/page.tsx +389 -0
  334. package/src/modules/messages/i18n/de.json +240 -0
  335. package/src/modules/messages/i18n/en.json +240 -0
  336. package/src/modules/messages/i18n/es.json +240 -0
  337. package/src/modules/messages/i18n/pl.json +240 -0
  338. package/src/modules/messages/index.ts +19 -0
  339. package/src/modules/messages/lib/actions.ts +204 -0
  340. package/src/modules/messages/lib/attachments.ts +197 -0
  341. package/src/modules/messages/lib/constants.ts +2 -0
  342. package/src/modules/messages/lib/email-sender.ts +255 -0
  343. package/src/modules/messages/lib/forwarding.ts +240 -0
  344. package/src/modules/messages/lib/message-objects-registry.ts +60 -0
  345. package/src/modules/messages/lib/message-types-registry.ts +48 -0
  346. package/src/modules/messages/lib/object-validation.ts +26 -0
  347. package/src/modules/messages/lib/operationMetadata.ts +43 -0
  348. package/src/modules/messages/lib/priorityUtils.ts +76 -0
  349. package/src/modules/messages/lib/routeHelpers.ts +65 -0
  350. package/src/modules/messages/message-objects.ts +5 -0
  351. package/src/modules/messages/message-types.ts +65 -0
  352. package/src/modules/messages/migrations/.snapshot-open-mercato.json +957 -0
  353. package/src/modules/messages/migrations/Migration20260213181243.ts +34 -0
  354. package/src/modules/messages/migrations/Migration20260215165126.ts +16 -0
  355. package/src/modules/messages/notifications.ts +25 -0
  356. package/src/modules/messages/setup.ts +19 -0
  357. package/src/modules/messages/subscribers/message-notification.ts +138 -0
  358. package/src/modules/messages/workers/send-email.worker.ts +321 -0
  359. package/src/modules/sales/backend/sales/documents/[id]/page.tsx +23 -7
  360. package/src/modules/sales/commands/payments.ts +12 -6
  361. package/src/modules/sales/i18n/de.json +3 -0
  362. package/src/modules/sales/i18n/en.json +3 -0
  363. package/src/modules/sales/i18n/es.json +3 -0
  364. package/src/modules/sales/i18n/pl.json +3 -0
  365. package/src/modules/sales/lib/messageObjectPreviews.ts +150 -0
  366. package/src/modules/sales/message-objects.ts +56 -0
  367. package/src/modules/sales/widgets/messages/SalesDocumentMessageDetail.tsx +57 -0
  368. package/src/modules/sales/widgets/messages/SalesDocumentMessagePreview.tsx +46 -0
  369. package/src/modules/sales/widgets/messages/index.ts +2 -0
  370. package/src/modules/staff/backend/staff/leave-requests/[id]/page.tsx +54 -0
  371. package/src/modules/staff/backend/staff/my-leave-requests/[id]/page.tsx +58 -0
  372. package/src/modules/staff/backend/staff/team-members/[id]/page.tsx +2 -32
  373. package/src/modules/staff/components/LeaveRequestDetail.tsx +135 -0
  374. package/src/modules/staff/components/LeaveRequestForm.tsx +3 -0
  375. package/src/modules/staff/components/LeaveRequestPreview.tsx +74 -0
  376. package/src/modules/staff/i18n/de.json +8 -0
  377. package/src/modules/staff/i18n/en.json +8 -0
  378. package/src/modules/staff/i18n/es.json +8 -0
  379. package/src/modules/staff/i18n/pl.json +8 -0
  380. package/src/modules/staff/lib/messageObjectPreviews.ts +182 -0
  381. package/src/modules/staff/message-objects.ts +102 -0
  382. package/src/modules/staff/message-types.ts +21 -0
  383. package/src/modules/staff/widgets/messages/StaffMessageObjectDetail.tsx +57 -0
  384. package/src/modules/staff/widgets/messages/StaffMessageObjectPreview.tsx +44 -0
  385. package/src/modules/staff/widgets/messages/index.ts +2 -0
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../../../src/modules/staff/backend/staff/team-members/%5Bid%5D/page.tsx"],
4
- "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport Link from 'next/link'\nimport dynamic from 'next/dynamic'\nimport { useRouter, useSearchParams } from 'next/navigation'\nimport type { PluggableList } from 'unified'\nimport { Page, PageBody } from '@open-mercato/ui/backend/Page'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { readApiResultOrThrow } from '@open-mercato/ui/backend/utils/apiCall'\nimport { extractCustomFieldEntries } from '@open-mercato/shared/lib/crud/custom-fields-client'\nimport { updateCrud, deleteCrud } from '@open-mercato/ui/backend/utils/crud'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { createTranslatorWithFallback } from '@open-mercato/shared/lib/i18n/translate'\nimport { AvailabilityRulesEditor } from '@open-mercato/core/modules/planner/components/AvailabilityRulesEditor'\nimport { buildMemberScheduleItems } from '@open-mercato/core/modules/staff/lib/memberSchedule'\nimport { TeamMemberForm, buildTeamMemberPayload, type TeamMemberFormValues } from '@open-mercato/core/modules/staff/components/TeamMemberForm'\nimport { NotesSection } from '@open-mercato/ui/backend/detail'\nimport { ActivitiesSection, type SectionAction } from '@open-mercato/ui/backend/detail'\nimport { AddressesSection as SharedAddressesSection } from '@open-mercato/ui/backend/detail'\nimport { renderDictionaryColor, renderDictionaryIcon, ICON_SUGGESTIONS } from '@open-mercato/core/modules/dictionaries/components/dictionaryAppearance'\nimport { createStaffNotesAdapter } from '@open-mercato/core/modules/staff/components/detail/notesAdapter'\nimport { createStaffActivitiesAdapter } from '@open-mercato/core/modules/staff/components/detail/activitiesAdapter'\nimport { createStaffAddressAdapter, createStaffAddressTypesAdapter } from '@open-mercato/core/modules/staff/components/detail/addressesAdapter'\nimport {\n createStaffDictionaryEntry,\n loadStaffDictionary,\n type DictionaryEntryOption,\n} from '@open-mercato/core/modules/staff/components/detail/dictionaries'\nimport { JobHistorySection } from '@open-mercato/core/modules/staff/components/detail/JobHistorySection'\nimport type { DictionarySelectLabels } from '@open-mercato/core/modules/dictionaries/components/DictionaryEntrySelect'\nimport { Plus } from 'lucide-react'\n\nconst isTestEnv = typeof process !== 'undefined' && process.env.NODE_ENV === 'test'\nconst MARKDOWN_CLASSNAME =\n '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'\n\ntype MarkdownPreviewProps = { children: string; className?: string; remarkPlugins?: PluggableList }\n\nconst MarkdownPreview: React.ComponentType<MarkdownPreviewProps> = isTestEnv\n ? ({ children, className }) => <div className={className}>{children}</div>\n : (dynamic(() => import('react-markdown').then((mod) => mod.default as React.ComponentType<MarkdownPreviewProps>), {\n ssr: false,\n loading: () => null,\n }) as unknown as React.ComponentType<MarkdownPreviewProps>)\n\nlet markdownPluginsPromise: Promise<PluggableList> | null = null\n\nasync function loadMarkdownPlugins(): Promise<PluggableList> {\n if (isTestEnv) return []\n if (!markdownPluginsPromise) {\n markdownPluginsPromise = import('remark-gfm')\n .then((mod) => [mod.default ?? mod] as PluggableList)\n .catch(() => [])\n }\n return markdownPluginsPromise\n}\n\ntype TeamMemberRecord = {\n id: string\n teamId?: string | null\n team_id?: string | null\n displayName: string\n display_name?: string\n description?: string | null\n userId?: string | null\n user_id?: string | null\n roleIds?: string[]\n role_ids?: string[]\n roleNames?: string[]\n tags?: string[]\n isActive?: boolean\n is_active?: boolean\n availabilityRuleSetId?: string | null\n availability_rule_set_id?: string | null\n user?: { id?: string; email?: string | null } | null\n team?: { id?: string; name?: string | null } | null\n customFields?: Record<string, unknown> | null\n} & Record<string, unknown>\n\ntype TeamMemberResponse = {\n items?: TeamMemberRecord[]\n}\n\nexport default function StaffTeamMemberDetailPage({ params }: { params?: { id?: string } }) {\n const memberId = params?.id\n const t = useT()\n const detailTranslator = React.useMemo(() => createTranslatorWithFallback(t), [t])\n const router = useRouter()\n const searchParams = useSearchParams()\n const [initialValues, setInitialValues] = React.useState<TeamMemberFormValues | null>(null)\n const [memberRecord, setMemberRecord] = React.useState<TeamMemberRecord | null>(null)\n const [availabilityRuleSetId, setAvailabilityRuleSetId] = React.useState<string | null>(null)\n const [activePanel, setActivePanel] = React.useState<'details' | 'availability' | 'jobHistory'>('details')\n const [activeTab, setActiveTab] = React.useState<'notes' | 'activities' | 'addresses'>('notes')\n const [sectionAction, setSectionAction] = React.useState<SectionAction | null>(null)\n const [activityDictionaryId, setActivityDictionaryId] = React.useState<string | null>(null)\n const [activityTypeEntries, setActivityTypeEntries] = React.useState<DictionaryEntryOption[]>([])\n const [markdownPlugins, setMarkdownPlugins] = React.useState<PluggableList>([])\n const flashShownRef = React.useRef(false)\n\n const notesAdapter = React.useMemo(() => createStaffNotesAdapter(detailTranslator), [detailTranslator])\n const activitiesAdapter = React.useMemo(() => createStaffActivitiesAdapter(detailTranslator), [detailTranslator])\n const addressesAdapter = React.useMemo(() => createStaffAddressAdapter(detailTranslator), [detailTranslator])\n const addressTypesAdapter = React.useMemo(() => createStaffAddressTypesAdapter(detailTranslator), [detailTranslator])\n\n React.useEffect(() => {\n void loadMarkdownPlugins().then((plugins) => setMarkdownPlugins(plugins))\n }, [])\n\n const activityTypeLabels = React.useMemo<DictionarySelectLabels>(() => ({\n placeholder: t('staff.teamMembers.detail.activities.dictionary.placeholder', 'Select an activity type'),\n addLabel: t('staff.teamMembers.detail.activities.dictionary.add', 'Add type'),\n addPrompt: t('staff.teamMembers.detail.activities.dictionary.prompt', 'Name the type'),\n dialogTitle: t('staff.teamMembers.detail.activities.dictionary.dialogTitle', 'Add activity type'),\n valueLabel: t('staff.teamMembers.detail.activities.dictionary.valueLabel', 'Name'),\n valuePlaceholder: t('staff.teamMembers.detail.activities.dictionary.valuePlaceholder', 'Name'),\n labelLabel: t('staff.teamMembers.detail.activities.dictionary.labelLabel', 'Label'),\n labelPlaceholder: t('staff.teamMembers.detail.activities.dictionary.labelPlaceholder', 'Display name shown in UI'),\n emptyError: t('staff.teamMembers.detail.activities.dictionary.emptyError', 'Please enter a name'),\n cancelLabel: t('staff.teamMembers.detail.activities.dictionary.cancel', 'Cancel'),\n saveLabel: t('staff.teamMembers.detail.activities.dictionary.save', 'Save'),\n saveShortcutHint: t('staff.teamMembers.detail.activities.dictionary.saveShortcut', '\u2318/Ctrl + Enter'),\n errorLoad: t('staff.teamMembers.detail.activities.dictionary.errorLoad', 'Failed to load options'),\n errorSave: t('staff.teamMembers.detail.activities.dictionary.errorSave', 'Failed to save option'),\n loadingLabel: t('staff.teamMembers.detail.activities.dictionary.loading', 'Loading\u2026'),\n manageTitle: t('staff.teamMembers.detail.activities.dictionary.manage', 'Manage dictionary'),\n }), [t])\n\n const loadActivityOptions = React.useCallback(async () => {\n const { dictionary, entries } = await loadStaffDictionary('activityTypes')\n setActivityDictionaryId(dictionary?.id ?? null)\n setActivityTypeEntries(entries)\n return entries\n }, [])\n\n const createActivityOption = React.useCallback(\n async (input: { value: string; label?: string; color?: string | null; icon?: string | null }) => {\n const entry = await createStaffDictionaryEntry('activityTypes', input)\n if (!entry) {\n throw new Error(t('staff.teamMembers.detail.activities.dictionary.errorSave', 'Failed to save option'))\n }\n return entry\n },\n [t],\n )\n\n React.useEffect(() => {\n loadActivityOptions().catch(() => {})\n }, [loadActivityOptions])\n\n const activityTypeMap = React.useMemo(\n () => new Map(activityTypeEntries.map((entry) => [entry.value, entry])),\n [activityTypeEntries],\n )\n\n const resolveActivityPresentation = React.useCallback(\n (activity: { activityType: string; appearanceIcon?: string | null; appearanceColor?: string | null }) => {\n const entry = activityTypeMap.get(activity.activityType)\n return {\n label: entry?.label ?? activity.activityType,\n icon: entry?.icon ?? activity.appearanceIcon ?? null,\n color: entry?.color ?? activity.appearanceColor ?? null,\n }\n },\n [activityTypeMap],\n )\n\n const manageActivityHref = React.useMemo(() => {\n if (!activityDictionaryId) return '/backend/config/dictionaries'\n return `/backend/config/dictionaries?dictionaryId=${encodeURIComponent(activityDictionaryId)}`\n }, [activityDictionaryId])\n\n const appearanceLabels = React.useMemo(() => ({\n colorLabel: t('staff.teamMembers.detail.activities.appearance.colorLabel', 'Color'),\n colorHelp: t('staff.teamMembers.detail.activities.appearance.colorHelp', 'Pick a highlight color for this entry.'),\n colorClearLabel: t('staff.teamMembers.detail.activities.appearance.colorClear', 'Remove color'),\n iconLabel: t('staff.teamMembers.detail.activities.appearance.iconLabel', 'Icon or emoji'),\n iconPlaceholder: t('staff.teamMembers.detail.activities.appearance.iconPlaceholder', 'Type an emoji or pick one of the suggestions.'),\n iconPickerTriggerLabel: t('staff.teamMembers.detail.activities.appearance.iconBrowse', 'Browse icons and emojis'),\n iconSearchPlaceholder: t('staff.teamMembers.detail.activities.appearance.iconSearchPlaceholder', 'Search icons or emojis\u2026'),\n iconSearchEmptyLabel: t('staff.teamMembers.detail.activities.appearance.iconSearchEmpty', 'No icons match your search.'),\n iconSuggestionsLabel: t('staff.teamMembers.detail.activities.appearance.iconSuggestions', 'Suggestions'),\n iconClearLabel: t('staff.teamMembers.detail.activities.appearance.iconClear', 'Remove icon'),\n previewEmptyLabel: t('staff.teamMembers.detail.activities.appearance.previewEmpty', 'No appearance selected'),\n }), [t])\n\n const renderCustomFields = React.useCallback((activity: { id?: string; customFields?: Array<{ key: string; label?: string | null; value: unknown }> }) => {\n const entries = Array.isArray(activity.customFields) ? activity.customFields : []\n if (!entries.length) return null\n const emptyLabel = t('staff.teamMembers.detail.activities.customFields.empty', 'Not provided')\n return (\n <div className=\"grid gap-3 sm:grid-cols-2\">\n {entries.map((entry, index) => {\n const label = entry.label ?? entry.key\n const value = entry.value\n const hasValue = !(value == null || value === '' || (Array.isArray(value) && value.length === 0))\n const content = hasValue\n ? Array.isArray(value)\n ? value.map((item) => String(item)).join(', ')\n : String(value)\n : emptyLabel\n return (\n <div\n key={`activity-${activity.id ?? 'row'}-custom-${index}`}\n className=\"rounded-md border border-border/60 bg-muted/10 px-3 py-2\"\n >\n <div className=\"text-xs font-medium text-muted-foreground\">{label}</div>\n <div className=\"mt-1 text-sm text-foreground\">{content}</div>\n </div>\n )\n })}\n </div>\n )\n }, [t])\n\n React.useEffect(() => {\n if (!memberId) return\n const memberIdValue = memberId\n let cancelled = false\n async function loadMember() {\n try {\n const params = new URLSearchParams({ page: '1', pageSize: '1', ids: memberIdValue })\n const payload = await readApiResultOrThrow<TeamMemberResponse>(\n `/api/staff/team-members?${params.toString()}`,\n undefined,\n { errorMessage: t('staff.teamMembers.form.errors.load', 'Failed to load team member.') },\n )\n const record = Array.isArray(payload.items) ? payload.items[0] : null\n if (!record) throw new Error(t('staff.teamMembers.form.errors.notFound', 'Team member not found.'))\n const customFields = extractCustomFieldEntries(record)\n if (!cancelled) {\n const resolvedTeamId = record.teamId ?? record.team_id ?? null\n const normalizedRoleIds = normalizeStringList(resolvePreferredArray(record.roleIds, record.role_ids))\n setInitialValues({\n id: record.id,\n teamId: resolvedTeamId,\n userId: record.userId ?? record.user_id ?? null,\n displayName: record.displayName ?? record.display_name ?? '',\n description: record.description ?? '',\n roleIds: normalizedRoleIds,\n tags: normalizeStringList(record.tags),\n isActive: record.isActive ?? record.is_active ?? true,\n ...customFields,\n })\n setMemberRecord(record)\n setAvailabilityRuleSetId(\n typeof record.availabilityRuleSetId === 'string'\n ? record.availabilityRuleSetId\n : typeof record.availability_rule_set_id === 'string'\n ? record.availability_rule_set_id\n : null,\n )\n }\n } catch (error) {\n const message = error instanceof Error ? error.message : t('staff.teamMembers.form.errors.load', 'Failed to load team member.')\n flash(message, 'error')\n }\n }\n void loadMember()\n return () => { cancelled = true }\n }, [memberId, t])\n\n React.useEffect(() => {\n if (!searchParams) return\n const created = searchParams.get('created') === '1'\n if (created && !flashShownRef.current) {\n flashShownRef.current = true\n flash(t('staff.teamMembers.flash.createdAvailability', 'Saved. You can now set availability.'), 'success')\n const nextParams = new URLSearchParams(searchParams.toString())\n nextParams.delete('created')\n const nextQuery = nextParams.toString()\n const nextPath = memberId\n ? `/backend/staff/team-members/${encodeURIComponent(memberId)}${nextQuery ? `?${nextQuery}` : ''}`\n : `/backend/staff/team-members${nextQuery ? `?${nextQuery}` : ''}`\n router.replace(nextPath)\n }\n }, [memberId, router, searchParams, t])\n\n const handleSubmit = React.useCallback(async (values: TeamMemberFormValues) => {\n if (!memberId) return\n const payload = buildTeamMemberPayload(values, { id: memberId })\n await updateCrud('staff/team-members', payload, {\n errorMessage: t('staff.teamMembers.form.errors.update', 'Failed to update team member.'),\n })\n flash(t('staff.teamMembers.form.flash.updated', 'Team member updated.'), 'success')\n router.push('/backend/staff/team-members')\n }, [memberId, router, t])\n\n const handleDelete = React.useCallback(async () => {\n if (!memberId) return\n await deleteCrud('staff/team-members', memberId, {\n errorMessage: t('staff.teamMembers.form.errors.delete', 'Failed to delete team member.'),\n })\n flash(t('staff.teamMembers.form.flash.deleted', 'Team member deleted.'), 'success')\n router.push('/backend/staff/team-members')\n }, [memberId, router, t])\n\n const handleRulesetChange = React.useCallback(async (nextId: string | null) => {\n if (!memberId) return\n await updateCrud('staff/team-members', { id: memberId, availabilityRuleSetId: nextId }, {\n errorMessage: t('staff.teamMembers.availability.ruleset.updateError', 'Failed to update schedule.'),\n })\n setAvailabilityRuleSetId(nextId)\n flash(t('staff.teamMembers.availability.ruleset.updateSuccess', 'Schedule updated.'), 'success')\n }, [memberId, t])\n\n const panelTabs = React.useMemo(() => ([\n { id: 'details' as const, label: t('staff.teamMembers.detail.tabs.details', 'Details') },\n { id: 'availability' as const, label: t('staff.teamMembers.detail.tabs.availability', 'Availability') },\n { id: 'jobHistory' as const, label: t('staff.teamMembers.detail.tabs.jobHistory', 'Job history') },\n ]), [t])\n\n const tabs = React.useMemo(() => ([\n { id: 'notes' as const, label: t('staff.teamMembers.detail.tabs.notes', 'Notes') },\n { id: 'activities' as const, label: t('staff.teamMembers.detail.tabs.activities', 'Activities') },\n { id: 'addresses' as const, label: t('staff.teamMembers.detail.tabs.addresses', 'Addresses') },\n ]), [t])\n\n const resolvedInitialValues = initialValues ?? {\n roleIds: [],\n isActive: true,\n }\n\n const displayName = memberRecord?.displayName ?? memberRecord?.display_name ?? resolvedInitialValues.displayName ?? ''\n const teamLabel = memberRecord?.team?.name ?? t('staff.teamMembers.detail.team.unassigned', 'Unassigned team')\n const roleLabels = Array.isArray(memberRecord?.roleNames) && memberRecord?.roleNames.length\n ? memberRecord?.roleNames\n : [t('staff.teamMembers.detail.roles.unassigned', 'No roles assigned')]\n const userEmail = memberRecord?.user?.email ?? null\n\n return (\n <Page>\n <PageBody>\n <div className=\"space-y-6\">\n <div className=\"flex flex-wrap items-center justify-between gap-3\">\n <div className=\"flex items-center gap-3\">\n <Link\n href=\"/backend/staff/team-members\"\n className=\"inline-flex items-center text-sm text-muted-foreground hover:text-foreground\"\n >\n <span aria-hidden className=\"mr-1 text-base\">\u2190</span>\n <span className=\"sr-only\">{t('staff.teamMembers.detail.back', 'Back to team members')}</span>\n </Link>\n <div className=\"space-y-1\">\n <h1 className=\"text-2xl font-semibold text-foreground\">\n {displayName || t('staff.teamMembers.detail.untitled', 'Unnamed team member')}\n </h1>\n <p className=\"text-sm text-muted-foreground\">\n {t('staff.teamMembers.detail.subtitle', 'Team member profile and activity')}\n </p>\n </div>\n </div>\n </div>\n\n <div className=\"border-b\">\n <nav\n className=\"flex flex-wrap items-center gap-5 text-sm\"\n aria-label={t('staff.teamMembers.detail.tabs.label', 'Team member sections')}\n >\n {panelTabs.map((tab) => (\n <button\n key={tab.id}\n type=\"button\"\n role=\"tab\"\n aria-selected={activePanel === tab.id}\n onClick={() => setActivePanel(tab.id)}\n className={`relative -mb-px border-b-2 px-0 py-2 text-sm font-medium transition-colors ${\n activePanel === tab.id\n ? 'border-primary text-foreground'\n : 'border-transparent text-muted-foreground hover:text-foreground'\n }`}\n >\n {tab.label}\n </button>\n ))}\n </nav>\n </div>\n\n {activePanel === 'details' ? (\n <>\n <div className=\"grid gap-6 lg:grid-cols-[minmax(0,2fr),minmax(0,1.1fr)]\">\n <div className=\"space-y-6\">\n <div className=\"rounded-lg border bg-card p-4\">\n <h2 className=\"mb-4 text-sm font-semibold uppercase text-muted-foreground\">\n {t('staff.teamMembers.detail.highlights', 'Highlights')}\n </h2>\n <div className=\"grid gap-4 sm:grid-cols-2\">\n <div>\n <p className=\"text-xs font-medium uppercase text-muted-foreground\">\n {t('staff.teamMembers.detail.fields.team', 'Team')}\n </p>\n <p className=\"text-base text-foreground\">{teamLabel}</p>\n </div>\n <div>\n <p className=\"text-xs font-medium uppercase text-muted-foreground\">\n {t('staff.teamMembers.detail.fields.roles', 'Roles')}\n </p>\n <p className=\"text-base text-foreground\">{roleLabels.join(', ')}</p>\n </div>\n <div>\n <p className=\"text-xs font-medium uppercase text-muted-foreground\">\n {t('staff.teamMembers.detail.fields.user', 'User')}\n </p>\n <p className=\"text-base text-foreground\">\n {userEmail ?? t('staff.teamMembers.detail.fields.userEmpty', 'No user linked')}\n </p>\n </div>\n <div>\n <p className=\"text-xs font-medium uppercase text-muted-foreground\">\n {t('staff.teamMembers.detail.fields.status', 'Status')}\n </p>\n <p className=\"text-base text-foreground\">\n {memberRecord?.isActive ?? memberRecord?.is_active\n ? t('staff.teamMembers.detail.status.active', 'Active')\n : t('staff.teamMembers.detail.status.inactive', 'Inactive')}\n </p>\n </div>\n </div>\n </div>\n\n <div className=\"rounded-lg border bg-card p-4\">\n <div className=\"flex flex-wrap items-center justify-between gap-3\">\n <div className=\"flex gap-2\">\n {tabs.map((tab) => (\n <button\n key={tab.id}\n type=\"button\"\n onClick={() => setActiveTab(tab.id)}\n className={`relative -mb-px border-b-2 px-0 py-1 text-sm font-medium transition-colors ${\n activeTab === tab.id\n ? 'border-primary text-foreground'\n : 'border-transparent text-muted-foreground hover:text-foreground'\n }`}\n >\n {tab.label}\n </button>\n ))}\n </div>\n {sectionAction ? (\n <Button\n type=\"button\"\n size=\"sm\"\n disabled={sectionAction.disabled}\n onClick={() => sectionAction.onClick()}\n >\n {sectionAction.icon ?? (activeTab === 'addresses' ? <Plus className=\"mr-2 h-4 w-4\" /> : null)}\n {sectionAction.label}\n </Button>\n ) : null}\n </div>\n {activeTab === 'notes' ? (\n <NotesSection\n entityId={memberId ?? null}\n emptyLabel={t('staff.teamMembers.detail.notes.empty', 'No notes yet.')}\n viewerUserId={null}\n viewerName={null}\n viewerEmail={null}\n addActionLabel={t('staff.teamMembers.detail.notes.add', 'Add note')}\n emptyState={{\n title: t('staff.teamMembers.detail.notes.emptyTitle', 'Keep everyone in the loop'),\n actionLabel: t('staff.teamMembers.detail.notes.emptyAction', 'Add a note'),\n }}\n onActionChange={setSectionAction}\n translator={detailTranslator}\n labelPrefix=\"staff.teamMembers.detail.notes\"\n inlineLabelPrefix=\"staff.teamMembers.detail.inline\"\n dataAdapter={notesAdapter}\n renderIcon={renderDictionaryIcon}\n renderColor={renderDictionaryColor}\n iconSuggestions={ICON_SUGGESTIONS}\n />\n ) : null}\n {activeTab === 'activities' ? (\n <ActivitiesSection\n entityId={memberId ?? null}\n addActionLabel={t('staff.teamMembers.detail.activities.add', 'Log activity')}\n emptyState={{\n title: t('staff.teamMembers.detail.activities.emptyTitle', 'No activities yet'),\n actionLabel: t('staff.teamMembers.detail.activities.emptyAction', 'Add an activity'),\n }}\n onActionChange={setSectionAction}\n dataAdapter={activitiesAdapter}\n activityTypeLabels={activityTypeLabels}\n loadActivityOptions={loadActivityOptions}\n createActivityOption={createActivityOption}\n resolveActivityPresentation={resolveActivityPresentation}\n renderCustomFields={renderCustomFields}\n labelPrefix=\"staff.teamMembers.detail.activities\"\n renderIcon={renderDictionaryIcon}\n renderColor={renderDictionaryColor}\n appearanceLabels={appearanceLabels}\n manageHref={manageActivityHref}\n customFieldEntityIds={['staff:staff_team_member_activity']}\n />\n ) : null}\n {activeTab === 'addresses' ? (\n <SharedAddressesSection\n entityId={memberId ?? null}\n emptyLabel={t('staff.teamMembers.detail.addresses.empty', 'No addresses yet.')}\n addActionLabel={t('staff.teamMembers.detail.addresses.add', 'Add address')}\n emptyState={{\n title: t('staff.teamMembers.detail.addresses.emptyTitle', 'No addresses yet'),\n actionLabel: t('staff.teamMembers.detail.addresses.emptyAction', 'Add an address'),\n }}\n onActionChange={setSectionAction}\n dataAdapter={addressesAdapter}\n addressTypesAdapter={addressTypesAdapter}\n labelPrefix=\"staff.teamMembers.detail.addresses\"\n />\n ) : null}\n </div>\n </div>\n <div className=\"space-y-4\">\n <div className=\"rounded-lg border bg-card p-4\">\n <h2 className=\"mb-4 text-sm font-semibold uppercase text-muted-foreground\">\n {t('staff.teamMembers.detail.details', 'Member details')}\n </h2>\n <div className=\"space-y-2\">\n {memberRecord?.description ? (\n <MarkdownPreview remarkPlugins={markdownPlugins} className={MARKDOWN_CLASSNAME}>\n {memberRecord.description}\n </MarkdownPreview>\n ) : (\n <p className=\"text-sm text-muted-foreground\">\n {t('staff.teamMembers.detail.descriptionEmpty', 'No description provided.')}\n </p>\n )}\n </div>\n </div>\n </div>\n </div>\n\n <div className=\"rounded-lg border bg-card p-4\">\n <h2 className=\"mb-4 text-sm font-semibold uppercase text-muted-foreground\">\n {t('staff.teamMembers.detail.formTitle', 'Member settings')}\n </h2>\n <TeamMemberForm\n embedded\n title={t('staff.teamMembers.form.editTitle', 'Edit team member')}\n backHref=\"/backend/staff/team-members\"\n cancelHref=\"/backend/staff/team-members\"\n initialValues={resolvedInitialValues}\n onSubmit={handleSubmit}\n onDelete={handleDelete}\n isLoading={!initialValues}\n loadingMessage={t('staff.teamMembers.form.loading', 'Loading team member...')}\n />\n </div>\n </>\n ) : activePanel === 'availability' ? (\n <AvailabilityRulesEditor\n subjectType=\"member\"\n subjectId={memberId ?? ''}\n labelPrefix=\"staff.teamMembers\"\n mode=\"availability\"\n rulesetId={availabilityRuleSetId}\n onRulesetChange={handleRulesetChange}\n buildScheduleItems={({ availabilityRules, translate: translateLabel }) => (\n buildMemberScheduleItems({ availabilityRules, translate: translateLabel })\n )}\n />\n ) : (\n <div className=\"rounded-lg border bg-card p-4\">\n <JobHistorySection memberId={memberId ?? null} />\n </div>\n )}\n </div>\n </PageBody>\n </Page>\n )\n}\n\nfunction normalizeStringList(value: unknown): string[] {\n if (!Array.isArray(value)) return []\n return value\n .map((entry) => (typeof entry === 'string' ? entry.trim() : ''))\n .filter((entry) => entry.length > 0)\n}\n\nfunction resolvePreferredArray<T>(primary?: T[] | null, fallback?: T[] | null): T[] | undefined {\n if (Array.isArray(primary) && primary.length) return primary\n if (Array.isArray(fallback) && fallback.length) return fallback\n return Array.isArray(primary) ? primary : Array.isArray(fallback) ? fallback : undefined\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport Link from 'next/link'\nimport { useRouter, useSearchParams } from 'next/navigation'\nimport { Page, PageBody } from '@open-mercato/ui/backend/Page'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { readApiResultOrThrow } from '@open-mercato/ui/backend/utils/apiCall'\nimport { extractCustomFieldEntries } from '@open-mercato/shared/lib/crud/custom-fields-client'\nimport { updateCrud, deleteCrud } from '@open-mercato/ui/backend/utils/crud'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { createTranslatorWithFallback } from '@open-mercato/shared/lib/i18n/translate'\nimport { AvailabilityRulesEditor } from '@open-mercato/core/modules/planner/components/AvailabilityRulesEditor'\nimport { buildMemberScheduleItems } from '@open-mercato/core/modules/staff/lib/memberSchedule'\nimport { TeamMemberForm, buildTeamMemberPayload, type TeamMemberFormValues } from '@open-mercato/core/modules/staff/components/TeamMemberForm'\nimport { NotesSection } from '@open-mercato/ui/backend/detail'\nimport { ActivitiesSection, type SectionAction } from '@open-mercato/ui/backend/detail'\nimport { AddressesSection as SharedAddressesSection } from '@open-mercato/ui/backend/detail'\nimport { renderDictionaryColor, renderDictionaryIcon, ICON_SUGGESTIONS } from '@open-mercato/core/modules/dictionaries/components/dictionaryAppearance'\nimport { createStaffNotesAdapter } from '@open-mercato/core/modules/staff/components/detail/notesAdapter'\nimport { createStaffActivitiesAdapter } from '@open-mercato/core/modules/staff/components/detail/activitiesAdapter'\nimport { createStaffAddressAdapter, createStaffAddressTypesAdapter } from '@open-mercato/core/modules/staff/components/detail/addressesAdapter'\nimport { MarkdownContent } from '@open-mercato/ui/backend/markdown/MarkdownContent'\nimport {\n createStaffDictionaryEntry,\n loadStaffDictionary,\n type DictionaryEntryOption,\n} from '@open-mercato/core/modules/staff/components/detail/dictionaries'\nimport { JobHistorySection } from '@open-mercato/core/modules/staff/components/detail/JobHistorySection'\nimport type { DictionarySelectLabels } from '@open-mercato/core/modules/dictionaries/components/DictionaryEntrySelect'\nimport { Plus } from 'lucide-react'\n\nconst MARKDOWN_CLASSNAME =\n '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'\n\ntype TeamMemberRecord = {\n id: string\n teamId?: string | null\n team_id?: string | null\n displayName: string\n display_name?: string\n description?: string | null\n userId?: string | null\n user_id?: string | null\n roleIds?: string[]\n role_ids?: string[]\n roleNames?: string[]\n tags?: string[]\n isActive?: boolean\n is_active?: boolean\n availabilityRuleSetId?: string | null\n availability_rule_set_id?: string | null\n user?: { id?: string; email?: string | null } | null\n team?: { id?: string; name?: string | null } | null\n customFields?: Record<string, unknown> | null\n} & Record<string, unknown>\n\ntype TeamMemberResponse = {\n items?: TeamMemberRecord[]\n}\n\nexport default function StaffTeamMemberDetailPage({ params }: { params?: { id?: string } }) {\n const memberId = params?.id\n const t = useT()\n const detailTranslator = React.useMemo(() => createTranslatorWithFallback(t), [t])\n const router = useRouter()\n const searchParams = useSearchParams()\n const [initialValues, setInitialValues] = React.useState<TeamMemberFormValues | null>(null)\n const [memberRecord, setMemberRecord] = React.useState<TeamMemberRecord | null>(null)\n const [availabilityRuleSetId, setAvailabilityRuleSetId] = React.useState<string | null>(null)\n const [activePanel, setActivePanel] = React.useState<'details' | 'availability' | 'jobHistory'>('details')\n const [activeTab, setActiveTab] = React.useState<'notes' | 'activities' | 'addresses'>('notes')\n const [sectionAction, setSectionAction] = React.useState<SectionAction | null>(null)\n const [activityDictionaryId, setActivityDictionaryId] = React.useState<string | null>(null)\n const [activityTypeEntries, setActivityTypeEntries] = React.useState<DictionaryEntryOption[]>([])\n const flashShownRef = React.useRef(false)\n\n const notesAdapter = React.useMemo(() => createStaffNotesAdapter(detailTranslator), [detailTranslator])\n const activitiesAdapter = React.useMemo(() => createStaffActivitiesAdapter(detailTranslator), [detailTranslator])\n const addressesAdapter = React.useMemo(() => createStaffAddressAdapter(detailTranslator), [detailTranslator])\n const addressTypesAdapter = React.useMemo(() => createStaffAddressTypesAdapter(detailTranslator), [detailTranslator])\n\n const activityTypeLabels = React.useMemo<DictionarySelectLabels>(() => ({\n placeholder: t('staff.teamMembers.detail.activities.dictionary.placeholder', 'Select an activity type'),\n addLabel: t('staff.teamMembers.detail.activities.dictionary.add', 'Add type'),\n addPrompt: t('staff.teamMembers.detail.activities.dictionary.prompt', 'Name the type'),\n dialogTitle: t('staff.teamMembers.detail.activities.dictionary.dialogTitle', 'Add activity type'),\n valueLabel: t('staff.teamMembers.detail.activities.dictionary.valueLabel', 'Name'),\n valuePlaceholder: t('staff.teamMembers.detail.activities.dictionary.valuePlaceholder', 'Name'),\n labelLabel: t('staff.teamMembers.detail.activities.dictionary.labelLabel', 'Label'),\n labelPlaceholder: t('staff.teamMembers.detail.activities.dictionary.labelPlaceholder', 'Display name shown in UI'),\n emptyError: t('staff.teamMembers.detail.activities.dictionary.emptyError', 'Please enter a name'),\n cancelLabel: t('staff.teamMembers.detail.activities.dictionary.cancel', 'Cancel'),\n saveLabel: t('staff.teamMembers.detail.activities.dictionary.save', 'Save'),\n saveShortcutHint: t('staff.teamMembers.detail.activities.dictionary.saveShortcut', '\u2318/Ctrl + Enter'),\n errorLoad: t('staff.teamMembers.detail.activities.dictionary.errorLoad', 'Failed to load options'),\n errorSave: t('staff.teamMembers.detail.activities.dictionary.errorSave', 'Failed to save option'),\n loadingLabel: t('staff.teamMembers.detail.activities.dictionary.loading', 'Loading\u2026'),\n manageTitle: t('staff.teamMembers.detail.activities.dictionary.manage', 'Manage dictionary'),\n }), [t])\n\n const loadActivityOptions = React.useCallback(async () => {\n const { dictionary, entries } = await loadStaffDictionary('activityTypes')\n setActivityDictionaryId(dictionary?.id ?? null)\n setActivityTypeEntries(entries)\n return entries\n }, [])\n\n const createActivityOption = React.useCallback(\n async (input: { value: string; label?: string; color?: string | null; icon?: string | null }) => {\n const entry = await createStaffDictionaryEntry('activityTypes', input)\n if (!entry) {\n throw new Error(t('staff.teamMembers.detail.activities.dictionary.errorSave', 'Failed to save option'))\n }\n return entry\n },\n [t],\n )\n\n React.useEffect(() => {\n loadActivityOptions().catch(() => {})\n }, [loadActivityOptions])\n\n const activityTypeMap = React.useMemo(\n () => new Map(activityTypeEntries.map((entry) => [entry.value, entry])),\n [activityTypeEntries],\n )\n\n const resolveActivityPresentation = React.useCallback(\n (activity: { activityType: string; appearanceIcon?: string | null; appearanceColor?: string | null }) => {\n const entry = activityTypeMap.get(activity.activityType)\n return {\n label: entry?.label ?? activity.activityType,\n icon: entry?.icon ?? activity.appearanceIcon ?? null,\n color: entry?.color ?? activity.appearanceColor ?? null,\n }\n },\n [activityTypeMap],\n )\n\n const manageActivityHref = React.useMemo(() => {\n if (!activityDictionaryId) return '/backend/config/dictionaries'\n return `/backend/config/dictionaries?dictionaryId=${encodeURIComponent(activityDictionaryId)}`\n }, [activityDictionaryId])\n\n const appearanceLabels = React.useMemo(() => ({\n colorLabel: t('staff.teamMembers.detail.activities.appearance.colorLabel', 'Color'),\n colorHelp: t('staff.teamMembers.detail.activities.appearance.colorHelp', 'Pick a highlight color for this entry.'),\n colorClearLabel: t('staff.teamMembers.detail.activities.appearance.colorClear', 'Remove color'),\n iconLabel: t('staff.teamMembers.detail.activities.appearance.iconLabel', 'Icon or emoji'),\n iconPlaceholder: t('staff.teamMembers.detail.activities.appearance.iconPlaceholder', 'Type an emoji or pick one of the suggestions.'),\n iconPickerTriggerLabel: t('staff.teamMembers.detail.activities.appearance.iconBrowse', 'Browse icons and emojis'),\n iconSearchPlaceholder: t('staff.teamMembers.detail.activities.appearance.iconSearchPlaceholder', 'Search icons or emojis\u2026'),\n iconSearchEmptyLabel: t('staff.teamMembers.detail.activities.appearance.iconSearchEmpty', 'No icons match your search.'),\n iconSuggestionsLabel: t('staff.teamMembers.detail.activities.appearance.iconSuggestions', 'Suggestions'),\n iconClearLabel: t('staff.teamMembers.detail.activities.appearance.iconClear', 'Remove icon'),\n previewEmptyLabel: t('staff.teamMembers.detail.activities.appearance.previewEmpty', 'No appearance selected'),\n }), [t])\n\n const renderCustomFields = React.useCallback((activity: { id?: string; customFields?: Array<{ key: string; label?: string | null; value: unknown }> }) => {\n const entries = Array.isArray(activity.customFields) ? activity.customFields : []\n if (!entries.length) return null\n const emptyLabel = t('staff.teamMembers.detail.activities.customFields.empty', 'Not provided')\n return (\n <div className=\"grid gap-3 sm:grid-cols-2\">\n {entries.map((entry, index) => {\n const label = entry.label ?? entry.key\n const value = entry.value\n const hasValue = !(value == null || value === '' || (Array.isArray(value) && value.length === 0))\n const content = hasValue\n ? Array.isArray(value)\n ? value.map((item) => String(item)).join(', ')\n : String(value)\n : emptyLabel\n return (\n <div\n key={`activity-${activity.id ?? 'row'}-custom-${index}`}\n className=\"rounded-md border border-border/60 bg-muted/10 px-3 py-2\"\n >\n <div className=\"text-xs font-medium text-muted-foreground\">{label}</div>\n <div className=\"mt-1 text-sm text-foreground\">{content}</div>\n </div>\n )\n })}\n </div>\n )\n }, [t])\n\n React.useEffect(() => {\n if (!memberId) return\n const memberIdValue = memberId\n let cancelled = false\n async function loadMember() {\n try {\n const params = new URLSearchParams({ page: '1', pageSize: '1', ids: memberIdValue })\n const payload = await readApiResultOrThrow<TeamMemberResponse>(\n `/api/staff/team-members?${params.toString()}`,\n undefined,\n { errorMessage: t('staff.teamMembers.form.errors.load', 'Failed to load team member.') },\n )\n const record = Array.isArray(payload.items) ? payload.items[0] : null\n if (!record) throw new Error(t('staff.teamMembers.form.errors.notFound', 'Team member not found.'))\n const customFields = extractCustomFieldEntries(record)\n if (!cancelled) {\n const resolvedTeamId = record.teamId ?? record.team_id ?? null\n const normalizedRoleIds = normalizeStringList(resolvePreferredArray(record.roleIds, record.role_ids))\n setInitialValues({\n id: record.id,\n teamId: resolvedTeamId,\n userId: record.userId ?? record.user_id ?? null,\n displayName: record.displayName ?? record.display_name ?? '',\n description: record.description ?? '',\n roleIds: normalizedRoleIds,\n tags: normalizeStringList(record.tags),\n isActive: record.isActive ?? record.is_active ?? true,\n ...customFields,\n })\n setMemberRecord(record)\n setAvailabilityRuleSetId(\n typeof record.availabilityRuleSetId === 'string'\n ? record.availabilityRuleSetId\n : typeof record.availability_rule_set_id === 'string'\n ? record.availability_rule_set_id\n : null,\n )\n }\n } catch (error) {\n const message = error instanceof Error ? error.message : t('staff.teamMembers.form.errors.load', 'Failed to load team member.')\n flash(message, 'error')\n }\n }\n void loadMember()\n return () => { cancelled = true }\n }, [memberId, t])\n\n React.useEffect(() => {\n if (!searchParams) return\n const created = searchParams.get('created') === '1'\n if (created && !flashShownRef.current) {\n flashShownRef.current = true\n flash(t('staff.teamMembers.flash.createdAvailability', 'Saved. You can now set availability.'), 'success')\n const nextParams = new URLSearchParams(searchParams.toString())\n nextParams.delete('created')\n const nextQuery = nextParams.toString()\n const nextPath = memberId\n ? `/backend/staff/team-members/${encodeURIComponent(memberId)}${nextQuery ? `?${nextQuery}` : ''}`\n : `/backend/staff/team-members${nextQuery ? `?${nextQuery}` : ''}`\n router.replace(nextPath)\n }\n }, [memberId, router, searchParams, t])\n\n const handleSubmit = React.useCallback(async (values: TeamMemberFormValues) => {\n if (!memberId) return\n const payload = buildTeamMemberPayload(values, { id: memberId })\n await updateCrud('staff/team-members', payload, {\n errorMessage: t('staff.teamMembers.form.errors.update', 'Failed to update team member.'),\n })\n flash(t('staff.teamMembers.form.flash.updated', 'Team member updated.'), 'success')\n router.push('/backend/staff/team-members')\n }, [memberId, router, t])\n\n const handleDelete = React.useCallback(async () => {\n if (!memberId) return\n await deleteCrud('staff/team-members', memberId, {\n errorMessage: t('staff.teamMembers.form.errors.delete', 'Failed to delete team member.'),\n })\n flash(t('staff.teamMembers.form.flash.deleted', 'Team member deleted.'), 'success')\n router.push('/backend/staff/team-members')\n }, [memberId, router, t])\n\n const handleRulesetChange = React.useCallback(async (nextId: string | null) => {\n if (!memberId) return\n await updateCrud('staff/team-members', { id: memberId, availabilityRuleSetId: nextId }, {\n errorMessage: t('staff.teamMembers.availability.ruleset.updateError', 'Failed to update schedule.'),\n })\n setAvailabilityRuleSetId(nextId)\n flash(t('staff.teamMembers.availability.ruleset.updateSuccess', 'Schedule updated.'), 'success')\n }, [memberId, t])\n\n const panelTabs = React.useMemo(() => ([\n { id: 'details' as const, label: t('staff.teamMembers.detail.tabs.details', 'Details') },\n { id: 'availability' as const, label: t('staff.teamMembers.detail.tabs.availability', 'Availability') },\n { id: 'jobHistory' as const, label: t('staff.teamMembers.detail.tabs.jobHistory', 'Job history') },\n ]), [t])\n\n const tabs = React.useMemo(() => ([\n { id: 'notes' as const, label: t('staff.teamMembers.detail.tabs.notes', 'Notes') },\n { id: 'activities' as const, label: t('staff.teamMembers.detail.tabs.activities', 'Activities') },\n { id: 'addresses' as const, label: t('staff.teamMembers.detail.tabs.addresses', 'Addresses') },\n ]), [t])\n\n const resolvedInitialValues = initialValues ?? {\n roleIds: [],\n isActive: true,\n }\n\n const displayName = memberRecord?.displayName ?? memberRecord?.display_name ?? resolvedInitialValues.displayName ?? ''\n const teamLabel = memberRecord?.team?.name ?? t('staff.teamMembers.detail.team.unassigned', 'Unassigned team')\n const roleLabels = Array.isArray(memberRecord?.roleNames) && memberRecord?.roleNames.length\n ? memberRecord?.roleNames\n : [t('staff.teamMembers.detail.roles.unassigned', 'No roles assigned')]\n const userEmail = memberRecord?.user?.email ?? null\n\n return (\n <Page>\n <PageBody>\n <div className=\"space-y-6\">\n <div className=\"flex flex-wrap items-center justify-between gap-3\">\n <div className=\"flex items-center gap-3\">\n <Link\n href=\"/backend/staff/team-members\"\n className=\"inline-flex items-center text-sm text-muted-foreground hover:text-foreground\"\n >\n <span aria-hidden className=\"mr-1 text-base\">\u2190</span>\n <span className=\"sr-only\">{t('staff.teamMembers.detail.back', 'Back to team members')}</span>\n </Link>\n <div className=\"space-y-1\">\n <h1 className=\"text-2xl font-semibold text-foreground\">\n {displayName || t('staff.teamMembers.detail.untitled', 'Unnamed team member')}\n </h1>\n <p className=\"text-sm text-muted-foreground\">\n {t('staff.teamMembers.detail.subtitle', 'Team member profile and activity')}\n </p>\n </div>\n </div>\n </div>\n\n <div className=\"border-b\">\n <nav\n className=\"flex flex-wrap items-center gap-5 text-sm\"\n aria-label={t('staff.teamMembers.detail.tabs.label', 'Team member sections')}\n >\n {panelTabs.map((tab) => (\n <button\n key={tab.id}\n type=\"button\"\n role=\"tab\"\n aria-selected={activePanel === tab.id}\n onClick={() => setActivePanel(tab.id)}\n className={`relative -mb-px border-b-2 px-0 py-2 text-sm font-medium transition-colors ${\n activePanel === tab.id\n ? 'border-primary text-foreground'\n : 'border-transparent text-muted-foreground hover:text-foreground'\n }`}\n >\n {tab.label}\n </button>\n ))}\n </nav>\n </div>\n\n {activePanel === 'details' ? (\n <>\n <div className=\"grid gap-6 lg:grid-cols-[minmax(0,2fr),minmax(0,1.1fr)]\">\n <div className=\"space-y-6\">\n <div className=\"rounded-lg border bg-card p-4\">\n <h2 className=\"mb-4 text-sm font-semibold uppercase text-muted-foreground\">\n {t('staff.teamMembers.detail.highlights', 'Highlights')}\n </h2>\n <div className=\"grid gap-4 sm:grid-cols-2\">\n <div>\n <p className=\"text-xs font-medium uppercase text-muted-foreground\">\n {t('staff.teamMembers.detail.fields.team', 'Team')}\n </p>\n <p className=\"text-base text-foreground\">{teamLabel}</p>\n </div>\n <div>\n <p className=\"text-xs font-medium uppercase text-muted-foreground\">\n {t('staff.teamMembers.detail.fields.roles', 'Roles')}\n </p>\n <p className=\"text-base text-foreground\">{roleLabels.join(', ')}</p>\n </div>\n <div>\n <p className=\"text-xs font-medium uppercase text-muted-foreground\">\n {t('staff.teamMembers.detail.fields.user', 'User')}\n </p>\n <p className=\"text-base text-foreground\">\n {userEmail ?? t('staff.teamMembers.detail.fields.userEmpty', 'No user linked')}\n </p>\n </div>\n <div>\n <p className=\"text-xs font-medium uppercase text-muted-foreground\">\n {t('staff.teamMembers.detail.fields.status', 'Status')}\n </p>\n <p className=\"text-base text-foreground\">\n {memberRecord?.isActive ?? memberRecord?.is_active\n ? t('staff.teamMembers.detail.status.active', 'Active')\n : t('staff.teamMembers.detail.status.inactive', 'Inactive')}\n </p>\n </div>\n </div>\n </div>\n\n <div className=\"rounded-lg border bg-card p-4\">\n <div className=\"flex flex-wrap items-center justify-between gap-3\">\n <div className=\"flex gap-2\">\n {tabs.map((tab) => (\n <button\n key={tab.id}\n type=\"button\"\n onClick={() => setActiveTab(tab.id)}\n className={`relative -mb-px border-b-2 px-0 py-1 text-sm font-medium transition-colors ${\n activeTab === tab.id\n ? 'border-primary text-foreground'\n : 'border-transparent text-muted-foreground hover:text-foreground'\n }`}\n >\n {tab.label}\n </button>\n ))}\n </div>\n {sectionAction ? (\n <Button\n type=\"button\"\n size=\"sm\"\n disabled={sectionAction.disabled}\n onClick={() => sectionAction.onClick()}\n >\n {sectionAction.icon ?? (activeTab === 'addresses' ? <Plus className=\"mr-2 h-4 w-4\" /> : null)}\n {sectionAction.label}\n </Button>\n ) : null}\n </div>\n {activeTab === 'notes' ? (\n <NotesSection\n entityId={memberId ?? null}\n emptyLabel={t('staff.teamMembers.detail.notes.empty', 'No notes yet.')}\n viewerUserId={null}\n viewerName={null}\n viewerEmail={null}\n addActionLabel={t('staff.teamMembers.detail.notes.add', 'Add note')}\n emptyState={{\n title: t('staff.teamMembers.detail.notes.emptyTitle', 'Keep everyone in the loop'),\n actionLabel: t('staff.teamMembers.detail.notes.emptyAction', 'Add a note'),\n }}\n onActionChange={setSectionAction}\n translator={detailTranslator}\n labelPrefix=\"staff.teamMembers.detail.notes\"\n inlineLabelPrefix=\"staff.teamMembers.detail.inline\"\n dataAdapter={notesAdapter}\n renderIcon={renderDictionaryIcon}\n renderColor={renderDictionaryColor}\n iconSuggestions={ICON_SUGGESTIONS}\n />\n ) : null}\n {activeTab === 'activities' ? (\n <ActivitiesSection\n entityId={memberId ?? null}\n addActionLabel={t('staff.teamMembers.detail.activities.add', 'Log activity')}\n emptyState={{\n title: t('staff.teamMembers.detail.activities.emptyTitle', 'No activities yet'),\n actionLabel: t('staff.teamMembers.detail.activities.emptyAction', 'Add an activity'),\n }}\n onActionChange={setSectionAction}\n dataAdapter={activitiesAdapter}\n activityTypeLabels={activityTypeLabels}\n loadActivityOptions={loadActivityOptions}\n createActivityOption={createActivityOption}\n resolveActivityPresentation={resolveActivityPresentation}\n renderCustomFields={renderCustomFields}\n labelPrefix=\"staff.teamMembers.detail.activities\"\n renderIcon={renderDictionaryIcon}\n renderColor={renderDictionaryColor}\n appearanceLabels={appearanceLabels}\n manageHref={manageActivityHref}\n customFieldEntityIds={['staff:staff_team_member_activity']}\n />\n ) : null}\n {activeTab === 'addresses' ? (\n <SharedAddressesSection\n entityId={memberId ?? null}\n emptyLabel={t('staff.teamMembers.detail.addresses.empty', 'No addresses yet.')}\n addActionLabel={t('staff.teamMembers.detail.addresses.add', 'Add address')}\n emptyState={{\n title: t('staff.teamMembers.detail.addresses.emptyTitle', 'No addresses yet'),\n actionLabel: t('staff.teamMembers.detail.addresses.emptyAction', 'Add an address'),\n }}\n onActionChange={setSectionAction}\n dataAdapter={addressesAdapter}\n addressTypesAdapter={addressTypesAdapter}\n labelPrefix=\"staff.teamMembers.detail.addresses\"\n />\n ) : null}\n </div>\n </div>\n <div className=\"space-y-4\">\n <div className=\"rounded-lg border bg-card p-4\">\n <h2 className=\"mb-4 text-sm font-semibold uppercase text-muted-foreground\">\n {t('staff.teamMembers.detail.details', 'Member details')}\n </h2>\n <div className=\"space-y-2\">\n {memberRecord?.description ? (\n <MarkdownContent body={memberRecord.description} format=\"markdown\" className={MARKDOWN_CLASSNAME} />\n ) : (\n <p className=\"text-sm text-muted-foreground\">\n {t('staff.teamMembers.detail.descriptionEmpty', 'No description provided.')}\n </p>\n )}\n </div>\n </div>\n </div>\n </div>\n\n <div className=\"rounded-lg border bg-card p-4\">\n <h2 className=\"mb-4 text-sm font-semibold uppercase text-muted-foreground\">\n {t('staff.teamMembers.detail.formTitle', 'Member settings')}\n </h2>\n <TeamMemberForm\n embedded\n title={t('staff.teamMembers.form.editTitle', 'Edit team member')}\n backHref=\"/backend/staff/team-members\"\n cancelHref=\"/backend/staff/team-members\"\n initialValues={resolvedInitialValues}\n onSubmit={handleSubmit}\n onDelete={handleDelete}\n isLoading={!initialValues}\n loadingMessage={t('staff.teamMembers.form.loading', 'Loading team member...')}\n />\n </div>\n </>\n ) : activePanel === 'availability' ? (\n <AvailabilityRulesEditor\n subjectType=\"member\"\n subjectId={memberId ?? ''}\n labelPrefix=\"staff.teamMembers\"\n mode=\"availability\"\n rulesetId={availabilityRuleSetId}\n onRulesetChange={handleRulesetChange}\n buildScheduleItems={({ availabilityRules, translate: translateLabel }) => (\n buildMemberScheduleItems({ availabilityRules, translate: translateLabel })\n )}\n />\n ) : (\n <div className=\"rounded-lg border bg-card p-4\">\n <JobHistorySection memberId={memberId ?? null} />\n </div>\n )}\n </div>\n </PageBody>\n </Page>\n )\n}\n\nfunction normalizeStringList(value: unknown): string[] {\n if (!Array.isArray(value)) return []\n return value\n .map((entry) => (typeof entry === 'string' ? entry.trim() : ''))\n .filter((entry) => entry.length > 0)\n}\n\nfunction resolvePreferredArray<T>(primary?: T[] | null, fallback?: T[] | null): T[] | undefined {\n if (Array.isArray(primary) && primary.length) return primary\n if (Array.isArray(fallback) && fallback.length) return fallback\n return Array.isArray(primary) ? primary : Array.isArray(fallback) ? fallback : undefined\n}\n"],
5
+ "mappings": ";AAgLY,SAiLA,UA7KE,KAJF;AA9KZ,YAAY,WAAW;AACvB,OAAO,UAAU;AACjB,SAAS,WAAW,uBAAuB;AAC3C,SAAS,MAAM,gBAAgB;AAC/B,SAAS,cAAc;AACvB,SAAS,4BAA4B;AACrC,SAAS,iCAAiC;AAC1C,SAAS,YAAY,kBAAkB;AACvC,SAAS,aAAa;AACtB,SAAS,YAAY;AACrB,SAAS,oCAAoC;AAC7C,SAAS,+BAA+B;AACxC,SAAS,gCAAgC;AACzC,SAAS,gBAAgB,8BAAyD;AAClF,SAAS,oBAAoB;AAC7B,SAAS,yBAA6C;AACtD,SAAS,oBAAoB,8BAA8B;AAC3D,SAAS,uBAAuB,sBAAsB,wBAAwB;AAC9E,SAAS,+BAA+B;AACxC,SAAS,oCAAoC;AAC7C,SAAS,2BAA2B,sCAAsC;AAC1E,SAAS,uBAAuB;AAChC;AAAA,EACE;AAAA,EACA;AAAA,OAEK;AACP,SAAS,yBAAyB;AAElC,SAAS,YAAY;AAErB,MAAM,qBACJ;AA4Ba,SAAR,0BAA2C,EAAE,OAAO,GAAiC;AAC1F,QAAM,WAAW,QAAQ;AACzB,QAAM,IAAI,KAAK;AACf,QAAM,mBAAmB,MAAM,QAAQ,MAAM,6BAA6B,CAAC,GAAG,CAAC,CAAC,CAAC;AACjF,QAAM,SAAS,UAAU;AACzB,QAAM,eAAe,gBAAgB;AACrC,QAAM,CAAC,eAAe,gBAAgB,IAAI,MAAM,SAAsC,IAAI;AAC1F,QAAM,CAAC,cAAc,eAAe,IAAI,MAAM,SAAkC,IAAI;AACpF,QAAM,CAAC,uBAAuB,wBAAwB,IAAI,MAAM,SAAwB,IAAI;AAC5F,QAAM,CAAC,aAAa,cAAc,IAAI,MAAM,SAAoD,SAAS;AACzG,QAAM,CAAC,WAAW,YAAY,IAAI,MAAM,SAA+C,OAAO;AAC9F,QAAM,CAAC,eAAe,gBAAgB,IAAI,MAAM,SAA+B,IAAI;AACnF,QAAM,CAAC,sBAAsB,uBAAuB,IAAI,MAAM,SAAwB,IAAI;AAC1F,QAAM,CAAC,qBAAqB,sBAAsB,IAAI,MAAM,SAAkC,CAAC,CAAC;AAChG,QAAM,gBAAgB,MAAM,OAAO,KAAK;AAExC,QAAM,eAAe,MAAM,QAAQ,MAAM,wBAAwB,gBAAgB,GAAG,CAAC,gBAAgB,CAAC;AACtG,QAAM,oBAAoB,MAAM,QAAQ,MAAM,6BAA6B,gBAAgB,GAAG,CAAC,gBAAgB,CAAC;AAChH,QAAM,mBAAmB,MAAM,QAAQ,MAAM,0BAA0B,gBAAgB,GAAG,CAAC,gBAAgB,CAAC;AAC5G,QAAM,sBAAsB,MAAM,QAAQ,MAAM,+BAA+B,gBAAgB,GAAG,CAAC,gBAAgB,CAAC;AAEpH,QAAM,qBAAqB,MAAM,QAAgC,OAAO;AAAA,IACtE,aAAa,EAAE,8DAA8D,yBAAyB;AAAA,IACtG,UAAU,EAAE,sDAAsD,UAAU;AAAA,IAC5E,WAAW,EAAE,yDAAyD,eAAe;AAAA,IACrF,aAAa,EAAE,8DAA8D,mBAAmB;AAAA,IAChG,YAAY,EAAE,6DAA6D,MAAM;AAAA,IACjF,kBAAkB,EAAE,mEAAmE,MAAM;AAAA,IAC7F,YAAY,EAAE,6DAA6D,OAAO;AAAA,IAClF,kBAAkB,EAAE,mEAAmE,0BAA0B;AAAA,IACjH,YAAY,EAAE,6DAA6D,qBAAqB;AAAA,IAChG,aAAa,EAAE,yDAAyD,QAAQ;AAAA,IAChF,WAAW,EAAE,uDAAuD,MAAM;AAAA,IAC1E,kBAAkB,EAAE,+DAA+D,qBAAgB;AAAA,IACnG,WAAW,EAAE,4DAA4D,wBAAwB;AAAA,IACjG,WAAW,EAAE,4DAA4D,uBAAuB;AAAA,IAChG,cAAc,EAAE,0DAA0D,eAAU;AAAA,IACpF,aAAa,EAAE,yDAAyD,mBAAmB;AAAA,EAC7F,IAAI,CAAC,CAAC,CAAC;AAEP,QAAM,sBAAsB,MAAM,YAAY,YAAY;AACxD,UAAM,EAAE,YAAY,QAAQ,IAAI,MAAM,oBAAoB,eAAe;AACzE,4BAAwB,YAAY,MAAM,IAAI;AAC9C,2BAAuB,OAAO;AAC9B,WAAO;AAAA,EACT,GAAG,CAAC,CAAC;AAEL,QAAM,uBAAuB,MAAM;AAAA,IACjC,OAAO,UAA0F;AAC/F,YAAM,QAAQ,MAAM,2BAA2B,iBAAiB,KAAK;AACrE,UAAI,CAAC,OAAO;AACV,cAAM,IAAI,MAAM,EAAE,4DAA4D,uBAAuB,CAAC;AAAA,MACxG;AACA,aAAO;AAAA,IACT;AAAA,IACA,CAAC,CAAC;AAAA,EACJ;AAEA,QAAM,UAAU,MAAM;AACpB,wBAAoB,EAAE,MAAM,MAAM;AAAA,IAAC,CAAC;AAAA,EACtC,GAAG,CAAC,mBAAmB,CAAC;AAExB,QAAM,kBAAkB,MAAM;AAAA,IAC5B,MAAM,IAAI,IAAI,oBAAoB,IAAI,CAAC,UAAU,CAAC,MAAM,OAAO,KAAK,CAAC,CAAC;AAAA,IACtE,CAAC,mBAAmB;AAAA,EACtB;AAEA,QAAM,8BAA8B,MAAM;AAAA,IACxC,CAAC,aAAwG;AACvG,YAAM,QAAQ,gBAAgB,IAAI,SAAS,YAAY;AACvD,aAAO;AAAA,QACL,OAAO,OAAO,SAAS,SAAS;AAAA,QAChC,MAAM,OAAO,QAAQ,SAAS,kBAAkB;AAAA,QAChD,OAAO,OAAO,SAAS,SAAS,mBAAmB;AAAA,MACrD;AAAA,IACF;AAAA,IACA,CAAC,eAAe;AAAA,EAClB;AAEA,QAAM,qBAAqB,MAAM,QAAQ,MAAM;AAC7C,QAAI,CAAC,qBAAsB,QAAO;AAClC,WAAO,6CAA6C,mBAAmB,oBAAoB,CAAC;AAAA,EAC9F,GAAG,CAAC,oBAAoB,CAAC;AAEzB,QAAM,mBAAmB,MAAM,QAAQ,OAAO;AAAA,IAC5C,YAAY,EAAE,6DAA6D,OAAO;AAAA,IAClF,WAAW,EAAE,4DAA4D,wCAAwC;AAAA,IACjH,iBAAiB,EAAE,6DAA6D,cAAc;AAAA,IAC9F,WAAW,EAAE,4DAA4D,eAAe;AAAA,IACxF,iBAAiB,EAAE,kEAAkE,+CAA+C;AAAA,IACpI,wBAAwB,EAAE,6DAA6D,yBAAyB;AAAA,IAChH,uBAAuB,EAAE,wEAAwE,8BAAyB;AAAA,IAC1H,sBAAsB,EAAE,kEAAkE,6BAA6B;AAAA,IACvH,sBAAsB,EAAE,kEAAkE,aAAa;AAAA,IACvG,gBAAgB,EAAE,4DAA4D,aAAa;AAAA,IAC3F,mBAAmB,EAAE,+DAA+D,wBAAwB;AAAA,EAC9G,IAAI,CAAC,CAAC,CAAC;AAEP,QAAM,qBAAqB,MAAM,YAAY,CAAC,aAA4G;AACxJ,UAAM,UAAU,MAAM,QAAQ,SAAS,YAAY,IAAI,SAAS,eAAe,CAAC;AAChF,QAAI,CAAC,QAAQ,OAAQ,QAAO;AAC5B,UAAM,aAAa,EAAE,0DAA0D,cAAc;AAC7F,WACE,oBAAC,SAAI,WAAU,6BACZ,kBAAQ,IAAI,CAAC,OAAO,UAAU;AAC7B,YAAM,QAAQ,MAAM,SAAS,MAAM;AACnC,YAAM,QAAQ,MAAM;AACpB,YAAM,WAAW,EAAE,SAAS,QAAQ,UAAU,MAAO,MAAM,QAAQ,KAAK,KAAK,MAAM,WAAW;AAC9F,YAAM,UAAU,WACZ,MAAM,QAAQ,KAAK,IACjB,MAAM,IAAI,CAAC,SAAS,OAAO,IAAI,CAAC,EAAE,KAAK,IAAI,IAC3C,OAAO,KAAK,IACd;AACJ,aACE;AAAA,QAAC;AAAA;AAAA,UAEC,WAAU;AAAA,UAEV;AAAA,gCAAC,SAAI,WAAU,6CAA6C,iBAAM;AAAA,YAClE,oBAAC,SAAI,WAAU,gCAAgC,mBAAQ;AAAA;AAAA;AAAA,QAJlD,YAAY,SAAS,MAAM,KAAK,WAAW,KAAK;AAAA,MAKvD;AAAA,IAEJ,CAAC,GACH;AAAA,EAEJ,GAAG,CAAC,CAAC,CAAC;AAEN,QAAM,UAAU,MAAM;AACpB,QAAI,CAAC,SAAU;AACf,UAAM,gBAAgB;AACtB,QAAI,YAAY;AAChB,mBAAe,aAAa;AAC1B,UAAI;AACF,cAAMA,UAAS,IAAI,gBAAgB,EAAE,MAAM,KAAK,UAAU,KAAK,KAAK,cAAc,CAAC;AACnF,cAAM,UAAU,MAAM;AAAA,UACpB,2BAA2BA,QAAO,SAAS,CAAC;AAAA,UAC5C;AAAA,UACA,EAAE,cAAc,EAAE,sCAAsC,6BAA6B,EAAE;AAAA,QACzF;AACA,cAAM,SAAS,MAAM,QAAQ,QAAQ,KAAK,IAAI,QAAQ,MAAM,CAAC,IAAI;AACjE,YAAI,CAAC,OAAQ,OAAM,IAAI,MAAM,EAAE,0CAA0C,wBAAwB,CAAC;AAClG,cAAM,eAAe,0BAA0B,MAAM;AACrD,YAAI,CAAC,WAAW;AACd,gBAAM,iBAAiB,OAAO,UAAU,OAAO,WAAW;AAC1D,gBAAM,oBAAoB,oBAAoB,sBAAsB,OAAO,SAAS,OAAO,QAAQ,CAAC;AACpG,2BAAiB;AAAA,YACf,IAAI,OAAO;AAAA,YACX,QAAQ;AAAA,YACR,QAAQ,OAAO,UAAU,OAAO,WAAW;AAAA,YAC3C,aAAa,OAAO,eAAe,OAAO,gBAAgB;AAAA,YAC1D,aAAa,OAAO,eAAe;AAAA,YACnC,SAAS;AAAA,YACT,MAAM,oBAAoB,OAAO,IAAI;AAAA,YACrC,UAAU,OAAO,YAAY,OAAO,aAAa;AAAA,YACjD,GAAG;AAAA,UACL,CAAC;AACD,0BAAgB,MAAM;AACtB;AAAA,YACE,OAAO,OAAO,0BAA0B,WACpC,OAAO,wBACP,OAAO,OAAO,6BAA6B,WACzC,OAAO,2BACP;AAAA,UACR;AAAA,QACF;AAAA,MACF,SAAS,OAAO;AACd,cAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU,EAAE,sCAAsC,6BAA6B;AAC9H,cAAM,SAAS,OAAO;AAAA,MACxB;AAAA,IACF;AACA,SAAK,WAAW;AAChB,WAAO,MAAM;AAAE,kBAAY;AAAA,IAAK;AAAA,EAClC,GAAG,CAAC,UAAU,CAAC,CAAC;AAEhB,QAAM,UAAU,MAAM;AACpB,QAAI,CAAC,aAAc;AACnB,UAAM,UAAU,aAAa,IAAI,SAAS,MAAM;AAChD,QAAI,WAAW,CAAC,cAAc,SAAS;AACrC,oBAAc,UAAU;AACxB,YAAM,EAAE,+CAA+C,sCAAsC,GAAG,SAAS;AACzG,YAAM,aAAa,IAAI,gBAAgB,aAAa,SAAS,CAAC;AAC9D,iBAAW,OAAO,SAAS;AAC3B,YAAM,YAAY,WAAW,SAAS;AACtC,YAAM,WAAW,WACb,+BAA+B,mBAAmB,QAAQ,CAAC,GAAG,YAAY,IAAI,SAAS,KAAK,EAAE,KAC9F,8BAA8B,YAAY,IAAI,SAAS,KAAK,EAAE;AAClE,aAAO,QAAQ,QAAQ;AAAA,IACzB;AAAA,EACF,GAAG,CAAC,UAAU,QAAQ,cAAc,CAAC,CAAC;AAEtC,QAAM,eAAe,MAAM,YAAY,OAAO,WAAiC;AAC7E,QAAI,CAAC,SAAU;AACf,UAAM,UAAU,uBAAuB,QAAQ,EAAE,IAAI,SAAS,CAAC;AAC/D,UAAM,WAAW,sBAAsB,SAAS;AAAA,MAC9C,cAAc,EAAE,wCAAwC,+BAA+B;AAAA,IACzF,CAAC;AACD,UAAM,EAAE,wCAAwC,sBAAsB,GAAG,SAAS;AAClF,WAAO,KAAK,6BAA6B;AAAA,EAC3C,GAAG,CAAC,UAAU,QAAQ,CAAC,CAAC;AAExB,QAAM,eAAe,MAAM,YAAY,YAAY;AACjD,QAAI,CAAC,SAAU;AACf,UAAM,WAAW,sBAAsB,UAAU;AAAA,MAC/C,cAAc,EAAE,wCAAwC,+BAA+B;AAAA,IACzF,CAAC;AACD,UAAM,EAAE,wCAAwC,sBAAsB,GAAG,SAAS;AAClF,WAAO,KAAK,6BAA6B;AAAA,EAC3C,GAAG,CAAC,UAAU,QAAQ,CAAC,CAAC;AAExB,QAAM,sBAAsB,MAAM,YAAY,OAAO,WAA0B;AAC7E,QAAI,CAAC,SAAU;AACf,UAAM,WAAW,sBAAsB,EAAE,IAAI,UAAU,uBAAuB,OAAO,GAAG;AAAA,MACtF,cAAc,EAAE,sDAAsD,4BAA4B;AAAA,IACpG,CAAC;AACD,6BAAyB,MAAM;AAC/B,UAAM,EAAE,wDAAwD,mBAAmB,GAAG,SAAS;AAAA,EACjG,GAAG,CAAC,UAAU,CAAC,CAAC;AAEhB,QAAM,YAAY,MAAM,QAAQ,MAAO;AAAA,IACrC,EAAE,IAAI,WAAoB,OAAO,EAAE,yCAAyC,SAAS,EAAE;AAAA,IACvF,EAAE,IAAI,gBAAyB,OAAO,EAAE,8CAA8C,cAAc,EAAE;AAAA,IACtG,EAAE,IAAI,cAAuB,OAAO,EAAE,4CAA4C,aAAa,EAAE;AAAA,EACnG,GAAI,CAAC,CAAC,CAAC;AAEP,QAAM,OAAO,MAAM,QAAQ,MAAO;AAAA,IAChC,EAAE,IAAI,SAAkB,OAAO,EAAE,uCAAuC,OAAO,EAAE;AAAA,IACjF,EAAE,IAAI,cAAuB,OAAO,EAAE,4CAA4C,YAAY,EAAE;AAAA,IAChG,EAAE,IAAI,aAAsB,OAAO,EAAE,2CAA2C,WAAW,EAAE;AAAA,EAC/F,GAAI,CAAC,CAAC,CAAC;AAEP,QAAM,wBAAwB,iBAAiB;AAAA,IAC7C,SAAS,CAAC;AAAA,IACV,UAAU;AAAA,EACZ;AAEA,QAAM,cAAc,cAAc,eAAe,cAAc,gBAAgB,sBAAsB,eAAe;AACpH,QAAM,YAAY,cAAc,MAAM,QAAQ,EAAE,4CAA4C,iBAAiB;AAC7G,QAAM,aAAa,MAAM,QAAQ,cAAc,SAAS,KAAK,cAAc,UAAU,SACjF,cAAc,YACd,CAAC,EAAE,6CAA6C,mBAAmB,CAAC;AACxE,QAAM,YAAY,cAAc,MAAM,SAAS;AAE/C,SACE,oBAAC,QACC,8BAAC,YACC,+BAAC,SAAI,WAAU,aACb;AAAA,wBAAC,SAAI,WAAU,qDACb,+BAAC,SAAI,WAAU,2BACb;AAAA;AAAA,QAAC;AAAA;AAAA,UACC,MAAK;AAAA,UACL,WAAU;AAAA,UAEV;AAAA,gCAAC,UAAK,eAAW,MAAC,WAAU,kBAAiB,oBAAC;AAAA,YAC9C,oBAAC,UAAK,WAAU,WAAW,YAAE,iCAAiC,sBAAsB,GAAE;AAAA;AAAA;AAAA,MACxF;AAAA,MACA,qBAAC,SAAI,WAAU,aACb;AAAA,4BAAC,QAAG,WAAU,0CACX,yBAAe,EAAE,qCAAqC,qBAAqB,GAC9E;AAAA,QACA,oBAAC,OAAE,WAAU,iCACV,YAAE,qCAAqC,kCAAkC,GAC5E;AAAA,SACF;AAAA,OACF,GACF;AAAA,IAEA,oBAAC,SAAI,WAAU,YACb;AAAA,MAAC;AAAA;AAAA,QACC,WAAU;AAAA,QACV,cAAY,EAAE,uCAAuC,sBAAsB;AAAA,QAE1E,oBAAU,IAAI,CAAC,QACd;AAAA,UAAC;AAAA;AAAA,YAEC,MAAK;AAAA,YACL,MAAK;AAAA,YACL,iBAAe,gBAAgB,IAAI;AAAA,YACnC,SAAS,MAAM,eAAe,IAAI,EAAE;AAAA,YACpC,WAAW,8EACT,gBAAgB,IAAI,KAChB,mCACA,gEACN;AAAA,YAEC,cAAI;AAAA;AAAA,UAXA,IAAI;AAAA,QAYX,CACD;AAAA;AAAA,IACH,GACF;AAAA,IAEC,gBAAgB,YACf,iCACE;AAAA,2BAAC,SAAI,WAAU,2DACb;AAAA,6BAAC,SAAI,WAAU,aACb;AAAA,+BAAC,SAAI,WAAU,iCACb;AAAA,gCAAC,QAAG,WAAU,8DACX,YAAE,uCAAuC,YAAY,GACxD;AAAA,YACA,qBAAC,SAAI,WAAU,6BACb;AAAA,mCAAC,SACC;AAAA,oCAAC,OAAE,WAAU,uDACV,YAAE,wCAAwC,MAAM,GACnD;AAAA,gBACA,oBAAC,OAAE,WAAU,6BAA6B,qBAAU;AAAA,iBACtD;AAAA,cACA,qBAAC,SACC;AAAA,oCAAC,OAAE,WAAU,uDACV,YAAE,yCAAyC,OAAO,GACrD;AAAA,gBACA,oBAAC,OAAE,WAAU,6BAA6B,qBAAW,KAAK,IAAI,GAAE;AAAA,iBAClE;AAAA,cACA,qBAAC,SACC;AAAA,oCAAC,OAAE,WAAU,uDACV,YAAE,wCAAwC,MAAM,GACnD;AAAA,gBACA,oBAAC,OAAE,WAAU,6BACV,uBAAa,EAAE,6CAA6C,gBAAgB,GAC/E;AAAA,iBACF;AAAA,cACA,qBAAC,SACC;AAAA,oCAAC,OAAE,WAAU,uDACV,YAAE,0CAA0C,QAAQ,GACvD;AAAA,gBACA,oBAAC,OAAE,WAAU,6BACV,wBAAc,YAAY,cAAc,YACrC,EAAE,0CAA0C,QAAQ,IACpD,EAAE,4CAA4C,UAAU,GAC9D;AAAA,iBACF;AAAA,eACF;AAAA,aACF;AAAA,UAEA,qBAAC,SAAI,WAAU,iCACb;AAAA,iCAAC,SAAI,WAAU,qDACb;AAAA,kCAAC,SAAI,WAAU,cACZ,eAAK,IAAI,CAAC,QACT;AAAA,gBAAC;AAAA;AAAA,kBAEC,MAAK;AAAA,kBACL,SAAS,MAAM,aAAa,IAAI,EAAE;AAAA,kBAClC,WAAW,8EACT,cAAc,IAAI,KACd,mCACA,gEACN;AAAA,kBAEC,cAAI;AAAA;AAAA,gBATA,IAAI;AAAA,cAUX,CACD,GACH;AAAA,cACC,gBACC;AAAA,gBAAC;AAAA;AAAA,kBACC,MAAK;AAAA,kBACL,MAAK;AAAA,kBACL,UAAU,cAAc;AAAA,kBACxB,SAAS,MAAM,cAAc,QAAQ;AAAA,kBAEpC;AAAA,kCAAc,SAAS,cAAc,cAAc,oBAAC,QAAK,WAAU,gBAAe,IAAK;AAAA,oBACvF,cAAc;AAAA;AAAA;AAAA,cACjB,IACE;AAAA,eACN;AAAA,YACC,cAAc,UACb;AAAA,cAAC;AAAA;AAAA,gBACC,UAAU,YAAY;AAAA,gBACtB,YAAY,EAAE,wCAAwC,eAAe;AAAA,gBACrE,cAAc;AAAA,gBACd,YAAY;AAAA,gBACZ,aAAa;AAAA,gBACb,gBAAgB,EAAE,sCAAsC,UAAU;AAAA,gBAClE,YAAY;AAAA,kBACV,OAAO,EAAE,6CAA6C,2BAA2B;AAAA,kBACjF,aAAa,EAAE,8CAA8C,YAAY;AAAA,gBAC3E;AAAA,gBACA,gBAAgB;AAAA,gBAChB,YAAY;AAAA,gBACZ,aAAY;AAAA,gBACZ,mBAAkB;AAAA,gBAClB,aAAa;AAAA,gBACb,YAAY;AAAA,gBACZ,aAAa;AAAA,gBACb,iBAAiB;AAAA;AAAA,YACnB,IACE;AAAA,YACH,cAAc,eACb;AAAA,cAAC;AAAA;AAAA,gBACC,UAAU,YAAY;AAAA,gBACtB,gBAAgB,EAAE,2CAA2C,cAAc;AAAA,gBAC3E,YAAY;AAAA,kBACV,OAAO,EAAE,kDAAkD,mBAAmB;AAAA,kBAC9E,aAAa,EAAE,mDAAmD,iBAAiB;AAAA,gBACrF;AAAA,gBACA,gBAAgB;AAAA,gBAChB,aAAa;AAAA,gBACb;AAAA,gBACA;AAAA,gBACA;AAAA,gBACA;AAAA,gBACA;AAAA,gBACA,aAAY;AAAA,gBACZ,YAAY;AAAA,gBACZ,aAAa;AAAA,gBACb;AAAA,gBACA,YAAY;AAAA,gBACZ,sBAAsB,CAAC,kCAAkC;AAAA;AAAA,YAC3D,IACE;AAAA,YACH,cAAc,cACb;AAAA,cAAC;AAAA;AAAA,gBACC,UAAU,YAAY;AAAA,gBACtB,YAAY,EAAE,4CAA4C,mBAAmB;AAAA,gBAC7E,gBAAgB,EAAE,0CAA0C,aAAa;AAAA,gBACzE,YAAY;AAAA,kBACV,OAAO,EAAE,iDAAiD,kBAAkB;AAAA,kBAC5E,aAAa,EAAE,kDAAkD,gBAAgB;AAAA,gBACnF;AAAA,gBACA,gBAAgB;AAAA,gBAChB,aAAa;AAAA,gBACb;AAAA,gBACA,aAAY;AAAA;AAAA,YACd,IACE;AAAA,aACN;AAAA,WACF;AAAA,QACA,oBAAC,SAAI,WAAU,aACb,+BAAC,SAAI,WAAU,iCACb;AAAA,8BAAC,QAAG,WAAU,8DACX,YAAE,oCAAoC,gBAAgB,GACzD;AAAA,UACA,oBAAC,SAAI,WAAU,aACZ,wBAAc,cACb,oBAAC,mBAAgB,MAAM,aAAa,aAAa,QAAO,YAAW,WAAW,oBAAoB,IAElG,oBAAC,OAAE,WAAU,iCACV,YAAE,6CAA6C,0BAA0B,GAC5E,GAEJ;AAAA,WACF,GACF;AAAA,SACF;AAAA,MAEA,qBAAC,SAAI,WAAU,iCACb;AAAA,4BAAC,QAAG,WAAU,8DACX,YAAE,sCAAsC,iBAAiB,GAC5D;AAAA,QACA;AAAA,UAAC;AAAA;AAAA,YACC,UAAQ;AAAA,YACR,OAAO,EAAE,oCAAoC,kBAAkB;AAAA,YAC/D,UAAS;AAAA,YACT,YAAW;AAAA,YACX,eAAe;AAAA,YACf,UAAU;AAAA,YACV,UAAU;AAAA,YACV,WAAW,CAAC;AAAA,YACZ,gBAAgB,EAAE,kCAAkC,wBAAwB;AAAA;AAAA,QAC9E;AAAA,SACF;AAAA,OACF,IACE,gBAAgB,iBAClB;AAAA,MAAC;AAAA;AAAA,QACC,aAAY;AAAA,QACZ,WAAW,YAAY;AAAA,QACvB,aAAY;AAAA,QACZ,MAAK;AAAA,QACL,WAAW;AAAA,QACX,iBAAiB;AAAA,QACjB,oBAAoB,CAAC,EAAE,mBAAmB,WAAW,eAAe,MAClE,yBAAyB,EAAE,mBAAmB,WAAW,eAAe,CAAC;AAAA;AAAA,IAE7E,IAEA,oBAAC,SAAI,WAAU,iCACb,8BAAC,qBAAkB,UAAU,YAAY,MAAM,GACjD;AAAA,KAEJ,GACF,GACF;AAEJ;AAEA,SAAS,oBAAoB,OAA0B;AACrD,MAAI,CAAC,MAAM,QAAQ,KAAK,EAAG,QAAO,CAAC;AACnC,SAAO,MACJ,IAAI,CAAC,UAAW,OAAO,UAAU,WAAW,MAAM,KAAK,IAAI,EAAG,EAC9D,OAAO,CAAC,UAAU,MAAM,SAAS,CAAC;AACvC;AAEA,SAAS,sBAAyB,SAAsB,UAAwC;AAC9F,MAAI,MAAM,QAAQ,OAAO,KAAK,QAAQ,OAAQ,QAAO;AACrD,MAAI,MAAM,QAAQ,QAAQ,KAAK,SAAS,OAAQ,QAAO;AACvD,SAAO,MAAM,QAAQ,OAAO,IAAI,UAAU,MAAM,QAAQ,QAAQ,IAAI,WAAW;AACjF;",
6
6
  "names": ["params"]
7
7
  }
@@ -0,0 +1,112 @@
1
+ "use client";
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
+ import * as React from "react";
4
+ import { useT } from "@open-mercato/shared/lib/i18n/context";
5
+ import { Button } from "@open-mercato/ui/primitives/button";
6
+ import { apiCall } from "@open-mercato/ui/backend/utils/apiCall";
7
+ import { LeaveRequestPreview } from "./LeaveRequestPreview.js";
8
+ function LeaveRequestDetail(props) {
9
+ const t = useT();
10
+ const [executingActionId, setExecutingActionId] = React.useState(null);
11
+ const [actionTakenByName, setActionTakenByName] = React.useState(null);
12
+ const actionTakenAtLabel = formatDateTime(props.actionTakenAt);
13
+ const hasActionTaken = Boolean(props.actionTaken);
14
+ const actionTakenId = extractActionId(props.actionTaken);
15
+ const actionTakenLabel = resolveActionLabel(actionTakenId, props.actions, t);
16
+ const actionTakenByLabel = formatUserLabel(props.actionTakenByUserId, actionTakenByName, t);
17
+ React.useEffect(() => {
18
+ const userId = props.actionTakenByUserId;
19
+ if (typeof userId !== "string" || userId.trim().length === 0) {
20
+ setActionTakenByName(null);
21
+ return;
22
+ }
23
+ let cancelled = false;
24
+ const safeUserId = userId.trim();
25
+ async function loadActionTakenByUserName() {
26
+ const call = await apiCall(
27
+ `/api/auth/users?id=${encodeURIComponent(safeUserId)}`
28
+ );
29
+ const entry = Array.isArray(call.result?.items) ? call.result.items[0] : null;
30
+ const resolvedName = typeof entry?.name === "string" && entry.name.trim().length > 0 ? entry.name.trim() : typeof entry?.email === "string" && entry.email.trim().length > 0 ? entry.email.trim() : null;
31
+ if (!cancelled) {
32
+ setActionTakenByName(resolvedName);
33
+ }
34
+ }
35
+ void loadActionTakenByUserName();
36
+ return () => {
37
+ cancelled = true;
38
+ };
39
+ }, [props.actionTakenByUserId]);
40
+ return /* @__PURE__ */ jsxs("div", { className: "space-y-3 rounded border p-3", children: [
41
+ /* @__PURE__ */ jsx(
42
+ LeaveRequestPreview,
43
+ {
44
+ entityId: props.entityId,
45
+ entityModule: props.entityModule,
46
+ entityType: props.entityType,
47
+ snapshot: props.snapshot,
48
+ previewData: props.previewData,
49
+ actionRequired: props.actionRequired,
50
+ actionType: props.actionType,
51
+ actionLabel: props.actionLabel
52
+ }
53
+ ),
54
+ props.actions.length ? /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-2", children: props.actions.map((action) => /* @__PURE__ */ jsx(
55
+ Button,
56
+ {
57
+ type: "button",
58
+ size: "sm",
59
+ variant: action.variant ?? "default",
60
+ disabled: executingActionId !== null || hasActionTaken,
61
+ onClick: async () => {
62
+ if (executingActionId !== null || hasActionTaken) return;
63
+ setExecutingActionId(action.id);
64
+ try {
65
+ await props.onAction(action.id, { id: props.entityId });
66
+ } finally {
67
+ setExecutingActionId(null);
68
+ }
69
+ },
70
+ children: executingActionId === action.id ? t("messages.actions.executing", "Executing...") : t(action.labelKey ?? action.id, action.id)
71
+ },
72
+ action.id
73
+ )) }) : null,
74
+ hasActionTaken ? /* @__PURE__ */ jsxs("p", { className: "text-xs text-muted-foreground", children: [
75
+ t("messages.actions.taken", "Action taken"),
76
+ ": ",
77
+ actionTakenLabel,
78
+ actionTakenAtLabel ? ` (${actionTakenAtLabel})` : "",
79
+ actionTakenByLabel ? ` ${t("messages.actions.by", "by")} ${actionTakenByLabel}` : ""
80
+ ] }) : null
81
+ ] });
82
+ }
83
+ function formatDateTime(value) {
84
+ if (!value) return "";
85
+ const date = value instanceof Date ? value : new Date(value);
86
+ if (Number.isNaN(date.getTime())) return "";
87
+ return date.toLocaleString();
88
+ }
89
+ function extractActionId(actionTaken) {
90
+ if (!actionTaken) return null;
91
+ const value = actionTaken.trim();
92
+ if (value.length === 0) return null;
93
+ const parts = value.split(":");
94
+ return parts[parts.length - 1] ?? value;
95
+ }
96
+ function resolveActionLabel(actionId, actions, t) {
97
+ if (!actionId) return "-";
98
+ const action = actions.find((item) => item.id === actionId);
99
+ if (!action) return actionId;
100
+ return t(action.labelKey ?? action.id, action.id);
101
+ }
102
+ function formatUserLabel(userId, userName, t) {
103
+ if (!userId) return "";
104
+ if (userName) return userName;
105
+ return t("common.user", "user");
106
+ }
107
+ var LeaveRequestDetail_default = LeaveRequestDetail;
108
+ export {
109
+ LeaveRequestDetail,
110
+ LeaveRequestDetail_default as default
111
+ };
112
+ //# sourceMappingURL=LeaveRequestDetail.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../src/modules/staff/components/LeaveRequestDetail.tsx"],
4
+ "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport type { ObjectDetailProps } from '@open-mercato/shared/modules/messages/types'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { apiCall } from '@open-mercato/ui/backend/utils/apiCall'\nimport { LeaveRequestPreview } from './LeaveRequestPreview'\n\nexport function LeaveRequestDetail(props: ObjectDetailProps) {\n const t = useT()\n const [executingActionId, setExecutingActionId] = React.useState<string | null>(null)\n const [actionTakenByName, setActionTakenByName] = React.useState<string | null>(null)\n const actionTakenAtLabel = formatDateTime(props.actionTakenAt)\n const hasActionTaken = Boolean(props.actionTaken)\n const actionTakenId = extractActionId(props.actionTaken)\n const actionTakenLabel = resolveActionLabel(actionTakenId, props.actions, t)\n const actionTakenByLabel = formatUserLabel(props.actionTakenByUserId, actionTakenByName, t)\n\n React.useEffect(() => {\n const userId = props.actionTakenByUserId\n if (typeof userId !== 'string' || userId.trim().length === 0) {\n setActionTakenByName(null)\n return\n }\n\n let cancelled = false\n const safeUserId = userId.trim()\n async function loadActionTakenByUserName() {\n const call = await apiCall<{ items?: Array<{ id?: string; name?: string | null; email?: string | null }> }>(\n `/api/auth/users?id=${encodeURIComponent(safeUserId)}`,\n )\n const entry = Array.isArray(call.result?.items) ? call.result.items[0] : null\n const resolvedName = typeof entry?.name === 'string' && entry.name.trim().length > 0\n ? entry.name.trim()\n : typeof entry?.email === 'string' && entry.email.trim().length > 0\n ? entry.email.trim()\n : null\n if (!cancelled) {\n setActionTakenByName(resolvedName)\n }\n }\n\n void loadActionTakenByUserName()\n return () => { cancelled = true }\n }, [props.actionTakenByUserId])\n\n return (\n <div className=\"space-y-3 rounded border p-3\">\n <LeaveRequestPreview\n entityId={props.entityId}\n entityModule={props.entityModule}\n entityType={props.entityType}\n snapshot={props.snapshot}\n previewData={props.previewData}\n actionRequired={props.actionRequired}\n actionType={props.actionType}\n actionLabel={props.actionLabel}\n />\n\n {props.actions.length ? (\n <div className=\"flex flex-wrap gap-2\">\n {props.actions.map((action) => (\n <Button\n key={action.id}\n type=\"button\"\n size=\"sm\"\n variant={action.variant ?? 'default'}\n disabled={executingActionId !== null || hasActionTaken}\n onClick={async () => {\n if (executingActionId !== null || hasActionTaken) return\n setExecutingActionId(action.id)\n try {\n await props.onAction(action.id, { id: props.entityId })\n } finally {\n setExecutingActionId(null)\n }\n }}\n >\n {executingActionId === action.id\n ? t('messages.actions.executing', 'Executing...')\n : t(action.labelKey ?? action.id, action.id)}\n </Button>\n ))}\n </div>\n ) : null}\n\n {hasActionTaken ? (\n <p className=\"text-xs text-muted-foreground\">\n {t('messages.actions.taken', 'Action taken')}: {actionTakenLabel}\n {actionTakenAtLabel ? ` (${actionTakenAtLabel})` : ''}\n {actionTakenByLabel ? ` ${t('messages.actions.by', 'by')} ${actionTakenByLabel}` : ''}\n </p>\n ) : null}\n </div>\n )\n}\n\nfunction formatDateTime(value?: Date | string | null): string {\n if (!value) return ''\n const date = value instanceof Date ? value : new Date(value)\n if (Number.isNaN(date.getTime())) return ''\n return date.toLocaleString()\n}\n\nfunction extractActionId(actionTaken?: string | null): string | null {\n if (!actionTaken) return null\n const value = actionTaken.trim()\n if (value.length === 0) return null\n const parts = value.split(':')\n return parts[parts.length - 1] ?? value\n}\n\nfunction resolveActionLabel(\n actionId: string | null,\n actions: ObjectDetailProps['actions'],\n t: ReturnType<typeof useT>,\n): string {\n if (!actionId) return '-'\n const action = actions.find((item) => item.id === actionId)\n if (!action) return actionId\n return t(action.labelKey ?? action.id, action.id)\n}\n\nfunction formatUserLabel(\n userId: string | null | undefined,\n userName: string | null,\n t: ReturnType<typeof useT>,\n): string {\n if (!userId) return ''\n if (userName) return userName\n return t('common.user', 'user')\n}\n\nexport default LeaveRequestDetail\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
@@ -53,7 +53,8 @@ function LeaveRequestForm(props) {
53
53
  isLoading,
54
54
  loadingMessage,
55
55
  allowMemberSelect = true,
56
- memberLabel
56
+ memberLabel,
57
+ extraActions
57
58
  } = props;
58
59
  const t = useT();
59
60
  const scopeVersion = useOrganizationScopeVersion();
@@ -275,6 +276,7 @@ function LeaveRequestForm(props) {
275
276
  cancelHref,
276
277
  onSubmit,
277
278
  onDelete,
279
+ extraActions,
278
280
  isLoading,
279
281
  loadingMessage
280
282
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/modules/staff/components/LeaveRequestForm.tsx"],
4
- "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { CrudForm, type CrudField } from '@open-mercato/ui/backend/CrudForm'\nimport { LookupSelect, type LookupSelectItem } from '@open-mercato/ui/backend/inputs'\nimport { DictionaryEntrySelect, type DictionarySelectLabels } from '@open-mercato/core/modules/dictionaries/components/DictionaryEntrySelect'\nimport {\n createUnavailabilityReasonEntry,\n loadUnavailabilityReasonEntries,\n type UnavailabilityReasonEntry,\n} from '@open-mercato/core/modules/planner/components/unavailabilityReasons'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { apiCall } from '@open-mercato/ui/backend/utils/apiCall'\nimport { useOrganizationScopeVersion } from '@open-mercato/shared/lib/frontend/useOrganizationScope'\n\ntype TeamMemberResponse = {\n items?: Array<Record<string, unknown>>\n}\n\ntype FeatureCheckResponse = {\n ok?: boolean\n granted?: string[]\n}\n\nexport type LeaveRequestFormValues = {\n id?: string\n memberId?: string | null\n memberLabel?: string | null\n startDate?: string | Date | null\n endDate?: string | Date | null\n timezone?: string | null\n unavailabilityReasonEntryId?: string | null\n unavailabilityReasonValue?: string | null\n note?: string | null\n}\n\nexport type LeaveRequestFormProps = {\n title: string\n submitLabel?: string\n backHref: string\n cancelHref: string\n initialValues: LeaveRequestFormValues\n onSubmit: (values: LeaveRequestFormValues) => Promise<void>\n onDelete?: () => Promise<void>\n isLoading?: boolean\n loadingMessage?: string\n allowMemberSelect?: boolean\n memberLabel?: string | null\n}\n\nconst DEFAULT_TIMEZONE = 'UTC'\n\nfunction toDateInputValue(value?: string | Date | null): string | null {\n if (!value) return null\n if (value instanceof Date) {\n if (Number.isNaN(value.getTime())) return null\n return value.toISOString().slice(0, 10)\n }\n if (typeof value === 'string') {\n const trimmed = value.trim()\n if (!trimmed) return null\n if (/^\\d{4}-\\d{2}-\\d{2}/.test(trimmed)) return trimmed.slice(0, 10)\n const parsed = new Date(trimmed)\n if (Number.isNaN(parsed.getTime())) return null\n return parsed.toISOString().slice(0, 10)\n }\n return null\n}\n\nexport function buildLeaveRequestPayload(\n values: LeaveRequestFormValues,\n options: { id?: string } = {},\n): Record<string, unknown> {\n const timezone = values.timezone ?? Intl.DateTimeFormat().resolvedOptions().timeZone ?? DEFAULT_TIMEZONE\n return {\n ...(options.id ? { id: options.id } : {}),\n memberId: values.memberId ?? null,\n startDate: values.startDate ?? null,\n endDate: values.endDate ?? null,\n timezone,\n unavailabilityReasonEntryId: values.unavailabilityReasonEntryId ?? null,\n unavailabilityReasonValue: values.unavailabilityReasonValue ?? null,\n note: values.note ?? null,\n }\n}\n\nexport function LeaveRequestForm(props: LeaveRequestFormProps) {\n const {\n title,\n submitLabel,\n backHref,\n cancelHref,\n initialValues,\n onSubmit,\n onDelete,\n isLoading,\n loadingMessage,\n allowMemberSelect = true,\n memberLabel,\n } = props\n const t = useT()\n const scopeVersion = useOrganizationScopeVersion()\n const [memberOptions, setMemberOptions] = React.useState<LookupSelectItem[]>([])\n const [reasonEntriesById, setReasonEntriesById] = React.useState<Record<string, UnavailabilityReasonEntry>>({})\n const [canManageReasons, setCanManageReasons] = React.useState(false)\n const resolvedMemberLabel = memberLabel ?? initialValues.memberLabel ?? null\n const normalizedInitialValues = React.useMemo<LeaveRequestFormValues>(() => ({\n ...initialValues,\n startDate: toDateInputValue(initialValues.startDate),\n endDate: toDateInputValue(initialValues.endDate),\n }), [initialValues])\n\n const labels = React.useMemo(() => ({\n member: t('staff.leaveRequests.form.fields.member', 'Team member'),\n startDate: t('staff.leaveRequests.form.fields.startDate', 'Start date'),\n endDate: t('staff.leaveRequests.form.fields.endDate', 'End date'),\n reason: t('staff.leaveRequests.form.fields.reason', 'Reason'),\n note: t('staff.leaveRequests.form.fields.note', 'Note'),\n notePlaceholder: t('staff.leaveRequests.form.fields.notePlaceholder', 'Optional note'),\n }), [t])\n\n const reasonLabels = React.useMemo<DictionarySelectLabels>(() => ({\n placeholder: t('staff.leaveRequests.form.reason.placeholder', 'Select a reason'),\n addLabel: t('staff.leaveRequests.form.reason.add', 'Add reason'),\n addPrompt: t('staff.leaveRequests.form.reason.prompt', 'Name the reason'),\n dialogTitle: t('staff.leaveRequests.form.reason.dialogTitle', 'Add reason'),\n valueLabel: t('staff.leaveRequests.form.reason.valueLabel', 'Reason'),\n valuePlaceholder: t('staff.leaveRequests.form.reason.valuePlaceholder', 'Reason name'),\n labelLabel: t('staff.leaveRequests.form.reason.labelLabel', 'Label'),\n labelPlaceholder: t('staff.leaveRequests.form.reason.labelPlaceholder', 'Display label (optional)'),\n emptyError: t('staff.leaveRequests.form.reason.emptyError', 'Please enter a reason'),\n cancelLabel: t('staff.leaveRequests.form.reason.cancel', 'Cancel'),\n saveLabel: t('staff.leaveRequests.form.reason.save', 'Save'),\n saveShortcutHint: t('staff.leaveRequests.form.reason.saveShortcut', 'Cmd/Ctrl + Enter'),\n errorLoad: t('staff.leaveRequests.form.reason.errorLoad', 'Failed to load reasons'),\n errorSave: t('staff.leaveRequests.form.reason.errorSave', 'Failed to save reason'),\n loadingLabel: t('staff.leaveRequests.form.reason.loading', 'Loading\u2026'),\n manageTitle: t('staff.leaveRequests.form.reason.manage', 'Manage reasons'),\n }), [t])\n\n const fetchReasonOptions = React.useCallback(async () => {\n const entries = await loadUnavailabilityReasonEntries('member')\n const map: Record<string, UnavailabilityReasonEntry> = {}\n entries.forEach((entry) => {\n map[entry.id] = entry\n })\n setReasonEntriesById(map)\n return entries.map((entry) => ({\n value: entry.id,\n label: entry.label ?? entry.value,\n color: entry.color,\n icon: entry.icon,\n }))\n }, [])\n\n const createReasonOption = React.useCallback(\n async (input: { value: string; label?: string; color?: string | null; icon?: string | null }) => {\n const entry = await createUnavailabilityReasonEntry('member', input)\n if (!entry) return null\n setReasonEntriesById((prev) => ({ ...prev, [entry.id]: entry }))\n return {\n value: entry.id,\n label: entry.label ?? entry.value,\n color: entry.color,\n icon: entry.icon,\n }\n },\n [],\n )\n\n const fetchMemberOptions = React.useCallback(async (query?: string): Promise<LookupSelectItem[]> => {\n const params = new URLSearchParams({ page: '1', pageSize: '50' })\n if (query && query.trim().length) params.set('search', query.trim())\n const call = await apiCall<TeamMemberResponse>(`/api/staff/team-members?${params.toString()}`)\n const items = Array.isArray(call.result?.items) ? call.result.items : []\n const options = items\n .map((item) => {\n const id = typeof item?.id === 'string' ? item.id : null\n const displayName = typeof item?.displayName === 'string'\n ? item.displayName\n : typeof item?.display_name === 'string'\n ? item.display_name\n : null\n if (!id || !displayName) return null\n return { id, title: displayName }\n })\n .filter((option): option is LookupSelectItem => option !== null)\n setMemberOptions(options)\n return options\n }, [])\n\n React.useEffect(() => {\n if (!allowMemberSelect) return\n fetchMemberOptions().catch(() => {})\n }, [allowMemberSelect, fetchMemberOptions, scopeVersion])\n\n React.useEffect(() => {\n let cancelled = false\n async function loadPermissions() {\n try {\n const call = await apiCall<FeatureCheckResponse>('/api/auth/feature-check', {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ features: ['dictionaries.manage'] }),\n })\n const granted = Array.isArray(call.result?.granted) ? call.result?.granted ?? [] : []\n if (!cancelled) {\n setCanManageReasons(granted.includes('dictionaries.manage'))\n }\n } catch {\n if (!cancelled) setCanManageReasons(false)\n }\n }\n void loadPermissions()\n return () => { cancelled = true }\n }, [scopeVersion])\n\n React.useEffect(() => {\n const selected = typeof initialValues.memberId === 'string' ? initialValues.memberId : null\n if (!selected || !allowMemberSelect) return\n if (memberOptions.some((option) => option.id === selected)) return\n const selectedId = selected\n let cancelled = false\n async function loadMember() {\n try {\n const params = new URLSearchParams({ page: '1', pageSize: '1' })\n params.set('ids', selectedId)\n const call = await apiCall<TeamMemberResponse>(`/api/staff/team-members?${params.toString()}`)\n const item = Array.isArray(call.result?.items) ? call.result.items[0] : null\n const id = typeof item?.id === 'string' ? item.id : null\n const displayName = typeof item?.displayName === 'string'\n ? item.displayName\n : typeof item?.display_name === 'string'\n ? item.display_name\n : null\n if (!id || !displayName) return\n if (!cancelled) {\n setMemberOptions((prev) => {\n if (prev.some((option) => option.id === id)) return prev\n return [{ id, title: displayName }, ...prev]\n })\n }\n } catch {\n if (!cancelled) setMemberOptions((prev) => prev)\n }\n }\n loadMember()\n return () => { cancelled = true }\n }, [allowMemberSelect, initialValues.memberId, memberOptions])\n\n const fields = React.useMemo<CrudField[]>(() => {\n const baseFields: CrudField[] = []\n baseFields.push({\n id: 'memberId',\n label: labels.member,\n type: 'custom',\n disabled: !allowMemberSelect,\n component: ({ value, setValue, disabled }) => {\n if (!allowMemberSelect) {\n return (\n <div className=\"text-sm text-muted-foreground\">\n {resolvedMemberLabel ?? t('staff.leaveRequests.form.fields.member.self', 'Your profile')}\n </div>\n )\n }\n return (\n <LookupSelect\n value={typeof value === 'string' ? value : null}\n onChange={(next) => setValue(next)}\n fetchOptions={fetchMemberOptions}\n options={memberOptions}\n placeholder={labels.member}\n disabled={disabled}\n />\n )\n },\n })\n baseFields.push(\n { id: 'startDate', label: labels.startDate, type: 'date', required: true, layout: 'half' },\n { id: 'endDate', label: labels.endDate, type: 'date', required: true, layout: 'half' },\n {\n id: 'unavailabilityReasonEntryId',\n label: labels.reason,\n type: 'custom',\n component: ({ value, setValue, setFormValue }) => (\n <DictionaryEntrySelect\n value={typeof value === 'string' ? value : undefined}\n onChange={(next) => {\n setValue(next ?? null)\n if (setFormValue) {\n const entry = next ? reasonEntriesById[next] : null\n setFormValue('unavailabilityReasonValue', entry?.value ?? null)\n }\n }}\n fetchOptions={fetchReasonOptions}\n createOption={canManageReasons ? createReasonOption : undefined}\n labels={reasonLabels}\n selectClassName=\"w-full\"\n manageHref={canManageReasons ? '/backend/config/dictionaries' : undefined}\n allowInlineCreate={canManageReasons}\n showManage={canManageReasons}\n />\n ),\n },\n {\n id: 'note',\n label: labels.note,\n type: 'textarea',\n placeholder: labels.notePlaceholder,\n },\n )\n return baseFields\n }, [\n allowMemberSelect,\n fetchMemberOptions,\n fetchReasonOptions,\n createReasonOption,\n labels,\n memberOptions,\n reasonEntriesById,\n reasonLabels,\n resolvedMemberLabel,\n t,\n canManageReasons,\n ])\n\n return (\n <CrudForm\n title={title}\n fields={fields}\n initialValues={normalizedInitialValues}\n submitLabel={submitLabel}\n backHref={backHref}\n cancelHref={cancelHref}\n onSubmit={onSubmit}\n onDelete={onDelete}\n isLoading={isLoading}\n loadingMessage={loadingMessage}\n />\n )\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { CrudForm, type CrudField } from '@open-mercato/ui/backend/CrudForm'\nimport { LookupSelect, type LookupSelectItem } from '@open-mercato/ui/backend/inputs'\nimport { DictionaryEntrySelect, type DictionarySelectLabels } from '@open-mercato/core/modules/dictionaries/components/DictionaryEntrySelect'\nimport {\n createUnavailabilityReasonEntry,\n loadUnavailabilityReasonEntries,\n type UnavailabilityReasonEntry,\n} from '@open-mercato/core/modules/planner/components/unavailabilityReasons'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { apiCall } from '@open-mercato/ui/backend/utils/apiCall'\nimport { useOrganizationScopeVersion } from '@open-mercato/shared/lib/frontend/useOrganizationScope'\n\ntype TeamMemberResponse = {\n items?: Array<Record<string, unknown>>\n}\n\ntype FeatureCheckResponse = {\n ok?: boolean\n granted?: string[]\n}\n\nexport type LeaveRequestFormValues = {\n id?: string\n memberId?: string | null\n memberLabel?: string | null\n startDate?: string | Date | null\n endDate?: string | Date | null\n timezone?: string | null\n unavailabilityReasonEntryId?: string | null\n unavailabilityReasonValue?: string | null\n note?: string | null\n}\n\nexport type LeaveRequestFormProps = {\n title: string\n submitLabel?: string\n backHref: string\n cancelHref: string\n initialValues: LeaveRequestFormValues\n onSubmit: (values: LeaveRequestFormValues) => Promise<void>\n onDelete?: () => Promise<void>\n isLoading?: boolean\n loadingMessage?: string\n allowMemberSelect?: boolean\n memberLabel?: string | null\n extraActions?: React.ReactNode\n}\n\nconst DEFAULT_TIMEZONE = 'UTC'\n\nfunction toDateInputValue(value?: string | Date | null): string | null {\n if (!value) return null\n if (value instanceof Date) {\n if (Number.isNaN(value.getTime())) return null\n return value.toISOString().slice(0, 10)\n }\n if (typeof value === 'string') {\n const trimmed = value.trim()\n if (!trimmed) return null\n if (/^\\d{4}-\\d{2}-\\d{2}/.test(trimmed)) return trimmed.slice(0, 10)\n const parsed = new Date(trimmed)\n if (Number.isNaN(parsed.getTime())) return null\n return parsed.toISOString().slice(0, 10)\n }\n return null\n}\n\nexport function buildLeaveRequestPayload(\n values: LeaveRequestFormValues,\n options: { id?: string } = {},\n): Record<string, unknown> {\n const timezone = values.timezone ?? Intl.DateTimeFormat().resolvedOptions().timeZone ?? DEFAULT_TIMEZONE\n return {\n ...(options.id ? { id: options.id } : {}),\n memberId: values.memberId ?? null,\n startDate: values.startDate ?? null,\n endDate: values.endDate ?? null,\n timezone,\n unavailabilityReasonEntryId: values.unavailabilityReasonEntryId ?? null,\n unavailabilityReasonValue: values.unavailabilityReasonValue ?? null,\n note: values.note ?? null,\n }\n}\n\nexport function LeaveRequestForm(props: LeaveRequestFormProps) {\n const {\n title,\n submitLabel,\n backHref,\n cancelHref,\n initialValues,\n onSubmit,\n onDelete,\n isLoading,\n loadingMessage,\n allowMemberSelect = true,\n memberLabel,\n extraActions,\n } = props\n const t = useT()\n const scopeVersion = useOrganizationScopeVersion()\n const [memberOptions, setMemberOptions] = React.useState<LookupSelectItem[]>([])\n const [reasonEntriesById, setReasonEntriesById] = React.useState<Record<string, UnavailabilityReasonEntry>>({})\n const [canManageReasons, setCanManageReasons] = React.useState(false)\n const resolvedMemberLabel = memberLabel ?? initialValues.memberLabel ?? null\n const normalizedInitialValues = React.useMemo<LeaveRequestFormValues>(() => ({\n ...initialValues,\n startDate: toDateInputValue(initialValues.startDate),\n endDate: toDateInputValue(initialValues.endDate),\n }), [initialValues])\n\n const labels = React.useMemo(() => ({\n member: t('staff.leaveRequests.form.fields.member', 'Team member'),\n startDate: t('staff.leaveRequests.form.fields.startDate', 'Start date'),\n endDate: t('staff.leaveRequests.form.fields.endDate', 'End date'),\n reason: t('staff.leaveRequests.form.fields.reason', 'Reason'),\n note: t('staff.leaveRequests.form.fields.note', 'Note'),\n notePlaceholder: t('staff.leaveRequests.form.fields.notePlaceholder', 'Optional note'),\n }), [t])\n\n const reasonLabels = React.useMemo<DictionarySelectLabels>(() => ({\n placeholder: t('staff.leaveRequests.form.reason.placeholder', 'Select a reason'),\n addLabel: t('staff.leaveRequests.form.reason.add', 'Add reason'),\n addPrompt: t('staff.leaveRequests.form.reason.prompt', 'Name the reason'),\n dialogTitle: t('staff.leaveRequests.form.reason.dialogTitle', 'Add reason'),\n valueLabel: t('staff.leaveRequests.form.reason.valueLabel', 'Reason'),\n valuePlaceholder: t('staff.leaveRequests.form.reason.valuePlaceholder', 'Reason name'),\n labelLabel: t('staff.leaveRequests.form.reason.labelLabel', 'Label'),\n labelPlaceholder: t('staff.leaveRequests.form.reason.labelPlaceholder', 'Display label (optional)'),\n emptyError: t('staff.leaveRequests.form.reason.emptyError', 'Please enter a reason'),\n cancelLabel: t('staff.leaveRequests.form.reason.cancel', 'Cancel'),\n saveLabel: t('staff.leaveRequests.form.reason.save', 'Save'),\n saveShortcutHint: t('staff.leaveRequests.form.reason.saveShortcut', 'Cmd/Ctrl + Enter'),\n errorLoad: t('staff.leaveRequests.form.reason.errorLoad', 'Failed to load reasons'),\n errorSave: t('staff.leaveRequests.form.reason.errorSave', 'Failed to save reason'),\n loadingLabel: t('staff.leaveRequests.form.reason.loading', 'Loading\u2026'),\n manageTitle: t('staff.leaveRequests.form.reason.manage', 'Manage reasons'),\n }), [t])\n\n const fetchReasonOptions = React.useCallback(async () => {\n const entries = await loadUnavailabilityReasonEntries('member')\n const map: Record<string, UnavailabilityReasonEntry> = {}\n entries.forEach((entry) => {\n map[entry.id] = entry\n })\n setReasonEntriesById(map)\n return entries.map((entry) => ({\n value: entry.id,\n label: entry.label ?? entry.value,\n color: entry.color,\n icon: entry.icon,\n }))\n }, [])\n\n const createReasonOption = React.useCallback(\n async (input: { value: string; label?: string; color?: string | null; icon?: string | null }) => {\n const entry = await createUnavailabilityReasonEntry('member', input)\n if (!entry) return null\n setReasonEntriesById((prev) => ({ ...prev, [entry.id]: entry }))\n return {\n value: entry.id,\n label: entry.label ?? entry.value,\n color: entry.color,\n icon: entry.icon,\n }\n },\n [],\n )\n\n const fetchMemberOptions = React.useCallback(async (query?: string): Promise<LookupSelectItem[]> => {\n const params = new URLSearchParams({ page: '1', pageSize: '50' })\n if (query && query.trim().length) params.set('search', query.trim())\n const call = await apiCall<TeamMemberResponse>(`/api/staff/team-members?${params.toString()}`)\n const items = Array.isArray(call.result?.items) ? call.result.items : []\n const options = items\n .map((item) => {\n const id = typeof item?.id === 'string' ? item.id : null\n const displayName = typeof item?.displayName === 'string'\n ? item.displayName\n : typeof item?.display_name === 'string'\n ? item.display_name\n : null\n if (!id || !displayName) return null\n return { id, title: displayName }\n })\n .filter((option): option is LookupSelectItem => option !== null)\n setMemberOptions(options)\n return options\n }, [])\n\n React.useEffect(() => {\n if (!allowMemberSelect) return\n fetchMemberOptions().catch(() => {})\n }, [allowMemberSelect, fetchMemberOptions, scopeVersion])\n\n React.useEffect(() => {\n let cancelled = false\n async function loadPermissions() {\n try {\n const call = await apiCall<FeatureCheckResponse>('/api/auth/feature-check', {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ features: ['dictionaries.manage'] }),\n })\n const granted = Array.isArray(call.result?.granted) ? call.result?.granted ?? [] : []\n if (!cancelled) {\n setCanManageReasons(granted.includes('dictionaries.manage'))\n }\n } catch {\n if (!cancelled) setCanManageReasons(false)\n }\n }\n void loadPermissions()\n return () => { cancelled = true }\n }, [scopeVersion])\n\n React.useEffect(() => {\n const selected = typeof initialValues.memberId === 'string' ? initialValues.memberId : null\n if (!selected || !allowMemberSelect) return\n if (memberOptions.some((option) => option.id === selected)) return\n const selectedId = selected\n let cancelled = false\n async function loadMember() {\n try {\n const params = new URLSearchParams({ page: '1', pageSize: '1' })\n params.set('ids', selectedId)\n const call = await apiCall<TeamMemberResponse>(`/api/staff/team-members?${params.toString()}`)\n const item = Array.isArray(call.result?.items) ? call.result.items[0] : null\n const id = typeof item?.id === 'string' ? item.id : null\n const displayName = typeof item?.displayName === 'string'\n ? item.displayName\n : typeof item?.display_name === 'string'\n ? item.display_name\n : null\n if (!id || !displayName) return\n if (!cancelled) {\n setMemberOptions((prev) => {\n if (prev.some((option) => option.id === id)) return prev\n return [{ id, title: displayName }, ...prev]\n })\n }\n } catch {\n if (!cancelled) setMemberOptions((prev) => prev)\n }\n }\n loadMember()\n return () => { cancelled = true }\n }, [allowMemberSelect, initialValues.memberId, memberOptions])\n\n const fields = React.useMemo<CrudField[]>(() => {\n const baseFields: CrudField[] = []\n baseFields.push({\n id: 'memberId',\n label: labels.member,\n type: 'custom',\n disabled: !allowMemberSelect,\n component: ({ value, setValue, disabled }) => {\n if (!allowMemberSelect) {\n return (\n <div className=\"text-sm text-muted-foreground\">\n {resolvedMemberLabel ?? t('staff.leaveRequests.form.fields.member.self', 'Your profile')}\n </div>\n )\n }\n return (\n <LookupSelect\n value={typeof value === 'string' ? value : null}\n onChange={(next) => setValue(next)}\n fetchOptions={fetchMemberOptions}\n options={memberOptions}\n placeholder={labels.member}\n disabled={disabled}\n />\n )\n },\n })\n baseFields.push(\n { id: 'startDate', label: labels.startDate, type: 'date', required: true, layout: 'half' },\n { id: 'endDate', label: labels.endDate, type: 'date', required: true, layout: 'half' },\n {\n id: 'unavailabilityReasonEntryId',\n label: labels.reason,\n type: 'custom',\n component: ({ value, setValue, setFormValue }) => (\n <DictionaryEntrySelect\n value={typeof value === 'string' ? value : undefined}\n onChange={(next) => {\n setValue(next ?? null)\n if (setFormValue) {\n const entry = next ? reasonEntriesById[next] : null\n setFormValue('unavailabilityReasonValue', entry?.value ?? null)\n }\n }}\n fetchOptions={fetchReasonOptions}\n createOption={canManageReasons ? createReasonOption : undefined}\n labels={reasonLabels}\n selectClassName=\"w-full\"\n manageHref={canManageReasons ? '/backend/config/dictionaries' : undefined}\n allowInlineCreate={canManageReasons}\n showManage={canManageReasons}\n />\n ),\n },\n {\n id: 'note',\n label: labels.note,\n type: 'textarea',\n placeholder: labels.notePlaceholder,\n },\n )\n return baseFields\n }, [\n allowMemberSelect,\n fetchMemberOptions,\n fetchReasonOptions,\n createReasonOption,\n labels,\n memberOptions,\n reasonEntriesById,\n reasonLabels,\n resolvedMemberLabel,\n t,\n canManageReasons,\n ])\n\n return (\n <CrudForm\n title={title}\n fields={fields}\n initialValues={normalizedInitialValues}\n submitLabel={submitLabel}\n backHref={backHref}\n cancelHref={cancelHref}\n onSubmit={onSubmit}\n onDelete={onDelete}\n extraActions={extraActions}\n isLoading={isLoading}\n loadingMessage={loadingMessage}\n />\n )\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -0,0 +1,43 @@
1
+ "use client";
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
+ import { CalendarClock } from "lucide-react";
4
+ import { Badge } from "@open-mercato/ui/primitives/badge";
5
+ function LeaveRequestPreview({
6
+ snapshot,
7
+ previewData,
8
+ actionRequired,
9
+ actionLabel
10
+ }) {
11
+ const data = snapshot;
12
+ const title = previewData?.title || "Leave Request";
13
+ const subtitle = previewData?.subtitle || (data ? `${data.employeeName} - ${data.startDate} to ${data.endDate}` : "Leave Request Details");
14
+ const status = previewData?.status || data?.status;
15
+ const statusColor = previewData?.statusColor || "amber";
16
+ return /* @__PURE__ */ jsxs("div", { className: "flex items-start gap-3 rounded-md border p-3 bg-muted/30", children: [
17
+ /* @__PURE__ */ jsx(CalendarClock, { className: "h-5 w-5 text-muted-foreground mt-0.5 flex-shrink-0" }),
18
+ /* @__PURE__ */ jsxs("div", { className: "flex-1 min-w-0", children: [
19
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 flex-wrap", children: [
20
+ /* @__PURE__ */ jsx("span", { className: "font-medium text-sm", children: title }),
21
+ actionRequired && /* @__PURE__ */ jsx(Badge, { variant: "secondary", className: "text-xs", children: actionLabel || "Action Required" })
22
+ ] }),
23
+ subtitle && /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground mt-1", children: subtitle }),
24
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 mt-2", children: [
25
+ status && /* @__PURE__ */ jsx(Badge, { variant: "outline", className: "text-xs", children: status }),
26
+ data?.type && /* @__PURE__ */ jsx(Badge, { variant: "secondary", className: "text-xs", children: data.type })
27
+ ] }),
28
+ previewData?.metadata && Object.keys(previewData.metadata).length > 0 && /* @__PURE__ */ jsx("div", { className: "mt-2 space-y-1", children: Object.entries(previewData.metadata).slice(0, 3).map(([key, value]) => /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1 text-xs text-muted-foreground", children: [
29
+ /* @__PURE__ */ jsxs("span", { className: "font-medium", children: [
30
+ key,
31
+ ":"
32
+ ] }),
33
+ /* @__PURE__ */ jsx("span", { className: "truncate", children: value })
34
+ ] }, key)) })
35
+ ] })
36
+ ] });
37
+ }
38
+ var LeaveRequestPreview_default = LeaveRequestPreview;
39
+ export {
40
+ LeaveRequestPreview,
41
+ LeaveRequestPreview_default as default
42
+ };
43
+ //# sourceMappingURL=LeaveRequestPreview.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../src/modules/staff/components/LeaveRequestPreview.tsx"],
4
+ "sourcesContent": ["\"use client\"\n\nimport type { ObjectPreviewProps } from '@open-mercato/shared/modules/messages/types'\nimport { CalendarClock } from 'lucide-react'\nimport { Badge } from '@open-mercato/ui/primitives/badge'\n\nexport function LeaveRequestPreview({\n snapshot,\n previewData,\n actionRequired,\n actionLabel\n}: ObjectPreviewProps) {\n // Use previewData if available, otherwise fall back to snapshot\n const data = snapshot as {\n employeeName?: string\n startDate?: string\n endDate?: string\n status?: string\n type?: string\n } | undefined\n\n const title = previewData?.title || 'Leave Request'\n const subtitle = previewData?.subtitle || (data ?\n `${data.employeeName} - ${data.startDate} to ${data.endDate}` :\n 'Leave Request Details'\n )\n const status = previewData?.status || data?.status\n const statusColor = previewData?.statusColor || 'amber'\n\n return (\n <div className=\"flex items-start gap-3 rounded-md border p-3 bg-muted/30\">\n <CalendarClock className=\"h-5 w-5 text-muted-foreground mt-0.5 flex-shrink-0\" />\n <div className=\"flex-1 min-w-0\">\n <div className=\"flex items-center gap-2 flex-wrap\">\n <span className=\"font-medium text-sm\">{title}</span>\n {actionRequired && (\n <Badge variant=\"secondary\" className=\"text-xs\">\n {actionLabel || 'Action Required'}\n </Badge>\n )}\n </div>\n {subtitle && (\n <p className=\"text-sm text-muted-foreground mt-1\">\n {subtitle}\n </p>\n )}\n <div className=\"flex items-center gap-2 mt-2\">\n {status && (\n <Badge variant=\"outline\" className=\"text-xs\">\n {status}\n </Badge>\n )}\n {data?.type && (\n <Badge variant=\"secondary\" className=\"text-xs\">\n {data.type}\n </Badge>\n )}\n </div>\n {previewData?.metadata && Object.keys(previewData.metadata).length > 0 && (\n <div className=\"mt-2 space-y-1\">\n {Object.entries(previewData.metadata).slice(0, 3).map(([key, value]) => (\n <div key={key} className=\"flex items-center gap-1 text-xs text-muted-foreground\">\n <span className=\"font-medium\">{key}:</span>\n <span className=\"truncate\">{value}</span>\n </div>\n ))}\n </div>\n )}\n </div>\n </div>\n )\n}\n\nexport default LeaveRequestPreview"],
5
+ "mappings": ";AA+BM,cAEE,YAFF;AA5BN,SAAS,qBAAqB;AAC9B,SAAS,aAAa;AAEf,SAAS,oBAAoB;AAAA,EAClC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAAuB;AAErB,QAAM,OAAO;AAQb,QAAM,QAAQ,aAAa,SAAS;AACpC,QAAM,WAAW,aAAa,aAAa,OACzC,GAAG,KAAK,YAAY,MAAM,KAAK,SAAS,OAAO,KAAK,OAAO,KAC3D;AAEF,QAAM,SAAS,aAAa,UAAU,MAAM;AAC5C,QAAM,cAAc,aAAa,eAAe;AAEhD,SACE,qBAAC,SAAI,WAAU,4DACb;AAAA,wBAAC,iBAAc,WAAU,sDAAqD;AAAA,IAC9E,qBAAC,SAAI,WAAU,kBACb;AAAA,2BAAC,SAAI,WAAU,qCACb;AAAA,4BAAC,UAAK,WAAU,uBAAuB,iBAAM;AAAA,QAC5C,kBACC,oBAAC,SAAM,SAAQ,aAAY,WAAU,WAClC,yBAAe,mBAClB;AAAA,SAEJ;AAAA,MACC,YACC,oBAAC,OAAE,WAAU,sCACV,oBACH;AAAA,MAEF,qBAAC,SAAI,WAAU,gCACZ;AAAA,kBACC,oBAAC,SAAM,SAAQ,WAAU,WAAU,WAChC,kBACH;AAAA,QAED,MAAM,QACL,oBAAC,SAAM,SAAQ,aAAY,WAAU,WAClC,eAAK,MACR;AAAA,SAEJ;AAAA,MACC,aAAa,YAAY,OAAO,KAAK,YAAY,QAAQ,EAAE,SAAS,KACnE,oBAAC,SAAI,WAAU,kBACZ,iBAAO,QAAQ,YAAY,QAAQ,EAAE,MAAM,GAAG,CAAC,EAAE,IAAI,CAAC,CAAC,KAAK,KAAK,MAChE,qBAAC,SAAc,WAAU,yDACvB;AAAA,6BAAC,UAAK,WAAU,eAAe;AAAA;AAAA,UAAI;AAAA,WAAC;AAAA,QACpC,oBAAC,UAAK,WAAU,YAAY,iBAAM;AAAA,WAF1B,GAGV,CACD,GACH;AAAA,OAEJ;AAAA,KACF;AAEJ;AAEA,IAAO,8BAAQ;",
6
+ "names": []
7
+ }