@open-mercato/core 0.10.1-develop.7716.1.cefe7975df → 0.10.1-develop.7721.1.b5c4a00a68

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 (384) hide show
  1. package/.turbo/turbo-build.log +2 -2
  2. package/dist/generated/entities/staff_support_routing_rule/index.js +21 -0
  3. package/dist/generated/entities/staff_support_routing_rule/index.js.map +7 -0
  4. package/dist/generated/entities/staff_support_ticket/index.js +37 -0
  5. package/dist/generated/entities/staff_support_ticket/index.js.map +7 -0
  6. package/dist/generated/entities/staff_time_project/index.js +2 -0
  7. package/dist/generated/entities/staff_time_project/index.js.map +2 -2
  8. package/dist/generated/entities/staff_time_project_member/index.js +2 -0
  9. package/dist/generated/entities/staff_time_project_member/index.js.map +2 -2
  10. package/dist/generated/entities/staff_time_task/index.js +4 -0
  11. package/dist/generated/entities/staff_time_task/index.js.map +2 -2
  12. package/dist/generated/entities/staff_time_task_comment/index.js +9 -1
  13. package/dist/generated/entities/staff_time_task_comment/index.js.map +2 -2
  14. package/dist/generated/entities.ids.generated.js +2 -0
  15. package/dist/generated/entities.ids.generated.js.map +2 -2
  16. package/dist/generated/entity-fields-registry.js +38 -0
  17. package/dist/generated/entity-fields-registry.js.map +2 -2
  18. package/dist/helpers/integration/auth.js +1 -0
  19. package/dist/helpers/integration/auth.js.map +2 -2
  20. package/dist/helpers/integration/communicationChannelsFixtures.js +20 -0
  21. package/dist/helpers/integration/communicationChannelsFixtures.js.map +2 -2
  22. package/dist/modules/catalog/api/bulk-delete/route.js +12 -2
  23. package/dist/modules/catalog/api/bulk-delete/route.js.map +2 -2
  24. package/dist/modules/communication_channels/api/post/test-seed/route.js +17 -3
  25. package/dist/modules/communication_channels/api/post/test-seed/route.js.map +2 -2
  26. package/dist/modules/communication_channels/lib/test-seed.js +38 -5
  27. package/dist/modules/communication_channels/lib/test-seed.js.map +2 -2
  28. package/dist/modules/messages/commands/messages.js +2 -3
  29. package/dist/modules/messages/commands/messages.js.map +2 -2
  30. package/dist/modules/portal/frontend/[orgSlug]/portal/dashboard/page.js +2 -2
  31. package/dist/modules/portal/frontend/[orgSlug]/portal/dashboard/page.js.map +2 -2
  32. package/dist/modules/staff/api/portal/support-tickets/[id]/attachments/route.js +299 -0
  33. package/dist/modules/staff/api/portal/support-tickets/[id]/attachments/route.js.map +7 -0
  34. package/dist/modules/staff/api/portal/support-tickets/[id]/messages/route.js +91 -0
  35. package/dist/modules/staff/api/portal/support-tickets/[id]/messages/route.js.map +7 -0
  36. package/dist/modules/staff/api/portal/support-tickets/[id]/route.js +146 -0
  37. package/dist/modules/staff/api/portal/support-tickets/[id]/route.js.map +7 -0
  38. package/dist/modules/staff/api/portal/support-tickets/options/route.js +60 -0
  39. package/dist/modules/staff/api/portal/support-tickets/options/route.js.map +7 -0
  40. package/dist/modules/staff/api/portal/support-tickets/route.js +220 -0
  41. package/dist/modules/staff/api/portal/support-tickets/route.js.map +7 -0
  42. package/dist/modules/staff/api/timesheets/settings/route.js +7 -1
  43. package/dist/modules/staff/api/timesheets/settings/route.js.map +2 -2
  44. package/dist/modules/staff/api/timesheets/support-routing-rules/fallback-project/route.js +122 -0
  45. package/dist/modules/staff/api/timesheets/support-routing-rules/fallback-project/route.js.map +7 -0
  46. package/dist/modules/staff/api/timesheets/support-routing-rules/preview/route.js +180 -0
  47. package/dist/modules/staff/api/timesheets/support-routing-rules/preview/route.js.map +7 -0
  48. package/dist/modules/staff/api/timesheets/support-routing-rules/route.js +147 -0
  49. package/dist/modules/staff/api/timesheets/support-routing-rules/route.js.map +7 -0
  50. package/dist/modules/staff/api/timesheets/support-tickets/options/route.js +133 -0
  51. package/dist/modules/staff/api/timesheets/support-tickets/options/route.js.map +7 -0
  52. package/dist/modules/staff/api/timesheets/tasks/[id]/claim/route.js +33 -0
  53. package/dist/modules/staff/api/timesheets/tasks/[id]/claim/route.js.map +7 -0
  54. package/dist/modules/staff/api/timesheets/tasks/[id]/comments/route.js +25 -7
  55. package/dist/modules/staff/api/timesheets/tasks/[id]/comments/route.js.map +2 -2
  56. package/dist/modules/staff/api/timesheets/tasks/[id]/release/route.js +32 -0
  57. package/dist/modules/staff/api/timesheets/tasks/[id]/release/route.js.map +7 -0
  58. package/dist/modules/staff/api/timesheets/tasks/route.js +94 -47
  59. package/dist/modules/staff/api/timesheets/tasks/route.js.map +2 -2
  60. package/dist/modules/staff/api/timesheets/time-projects/[id]/employees/route.js +3 -0
  61. package/dist/modules/staff/api/timesheets/time-projects/[id]/employees/route.js.map +2 -2
  62. package/dist/modules/staff/api/timesheets/time-projects/route.js +3 -0
  63. package/dist/modules/staff/api/timesheets/time-projects/route.js.map +2 -2
  64. package/dist/modules/staff/backend/staff/time-tracking/projects/[id]/edit/page.js +1 -0
  65. package/dist/modules/staff/backend/staff/time-tracking/projects/[id]/edit/page.js.map +2 -2
  66. package/dist/modules/staff/backend/staff/time-tracking/projects/[id]/page.js +54 -4
  67. package/dist/modules/staff/backend/staff/time-tracking/projects/[id]/page.js.map +2 -2
  68. package/dist/modules/staff/backend/staff/time-tracking/projects/projectFormConfig.js +21 -5
  69. package/dist/modules/staff/backend/staff/time-tracking/projects/projectFormConfig.js.map +2 -2
  70. package/dist/modules/staff/backend/staff/time-tracking/settings/page.js +319 -254
  71. package/dist/modules/staff/backend/staff/time-tracking/settings/page.js.map +2 -2
  72. package/dist/modules/staff/backend/staff/time-tracking/support/page.js +363 -0
  73. package/dist/modules/staff/backend/staff/time-tracking/support/page.js.map +7 -0
  74. package/dist/modules/staff/backend/staff/time-tracking/support/page.meta.js +18 -0
  75. package/dist/modules/staff/backend/staff/time-tracking/support/page.meta.js.map +7 -0
  76. package/dist/modules/staff/cli.js +27 -0
  77. package/dist/modules/staff/cli.js.map +2 -2
  78. package/dist/modules/staff/commands/index.js +3 -0
  79. package/dist/modules/staff/commands/index.js.map +2 -2
  80. package/dist/modules/staff/commands/shared.js +10 -1
  81. package/dist/modules/staff/commands/shared.js.map +2 -2
  82. package/dist/modules/staff/commands/support-routing-rules.js +505 -0
  83. package/dist/modules/staff/commands/support-routing-rules.js.map +7 -0
  84. package/dist/modules/staff/commands/support-tickets.js +513 -0
  85. package/dist/modules/staff/commands/support-tickets.js.map +7 -0
  86. package/dist/modules/staff/commands/timesheets-projects.js +72 -14
  87. package/dist/modules/staff/commands/timesheets-projects.js.map +2 -2
  88. package/dist/modules/staff/commands/timesheets-task-assignment.js +270 -0
  89. package/dist/modules/staff/commands/timesheets-task-assignment.js.map +7 -0
  90. package/dist/modules/staff/commands/timesheets-task-comments.js +58 -12
  91. package/dist/modules/staff/commands/timesheets-task-comments.js.map +2 -2
  92. package/dist/modules/staff/commands/timesheets-tasks.js +91 -9
  93. package/dist/modules/staff/commands/timesheets-tasks.js.map +3 -3
  94. package/dist/modules/staff/data/enrichers.js +141 -0
  95. package/dist/modules/staff/data/enrichers.js.map +2 -2
  96. package/dist/modules/staff/data/entities.js +143 -2
  97. package/dist/modules/staff/data/entities.js.map +2 -2
  98. package/dist/modules/staff/data/extensions.js +8 -0
  99. package/dist/modules/staff/data/extensions.js.map +2 -2
  100. package/dist/modules/staff/data/validators.js +34 -5
  101. package/dist/modules/staff/data/validators.js.map +2 -2
  102. package/dist/modules/staff/encryption.js +6 -0
  103. package/dist/modules/staff/encryption.js.map +2 -2
  104. package/dist/modules/staff/events.js +25 -0
  105. package/dist/modules/staff/events.js.map +2 -2
  106. package/dist/modules/staff/events.payloads.js +54 -0
  107. package/dist/modules/staff/events.payloads.js.map +2 -2
  108. package/dist/modules/staff/extension-points.js +1 -0
  109. package/dist/modules/staff/extension-points.js.map +2 -2
  110. package/dist/modules/staff/frontend/[orgSlug]/portal/support/[id]/page.js +468 -0
  111. package/dist/modules/staff/frontend/[orgSlug]/portal/support/[id]/page.js.map +7 -0
  112. package/dist/modules/staff/frontend/[orgSlug]/portal/support/[id]/page.meta.js +12 -0
  113. package/dist/modules/staff/frontend/[orgSlug]/portal/support/[id]/page.meta.js.map +7 -0
  114. package/dist/modules/staff/frontend/[orgSlug]/portal/support/new/page.js +316 -0
  115. package/dist/modules/staff/frontend/[orgSlug]/portal/support/new/page.js.map +7 -0
  116. package/dist/modules/staff/frontend/[orgSlug]/portal/support/new/page.meta.js +12 -0
  117. package/dist/modules/staff/frontend/[orgSlug]/portal/support/new/page.meta.js.map +7 -0
  118. package/dist/modules/staff/frontend/[orgSlug]/portal/support/page.js +190 -0
  119. package/dist/modules/staff/frontend/[orgSlug]/portal/support/page.js.map +7 -0
  120. package/dist/modules/staff/frontend/[orgSlug]/portal/support/page.meta.js +19 -0
  121. package/dist/modules/staff/frontend/[orgSlug]/portal/support/page.meta.js.map +7 -0
  122. package/dist/modules/staff/index.js +1 -1
  123. package/dist/modules/staff/index.js.map +2 -2
  124. package/dist/modules/staff/lib/crud.js +2 -0
  125. package/dist/modules/staff/lib/crud.js.map +2 -2
  126. package/dist/modules/staff/lib/support/attachments.js +36 -0
  127. package/dist/modules/staff/lib/support/attachments.js.map +7 -0
  128. package/dist/modules/staff/lib/support/dictionaries.js +87 -0
  129. package/dist/modules/staff/lib/support/dictionaries.js.map +7 -0
  130. package/dist/modules/staff/lib/support/notifications.js +111 -0
  131. package/dist/modules/staff/lib/support/notifications.js.map +7 -0
  132. package/dist/modules/staff/lib/support/portalAttachments.js +100 -0
  133. package/dist/modules/staff/lib/support/portalAttachments.js.map +7 -0
  134. package/dist/modules/staff/lib/support/portalBroadcast.js +117 -0
  135. package/dist/modules/staff/lib/support/portalBroadcast.js.map +7 -0
  136. package/dist/modules/staff/lib/support/portalRoute.js +99 -0
  137. package/dist/modules/staff/lib/support/portalRoute.js.map +7 -0
  138. package/dist/modules/staff/lib/support/portalStatus.js +12 -0
  139. package/dist/modules/staff/lib/support/portalStatus.js.map +7 -0
  140. package/dist/modules/staff/lib/support/portalTickets.js +148 -0
  141. package/dist/modules/staff/lib/support/portalTickets.js.map +7 -0
  142. package/dist/modules/staff/lib/support/routing.js +81 -0
  143. package/dist/modules/staff/lib/support/routing.js.map +7 -0
  144. package/dist/modules/staff/lib/support/routingRuleDraft.js +25 -0
  145. package/dist/modules/staff/lib/support/routingRuleDraft.js.map +7 -0
  146. package/dist/modules/staff/lib/support/seedDefaults.js +114 -0
  147. package/dist/modules/staff/lib/support/seedDefaults.js.map +7 -0
  148. package/dist/modules/staff/lib/support/supportActivity.js +26 -0
  149. package/dist/modules/staff/lib/support/supportActivity.js.map +7 -0
  150. package/dist/modules/staff/lib/support-ui/PortalSupportNotLinked.js +23 -0
  151. package/dist/modules/staff/lib/support-ui/PortalSupportNotLinked.js.map +7 -0
  152. package/dist/modules/staff/lib/support-ui/portalAttachmentValidation.js +112 -0
  153. package/dist/modules/staff/lib/support-ui/portalAttachmentValidation.js.map +7 -0
  154. package/dist/modules/staff/lib/support-ui/portalPendingUploads.js +45 -0
  155. package/dist/modules/staff/lib/support-ui/portalPendingUploads.js.map +7 -0
  156. package/dist/modules/staff/lib/support-ui/portalSupport.js +51 -0
  157. package/dist/modules/staff/lib/support-ui/portalSupport.js.map +7 -0
  158. package/dist/modules/staff/lib/time-tracking/access.js +3 -39
  159. package/dist/modules/staff/lib/time-tracking/access.js.map +2 -2
  160. package/dist/modules/staff/lib/time-tracking/listProjectAccess.js +54 -0
  161. package/dist/modules/staff/lib/time-tracking/listProjectAccess.js.map +7 -0
  162. package/dist/modules/staff/lib/time-tracking/portalRecipients.js +92 -2
  163. package/dist/modules/staff/lib/time-tracking/portalRecipients.js.map +2 -2
  164. package/dist/modules/staff/lib/time-tracking/projectAssignment.js +112 -0
  165. package/dist/modules/staff/lib/time-tracking/projectAssignment.js.map +7 -0
  166. package/dist/modules/staff/lib/time-tracking/settingKeys.js +14 -0
  167. package/dist/modules/staff/lib/time-tracking/settingKeys.js.map +2 -2
  168. package/dist/modules/staff/lib/time-tracking/settings.js +28 -1
  169. package/dist/modules/staff/lib/time-tracking/settings.js.map +2 -2
  170. package/dist/modules/staff/lib/time-tracking/taskAssignmentRoute.js +143 -0
  171. package/dist/modules/staff/lib/time-tracking/taskAssignmentRoute.js.map +7 -0
  172. package/dist/modules/staff/lib/time-tracking/workRecipients.js +92 -0
  173. package/dist/modules/staff/lib/time-tracking/workRecipients.js.map +7 -0
  174. package/dist/modules/staff/lib/time-tracking-ui/BoardFilterChips.js +32 -3
  175. package/dist/modules/staff/lib/time-tracking-ui/BoardFilterChips.js.map +2 -2
  176. package/dist/modules/staff/lib/time-tracking-ui/KanbanBoard.js +29 -0
  177. package/dist/modules/staff/lib/time-tracking-ui/KanbanBoard.js.map +2 -2
  178. package/dist/modules/staff/lib/time-tracking-ui/KanbanCard.js +4 -0
  179. package/dist/modules/staff/lib/time-tracking-ui/KanbanCard.js.map +2 -2
  180. package/dist/modules/staff/lib/time-tracking-ui/ProjectFormSections.js +141 -1
  181. package/dist/modules/staff/lib/time-tracking-ui/ProjectFormSections.js.map +2 -2
  182. package/dist/modules/staff/lib/time-tracking-ui/SupportAttachmentsSection.js +153 -0
  183. package/dist/modules/staff/lib/time-tracking-ui/SupportAttachmentsSection.js.map +7 -0
  184. package/dist/modules/staff/lib/time-tracking-ui/SupportRequestPanel.js +67 -0
  185. package/dist/modules/staff/lib/time-tracking-ui/SupportRequestPanel.js.map +7 -0
  186. package/dist/modules/staff/lib/time-tracking-ui/TaskBoardScreen.js +3 -1
  187. package/dist/modules/staff/lib/time-tracking-ui/TaskBoardScreen.js.map +2 -2
  188. package/dist/modules/staff/lib/time-tracking-ui/TaskCommentThread.js +80 -17
  189. package/dist/modules/staff/lib/time-tracking-ui/TaskCommentThread.js.map +2 -2
  190. package/dist/modules/staff/lib/time-tracking-ui/TaskDrawer.js +230 -41
  191. package/dist/modules/staff/lib/time-tracking-ui/TaskDrawer.js.map +2 -2
  192. package/dist/modules/staff/lib/time-tracking-ui/boardDirectory.js +21 -1
  193. package/dist/modules/staff/lib/time-tracking-ui/boardDirectory.js.map +2 -2
  194. package/dist/modules/staff/lib/time-tracking-ui/boardFilters.js +16 -4
  195. package/dist/modules/staff/lib/time-tracking-ui/boardFilters.js.map +2 -2
  196. package/dist/modules/staff/lib/time-tracking-ui/kanbanBoardData.js +3 -1
  197. package/dist/modules/staff/lib/time-tracking-ui/kanbanBoardData.js.map +2 -2
  198. package/dist/modules/staff/lib/time-tracking-ui/projectDefaultAssignee.js +68 -0
  199. package/dist/modules/staff/lib/time-tracking-ui/projectDefaultAssignee.js.map +7 -0
  200. package/dist/modules/staff/lib/time-tracking-ui/settingsTabs.js +7 -0
  201. package/dist/modules/staff/lib/time-tracking-ui/settingsTabs.js.map +7 -0
  202. package/dist/modules/staff/lib/time-tracking-ui/supportTicketData.js +50 -0
  203. package/dist/modules/staff/lib/time-tracking-ui/supportTicketData.js.map +7 -0
  204. package/dist/modules/staff/lib/time-tracking-ui/taskDrawerData.js +13 -1
  205. package/dist/modules/staff/lib/time-tracking-ui/taskDrawerData.js.map +2 -2
  206. package/dist/modules/staff/lib/time-tracking-ui/timeTrackingSettingsForm.js +36 -2
  207. package/dist/modules/staff/lib/time-tracking-ui/timeTrackingSettingsForm.js.map +2 -2
  208. package/dist/modules/staff/migrations/Migration20261009180543_staff.js +42 -0
  209. package/dist/modules/staff/migrations/Migration20261009180543_staff.js.map +7 -0
  210. package/dist/modules/staff/notifications.js +92 -0
  211. package/dist/modules/staff/notifications.js.map +2 -2
  212. package/dist/modules/staff/setup.js +6 -2
  213. package/dist/modules/staff/setup.js.map +2 -2
  214. package/dist/modules/staff/subscribers/support-portal-comment-created.js +26 -0
  215. package/dist/modules/staff/subscribers/support-portal-comment-created.js.map +7 -0
  216. package/dist/modules/staff/subscribers/support-portal-comment-deleted.js +26 -0
  217. package/dist/modules/staff/subscribers/support-portal-comment-deleted.js.map +7 -0
  218. package/dist/modules/staff/subscribers/support-portal-comment-updated.js +26 -0
  219. package/dist/modules/staff/subscribers/support-portal-comment-updated.js.map +7 -0
  220. package/dist/modules/staff/subscribers/support-portal-status-column-updated.js +24 -0
  221. package/dist/modules/staff/subscribers/support-portal-status-column-updated.js.map +7 -0
  222. package/dist/modules/staff/subscribers/support-portal-task-status-changed.js +25 -0
  223. package/dist/modules/staff/subscribers/support-portal-task-status-changed.js.map +7 -0
  224. package/dist/modules/staff/subscribers/support-portal-task-updated.js +25 -0
  225. package/dist/modules/staff/subscribers/support-portal-task-updated.js.map +7 -0
  226. package/dist/modules/staff/subscribers/support-ticket-created-notification.js +25 -0
  227. package/dist/modules/staff/subscribers/support-ticket-created-notification.js.map +7 -0
  228. package/dist/modules/staff/subscribers/support-ticket-customer-replied-notification.js +25 -0
  229. package/dist/modules/staff/subscribers/support-ticket-customer-replied-notification.js.map +7 -0
  230. package/dist/modules/staff/subscribers/support-ticket-unnotified-clear.js +26 -0
  231. package/dist/modules/staff/subscribers/support-ticket-unnotified-clear.js.map +7 -0
  232. package/dist/modules/staff/subscribers/time-task-assignee-notification.js +83 -0
  233. package/dist/modules/staff/subscribers/time-task-assignee-notification.js.map +7 -0
  234. package/dist/modules/staff/widgets/injection/portal-support-tickets/widget.client.js +112 -0
  235. package/dist/modules/staff/widgets/injection/portal-support-tickets/widget.client.js.map +7 -0
  236. package/dist/modules/staff/widgets/injection/portal-support-tickets/widget.js +17 -0
  237. package/dist/modules/staff/widgets/injection/portal-support-tickets/widget.js.map +7 -0
  238. package/dist/modules/staff/widgets/injection/time-tracking-settings-support/widget.client.js +870 -0
  239. package/dist/modules/staff/widgets/injection/time-tracking-settings-support/widget.client.js.map +7 -0
  240. package/dist/modules/staff/widgets/injection/time-tracking-settings-support/widget.js +17 -0
  241. package/dist/modules/staff/widgets/injection/time-tracking-settings-support/widget.js.map +7 -0
  242. package/dist/modules/staff/widgets/injection-table.js +16 -1
  243. package/dist/modules/staff/widgets/injection-table.js.map +2 -2
  244. package/dist/modules/wms/components/backend/WmsLocationDetailPage.js +4 -18
  245. package/dist/modules/wms/components/backend/WmsLocationDetailPage.js.map +2 -2
  246. package/dist/modules/wms/components/backend/WmsLotDetailPage.js +4 -18
  247. package/dist/modules/wms/components/backend/WmsLotDetailPage.js.map +2 -2
  248. package/dist/modules/wms/components/backend/WmsSkuDetailPage.js +4 -18
  249. package/dist/modules/wms/components/backend/WmsSkuDetailPage.js.map +2 -2
  250. package/dist/modules/wms/lib/inventoryCsvExport.js +19 -0
  251. package/dist/modules/wms/lib/inventoryCsvExport.js.map +7 -0
  252. package/generated/entities/staff_support_routing_rule/index.ts +9 -0
  253. package/generated/entities/staff_support_ticket/index.ts +17 -0
  254. package/generated/entities/staff_time_project/index.ts +1 -0
  255. package/generated/entities/staff_time_project_member/index.ts +1 -0
  256. package/generated/entities/staff_time_task/index.ts +2 -0
  257. package/generated/entities/staff_time_task_comment/index.ts +4 -0
  258. package/generated/entities.ids.generated.ts +2 -0
  259. package/generated/entity-fields-registry.ts +38 -0
  260. package/package.json +8 -8
  261. package/src/helpers/integration/auth.ts +1 -1
  262. package/src/helpers/integration/communicationChannelsFixtures.ts +60 -2
  263. package/src/modules/catalog/api/bulk-delete/route.ts +12 -2
  264. package/src/modules/communication_channels/api/post/test-seed/route.ts +29 -12
  265. package/src/modules/communication_channels/lib/test-seed.ts +49 -10
  266. package/src/modules/messages/commands/messages.ts +8 -10
  267. package/src/modules/portal/frontend/[orgSlug]/portal/dashboard/page.tsx +2 -2
  268. package/src/modules/staff/AGENTS.md +22 -6
  269. package/src/modules/staff/api/portal/support-tickets/[id]/attachments/route.ts +327 -0
  270. package/src/modules/staff/api/portal/support-tickets/[id]/messages/route.ts +94 -0
  271. package/src/modules/staff/api/portal/support-tickets/[id]/route.ts +160 -0
  272. package/src/modules/staff/api/portal/support-tickets/options/route.ts +60 -0
  273. package/src/modules/staff/api/portal/support-tickets/route.ts +253 -0
  274. package/src/modules/staff/api/timesheets/settings/route.ts +7 -1
  275. package/src/modules/staff/api/timesheets/support-routing-rules/fallback-project/route.ts +131 -0
  276. package/src/modules/staff/api/timesheets/support-routing-rules/preview/route.ts +248 -0
  277. package/src/modules/staff/api/timesheets/support-routing-rules/route.ts +150 -0
  278. package/src/modules/staff/api/timesheets/support-tickets/options/route.ts +181 -0
  279. package/src/modules/staff/api/timesheets/tasks/[id]/claim/route.ts +31 -0
  280. package/src/modules/staff/api/timesheets/tasks/[id]/comments/route.ts +25 -7
  281. package/src/modules/staff/api/timesheets/tasks/[id]/release/route.ts +30 -0
  282. package/src/modules/staff/api/timesheets/tasks/route.ts +119 -67
  283. package/src/modules/staff/api/timesheets/time-projects/[id]/employees/route.ts +3 -0
  284. package/src/modules/staff/api/timesheets/time-projects/route.ts +3 -0
  285. package/src/modules/staff/backend/staff/time-tracking/projects/[id]/edit/page.tsx +1 -0
  286. package/src/modules/staff/backend/staff/time-tracking/projects/[id]/page.tsx +67 -2
  287. package/src/modules/staff/backend/staff/time-tracking/projects/projectFormConfig.ts +22 -3
  288. package/src/modules/staff/backend/staff/time-tracking/settings/page.tsx +405 -308
  289. package/src/modules/staff/backend/staff/time-tracking/support/page.meta.ts +14 -0
  290. package/src/modules/staff/backend/staff/time-tracking/support/page.tsx +428 -0
  291. package/src/modules/staff/cli.ts +28 -0
  292. package/src/modules/staff/commands/index.ts +3 -0
  293. package/src/modules/staff/commands/shared.ts +22 -1
  294. package/src/modules/staff/commands/support-routing-rules.ts +595 -0
  295. package/src/modules/staff/commands/support-tickets.ts +580 -0
  296. package/src/modules/staff/commands/timesheets-projects.ts +84 -13
  297. package/src/modules/staff/commands/timesheets-task-assignment.ts +319 -0
  298. package/src/modules/staff/commands/timesheets-task-comments.ts +70 -15
  299. package/src/modules/staff/commands/timesheets-tasks.ts +106 -15
  300. package/src/modules/staff/data/enrichers.ts +203 -0
  301. package/src/modules/staff/data/entities.ts +133 -2
  302. package/src/modules/staff/data/extensions.ts +9 -1
  303. package/src/modules/staff/data/validators.ts +36 -0
  304. package/src/modules/staff/encryption.ts +6 -0
  305. package/src/modules/staff/events.payloads.ts +87 -1
  306. package/src/modules/staff/events.ts +26 -0
  307. package/src/modules/staff/extension-points.ts +1 -0
  308. package/src/modules/staff/frontend/[orgSlug]/portal/support/[id]/page.meta.ts +10 -0
  309. package/src/modules/staff/frontend/[orgSlug]/portal/support/[id]/page.tsx +553 -0
  310. package/src/modules/staff/frontend/[orgSlug]/portal/support/new/page.meta.ts +10 -0
  311. package/src/modules/staff/frontend/[orgSlug]/portal/support/new/page.tsx +346 -0
  312. package/src/modules/staff/frontend/[orgSlug]/portal/support/page.meta.ts +17 -0
  313. package/src/modules/staff/frontend/[orgSlug]/portal/support/page.tsx +253 -0
  314. package/src/modules/staff/i18n/de.json +291 -0
  315. package/src/modules/staff/i18n/en.json +291 -0
  316. package/src/modules/staff/i18n/es.json +291 -0
  317. package/src/modules/staff/i18n/ko.json +291 -0
  318. package/src/modules/staff/i18n/pl.json +291 -0
  319. package/src/modules/staff/index.ts +1 -1
  320. package/src/modules/staff/lib/crud.ts +2 -1
  321. package/src/modules/staff/lib/support/attachments.ts +43 -0
  322. package/src/modules/staff/lib/support/dictionaries.ts +120 -0
  323. package/src/modules/staff/lib/support/notifications.ts +138 -0
  324. package/src/modules/staff/lib/support/portalAttachments.ts +140 -0
  325. package/src/modules/staff/lib/support/portalBroadcast.ts +161 -0
  326. package/src/modules/staff/lib/support/portalRoute.ts +108 -0
  327. package/src/modules/staff/lib/support/portalStatus.ts +15 -0
  328. package/src/modules/staff/lib/support/portalTickets.ts +233 -0
  329. package/src/modules/staff/lib/support/routing.ts +117 -0
  330. package/src/modules/staff/lib/support/routingRuleDraft.ts +45 -0
  331. package/src/modules/staff/lib/support/seedDefaults.ts +151 -0
  332. package/src/modules/staff/lib/support/supportActivity.ts +39 -0
  333. package/src/modules/staff/lib/support-ui/PortalSupportNotLinked.tsx +20 -0
  334. package/src/modules/staff/lib/support-ui/portalAttachmentValidation.ts +125 -0
  335. package/src/modules/staff/lib/support-ui/portalPendingUploads.ts +49 -0
  336. package/src/modules/staff/lib/support-ui/portalSupport.ts +116 -0
  337. package/src/modules/staff/lib/time-tracking/access.ts +2 -62
  338. package/src/modules/staff/lib/time-tracking/listProjectAccess.ts +78 -0
  339. package/src/modules/staff/lib/time-tracking/portalRecipients.ts +173 -5
  340. package/src/modules/staff/lib/time-tracking/projectAssignment.ts +172 -0
  341. package/src/modules/staff/lib/time-tracking/settingKeys.ts +11 -0
  342. package/src/modules/staff/lib/time-tracking/settings.ts +46 -1
  343. package/src/modules/staff/lib/time-tracking/taskAssignmentRoute.ts +165 -0
  344. package/src/modules/staff/lib/time-tracking/workRecipients.ts +183 -0
  345. package/src/modules/staff/lib/time-tracking-ui/BoardFilterChips.tsx +36 -1
  346. package/src/modules/staff/lib/time-tracking-ui/KanbanBoard.tsx +30 -0
  347. package/src/modules/staff/lib/time-tracking-ui/KanbanCard.tsx +9 -0
  348. package/src/modules/staff/lib/time-tracking-ui/ProjectFormSections.tsx +185 -6
  349. package/src/modules/staff/lib/time-tracking-ui/SupportAttachmentsSection.tsx +188 -0
  350. package/src/modules/staff/lib/time-tracking-ui/SupportRequestPanel.tsx +102 -0
  351. package/src/modules/staff/lib/time-tracking-ui/TaskBoardScreen.tsx +2 -0
  352. package/src/modules/staff/lib/time-tracking-ui/TaskCommentThread.tsx +105 -22
  353. package/src/modules/staff/lib/time-tracking-ui/TaskDrawer.tsx +278 -43
  354. package/src/modules/staff/lib/time-tracking-ui/boardDirectory.ts +21 -0
  355. package/src/modules/staff/lib/time-tracking-ui/boardFilters.ts +16 -2
  356. package/src/modules/staff/lib/time-tracking-ui/kanbanBoardData.ts +4 -0
  357. package/src/modules/staff/lib/time-tracking-ui/projectDefaultAssignee.ts +117 -0
  358. package/src/modules/staff/lib/time-tracking-ui/settingsTabs.ts +17 -0
  359. package/src/modules/staff/lib/time-tracking-ui/supportTicketData.ts +71 -0
  360. package/src/modules/staff/lib/time-tracking-ui/taskDrawerData.ts +15 -0
  361. package/src/modules/staff/lib/time-tracking-ui/timeTrackingSettingsForm.ts +48 -1
  362. package/src/modules/staff/migrations/.snapshot-open-mercato.json +714 -0
  363. package/src/modules/staff/migrations/Migration20261009180543_staff.ts +50 -0
  364. package/src/modules/staff/notifications.ts +92 -0
  365. package/src/modules/staff/setup.ts +6 -2
  366. package/src/modules/staff/subscribers/support-portal-comment-created.ts +35 -0
  367. package/src/modules/staff/subscribers/support-portal-comment-deleted.ts +35 -0
  368. package/src/modules/staff/subscribers/support-portal-comment-updated.ts +35 -0
  369. package/src/modules/staff/subscribers/support-portal-status-column-updated.ts +36 -0
  370. package/src/modules/staff/subscribers/support-portal-task-status-changed.ts +34 -0
  371. package/src/modules/staff/subscribers/support-portal-task-updated.ts +34 -0
  372. package/src/modules/staff/subscribers/support-ticket-created-notification.ts +27 -0
  373. package/src/modules/staff/subscribers/support-ticket-customer-replied-notification.ts +27 -0
  374. package/src/modules/staff/subscribers/support-ticket-unnotified-clear.ts +29 -0
  375. package/src/modules/staff/subscribers/time-task-assignee-notification.ts +102 -0
  376. package/src/modules/staff/widgets/injection/portal-support-tickets/widget.client.tsx +151 -0
  377. package/src/modules/staff/widgets/injection/portal-support-tickets/widget.ts +16 -0
  378. package/src/modules/staff/widgets/injection/time-tracking-settings-support/widget.client.tsx +1058 -0
  379. package/src/modules/staff/widgets/injection/time-tracking-settings-support/widget.ts +17 -0
  380. package/src/modules/staff/widgets/injection-table.ts +15 -0
  381. package/src/modules/wms/components/backend/WmsLocationDetailPage.tsx +4 -20
  382. package/src/modules/wms/components/backend/WmsLotDetailPage.tsx +4 -20
  383. package/src/modules/wms/components/backend/WmsSkuDetailPage.tsx +4 -19
  384. package/src/modules/wms/lib/inventoryCsvExport.ts +18 -0
@@ -0,0 +1,270 @@
1
+ import { z } from "zod";
2
+ import { registerCommand } from "@open-mercato/shared/lib/commands";
3
+ import { CrudHttpError } from "@open-mercato/shared/lib/crud/errors";
4
+ import { resolveTranslations } from "@open-mercato/shared/lib/i18n/server";
5
+ import { emitCrudSideEffects, emitCrudUndoSideEffects } from "@open-mercato/shared/lib/commands/helpers";
6
+ import { findOneWithDecryption } from "@open-mercato/shared/lib/encryption/find";
7
+ import { StaffTeamMember, StaffTimeTask, StaffTimeTaskStatus } from "../data/entities.js";
8
+ import {
9
+ isActiveProjectAssigner,
10
+ resolveAssignmentGraceDaysFromContainer
11
+ } from "../lib/time-tracking/projectAssignment.js";
12
+ import {
13
+ TASK_LIST_CACHE_RESOURCE,
14
+ emitTaskAssigneeChanged,
15
+ taskCrudEvents,
16
+ taskCrudIndexer
17
+ } from "./timesheets-tasks.js";
18
+ import {
19
+ applyScopeToWhere,
20
+ commandActorScope,
21
+ ensureOrganizationScope,
22
+ ensureTenantScope,
23
+ extractUndoPayload
24
+ } from "./shared.js";
25
+ const staffTimeTaskAssignmentCommandIds = {
26
+ claim: "staff.timesheets.tasks.claim",
27
+ release: "staff.timesheets.tasks.release"
28
+ };
29
+ const TASK_ALREADY_CLAIMED_CODE = "task_already_claimed";
30
+ const TASK_NOT_CLAIMABLE_CODE = "task_not_claimable";
31
+ const TASK_NOT_ASSIGNER_CODE = "task_not_assigner";
32
+ const TASK_NOT_ASSIGNEE_CODE = "task_not_assignee";
33
+ const TASK_ASSIGNMENT_CHANGED_CODE = "task_assignment_changed";
34
+ const assignmentInputSchema = z.object({ id: z.string().uuid() });
35
+ function notFound(translate) {
36
+ return new CrudHttpError(404, {
37
+ error: translate("staff.time_tracking.tasks.errors.notFound", "Task not found or not accessible.")
38
+ });
39
+ }
40
+ function notClaimable(translate) {
41
+ return new CrudHttpError(422, {
42
+ code: TASK_NOT_CLAIMABLE_CODE,
43
+ error: translate("staff.timesheets.errors.taskNotClaimable", "A finished task cannot be claimed.")
44
+ });
45
+ }
46
+ function alreadyClaimed(translate) {
47
+ return new CrudHttpError(409, {
48
+ code: TASK_ALREADY_CLAIMED_CODE,
49
+ error: translate("staff.timesheets.errors.taskAlreadyClaimed", "Someone else already took this task.")
50
+ });
51
+ }
52
+ async function loadDoneStatusIds(em, task) {
53
+ const statuses = await em.find(
54
+ StaffTimeTaskStatus,
55
+ { timeProjectId: task.timeProjectId, tenantId: task.tenantId, organizationId: task.organizationId, isDone: true },
56
+ { fields: ["id"] }
57
+ );
58
+ return statuses.map((status) => status.id);
59
+ }
60
+ async function resolveActorStaffMemberId(em, ctx, task) {
61
+ const userId = ctx.auth?.sub ?? null;
62
+ if (!userId) return null;
63
+ const member = await findOneWithDecryption(
64
+ em,
65
+ StaffTeamMember,
66
+ { userId, tenantId: task.tenantId, organizationId: task.organizationId, deletedAt: null },
67
+ void 0,
68
+ { tenantId: task.tenantId, organizationId: task.organizationId }
69
+ );
70
+ return member?.id ?? null;
71
+ }
72
+ async function loadScopedTask(em, ctx, id, translate) {
73
+ const task = await em.findOne(
74
+ StaffTimeTask,
75
+ applyScopeToWhere({ id, deletedAt: null }, commandActorScope(ctx))
76
+ );
77
+ if (!task) throw notFound(translate);
78
+ ensureTenantScope(ctx, task.tenantId);
79
+ ensureOrganizationScope(ctx, task.organizationId);
80
+ return task;
81
+ }
82
+ async function emitTaskSideEffects(ctx, task, undo = false) {
83
+ const params = {
84
+ dataEngine: ctx.container.resolve("dataEngine"),
85
+ action: "updated",
86
+ entity: task,
87
+ identifiers: { id: task.id, organizationId: task.organizationId, tenantId: task.tenantId },
88
+ events: taskCrudEvents,
89
+ indexer: taskCrudIndexer
90
+ };
91
+ if (undo) await emitCrudUndoSideEffects(params);
92
+ else await emitCrudSideEffects(params);
93
+ }
94
+ function captureWrittenState(em, task, assigneeStaffMemberId, updatedAt) {
95
+ em.clear();
96
+ task.assigneeStaffMemberId = assigneeStaffMemberId;
97
+ task.updatedAt = updatedAt;
98
+ return task;
99
+ }
100
+ function toResult(task) {
101
+ return {
102
+ taskId: task.id,
103
+ tenantId: task.tenantId,
104
+ organizationId: task.organizationId,
105
+ timeProjectId: task.timeProjectId,
106
+ assigneeStaffMemberId: task.assigneeStaffMemberId ?? null,
107
+ updatedAt: task.updatedAt ? task.updatedAt.toISOString() : null
108
+ };
109
+ }
110
+ async function restoreAssignee(ctx, payload) {
111
+ const em = ctx.container.resolve("em").fork();
112
+ const where = {
113
+ id: payload.taskId,
114
+ tenantId: payload.tenantId,
115
+ organizationId: payload.organizationId,
116
+ assigneeStaffMemberId: payload.nextAssigneeStaffMemberId,
117
+ deletedAt: null
118
+ };
119
+ const current = await em.findOne(StaffTimeTask, where);
120
+ if (!current) return;
121
+ const now = /* @__PURE__ */ new Date();
122
+ const affected = await em.nativeUpdate(
123
+ StaffTimeTask,
124
+ where,
125
+ { assigneeStaffMemberId: payload.previousAssigneeStaffMemberId, updatedAt: now }
126
+ );
127
+ if (affected === 0) return;
128
+ const task = captureWrittenState(em, current, payload.previousAssigneeStaffMemberId, now);
129
+ await emitTaskSideEffects(ctx, task, true);
130
+ emitTaskAssigneeChanged(task, payload.nextAssigneeStaffMemberId, "manual", ctx.auth?.sub ?? null);
131
+ }
132
+ const claimTaskCommand = {
133
+ id: staffTimeTaskAssignmentCommandIds.claim,
134
+ async execute(rawInput, ctx) {
135
+ const parsed = assignmentInputSchema.parse(rawInput);
136
+ const { translate } = await resolveTranslations();
137
+ const em = ctx.container.resolve("em").fork();
138
+ const task = await loadScopedTask(em, ctx, parsed.id, translate);
139
+ const scope = { tenantId: task.tenantId, organizationId: task.organizationId };
140
+ const staffMemberId = await resolveActorStaffMemberId(em, ctx, task);
141
+ const graceDays = await resolveAssignmentGraceDaysFromContainer(ctx.container, task.tenantId);
142
+ const isAssigner = staffMemberId ? await isActiveProjectAssigner(em, scope, task.timeProjectId, staffMemberId, { graceDays }) : false;
143
+ if (!staffMemberId || !isAssigner) {
144
+ throw new CrudHttpError(403, {
145
+ code: TASK_NOT_ASSIGNER_CODE,
146
+ error: translate("staff.timesheets.errors.notAssigner", "Only active assigners of this project can claim tasks.")
147
+ });
148
+ }
149
+ const doneStatusIds = await loadDoneStatusIds(em, task);
150
+ if (doneStatusIds.includes(task.taskStatusId)) throw notClaimable(translate);
151
+ if (task.assigneeStaffMemberId) throw alreadyClaimed(translate);
152
+ const now = /* @__PURE__ */ new Date();
153
+ const affected = await em.nativeUpdate(
154
+ StaffTimeTask,
155
+ {
156
+ id: task.id,
157
+ tenantId: task.tenantId,
158
+ organizationId: task.organizationId,
159
+ assigneeStaffMemberId: null,
160
+ deletedAt: null,
161
+ ...doneStatusIds.length > 0 ? { taskStatusId: { $nin: doneStatusIds } } : {}
162
+ },
163
+ { assigneeStaffMemberId: staffMemberId, updatedAt: now }
164
+ );
165
+ if (affected === 0) {
166
+ em.clear();
167
+ const current = await em.findOne(StaffTimeTask, { id: task.id, tenantId: task.tenantId, organizationId: task.organizationId, deletedAt: null });
168
+ if (!current) throw notFound(translate);
169
+ if (!current.assigneeStaffMemberId && doneStatusIds.includes(current.taskStatusId)) throw notClaimable(translate);
170
+ throw alreadyClaimed(translate);
171
+ }
172
+ const claimed = captureWrittenState(em, task, staffMemberId, now);
173
+ await emitTaskSideEffects(ctx, claimed);
174
+ emitTaskAssigneeChanged(claimed, null, "claim", ctx.auth?.sub ?? null);
175
+ return toResult(claimed);
176
+ },
177
+ buildLog: async ({ result }) => {
178
+ const { translate } = await resolveTranslations();
179
+ return {
180
+ actionLabel: translate("staff.audit.timesheets.tasks.claim", "Claim task"),
181
+ resourceKind: "staff.timesheets.task",
182
+ resourceId: result.taskId,
183
+ tenantId: result.tenantId,
184
+ organizationId: result.organizationId,
185
+ context: { cacheAliases: [TASK_LIST_CACHE_RESOURCE] },
186
+ payload: {
187
+ undo: {
188
+ taskId: result.taskId,
189
+ tenantId: result.tenantId,
190
+ organizationId: result.organizationId,
191
+ previousAssigneeStaffMemberId: null,
192
+ nextAssigneeStaffMemberId: result.assigneeStaffMemberId
193
+ }
194
+ }
195
+ };
196
+ },
197
+ undo: async ({ logEntry, ctx }) => {
198
+ const payload = extractUndoPayload(logEntry);
199
+ if (!payload?.taskId) return;
200
+ await restoreAssignee(ctx, payload);
201
+ }
202
+ };
203
+ const releaseTaskCommand = {
204
+ id: staffTimeTaskAssignmentCommandIds.release,
205
+ async execute(rawInput, ctx) {
206
+ const parsed = assignmentInputSchema.parse(rawInput);
207
+ const { translate } = await resolveTranslations();
208
+ const em = ctx.container.resolve("em").fork();
209
+ const task = await loadScopedTask(em, ctx, parsed.id, translate);
210
+ const staffMemberId = await resolveActorStaffMemberId(em, ctx, task);
211
+ if (!staffMemberId || task.assigneeStaffMemberId !== staffMemberId) {
212
+ throw new CrudHttpError(403, {
213
+ code: TASK_NOT_ASSIGNEE_CODE,
214
+ error: translate("staff.timesheets.errors.notAssignee", "Only the current assignee can release this task.")
215
+ });
216
+ }
217
+ const now = /* @__PURE__ */ new Date();
218
+ const affected = await em.nativeUpdate(
219
+ StaffTimeTask,
220
+ { id: task.id, tenantId: task.tenantId, organizationId: task.organizationId, assigneeStaffMemberId: staffMemberId, deletedAt: null },
221
+ { assigneeStaffMemberId: null, updatedAt: now }
222
+ );
223
+ if (affected === 0) {
224
+ throw new CrudHttpError(409, {
225
+ code: TASK_ASSIGNMENT_CHANGED_CODE,
226
+ error: translate("staff.timesheets.errors.taskChanged", "The task changed while you were releasing it. Refresh and try again.")
227
+ });
228
+ }
229
+ const released = captureWrittenState(em, task, null, now);
230
+ await emitTaskSideEffects(ctx, released);
231
+ emitTaskAssigneeChanged(released, staffMemberId, "release", ctx.auth?.sub ?? null);
232
+ return { ...toResult(released), previousAssigneeStaffMemberId: staffMemberId };
233
+ },
234
+ buildLog: async ({ result }) => {
235
+ const { translate } = await resolveTranslations();
236
+ return {
237
+ actionLabel: translate("staff.audit.timesheets.tasks.release", "Release task"),
238
+ resourceKind: "staff.timesheets.task",
239
+ resourceId: result.taskId,
240
+ tenantId: result.tenantId,
241
+ organizationId: result.organizationId,
242
+ context: { cacheAliases: [TASK_LIST_CACHE_RESOURCE] },
243
+ payload: {
244
+ undo: {
245
+ taskId: result.taskId,
246
+ tenantId: result.tenantId,
247
+ organizationId: result.organizationId,
248
+ previousAssigneeStaffMemberId: result.previousAssigneeStaffMemberId,
249
+ nextAssigneeStaffMemberId: null
250
+ }
251
+ }
252
+ };
253
+ },
254
+ undo: async ({ logEntry, ctx }) => {
255
+ const payload = extractUndoPayload(logEntry);
256
+ if (!payload?.taskId) return;
257
+ await restoreAssignee(ctx, payload);
258
+ }
259
+ };
260
+ registerCommand(claimTaskCommand);
261
+ registerCommand(releaseTaskCommand);
262
+ export {
263
+ TASK_ALREADY_CLAIMED_CODE,
264
+ TASK_ASSIGNMENT_CHANGED_CODE,
265
+ TASK_NOT_ASSIGNEE_CODE,
266
+ TASK_NOT_ASSIGNER_CODE,
267
+ TASK_NOT_CLAIMABLE_CODE,
268
+ staffTimeTaskAssignmentCommandIds
269
+ };
270
+ //# sourceMappingURL=timesheets-task-assignment.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../src/modules/staff/commands/timesheets-task-assignment.ts"],
4
+ "sourcesContent": ["import { z } from 'zod'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport type { CommandHandler, CommandRuntimeContext } from '@open-mercato/shared/lib/commands'\nimport { registerCommand } from '@open-mercato/shared/lib/commands'\nimport { CrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport { emitCrudSideEffects, emitCrudUndoSideEffects } from '@open-mercato/shared/lib/commands/helpers'\nimport type { DataEngine } from '@open-mercato/shared/lib/data/engine'\nimport { findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport { StaffTeamMember, StaffTimeTask, StaffTimeTaskStatus } from '../data/entities'\nimport {\n isActiveProjectAssigner,\n resolveAssignmentGraceDaysFromContainer,\n} from '../lib/time-tracking/projectAssignment'\nimport {\n TASK_LIST_CACHE_RESOURCE,\n emitTaskAssigneeChanged,\n taskCrudEvents,\n taskCrudIndexer,\n} from './timesheets-tasks'\nimport {\n applyScopeToWhere,\n commandActorScope,\n ensureOrganizationScope,\n ensureTenantScope,\n extractUndoPayload,\n} from './shared'\n\nexport const staffTimeTaskAssignmentCommandIds = {\n claim: 'staff.timesheets.tasks.claim',\n release: 'staff.timesheets.tasks.release',\n} as const\n\nexport const TASK_ALREADY_CLAIMED_CODE = 'task_already_claimed'\nexport const TASK_NOT_CLAIMABLE_CODE = 'task_not_claimable'\nexport const TASK_NOT_ASSIGNER_CODE = 'task_not_assigner'\nexport const TASK_NOT_ASSIGNEE_CODE = 'task_not_assignee'\nexport const TASK_ASSIGNMENT_CHANGED_CODE = 'task_assignment_changed'\n\nconst assignmentInputSchema = z.object({ id: z.string().uuid() })\n\nexport type StaffTimeTaskAssignmentInput = z.infer<typeof assignmentInputSchema>\n\nexport type StaffTimeTaskAssignmentResult = {\n taskId: string\n tenantId: string\n organizationId: string\n timeProjectId: string\n assigneeStaffMemberId: string | null\n updatedAt: string | null\n}\n\ntype AssignmentUndoPayload = {\n taskId: string\n tenantId: string\n organizationId: string\n previousAssigneeStaffMemberId: string | null\n nextAssigneeStaffMemberId: string | null\n}\n\ntype Translate = (key: string, fallback: string) => string\n\nfunction notFound(translate: Translate): CrudHttpError {\n return new CrudHttpError(404, {\n error: translate('staff.time_tracking.tasks.errors.notFound', 'Task not found or not accessible.'),\n })\n}\n\nfunction notClaimable(translate: Translate): CrudHttpError {\n return new CrudHttpError(422, {\n code: TASK_NOT_CLAIMABLE_CODE,\n error: translate('staff.timesheets.errors.taskNotClaimable', 'A finished task cannot be claimed.'),\n })\n}\n\nfunction alreadyClaimed(translate: Translate): CrudHttpError {\n return new CrudHttpError(409, {\n code: TASK_ALREADY_CLAIMED_CODE,\n error: translate('staff.timesheets.errors.taskAlreadyClaimed', 'Someone else already took this task.'),\n })\n}\n\nasync function loadDoneStatusIds(em: EntityManager, task: StaffTimeTask): Promise<string[]> {\n const statuses = await em.find(\n StaffTimeTaskStatus,\n { timeProjectId: task.timeProjectId, tenantId: task.tenantId, organizationId: task.organizationId, isDone: true },\n { fields: ['id'] },\n )\n return statuses.map((status) => status.id)\n}\n\nasync function resolveActorStaffMemberId(em: EntityManager, ctx: CommandRuntimeContext, task: StaffTimeTask): Promise<string | null> {\n const userId = ctx.auth?.sub ?? null\n if (!userId) return null\n const member = await findOneWithDecryption(\n em,\n StaffTeamMember,\n { userId, tenantId: task.tenantId, organizationId: task.organizationId, deletedAt: null },\n undefined,\n { tenantId: task.tenantId, organizationId: task.organizationId },\n )\n return member?.id ?? null\n}\n\nasync function loadScopedTask(em: EntityManager, ctx: CommandRuntimeContext, id: string, translate: Translate): Promise<StaffTimeTask> {\n const task = await em.findOne(\n StaffTimeTask,\n applyScopeToWhere<StaffTimeTask>({ id, deletedAt: null }, commandActorScope(ctx)),\n )\n if (!task) throw notFound(translate)\n ensureTenantScope(ctx, task.tenantId)\n ensureOrganizationScope(ctx, task.organizationId)\n return task\n}\n\nasync function emitTaskSideEffects(ctx: CommandRuntimeContext, task: StaffTimeTask, undo = false): Promise<void> {\n const params = {\n dataEngine: ctx.container.resolve('dataEngine') as DataEngine,\n action: 'updated' as const,\n entity: task,\n identifiers: { id: task.id, organizationId: task.organizationId, tenantId: task.tenantId },\n events: taskCrudEvents,\n indexer: taskCrudIndexer,\n }\n if (undo) await emitCrudUndoSideEffects(params)\n else await emitCrudSideEffects(params)\n}\n\n/**\n * The conditional UPDATE is the only moment this writer owns the row, so the outcome is\n * built from what it wrote rather than re-read: a later writer may already have replaced\n * the assignee, and reporting or undoing their value would be wrong.\n */\nfunction captureWrittenState(\n em: EntityManager,\n task: StaffTimeTask,\n assigneeStaffMemberId: string | null,\n updatedAt: Date,\n): StaffTimeTask {\n em.clear()\n task.assigneeStaffMemberId = assigneeStaffMemberId\n task.updatedAt = updatedAt\n return task\n}\n\nfunction toResult(task: StaffTimeTask): StaffTimeTaskAssignmentResult {\n return {\n taskId: task.id,\n tenantId: task.tenantId,\n organizationId: task.organizationId,\n timeProjectId: task.timeProjectId,\n assigneeStaffMemberId: task.assigneeStaffMemberId ?? null,\n updatedAt: task.updatedAt ? task.updatedAt.toISOString() : null,\n }\n}\n\nasync function restoreAssignee(\n ctx: CommandRuntimeContext,\n payload: AssignmentUndoPayload,\n): Promise<void> {\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const where = {\n id: payload.taskId,\n tenantId: payload.tenantId,\n organizationId: payload.organizationId,\n assigneeStaffMemberId: payload.nextAssigneeStaffMemberId,\n deletedAt: null,\n }\n const current = await em.findOne(StaffTimeTask, where)\n if (!current) return\n const now = new Date()\n const affected = await em.nativeUpdate(\n StaffTimeTask,\n where,\n { assigneeStaffMemberId: payload.previousAssigneeStaffMemberId, updatedAt: now },\n )\n if (affected === 0) return\n const task = captureWrittenState(em, current, payload.previousAssigneeStaffMemberId, now)\n await emitTaskSideEffects(ctx, task, true)\n emitTaskAssigneeChanged(task, payload.nextAssigneeStaffMemberId, 'manual', ctx.auth?.sub ?? null)\n}\n\nconst claimTaskCommand: CommandHandler<StaffTimeTaskAssignmentInput, StaffTimeTaskAssignmentResult> = {\n id: staffTimeTaskAssignmentCommandIds.claim,\n async execute(rawInput, ctx) {\n const parsed = assignmentInputSchema.parse(rawInput)\n const { translate } = await resolveTranslations()\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const task = await loadScopedTask(em, ctx, parsed.id, translate)\n const scope = { tenantId: task.tenantId, organizationId: task.organizationId }\n\n const staffMemberId = await resolveActorStaffMemberId(em, ctx, task)\n const graceDays = await resolveAssignmentGraceDaysFromContainer(ctx.container, task.tenantId)\n const isAssigner = staffMemberId\n ? await isActiveProjectAssigner(em, scope, task.timeProjectId, staffMemberId, { graceDays })\n : false\n if (!staffMemberId || !isAssigner) {\n throw new CrudHttpError(403, {\n code: TASK_NOT_ASSIGNER_CODE,\n error: translate('staff.timesheets.errors.notAssigner', 'Only active assigners of this project can claim tasks.'),\n })\n }\n\n const doneStatusIds = await loadDoneStatusIds(em, task)\n if (doneStatusIds.includes(task.taskStatusId)) throw notClaimable(translate)\n if (task.assigneeStaffMemberId) throw alreadyClaimed(translate)\n\n const now = new Date()\n const affected = await em.nativeUpdate(\n StaffTimeTask,\n {\n id: task.id,\n tenantId: task.tenantId,\n organizationId: task.organizationId,\n assigneeStaffMemberId: null,\n deletedAt: null,\n ...(doneStatusIds.length > 0 ? { taskStatusId: { $nin: doneStatusIds } } : {}),\n },\n { assigneeStaffMemberId: staffMemberId, updatedAt: now },\n )\n if (affected === 0) {\n em.clear()\n const current = await em.findOne(StaffTimeTask, { id: task.id, tenantId: task.tenantId, organizationId: task.organizationId, deletedAt: null })\n if (!current) throw notFound(translate)\n if (!current.assigneeStaffMemberId && doneStatusIds.includes(current.taskStatusId)) throw notClaimable(translate)\n throw alreadyClaimed(translate)\n }\n const claimed = captureWrittenState(em, task, staffMemberId, now)\n await emitTaskSideEffects(ctx, claimed)\n emitTaskAssigneeChanged(claimed, null, 'claim', ctx.auth?.sub ?? null)\n return toResult(claimed)\n },\n buildLog: async ({ result }) => {\n const { translate } = await resolveTranslations()\n return {\n actionLabel: translate('staff.audit.timesheets.tasks.claim', 'Claim task'),\n resourceKind: 'staff.timesheets.task',\n resourceId: result.taskId,\n tenantId: result.tenantId,\n organizationId: result.organizationId,\n context: { cacheAliases: [TASK_LIST_CACHE_RESOURCE] },\n payload: {\n undo: {\n taskId: result.taskId,\n tenantId: result.tenantId,\n organizationId: result.organizationId,\n previousAssigneeStaffMemberId: null,\n nextAssigneeStaffMemberId: result.assigneeStaffMemberId,\n } satisfies AssignmentUndoPayload,\n },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const payload = extractUndoPayload<AssignmentUndoPayload>(logEntry)\n if (!payload?.taskId) return\n await restoreAssignee(ctx, payload)\n },\n}\n\nconst releaseTaskCommand: CommandHandler<StaffTimeTaskAssignmentInput, StaffTimeTaskAssignmentResult & { previousAssigneeStaffMemberId: string | null }> = {\n id: staffTimeTaskAssignmentCommandIds.release,\n async execute(rawInput, ctx) {\n const parsed = assignmentInputSchema.parse(rawInput)\n const { translate } = await resolveTranslations()\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const task = await loadScopedTask(em, ctx, parsed.id, translate)\n const staffMemberId = await resolveActorStaffMemberId(em, ctx, task)\n if (!staffMemberId || task.assigneeStaffMemberId !== staffMemberId) {\n throw new CrudHttpError(403, {\n code: TASK_NOT_ASSIGNEE_CODE,\n error: translate('staff.timesheets.errors.notAssignee', 'Only the current assignee can release this task.'),\n })\n }\n const now = new Date()\n const affected = await em.nativeUpdate(\n StaffTimeTask,\n { id: task.id, tenantId: task.tenantId, organizationId: task.organizationId, assigneeStaffMemberId: staffMemberId, deletedAt: null },\n { assigneeStaffMemberId: null, updatedAt: now },\n )\n if (affected === 0) {\n throw new CrudHttpError(409, {\n code: TASK_ASSIGNMENT_CHANGED_CODE,\n error: translate('staff.timesheets.errors.taskChanged', 'The task changed while you were releasing it. Refresh and try again.'),\n })\n }\n const released = captureWrittenState(em, task, null, now)\n await emitTaskSideEffects(ctx, released)\n emitTaskAssigneeChanged(released, staffMemberId, 'release', ctx.auth?.sub ?? null)\n return { ...toResult(released), previousAssigneeStaffMemberId: staffMemberId }\n },\n buildLog: async ({ result }) => {\n const { translate } = await resolveTranslations()\n return {\n actionLabel: translate('staff.audit.timesheets.tasks.release', 'Release task'),\n resourceKind: 'staff.timesheets.task',\n resourceId: result.taskId,\n tenantId: result.tenantId,\n organizationId: result.organizationId,\n context: { cacheAliases: [TASK_LIST_CACHE_RESOURCE] },\n payload: {\n undo: {\n taskId: result.taskId,\n tenantId: result.tenantId,\n organizationId: result.organizationId,\n previousAssigneeStaffMemberId: result.previousAssigneeStaffMemberId,\n nextAssigneeStaffMemberId: null,\n } satisfies AssignmentUndoPayload,\n },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const payload = extractUndoPayload<AssignmentUndoPayload>(logEntry)\n if (!payload?.taskId) return\n await restoreAssignee(ctx, payload)\n },\n}\n\nregisterCommand(claimTaskCommand)\nregisterCommand(releaseTaskCommand)\n"],
5
+ "mappings": "AAAA,SAAS,SAAS;AAGlB,SAAS,uBAAuB;AAChC,SAAS,qBAAqB;AAC9B,SAAS,2BAA2B;AACpC,SAAS,qBAAqB,+BAA+B;AAE7D,SAAS,6BAA6B;AACtC,SAAS,iBAAiB,eAAe,2BAA2B;AACpE;AAAA,EACE;AAAA,EACA;AAAA,OACK;AACP;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAEA,MAAM,oCAAoC;AAAA,EAC/C,OAAO;AAAA,EACP,SAAS;AACX;AAEO,MAAM,4BAA4B;AAClC,MAAM,0BAA0B;AAChC,MAAM,yBAAyB;AAC/B,MAAM,yBAAyB;AAC/B,MAAM,+BAA+B;AAE5C,MAAM,wBAAwB,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;AAuBhE,SAAS,SAAS,WAAqC;AACrD,SAAO,IAAI,cAAc,KAAK;AAAA,IAC5B,OAAO,UAAU,6CAA6C,mCAAmC;AAAA,EACnG,CAAC;AACH;AAEA,SAAS,aAAa,WAAqC;AACzD,SAAO,IAAI,cAAc,KAAK;AAAA,IAC5B,MAAM;AAAA,IACN,OAAO,UAAU,4CAA4C,oCAAoC;AAAA,EACnG,CAAC;AACH;AAEA,SAAS,eAAe,WAAqC;AAC3D,SAAO,IAAI,cAAc,KAAK;AAAA,IAC5B,MAAM;AAAA,IACN,OAAO,UAAU,8CAA8C,sCAAsC;AAAA,EACvG,CAAC;AACH;AAEA,eAAe,kBAAkB,IAAmB,MAAwC;AAC1F,QAAM,WAAW,MAAM,GAAG;AAAA,IACxB;AAAA,IACA,EAAE,eAAe,KAAK,eAAe,UAAU,KAAK,UAAU,gBAAgB,KAAK,gBAAgB,QAAQ,KAAK;AAAA,IAChH,EAAE,QAAQ,CAAC,IAAI,EAAE;AAAA,EACnB;AACA,SAAO,SAAS,IAAI,CAAC,WAAW,OAAO,EAAE;AAC3C;AAEA,eAAe,0BAA0B,IAAmB,KAA4B,MAA6C;AACnI,QAAM,SAAS,IAAI,MAAM,OAAO;AAChC,MAAI,CAAC,OAAQ,QAAO;AACpB,QAAM,SAAS,MAAM;AAAA,IACnB;AAAA,IACA;AAAA,IACA,EAAE,QAAQ,UAAU,KAAK,UAAU,gBAAgB,KAAK,gBAAgB,WAAW,KAAK;AAAA,IACxF;AAAA,IACA,EAAE,UAAU,KAAK,UAAU,gBAAgB,KAAK,eAAe;AAAA,EACjE;AACA,SAAO,QAAQ,MAAM;AACvB;AAEA,eAAe,eAAe,IAAmB,KAA4B,IAAY,WAA8C;AACrI,QAAM,OAAO,MAAM,GAAG;AAAA,IACpB;AAAA,IACA,kBAAiC,EAAE,IAAI,WAAW,KAAK,GAAG,kBAAkB,GAAG,CAAC;AAAA,EAClF;AACA,MAAI,CAAC,KAAM,OAAM,SAAS,SAAS;AACnC,oBAAkB,KAAK,KAAK,QAAQ;AACpC,0BAAwB,KAAK,KAAK,cAAc;AAChD,SAAO;AACT;AAEA,eAAe,oBAAoB,KAA4B,MAAqB,OAAO,OAAsB;AAC/G,QAAM,SAAS;AAAA,IACb,YAAY,IAAI,UAAU,QAAQ,YAAY;AAAA,IAC9C,QAAQ;AAAA,IACR,QAAQ;AAAA,IACR,aAAa,EAAE,IAAI,KAAK,IAAI,gBAAgB,KAAK,gBAAgB,UAAU,KAAK,SAAS;AAAA,IACzF,QAAQ;AAAA,IACR,SAAS;AAAA,EACX;AACA,MAAI,KAAM,OAAM,wBAAwB,MAAM;AAAA,MACzC,OAAM,oBAAoB,MAAM;AACvC;AAOA,SAAS,oBACP,IACA,MACA,uBACA,WACe;AACf,KAAG,MAAM;AACT,OAAK,wBAAwB;AAC7B,OAAK,YAAY;AACjB,SAAO;AACT;AAEA,SAAS,SAAS,MAAoD;AACpE,SAAO;AAAA,IACL,QAAQ,KAAK;AAAA,IACb,UAAU,KAAK;AAAA,IACf,gBAAgB,KAAK;AAAA,IACrB,eAAe,KAAK;AAAA,IACpB,uBAAuB,KAAK,yBAAyB;AAAA,IACrD,WAAW,KAAK,YAAY,KAAK,UAAU,YAAY,IAAI;AAAA,EAC7D;AACF;AAEA,eAAe,gBACb,KACA,SACe;AACf,QAAM,KAAM,IAAI,UAAU,QAAQ,IAAI,EAAoB,KAAK;AAC/D,QAAM,QAAQ;AAAA,IACZ,IAAI,QAAQ;AAAA,IACZ,UAAU,QAAQ;AAAA,IAClB,gBAAgB,QAAQ;AAAA,IACxB,uBAAuB,QAAQ;AAAA,IAC/B,WAAW;AAAA,EACb;AACA,QAAM,UAAU,MAAM,GAAG,QAAQ,eAAe,KAAK;AACrD,MAAI,CAAC,QAAS;AACd,QAAM,MAAM,oBAAI,KAAK;AACrB,QAAM,WAAW,MAAM,GAAG;AAAA,IACxB;AAAA,IACA;AAAA,IACA,EAAE,uBAAuB,QAAQ,+BAA+B,WAAW,IAAI;AAAA,EACjF;AACA,MAAI,aAAa,EAAG;AACpB,QAAM,OAAO,oBAAoB,IAAI,SAAS,QAAQ,+BAA+B,GAAG;AACxF,QAAM,oBAAoB,KAAK,MAAM,IAAI;AACzC,0BAAwB,MAAM,QAAQ,2BAA2B,UAAU,IAAI,MAAM,OAAO,IAAI;AAClG;AAEA,MAAM,mBAAgG;AAAA,EACpG,IAAI,kCAAkC;AAAA,EACtC,MAAM,QAAQ,UAAU,KAAK;AAC3B,UAAM,SAAS,sBAAsB,MAAM,QAAQ;AACnD,UAAM,EAAE,UAAU,IAAI,MAAM,oBAAoB;AAChD,UAAM,KAAM,IAAI,UAAU,QAAQ,IAAI,EAAoB,KAAK;AAC/D,UAAM,OAAO,MAAM,eAAe,IAAI,KAAK,OAAO,IAAI,SAAS;AAC/D,UAAM,QAAQ,EAAE,UAAU,KAAK,UAAU,gBAAgB,KAAK,eAAe;AAE7E,UAAM,gBAAgB,MAAM,0BAA0B,IAAI,KAAK,IAAI;AACnE,UAAM,YAAY,MAAM,wCAAwC,IAAI,WAAW,KAAK,QAAQ;AAC5F,UAAM,aAAa,gBACf,MAAM,wBAAwB,IAAI,OAAO,KAAK,eAAe,eAAe,EAAE,UAAU,CAAC,IACzF;AACJ,QAAI,CAAC,iBAAiB,CAAC,YAAY;AACjC,YAAM,IAAI,cAAc,KAAK;AAAA,QAC3B,MAAM;AAAA,QACN,OAAO,UAAU,uCAAuC,wDAAwD;AAAA,MAClH,CAAC;AAAA,IACH;AAEA,UAAM,gBAAgB,MAAM,kBAAkB,IAAI,IAAI;AACtD,QAAI,cAAc,SAAS,KAAK,YAAY,EAAG,OAAM,aAAa,SAAS;AAC3E,QAAI,KAAK,sBAAuB,OAAM,eAAe,SAAS;AAE9D,UAAM,MAAM,oBAAI,KAAK;AACrB,UAAM,WAAW,MAAM,GAAG;AAAA,MACxB;AAAA,MACA;AAAA,QACE,IAAI,KAAK;AAAA,QACT,UAAU,KAAK;AAAA,QACf,gBAAgB,KAAK;AAAA,QACrB,uBAAuB;AAAA,QACvB,WAAW;AAAA,QACX,GAAI,cAAc,SAAS,IAAI,EAAE,cAAc,EAAE,MAAM,cAAc,EAAE,IAAI,CAAC;AAAA,MAC9E;AAAA,MACA,EAAE,uBAAuB,eAAe,WAAW,IAAI;AAAA,IACzD;AACA,QAAI,aAAa,GAAG;AAClB,SAAG,MAAM;AACT,YAAM,UAAU,MAAM,GAAG,QAAQ,eAAe,EAAE,IAAI,KAAK,IAAI,UAAU,KAAK,UAAU,gBAAgB,KAAK,gBAAgB,WAAW,KAAK,CAAC;AAC9I,UAAI,CAAC,QAAS,OAAM,SAAS,SAAS;AACtC,UAAI,CAAC,QAAQ,yBAAyB,cAAc,SAAS,QAAQ,YAAY,EAAG,OAAM,aAAa,SAAS;AAChH,YAAM,eAAe,SAAS;AAAA,IAChC;AACA,UAAM,UAAU,oBAAoB,IAAI,MAAM,eAAe,GAAG;AAChE,UAAM,oBAAoB,KAAK,OAAO;AACtC,4BAAwB,SAAS,MAAM,SAAS,IAAI,MAAM,OAAO,IAAI;AACrE,WAAO,SAAS,OAAO;AAAA,EACzB;AAAA,EACA,UAAU,OAAO,EAAE,OAAO,MAAM;AAC9B,UAAM,EAAE,UAAU,IAAI,MAAM,oBAAoB;AAChD,WAAO;AAAA,MACL,aAAa,UAAU,sCAAsC,YAAY;AAAA,MACzE,cAAc;AAAA,MACd,YAAY,OAAO;AAAA,MACnB,UAAU,OAAO;AAAA,MACjB,gBAAgB,OAAO;AAAA,MACvB,SAAS,EAAE,cAAc,CAAC,wBAAwB,EAAE;AAAA,MACpD,SAAS;AAAA,QACP,MAAM;AAAA,UACJ,QAAQ,OAAO;AAAA,UACf,UAAU,OAAO;AAAA,UACjB,gBAAgB,OAAO;AAAA,UACvB,+BAA+B;AAAA,UAC/B,2BAA2B,OAAO;AAAA,QACpC;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA,EACA,MAAM,OAAO,EAAE,UAAU,IAAI,MAAM;AACjC,UAAM,UAAU,mBAA0C,QAAQ;AAClE,QAAI,CAAC,SAAS,OAAQ;AACtB,UAAM,gBAAgB,KAAK,OAAO;AAAA,EACpC;AACF;AAEA,MAAM,qBAAqJ;AAAA,EACzJ,IAAI,kCAAkC;AAAA,EACtC,MAAM,QAAQ,UAAU,KAAK;AAC3B,UAAM,SAAS,sBAAsB,MAAM,QAAQ;AACnD,UAAM,EAAE,UAAU,IAAI,MAAM,oBAAoB;AAChD,UAAM,KAAM,IAAI,UAAU,QAAQ,IAAI,EAAoB,KAAK;AAC/D,UAAM,OAAO,MAAM,eAAe,IAAI,KAAK,OAAO,IAAI,SAAS;AAC/D,UAAM,gBAAgB,MAAM,0BAA0B,IAAI,KAAK,IAAI;AACnE,QAAI,CAAC,iBAAiB,KAAK,0BAA0B,eAAe;AAClE,YAAM,IAAI,cAAc,KAAK;AAAA,QAC3B,MAAM;AAAA,QACN,OAAO,UAAU,uCAAuC,kDAAkD;AAAA,MAC5G,CAAC;AAAA,IACH;AACA,UAAM,MAAM,oBAAI,KAAK;AACrB,UAAM,WAAW,MAAM,GAAG;AAAA,MACxB;AAAA,MACA,EAAE,IAAI,KAAK,IAAI,UAAU,KAAK,UAAU,gBAAgB,KAAK,gBAAgB,uBAAuB,eAAe,WAAW,KAAK;AAAA,MACnI,EAAE,uBAAuB,MAAM,WAAW,IAAI;AAAA,IAChD;AACA,QAAI,aAAa,GAAG;AAClB,YAAM,IAAI,cAAc,KAAK;AAAA,QAC3B,MAAM;AAAA,QACN,OAAO,UAAU,uCAAuC,sEAAsE;AAAA,MAChI,CAAC;AAAA,IACH;AACA,UAAM,WAAW,oBAAoB,IAAI,MAAM,MAAM,GAAG;AACxD,UAAM,oBAAoB,KAAK,QAAQ;AACvC,4BAAwB,UAAU,eAAe,WAAW,IAAI,MAAM,OAAO,IAAI;AACjF,WAAO,EAAE,GAAG,SAAS,QAAQ,GAAG,+BAA+B,cAAc;AAAA,EAC/E;AAAA,EACA,UAAU,OAAO,EAAE,OAAO,MAAM;AAC9B,UAAM,EAAE,UAAU,IAAI,MAAM,oBAAoB;AAChD,WAAO;AAAA,MACL,aAAa,UAAU,wCAAwC,cAAc;AAAA,MAC7E,cAAc;AAAA,MACd,YAAY,OAAO;AAAA,MACnB,UAAU,OAAO;AAAA,MACjB,gBAAgB,OAAO;AAAA,MACvB,SAAS,EAAE,cAAc,CAAC,wBAAwB,EAAE;AAAA,MACpD,SAAS;AAAA,QACP,MAAM;AAAA,UACJ,QAAQ,OAAO;AAAA,UACf,UAAU,OAAO;AAAA,UACjB,gBAAgB,OAAO;AAAA,UACvB,+BAA+B,OAAO;AAAA,UACtC,2BAA2B;AAAA,QAC7B;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA,EACA,MAAM,OAAO,EAAE,UAAU,IAAI,MAAM;AACjC,UAAM,UAAU,mBAA0C,QAAQ;AAClE,QAAI,CAAC,SAAS,OAAQ;AACtB,UAAM,gBAAgB,KAAK,OAAO;AAAA,EACpC;AACF;AAEA,gBAAgB,gBAAgB;AAChC,gBAAgB,kBAAkB;",
6
+ "names": []
7
+ }
@@ -9,7 +9,7 @@ import { StaffTimeTask, StaffTimeTaskComment } from "../data/entities.js";
9
9
  import { staffTimeTaskCommentCrudEvents } from "../lib/crud.js";
10
10
  import {
11
11
  staffTimeTaskCommentCreateSchema,
12
- staffTimeTaskCommentUpdateSchema
12
+ staffTimeTaskCommentUpdateRequestSchema
13
13
  } from "../data/validators.js";
14
14
  import {
15
15
  applyScopeToWhere,
@@ -35,6 +35,34 @@ const commentCrudIndexer = {
35
35
  entityType: "staff:staff_time_task_comment"
36
36
  };
37
37
  const commentCrudEvents = staffTimeTaskCommentCrudEvents;
38
+ function commentEvents(portalVisible) {
39
+ const base = staffTimeTaskCommentCrudEvents;
40
+ return {
41
+ ...base,
42
+ buildPayload: (context) => ({
43
+ ...base.buildPayload ? base.buildPayload(context) : {},
44
+ portalVisible
45
+ })
46
+ };
47
+ }
48
+ const TASK_COMMENT_CUSTOMER_IMMUTABLE_CODE = "task_comment_customer_immutable";
49
+ const TASK_COMMENT_VISIBILITY_INVALID_CODE = "task_comment_visibility_invalid";
50
+ function customerCommentImmutableError(translate) {
51
+ return new CrudHttpError(403, {
52
+ code: TASK_COMMENT_CUSTOMER_IMMUTABLE_CODE,
53
+ error: translate(
54
+ "staff.time_tracking.taskComments.errors.customerImmutable",
55
+ "Messages written by the customer cannot be edited or deleted."
56
+ )
57
+ });
58
+ }
59
+ function visibilityInvalidError(translate) {
60
+ const message = translate(
61
+ "staff.time_tracking.taskComments.errors.visibilityPortalOnly",
62
+ "Only customer support tickets can carry replies visible to the customer."
63
+ );
64
+ return new CrudHttpError(422, { code: TASK_COMMENT_VISIBILITY_INVALID_CODE, error: message, fieldErrors: { visibility: message } });
65
+ }
38
66
  function toCommentSnapshot(comment) {
39
67
  return {
40
68
  id: comment.id,
@@ -43,6 +71,8 @@ function toCommentSnapshot(comment) {
43
71
  taskId: comment.taskId,
44
72
  body: comment.body,
45
73
  authorUserId: comment.authorUserId ?? null,
74
+ visibility: comment.visibility ?? "internal",
75
+ authorKind: comment.authorKind ?? "staff",
46
76
  createdAt: comment.createdAt ? comment.createdAt.toISOString() : null,
47
77
  deletedAt: comment.deletedAt ? comment.deletedAt.toISOString() : null
48
78
  };
@@ -98,6 +128,7 @@ async function callerCanManageAll(ctx) {
98
128
  });
99
129
  }
100
130
  async function requireEditableComment(comment, ctx, translate) {
131
+ if (comment.authorKind === "customer") throw customerCommentImmutableError(translate);
101
132
  const actorId = stampAuthorUserId(ctx);
102
133
  if (actorId && comment.authorUserId === actorId) return;
103
134
  if (await callerCanManageAll(ctx)) return;
@@ -130,7 +161,10 @@ const createTaskCommentCommand = {
130
161
  await withAtomicFlush(
131
162
  em,
132
163
  [
133
- () => requireTaskInScope(em, parsed.taskId, scope, translate),
164
+ async () => {
165
+ const task = await requireTaskInScope(em, parsed.taskId, scope, translate);
166
+ if (parsed.visibility === "customer" && task.source !== "portal") throw visibilityInvalidError(translate);
167
+ },
134
168
  () => {
135
169
  created = em.create(StaffTimeTaskComment, {
136
170
  tenantId: parsed.tenantId,
@@ -138,6 +172,8 @@ const createTaskCommentCommand = {
138
172
  taskId: parsed.taskId,
139
173
  body: parsed.body,
140
174
  authorUserId,
175
+ visibility: parsed.visibility ?? "internal",
176
+ authorKind: "staff",
141
177
  createdAt: /* @__PURE__ */ new Date(),
142
178
  updatedAt: /* @__PURE__ */ new Date(),
143
179
  deletedAt: null
@@ -154,7 +190,7 @@ const createTaskCommentCommand = {
154
190
  action: "created",
155
191
  entity: record,
156
192
  identifiers: { id: record.id, organizationId: record.organizationId, tenantId: record.tenantId },
157
- events: commentCrudEvents,
193
+ events: commentEvents(record.visibility === "customer"),
158
194
  indexer: commentCrudIndexer
159
195
  });
160
196
  return { commentId: record.id, taskId: record.taskId, authorUserId: record.authorUserId ?? null };
@@ -208,7 +244,7 @@ const createTaskCommentCommand = {
208
244
  action: "deleted",
209
245
  entity: comment,
210
246
  identifiers: { id: comment.id, organizationId: comment.organizationId, tenantId: comment.tenantId },
211
- events: commentCrudEvents,
247
+ events: commentEvents(comment.visibility === "customer"),
212
248
  indexer: commentCrudIndexer
213
249
  });
214
250
  }
@@ -216,7 +252,7 @@ const createTaskCommentCommand = {
216
252
  const updateTaskCommentCommand = {
217
253
  id: staffTimeTaskCommentCommandIds.update,
218
254
  async prepare(rawInput, ctx) {
219
- const parsed = staffTimeTaskCommentUpdateSchema.safeParse(rawInput);
255
+ const parsed = staffTimeTaskCommentUpdateRequestSchema.safeParse(rawInput);
220
256
  if (!parsed.success) return {};
221
257
  const em = ctx.container.resolve("em");
222
258
  const before = await loadCommentSnapshot(em, parsed.data.id, staffSnapshotScopeFromContext(ctx));
@@ -224,7 +260,7 @@ const updateTaskCommentCommand = {
224
260
  return { before: { snapshot: before } };
225
261
  },
226
262
  async execute(rawInput, ctx) {
227
- const parsed = staffTimeTaskCommentUpdateSchema.parse(rawInput);
263
+ const parsed = staffTimeTaskCommentUpdateRequestSchema.parse(rawInput);
228
264
  const { translate } = await resolveTranslations();
229
265
  const em = ctx.container.resolve("em").fork();
230
266
  const scope = commandActorScope(ctx);
@@ -237,11 +273,17 @@ const updateTaskCommentCommand = {
237
273
  ensureOrganizationScope(ctx, comment.organizationId);
238
274
  assertCommentVersion(ctx, comment);
239
275
  await requireEditableComment(comment, ctx, translate);
276
+ const visibilityBefore = comment.visibility ?? "internal";
277
+ if (parsed.visibility === "customer" && visibilityBefore !== "customer") {
278
+ const task = await requireTaskInScope(em, comment.taskId, scope, translate);
279
+ if (task.source !== "portal") throw visibilityInvalidError(translate);
280
+ }
240
281
  await withAtomicFlush(
241
282
  em,
242
283
  [
243
284
  () => {
244
- comment.body = parsed.body;
285
+ if (parsed.body !== void 0) comment.body = parsed.body;
286
+ if (parsed.visibility !== void 0) comment.visibility = parsed.visibility;
245
287
  comment.updatedAt = /* @__PURE__ */ new Date();
246
288
  }
247
289
  ],
@@ -252,7 +294,7 @@ const updateTaskCommentCommand = {
252
294
  action: "updated",
253
295
  entity: comment,
254
296
  identifiers: { id: comment.id, organizationId: comment.organizationId, tenantId: comment.tenantId },
255
- events: commentCrudEvents,
297
+ events: commentEvents(visibilityBefore === "customer" || comment.visibility === "customer"),
256
298
  indexer: commentCrudIndexer
257
299
  });
258
300
  return { commentId: comment.id, taskId: comment.taskId };
@@ -277,7 +319,7 @@ const updateTaskCommentCommand = {
277
319
  changes: buildChanges(
278
320
  before,
279
321
  after,
280
- ["body"]
322
+ ["body", "visibility"]
281
323
  ),
282
324
  payload: {
283
325
  undo: { before, after }
@@ -299,6 +341,7 @@ const updateTaskCommentCommand = {
299
341
  [
300
342
  () => {
301
343
  comment.body = before.body;
344
+ comment.visibility = before.visibility ?? "internal";
302
345
  comment.updatedAt = /* @__PURE__ */ new Date();
303
346
  }
304
347
  ],
@@ -309,7 +352,7 @@ const updateTaskCommentCommand = {
309
352
  action: "updated",
310
353
  entity: comment,
311
354
  identifiers: { id: comment.id, organizationId: comment.organizationId, tenantId: comment.tenantId },
312
- events: commentCrudEvents,
355
+ events: commentEvents(true),
313
356
  indexer: commentCrudIndexer
314
357
  });
315
358
  }
@@ -358,7 +401,7 @@ const deleteTaskCommentCommand = {
358
401
  action: "deleted",
359
402
  entity: comment,
360
403
  identifiers: { id: comment.id, organizationId: comment.organizationId, tenantId: comment.tenantId },
361
- events: commentCrudEvents,
404
+ events: commentEvents(comment.visibility === "customer"),
362
405
  indexer: commentCrudIndexer
363
406
  });
364
407
  return { commentId: comment.id, taskId: comment.taskId };
@@ -402,6 +445,7 @@ const deleteTaskCommentCommand = {
402
445
  taskId: before.taskId,
403
446
  body: before.body,
404
447
  authorUserId: before.authorUserId,
448
+ visibility: before.visibility ?? "internal",
405
449
  createdAt: before.createdAt ? new Date(before.createdAt) : /* @__PURE__ */ new Date(),
406
450
  updatedAt: /* @__PURE__ */ new Date(),
407
451
  deletedAt: null
@@ -422,7 +466,7 @@ const deleteTaskCommentCommand = {
422
466
  action: "created",
423
467
  entity: comment,
424
468
  identifiers: { id: before.id, organizationId: before.organizationId, tenantId: before.tenantId },
425
- events: commentCrudEvents,
469
+ events: commentEvents(before.visibility === "customer"),
426
470
  indexer: commentCrudIndexer
427
471
  });
428
472
  }
@@ -432,7 +476,9 @@ registerCommand(updateTaskCommentCommand);
432
476
  registerCommand(deleteTaskCommentCommand);
433
477
  export {
434
478
  STAFF_TIME_TASK_COMMENT_RESOURCE_KIND,
479
+ TASK_COMMENT_CUSTOMER_IMMUTABLE_CODE,
435
480
  TASK_COMMENT_NOT_AUTHOR_CODE,
481
+ TASK_COMMENT_VISIBILITY_INVALID_CODE,
436
482
  staffTimeTaskCommentCommandIds
437
483
  };
438
484
  //# sourceMappingURL=timesheets-task-comments.js.map