@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
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../src/modules/staff/lib/time-tracking-ui/TaskDrawer.tsx"],
4
- "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { useRouter } from 'next/navigation'\nimport { useQuery, useQueryClient } from '@tanstack/react-query'\nimport { Plus, X } from 'lucide-react'\nimport {\n Drawer,\n DrawerBody,\n DrawerContent,\n DrawerDescription,\n DrawerFooter,\n DrawerHeader,\n DrawerTitle,\n} from '@open-mercato/ui/primitives/drawer'\nimport { Avatar } from '@open-mercato/ui/primitives/avatar'\nimport { Badge } from '@open-mercato/ui/primitives/badge'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { Checkbox } from '@open-mercato/ui/primitives/checkbox'\nimport { Input } from '@open-mercato/ui/primitives/input'\nimport { Progress } from '@open-mercato/ui/primitives/progress'\nimport {\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from '@open-mercato/ui/primitives/select'\nimport { ErrorMessage, LoadingMessage } from '@open-mercato/ui/backend/detail'\nimport { apiCall, apiCallOrThrow, withScopedApiRequestHeaders } from '@open-mercato/ui/backend/utils/apiCall'\nimport { buildOptimisticLockHeader } from '@open-mercato/ui/backend/utils/optimisticLock'\nimport { surfaceRecordConflict } from '@open-mercato/ui/backend/conflicts'\nimport { useBackendChrome } from '@open-mercato/ui/backend/BackendChromeProvider'\nimport { useConfirmDialog } from '@open-mercato/ui/backend/confirm-dialog'\nimport { useGuardedMutation } from '@open-mercato/ui/backend/injection/useGuardedMutation'\nimport { InjectionSpot } from '@open-mercato/ui/backend/injection/InjectionSpot'\nimport { extensionPoints } from '@open-mercato/core/modules/staff/extension-points'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { formatCurrency, formatDate } from '@open-mercato/ui/utils/format'\nimport { hasFeature } from '@open-mercato/shared/security/features'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { useOrganizationScopeVersion } from '@open-mercato/shared/lib/frontend/useOrganizationScope'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\nimport { TaskCommentThread } from './TaskCommentThread'\nimport { TaskQuickLog, type QuickLogSubmission } from './TaskQuickLog'\nimport { TagPicker, tagChipStyle } from './TagPicker'\nimport { slugifyProjectName } from '../time-tracking/projectCode'\nimport type { TagOption } from './timeEntryDialogState'\nimport {\n computeSubtaskProgress,\n formatBoardMinutes,\n readItems,\n readString,\n readTotal,\n sortByPosition,\n toBoardStatus,\n toBoardTask,\n type BoardApiRow,\n type BoardStatus,\n type BoardTask,\n} from './kanbanBoardData'\nimport {\n childMinutes,\n formatElapsed,\n formatSplitMinutes,\n resolveDefaultStatusId,\n resolveDoneStatusId,\n summarizeEntries,\n toDrawerComment,\n toDrawerEntry,\n todayIsoDate,\n buildEntryClocks,\n type DrawerComment,\n type DrawerEntry,\n} from './taskDrawerData'\n\nconst logger = createLogger('staff').child({ component: 'TaskDrawer' })\n\nconst TASK_ENTITY_ID = 'staff:staff_time_task'\n\ntype TaskDetailInjectionContext = {\n entityId: string\n recordId: string\n taskId: string\n timeProjectId: string | null\n retryLastMutation: () => Promise<boolean>\n}\n\nexport const TASK_DRAWER_MUTATION_CONTEXT_ID = 'staff.time_tracking.task_drawer'\nexport const RATES_FEATURE = 'staff.timesheets.rates.view'\nexport const TASKS_MANAGE_FEATURE = 'staff.timesheets.tasks.manage'\nexport const ENTRIES_LIST_PATH = '/backend/staff/time-tracking/entries'\n\nconst DRAWER_QUERY_ROOT = ['staff', 'time-tracking', 'task-drawer'] as const\nconst DIRECTORY_PAGE_SIZE = 100\nconst CHILD_PAGE_SIZE = 100\nconst COMMENT_PAGE_SIZE = 50\nconst ENTRY_PAGE_SIZE = 100\n/** Bounded sweep: the entries route has no `taskId` filter (see the docblock). */\nconst MAX_ENTRY_PAGES = 3\nconst RECENT_ENTRY_COUNT = 5\nconst ELAPSED_TICK_MS = 1000\n\nexport type TaskDrawerProps = {\n taskId: string\n open: boolean\n onOpenChange: (open: boolean) => void\n /** Shown in the header line next to the created date; the host already knows it. */\n projectName?: string | null\n /** `?panel=time` \u2014 \"Add time\" on a board card lands with the quick log focused. */\n focusPanel?: string | null\n /** Lets the host (the board) refetch after a write that changed hours or status. */\n onChanged?: () => void\n}\n\ntype LoadedTask = { task: BoardTask; createdAt: string | null }\n\ntype RunningTimer = { entryId: string | null; taskId: string | null; startedAt: string | null }\n\ntype ProjectEntries = { rows: BoardApiRow[]; partial: boolean }\n\nasync function fetchProjectEntries(projectId: string): Promise<ProjectEntries> {\n const rows: BoardApiRow[] = []\n for (let page = 1; page <= MAX_ENTRY_PAGES; page += 1) {\n const call = await apiCall<Record<string, unknown>>(\n `/api/staff/timesheets/time-entries?projectId=${encodeURIComponent(projectId)}&page=${page}&pageSize=${ENTRY_PAGE_SIZE}&sortField=date&sortDir=desc`,\n )\n if (!call.ok) return { rows, partial: true }\n const items = readItems(call.result)\n rows.push(...items)\n if (items.length < ENTRY_PAGE_SIZE) return { rows, partial: false }\n if (rows.length >= readTotal(call.result, rows.length)) return { rows, partial: false }\n }\n return { rows, partial: true }\n}\n\n/**\n * The task drawer of screen 7 (T3.7, US-C3/C4/C5, US-D4).\n *\n * Four decisions carry the design intent:\n *\n * 1. **Subtasks are child tasks, and ticking one is a status move** (D-2, and\n * the decision that supersedes the mockup's note 4). There is no boolean on\n * a task, so the checkbox PATCHes the child to the project's first `is_done`\n * column and un-ticking returns it to the `is_default` one. Each row carries\n * its own assignee and its own hours because a subtask holds time like any\n * other task; the parent's headline is the inclusive rollup that already\n * contains them.\n * 2. **Depth is capped at one.** A child task's drawer renders no subtask\n * section at all: the server answers `400 subtask_depth_exceeded`, so an\n * \"add subtask\" affordance there could only ever fail.\n * 3. **Properties save per field, so each write carries its own version.** A\n * blur PATCHes with `buildOptimisticLockHeader(versionRef.current)` and then refreshes\n * the token \u2014 from the status route's `updatedAt` when it returns one, by\n * refetching the row when it does not (the CRUD update answers `{ok:true}`).\n * Saves are chained on `saveChainRef` so the second field never leaves with\n * the version the first one just invalidated.\n * 4. **One timer at a time** (note 2, US-D4). Starting here while a timer runs\n * on another task asks first, then stops that one before starting this one.\n *\n * Two known gaps, both other tasks' work, are handled honestly rather than faked:\n * the task projection carries no tag ids yet (T3.10), so the tag row can add but\n * cannot list what the server already has; and the entries route has no `taskId`\n * filter, so the recent-entries list and the billable split come from a bounded\n * sweep of the project's entries and say so when the bound is hit.\n */\nexport function TaskDrawer({\n taskId,\n open,\n onOpenChange,\n projectName,\n focusPanel,\n onChanged,\n}: TaskDrawerProps) {\n const t = useT()\n const router = useRouter()\n const queryClient = useQueryClient()\n const scopeVersion = useOrganizationScopeVersion()\n const { payload } = useBackendChrome()\n const { confirm, ConfirmDialogElement } = useConfirmDialog()\n\n const canSeeMoney = hasFeature(payload?.grantedFeatures, RATES_FEATURE)\n const canManage = hasFeature(payload?.grantedFeatures, TASKS_MANAGE_FEATURE)\n\n const [tagIds, setTagIds] = React.useState<string[]>([])\n const [subtaskDraft, setSubtaskDraft] = React.useState('')\n const [composingSubtask, setComposingSubtask] = React.useState(false)\n const [busy, setBusy] = React.useState(false)\n const [timerBusy, setTimerBusy] = React.useState(false)\n const [now, setNow] = React.useState(() => Date.now())\n\n const saveChainRef = React.useRef<Promise<void>>(Promise.resolve())\n /**\n * The lock token lives in a ref, not in state: a save queued behind another one\n * must read the version at the moment it *runs*, not the one its closure was\n * built with, or a second blur landing before a re-render would still leave\n * with the token the first blur just invalidated.\n */\n const versionRef = React.useRef<string | null>(null)\n\n const { runMutation, retryLastMutation } = useGuardedMutation<{\n formId: string\n resourceKind: string\n resourceId: string\n retryLastMutation: () => Promise<boolean>\n }>({\n contextId: TASK_DRAWER_MUTATION_CONTEXT_ID,\n blockedMessage: t('staff.time_tracking.taskDrawer.properties.saveError', 'Could not save the change.'),\n })\n\n const taskQuery = useQuery<LoadedTask | null>({\n queryKey: [...DRAWER_QUERY_ROOT, 'task', `scope:${scopeVersion}`, taskId],\n enabled: open && taskId.length > 0,\n staleTime: 5_000,\n queryFn: async () => {\n const call = await apiCall<Record<string, unknown>>(\n `/api/staff/timesheets/tasks?id=${encodeURIComponent(taskId)}&pageSize=1`,\n )\n if (!call.ok) throw new Error('[internal] task request failed')\n const row = readItems(call.result)[0]\n if (!row) return null\n const parsed = toBoardTask(row)\n return parsed ? { task: parsed, createdAt: readString(row, 'created_at', 'createdAt') } : null\n },\n })\n\n const task = taskQuery.data?.task ?? null\n const createdAt = taskQuery.data?.createdAt ?? null\n const projectId = task?.timeProjectId ?? null\n\n const taskInjectionContext = React.useMemo<TaskDetailInjectionContext>(\n () => ({\n entityId: TASK_ENTITY_ID,\n recordId: taskId,\n taskId,\n timeProjectId: projectId,\n retryLastMutation,\n }),\n [projectId, retryLastMutation, taskId],\n )\n const isChildTask = !!task?.parentTaskId\n\n React.useEffect(() => {\n if (!task) return\n versionRef.current = task.updatedAt\n setTagIds(task.tagIds)\n }, [task])\n\n const statusesQuery = useQuery<BoardStatus[]>({\n queryKey: [...DRAWER_QUERY_ROOT, 'statuses', `scope:${scopeVersion}`, projectId ?? 'none'],\n enabled: open && !!projectId,\n staleTime: 30_000,\n queryFn: async () => {\n if (!projectId) return []\n const call = await apiCall<Record<string, unknown>>(\n `/api/staff/timesheets/task-statuses?timeProjectId=${encodeURIComponent(projectId)}&page=1&pageSize=${DIRECTORY_PAGE_SIZE}&sortField=position&sortDir=asc`,\n )\n if (!call.ok) return []\n const statuses = readItems(call.result)\n .map(toBoardStatus)\n .filter((status): status is BoardStatus => status !== null)\n return sortByPosition(statuses)\n },\n })\n\n const statuses = React.useMemo(() => statusesQuery.data ?? [], [statusesQuery.data])\n\n const childrenQuery = useQuery<BoardTask[]>({\n queryKey: [...DRAWER_QUERY_ROOT, 'children', `scope:${scopeVersion}`, taskId],\n enabled: open && !isChildTask && !!task,\n staleTime: 10_000,\n queryFn: async () => {\n const call = await apiCall<Record<string, unknown>>(\n `/api/staff/timesheets/tasks?parentTaskId=${encodeURIComponent(taskId)}&page=1&pageSize=${CHILD_PAGE_SIZE}&sortField=position&sortDir=asc`,\n )\n if (!call.ok) return []\n const rows = readItems(call.result)\n .map(toBoardTask)\n .filter((row): row is BoardTask => row !== null)\n return sortByPosition(rows)\n },\n })\n\n const children = React.useMemo(() => childrenQuery.data ?? [], [childrenQuery.data])\n\n const commentsQuery = useQuery<DrawerComment[]>({\n queryKey: [...DRAWER_QUERY_ROOT, 'comments', `scope:${scopeVersion}`, taskId],\n enabled: open && taskId.length > 0,\n staleTime: 10_000,\n queryFn: async () => {\n const call = await apiCall<Record<string, unknown>>(\n `/api/staff/timesheets/tasks/${encodeURIComponent(taskId)}/comments?page=1&pageSize=${COMMENT_PAGE_SIZE}`,\n )\n if (!call.ok) throw new Error('[internal] comments request failed')\n return readItems(call.result)\n .map(toDrawerComment)\n .filter((comment): comment is DrawerComment => comment !== null)\n },\n })\n\n const entriesQuery = useQuery<ProjectEntries>({\n queryKey: [...DRAWER_QUERY_ROOT, 'entries', `scope:${scopeVersion}`, projectId ?? 'none'],\n enabled: open && !!projectId,\n staleTime: 10_000,\n queryFn: async () => (projectId ? fetchProjectEntries(projectId) : { rows: [], partial: false }),\n })\n\n const selfQuery = useQuery<string | null>({\n queryKey: [...DRAWER_QUERY_ROOT, 'self', `scope:${scopeVersion}`],\n enabled: open,\n staleTime: 300_000,\n queryFn: async () => {\n const call = await apiCall<{ member?: { id?: string | null } | null }>('/api/staff/team-members/self')\n return call.ok ? call.result?.member?.id ?? null : null\n },\n })\n\n const selfStaffMemberId = selfQuery.data ?? null\n\n const membersQuery = useQuery<{ id: string; name: string }[]>({\n queryKey: [...DRAWER_QUERY_ROOT, 'members', `scope:${scopeVersion}`],\n enabled: open,\n staleTime: 300_000,\n queryFn: async () => {\n const call = await apiCall<Record<string, unknown>>(\n `/api/staff/team-members?page=1&pageSize=${DIRECTORY_PAGE_SIZE}&isActive=true`,\n )\n if (!call.ok) return []\n return readItems(call.result)\n .map((row) => {\n const id = readString(row, 'id')\n if (!id) return null\n return { id, name: readString(row, 'display_name', 'displayName') ?? id }\n })\n .filter((member): member is { id: string; name: string } => member !== null)\n },\n })\n\n const members = React.useMemo(() => membersQuery.data ?? [], [membersQuery.data])\n\n const memberNames = React.useMemo(() => {\n const map = new Map<string, string>()\n for (const member of members) map.set(member.id, member.name)\n return map\n }, [members])\n\n const tagsQuery = useQuery<TagOption[]>({\n queryKey: [...DRAWER_QUERY_ROOT, 'tags', `scope:${scopeVersion}`],\n enabled: open,\n staleTime: 300_000,\n queryFn: async () => {\n const call = await apiCall<Record<string, unknown>>(\n `/api/staff/timesheets/tags?page=1&pageSize=${DIRECTORY_PAGE_SIZE}&sortField=label&sortDir=asc`,\n )\n if (!call.ok) return []\n return readItems(call.result)\n .map((row) => {\n const id = readString(row, 'id')\n if (!id) return null\n // The colour has to travel with the tag or the drawer's chips render\n // grey while the entry dialog's render tinted from the same rows.\n return { id, label: readString(row, 'label') ?? id, color: readString(row, 'color') }\n })\n .filter((tag): tag is TagOption => tag !== null)\n },\n })\n\n const tagOptions = React.useMemo(() => tagsQuery.data ?? [], [tagsQuery.data])\n\n const runningTimerQuery = useQuery<RunningTimer>({\n queryKey: [...DRAWER_QUERY_ROOT, 'running-timer', `scope:${scopeVersion}`, selfStaffMemberId ?? 'none'],\n enabled: open && !!selfStaffMemberId,\n staleTime: 15_000,\n refetchInterval: 30_000,\n queryFn: async () => {\n if (!selfStaffMemberId) return { entryId: null, taskId: null, startedAt: null }\n const call = await apiCall<Record<string, unknown>>(\n `/api/staff/timesheets/time-entries?staffMemberId=${encodeURIComponent(selfStaffMemberId)}&running=true&pageSize=${ENTRY_PAGE_SIZE}`,\n )\n if (!call.ok) return { entryId: null, taskId: null, startedAt: null }\n for (const row of readItems(call.result)) {\n const startedAt = readString(row, 'started_at', 'startedAt')\n const endedAt = readString(row, 'ended_at', 'endedAt')\n if (!startedAt || endedAt) continue\n return {\n entryId: readString(row, 'id'),\n taskId: readString(row, 'task_id', 'taskId'),\n startedAt,\n }\n }\n return { entryId: null, taskId: null, startedAt: null }\n },\n })\n\n const runningTimer = runningTimerQuery.data ?? { entryId: null, taskId: null, startedAt: null }\n const timerRunningHere = !!runningTimer.entryId && runningTimer.taskId === taskId\n\n React.useEffect(() => {\n if (!open || !timerRunningHere) return\n const handle = setInterval(() => setNow(Date.now()), ELAPSED_TICK_MS)\n return () => clearInterval(handle)\n }, [open, timerRunningHere])\n\n const doneStatusIds = React.useMemo(\n () => new Set(statuses.filter((status) => status.isDone).map((status) => status.id)),\n [statuses],\n )\n\n // `doneChildCount` does not exist on the API yet, so the drawer derives it the\n // way the board does \u2014 from the children it already fetched for the list. One\n // extra page of rows per open, no extra request.\n const progress = React.useMemo(\n () => computeSubtaskProgress(children, doneStatusIds).get(taskId) ?? { total: 0, done: 0 },\n [children, doneStatusIds, taskId],\n )\n\n const subtaskTotal = Math.max(progress.total, task?.childCount ?? 0)\n\n const taskIdsForEntries = React.useMemo(() => {\n const ids = new Set<string>([taskId])\n for (const child of children) ids.add(child.id)\n return ids\n }, [children, taskId])\n\n const entries = React.useMemo<DrawerEntry[]>(() => {\n const rows = entriesQuery.data?.rows ?? []\n return rows\n .map(toDrawerEntry)\n .filter((entry): entry is DrawerEntry => entry !== null && !!entry.taskId && taskIdsForEntries.has(entry.taskId))\n }, [entriesQuery.data, taskIdsForEntries])\n\n const totals = React.useMemo(\n () => summarizeEntries(entries, entriesQuery.data?.partial ?? false),\n [entries, entriesQuery.data],\n )\n\n const invalidate = React.useCallback(\n (part: 'task' | 'children' | 'entries' | 'comments') => {\n queryClient.invalidateQueries({ queryKey: [...DRAWER_QUERY_ROOT, part] }).catch(() => {})\n },\n [queryClient],\n )\n\n const refreshVersion = React.useCallback(async (): Promise<void> => {\n const refreshed = await taskQuery.refetch()\n const updatedAt = refreshed.data?.task.updatedAt ?? null\n if (updatedAt) versionRef.current = updatedAt\n }, [taskQuery])\n\n const reportError = React.useCallback(\n (error: unknown, fallback: string) => {\n if (surfaceRecordConflict(error, t)) {\n invalidate('task')\n invalidate('children')\n return\n }\n logger.error('staff.time_tracking task drawer mutation failed', { err: error })\n flash(error instanceof Error && error.message ? error.message : fallback, 'error')\n },\n [invalidate, t],\n )\n\n const handleQuickLog = React.useCallback(\n async ({ minutes, description, date, isBillable, startClock, endClock }: QuickLogSubmission): Promise<boolean> => {\n if (!selfStaffMemberId || !projectId) {\n flash(t('staff.time_tracking.taskDrawer.quickLog.error', 'Could not save the time entry.'), 'error')\n return false\n }\n setBusy(true)\n try {\n await runMutation({\n operation: () =>\n apiCallOrThrow(\n '/api/staff/timesheets/time-entries',\n {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({\n // US-C5 defaults \u2014 today, me, billable \u2014 unless the Details block\n // overrode them. The person is never overridable here: logging\n // for somebody else is a different act with different permissions.\n staffMemberId: selfStaffMemberId,\n date,\n durationMinutes: minutes,\n taskId,\n timeProjectId: projectId,\n isBillable,\n description,\n // The clocks are sent as a pair or not at all. A lone\n // `started_at` is indistinguishable from a running timer \u2014\n // `started_at IS NOT NULL AND ended_at IS NULL` is exactly how\n // the list identifies one \u2014 so an entry with a start and no end\n // shows up as a timer running since that moment, and stopping it\n // fails because no timer segment was ever opened for it.\n //\n // The duration is authoritative here, so the missing half is\n // derived from it rather than left blank: the same 2-of-3\n // arithmetic the full entry form does.\n ...buildEntryClocks({ date, startClock, endClock, minutes }),\n source: 'manual',\n }),\n },\n { errorMessage: t('staff.time_tracking.taskDrawer.quickLog.error', 'Could not save the time entry.') },\n ),\n context: {\n formId: TASK_DRAWER_MUTATION_CONTEXT_ID,\n resourceKind: 'staff.timesheets.time_entry',\n resourceId: taskId,\n retryLastMutation,\n },\n })\n flash(t('staff.time_tracking.taskDrawer.quickLog.saved', 'Time logged.'), 'success')\n invalidate('task')\n invalidate('entries')\n onChanged?.()\n return true\n } catch (error) {\n reportError(error, t('staff.time_tracking.taskDrawer.quickLog.error', 'Could not save the time entry.'))\n return false\n } finally {\n setBusy(false)\n }\n },\n [invalidate, onChanged, projectId, reportError, retryLastMutation, runMutation, selfStaffMemberId, t, taskId],\n )\n\n const stopTimerEntry = React.useCallback(\n async (entryId: string) => {\n await runMutation({\n operation: () =>\n apiCallOrThrow(\n `/api/staff/timesheets/time-entries/${encodeURIComponent(entryId)}/timer-stop`,\n { method: 'POST' },\n { errorMessage: t('staff.time_tracking.board.timer.stopError', 'Could not stop the timer.') },\n ),\n context: {\n formId: TASK_DRAWER_MUTATION_CONTEXT_ID,\n resourceKind: 'staff.timesheets.time_entry',\n resourceId: entryId,\n retryLastMutation,\n },\n })\n },\n [retryLastMutation, runMutation, t],\n )\n\n const handleStopTimer = React.useCallback(async () => {\n if (!runningTimer.entryId) return\n setTimerBusy(true)\n try {\n await stopTimerEntry(runningTimer.entryId)\n await runningTimerQuery.refetch()\n invalidate('task')\n invalidate('entries')\n onChanged?.()\n } catch (error) {\n reportError(error, t('staff.time_tracking.board.timer.stopError', 'Could not stop the timer.'))\n } finally {\n setTimerBusy(false)\n }\n }, [invalidate, onChanged, reportError, runningTimer.entryId, runningTimerQuery, stopTimerEntry, t])\n\n const handleStartTimer = React.useCallback(async () => {\n if (!selfStaffMemberId || !projectId) return\n // Note 2 / US-D4: one timer at a time. A timer running on another task is\n // stopped only after the person says so \u2014 never silently.\n if (runningTimer.entryId && runningTimer.taskId !== taskId) {\n const confirmed = await confirm({\n title: t('staff.time_tracking.taskDrawer.timer.switchTitle', 'A timer is running on another task'),\n text: t(\n 'staff.time_tracking.taskDrawer.timer.switchText',\n 'Only one timer can run at a time. Stop the other one and start it here?',\n ),\n })\n if (!confirmed) return\n }\n setTimerBusy(true)\n try {\n if (runningTimer.entryId && runningTimer.taskId !== taskId) {\n await stopTimerEntry(runningTimer.entryId)\n }\n const started = await runMutation({\n operation: () =>\n apiCallOrThrow<Record<string, unknown>>(\n '/api/staff/timesheets/time-entries/start-timer',\n {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({\n staffMemberId: selfStaffMemberId,\n timeProjectId: projectId,\n date: todayIsoDate(),\n notes: task?.title ?? null,\n }),\n },\n { errorMessage: t('staff.time_tracking.board.timer.startError', 'Could not start the timer.') },\n ),\n context: {\n formId: TASK_DRAWER_MUTATION_CONTEXT_ID,\n resourceKind: 'staff.timesheets.time_entry',\n resourceId: taskId,\n retryLastMutation,\n },\n })\n const entryId = typeof started?.result?.id === 'string' ? started.result.id : null\n if (entryId) {\n // `start-timer` is atomic but takes no task, so the link is a second\n // write \u2014 the board does the same. A failure leaves a running,\n // project-level timer rather than no timer at all.\n const linked = await apiCall('/api/staff/timesheets/time-entries', {\n method: 'PUT',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ id: entryId, taskId }),\n })\n if (!linked.ok) {\n flash(\n t(\n 'staff.time_tracking.board.timer.linkWarning',\n 'The timer is running but is not linked to this task yet.',\n ),\n 'warning',\n )\n }\n }\n await runningTimerQuery.refetch()\n onChanged?.()\n } catch (error) {\n reportError(error, t('staff.time_tracking.board.timer.startError', 'Could not start the timer.'))\n } finally {\n setTimerBusy(false)\n }\n }, [\n confirm,\n onChanged,\n projectId,\n reportError,\n retryLastMutation,\n runMutation,\n runningTimer.entryId,\n runningTimer.taskId,\n runningTimerQuery,\n selfStaffMemberId,\n stopTimerEntry,\n t,\n task?.title,\n taskId,\n ])\n\n /**\n * Property saves are serialized. Each one leaves with the version the previous\n * one produced, so the second blur cannot 409 against the first.\n */\n const runSerialized = React.useCallback(async (job: () => Promise<void>): Promise<void> => {\n const chained = saveChainRef.current.then(job, job)\n saveChainRef.current = chained.catch(() => {})\n return chained\n }, [])\n\n const saveTaskField = React.useCallback(\n async (patch: Record<string, unknown>) => {\n if (!task) return\n setBusy(true)\n try {\n await runSerialized(async () => {\n await runMutation({\n operation: () =>\n withScopedApiRequestHeaders(buildOptimisticLockHeader(versionRef.current), () =>\n apiCallOrThrow(\n '/api/staff/timesheets/tasks',\n {\n method: 'PUT',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ id: taskId, ...patch }),\n },\n {\n errorMessage: t(\n 'staff.time_tracking.taskDrawer.properties.saveError',\n 'Could not save the change.',\n ),\n },\n ),\n ),\n context: {\n formId: TASK_DRAWER_MUTATION_CONTEXT_ID,\n resourceKind: 'staff.timesheets.task',\n resourceId: taskId,\n retryLastMutation,\n },\n })\n // The CRUD update answers `{ok:true}`, so the new version comes from\n // the row itself \u2014 and it must land before the next field saves.\n await refreshVersion()\n })\n onChanged?.()\n } catch (error) {\n reportError(error, t('staff.time_tracking.taskDrawer.properties.saveError', 'Could not save the change.'))\n } finally {\n setBusy(false)\n }\n },\n [onChanged, refreshVersion, reportError, retryLastMutation, runMutation, runSerialized, t, task, taskId],\n )\n\n const handleStatusChange = React.useCallback(\n (statusId: string) => {\n if (!task || statusId === task.taskStatusId) return\n setBusy(true)\n void runSerialized(async () => {\n try {\n const moved = await runMutation({\n operation: () =>\n withScopedApiRequestHeaders(buildOptimisticLockHeader(versionRef.current), () =>\n apiCallOrThrow<Record<string, unknown>>(\n `/api/staff/timesheets/tasks/${encodeURIComponent(taskId)}/status`,\n {\n method: 'PATCH',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ taskStatusId: statusId }),\n },\n { errorMessage: t('staff.time_tracking.board.move.error', 'Could not move the task.') },\n ),\n ),\n context: {\n formId: TASK_DRAWER_MUTATION_CONTEXT_ID,\n resourceKind: 'staff.timesheets.task',\n resourceId: taskId,\n retryLastMutation,\n },\n })\n // This route does return the new version, so the token refreshes from\n // the response instead of costing a refetch.\n const updatedAt = typeof moved?.result?.updatedAt === 'string' ? moved.result.updatedAt : null\n if (updatedAt) versionRef.current = updatedAt\n else await refreshVersion()\n invalidate('task')\n onChanged?.()\n } catch (error) {\n reportError(error, t('staff.time_tracking.board.move.error', 'Could not move the task.'))\n } finally {\n setBusy(false)\n }\n })\n },\n [\n invalidate,\n onChanged,\n refreshVersion,\n reportError,\n retryLastMutation,\n runMutation,\n runSerialized,\n t,\n task,\n taskId,\n ],\n )\n\n const handleAssigneeChange = React.useCallback(\n (value: string) => {\n void saveTaskField({ assigneeStaffMemberId: value === '' ? null : value })\n },\n [saveTaskField],\n )\n\n const handleToggleSubtask = React.useCallback(\n async (child: BoardTask, done: boolean) => {\n const targetStatusId = done ? resolveDoneStatusId(statuses) : resolveDefaultStatusId(statuses)\n if (!targetStatusId) return\n setBusy(true)\n try {\n await runMutation({\n operation: () =>\n // The child's own version, not the parent's \u2014 this write touches the\n // child row.\n withScopedApiRequestHeaders(buildOptimisticLockHeader(child.updatedAt), () =>\n apiCallOrThrow(\n `/api/staff/timesheets/tasks/${encodeURIComponent(child.id)}/status`,\n {\n method: 'PATCH',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ taskStatusId: targetStatusId }),\n },\n {\n errorMessage: t(\n 'staff.time_tracking.taskDrawer.subtasks.toggleError',\n 'Could not change the subtask status.',\n ),\n },\n ),\n ),\n context: {\n formId: TASK_DRAWER_MUTATION_CONTEXT_ID,\n resourceKind: 'staff.timesheets.task',\n resourceId: child.id,\n retryLastMutation,\n },\n })\n invalidate('children')\n invalidate('task')\n onChanged?.()\n } catch (error) {\n reportError(\n error,\n t('staff.time_tracking.taskDrawer.subtasks.toggleError', 'Could not change the subtask status.'),\n )\n } finally {\n setBusy(false)\n }\n },\n [invalidate, onChanged, reportError, retryLastMutation, runMutation, statuses, t],\n )\n\n const handleAddSubtask = React.useCallback(async () => {\n const title = subtaskDraft.trim()\n if (!title || !projectId) {\n setComposingSubtask(false)\n setSubtaskDraft('')\n return\n }\n setBusy(true)\n try {\n await runMutation({\n operation: () =>\n apiCallOrThrow(\n '/api/staff/timesheets/tasks',\n {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({\n timeProjectId: projectId,\n parentTaskId: taskId,\n title,\n assigneeStaffMemberId: selfStaffMemberId,\n }),\n },\n {\n errorMessage: t('staff.time_tracking.taskDrawer.subtasks.addError', 'Could not add the subtask.'),\n },\n ),\n context: {\n formId: TASK_DRAWER_MUTATION_CONTEXT_ID,\n resourceKind: 'staff.timesheets.task',\n resourceId: taskId,\n retryLastMutation,\n },\n })\n setSubtaskDraft('')\n setComposingSubtask(false)\n invalidate('children')\n invalidate('task')\n onChanged?.()\n } catch (error) {\n reportError(error, t('staff.time_tracking.taskDrawer.subtasks.addError', 'Could not add the subtask.'))\n } finally {\n setBusy(false)\n }\n }, [\n invalidate,\n onChanged,\n projectId,\n reportError,\n retryLastMutation,\n runMutation,\n selfStaffMemberId,\n subtaskDraft,\n t,\n taskId,\n ])\n\n const handlePostComment = React.useCallback(\n async (body: string): Promise<boolean> => {\n setBusy(true)\n try {\n await runMutation({\n operation: () =>\n apiCallOrThrow(\n `/api/staff/timesheets/tasks/${encodeURIComponent(taskId)}/comments`,\n {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ body }),\n },\n { errorMessage: t('staff.time_tracking.taskDrawer.comments.error', 'Could not post the comment.') },\n ),\n context: {\n formId: TASK_DRAWER_MUTATION_CONTEXT_ID,\n resourceKind: 'staff.timesheets.task_comment',\n resourceId: taskId,\n retryLastMutation,\n },\n })\n invalidate('comments')\n return true\n } catch (error) {\n reportError(error, t('staff.time_tracking.taskDrawer.comments.error', 'Could not post the comment.'))\n return false\n } finally {\n setBusy(false)\n }\n },\n [invalidate, reportError, retryLastMutation, runMutation, t, taskId],\n )\n\n const handleTagChange = React.useCallback(\n async (tagId: string, assign: boolean) => {\n setBusy(true)\n const previous = tagIds\n setTagIds((current) =>\n assign ? Array.from(new Set([...current, tagId])) : current.filter((id) => id !== tagId),\n )\n try {\n await runMutation({\n operation: () =>\n apiCallOrThrow(\n '/api/staff/timesheets/tags/task-assignments',\n {\n method: assign ? 'POST' : 'DELETE',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ taskId, tagIds: [tagId] }),\n },\n { errorMessage: t('staff.time_tracking.taskDrawer.properties.tagError', 'Could not save the tags.') },\n ),\n context: {\n formId: TASK_DRAWER_MUTATION_CONTEXT_ID,\n resourceKind: 'staff.timesheets.task_tag',\n resourceId: taskId,\n retryLastMutation,\n },\n })\n onChanged?.()\n } catch (error) {\n setTagIds(previous)\n reportError(error, t('staff.time_tracking.taskDrawer.properties.tagError', 'Could not save the tags.'))\n } finally {\n setBusy(false)\n }\n },\n [onChanged, reportError, retryLastMutation, runMutation, t, tagIds, taskId],\n )\n\n /**\n * Creates the tag and hands it back so the picker can assign it through the\n * drawer's own `handleTagChange` \u2014 the task-assignments write is untouched, the\n * new row simply becomes assignable without leaving the drawer. The list is\n * invalidated rather than patched so the server-assigned colour arrives with it.\n */\n const createTag = React.useCallback(\n async (label: string): Promise<TagOption | null> => {\n try {\n const created = await apiCallOrThrow<Record<string, unknown>>(\n '/api/staff/timesheets/tags',\n {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ label, slug: slugifyProjectName(label).toLowerCase() }),\n },\n {\n errorMessage: t(\n 'staff.time_tracking.entryDialog.errors.createTag',\n 'Could not create the tag.',\n ),\n },\n )\n const id = typeof created?.result?.id === 'string' ? created.result.id : null\n if (!id) return null\n await queryClient.invalidateQueries({ queryKey: [...DRAWER_QUERY_ROOT, 'tags'] })\n return { id, label, color: null }\n } catch (error) {\n logger.warn('staff.time_tracking task drawer tag create failed', { err: error })\n flash(t('staff.time_tracking.entryDialog.errors.createTag', 'Could not create the tag.'), 'error')\n return null\n }\n },\n [queryClient, t],\n )\n\n const status = React.useMemo(\n () => statuses.find((entry) => entry.id === task?.taskStatusId) ?? null,\n [statuses, task?.taskStatusId],\n )\n\n const statusVariant = status?.isDone ? 'success' : status?.isDefault ? 'neutral' : 'info'\n\n const recentEntries = React.useMemo(() => entries.slice(0, RECENT_ENTRY_COUNT), [entries])\n\n const assignedTags = React.useMemo<TagOption[]>(() => {\n const byId = new Map(tagOptions.map((tag) => [tag.id, tag]))\n return tagIds.map((id) => byId.get(id) ?? { id, label: id, color: null })\n }, [tagIds, tagOptions])\n\n const availableTags = React.useMemo(\n () => tagOptions.filter((tag) => !tagIds.includes(tag.id)),\n [tagIds, tagOptions],\n )\n\n const doneStatusId = resolveDoneStatusId(statuses)\n\n const body = (() => {\n if (taskQuery.isLoading) {\n return <LoadingMessage label={t('staff.time_tracking.taskDrawer.loading', 'Loading the task\u2026')} />\n }\n if (taskQuery.isError) {\n return <ErrorMessage label={t('staff.time_tracking.taskDrawer.loadError', 'Could not load the task.')} />\n }\n if (!task) {\n return (\n <ErrorMessage\n label={t('staff.time_tracking.tasks.errors.notFound', 'Task not found or not accessible.')}\n />\n )\n }\n\n return (\n <div className=\"flex flex-col gap-6\">\n <TaskQuickLog\n onLog={handleQuickLog}\n logging={busy}\n timerRunningHere={timerRunningHere}\n elapsed={timerRunningHere ? formatElapsed(runningTimer.startedAt, now) : null}\n timerBusy={timerBusy}\n onStartTimer={() => { void handleStartTimer() }}\n onStopTimer={() => { void handleStopTimer() }}\n autoFocus={focusPanel === 'time'}\n disabled={!canManage}\n today={todayIsoDate()}\n />\n\n <section className=\"flex flex-col gap-2\" data-testid=\"task-drawer-logged\">\n <div className=\"flex items-baseline justify-between gap-3\">\n <h3 className=\"text-xs font-semibold uppercase tracking-wide text-muted-foreground\">\n {t('staff.time_tracking.taskDrawer.logged.title', 'Logged time')}\n </h3>\n <span className=\"font-mono text-base font-semibold tabular-nums text-foreground\" data-testid=\"task-drawer-logged-total\">\n {formatBoardMinutes(task.loggedMinutes)}\n </span>\n </div>\n <p className=\"text-xs text-muted-foreground\" data-testid=\"task-drawer-logged-split\">\n {t('staff.time_tracking.taskDrawer.logged.split', 'own {own} \u00B7 subtasks {children}', {\n own: formatSplitMinutes(task.ownMinutes),\n children: formatSplitMinutes(childMinutes(task.loggedMinutes, task.ownMinutes)),\n })}\n </p>\n <div className=\"flex flex-wrap items-center gap-2\">\n <Badge variant=\"success\">\n {t('staff.time_tracking.taskDrawer.logged.billable', 'billable {hours}', {\n hours: formatBoardMinutes(totals.billableMinutes),\n })}\n </Badge>\n <Badge variant=\"neutral\">\n {t('staff.time_tracking.taskDrawer.logged.nonBillable', 'non-billable {hours}', {\n hours: formatBoardMinutes(totals.nonBillableMinutes),\n })}\n </Badge>\n {canSeeMoney && totals.cost !== null ? (\n <Badge variant=\"info\" data-testid=\"task-drawer-cost\">\n {t('staff.time_tracking.taskDrawer.logged.cost', 'cost {amount}', {\n amount: formatCurrency(totals.cost, totals.currencyCode) ?? String(totals.cost),\n })}\n </Badge>\n ) : null}\n </div>\n {totals.partial ? (\n <p className=\"text-xs text-muted-foreground\">\n {t(\n 'staff.time_tracking.taskDrawer.logged.partialHint',\n 'The split is counted from the most recent entries in this project.',\n )}\n </p>\n ) : null}\n </section>\n\n {isChildTask ? null : (\n <section className=\"flex flex-col gap-3\" data-testid=\"task-drawer-subtasks\">\n <div className=\"flex items-baseline justify-between gap-3\">\n <h3 className=\"text-xs font-semibold uppercase tracking-wide text-muted-foreground\">\n {t('staff.time_tracking.taskDrawer.subtasks.title', 'Subtasks')}\n </h3>\n <span className=\"font-mono text-xs tabular-nums text-muted-foreground\">\n {t('staff.time_tracking.taskDrawer.subtasks.count', '{done}/{total}', {\n done: progress.done,\n total: subtaskTotal,\n })}\n </span>\n </div>\n {subtaskTotal > 0 ? (\n <Progress value={subtaskTotal > 0 ? (progress.done / subtaskTotal) * 100 : 0} max={100} />\n ) : null}\n {children.length === 0 ? (\n <p className=\"text-xs text-muted-foreground\">\n {t('staff.time_tracking.taskDrawer.subtasks.empty', 'No subtasks yet.')}\n </p>\n ) : (\n <ul className=\"flex flex-col\">\n {children.map((child) => {\n const done = (child.taskStatusId && doneStatusIds.has(child.taskStatusId)) || !!child.closedAt\n const assigneeName = child.assigneeStaffMemberId\n ? memberNames.get(child.assigneeStaffMemberId) ?? child.assigneeStaffMemberId\n : t('staff.time_tracking.taskDrawer.properties.unassigned', 'Unassigned')\n return (\n <li\n key={child.id}\n className=\"flex items-center gap-2.5 border-b border-border py-2 last:border-b-0\"\n data-subtask-id={child.id}\n >\n <Checkbox\n checked={done}\n disabled={!canManage || busy || !doneStatusId}\n aria-label={t('staff.time_tracking.taskDrawer.subtasks.toggle', 'Mark {title} as done', {\n title: child.title,\n })}\n onCheckedChange={(next) => { void handleToggleSubtask(child, next === true) }}\n />\n <span\n className={`min-w-0 flex-1 truncate text-sm ${done ? 'text-muted-foreground line-through' : 'text-foreground'}`}\n >\n {child.title}\n </span>\n <Avatar label={assigneeName} size=\"xs\" />\n <span className=\"font-mono text-xs tabular-nums text-muted-foreground\">\n {formatBoardMinutes(child.loggedMinutes)}\n </span>\n </li>\n )\n })}\n </ul>\n )}\n {!doneStatusId && children.length > 0 ? (\n <p className=\"text-xs text-muted-foreground\">\n {t(\n 'staff.time_tracking.taskDrawer.subtasks.noDoneColumn',\n 'This project has no done column, so a subtask has no way to close.',\n )}\n </p>\n ) : null}\n {canManage ? (\n composingSubtask ? (\n <Input\n autoFocus\n size=\"sm\"\n value={subtaskDraft}\n disabled={busy}\n onChange={(event) => setSubtaskDraft(event.target.value)}\n onKeyDown={(event) => {\n if (event.key === 'Enter') {\n event.preventDefault()\n void handleAddSubtask()\n }\n if (event.key === 'Escape') {\n event.preventDefault()\n event.stopPropagation()\n setComposingSubtask(false)\n setSubtaskDraft('')\n }\n }}\n placeholder={t('staff.time_tracking.taskDrawer.subtasks.placeholder', 'Subtask title')}\n aria-label={t('staff.time_tracking.taskDrawer.subtasks.add', 'Add subtask')}\n data-testid=\"task-drawer-subtask-input\"\n />\n ) : (\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n className=\"self-start\"\n onClick={() => setComposingSubtask(true)}\n data-testid=\"task-drawer-add-subtask\"\n >\n <Plus className=\"size-3.5\" aria-hidden=\"true\" />\n {t('staff.time_tracking.taskDrawer.subtasks.add', 'Add subtask')}\n </Button>\n )\n ) : null}\n </section>\n )}\n\n <section className=\"flex flex-col gap-3\" data-testid=\"task-drawer-entries\">\n <h3 className=\"text-xs font-semibold uppercase tracking-wide text-muted-foreground\">\n {t('staff.time_tracking.taskDrawer.entries.title', 'Time entries ({count})', { count: entries.length })}\n </h3>\n {recentEntries.length === 0 ? (\n <p className=\"text-xs text-muted-foreground\">\n {t('staff.time_tracking.taskDrawer.entries.empty', 'No time entries yet.')}\n </p>\n ) : (\n <ul className=\"flex flex-col\">\n {recentEntries.map((entry) => (\n <li\n key={entry.id}\n className=\"flex items-center gap-3 border-b border-border py-2 text-sm last:border-b-0\"\n data-entry-id={entry.id}\n >\n <span className=\"w-20 shrink-0 text-xs text-muted-foreground\">\n {formatDate(entry.date) ?? ''}\n </span>\n <span className=\"min-w-0 flex-1 truncate text-foreground\">\n {entry.description\n ?? t('staff.time_tracking.taskDrawer.entries.noDescription', 'No description')}\n </span>\n <span className=\"font-mono text-xs tabular-nums text-muted-foreground\">\n {formatBoardMinutes(entry.durationMinutes)}\n </span>\n </li>\n ))}\n </ul>\n )}\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n className=\"self-start\"\n onClick={() => router.push(`${ENTRIES_LIST_PATH}?taskId=${encodeURIComponent(taskId)}`)}\n >\n {t('staff.time_tracking.taskDrawer.entries.showAll', 'Show all')}\n </Button>\n </section>\n\n <TaskCommentThread\n comments={commentsQuery.data ?? []}\n loading={commentsQuery.isLoading}\n loadError={commentsQuery.isError}\n posting={busy}\n canComment={canManage}\n onPost={handlePostComment}\n />\n\n <section className=\"flex flex-col gap-3\" data-testid=\"task-drawer-properties\">\n <h3 className=\"text-xs font-semibold uppercase tracking-wide text-muted-foreground\">\n {t('staff.time_tracking.taskDrawer.properties.title', 'Properties')}\n </h3>\n <div className=\"grid grid-cols-1 gap-3 sm:grid-cols-2\">\n <div className=\"flex flex-col gap-1.5\">\n <span className=\"text-xs font-medium text-muted-foreground\">\n {t('staff.time_tracking.taskDrawer.properties.status', 'Status')}\n </span>\n <Select\n value={task.taskStatusId ?? undefined}\n disabled={!canManage || busy}\n onValueChange={handleStatusChange}\n >\n <SelectTrigger\n aria-label={t('staff.time_tracking.taskDrawer.properties.status', 'Status')}\n data-testid=\"task-drawer-status-select\"\n >\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {statuses.map((entry) => (\n <SelectItem key={entry.id} value={entry.id}>\n {entry.name}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n <div className=\"flex flex-col gap-1.5\">\n <span className=\"text-xs font-medium text-muted-foreground\">\n {t('staff.time_tracking.taskDrawer.properties.assignee', 'Assignee')}\n </span>\n <Select\n value={task.assigneeStaffMemberId ?? undefined}\n disabled={!canManage || busy}\n onValueChange={handleAssigneeChange}\n >\n <SelectTrigger\n aria-label={t('staff.time_tracking.taskDrawer.properties.assignee', 'Assignee')}\n data-testid=\"task-drawer-assignee-select\"\n >\n <SelectValue\n placeholder={t('staff.time_tracking.taskDrawer.properties.unassigned', 'Unassigned')}\n />\n </SelectTrigger>\n <SelectContent>\n {members.map((member) => (\n <SelectItem key={member.id} value={member.id}>\n {member.name}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n </div>\n\n <div className=\"flex flex-col gap-1.5\">\n <span className=\"text-xs font-medium text-muted-foreground\">\n {t('staff.time_tracking.taskDrawer.properties.tags', 'Tags')}\n </span>\n <div className=\"flex flex-wrap items-center gap-2\">\n {assignedTags.map((tag) => (\n <Badge key={tag.id} variant=\"neutral\" className=\"gap-1\" style={tagChipStyle(tag)}>\n {tag.label}\n {canManage ? (\n <button\n type=\"button\"\n aria-label={t('staff.time_tracking.taskDrawer.properties.removeTag', 'Remove tag {label}', {\n label: tag.label,\n })}\n onClick={() => { void handleTagChange(tag.id, false) }}\n >\n <X className=\"size-3\" aria-hidden=\"true\" />\n </button>\n ) : null}\n </Badge>\n ))}\n {canManage ? (\n <TagPicker\n available={availableTags}\n disabled={busy}\n onAssign={(id) => { void handleTagChange(id, true) }}\n onCreate={createTag}\n labels={{\n add: t('staff.time_tracking.taskDrawer.properties.addTag', 'Add tag'),\n create: (name) =>\n t('staff.time_tracking.entryDialog.createTag', 'Create \"{name}\"', { name }),\n empty: t('staff.time_tracking.entryDialog.noTags', 'No matching tag'),\n }}\n testIdPrefix=\"task-drawer\"\n />\n ) : null}\n </div>\n <p className=\"text-xs text-muted-foreground\">\n {t(\n 'staff.time_tracking.taskDrawer.properties.tagsPending',\n 'Tags already saved on this task appear here once the task list returns them.',\n )}\n </p>\n </div>\n <InjectionSpot\n spotId={extensionPoints.hosts.taskDetailSidebar.spotId}\n context={taskInjectionContext}\n data={task}\n />\n </section>\n\n <InjectionSpot\n spotId={extensionPoints.hosts.taskDetailTabs.spotId}\n context={taskInjectionContext}\n data={task}\n />\n </div>\n )\n })()\n\n const createdLabel = formatDate(createdAt)\n const headerLine = [\n projectName ?? null,\n createdLabel\n ? t('staff.time_tracking.taskDrawer.created', 'created {date}', { date: createdLabel })\n : null,\n ]\n .filter((part): part is string => !!part)\n .join(' \u00B7 ')\n\n return (\n <Drawer open={open} onOpenChange={onOpenChange}>\n <DrawerContent\n className=\"sm:max-w-xl\"\n closeAriaLabel={t('staff.time_tracking.taskDrawer.close', 'Close')}\n data-testid=\"task-drawer\"\n >\n <DrawerHeader>\n <div className=\"flex min-w-0 flex-1 flex-col gap-1.5\">\n <div className=\"flex items-center gap-2\">\n {status ? <Badge variant={statusVariant}>{status.name}</Badge> : null}\n {task?.reference ? (\n <span className=\"text-xs text-muted-foreground\">{task.reference}</span>\n ) : null}\n <InjectionSpot\n spotId={extensionPoints.hosts.taskDetailStatusBadges.spotId}\n context={taskInjectionContext}\n data={task}\n />\n </div>\n <DrawerTitle className=\"truncate\">{task?.title ?? ''}</DrawerTitle>\n <DrawerDescription>{headerLine}</DrawerDescription>\n <InjectionSpot\n spotId={extensionPoints.hosts.taskDetailHeader.spotId}\n context={taskInjectionContext}\n data={task}\n />\n </div>\n </DrawerHeader>\n <DrawerBody>{body}</DrawerBody>\n <DrawerFooter\n leading={\n <span className=\"text-xs text-muted-foreground\">\n {t('staff.time_tracking.taskDrawer.autoSave', 'Changes are saved automatically')}\n </span>\n }\n >\n <InjectionSpot\n spotId={extensionPoints.hosts.taskDetailFooter.spotId}\n context={taskInjectionContext}\n data={task}\n />\n <Button type=\"button\" variant=\"outline\" onClick={() => onOpenChange(false)}>\n {t('staff.time_tracking.taskDrawer.close', 'Close')}\n </Button>\n </DrawerFooter>\n {ConfirmDialogElement}\n </DrawerContent>\n </Drawer>\n )\n}\n\nexport default TaskDrawer\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { useRouter } from 'next/navigation'\nimport { useQuery, useQueryClient } from '@tanstack/react-query'\nimport { Plus, X } from 'lucide-react'\nimport {\n Drawer,\n DrawerBody,\n DrawerContent,\n DrawerDescription,\n DrawerFooter,\n DrawerHeader,\n DrawerTitle,\n} from '@open-mercato/ui/primitives/drawer'\nimport { Alert, AlertDescription } from '@open-mercato/ui/primitives/alert'\nimport { Avatar } from '@open-mercato/ui/primitives/avatar'\nimport { Badge } from '@open-mercato/ui/primitives/badge'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { Checkbox } from '@open-mercato/ui/primitives/checkbox'\nimport { Input } from '@open-mercato/ui/primitives/input'\nimport { Progress } from '@open-mercato/ui/primitives/progress'\nimport {\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from '@open-mercato/ui/primitives/select'\nimport { ErrorMessage, LoadingMessage } from '@open-mercato/ui/backend/detail'\nimport { apiCall, apiCallOrThrow, withScopedApiRequestHeaders } from '@open-mercato/ui/backend/utils/apiCall'\nimport {\n buildOptimisticLockHeader,\n extractOptimisticLockConflict,\n extractRecordLockConflict,\n} from '@open-mercato/ui/backend/utils/optimisticLock'\nimport { surfaceRecordConflict } from '@open-mercato/ui/backend/conflicts'\nimport { useBackendChrome } from '@open-mercato/ui/backend/BackendChromeProvider'\nimport { useConfirmDialog } from '@open-mercato/ui/backend/confirm-dialog'\nimport { useGuardedMutation } from '@open-mercato/ui/backend/injection/useGuardedMutation'\nimport { InjectionSpot } from '@open-mercato/ui/backend/injection/InjectionSpot'\nimport { extensionPoints } from '@open-mercato/core/modules/staff/extension-points'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { formatCurrency, formatDate } from '@open-mercato/ui/utils/format'\nimport { hasFeature } from '@open-mercato/shared/security/features'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { useOrganizationScopeVersion } from '@open-mercato/shared/lib/frontend/useOrganizationScope'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\nimport { TaskCommentThread, type CommentVisibility } from './TaskCommentThread'\nimport { useAppEvent } from '@open-mercato/ui/backend/injection/useAppEvent'\nimport { SupportRequestPanel } from './SupportRequestPanel'\nimport { SupportAttachmentsSection } from './SupportAttachmentsSection'\nimport { readSupportTicket, SUPPORT_ACTIVITY_EVENT, type SupportTicketView } from './supportTicketData'\nimport { TaskQuickLog, type QuickLogSubmission } from './TaskQuickLog'\nimport { TagPicker, tagChipStyle } from './TagPicker'\nimport { slugifyProjectName } from '../time-tracking/projectCode'\nimport type { TagOption } from './timeEntryDialogState'\nimport {\n computeSubtaskProgress,\n formatBoardMinutes,\n readItems,\n readString,\n readTotal,\n sortByPosition,\n toBoardStatus,\n toBoardTask,\n type BoardApiRow,\n type BoardStatus,\n type BoardTask,\n} from './kanbanBoardData'\nimport {\n childMinutes,\n formatElapsed,\n formatSplitMinutes,\n resolveDefaultStatusId,\n resolveDoneStatusId,\n summarizeEntries,\n toDrawerComment,\n toDrawerEntry,\n todayIsoDate,\n buildEntryClocks,\n isAssignmentEventForTask,\n TASK_ASSIGNMENT_CHANGED_EVENT,\n TASK_DRAWER_QUERY_ROOT,\n type DrawerComment,\n type DrawerEntry,\n} from './taskDrawerData'\n\nconst logger = createLogger('staff').child({ component: 'TaskDrawer' })\n\nconst TASK_ENTITY_ID = 'staff:staff_time_task'\n\ntype TaskDetailInjectionContext = {\n entityId: string\n recordId: string\n taskId: string\n timeProjectId: string | null\n retryLastMutation: () => Promise<boolean>\n}\n\nexport const TASK_DRAWER_MUTATION_CONTEXT_ID = 'staff.time_tracking.task_drawer'\n\n/**\n * The drawer's writes carry no `resourceKind`/`resourceId`, so the enterprise\n * record-lock widget leaves them alone: every property saves on its own (the\n * spec's per-field saves), and the `updated_at` version each save sends is what\n * guards concurrent edits. A lock taken per save, never released and never\n * resolvable from here, could only block the user (#8045, #8101). The `formId`\n * is still scoped to the task so no guarded-mutation state crosses tasks.\n */\nfunction taskDrawerFormId(taskId: string): string {\n return `${TASK_DRAWER_MUTATION_CONTEXT_ID}:${taskId}`\n}\n\nconst ASSIGNMENT_RACE_CODES = new Set(['task_already_claimed', 'task_assignment_changed'])\n\nfunction isAssignmentRace(error: unknown): boolean {\n if (extractOptimisticLockConflict(error) || extractRecordLockConflict(error)) return true\n if (!error || typeof error !== 'object') return false\n const code = (error as { code?: unknown }).code\n return typeof code === 'string' && ASSIGNMENT_RACE_CODES.has(code)\n}\nexport const RATES_FEATURE = 'staff.timesheets.rates.view'\nexport const TASKS_MANAGE_FEATURE = 'staff.timesheets.tasks.manage'\nexport const ENTRIES_LIST_PATH = '/backend/staff/time-tracking/entries'\n\nconst DRAWER_QUERY_ROOT = TASK_DRAWER_QUERY_ROOT\nconst DIRECTORY_PAGE_SIZE = 100\nconst CHILD_PAGE_SIZE = 100\nconst COMMENT_PAGE_SIZE = 50\nconst ENTRY_PAGE_SIZE = 100\n/** Bounded sweep: the entries route has no `taskId` filter (see the docblock). */\nconst MAX_ENTRY_PAGES = 3\nconst RECENT_ENTRY_COUNT = 5\nconst ELAPSED_TICK_MS = 1000\n\nexport type TaskDrawerProps = {\n taskId: string\n open: boolean\n onOpenChange: (open: boolean) => void\n /** Shown in the header line next to the created date; the host already knows it. */\n projectName?: string | null\n /** `?panel=time` \u2014 \"Add time\" on a board card lands with the quick log focused. */\n focusPanel?: string | null\n /** Lets the host (the board) refetch after a write that changed hours or status. */\n onChanged?: () => void\n}\n\ntype TaskAssignmentAbility = { canClaim: boolean; canRelease: boolean }\n\ntype LoadedTask = {\n task: BoardTask\n createdAt: string | null\n support: SupportTicketView | null\n assignment: TaskAssignmentAbility\n}\n\nfunction readTaskAssignmentAbility(row: BoardApiRow): TaskAssignmentAbility {\n const block = (row as Record<string, unknown>)._staffAssignment\n if (!block || typeof block !== 'object') return { canClaim: false, canRelease: false }\n const ability = block as Record<string, unknown>\n return { canClaim: ability.canClaim === true, canRelease: ability.canRelease === true }\n}\n\ntype RunningTimer = { entryId: string | null; taskId: string | null; startedAt: string | null }\n\ntype ProjectEntries = { rows: BoardApiRow[]; partial: boolean }\n\nasync function fetchProjectEntries(projectId: string): Promise<ProjectEntries> {\n const rows: BoardApiRow[] = []\n for (let page = 1; page <= MAX_ENTRY_PAGES; page += 1) {\n const call = await apiCall<Record<string, unknown>>(\n `/api/staff/timesheets/time-entries?projectId=${encodeURIComponent(projectId)}&page=${page}&pageSize=${ENTRY_PAGE_SIZE}&sortField=date&sortDir=desc`,\n )\n if (!call.ok) return { rows, partial: true }\n const items = readItems(call.result)\n rows.push(...items)\n if (items.length < ENTRY_PAGE_SIZE) return { rows, partial: false }\n if (rows.length >= readTotal(call.result, rows.length)) return { rows, partial: false }\n }\n return { rows, partial: true }\n}\n\n/**\n * The task drawer of screen 7 (T3.7, US-C3/C4/C5, US-D4).\n *\n * Four decisions carry the design intent:\n *\n * 1. **Subtasks are child tasks, and ticking one is a status move** (D-2, and\n * the decision that supersedes the mockup's note 4). There is no boolean on\n * a task, so the checkbox PATCHes the child to the project's first `is_done`\n * column and un-ticking returns it to the `is_default` one. Each row carries\n * its own assignee and its own hours because a subtask holds time like any\n * other task; the parent's headline is the inclusive rollup that already\n * contains them.\n * 2. **Depth is capped at one.** A child task's drawer renders no subtask\n * section at all: the server answers `400 subtask_depth_exceeded`, so an\n * \"add subtask\" affordance there could only ever fail.\n * 3. **Properties save per field, so each write carries its own version.** A\n * blur PATCHes with `buildOptimisticLockHeader(versionRef.current)` and then refreshes\n * the token \u2014 from the status route's `updatedAt` when it returns one, by\n * refetching the row when it does not (the CRUD update answers `{ok:true}`).\n * Saves are chained on `saveChainRef` so the second field never leaves with\n * the version the first one just invalidated.\n * 4. **One timer at a time** (note 2, US-D4). Starting here while a timer runs\n * on another task asks first, then stops that one before starting this one.\n *\n * Two known gaps, both other tasks' work, are handled honestly rather than faked:\n * the task projection carries no tag ids yet (T3.10), so the tag row can add but\n * cannot list what the server already has; and the entries route has no `taskId`\n * filter, so the recent-entries list and the billable split come from a bounded\n * sweep of the project's entries and say so when the bound is hit.\n */\nexport function TaskDrawer({\n taskId,\n open,\n onOpenChange,\n projectName,\n focusPanel,\n onChanged,\n}: TaskDrawerProps) {\n const t = useT()\n const router = useRouter()\n const queryClient = useQueryClient()\n const scopeVersion = useOrganizationScopeVersion()\n const { payload } = useBackendChrome()\n const { confirm, ConfirmDialogElement } = useConfirmDialog()\n\n const canSeeMoney = hasFeature(payload?.grantedFeatures, RATES_FEATURE)\n const canManage = hasFeature(payload?.grantedFeatures, TASKS_MANAGE_FEATURE)\n\n const [tagIds, setTagIds] = React.useState<string[]>([])\n const [subtaskDraft, setSubtaskDraft] = React.useState('')\n const [composingSubtask, setComposingSubtask] = React.useState(false)\n const [busy, setBusy] = React.useState(false)\n const [timerBusy, setTimerBusy] = React.useState(false)\n const [now, setNow] = React.useState(() => Date.now())\n\n const saveChainRef = React.useRef<Promise<void>>(Promise.resolve())\n /**\n * The lock token lives in a ref, not in state: a save queued behind another one\n * must read the version at the moment it *runs*, not the one its closure was\n * built with, or a second blur landing before a re-render would still leave\n * with the token the first blur just invalidated.\n */\n const versionRef = React.useRef<string | null>(null)\n const [notice, setNotice] = React.useState<{ status: 'warning' | 'error'; message: string } | null>(null)\n\n const { runMutation: runGuardedMutation, retryLastMutation } = useGuardedMutation<{\n formId: string\n retryLastMutation: () => Promise<boolean>\n }>({\n contextId: TASK_DRAWER_MUTATION_CONTEXT_ID,\n blockedMessage: t('staff.time_tracking.taskDrawer.properties.saveError', 'Could not save the change.'),\n })\n const runMutation = React.useCallback<typeof runGuardedMutation>(\n (input) => {\n setNotice(null)\n return runGuardedMutation(input)\n },\n [runGuardedMutation],\n )\n\n const taskQuery = useQuery<LoadedTask | null>({\n queryKey: [...DRAWER_QUERY_ROOT, 'task', `scope:${scopeVersion}`, taskId],\n enabled: open && taskId.length > 0,\n staleTime: 5_000,\n queryFn: async () => {\n const call = await apiCall<Record<string, unknown>>(\n `/api/staff/timesheets/tasks?id=${encodeURIComponent(taskId)}&pageSize=1`,\n )\n if (!call.ok) throw new Error('[internal] task request failed')\n const row = readItems(call.result)[0]\n if (!row) return null\n const parsed = toBoardTask(row)\n return parsed\n ? {\n task: parsed,\n createdAt: readString(row, 'created_at', 'createdAt'),\n support: readSupportTicket(row),\n assignment: readTaskAssignmentAbility(row),\n }\n : null\n },\n })\n\n const task = taskQuery.data?.task ?? null\n const createdAt = taskQuery.data?.createdAt ?? null\n const support = taskQuery.data?.support ?? null\n const projectId = task?.timeProjectId ?? null\n\n const taskInjectionContext = React.useMemo<TaskDetailInjectionContext>(\n () => ({\n entityId: TASK_ENTITY_ID,\n recordId: taskId,\n taskId,\n timeProjectId: projectId,\n retryLastMutation,\n }),\n [projectId, retryLastMutation, taskId],\n )\n const isChildTask = !!task?.parentTaskId\n\n React.useEffect(() => {\n if (!task) return\n versionRef.current = task.updatedAt\n setTagIds(task.tagIds)\n }, [task])\n\n React.useEffect(() => {\n setNotice(null)\n }, [taskId])\n\n const statusesQuery = useQuery<BoardStatus[]>({\n queryKey: [...DRAWER_QUERY_ROOT, 'statuses', `scope:${scopeVersion}`, projectId ?? 'none'],\n enabled: open && !!projectId,\n staleTime: 30_000,\n queryFn: async () => {\n if (!projectId) return []\n const call = await apiCall<Record<string, unknown>>(\n `/api/staff/timesheets/task-statuses?timeProjectId=${encodeURIComponent(projectId)}&page=1&pageSize=${DIRECTORY_PAGE_SIZE}&sortField=position&sortDir=asc`,\n )\n if (!call.ok) return []\n const statuses = readItems(call.result)\n .map(toBoardStatus)\n .filter((status): status is BoardStatus => status !== null)\n return sortByPosition(statuses)\n },\n })\n\n const statuses = React.useMemo(() => statusesQuery.data ?? [], [statusesQuery.data])\n\n const childrenQuery = useQuery<BoardTask[]>({\n queryKey: [...DRAWER_QUERY_ROOT, 'children', `scope:${scopeVersion}`, taskId],\n enabled: open && !isChildTask && !!task,\n staleTime: 10_000,\n queryFn: async () => {\n const call = await apiCall<Record<string, unknown>>(\n `/api/staff/timesheets/tasks?parentTaskId=${encodeURIComponent(taskId)}&page=1&pageSize=${CHILD_PAGE_SIZE}&sortField=position&sortDir=asc`,\n )\n if (!call.ok) return []\n const rows = readItems(call.result)\n .map(toBoardTask)\n .filter((row): row is BoardTask => row !== null)\n return sortByPosition(rows)\n },\n })\n\n const children = React.useMemo(() => childrenQuery.data ?? [], [childrenQuery.data])\n\n const commentsQuery = useQuery<DrawerComment[]>({\n queryKey: [...DRAWER_QUERY_ROOT, 'comments', `scope:${scopeVersion}`, taskId],\n enabled: open && taskId.length > 0,\n staleTime: 10_000,\n queryFn: async () => {\n const call = await apiCall<Record<string, unknown>>(\n `/api/staff/timesheets/tasks/${encodeURIComponent(taskId)}/comments?page=1&pageSize=${COMMENT_PAGE_SIZE}`,\n )\n if (!call.ok) throw new Error('[internal] comments request failed')\n return readItems(call.result)\n .map(toDrawerComment)\n .filter((comment): comment is DrawerComment => comment !== null)\n },\n })\n\n const entriesQuery = useQuery<ProjectEntries>({\n queryKey: [...DRAWER_QUERY_ROOT, 'entries', `scope:${scopeVersion}`, projectId ?? 'none'],\n enabled: open && !!projectId,\n staleTime: 10_000,\n queryFn: async () => (projectId ? fetchProjectEntries(projectId) : { rows: [], partial: false }),\n })\n\n const selfQuery = useQuery<string | null>({\n queryKey: [...DRAWER_QUERY_ROOT, 'self', `scope:${scopeVersion}`],\n enabled: open,\n staleTime: 300_000,\n queryFn: async () => {\n const call = await apiCall<{ member?: { id?: string | null } | null }>('/api/staff/team-members/self')\n return call.ok ? call.result?.member?.id ?? null : null\n },\n })\n\n const selfStaffMemberId = selfQuery.data ?? null\n\n const membersQuery = useQuery<{ id: string; name: string }[]>({\n queryKey: [...DRAWER_QUERY_ROOT, 'members', `scope:${scopeVersion}`],\n enabled: open,\n staleTime: 300_000,\n queryFn: async () => {\n const call = await apiCall<Record<string, unknown>>(\n `/api/staff/team-members?page=1&pageSize=${DIRECTORY_PAGE_SIZE}&isActive=true`,\n )\n if (!call.ok) return []\n return readItems(call.result)\n .map((row) => {\n const id = readString(row, 'id')\n if (!id) return null\n return { id, name: readString(row, 'display_name', 'displayName') ?? id }\n })\n .filter((member): member is { id: string; name: string } => member !== null)\n },\n })\n\n const members = React.useMemo(() => membersQuery.data ?? [], [membersQuery.data])\n\n const memberNames = React.useMemo(() => {\n const map = new Map<string, string>()\n for (const member of members) map.set(member.id, member.name)\n return map\n }, [members])\n\n const tagsQuery = useQuery<TagOption[]>({\n queryKey: [...DRAWER_QUERY_ROOT, 'tags', `scope:${scopeVersion}`],\n enabled: open,\n staleTime: 300_000,\n queryFn: async () => {\n const call = await apiCall<Record<string, unknown>>(\n `/api/staff/timesheets/tags?page=1&pageSize=${DIRECTORY_PAGE_SIZE}&sortField=label&sortDir=asc`,\n )\n if (!call.ok) return []\n return readItems(call.result)\n .map((row) => {\n const id = readString(row, 'id')\n if (!id) return null\n // The colour has to travel with the tag or the drawer's chips render\n // grey while the entry dialog's render tinted from the same rows.\n return { id, label: readString(row, 'label') ?? id, color: readString(row, 'color') }\n })\n .filter((tag): tag is TagOption => tag !== null)\n },\n })\n\n const tagOptions = React.useMemo(() => tagsQuery.data ?? [], [tagsQuery.data])\n\n const runningTimerQuery = useQuery<RunningTimer>({\n queryKey: [...DRAWER_QUERY_ROOT, 'running-timer', `scope:${scopeVersion}`, selfStaffMemberId ?? 'none'],\n enabled: open && !!selfStaffMemberId,\n staleTime: 15_000,\n refetchInterval: 30_000,\n queryFn: async () => {\n if (!selfStaffMemberId) return { entryId: null, taskId: null, startedAt: null }\n const call = await apiCall<Record<string, unknown>>(\n `/api/staff/timesheets/time-entries?staffMemberId=${encodeURIComponent(selfStaffMemberId)}&running=true&pageSize=${ENTRY_PAGE_SIZE}`,\n )\n if (!call.ok) return { entryId: null, taskId: null, startedAt: null }\n for (const row of readItems(call.result)) {\n const startedAt = readString(row, 'started_at', 'startedAt')\n const endedAt = readString(row, 'ended_at', 'endedAt')\n if (!startedAt || endedAt) continue\n return {\n entryId: readString(row, 'id'),\n taskId: readString(row, 'task_id', 'taskId'),\n startedAt,\n }\n }\n return { entryId: null, taskId: null, startedAt: null }\n },\n })\n\n const runningTimer = runningTimerQuery.data ?? { entryId: null, taskId: null, startedAt: null }\n const timerRunningHere = !!runningTimer.entryId && runningTimer.taskId === taskId\n\n React.useEffect(() => {\n if (!open || !timerRunningHere) return\n const handle = setInterval(() => setNow(Date.now()), ELAPSED_TICK_MS)\n return () => clearInterval(handle)\n }, [open, timerRunningHere])\n\n const doneStatusIds = React.useMemo(\n () => new Set(statuses.filter((status) => status.isDone).map((status) => status.id)),\n [statuses],\n )\n\n // `doneChildCount` does not exist on the API yet, so the drawer derives it the\n // way the board does \u2014 from the children it already fetched for the list. One\n // extra page of rows per open, no extra request.\n const progress = React.useMemo(\n () => computeSubtaskProgress(children, doneStatusIds).get(taskId) ?? { total: 0, done: 0 },\n [children, doneStatusIds, taskId],\n )\n\n const subtaskTotal = Math.max(progress.total, task?.childCount ?? 0)\n\n const taskIdsForEntries = React.useMemo(() => {\n const ids = new Set<string>([taskId])\n for (const child of children) ids.add(child.id)\n return ids\n }, [children, taskId])\n\n const entries = React.useMemo<DrawerEntry[]>(() => {\n const rows = entriesQuery.data?.rows ?? []\n return rows\n .map(toDrawerEntry)\n .filter((entry): entry is DrawerEntry => entry !== null && !!entry.taskId && taskIdsForEntries.has(entry.taskId))\n }, [entriesQuery.data, taskIdsForEntries])\n\n const totals = React.useMemo(\n () => summarizeEntries(entries, entriesQuery.data?.partial ?? false),\n [entries, entriesQuery.data],\n )\n\n const invalidate = React.useCallback(\n (part: 'task' | 'children' | 'entries' | 'comments' | 'attachments') => {\n queryClient.invalidateQueries({ queryKey: [...DRAWER_QUERY_ROOT, part] }).catch(() => {})\n },\n [queryClient],\n )\n\n const refreshVersion = React.useCallback(async (): Promise<void> => {\n const refreshed = await taskQuery.refetch()\n const updatedAt = refreshed.data?.task.updatedAt ?? null\n if (updatedAt) versionRef.current = updatedAt\n }, [taskQuery])\n\n /**\n * A failed save is reported inside the drawer, which covers the page-level\n * conflict bar. It is a conflict when the server says so, or when the task\n * moved underneath the save (a guard can refuse it before any request leaves);\n * either way the drawer refetches, so the next attempt starts from what it shows.\n */\n const reportError = React.useCallback(\n (error: unknown, fallback: string) => {\n const conflictNotice = {\n status: 'warning' as const,\n message: t(\n 'staff.time_tracking.taskDrawer.conflict',\n 'Someone else changed this task. It now shows the latest version. Check it and try again.',\n ),\n }\n const seenVersion = versionRef.current\n invalidate('children')\n if (surfaceRecordConflict(error, t)) {\n setNotice(conflictNotice)\n invalidate('task')\n return\n }\n logger.error('staff.time_tracking task drawer mutation failed', { err: error })\n setNotice({ status: 'error', message: error instanceof Error && error.message ? error.message : fallback })\n void taskQuery.refetch().then((refreshed) => {\n const currentVersion = refreshed.data?.task.updatedAt ?? null\n if (seenVersion && currentVersion && currentVersion !== seenVersion) setNotice(conflictNotice)\n })\n },\n [invalidate, t, taskQuery],\n )\n\n const handleQuickLog = React.useCallback(\n async ({ minutes, description, date, isBillable, startClock, endClock }: QuickLogSubmission): Promise<boolean> => {\n if (!selfStaffMemberId || !projectId) {\n flash(t('staff.time_tracking.taskDrawer.quickLog.error', 'Could not save the time entry.'), 'error')\n return false\n }\n setBusy(true)\n try {\n await runMutation({\n operation: () =>\n apiCallOrThrow(\n '/api/staff/timesheets/time-entries',\n {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({\n // US-C5 defaults \u2014 today, me, billable \u2014 unless the Details block\n // overrode them. The person is never overridable here: logging\n // for somebody else is a different act with different permissions.\n staffMemberId: selfStaffMemberId,\n date,\n durationMinutes: minutes,\n taskId,\n timeProjectId: projectId,\n isBillable,\n description,\n // The clocks are sent as a pair or not at all. A lone\n // `started_at` is indistinguishable from a running timer \u2014\n // `started_at IS NOT NULL AND ended_at IS NULL` is exactly how\n // the list identifies one \u2014 so an entry with a start and no end\n // shows up as a timer running since that moment, and stopping it\n // fails because no timer segment was ever opened for it.\n //\n // The duration is authoritative here, so the missing half is\n // derived from it rather than left blank: the same 2-of-3\n // arithmetic the full entry form does.\n ...buildEntryClocks({ date, startClock, endClock, minutes }),\n source: 'manual',\n }),\n },\n { errorMessage: t('staff.time_tracking.taskDrawer.quickLog.error', 'Could not save the time entry.') },\n ),\n context: {\n formId: taskDrawerFormId(taskId),\n retryLastMutation,\n },\n })\n flash(t('staff.time_tracking.taskDrawer.quickLog.saved', 'Time logged.'), 'success')\n invalidate('task')\n invalidate('entries')\n onChanged?.()\n return true\n } catch (error) {\n reportError(error, t('staff.time_tracking.taskDrawer.quickLog.error', 'Could not save the time entry.'))\n return false\n } finally {\n setBusy(false)\n }\n },\n [invalidate, onChanged, projectId, reportError, retryLastMutation, runMutation, selfStaffMemberId, t, taskId],\n )\n\n const stopTimerEntry = React.useCallback(\n async (entryId: string) => {\n await runMutation({\n operation: () =>\n apiCallOrThrow(\n `/api/staff/timesheets/time-entries/${encodeURIComponent(entryId)}/timer-stop`,\n { method: 'POST' },\n { errorMessage: t('staff.time_tracking.board.timer.stopError', 'Could not stop the timer.') },\n ),\n context: {\n formId: taskDrawerFormId(taskId),\n retryLastMutation,\n },\n })\n },\n [retryLastMutation, runMutation, t, taskId],\n )\n\n const handleStopTimer = React.useCallback(async () => {\n if (!runningTimer.entryId) return\n setTimerBusy(true)\n try {\n await stopTimerEntry(runningTimer.entryId)\n await runningTimerQuery.refetch()\n invalidate('task')\n invalidate('entries')\n onChanged?.()\n } catch (error) {\n reportError(error, t('staff.time_tracking.board.timer.stopError', 'Could not stop the timer.'))\n } finally {\n setTimerBusy(false)\n }\n }, [invalidate, onChanged, reportError, runningTimer.entryId, runningTimerQuery, stopTimerEntry, t])\n\n const handleStartTimer = React.useCallback(async () => {\n if (!selfStaffMemberId || !projectId) return\n // Note 2 / US-D4: one timer at a time. A timer running on another task is\n // stopped only after the person says so \u2014 never silently.\n if (runningTimer.entryId && runningTimer.taskId !== taskId) {\n const confirmed = await confirm({\n title: t('staff.time_tracking.taskDrawer.timer.switchTitle', 'A timer is running on another task'),\n text: t(\n 'staff.time_tracking.taskDrawer.timer.switchText',\n 'Only one timer can run at a time. Stop the other one and start it here?',\n ),\n })\n if (!confirmed) return\n }\n setTimerBusy(true)\n try {\n if (runningTimer.entryId && runningTimer.taskId !== taskId) {\n await stopTimerEntry(runningTimer.entryId)\n }\n const started = await runMutation({\n operation: () =>\n apiCallOrThrow<Record<string, unknown>>(\n '/api/staff/timesheets/time-entries/start-timer',\n {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({\n staffMemberId: selfStaffMemberId,\n timeProjectId: projectId,\n date: todayIsoDate(),\n notes: task?.title ?? null,\n }),\n },\n { errorMessage: t('staff.time_tracking.board.timer.startError', 'Could not start the timer.') },\n ),\n context: {\n formId: taskDrawerFormId(taskId),\n retryLastMutation,\n },\n })\n const entryId = typeof started?.result?.id === 'string' ? started.result.id : null\n if (entryId) {\n // `start-timer` is atomic but takes no task, so the link is a second\n // write \u2014 the board does the same. A failure leaves a running,\n // project-level timer rather than no timer at all.\n const linked = await apiCall('/api/staff/timesheets/time-entries', {\n method: 'PUT',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ id: entryId, taskId }),\n })\n if (!linked.ok) {\n flash(\n t(\n 'staff.time_tracking.board.timer.linkWarning',\n 'The timer is running but is not linked to this task yet.',\n ),\n 'warning',\n )\n }\n }\n await runningTimerQuery.refetch()\n onChanged?.()\n } catch (error) {\n reportError(error, t('staff.time_tracking.board.timer.startError', 'Could not start the timer.'))\n } finally {\n setTimerBusy(false)\n }\n }, [\n confirm,\n onChanged,\n projectId,\n reportError,\n retryLastMutation,\n runMutation,\n runningTimer.entryId,\n runningTimer.taskId,\n runningTimerQuery,\n selfStaffMemberId,\n stopTimerEntry,\n t,\n task?.title,\n taskId,\n ])\n\n /**\n * Property saves are serialized. Each one leaves with the version the previous\n * one produced, so the second blur cannot 409 against the first.\n */\n const runSerialized = React.useCallback(async (job: () => Promise<void>): Promise<void> => {\n const chained = saveChainRef.current.then(job, job)\n saveChainRef.current = chained.catch(() => {})\n return chained\n }, [])\n\n const saveTaskField = React.useCallback(\n async (patch: Record<string, unknown>) => {\n if (!task) return\n setBusy(true)\n try {\n await runSerialized(async () => {\n await runMutation({\n operation: () =>\n withScopedApiRequestHeaders(buildOptimisticLockHeader(versionRef.current), () =>\n apiCallOrThrow(\n '/api/staff/timesheets/tasks',\n {\n method: 'PUT',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ id: taskId, ...patch }),\n },\n {\n errorMessage: t(\n 'staff.time_tracking.taskDrawer.properties.saveError',\n 'Could not save the change.',\n ),\n },\n ),\n ),\n context: {\n formId: taskDrawerFormId(taskId),\n retryLastMutation,\n },\n })\n // The CRUD update answers `{ok:true}`, so the new version comes from\n // the row itself \u2014 and it must land before the next field saves.\n await refreshVersion()\n })\n onChanged?.()\n } catch (error) {\n reportError(error, t('staff.time_tracking.taskDrawer.properties.saveError', 'Could not save the change.'))\n } finally {\n setBusy(false)\n }\n },\n [onChanged, refreshVersion, reportError, retryLastMutation, runMutation, runSerialized, t, task, taskId],\n )\n\n const handleStatusChange = React.useCallback(\n (statusId: string) => {\n if (!task || statusId === task.taskStatusId) return\n setBusy(true)\n void runSerialized(async () => {\n try {\n const moved = await runMutation({\n operation: () =>\n withScopedApiRequestHeaders(buildOptimisticLockHeader(versionRef.current), () =>\n apiCallOrThrow<Record<string, unknown>>(\n `/api/staff/timesheets/tasks/${encodeURIComponent(taskId)}/status`,\n {\n method: 'PATCH',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ taskStatusId: statusId }),\n },\n { errorMessage: t('staff.time_tracking.board.move.error', 'Could not move the task.') },\n ),\n ),\n context: {\n formId: taskDrawerFormId(taskId),\n retryLastMutation,\n },\n })\n // This route does return the new version, so the token refreshes from\n // the response instead of costing a refetch.\n const updatedAt = typeof moved?.result?.updatedAt === 'string' ? moved.result.updatedAt : null\n if (updatedAt) versionRef.current = updatedAt\n else await refreshVersion()\n invalidate('task')\n onChanged?.()\n } catch (error) {\n reportError(error, t('staff.time_tracking.board.move.error', 'Could not move the task.'))\n } finally {\n setBusy(false)\n }\n })\n },\n [\n invalidate,\n onChanged,\n refreshVersion,\n reportError,\n retryLastMutation,\n runMutation,\n runSerialized,\n t,\n task,\n taskId,\n ],\n )\n\n const handleAssigneeChange = React.useCallback(\n (value: string) => {\n void saveTaskField({ assigneeStaffMemberId: value === '' ? null : value })\n },\n [saveTaskField],\n )\n\n const canClaim = taskQuery.data?.assignment.canClaim === true\n const canRelease = taskQuery.data?.assignment.canRelease === true\n\n const runAssignmentAction = React.useCallback(\n (action: 'claim' | 'release') => {\n if (!task) return\n const errorMessage =\n action === 'claim'\n ? t('staff.time_tracking.taskDrawer.assignment.claimError', 'Could not claim the task.')\n : t('staff.time_tracking.taskDrawer.assignment.releaseError', 'Could not release the task.')\n setBusy(true)\n void runSerialized(async () => {\n try {\n // No optimistic-lock header: claim and release are atomic conditional\n // updates on the assignee, so a lost race answers with its own reason\n // instead of a generic \"record changed\".\n const done = await runMutation({\n operation: () =>\n apiCallOrThrow<Record<string, unknown>>(\n `/api/staff/timesheets/tasks/${encodeURIComponent(taskId)}/${action}`,\n { method: 'POST' },\n { errorMessage },\n ),\n context: {\n formId: taskDrawerFormId(taskId),\n retryLastMutation,\n },\n })\n const updatedAt = typeof done?.result?.updatedAt === 'string' ? done.result.updatedAt : null\n if (updatedAt) versionRef.current = updatedAt\n else await refreshVersion()\n invalidate('task')\n flash(\n action === 'claim'\n ? t('staff.time_tracking.taskDrawer.assignment.claimed', 'Task claimed.')\n : t('staff.time_tracking.taskDrawer.assignment.released', 'Task released.'),\n 'success',\n )\n onChanged?.()\n } catch (error) {\n if (!isAssignmentRace(error)) {\n reportError(error, errorMessage)\n return\n }\n const refreshed = await taskQuery.refetch()\n const assigneeId = refreshed.data?.task.assigneeStaffMemberId ?? null\n const takenByOther = action === 'claim' && assigneeId !== null && assigneeId !== selfStaffMemberId\n setNotice({\n status: 'warning',\n message: takenByOther\n ? t('staff.timesheets.errors.taskAlreadyClaimed', 'Someone else already took this task.')\n : t(\n 'staff.time_tracking.taskDrawer.conflict',\n 'Someone else changed this task. It now shows the latest version. Check it and try again.',\n ),\n })\n invalidate('task')\n onChanged?.()\n } finally {\n setBusy(false)\n }\n })\n },\n [\n invalidate,\n onChanged,\n refreshVersion,\n reportError,\n retryLastMutation,\n runMutation,\n runSerialized,\n selfStaffMemberId,\n t,\n task,\n taskId,\n taskQuery,\n ],\n )\n\n const handleToggleSubtask = React.useCallback(\n async (child: BoardTask, done: boolean) => {\n const targetStatusId = done ? resolveDoneStatusId(statuses) : resolveDefaultStatusId(statuses)\n if (!targetStatusId) return\n setBusy(true)\n try {\n await runMutation({\n operation: () =>\n // The child's own version, not the parent's \u2014 this write touches the\n // child row.\n withScopedApiRequestHeaders(buildOptimisticLockHeader(child.updatedAt), () =>\n apiCallOrThrow(\n `/api/staff/timesheets/tasks/${encodeURIComponent(child.id)}/status`,\n {\n method: 'PATCH',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ taskStatusId: targetStatusId }),\n },\n {\n errorMessage: t(\n 'staff.time_tracking.taskDrawer.subtasks.toggleError',\n 'Could not change the subtask status.',\n ),\n },\n ),\n ),\n context: {\n formId: taskDrawerFormId(taskId),\n retryLastMutation,\n },\n })\n invalidate('children')\n invalidate('task')\n onChanged?.()\n } catch (error) {\n reportError(\n error,\n t('staff.time_tracking.taskDrawer.subtasks.toggleError', 'Could not change the subtask status.'),\n )\n } finally {\n setBusy(false)\n }\n },\n [invalidate, onChanged, reportError, retryLastMutation, runMutation, statuses, t, taskId],\n )\n\n const handleAddSubtask = React.useCallback(async () => {\n const title = subtaskDraft.trim()\n if (!title || !projectId) {\n setComposingSubtask(false)\n setSubtaskDraft('')\n return\n }\n setBusy(true)\n try {\n await runMutation({\n operation: () =>\n apiCallOrThrow(\n '/api/staff/timesheets/tasks',\n {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({\n timeProjectId: projectId,\n parentTaskId: taskId,\n title,\n assigneeStaffMemberId: selfStaffMemberId,\n }),\n },\n {\n errorMessage: t('staff.time_tracking.taskDrawer.subtasks.addError', 'Could not add the subtask.'),\n },\n ),\n context: {\n formId: taskDrawerFormId(taskId),\n retryLastMutation,\n },\n })\n setSubtaskDraft('')\n setComposingSubtask(false)\n invalidate('children')\n invalidate('task')\n onChanged?.()\n } catch (error) {\n reportError(error, t('staff.time_tracking.taskDrawer.subtasks.addError', 'Could not add the subtask.'))\n } finally {\n setBusy(false)\n }\n }, [\n invalidate,\n onChanged,\n projectId,\n reportError,\n retryLastMutation,\n runMutation,\n selfStaffMemberId,\n subtaskDraft,\n t,\n taskId,\n ])\n\n useAppEvent(\n SUPPORT_ACTIVITY_EVENT,\n (event) => {\n const eventTaskId = (event.payload as { taskId?: unknown } | undefined)?.taskId\n if (!open || eventTaskId !== taskId) return\n invalidate('task')\n invalidate('comments')\n invalidate('attachments')\n },\n [invalidate, open, taskId],\n )\n\n useAppEvent(\n TASK_ASSIGNMENT_CHANGED_EVENT,\n (event) => {\n if (!open || !isAssignmentEventForTask(event.payload, taskId)) return\n invalidate('task')\n },\n [invalidate, open, taskId],\n )\n\n const handleMakeInternal = React.useCallback(\n async (comment: DrawerComment): Promise<void> => {\n setBusy(true)\n try {\n await runMutation({\n operation: () =>\n withScopedApiRequestHeaders(buildOptimisticLockHeader(comment.updatedAt), () =>\n apiCallOrThrow(\n `/api/staff/timesheets/tasks/${encodeURIComponent(taskId)}/comments`,\n {\n method: 'PUT',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ id: comment.id, visibility: 'internal' }),\n },\n { errorMessage: t('staff.time_tracking.taskComments.makeInternalError', 'Could not change the reply.') },\n ),\n ),\n context: {\n formId: taskDrawerFormId(taskId),\n retryLastMutation,\n },\n })\n invalidate('comments')\n } catch (error) {\n if (surfaceRecordConflict(error, t)) {\n invalidate('comments')\n return\n }\n reportError(error, t('staff.time_tracking.taskComments.makeInternalError', 'Could not change the reply.'))\n } finally {\n setBusy(false)\n }\n },\n [invalidate, reportError, retryLastMutation, runMutation, t, taskId],\n )\n\n const handlePostComment = React.useCallback(\n async (body: string, visibility: CommentVisibility = 'internal'): Promise<boolean> => {\n setBusy(true)\n try {\n await runMutation({\n operation: () =>\n apiCallOrThrow(\n `/api/staff/timesheets/tasks/${encodeURIComponent(taskId)}/comments`,\n {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ body, visibility }),\n },\n { errorMessage: t('staff.time_tracking.taskDrawer.comments.error', 'Could not post the comment.') },\n ),\n context: {\n formId: taskDrawerFormId(taskId),\n retryLastMutation,\n },\n })\n invalidate('comments')\n return true\n } catch (error) {\n reportError(error, t('staff.time_tracking.taskDrawer.comments.error', 'Could not post the comment.'))\n return false\n } finally {\n setBusy(false)\n }\n },\n [invalidate, reportError, retryLastMutation, runMutation, t, taskId],\n )\n\n const handleTagChange = React.useCallback(\n async (tagId: string, assign: boolean) => {\n setBusy(true)\n const previous = tagIds\n setTagIds((current) =>\n assign ? Array.from(new Set([...current, tagId])) : current.filter((id) => id !== tagId),\n )\n try {\n await runMutation({\n operation: () =>\n apiCallOrThrow(\n '/api/staff/timesheets/tags/task-assignments',\n {\n method: assign ? 'POST' : 'DELETE',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ taskId, tagIds: [tagId] }),\n },\n { errorMessage: t('staff.time_tracking.taskDrawer.properties.tagError', 'Could not save the tags.') },\n ),\n context: {\n formId: taskDrawerFormId(taskId),\n retryLastMutation,\n },\n })\n onChanged?.()\n } catch (error) {\n setTagIds(previous)\n reportError(error, t('staff.time_tracking.taskDrawer.properties.tagError', 'Could not save the tags.'))\n } finally {\n setBusy(false)\n }\n },\n [onChanged, reportError, retryLastMutation, runMutation, t, tagIds, taskId],\n )\n\n /**\n * Creates the tag and hands it back so the picker can assign it through the\n * drawer's own `handleTagChange` \u2014 the task-assignments write is untouched, the\n * new row simply becomes assignable without leaving the drawer. The list is\n * invalidated rather than patched so the server-assigned colour arrives with it.\n */\n const createTag = React.useCallback(\n async (label: string): Promise<TagOption | null> => {\n try {\n const created = await apiCallOrThrow<Record<string, unknown>>(\n '/api/staff/timesheets/tags',\n {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ label, slug: slugifyProjectName(label).toLowerCase() }),\n },\n {\n errorMessage: t(\n 'staff.time_tracking.entryDialog.errors.createTag',\n 'Could not create the tag.',\n ),\n },\n )\n const id = typeof created?.result?.id === 'string' ? created.result.id : null\n if (!id) return null\n await queryClient.invalidateQueries({ queryKey: [...DRAWER_QUERY_ROOT, 'tags'] })\n return { id, label, color: null }\n } catch (error) {\n logger.warn('staff.time_tracking task drawer tag create failed', { err: error })\n flash(t('staff.time_tracking.entryDialog.errors.createTag', 'Could not create the tag.'), 'error')\n return null\n }\n },\n [queryClient, t],\n )\n\n const status = React.useMemo(\n () => statuses.find((entry) => entry.id === task?.taskStatusId) ?? null,\n [statuses, task?.taskStatusId],\n )\n\n const statusVariant = status?.isDone ? 'success' : status?.isDefault ? 'neutral' : 'info'\n\n const recentEntries = React.useMemo(() => entries.slice(0, RECENT_ENTRY_COUNT), [entries])\n\n const assignedTags = React.useMemo<TagOption[]>(() => {\n const byId = new Map(tagOptions.map((tag) => [tag.id, tag]))\n return tagIds.map((id) => byId.get(id) ?? { id, label: id, color: null })\n }, [tagIds, tagOptions])\n\n const availableTags = React.useMemo(\n () => tagOptions.filter((tag) => !tagIds.includes(tag.id)),\n [tagIds, tagOptions],\n )\n\n const doneStatusId = resolveDoneStatusId(statuses)\n\n const body = (() => {\n if (taskQuery.isLoading) {\n return <LoadingMessage label={t('staff.time_tracking.taskDrawer.loading', 'Loading the task\u2026')} />\n }\n if (taskQuery.isError) {\n return <ErrorMessage label={t('staff.time_tracking.taskDrawer.loadError', 'Could not load the task.')} />\n }\n if (!task) {\n return (\n <ErrorMessage\n label={t('staff.time_tracking.tasks.errors.notFound', 'Task not found or not accessible.')}\n />\n )\n }\n\n return (\n <div className=\"flex flex-col gap-6\">\n {notice ? (\n <Alert status={notice.status} data-testid=\"task-drawer-notice\">\n <AlertDescription>{notice.message}</AlertDescription>\n </Alert>\n ) : null}\n {support ? <SupportRequestPanel ticket={support} /> : null}\n {support ? <SupportAttachmentsSection taskId={taskId} /> : null}\n <TaskQuickLog\n onLog={handleQuickLog}\n logging={busy}\n timerRunningHere={timerRunningHere}\n elapsed={timerRunningHere ? formatElapsed(runningTimer.startedAt, now) : null}\n timerBusy={timerBusy}\n onStartTimer={() => { void handleStartTimer() }}\n onStopTimer={() => { void handleStopTimer() }}\n autoFocus={focusPanel === 'time'}\n disabled={!canManage}\n today={todayIsoDate()}\n />\n\n <section className=\"flex flex-col gap-2\" data-testid=\"task-drawer-logged\">\n <div className=\"flex items-baseline justify-between gap-3\">\n <h3 className=\"text-xs font-semibold uppercase tracking-wide text-muted-foreground\">\n {t('staff.time_tracking.taskDrawer.logged.title', 'Logged time')}\n </h3>\n <span className=\"font-mono text-base font-semibold tabular-nums text-foreground\" data-testid=\"task-drawer-logged-total\">\n {formatBoardMinutes(task.loggedMinutes)}\n </span>\n </div>\n <p className=\"text-xs text-muted-foreground\" data-testid=\"task-drawer-logged-split\">\n {t('staff.time_tracking.taskDrawer.logged.split', 'own {own} \u00B7 subtasks {children}', {\n own: formatSplitMinutes(task.ownMinutes),\n children: formatSplitMinutes(childMinutes(task.loggedMinutes, task.ownMinutes)),\n })}\n </p>\n <div className=\"flex flex-wrap items-center gap-2\">\n <Badge variant=\"success\">\n {t('staff.time_tracking.taskDrawer.logged.billable', 'billable {hours}', {\n hours: formatBoardMinutes(totals.billableMinutes),\n })}\n </Badge>\n <Badge variant=\"neutral\">\n {t('staff.time_tracking.taskDrawer.logged.nonBillable', 'non-billable {hours}', {\n hours: formatBoardMinutes(totals.nonBillableMinutes),\n })}\n </Badge>\n {canSeeMoney && totals.cost !== null ? (\n <Badge variant=\"info\" data-testid=\"task-drawer-cost\">\n {t('staff.time_tracking.taskDrawer.logged.cost', 'cost {amount}', {\n amount: formatCurrency(totals.cost, totals.currencyCode) ?? String(totals.cost),\n })}\n </Badge>\n ) : null}\n </div>\n {totals.partial ? (\n <p className=\"text-xs text-muted-foreground\">\n {t(\n 'staff.time_tracking.taskDrawer.logged.partialHint',\n 'The split is counted from the most recent entries in this project.',\n )}\n </p>\n ) : null}\n </section>\n\n {isChildTask ? null : (\n <section className=\"flex flex-col gap-3\" data-testid=\"task-drawer-subtasks\">\n <div className=\"flex items-baseline justify-between gap-3\">\n <h3 className=\"text-xs font-semibold uppercase tracking-wide text-muted-foreground\">\n {t('staff.time_tracking.taskDrawer.subtasks.title', 'Subtasks')}\n </h3>\n <span className=\"font-mono text-xs tabular-nums text-muted-foreground\">\n {t('staff.time_tracking.taskDrawer.subtasks.count', '{done}/{total}', {\n done: progress.done,\n total: subtaskTotal,\n })}\n </span>\n </div>\n {subtaskTotal > 0 ? (\n <Progress value={subtaskTotal > 0 ? (progress.done / subtaskTotal) * 100 : 0} max={100} />\n ) : null}\n {children.length === 0 ? (\n <p className=\"text-xs text-muted-foreground\">\n {t('staff.time_tracking.taskDrawer.subtasks.empty', 'No subtasks yet.')}\n </p>\n ) : (\n <ul className=\"flex flex-col\">\n {children.map((child) => {\n const done = (child.taskStatusId && doneStatusIds.has(child.taskStatusId)) || !!child.closedAt\n const assigneeName = child.assigneeStaffMemberId\n ? memberNames.get(child.assigneeStaffMemberId) ?? child.assigneeStaffMemberId\n : t('staff.time_tracking.taskDrawer.properties.unassigned', 'Unassigned')\n return (\n <li\n key={child.id}\n className=\"flex items-center gap-2.5 border-b border-border py-2 last:border-b-0\"\n data-subtask-id={child.id}\n >\n <Checkbox\n checked={done}\n disabled={!canManage || busy || !doneStatusId}\n aria-label={t('staff.time_tracking.taskDrawer.subtasks.toggle', 'Mark {title} as done', {\n title: child.title,\n })}\n onCheckedChange={(next) => { void handleToggleSubtask(child, next === true) }}\n />\n <span\n className={`min-w-0 flex-1 truncate text-sm ${done ? 'text-muted-foreground line-through' : 'text-foreground'}`}\n >\n {child.title}\n </span>\n <Avatar label={assigneeName} size=\"xs\" />\n <span className=\"font-mono text-xs tabular-nums text-muted-foreground\">\n {formatBoardMinutes(child.loggedMinutes)}\n </span>\n </li>\n )\n })}\n </ul>\n )}\n {!doneStatusId && children.length > 0 ? (\n <p className=\"text-xs text-muted-foreground\">\n {t(\n 'staff.time_tracking.taskDrawer.subtasks.noDoneColumn',\n 'This project has no done column, so a subtask has no way to close.',\n )}\n </p>\n ) : null}\n {canManage ? (\n composingSubtask ? (\n <Input\n autoFocus\n size=\"sm\"\n value={subtaskDraft}\n disabled={busy}\n onChange={(event) => setSubtaskDraft(event.target.value)}\n onKeyDown={(event) => {\n if (event.key === 'Enter') {\n event.preventDefault()\n void handleAddSubtask()\n }\n if (event.key === 'Escape') {\n event.preventDefault()\n event.stopPropagation()\n setComposingSubtask(false)\n setSubtaskDraft('')\n }\n }}\n placeholder={t('staff.time_tracking.taskDrawer.subtasks.placeholder', 'Subtask title')}\n aria-label={t('staff.time_tracking.taskDrawer.subtasks.add', 'Add subtask')}\n data-testid=\"task-drawer-subtask-input\"\n />\n ) : (\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n className=\"self-start\"\n onClick={() => setComposingSubtask(true)}\n data-testid=\"task-drawer-add-subtask\"\n >\n <Plus className=\"size-3.5\" aria-hidden=\"true\" />\n {t('staff.time_tracking.taskDrawer.subtasks.add', 'Add subtask')}\n </Button>\n )\n ) : null}\n </section>\n )}\n\n <section className=\"flex flex-col gap-3\" data-testid=\"task-drawer-entries\">\n <h3 className=\"text-xs font-semibold uppercase tracking-wide text-muted-foreground\">\n {t('staff.time_tracking.taskDrawer.entries.title', 'Time entries ({count})', { count: entries.length })}\n </h3>\n {recentEntries.length === 0 ? (\n <p className=\"text-xs text-muted-foreground\">\n {t('staff.time_tracking.taskDrawer.entries.empty', 'No time entries yet.')}\n </p>\n ) : (\n <ul className=\"flex flex-col\">\n {recentEntries.map((entry) => (\n <li\n key={entry.id}\n className=\"flex items-center gap-3 border-b border-border py-2 text-sm last:border-b-0\"\n data-entry-id={entry.id}\n >\n <span className=\"w-20 shrink-0 text-xs text-muted-foreground\">\n {formatDate(entry.date) ?? ''}\n </span>\n <span className=\"min-w-0 flex-1 truncate text-foreground\">\n {entry.description\n ?? t('staff.time_tracking.taskDrawer.entries.noDescription', 'No description')}\n </span>\n <span className=\"font-mono text-xs tabular-nums text-muted-foreground\">\n {formatBoardMinutes(entry.durationMinutes)}\n </span>\n </li>\n ))}\n </ul>\n )}\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n className=\"self-start\"\n onClick={() => router.push(`${ENTRIES_LIST_PATH}?taskId=${encodeURIComponent(taskId)}`)}\n >\n {t('staff.time_tracking.taskDrawer.entries.showAll', 'Show all')}\n </Button>\n </section>\n\n <TaskCommentThread\n comments={commentsQuery.data ?? []}\n loading={commentsQuery.isLoading}\n loadError={commentsQuery.isError}\n posting={busy}\n canComment={canManage}\n onPost={handlePostComment}\n mode={support ? 'support' : 'internal'}\n onMakeInternal={support ? handleMakeInternal : undefined}\n />\n\n <section className=\"flex flex-col gap-3\" data-testid=\"task-drawer-properties\">\n <h3 className=\"text-xs font-semibold uppercase tracking-wide text-muted-foreground\">\n {t('staff.time_tracking.taskDrawer.properties.title', 'Properties')}\n </h3>\n <div className=\"grid grid-cols-1 gap-3 sm:grid-cols-2\">\n <div className=\"flex flex-col gap-1.5\">\n <span className=\"text-xs font-medium text-muted-foreground\">\n {t('staff.time_tracking.taskDrawer.properties.status', 'Status')}\n </span>\n <Select\n value={task.taskStatusId ?? undefined}\n disabled={!canManage || busy}\n onValueChange={handleStatusChange}\n >\n <SelectTrigger\n aria-label={t('staff.time_tracking.taskDrawer.properties.status', 'Status')}\n data-testid=\"task-drawer-status-select\"\n >\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {statuses.map((entry) => (\n <SelectItem key={entry.id} value={entry.id}>\n {entry.name}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n <div className=\"flex flex-col gap-1.5\">\n <span className=\"text-xs font-medium text-muted-foreground\">\n {t('staff.time_tracking.taskDrawer.properties.assignee', 'Assignee')}\n </span>\n <Select\n value={task.assigneeStaffMemberId ?? undefined}\n disabled={!canManage || busy}\n onValueChange={handleAssigneeChange}\n >\n <SelectTrigger\n aria-label={t('staff.time_tracking.taskDrawer.properties.assignee', 'Assignee')}\n data-testid=\"task-drawer-assignee-select\"\n >\n <SelectValue\n placeholder={t('staff.time_tracking.taskDrawer.properties.unassigned', 'Unassigned')}\n />\n </SelectTrigger>\n <SelectContent>\n {members.map((member) => (\n <SelectItem key={member.id} value={member.id}>\n {member.name}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n {canClaim ? (\n <Button\n type=\"button\"\n size=\"sm\"\n variant=\"outline\"\n disabled={busy}\n onClick={() => runAssignmentAction('claim')}\n data-testid=\"task-drawer-claim\"\n >\n {t('staff.time_tracking.taskDrawer.assignment.claim', 'Claim')}\n </Button>\n ) : null}\n {canRelease ? (\n <Button\n type=\"button\"\n size=\"sm\"\n variant=\"ghost\"\n disabled={busy}\n onClick={() => runAssignmentAction('release')}\n data-testid=\"task-drawer-release\"\n >\n {t('staff.time_tracking.taskDrawer.assignment.release', 'Release')}\n </Button>\n ) : null}\n </div>\n </div>\n\n <div className=\"flex flex-col gap-1.5\">\n <span className=\"text-xs font-medium text-muted-foreground\">\n {t('staff.time_tracking.taskDrawer.properties.tags', 'Tags')}\n </span>\n <div className=\"flex flex-wrap items-center gap-2\">\n {assignedTags.map((tag) => (\n <Badge key={tag.id} variant=\"neutral\" className=\"gap-1\" style={tagChipStyle(tag)}>\n {tag.label}\n {canManage ? (\n <button\n type=\"button\"\n aria-label={t('staff.time_tracking.taskDrawer.properties.removeTag', 'Remove tag {label}', {\n label: tag.label,\n })}\n onClick={() => { void handleTagChange(tag.id, false) }}\n >\n <X className=\"size-3\" aria-hidden=\"true\" />\n </button>\n ) : null}\n </Badge>\n ))}\n {canManage ? (\n <TagPicker\n available={availableTags}\n disabled={busy}\n onAssign={(id) => { void handleTagChange(id, true) }}\n onCreate={createTag}\n labels={{\n add: t('staff.time_tracking.taskDrawer.properties.addTag', 'Add tag'),\n create: (name) =>\n t('staff.time_tracking.entryDialog.createTag', 'Create \"{name}\"', { name }),\n empty: t('staff.time_tracking.entryDialog.noTags', 'No matching tag'),\n }}\n testIdPrefix=\"task-drawer\"\n />\n ) : null}\n </div>\n <p className=\"text-xs text-muted-foreground\">\n {t(\n 'staff.time_tracking.taskDrawer.properties.tagsPending',\n 'Tags already saved on this task appear here once the task list returns them.',\n )}\n </p>\n </div>\n <InjectionSpot\n spotId={extensionPoints.hosts.taskDetailSidebar.spotId}\n context={taskInjectionContext}\n data={task}\n />\n </section>\n\n <InjectionSpot\n spotId={extensionPoints.hosts.taskDetailTabs.spotId}\n context={taskInjectionContext}\n data={task}\n />\n </div>\n )\n })()\n\n const createdLabel = formatDate(createdAt)\n const headerLine = [\n projectName ?? null,\n createdLabel\n ? t('staff.time_tracking.taskDrawer.created', 'created {date}', { date: createdLabel })\n : null,\n ]\n .filter((part): part is string => !!part)\n .join(' \u00B7 ')\n\n return (\n <Drawer open={open} onOpenChange={onOpenChange}>\n <DrawerContent\n className=\"sm:max-w-xl\"\n closeAriaLabel={t('staff.time_tracking.taskDrawer.close', 'Close')}\n data-testid=\"task-drawer\"\n >\n <DrawerHeader>\n <div className=\"flex min-w-0 flex-1 flex-col gap-1.5\">\n <div className=\"flex items-center gap-2\">\n {status ? <Badge variant={statusVariant}>{status.name}</Badge> : null}\n {task?.reference ? (\n <span className=\"text-xs text-muted-foreground\">{task.reference}</span>\n ) : null}\n <InjectionSpot\n spotId={extensionPoints.hosts.taskDetailStatusBadges.spotId}\n context={taskInjectionContext}\n data={task}\n />\n </div>\n <DrawerTitle className=\"truncate\">{task?.title ?? ''}</DrawerTitle>\n <DrawerDescription>{headerLine}</DrawerDescription>\n <InjectionSpot\n spotId={extensionPoints.hosts.taskDetailHeader.spotId}\n context={taskInjectionContext}\n data={task}\n />\n </div>\n </DrawerHeader>\n <DrawerBody>{body}</DrawerBody>\n <DrawerFooter\n leading={\n <span className=\"text-xs text-muted-foreground\">\n {t('staff.time_tracking.taskDrawer.autoSave', 'Changes are saved automatically')}\n </span>\n }\n >\n <InjectionSpot\n spotId={extensionPoints.hosts.taskDetailFooter.spotId}\n context={taskInjectionContext}\n data={task}\n />\n <Button type=\"button\" variant=\"outline\" onClick={() => onOpenChange(false)}>\n {t('staff.time_tracking.taskDrawer.close', 'Close')}\n </Button>\n </DrawerFooter>\n {ConfirmDialogElement}\n </DrawerContent>\n </Drawer>\n )\n}\n\nexport default TaskDrawer\n"],
5
+ "mappings": "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",
6
6
  "names": ["statuses", "status", "body"]
7
7
  }
@@ -158,6 +158,24 @@ function useStaffMemberDirectory(enabled) {
158
158
  }
159
159
  });
160
160
  }
161
+ function boardUnassignedCountQueryKey(scopeVersion, timeProjectId) {
162
+ return [...BOARD_QUERY_ROOT, "unassigned-count", `scope:${scopeVersion}`, `project:${timeProjectId}`];
163
+ }
164
+ function useUnassignedTaskCount(timeProjectId) {
165
+ const scopeVersion = useOrganizationScopeVersion();
166
+ return useQuery({
167
+ queryKey: boardUnassignedCountQueryKey(scopeVersion, timeProjectId ?? ""),
168
+ enabled: !!timeProjectId,
169
+ staleTime: 15e3,
170
+ queryFn: async () => {
171
+ const call = await apiCall(
172
+ `/api/staff/timesheets/tasks?timeProjectId=${encodeURIComponent(timeProjectId ?? "")}&unassigned=true&topLevelOnly=true&page=1&pageSize=1`
173
+ );
174
+ if (!call.ok) throw new Error("[internal] unassigned task count request failed");
175
+ return readTotal(call.result, 0);
176
+ }
177
+ });
178
+ }
161
179
  export {
162
180
  BOARD_DIRECTORY_PAGE_SIZE,
163
181
  BOARD_PAGE_SIZE,
@@ -165,12 +183,14 @@ export {
165
183
  boardColumnQueryKey,
166
184
  boardSelfQueryKey,
167
185
  boardStatusesQueryKey,
186
+ boardUnassignedCountQueryKey,
168
187
  fetchTaskPages,
169
188
  useAssigneeNames,
170
189
  useBoardSelf,
171
190
  useBoardStatuses,
172
191
  useStaffMemberDirectory,
173
192
  useTagDirectory,
174
- useTagLabels
193
+ useTagLabels,
194
+ useUnassignedTaskCount
175
195
  };
176
196
  //# sourceMappingURL=boardDirectory.js.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../src/modules/staff/lib/time-tracking-ui/boardDirectory.ts"],
4
- "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { useQuery } from '@tanstack/react-query'\nimport { apiCall } from '@open-mercato/ui/backend/utils/apiCall'\nimport { useOrganizationScopeVersion } from '@open-mercato/shared/lib/frontend/useOrganizationScope'\nimport {\n readItems,\n readString,\n readTotal,\n sortByPosition,\n toBoardStatus,\n type BoardApiRow,\n type BoardStatus,\n} from './kanbanBoardData'\n\n/**\n * The lookups every board surface needs \u2014 statuses, the caller's own staff member,\n * assignee names and tag labels \u2014 behind one set of query keys.\n *\n * They live here rather than inside `KanbanBoard` so the filter chips, the flat list\n * view and the board all read the *same* react-query entries. Two components asking for\n * the project's statuses must not produce two requests, and a status renamed in one\n * place must not stay stale in another.\n */\n\nexport const BOARD_QUERY_ROOT = ['staff', 'time-tracking', 'board'] as const\n\nexport const BOARD_PAGE_SIZE = 50\nexport const BOARD_DIRECTORY_PAGE_SIZE = 100\n\nexport type BoardSelf = {\n id: string\n displayName: string | null\n}\n\nexport type BoardTagOption = {\n id: string\n label: string\n}\n\nexport function boardStatusesQueryKey(scopeVersion: number, timeProjectId: string) {\n return [...BOARD_QUERY_ROOT, 'statuses', `scope:${scopeVersion}`, `project:${timeProjectId}`]\n}\n\nexport function boardColumnQueryKey(\n scopeVersion: number,\n timeProjectId: string,\n statusId: string,\n pages: number,\n filterKey: string,\n) {\n return [\n ...BOARD_QUERY_ROOT,\n 'column',\n `scope:${scopeVersion}`,\n `project:${timeProjectId}`,\n `status:${statusId}`,\n `pages:${pages}`,\n `filters:${filterKey}`,\n ]\n}\n\nexport function boardSelfQueryKey(scopeVersion: number) {\n return [...BOARD_QUERY_ROOT, 'self', `scope:${scopeVersion}`]\n}\n\nexport type TaskPageResult = { items: BoardApiRow[]; total: number }\n\n/**\n * Pulls up to `maxPages` pages of the tasks list, stopping as soon as the server has\n * nothing more to give. Shared by the column queries and the flat list so both agree on\n * what \"one more page\" means.\n */\nexport async function fetchTaskPages(\n params: (page: number) => string,\n maxPages: number,\n): Promise<TaskPageResult> {\n const items: BoardApiRow[] = []\n let total = 0\n for (let page = 1; page <= maxPages; page += 1) {\n const call = await apiCall<Record<string, unknown>>(`/api/staff/timesheets/tasks?${params(page)}`)\n if (!call.ok) throw new Error('[internal] task page request failed')\n const pageItems = readItems(call.result)\n total = readTotal(call.result, items.length + pageItems.length)\n items.push(...pageItems)\n if (pageItems.length < BOARD_PAGE_SIZE || items.length >= total) break\n }\n return { items, total }\n}\n\nexport function useBoardStatuses(timeProjectId: string) {\n const scopeVersion = useOrganizationScopeVersion()\n return useQuery<BoardStatus[]>({\n queryKey: boardStatusesQueryKey(scopeVersion, timeProjectId),\n staleTime: 30_000,\n queryFn: async () => {\n const call = await apiCall<Record<string, unknown>>(\n `/api/staff/timesheets/task-statuses?timeProjectId=${encodeURIComponent(timeProjectId)}&page=1&pageSize=${BOARD_DIRECTORY_PAGE_SIZE}&sortField=position&sortDir=asc`,\n )\n if (!call.ok) throw new Error('[internal] task-statuses request failed')\n const statuses = readItems(call.result)\n .map(toBoardStatus)\n .filter((status): status is BoardStatus => status !== null)\n return sortByPosition(statuses)\n },\n })\n}\n\n/**\n * The caller's own staff member \u2014 the identity behind \"Przypisane do mnie\" and the\n * avatar the chip draws.\n */\nexport function useBoardSelf() {\n const scopeVersion = useOrganizationScopeVersion()\n return useQuery<BoardSelf | null>({\n queryKey: boardSelfQueryKey(scopeVersion),\n staleTime: 300_000,\n queryFn: async () => {\n const call = await apiCall<{ member?: { id?: string | null; displayName?: string | null } | null }>(\n '/api/staff/team-members/self',\n )\n if (!call.ok) return null\n const id = call.result?.member?.id ?? null\n if (!id) return null\n return { id, displayName: call.result?.member?.displayName ?? null }\n },\n })\n}\n\n/** Display names for the assignees a surface is about to render. */\nexport function useAssigneeNames(assigneeIds: readonly string[]) {\n const scopeVersion = useOrganizationScopeVersion()\n const ids = React.useMemo(() => Array.from(new Set(assigneeIds)).sort((a, b) => (a < b ? -1 : a > b ? 1 : 0)), [assigneeIds])\n return useQuery<Map<string, string>>({\n queryKey: [...BOARD_QUERY_ROOT, 'assignees', `scope:${scopeVersion}`, ids.join(',')],\n enabled: ids.length > 0,\n staleTime: 300_000,\n queryFn: async () => {\n const call = await apiCall<Record<string, unknown>>(\n `/api/staff/team-members?ids=${ids.map(encodeURIComponent).join(',')}&pageSize=${BOARD_DIRECTORY_PAGE_SIZE}`,\n )\n const names = new Map<string, string>()\n if (!call.ok) return names\n for (const row of readItems(call.result)) {\n const id = readString(row, 'id')\n if (!id) continue\n names.set(id, readString(row, 'display_name', 'displayName') ?? id)\n }\n return names\n },\n })\n}\n\n/**\n * Labels for the tags a surface is about to render.\n *\n * Only tags that actually have a label land in the map. A caller renders a chip\n * for the ids it finds here and skips the rest, so a tag whose label has not\n * arrived yet \u2014 or a row that carries none \u2014 is never drawn as its raw id.\n */\nexport function useTagLabels(tagIds: readonly string[]) {\n const scopeVersion = useOrganizationScopeVersion()\n const ids = React.useMemo(() => Array.from(new Set(tagIds)).sort((a, b) => (a < b ? -1 : a > b ? 1 : 0)), [tagIds])\n return useQuery<Map<string, string>>({\n queryKey: [...BOARD_QUERY_ROOT, 'tags', `scope:${scopeVersion}`, ids.join(',')],\n enabled: ids.length > 0,\n staleTime: 300_000,\n queryFn: async () => {\n const call = await apiCall<Record<string, unknown>>(\n `/api/staff/timesheets/tags?ids=${ids.map(encodeURIComponent).join(',')}&pageSize=${BOARD_DIRECTORY_PAGE_SIZE}`,\n )\n const labels = new Map<string, string>()\n if (!call.ok) return labels\n for (const row of readItems(call.result)) {\n const id = readString(row, 'id')\n if (!id) continue\n const label = readString(row, 'label')\n if (!label) continue\n labels.set(id, label)\n }\n return labels\n },\n })\n}\n\n/** Every tag in the organization \u2014 the option list behind the \"Tag\" filter. */\nexport function useTagDirectory(enabled: boolean) {\n const scopeVersion = useOrganizationScopeVersion()\n return useQuery<BoardTagOption[]>({\n queryKey: [...BOARD_QUERY_ROOT, 'tag-directory', `scope:${scopeVersion}`],\n enabled,\n staleTime: 300_000,\n queryFn: async () => {\n const call = await apiCall<Record<string, unknown>>(\n `/api/staff/timesheets/tags?page=1&pageSize=${BOARD_DIRECTORY_PAGE_SIZE}&sortField=label&sortDir=asc`,\n )\n if (!call.ok) return []\n return readItems(call.result)\n .map((row) => {\n const id = readString(row, 'id')\n if (!id) return null\n return { id, label: readString(row, 'label') ?? id }\n })\n .filter((tag): tag is BoardTagOption => tag !== null)\n },\n })\n}\n\n/** The people who can be picked as an assignee filter. */\nexport function useStaffMemberDirectory(enabled: boolean) {\n const scopeVersion = useOrganizationScopeVersion()\n return useQuery<Array<{ id: string; displayName: string }>>({\n queryKey: [...BOARD_QUERY_ROOT, 'member-directory', `scope:${scopeVersion}`],\n enabled,\n staleTime: 300_000,\n queryFn: async () => {\n const call = await apiCall<Record<string, unknown>>(\n `/api/staff/team-members?page=1&pageSize=${BOARD_DIRECTORY_PAGE_SIZE}&sortField=displayName&sortDir=asc`,\n )\n if (!call.ok) return []\n return readItems(call.result)\n .map((row) => {\n const id = readString(row, 'id')\n if (!id) return null\n return { id, displayName: readString(row, 'display_name', 'displayName') ?? id }\n })\n .filter((member): member is { id: string; displayName: string } => member !== null)\n },\n })\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { useQuery } from '@tanstack/react-query'\nimport { apiCall } from '@open-mercato/ui/backend/utils/apiCall'\nimport { useOrganizationScopeVersion } from '@open-mercato/shared/lib/frontend/useOrganizationScope'\nimport {\n readItems,\n readString,\n readTotal,\n sortByPosition,\n toBoardStatus,\n type BoardApiRow,\n type BoardStatus,\n} from './kanbanBoardData'\n\n/**\n * The lookups every board surface needs \u2014 statuses, the caller's own staff member,\n * assignee names and tag labels \u2014 behind one set of query keys.\n *\n * They live here rather than inside `KanbanBoard` so the filter chips, the flat list\n * view and the board all read the *same* react-query entries. Two components asking for\n * the project's statuses must not produce two requests, and a status renamed in one\n * place must not stay stale in another.\n */\n\nexport const BOARD_QUERY_ROOT = ['staff', 'time-tracking', 'board'] as const\n\nexport const BOARD_PAGE_SIZE = 50\nexport const BOARD_DIRECTORY_PAGE_SIZE = 100\n\nexport type BoardSelf = {\n id: string\n displayName: string | null\n}\n\nexport type BoardTagOption = {\n id: string\n label: string\n}\n\nexport function boardStatusesQueryKey(scopeVersion: number, timeProjectId: string) {\n return [...BOARD_QUERY_ROOT, 'statuses', `scope:${scopeVersion}`, `project:${timeProjectId}`]\n}\n\nexport function boardColumnQueryKey(\n scopeVersion: number,\n timeProjectId: string,\n statusId: string,\n pages: number,\n filterKey: string,\n) {\n return [\n ...BOARD_QUERY_ROOT,\n 'column',\n `scope:${scopeVersion}`,\n `project:${timeProjectId}`,\n `status:${statusId}`,\n `pages:${pages}`,\n `filters:${filterKey}`,\n ]\n}\n\nexport function boardSelfQueryKey(scopeVersion: number) {\n return [...BOARD_QUERY_ROOT, 'self', `scope:${scopeVersion}`]\n}\n\nexport type TaskPageResult = { items: BoardApiRow[]; total: number }\n\n/**\n * Pulls up to `maxPages` pages of the tasks list, stopping as soon as the server has\n * nothing more to give. Shared by the column queries and the flat list so both agree on\n * what \"one more page\" means.\n */\nexport async function fetchTaskPages(\n params: (page: number) => string,\n maxPages: number,\n): Promise<TaskPageResult> {\n const items: BoardApiRow[] = []\n let total = 0\n for (let page = 1; page <= maxPages; page += 1) {\n const call = await apiCall<Record<string, unknown>>(`/api/staff/timesheets/tasks?${params(page)}`)\n if (!call.ok) throw new Error('[internal] task page request failed')\n const pageItems = readItems(call.result)\n total = readTotal(call.result, items.length + pageItems.length)\n items.push(...pageItems)\n if (pageItems.length < BOARD_PAGE_SIZE || items.length >= total) break\n }\n return { items, total }\n}\n\nexport function useBoardStatuses(timeProjectId: string) {\n const scopeVersion = useOrganizationScopeVersion()\n return useQuery<BoardStatus[]>({\n queryKey: boardStatusesQueryKey(scopeVersion, timeProjectId),\n staleTime: 30_000,\n queryFn: async () => {\n const call = await apiCall<Record<string, unknown>>(\n `/api/staff/timesheets/task-statuses?timeProjectId=${encodeURIComponent(timeProjectId)}&page=1&pageSize=${BOARD_DIRECTORY_PAGE_SIZE}&sortField=position&sortDir=asc`,\n )\n if (!call.ok) throw new Error('[internal] task-statuses request failed')\n const statuses = readItems(call.result)\n .map(toBoardStatus)\n .filter((status): status is BoardStatus => status !== null)\n return sortByPosition(statuses)\n },\n })\n}\n\n/**\n * The caller's own staff member \u2014 the identity behind \"Przypisane do mnie\" and the\n * avatar the chip draws.\n */\nexport function useBoardSelf() {\n const scopeVersion = useOrganizationScopeVersion()\n return useQuery<BoardSelf | null>({\n queryKey: boardSelfQueryKey(scopeVersion),\n staleTime: 300_000,\n queryFn: async () => {\n const call = await apiCall<{ member?: { id?: string | null; displayName?: string | null } | null }>(\n '/api/staff/team-members/self',\n )\n if (!call.ok) return null\n const id = call.result?.member?.id ?? null\n if (!id) return null\n return { id, displayName: call.result?.member?.displayName ?? null }\n },\n })\n}\n\n/** Display names for the assignees a surface is about to render. */\nexport function useAssigneeNames(assigneeIds: readonly string[]) {\n const scopeVersion = useOrganizationScopeVersion()\n const ids = React.useMemo(() => Array.from(new Set(assigneeIds)).sort((a, b) => (a < b ? -1 : a > b ? 1 : 0)), [assigneeIds])\n return useQuery<Map<string, string>>({\n queryKey: [...BOARD_QUERY_ROOT, 'assignees', `scope:${scopeVersion}`, ids.join(',')],\n enabled: ids.length > 0,\n staleTime: 300_000,\n queryFn: async () => {\n const call = await apiCall<Record<string, unknown>>(\n `/api/staff/team-members?ids=${ids.map(encodeURIComponent).join(',')}&pageSize=${BOARD_DIRECTORY_PAGE_SIZE}`,\n )\n const names = new Map<string, string>()\n if (!call.ok) return names\n for (const row of readItems(call.result)) {\n const id = readString(row, 'id')\n if (!id) continue\n names.set(id, readString(row, 'display_name', 'displayName') ?? id)\n }\n return names\n },\n })\n}\n\n/**\n * Labels for the tags a surface is about to render.\n *\n * Only tags that actually have a label land in the map. A caller renders a chip\n * for the ids it finds here and skips the rest, so a tag whose label has not\n * arrived yet \u2014 or a row that carries none \u2014 is never drawn as its raw id.\n */\nexport function useTagLabels(tagIds: readonly string[]) {\n const scopeVersion = useOrganizationScopeVersion()\n const ids = React.useMemo(() => Array.from(new Set(tagIds)).sort((a, b) => (a < b ? -1 : a > b ? 1 : 0)), [tagIds])\n return useQuery<Map<string, string>>({\n queryKey: [...BOARD_QUERY_ROOT, 'tags', `scope:${scopeVersion}`, ids.join(',')],\n enabled: ids.length > 0,\n staleTime: 300_000,\n queryFn: async () => {\n const call = await apiCall<Record<string, unknown>>(\n `/api/staff/timesheets/tags?ids=${ids.map(encodeURIComponent).join(',')}&pageSize=${BOARD_DIRECTORY_PAGE_SIZE}`,\n )\n const labels = new Map<string, string>()\n if (!call.ok) return labels\n for (const row of readItems(call.result)) {\n const id = readString(row, 'id')\n if (!id) continue\n const label = readString(row, 'label')\n if (!label) continue\n labels.set(id, label)\n }\n return labels\n },\n })\n}\n\n/** Every tag in the organization \u2014 the option list behind the \"Tag\" filter. */\nexport function useTagDirectory(enabled: boolean) {\n const scopeVersion = useOrganizationScopeVersion()\n return useQuery<BoardTagOption[]>({\n queryKey: [...BOARD_QUERY_ROOT, 'tag-directory', `scope:${scopeVersion}`],\n enabled,\n staleTime: 300_000,\n queryFn: async () => {\n const call = await apiCall<Record<string, unknown>>(\n `/api/staff/timesheets/tags?page=1&pageSize=${BOARD_DIRECTORY_PAGE_SIZE}&sortField=label&sortDir=asc`,\n )\n if (!call.ok) return []\n return readItems(call.result)\n .map((row) => {\n const id = readString(row, 'id')\n if (!id) return null\n return { id, label: readString(row, 'label') ?? id }\n })\n .filter((tag): tag is BoardTagOption => tag !== null)\n },\n })\n}\n\n/** The people who can be picked as an assignee filter. */\nexport function useStaffMemberDirectory(enabled: boolean) {\n const scopeVersion = useOrganizationScopeVersion()\n return useQuery<Array<{ id: string; displayName: string }>>({\n queryKey: [...BOARD_QUERY_ROOT, 'member-directory', `scope:${scopeVersion}`],\n enabled,\n staleTime: 300_000,\n queryFn: async () => {\n const call = await apiCall<Record<string, unknown>>(\n `/api/staff/team-members?page=1&pageSize=${BOARD_DIRECTORY_PAGE_SIZE}&sortField=displayName&sortDir=asc`,\n )\n if (!call.ok) return []\n return readItems(call.result)\n .map((row) => {\n const id = readString(row, 'id')\n if (!id) return null\n return { id, displayName: readString(row, 'display_name', 'displayName') ?? id }\n })\n .filter((member): member is { id: string; displayName: string } => member !== null)\n },\n })\n}\n\nexport function boardUnassignedCountQueryKey(scopeVersion: number, timeProjectId: string) {\n return [...BOARD_QUERY_ROOT, 'unassigned-count', `scope:${scopeVersion}`, `project:${timeProjectId}`]\n}\n\n/** How many top-level tasks in the project have no assignee \u2014 the claimable work the chip counts. */\nexport function useUnassignedTaskCount(timeProjectId: string | null | undefined) {\n const scopeVersion = useOrganizationScopeVersion()\n return useQuery<number>({\n queryKey: boardUnassignedCountQueryKey(scopeVersion, timeProjectId ?? ''),\n enabled: !!timeProjectId,\n staleTime: 15_000,\n queryFn: async () => {\n const call = await apiCall<Record<string, unknown>>(\n `/api/staff/timesheets/tasks?timeProjectId=${encodeURIComponent(timeProjectId ?? '')}&unassigned=true&topLevelOnly=true&page=1&pageSize=1`,\n )\n if (!call.ok) throw new Error('[internal] unassigned task count request failed')\n return readTotal(call.result, 0)\n },\n })\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }