@open-mercato/core 0.7.1-develop.7178.1.ab93fbaf4e → 0.7.1-develop.7180.1.9717fbbb43

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 (808) hide show
  1. package/.turbo/turbo-build.log +2 -2
  2. package/dist/generated/entities/staff_time_entry/index.js +14 -0
  3. package/dist/generated/entities/staff_time_entry/index.js.map +2 -2
  4. package/dist/generated/entities/staff_time_entry_tag/index.js +15 -0
  5. package/dist/generated/entities/staff_time_entry_tag/index.js.map +7 -0
  6. package/dist/generated/entities/staff_time_project/index.js +16 -0
  7. package/dist/generated/entities/staff_time_project/index.js.map +2 -2
  8. package/dist/generated/entities/staff_time_report/index.js +57 -0
  9. package/dist/generated/entities/staff_time_report/index.js.map +7 -0
  10. package/dist/generated/entities/staff_time_report_entry/index.js +27 -0
  11. package/dist/generated/entities/staff_time_report_entry/index.js.map +7 -0
  12. package/dist/generated/entities/staff_time_report_event/index.js +21 -0
  13. package/dist/generated/entities/staff_time_report_event/index.js.map +7 -0
  14. package/dist/generated/entities/staff_time_report_project/index.js +15 -0
  15. package/dist/generated/entities/staff_time_report_project/index.js.map +7 -0
  16. package/dist/generated/entities/staff_time_tag/index.js +21 -0
  17. package/dist/generated/entities/staff_time_tag/index.js.map +7 -0
  18. package/dist/generated/entities/staff_time_task/index.js +37 -0
  19. package/dist/generated/entities/staff_time_task/index.js.map +7 -0
  20. package/dist/generated/entities/staff_time_task_comment/index.js +21 -0
  21. package/dist/generated/entities/staff_time_task_comment/index.js.map +7 -0
  22. package/dist/generated/entities/staff_time_task_status/index.js +29 -0
  23. package/dist/generated/entities/staff_time_task_status/index.js.map +7 -0
  24. package/dist/generated/entities/staff_time_task_tag/index.js +15 -0
  25. package/dist/generated/entities/staff_time_task_tag/index.js.map +7 -0
  26. package/dist/generated/entities.ids.generated.js +11 -1
  27. package/dist/generated/entities.ids.generated.js.map +2 -2
  28. package/dist/generated/entity-fields-registry.js +149 -0
  29. package/dist/generated/entity-fields-registry.js.map +2 -2
  30. package/dist/helpers/integration/timesheetFixtures.js +7 -0
  31. package/dist/helpers/integration/timesheetFixtures.js.map +2 -2
  32. package/dist/modules/auth/cli.js +16 -0
  33. package/dist/modules/auth/cli.js.map +2 -2
  34. package/dist/modules/auth/lib/backendChrome.js +1 -0
  35. package/dist/modules/auth/lib/backendChrome.js.map +2 -2
  36. package/dist/modules/customer_accounts/lib/customerRoleAcls.js +44 -0
  37. package/dist/modules/customer_accounts/lib/customerRoleAcls.js.map +7 -0
  38. package/dist/modules/customer_accounts/setup.js +1 -28
  39. package/dist/modules/customer_accounts/setup.js.map +2 -2
  40. package/dist/modules/data_sync/api/options.js +2 -0
  41. package/dist/modules/data_sync/api/options.js.map +2 -2
  42. package/dist/modules/data_sync/backend/data-sync/page.js +39 -17
  43. package/dist/modules/data_sync/backend/data-sync/page.js.map +2 -2
  44. package/dist/modules/data_sync/lib/start-controls.js +42 -0
  45. package/dist/modules/data_sync/lib/start-controls.js.map +7 -0
  46. package/dist/modules/staff/acl.js +9 -1
  47. package/dist/modules/staff/acl.js.map +2 -2
  48. package/dist/modules/staff/ai-agents.js +142 -0
  49. package/dist/modules/staff/ai-agents.js.map +7 -0
  50. package/dist/modules/staff/ai-tools/time-tracking-pack.js +348 -0
  51. package/dist/modules/staff/ai-tools/time-tracking-pack.js.map +7 -0
  52. package/dist/modules/staff/ai-tools/types.js +54 -0
  53. package/dist/modules/staff/ai-tools/types.js.map +7 -0
  54. package/dist/modules/staff/ai-tools.js +8 -0
  55. package/dist/modules/staff/ai-tools.js.map +7 -0
  56. package/dist/modules/staff/analytics.js +84 -1
  57. package/dist/modules/staff/analytics.js.map +2 -2
  58. package/dist/modules/staff/api/guards.js +37 -3
  59. package/dist/modules/staff/api/guards.js.map +2 -2
  60. package/dist/modules/staff/api/leave-requests/accept/route.js +1 -3
  61. package/dist/modules/staff/api/leave-requests/accept/route.js.map +2 -2
  62. package/dist/modules/staff/api/leave-requests/reject/route.js +1 -3
  63. package/dist/modules/staff/api/leave-requests/reject/route.js.map +2 -2
  64. package/dist/modules/staff/api/portal/time-reports/[id]/route.js +131 -0
  65. package/dist/modules/staff/api/portal/time-reports/[id]/route.js.map +7 -0
  66. package/dist/modules/staff/api/portal/time-reports/route.js +179 -0
  67. package/dist/modules/staff/api/portal/time-reports/route.js.map +7 -0
  68. package/dist/modules/staff/api/team-members/self/route.js +1 -3
  69. package/dist/modules/staff/api/team-members/self/route.js.map +2 -2
  70. package/dist/modules/staff/api/team-members/tags/assign/route.js +1 -3
  71. package/dist/modules/staff/api/team-members/tags/assign/route.js.map +2 -2
  72. package/dist/modules/staff/api/team-members/tags/unassign/route.js +1 -3
  73. package/dist/modules/staff/api/team-members/tags/unassign/route.js.map +2 -2
  74. package/dist/modules/staff/api/timesheets/_shared/withTimesheetInterceptors.js +174 -0
  75. package/dist/modules/staff/api/timesheets/_shared/withTimesheetInterceptors.js.map +7 -0
  76. package/dist/modules/staff/api/timesheets/access-requests/route.js +226 -0
  77. package/dist/modules/staff/api/timesheets/access-requests/route.js.map +7 -0
  78. package/dist/modules/staff/api/timesheets/my-projects/[projectId]/route.js +15 -8
  79. package/dist/modules/staff/api/timesheets/my-projects/[projectId]/route.js.map +2 -2
  80. package/dist/modules/staff/api/timesheets/my-projects/route.js +13 -1
  81. package/dist/modules/staff/api/timesheets/my-projects/route.js.map +2 -2
  82. package/dist/modules/staff/api/timesheets/my-work/myWorkAggregate.js +42 -0
  83. package/dist/modules/staff/api/timesheets/my-work/myWorkAggregate.js.map +7 -0
  84. package/dist/modules/staff/api/timesheets/my-work/route.js +383 -0
  85. package/dist/modules/staff/api/timesheets/my-work/route.js.map +7 -0
  86. package/dist/modules/staff/api/timesheets/projects/kpis/route.js +14 -2
  87. package/dist/modules/staff/api/timesheets/projects/kpis/route.js.map +2 -2
  88. package/dist/modules/staff/api/timesheets/reports/[id]/close/route.js +168 -0
  89. package/dist/modules/staff/api/timesheets/reports/[id]/close/route.js.map +7 -0
  90. package/dist/modules/staff/api/timesheets/reports/[id]/export/route.js +246 -0
  91. package/dist/modules/staff/api/timesheets/reports/[id]/export/route.js.map +7 -0
  92. package/dist/modules/staff/api/timesheets/reports/[id]/sheet/route.js +183 -0
  93. package/dist/modules/staff/api/timesheets/reports/[id]/sheet/route.js.map +7 -0
  94. package/dist/modules/staff/api/timesheets/reports/[id]/unlock/route.js +164 -0
  95. package/dist/modules/staff/api/timesheets/reports/[id]/unlock/route.js.map +7 -0
  96. package/dist/modules/staff/api/timesheets/reports/preview/route.js +360 -0
  97. package/dist/modules/staff/api/timesheets/reports/preview/route.js.map +7 -0
  98. package/dist/modules/staff/api/timesheets/reports/route.js +316 -0
  99. package/dist/modules/staff/api/timesheets/reports/route.js.map +7 -0
  100. package/dist/modules/staff/api/timesheets/reports/shared.js +63 -0
  101. package/dist/modules/staff/api/timesheets/reports/shared.js.map +7 -0
  102. package/dist/modules/staff/api/timesheets/settings/reapply-rounding/route.js +164 -0
  103. package/dist/modules/staff/api/timesheets/settings/reapply-rounding/route.js.map +7 -0
  104. package/dist/modules/staff/api/timesheets/settings/rounding-impact/route.js +168 -0
  105. package/dist/modules/staff/api/timesheets/settings/rounding-impact/route.js.map +7 -0
  106. package/dist/modules/staff/api/timesheets/settings/route.js +199 -0
  107. package/dist/modules/staff/api/timesheets/settings/route.js.map +7 -0
  108. package/dist/modules/staff/api/timesheets/tags/access.js +76 -0
  109. package/dist/modules/staff/api/timesheets/tags/access.js.map +7 -0
  110. package/dist/modules/staff/api/timesheets/tags/entry-assignments/route.js +235 -0
  111. package/dist/modules/staff/api/timesheets/tags/entry-assignments/route.js.map +7 -0
  112. package/dist/modules/staff/api/timesheets/tags/route.js +160 -0
  113. package/dist/modules/staff/api/timesheets/tags/route.js.map +7 -0
  114. package/dist/modules/staff/api/timesheets/tags/task-assignments/route.js +233 -0
  115. package/dist/modules/staff/api/timesheets/tags/task-assignments/route.js.map +7 -0
  116. package/dist/modules/staff/api/timesheets/task-statuses/route.js +188 -0
  117. package/dist/modules/staff/api/timesheets/task-statuses/route.js.map +7 -0
  118. package/dist/modules/staff/api/timesheets/tasks/[id]/comments/route.js +399 -0
  119. package/dist/modules/staff/api/timesheets/tasks/[id]/comments/route.js.map +7 -0
  120. package/dist/modules/staff/api/timesheets/tasks/[id]/status/route.js +210 -0
  121. package/dist/modules/staff/api/timesheets/tasks/[id]/status/route.js.map +7 -0
  122. package/dist/modules/staff/api/timesheets/tasks/route.js +333 -0
  123. package/dist/modules/staff/api/timesheets/tasks/route.js.map +7 -0
  124. package/dist/modules/staff/api/timesheets/time-entries/[id]/duplicate/route.js +228 -0
  125. package/dist/modules/staff/api/timesheets/time-entries/[id]/duplicate/route.js.map +7 -0
  126. package/dist/modules/staff/api/timesheets/time-entries/[id]/segments/[segmentId]/route.js +29 -10
  127. package/dist/modules/staff/api/timesheets/time-entries/[id]/segments/[segmentId]/route.js.map +2 -2
  128. package/dist/modules/staff/api/timesheets/time-entries/[id]/segments/route.js +31 -18
  129. package/dist/modules/staff/api/timesheets/time-entries/[id]/segments/route.js.map +2 -2
  130. package/dist/modules/staff/api/timesheets/time-entries/[id]/timer-start/route.js +21 -7
  131. package/dist/modules/staff/api/timesheets/time-entries/[id]/timer-start/route.js.map +2 -2
  132. package/dist/modules/staff/api/timesheets/time-entries/[id]/timer-stop/route.js +27 -7
  133. package/dist/modules/staff/api/timesheets/time-entries/[id]/timer-stop/route.js.map +2 -2
  134. package/dist/modules/staff/api/timesheets/time-entries/bulk/route.js +215 -32
  135. package/dist/modules/staff/api/timesheets/time-entries/bulk/route.js.map +2 -2
  136. package/dist/modules/staff/api/timesheets/time-entries/copy-day/route.js +378 -0
  137. package/dist/modules/staff/api/timesheets/time-entries/copy-day/route.js.map +7 -0
  138. package/dist/modules/staff/api/timesheets/time-entries/overlaps/route.js +340 -0
  139. package/dist/modules/staff/api/timesheets/time-entries/overlaps/route.js.map +7 -0
  140. package/dist/modules/staff/api/timesheets/time-entries/route.js +238 -26
  141. package/dist/modules/staff/api/timesheets/time-entries/route.js.map +2 -2
  142. package/dist/modules/staff/api/timesheets/time-entries/start-timer/route.js +23 -8
  143. package/dist/modules/staff/api/timesheets/time-entries/start-timer/route.js.map +2 -2
  144. package/dist/modules/staff/api/timesheets/time-projects/[id]/change-currency/route.js +209 -0
  145. package/dist/modules/staff/api/timesheets/time-projects/[id]/change-currency/route.js.map +7 -0
  146. package/dist/modules/staff/api/timesheets/time-projects/[id]/employees/route.js +59 -2
  147. package/dist/modules/staff/api/timesheets/time-projects/[id]/employees/route.js.map +2 -2
  148. package/dist/modules/staff/api/timesheets/time-projects/route.js +264 -22
  149. package/dist/modules/staff/api/timesheets/time-projects/route.js.map +2 -2
  150. package/dist/modules/staff/backend/staff/time-tracking/board/page.js +110 -0
  151. package/dist/modules/staff/backend/staff/time-tracking/board/page.js.map +7 -0
  152. package/dist/modules/staff/backend/staff/time-tracking/board/page.meta.js +18 -0
  153. package/dist/modules/staff/backend/staff/time-tracking/board/page.meta.js.map +7 -0
  154. package/dist/modules/staff/backend/staff/time-tracking/entries/page.js +981 -0
  155. package/dist/modules/staff/backend/staff/time-tracking/entries/page.js.map +7 -0
  156. package/dist/modules/staff/backend/staff/time-tracking/entries/page.meta.js +18 -0
  157. package/dist/modules/staff/backend/staff/time-tracking/entries/page.meta.js.map +7 -0
  158. package/dist/modules/staff/backend/staff/time-tracking/page.js +583 -0
  159. package/dist/modules/staff/backend/staff/time-tracking/page.js.map +7 -0
  160. package/dist/modules/staff/backend/staff/time-tracking/page.meta.js +15 -0
  161. package/dist/modules/staff/backend/staff/time-tracking/page.meta.js.map +7 -0
  162. package/dist/modules/staff/backend/staff/time-tracking/projects/[id]/board/page.js +65 -0
  163. package/dist/modules/staff/backend/staff/time-tracking/projects/[id]/board/page.js.map +7 -0
  164. package/dist/modules/staff/backend/staff/time-tracking/projects/[id]/board/page.meta.js +16 -0
  165. package/dist/modules/staff/backend/staff/time-tracking/projects/[id]/board/page.meta.js.map +7 -0
  166. package/dist/modules/staff/backend/staff/time-tracking/projects/[id]/edit/page.js +229 -0
  167. package/dist/modules/staff/backend/staff/time-tracking/projects/[id]/edit/page.js.map +7 -0
  168. package/dist/modules/staff/backend/staff/time-tracking/projects/[id]/edit/page.meta.js +16 -0
  169. package/dist/modules/staff/backend/staff/time-tracking/projects/[id]/edit/page.meta.js.map +7 -0
  170. package/dist/modules/staff/backend/staff/time-tracking/projects/[id]/page.js +1014 -0
  171. package/dist/modules/staff/backend/staff/time-tracking/projects/[id]/page.js.map +7 -0
  172. package/dist/modules/staff/backend/staff/time-tracking/projects/[id]/page.meta.js +16 -0
  173. package/dist/modules/staff/backend/staff/time-tracking/projects/[id]/page.meta.js.map +7 -0
  174. package/dist/modules/staff/backend/staff/time-tracking/projects/create/page.js +97 -0
  175. package/dist/modules/staff/backend/staff/time-tracking/projects/create/page.js.map +7 -0
  176. package/dist/modules/staff/backend/staff/time-tracking/projects/create/page.meta.js +16 -0
  177. package/dist/modules/staff/backend/staff/time-tracking/projects/create/page.meta.js.map +7 -0
  178. package/dist/modules/staff/backend/staff/time-tracking/projects/page.js +1040 -0
  179. package/dist/modules/staff/backend/staff/time-tracking/projects/page.js.map +7 -0
  180. package/dist/modules/staff/backend/staff/time-tracking/projects/page.meta.js +19 -0
  181. package/dist/modules/staff/backend/staff/time-tracking/projects/page.meta.js.map +7 -0
  182. package/dist/modules/staff/backend/staff/time-tracking/projects/projectFormConfig.js +353 -0
  183. package/dist/modules/staff/backend/staff/time-tracking/projects/projectFormConfig.js.map +7 -0
  184. package/dist/modules/staff/backend/staff/time-tracking/reports/[id]/page.js +457 -0
  185. package/dist/modules/staff/backend/staff/time-tracking/reports/[id]/page.js.map +7 -0
  186. package/dist/modules/staff/backend/staff/time-tracking/reports/[id]/page.meta.js +16 -0
  187. package/dist/modules/staff/backend/staff/time-tracking/reports/[id]/page.meta.js.map +7 -0
  188. package/dist/modules/staff/backend/staff/time-tracking/reports/create/page.js +697 -0
  189. package/dist/modules/staff/backend/staff/time-tracking/reports/create/page.js.map +7 -0
  190. package/dist/modules/staff/backend/staff/time-tracking/reports/create/page.meta.js +16 -0
  191. package/dist/modules/staff/backend/staff/time-tracking/reports/create/page.meta.js.map +7 -0
  192. package/dist/modules/staff/backend/staff/time-tracking/reports/page.js +174 -0
  193. package/dist/modules/staff/backend/staff/time-tracking/reports/page.js.map +7 -0
  194. package/dist/modules/staff/backend/staff/time-tracking/reports/page.meta.js +18 -0
  195. package/dist/modules/staff/backend/staff/time-tracking/reports/page.meta.js.map +7 -0
  196. package/dist/modules/staff/backend/staff/time-tracking/settings/page.js +546 -0
  197. package/dist/modules/staff/backend/staff/time-tracking/settings/page.js.map +7 -0
  198. package/dist/modules/staff/backend/staff/time-tracking/settings/page.meta.js +18 -0
  199. package/dist/modules/staff/backend/staff/time-tracking/settings/page.meta.js.map +7 -0
  200. package/dist/modules/staff/backend/staff/time-tracking/timesheet/GridView.js +710 -0
  201. package/dist/modules/staff/backend/staff/time-tracking/timesheet/GridView.js.map +7 -0
  202. package/dist/modules/staff/backend/staff/time-tracking/timesheet/page.js +646 -0
  203. package/dist/modules/staff/backend/staff/time-tracking/timesheet/page.js.map +7 -0
  204. package/dist/modules/staff/backend/staff/time-tracking/timesheet/page.meta.js +18 -0
  205. package/dist/modules/staff/backend/staff/time-tracking/timesheet/page.meta.js.map +7 -0
  206. package/dist/modules/staff/backend/staff/timesheets/page.js +4 -793
  207. package/dist/modules/staff/backend/staff/timesheets/page.js.map +2 -2
  208. package/dist/modules/staff/backend/staff/timesheets/page.meta.js +2 -7
  209. package/dist/modules/staff/backend/staff/timesheets/page.meta.js.map +2 -2
  210. package/dist/modules/staff/backend/staff/timesheets/projects/[id]/edit/page.js +5 -136
  211. package/dist/modules/staff/backend/staff/timesheets/projects/[id]/edit/page.js.map +2 -2
  212. package/dist/modules/staff/backend/staff/timesheets/projects/[id]/edit/page.meta.js +1 -7
  213. package/dist/modules/staff/backend/staff/timesheets/projects/[id]/edit/page.meta.js.map +2 -2
  214. package/dist/modules/staff/backend/staff/timesheets/projects/[id]/page.js +5 -445
  215. package/dist/modules/staff/backend/staff/timesheets/projects/[id]/page.js.map +3 -3
  216. package/dist/modules/staff/backend/staff/timesheets/projects/[id]/page.meta.js +1 -7
  217. package/dist/modules/staff/backend/staff/timesheets/projects/[id]/page.meta.js.map +2 -2
  218. package/dist/modules/staff/backend/staff/timesheets/projects/create/page.js +4 -76
  219. package/dist/modules/staff/backend/staff/timesheets/projects/create/page.js.map +2 -2
  220. package/dist/modules/staff/backend/staff/timesheets/projects/create/page.meta.js +1 -7
  221. package/dist/modules/staff/backend/staff/timesheets/projects/create/page.meta.js.map +2 -2
  222. package/dist/modules/staff/backend/staff/timesheets/projects/page.js +4 -613
  223. package/dist/modules/staff/backend/staff/timesheets/projects/page.js.map +2 -2
  224. package/dist/modules/staff/backend/staff/timesheets/projects/page.meta.js +2 -17
  225. package/dist/modules/staff/backend/staff/timesheets/projects/page.meta.js.map +2 -2
  226. package/dist/modules/staff/ce.js +40 -0
  227. package/dist/modules/staff/ce.js.map +2 -2
  228. package/dist/modules/staff/cli.js +166 -1
  229. package/dist/modules/staff/cli.js.map +2 -2
  230. package/dist/modules/staff/commands/index.js +5 -0
  231. package/dist/modules/staff/commands/index.js.map +2 -2
  232. package/dist/modules/staff/commands/timesheets-entries.js +569 -21
  233. package/dist/modules/staff/commands/timesheets-entries.js.map +3 -3
  234. package/dist/modules/staff/commands/timesheets-projects.js +450 -7
  235. package/dist/modules/staff/commands/timesheets-projects.js.map +2 -2
  236. package/dist/modules/staff/commands/timesheets-reports.js +1036 -0
  237. package/dist/modules/staff/commands/timesheets-reports.js.map +7 -0
  238. package/dist/modules/staff/commands/timesheets-tags.js +1015 -0
  239. package/dist/modules/staff/commands/timesheets-tags.js.map +7 -0
  240. package/dist/modules/staff/commands/timesheets-task-comments.js +438 -0
  241. package/dist/modules/staff/commands/timesheets-task-comments.js.map +7 -0
  242. package/dist/modules/staff/commands/timesheets-task-statuses.js +723 -0
  243. package/dist/modules/staff/commands/timesheets-task-statuses.js.map +7 -0
  244. package/dist/modules/staff/commands/timesheets-tasks.js +834 -0
  245. package/dist/modules/staff/commands/timesheets-tasks.js.map +7 -0
  246. package/dist/modules/staff/data/enrichers.js +426 -12
  247. package/dist/modules/staff/data/enrichers.js.map +2 -2
  248. package/dist/modules/staff/data/entities.js +552 -3
  249. package/dist/modules/staff/data/entities.js.map +2 -2
  250. package/dist/modules/staff/data/extensions.js +41 -0
  251. package/dist/modules/staff/data/extensions.js.map +7 -0
  252. package/dist/modules/staff/data/validators.js +254 -9
  253. package/dist/modules/staff/data/validators.js.map +2 -2
  254. package/dist/modules/staff/di.js +42 -1
  255. package/dist/modules/staff/di.js.map +2 -2
  256. package/dist/modules/staff/events.js +84 -6
  257. package/dist/modules/staff/events.js.map +2 -2
  258. package/dist/modules/staff/events.payloads.js +158 -0
  259. package/dist/modules/staff/events.payloads.js.map +7 -0
  260. package/dist/modules/staff/extension-points.js +261 -0
  261. package/dist/modules/staff/extension-points.js.map +7 -0
  262. package/dist/modules/staff/frontend/[orgSlug]/portal/time-reports/[id]/page.js +138 -0
  263. package/dist/modules/staff/frontend/[orgSlug]/portal/time-reports/[id]/page.js.map +7 -0
  264. package/dist/modules/staff/frontend/[orgSlug]/portal/time-reports/[id]/page.meta.js +12 -0
  265. package/dist/modules/staff/frontend/[orgSlug]/portal/time-reports/[id]/page.meta.js.map +7 -0
  266. package/dist/modules/staff/frontend/[orgSlug]/portal/time-reports/page.js +133 -0
  267. package/dist/modules/staff/frontend/[orgSlug]/portal/time-reports/page.js.map +7 -0
  268. package/dist/modules/staff/frontend/[orgSlug]/portal/time-reports/page.meta.js +19 -0
  269. package/dist/modules/staff/frontend/[orgSlug]/portal/time-reports/page.meta.js.map +7 -0
  270. package/dist/modules/staff/lib/crud.js +11 -1
  271. package/dist/modules/staff/lib/crud.js.map +2 -2
  272. package/dist/modules/staff/lib/time-tracking/access.js +92 -0
  273. package/dist/modules/staff/lib/time-tracking/access.js.map +7 -0
  274. package/dist/modules/staff/lib/time-tracking/billability.js +39 -0
  275. package/dist/modules/staff/lib/time-tracking/billability.js.map +7 -0
  276. package/dist/modules/staff/lib/time-tracking/capacity.js +68 -0
  277. package/dist/modules/staff/lib/time-tracking/capacity.js.map +7 -0
  278. package/dist/modules/staff/lib/time-tracking/componentContracts.js +15 -0
  279. package/dist/modules/staff/lib/time-tracking/componentContracts.js.map +7 -0
  280. package/dist/modules/staff/lib/time-tracking/cost.js +82 -0
  281. package/dist/modules/staff/lib/time-tracking/cost.js.map +7 -0
  282. package/dist/modules/staff/lib/time-tracking/duration.js +55 -0
  283. package/dist/modules/staff/lib/time-tracking/duration.js.map +7 -0
  284. package/dist/modules/staff/lib/time-tracking/featureAccess.js +41 -0
  285. package/dist/modules/staff/lib/time-tracking/featureAccess.js.map +7 -0
  286. package/dist/modules/staff/lib/time-tracking/interval.js +72 -0
  287. package/dist/modules/staff/lib/time-tracking/interval.js.map +7 -0
  288. package/dist/modules/staff/lib/time-tracking/migrateProjectCodes.js +88 -0
  289. package/dist/modules/staff/lib/time-tracking/migrateProjectCodes.js.map +7 -0
  290. package/dist/modules/staff/lib/time-tracking/moneyVisibility.js +10 -0
  291. package/dist/modules/staff/lib/time-tracking/moneyVisibility.js.map +7 -0
  292. package/dist/modules/staff/lib/time-tracking/overlap.js +87 -0
  293. package/dist/modules/staff/lib/time-tracking/overlap.js.map +7 -0
  294. package/dist/modules/staff/lib/time-tracking/portalRecipients.js +27 -0
  295. package/dist/modules/staff/lib/time-tracking/portalRecipients.js.map +7 -0
  296. package/dist/modules/staff/lib/time-tracking/portalReports.js +16 -0
  297. package/dist/modules/staff/lib/time-tracking/portalReports.js.map +7 -0
  298. package/dist/modules/staff/lib/time-tracking/projectCode.js +135 -0
  299. package/dist/modules/staff/lib/time-tracking/projectCode.js.map +7 -0
  300. package/dist/modules/staff/lib/time-tracking/reapplyRounding.js +187 -0
  301. package/dist/modules/staff/lib/time-tracking/reapplyRounding.js.map +7 -0
  302. package/dist/modules/staff/lib/time-tracking/recalculationRunner.js +80 -0
  303. package/dist/modules/staff/lib/time-tracking/recalculationRunner.js.map +7 -0
  304. package/dist/modules/staff/lib/time-tracking/recalculations.js +56 -0
  305. package/dist/modules/staff/lib/time-tracking/recalculations.js.map +7 -0
  306. package/dist/modules/staff/lib/time-tracking/registries/invoke.js +63 -0
  307. package/dist/modules/staff/lib/time-tracking/registries/invoke.js.map +7 -0
  308. package/dist/modules/staff/lib/time-tracking/registries/registry.js +62 -0
  309. package/dist/modules/staff/lib/time-tracking/registries/registry.js.map +7 -0
  310. package/dist/modules/staff/lib/time-tracking/registries/scope.js +21 -0
  311. package/dist/modules/staff/lib/time-tracking/registries/scope.js.map +7 -0
  312. package/dist/modules/staff/lib/time-tracking/rollup.js +34 -0
  313. package/dist/modules/staff/lib/time-tracking/rollup.js.map +7 -0
  314. package/dist/modules/staff/lib/time-tracking/rounding.js +66 -0
  315. package/dist/modules/staff/lib/time-tracking/rounding.js.map +7 -0
  316. package/dist/modules/staff/lib/time-tracking/roundingImpact.js +39 -0
  317. package/dist/modules/staff/lib/time-tracking/roundingImpact.js.map +7 -0
  318. package/dist/modules/staff/lib/time-tracking/settingKeys.js +214 -0
  319. package/dist/modules/staff/lib/time-tracking/settingKeys.js.map +7 -0
  320. package/dist/modules/staff/lib/time-tracking/settings.js +87 -0
  321. package/dist/modules/staff/lib/time-tracking/settings.js.map +7 -0
  322. package/dist/modules/staff/lib/time-tracking/sqlInClause.js +9 -0
  323. package/dist/modules/staff/lib/time-tracking/sqlInClause.js.map +7 -0
  324. package/dist/modules/staff/lib/time-tracking/timeEntrySources.js +48 -0
  325. package/dist/modules/staff/lib/time-tracking/timeEntrySources.js.map +7 -0
  326. package/dist/modules/staff/lib/time-tracking-ui/BoardFilterChips.js +283 -0
  327. package/dist/modules/staff/lib/time-tracking-ui/BoardFilterChips.js.map +7 -0
  328. package/dist/modules/staff/lib/time-tracking-ui/CustomerPicker.js +324 -0
  329. package/dist/modules/staff/lib/time-tracking-ui/CustomerPicker.js.map +7 -0
  330. package/dist/modules/staff/lib/time-tracking-ui/DurationInput.js +121 -0
  331. package/dist/modules/staff/lib/time-tracking-ui/DurationInput.js.map +7 -0
  332. package/dist/modules/staff/lib/time-tracking-ui/EntryDetailsFields.js +188 -0
  333. package/dist/modules/staff/lib/time-tracking-ui/EntryDetailsFields.js.map +7 -0
  334. package/dist/modules/staff/lib/time-tracking-ui/EntryFilterBar.js +89 -0
  335. package/dist/modules/staff/lib/time-tracking-ui/EntryFilterBar.js.map +7 -0
  336. package/dist/modules/staff/lib/time-tracking-ui/KanbanBoard.js +855 -0
  337. package/dist/modules/staff/lib/time-tracking-ui/KanbanBoard.js.map +7 -0
  338. package/dist/modules/staff/lib/time-tracking-ui/KanbanCard.js +360 -0
  339. package/dist/modules/staff/lib/time-tracking-ui/KanbanCard.js.map +7 -0
  340. package/dist/modules/staff/lib/time-tracking-ui/KanbanColumn.js +259 -0
  341. package/dist/modules/staff/lib/time-tracking-ui/KanbanColumn.js.map +7 -0
  342. package/dist/modules/staff/lib/time-tracking-ui/NewTaskDialog.js +238 -0
  343. package/dist/modules/staff/lib/time-tracking-ui/NewTaskDialog.js.map +7 -0
  344. package/dist/modules/staff/lib/time-tracking-ui/NoProjectAccess.js +130 -0
  345. package/dist/modules/staff/lib/time-tracking-ui/NoProjectAccess.js.map +7 -0
  346. package/dist/modules/staff/lib/time-tracking-ui/PeriodSelector.js +98 -0
  347. package/dist/modules/staff/lib/time-tracking-ui/PeriodSelector.js.map +7 -0
  348. package/dist/modules/staff/lib/time-tracking-ui/PhasePlaceholder.js +27 -0
  349. package/dist/modules/staff/lib/time-tracking-ui/PhasePlaceholder.js.map +7 -0
  350. package/dist/modules/staff/lib/time-tracking-ui/ProjectCodeField.js +166 -0
  351. package/dist/modules/staff/lib/time-tracking-ui/ProjectCodeField.js.map +7 -0
  352. package/dist/modules/staff/lib/time-tracking-ui/ProjectCurrencyField.js +295 -0
  353. package/dist/modules/staff/lib/time-tracking-ui/ProjectCurrencyField.js.map +7 -0
  354. package/dist/modules/staff/lib/time-tracking-ui/ProjectFormSections.js +212 -0
  355. package/dist/modules/staff/lib/time-tracking-ui/ProjectFormSections.js.map +7 -0
  356. package/dist/modules/staff/lib/time-tracking-ui/ProjectTeamDrawer.js +729 -0
  357. package/dist/modules/staff/lib/time-tracking-ui/ProjectTeamDrawer.js.map +7 -0
  358. package/dist/modules/staff/lib/time-tracking-ui/ReportSheet.js +217 -0
  359. package/dist/modules/staff/lib/time-tracking-ui/ReportSheet.js.map +7 -0
  360. package/dist/modules/staff/lib/time-tracking-ui/TagPicker.js +158 -0
  361. package/dist/modules/staff/lib/time-tracking-ui/TagPicker.js.map +7 -0
  362. package/dist/modules/staff/lib/time-tracking-ui/TaskBoardScreen.js +224 -0
  363. package/dist/modules/staff/lib/time-tracking-ui/TaskBoardScreen.js.map +7 -0
  364. package/dist/modules/staff/lib/time-tracking-ui/TaskCommentThread.js +93 -0
  365. package/dist/modules/staff/lib/time-tracking-ui/TaskCommentThread.js.map +7 -0
  366. package/dist/modules/staff/lib/time-tracking-ui/TaskDrawer.js +1196 -0
  367. package/dist/modules/staff/lib/time-tracking-ui/TaskDrawer.js.map +7 -0
  368. package/dist/modules/staff/lib/time-tracking-ui/TaskListView.js +176 -0
  369. package/dist/modules/staff/lib/time-tracking-ui/TaskListView.js.map +7 -0
  370. package/dist/modules/staff/lib/time-tracking-ui/TaskPicker.js +307 -0
  371. package/dist/modules/staff/lib/time-tracking-ui/TaskPicker.js.map +7 -0
  372. package/dist/modules/staff/lib/time-tracking-ui/TaskQuickLog.js +284 -0
  373. package/dist/modules/staff/lib/time-tracking-ui/TaskQuickLog.js.map +7 -0
  374. package/dist/modules/staff/lib/time-tracking-ui/TimeEntriesSummaryFooter.js +73 -0
  375. package/dist/modules/staff/lib/time-tracking-ui/TimeEntriesSummaryFooter.js.map +7 -0
  376. package/dist/modules/staff/lib/time-tracking-ui/TimeEntryDialog.js +1499 -0
  377. package/dist/modules/staff/lib/time-tracking-ui/TimeEntryDialog.js.map +7 -0
  378. package/dist/modules/staff/lib/time-tracking-ui/TimeEntryDurationCell.js +95 -0
  379. package/dist/modules/staff/lib/time-tracking-ui/TimeEntryDurationCell.js.map +7 -0
  380. package/dist/modules/staff/lib/time-tracking-ui/TimesheetCalendar.js +188 -0
  381. package/dist/modules/staff/lib/time-tracking-ui/TimesheetCalendar.js.map +7 -0
  382. package/dist/modules/staff/lib/time-tracking-ui/TimesheetPeriodFooter.js +61 -0
  383. package/dist/modules/staff/lib/time-tracking-ui/TimesheetPeriodFooter.js.map +7 -0
  384. package/dist/modules/staff/lib/time-tracking-ui/TimesheetViewSwitch.js +45 -0
  385. package/dist/modules/staff/lib/time-tracking-ui/TimesheetViewSwitch.js.map +7 -0
  386. package/dist/modules/staff/lib/time-tracking-ui/boardDirectory.js +176 -0
  387. package/dist/modules/staff/lib/time-tracking-ui/boardDirectory.js.map +7 -0
  388. package/dist/modules/staff/lib/time-tracking-ui/boardFilters.js +82 -0
  389. package/dist/modules/staff/lib/time-tracking-ui/boardFilters.js.map +7 -0
  390. package/dist/modules/staff/lib/time-tracking-ui/boardSummary.js +5 -0
  391. package/dist/modules/staff/lib/time-tracking-ui/boardSummary.js.map +7 -0
  392. package/dist/modules/staff/lib/time-tracking-ui/kanbanBoardData.js +152 -0
  393. package/dist/modules/staff/lib/time-tracking-ui/kanbanBoardData.js.map +7 -0
  394. package/dist/modules/staff/lib/time-tracking-ui/projectTeamAssignment.js +92 -0
  395. package/dist/modules/staff/lib/time-tracking-ui/projectTeamAssignment.js.map +7 -0
  396. package/dist/modules/staff/lib/time-tracking-ui/taskDrawerData.js +111 -0
  397. package/dist/modules/staff/lib/time-tracking-ui/taskDrawerData.js.map +7 -0
  398. package/dist/modules/staff/lib/time-tracking-ui/timeEntryDialogState.js +243 -0
  399. package/dist/modules/staff/lib/time-tracking-ui/timeEntryDialogState.js.map +7 -0
  400. package/dist/modules/staff/lib/time-tracking-ui/timeEntryListData.js +151 -0
  401. package/dist/modules/staff/lib/time-tracking-ui/timeEntryListData.js.map +7 -0
  402. package/dist/modules/staff/lib/time-tracking-ui/timeTrackingSettingsForm.js +105 -0
  403. package/dist/modules/staff/lib/time-tracking-ui/timeTrackingSettingsForm.js.map +7 -0
  404. package/dist/modules/staff/lib/time-tracking-ui/timesheetData.js +132 -0
  405. package/dist/modules/staff/lib/time-tracking-ui/timesheetData.js.map +7 -0
  406. package/dist/modules/staff/lib/time-tracking-ui/timesheetPeriod.js +205 -0
  407. package/dist/modules/staff/lib/time-tracking-ui/timesheetPeriod.js.map +7 -0
  408. package/dist/modules/staff/lib/time-tracking-ui/timesheetTargets.js +53 -0
  409. package/dist/modules/staff/lib/time-tracking-ui/timesheetTargets.js.map +7 -0
  410. package/dist/modules/staff/lib/time-tracking-ui/useBoardFilters.js +77 -0
  411. package/dist/modules/staff/lib/time-tracking-ui/useBoardFilters.js.map +7 -0
  412. package/dist/modules/staff/lib/time-tracking-ui/useTimesheetPreferences.js +151 -0
  413. package/dist/modules/staff/lib/time-tracking-ui/useTimesheetPreferences.js.map +7 -0
  414. package/dist/modules/staff/lib/timeTrackingSeeds.js +809 -0
  415. package/dist/modules/staff/lib/timeTrackingSeeds.js.map +7 -0
  416. package/dist/modules/staff/lib/timesheets/timeEntryCacheInvalidation.js +2 -3
  417. package/dist/modules/staff/lib/timesheets/timeEntryCacheInvalidation.js.map +2 -2
  418. package/dist/modules/staff/lib/timesheets/timeEntryDecoration.js +152 -0
  419. package/dist/modules/staff/lib/timesheets/timeEntryDecoration.js.map +7 -0
  420. package/dist/modules/staff/lib/timesheets-projects/budgetBurn.js +36 -0
  421. package/dist/modules/staff/lib/timesheets-projects/budgetBurn.js.map +7 -0
  422. package/dist/modules/staff/lib/timesheets-projects/budgetThreshold.js +66 -0
  423. package/dist/modules/staff/lib/timesheets-projects/budgetThreshold.js.map +7 -0
  424. package/dist/modules/staff/lib/timesheets-projects/budgetThresholdState.js +78 -0
  425. package/dist/modules/staff/lib/timesheets-projects/budgetThresholdState.js.map +7 -0
  426. package/dist/modules/staff/lib/timesheets-projects/computeProjectFinancials.js +81 -0
  427. package/dist/modules/staff/lib/timesheets-projects/computeProjectFinancials.js.map +7 -0
  428. package/dist/modules/staff/lib/timesheets-projects/reportSelection.js +44 -0
  429. package/dist/modules/staff/lib/timesheets-projects/reportSelection.js.map +7 -0
  430. package/dist/modules/staff/lib/timesheets-projects-ui/AssignMembersDialog.js +169 -0
  431. package/dist/modules/staff/lib/timesheets-projects-ui/AssignMembersDialog.js.map +7 -0
  432. package/dist/modules/staff/lib/timesheets-projects-ui/ProjectBudgetCell.js +22 -0
  433. package/dist/modules/staff/lib/timesheets-projects-ui/ProjectBudgetCell.js.map +7 -0
  434. package/dist/modules/staff/lib/timesheets-projects-ui/ProjectCard.js +51 -1
  435. package/dist/modules/staff/lib/timesheets-projects-ui/ProjectCard.js.map +2 -2
  436. package/dist/modules/staff/lib/timesheets-projects-ui/ProjectsBulkActionsBar.js +70 -0
  437. package/dist/modules/staff/lib/timesheets-projects-ui/ProjectsBulkActionsBar.js.map +7 -0
  438. package/dist/modules/staff/lib/timesheets-reports/buildReportSheet.js +63 -0
  439. package/dist/modules/staff/lib/timesheets-reports/buildReportSheet.js.map +7 -0
  440. package/dist/modules/staff/lib/timesheets-reports/loadReportData.js +170 -0
  441. package/dist/modules/staff/lib/timesheets-reports/loadReportData.js.map +7 -0
  442. package/dist/modules/staff/lib/timesheets-reports/pdf.js +205 -0
  443. package/dist/modules/staff/lib/timesheets-reports/pdf.js.map +7 -0
  444. package/dist/modules/staff/lib/timesheets-reports/reportApprovalPolicies.js +68 -0
  445. package/dist/modules/staff/lib/timesheets-reports/reportApprovalPolicies.js.map +7 -0
  446. package/dist/modules/staff/lib/timesheets-reports/reportConfigData.js +135 -0
  447. package/dist/modules/staff/lib/timesheets-reports/reportConfigData.js.map +7 -0
  448. package/dist/modules/staff/lib/timesheets-reports/reportConfigState.js +48 -0
  449. package/dist/modules/staff/lib/timesheets-reports/reportConfigState.js.map +7 -0
  450. package/dist/modules/staff/lib/timesheets-reports/reportExport.js +262 -0
  451. package/dist/modules/staff/lib/timesheets-reports/reportExport.js.map +7 -0
  452. package/dist/modules/staff/lib/timesheets-reports/reportExportFormats.js +34 -0
  453. package/dist/modules/staff/lib/timesheets-reports/reportExportFormats.js.map +7 -0
  454. package/dist/modules/staff/lib/timesheets-reports/reportGroupings.js +79 -0
  455. package/dist/modules/staff/lib/timesheets-reports/reportGroupings.js.map +7 -0
  456. package/dist/modules/staff/lib/timesheets-reports/reportLabels.js +11 -0
  457. package/dist/modules/staff/lib/timesheets-reports/reportLabels.js.map +7 -0
  458. package/dist/modules/staff/lib/timesheets-reports/reportReference.js +83 -0
  459. package/dist/modules/staff/lib/timesheets-reports/reportReference.js.map +7 -0
  460. package/dist/modules/staff/lib/timesheets-reports/reportRows.js +42 -0
  461. package/dist/modules/staff/lib/timesheets-reports/reportRows.js.map +7 -0
  462. package/dist/modules/staff/lib/timesheets-reports/reportSheetData.js +107 -0
  463. package/dist/modules/staff/lib/timesheets-reports/reportSheetData.js.map +7 -0
  464. package/dist/modules/staff/lib/timesheets-reports/reportTotals.js +289 -0
  465. package/dist/modules/staff/lib/timesheets-reports/reportTotals.js.map +7 -0
  466. package/dist/modules/staff/lib/timesheets-reports/xlsx.js +145 -0
  467. package/dist/modules/staff/lib/timesheets-reports/xlsx.js.map +7 -0
  468. package/dist/modules/staff/lib/timesheets-tasks/computeTaskRollups.js +116 -0
  469. package/dist/modules/staff/lib/timesheets-tasks/computeTaskRollups.js.map +7 -0
  470. package/dist/modules/staff/lib/timesheets-tasks/defaultStatusTemplate.js +50 -0
  471. package/dist/modules/staff/lib/timesheets-tasks/defaultStatusTemplate.js.map +7 -0
  472. package/dist/modules/staff/lib/timesheets-tasks/seedProjectStatuses.js +27 -0
  473. package/dist/modules/staff/lib/timesheets-tasks/seedProjectStatuses.js.map +7 -0
  474. package/dist/modules/staff/lib/timesheets-tasks/statusPositions.js +65 -0
  475. package/dist/modules/staff/lib/timesheets-tasks/statusPositions.js.map +7 -0
  476. package/dist/modules/staff/lib/timesheets-tasks/statusSlug.js +31 -0
  477. package/dist/modules/staff/lib/timesheets-tasks/statusSlug.js.map +7 -0
  478. package/dist/modules/staff/lib/timesheets-tasks/taskHierarchy.js +31 -0
  479. package/dist/modules/staff/lib/timesheets-tasks/taskHierarchy.js.map +7 -0
  480. package/dist/modules/staff/lib/timesheets-tasks/taskHoursTotals.js +17 -0
  481. package/dist/modules/staff/lib/timesheets-tasks/taskHoursTotals.js.map +7 -0
  482. package/dist/modules/staff/lib/timesheets-tasks/taskReference.js +51 -0
  483. package/dist/modules/staff/lib/timesheets-tasks/taskReference.js.map +7 -0
  484. package/dist/modules/staff/lib/timesheets-ui/CreateProjectDialog.js +11 -4
  485. package/dist/modules/staff/lib/timesheets-ui/CreateProjectDialog.js.map +2 -2
  486. package/dist/modules/staff/lib/timesheets-ui/ListView.js +369 -111
  487. package/dist/modules/staff/lib/timesheets-ui/ListView.js.map +2 -2
  488. package/dist/modules/staff/lib/timesheets-ui/TimerBar.js +47 -2
  489. package/dist/modules/staff/lib/timesheets-ui/TimerBar.js.map +2 -2
  490. package/dist/modules/staff/migrations/Migration20260813073131_staff.js +79 -0
  491. package/dist/modules/staff/migrations/Migration20260813073131_staff.js.map +7 -0
  492. package/dist/modules/staff/migrations/Migration20260824143357_staff.js +15 -0
  493. package/dist/modules/staff/migrations/Migration20260824143357_staff.js.map +7 -0
  494. package/dist/modules/staff/notifications.client.js +16 -0
  495. package/dist/modules/staff/notifications.client.js.map +7 -0
  496. package/dist/modules/staff/notifications.handlers.js +80 -0
  497. package/dist/modules/staff/notifications.handlers.js.map +7 -0
  498. package/dist/modules/staff/notifications.js +168 -0
  499. package/dist/modules/staff/notifications.js.map +2 -2
  500. package/dist/modules/staff/search.js +159 -1
  501. package/dist/modules/staff/search.js.map +2 -2
  502. package/dist/modules/staff/setup.js +20 -1
  503. package/dist/modules/staff/setup.js.map +2 -2
  504. package/dist/modules/staff/subscribers/time-project-access-requested-notification.js +68 -0
  505. package/dist/modules/staff/subscribers/time-project-access-requested-notification.js.map +7 -0
  506. package/dist/modules/staff/subscribers/time-project-budget-threshold-notification.js +149 -0
  507. package/dist/modules/staff/subscribers/time-project-budget-threshold-notification.js.map +7 -0
  508. package/dist/modules/staff/subscribers/time-report-approved-notification.js +78 -0
  509. package/dist/modules/staff/subscribers/time-report-approved-notification.js.map +7 -0
  510. package/dist/modules/staff/subscribers/time-report-portal-broadcast.js +78 -0
  511. package/dist/modules/staff/subscribers/time-report-portal-broadcast.js.map +7 -0
  512. package/dist/modules/staff/translations.js +4 -1
  513. package/dist/modules/staff/translations.js.map +2 -2
  514. package/dist/modules/staff/widgets/components.js +7 -0
  515. package/dist/modules/staff/widgets/components.js.map +7 -0
  516. package/dist/modules/staff/widgets/injection/timer-sidebar-indicator/widget.js +5 -4
  517. package/dist/modules/staff/widgets/injection/timer-sidebar-indicator/widget.js.map +2 -2
  518. package/dist/modules/staff/widgets/notifications/TimeProjectAccessRequestedRenderer.js +135 -0
  519. package/dist/modules/staff/widgets/notifications/TimeProjectAccessRequestedRenderer.js.map +7 -0
  520. package/dist/modules/staff/widgets/notifications/index.js +5 -0
  521. package/dist/modules/staff/widgets/notifications/index.js.map +7 -0
  522. package/dist/modules/staff/workers/timesheets-reapply-rounding.js +43 -0
  523. package/dist/modules/staff/workers/timesheets-reapply-rounding.js.map +7 -0
  524. package/generated/entities/staff_time_entry/index.ts +7 -0
  525. package/generated/entities/staff_time_entry_tag/index.ts +6 -0
  526. package/generated/entities/staff_time_project/index.ts +8 -0
  527. package/generated/entities/staff_time_report/index.ts +27 -0
  528. package/generated/entities/staff_time_report_entry/index.ts +12 -0
  529. package/generated/entities/staff_time_report_event/index.ts +9 -0
  530. package/generated/entities/staff_time_report_project/index.ts +6 -0
  531. package/generated/entities/staff_time_tag/index.ts +9 -0
  532. package/generated/entities/staff_time_task/index.ts +17 -0
  533. package/generated/entities/staff_time_task_comment/index.ts +9 -0
  534. package/generated/entities/staff_time_task_status/index.ts +13 -0
  535. package/generated/entities/staff_time_task_tag/index.ts +6 -0
  536. package/generated/entities.ids.generated.ts +11 -1
  537. package/generated/entity-fields-registry.ts +149 -0
  538. package/package.json +7 -7
  539. package/src/helpers/integration/timesheetFixtures.ts +31 -1
  540. package/src/modules/auth/README.md +1 -1
  541. package/src/modules/auth/cli.ts +35 -1
  542. package/src/modules/auth/i18n/de.json +7 -0
  543. package/src/modules/auth/i18n/en.json +7 -0
  544. package/src/modules/auth/i18n/es.json +7 -0
  545. package/src/modules/auth/i18n/ko.json +7 -0
  546. package/src/modules/auth/i18n/pl.json +7 -0
  547. package/src/modules/auth/lib/backendChrome.tsx +1 -0
  548. package/src/modules/customer_accounts/lib/customerRoleAcls.ts +79 -0
  549. package/src/modules/customer_accounts/setup.ts +1 -45
  550. package/src/modules/data_sync/AGENTS.md +34 -0
  551. package/src/modules/data_sync/api/options.ts +2 -0
  552. package/src/modules/data_sync/backend/data-sync/page.tsx +82 -33
  553. package/src/modules/data_sync/i18n/de.json +1 -0
  554. package/src/modules/data_sync/i18n/en.json +1 -0
  555. package/src/modules/data_sync/i18n/es.json +1 -0
  556. package/src/modules/data_sync/i18n/ko.json +1 -0
  557. package/src/modules/data_sync/i18n/pl.json +1 -0
  558. package/src/modules/data_sync/lib/adapter.ts +45 -0
  559. package/src/modules/data_sync/lib/start-controls.ts +89 -0
  560. package/src/modules/staff/AGENTS.md +769 -1
  561. package/src/modules/staff/acl.ts +9 -0
  562. package/src/modules/staff/ai-agents.ts +189 -0
  563. package/src/modules/staff/ai-tools/time-tracking-pack.ts +494 -0
  564. package/src/modules/staff/ai-tools/types.ts +82 -0
  565. package/src/modules/staff/ai-tools.ts +13 -0
  566. package/src/modules/staff/analytics.ts +113 -0
  567. package/src/modules/staff/api/guards.ts +85 -3
  568. package/src/modules/staff/api/leave-requests/accept/route.ts +0 -2
  569. package/src/modules/staff/api/leave-requests/reject/route.ts +0 -2
  570. package/src/modules/staff/api/portal/time-reports/[id]/route.ts +182 -0
  571. package/src/modules/staff/api/portal/time-reports/route.ts +270 -0
  572. package/src/modules/staff/api/team-members/self/route.ts +0 -2
  573. package/src/modules/staff/api/team-members/tags/assign/route.ts +0 -2
  574. package/src/modules/staff/api/team-members/tags/unassign/route.ts +0 -2
  575. package/src/modules/staff/api/timesheets/_shared/withTimesheetInterceptors.ts +292 -0
  576. package/src/modules/staff/api/timesheets/access-requests/route.ts +299 -0
  577. package/src/modules/staff/api/timesheets/my-projects/[projectId]/route.ts +15 -7
  578. package/src/modules/staff/api/timesheets/my-projects/route.ts +14 -1
  579. package/src/modules/staff/api/timesheets/my-work/myWorkAggregate.ts +90 -0
  580. package/src/modules/staff/api/timesheets/my-work/route.ts +475 -0
  581. package/src/modules/staff/api/timesheets/projects/kpis/route.ts +15 -2
  582. package/src/modules/staff/api/timesheets/reports/[id]/close/route.ts +203 -0
  583. package/src/modules/staff/api/timesheets/reports/[id]/export/route.ts +315 -0
  584. package/src/modules/staff/api/timesheets/reports/[id]/sheet/route.ts +220 -0
  585. package/src/modules/staff/api/timesheets/reports/[id]/unlock/route.ts +208 -0
  586. package/src/modules/staff/api/timesheets/reports/preview/route.ts +449 -0
  587. package/src/modules/staff/api/timesheets/reports/route.ts +386 -0
  588. package/src/modules/staff/api/timesheets/reports/shared.ts +121 -0
  589. package/src/modules/staff/api/timesheets/settings/reapply-rounding/route.ts +205 -0
  590. package/src/modules/staff/api/timesheets/settings/rounding-impact/route.ts +226 -0
  591. package/src/modules/staff/api/timesheets/settings/route.ts +242 -0
  592. package/src/modules/staff/api/timesheets/tags/access.ts +134 -0
  593. package/src/modules/staff/api/timesheets/tags/entry-assignments/route.ts +290 -0
  594. package/src/modules/staff/api/timesheets/tags/route.ts +177 -0
  595. package/src/modules/staff/api/timesheets/tags/task-assignments/route.ts +294 -0
  596. package/src/modules/staff/api/timesheets/task-statuses/route.ts +232 -0
  597. package/src/modules/staff/api/timesheets/tasks/[id]/comments/route.ts +532 -0
  598. package/src/modules/staff/api/timesheets/tasks/[id]/status/route.ts +271 -0
  599. package/src/modules/staff/api/timesheets/tasks/route.ts +443 -0
  600. package/src/modules/staff/api/timesheets/time-entries/[id]/duplicate/route.ts +306 -0
  601. package/src/modules/staff/api/timesheets/time-entries/[id]/segments/[segmentId]/route.ts +32 -10
  602. package/src/modules/staff/api/timesheets/time-entries/[id]/segments/route.ts +32 -17
  603. package/src/modules/staff/api/timesheets/time-entries/[id]/timer-start/route.ts +27 -6
  604. package/src/modules/staff/api/timesheets/time-entries/[id]/timer-stop/route.ts +36 -6
  605. package/src/modules/staff/api/timesheets/time-entries/bulk/route.ts +331 -34
  606. package/src/modules/staff/api/timesheets/time-entries/copy-day/route.ts +533 -0
  607. package/src/modules/staff/api/timesheets/time-entries/overlaps/route.ts +457 -0
  608. package/src/modules/staff/api/timesheets/time-entries/route.ts +378 -24
  609. package/src/modules/staff/api/timesheets/time-entries/start-timer/route.ts +31 -9
  610. package/src/modules/staff/api/timesheets/time-projects/[id]/change-currency/route.ts +246 -0
  611. package/src/modules/staff/api/timesheets/time-projects/[id]/employees/route.ts +62 -2
  612. package/src/modules/staff/api/timesheets/time-projects/route.ts +397 -22
  613. package/src/modules/staff/backend/staff/time-tracking/board/page.meta.ts +14 -0
  614. package/src/modules/staff/backend/staff/time-tracking/board/page.tsx +151 -0
  615. package/src/modules/staff/backend/staff/time-tracking/entries/page.meta.ts +14 -0
  616. package/src/modules/staff/backend/staff/time-tracking/entries/page.tsx +1229 -0
  617. package/src/modules/staff/backend/staff/time-tracking/page.meta.ts +11 -0
  618. package/src/modules/staff/backend/staff/time-tracking/page.tsx +823 -0
  619. package/src/modules/staff/backend/staff/time-tracking/projects/[id]/board/page.meta.ts +12 -0
  620. package/src/modules/staff/backend/staff/time-tracking/projects/[id]/board/page.tsx +81 -0
  621. package/src/modules/staff/backend/staff/time-tracking/projects/[id]/edit/page.meta.ts +12 -0
  622. package/src/modules/staff/backend/staff/time-tracking/projects/[id]/edit/page.tsx +275 -0
  623. package/src/modules/staff/backend/staff/time-tracking/projects/[id]/page.meta.ts +12 -0
  624. package/src/modules/staff/backend/staff/time-tracking/projects/[id]/page.tsx +1420 -0
  625. package/src/modules/staff/backend/staff/time-tracking/projects/create/page.meta.ts +12 -0
  626. package/src/modules/staff/backend/staff/time-tracking/projects/create/page.tsx +109 -0
  627. package/src/modules/staff/backend/staff/time-tracking/projects/page.meta.ts +15 -0
  628. package/src/modules/staff/backend/staff/time-tracking/projects/page.tsx +1254 -0
  629. package/src/modules/staff/backend/staff/time-tracking/projects/projectFormConfig.ts +466 -0
  630. package/src/modules/staff/backend/staff/time-tracking/reports/[id]/page.meta.ts +12 -0
  631. package/src/modules/staff/backend/staff/time-tracking/reports/[id]/page.tsx +632 -0
  632. package/src/modules/staff/backend/staff/time-tracking/reports/create/page.meta.ts +12 -0
  633. package/src/modules/staff/backend/staff/time-tracking/reports/create/page.tsx +867 -0
  634. package/src/modules/staff/backend/staff/time-tracking/reports/page.meta.ts +14 -0
  635. package/src/modules/staff/backend/staff/time-tracking/reports/page.tsx +246 -0
  636. package/src/modules/staff/backend/staff/time-tracking/settings/page.meta.ts +14 -0
  637. package/src/modules/staff/backend/staff/time-tracking/settings/page.tsx +701 -0
  638. package/src/modules/staff/backend/staff/time-tracking/timesheet/GridView.tsx +953 -0
  639. package/src/modules/staff/backend/staff/time-tracking/timesheet/page.meta.ts +14 -0
  640. package/src/modules/staff/backend/staff/time-tracking/timesheet/page.tsx +800 -0
  641. package/src/modules/staff/backend/staff/timesheets/page.meta.ts +6 -6
  642. package/src/modules/staff/backend/staff/timesheets/page.tsx +12 -1010
  643. package/src/modules/staff/backend/staff/timesheets/projects/[id]/edit/page.meta.ts +0 -6
  644. package/src/modules/staff/backend/staff/timesheets/projects/[id]/edit/page.tsx +4 -158
  645. package/src/modules/staff/backend/staff/timesheets/projects/[id]/page.meta.ts +0 -6
  646. package/src/modules/staff/backend/staff/timesheets/projects/[id]/page.tsx +4 -609
  647. package/src/modules/staff/backend/staff/timesheets/projects/create/page.meta.ts +0 -6
  648. package/src/modules/staff/backend/staff/timesheets/projects/create/page.tsx +3 -89
  649. package/src/modules/staff/backend/staff/timesheets/projects/page.meta.ts +2 -19
  650. package/src/modules/staff/backend/staff/timesheets/projects/page.tsx +3 -778
  651. package/src/modules/staff/ce.ts +63 -0
  652. package/src/modules/staff/cli.ts +207 -1
  653. package/src/modules/staff/commands/index.ts +5 -0
  654. package/src/modules/staff/commands/timesheets-entries.ts +1006 -28
  655. package/src/modules/staff/commands/timesheets-projects.ts +593 -7
  656. package/src/modules/staff/commands/timesheets-reports.ts +1329 -0
  657. package/src/modules/staff/commands/timesheets-tags.ts +1315 -0
  658. package/src/modules/staff/commands/timesheets-task-comments.ts +583 -0
  659. package/src/modules/staff/commands/timesheets-task-statuses.ts +889 -0
  660. package/src/modules/staff/commands/timesheets-tasks.ts +1121 -0
  661. package/src/modules/staff/data/enrichers.ts +675 -10
  662. package/src/modules/staff/data/entities.ts +494 -2
  663. package/src/modules/staff/data/extensions.ts +68 -0
  664. package/src/modules/staff/data/validators.ts +348 -3
  665. package/src/modules/staff/di.ts +96 -0
  666. package/src/modules/staff/events.payloads.ts +268 -0
  667. package/src/modules/staff/events.ts +87 -5
  668. package/src/modules/staff/extension-points.ts +308 -0
  669. package/src/modules/staff/frontend/[orgSlug]/portal/time-reports/[id]/page.meta.ts +10 -0
  670. package/src/modules/staff/frontend/[orgSlug]/portal/time-reports/[id]/page.tsx +225 -0
  671. package/src/modules/staff/frontend/[orgSlug]/portal/time-reports/page.meta.ts +17 -0
  672. package/src/modules/staff/frontend/[orgSlug]/portal/time-reports/page.tsx +182 -0
  673. package/src/modules/staff/i18n/de.json +899 -0
  674. package/src/modules/staff/i18n/en.json +899 -0
  675. package/src/modules/staff/i18n/es.json +899 -0
  676. package/src/modules/staff/i18n/ko.json +899 -0
  677. package/src/modules/staff/i18n/pl.json +899 -0
  678. package/src/modules/staff/lib/crud.ts +14 -3
  679. package/src/modules/staff/lib/time-tracking/access.ts +155 -0
  680. package/src/modules/staff/lib/time-tracking/billability.ts +68 -0
  681. package/src/modules/staff/lib/time-tracking/capacity.ts +124 -0
  682. package/src/modules/staff/lib/time-tracking/componentContracts.ts +26 -0
  683. package/src/modules/staff/lib/time-tracking/cost.ts +144 -0
  684. package/src/modules/staff/lib/time-tracking/duration.ts +68 -0
  685. package/src/modules/staff/lib/time-tracking/featureAccess.ts +115 -0
  686. package/src/modules/staff/lib/time-tracking/interval.ts +90 -0
  687. package/src/modules/staff/lib/time-tracking/migrateProjectCodes.ts +161 -0
  688. package/src/modules/staff/lib/time-tracking/moneyVisibility.ts +37 -0
  689. package/src/modules/staff/lib/time-tracking/overlap.ts +159 -0
  690. package/src/modules/staff/lib/time-tracking/portalRecipients.ts +76 -0
  691. package/src/modules/staff/lib/time-tracking/portalReports.ts +31 -0
  692. package/src/modules/staff/lib/time-tracking/projectCode.ts +221 -0
  693. package/src/modules/staff/lib/time-tracking/reapplyRounding.ts +313 -0
  694. package/src/modules/staff/lib/time-tracking/recalculationRunner.ts +118 -0
  695. package/src/modules/staff/lib/time-tracking/recalculations.ts +135 -0
  696. package/src/modules/staff/lib/time-tracking/registries/invoke.ts +122 -0
  697. package/src/modules/staff/lib/time-tracking/registries/registry.ts +124 -0
  698. package/src/modules/staff/lib/time-tracking/registries/scope.ts +56 -0
  699. package/src/modules/staff/lib/time-tracking/rollup.ts +59 -0
  700. package/src/modules/staff/lib/time-tracking/rounding.ts +126 -0
  701. package/src/modules/staff/lib/time-tracking/roundingImpact.ts +75 -0
  702. package/src/modules/staff/lib/time-tracking/settingKeys.ts +274 -0
  703. package/src/modules/staff/lib/time-tracking/settings.ts +168 -0
  704. package/src/modules/staff/lib/time-tracking/sqlInClause.ts +22 -0
  705. package/src/modules/staff/lib/time-tracking/timeEntrySources.ts +85 -0
  706. package/src/modules/staff/lib/time-tracking-ui/BoardFilterChips.tsx +360 -0
  707. package/src/modules/staff/lib/time-tracking-ui/CustomerPicker.tsx +427 -0
  708. package/src/modules/staff/lib/time-tracking-ui/DurationInput.tsx +192 -0
  709. package/src/modules/staff/lib/time-tracking-ui/EntryDetailsFields.tsx +224 -0
  710. package/src/modules/staff/lib/time-tracking-ui/EntryFilterBar.tsx +144 -0
  711. package/src/modules/staff/lib/time-tracking-ui/KanbanBoard.tsx +1065 -0
  712. package/src/modules/staff/lib/time-tracking-ui/KanbanCard.tsx +435 -0
  713. package/src/modules/staff/lib/time-tracking-ui/KanbanColumn.tsx +316 -0
  714. package/src/modules/staff/lib/time-tracking-ui/NewTaskDialog.tsx +291 -0
  715. package/src/modules/staff/lib/time-tracking-ui/NoProjectAccess.tsx +162 -0
  716. package/src/modules/staff/lib/time-tracking-ui/PeriodSelector.tsx +149 -0
  717. package/src/modules/staff/lib/time-tracking-ui/PhasePlaceholder.tsx +31 -0
  718. package/src/modules/staff/lib/time-tracking-ui/ProjectCodeField.tsx +230 -0
  719. package/src/modules/staff/lib/time-tracking-ui/ProjectCurrencyField.tsx +432 -0
  720. package/src/modules/staff/lib/time-tracking-ui/ProjectFormSections.tsx +323 -0
  721. package/src/modules/staff/lib/time-tracking-ui/ProjectTeamDrawer.tsx +930 -0
  722. package/src/modules/staff/lib/time-tracking-ui/ReportSheet.tsx +357 -0
  723. package/src/modules/staff/lib/time-tracking-ui/TagPicker.tsx +195 -0
  724. package/src/modules/staff/lib/time-tracking-ui/TaskBoardScreen.tsx +243 -0
  725. package/src/modules/staff/lib/time-tracking-ui/TaskCommentThread.tsx +137 -0
  726. package/src/modules/staff/lib/time-tracking-ui/TaskDrawer.tsx +1405 -0
  727. package/src/modules/staff/lib/time-tracking-ui/TaskListView.tsx +260 -0
  728. package/src/modules/staff/lib/time-tracking-ui/TaskPicker.tsx +435 -0
  729. package/src/modules/staff/lib/time-tracking-ui/TaskQuickLog.tsx +296 -0
  730. package/src/modules/staff/lib/time-tracking-ui/TimeEntriesSummaryFooter.tsx +97 -0
  731. package/src/modules/staff/lib/time-tracking-ui/TimeEntryDialog.tsx +1929 -0
  732. package/src/modules/staff/lib/time-tracking-ui/TimeEntryDurationCell.tsx +126 -0
  733. package/src/modules/staff/lib/time-tracking-ui/TimesheetCalendar.tsx +265 -0
  734. package/src/modules/staff/lib/time-tracking-ui/TimesheetPeriodFooter.tsx +78 -0
  735. package/src/modules/staff/lib/time-tracking-ui/TimesheetViewSwitch.tsx +70 -0
  736. package/src/modules/staff/lib/time-tracking-ui/boardDirectory.ts +231 -0
  737. package/src/modules/staff/lib/time-tracking-ui/boardFilters.ts +128 -0
  738. package/src/modules/staff/lib/time-tracking-ui/boardSummary.ts +20 -0
  739. package/src/modules/staff/lib/time-tracking-ui/kanbanBoardData.ts +228 -0
  740. package/src/modules/staff/lib/time-tracking-ui/projectTeamAssignment.ts +143 -0
  741. package/src/modules/staff/lib/time-tracking-ui/taskDrawerData.ts +194 -0
  742. package/src/modules/staff/lib/time-tracking-ui/timeEntryDialogState.ts +400 -0
  743. package/src/modules/staff/lib/time-tracking-ui/timeEntryListData.ts +271 -0
  744. package/src/modules/staff/lib/time-tracking-ui/timeTrackingSettingsForm.ts +168 -0
  745. package/src/modules/staff/lib/time-tracking-ui/timesheetData.ts +225 -0
  746. package/src/modules/staff/lib/time-tracking-ui/timesheetPeriod.ts +278 -0
  747. package/src/modules/staff/lib/time-tracking-ui/timesheetTargets.ts +93 -0
  748. package/src/modules/staff/lib/time-tracking-ui/useBoardFilters.ts +107 -0
  749. package/src/modules/staff/lib/time-tracking-ui/useTimesheetPreferences.ts +245 -0
  750. package/src/modules/staff/lib/timeTrackingSeeds.ts +1037 -0
  751. package/src/modules/staff/lib/timesheets/timeEntryCacheInvalidation.ts +11 -10
  752. package/src/modules/staff/lib/timesheets/timeEntryDecoration.ts +256 -0
  753. package/src/modules/staff/lib/timesheets-projects/budgetBurn.ts +60 -0
  754. package/src/modules/staff/lib/timesheets-projects/budgetThreshold.ts +135 -0
  755. package/src/modules/staff/lib/timesheets-projects/budgetThresholdState.ts +137 -0
  756. package/src/modules/staff/lib/timesheets-projects/computeProjectFinancials.ts +142 -0
  757. package/src/modules/staff/lib/timesheets-projects/reportSelection.ts +82 -0
  758. package/src/modules/staff/lib/timesheets-projects-ui/AssignMembersDialog.tsx +237 -0
  759. package/src/modules/staff/lib/timesheets-projects-ui/ProjectBudgetCell.tsx +33 -0
  760. package/src/modules/staff/lib/timesheets-projects-ui/ProjectCard.tsx +56 -1
  761. package/src/modules/staff/lib/timesheets-projects-ui/ProjectsBulkActionsBar.tsx +90 -0
  762. package/src/modules/staff/lib/timesheets-reports/buildReportSheet.ts +125 -0
  763. package/src/modules/staff/lib/timesheets-reports/loadReportData.ts +251 -0
  764. package/src/modules/staff/lib/timesheets-reports/pdf.ts +263 -0
  765. package/src/modules/staff/lib/timesheets-reports/reportApprovalPolicies.ts +137 -0
  766. package/src/modules/staff/lib/timesheets-reports/reportConfigData.ts +247 -0
  767. package/src/modules/staff/lib/timesheets-reports/reportConfigState.ts +89 -0
  768. package/src/modules/staff/lib/timesheets-reports/reportExport.ts +373 -0
  769. package/src/modules/staff/lib/timesheets-reports/reportExportFormats.ts +71 -0
  770. package/src/modules/staff/lib/timesheets-reports/reportGroupings.ts +153 -0
  771. package/src/modules/staff/lib/timesheets-reports/reportLabels.ts +23 -0
  772. package/src/modules/staff/lib/timesheets-reports/reportReference.ts +146 -0
  773. package/src/modules/staff/lib/timesheets-reports/reportRows.ts +89 -0
  774. package/src/modules/staff/lib/timesheets-reports/reportSheetData.ts +196 -0
  775. package/src/modules/staff/lib/timesheets-reports/reportTotals.ts +544 -0
  776. package/src/modules/staff/lib/timesheets-reports/xlsx.ts +207 -0
  777. package/src/modules/staff/lib/timesheets-tasks/computeTaskRollups.ts +238 -0
  778. package/src/modules/staff/lib/timesheets-tasks/defaultStatusTemplate.ts +90 -0
  779. package/src/modules/staff/lib/timesheets-tasks/seedProjectStatuses.ts +43 -0
  780. package/src/modules/staff/lib/timesheets-tasks/statusPositions.ts +132 -0
  781. package/src/modules/staff/lib/timesheets-tasks/statusSlug.ts +43 -0
  782. package/src/modules/staff/lib/timesheets-tasks/taskHierarchy.ts +99 -0
  783. package/src/modules/staff/lib/timesheets-tasks/taskHoursTotals.ts +42 -0
  784. package/src/modules/staff/lib/timesheets-tasks/taskReference.ts +108 -0
  785. package/src/modules/staff/lib/timesheets-ui/CreateProjectDialog.tsx +18 -4
  786. package/src/modules/staff/lib/timesheets-ui/ListView.tsx +504 -149
  787. package/src/modules/staff/lib/timesheets-ui/TimerBar.tsx +50 -2
  788. package/src/modules/staff/migrations/.snapshot-open-mercato.json +3244 -228
  789. package/src/modules/staff/migrations/Migration20260813073131_staff.ts +94 -0
  790. package/src/modules/staff/migrations/Migration20260824143357_staff.ts +17 -0
  791. package/src/modules/staff/notifications.client.ts +16 -0
  792. package/src/modules/staff/notifications.handlers.ts +88 -0
  793. package/src/modules/staff/notifications.ts +163 -0
  794. package/src/modules/staff/search.ts +191 -1
  795. package/src/modules/staff/setup.ts +22 -0
  796. package/src/modules/staff/subscribers/time-project-access-requested-notification.ts +95 -0
  797. package/src/modules/staff/subscribers/time-project-budget-threshold-notification.ts +226 -0
  798. package/src/modules/staff/subscribers/time-report-approved-notification.ts +116 -0
  799. package/src/modules/staff/subscribers/time-report-portal-broadcast.ts +120 -0
  800. package/src/modules/staff/translations.ts +17 -0
  801. package/src/modules/staff/widgets/components.ts +34 -0
  802. package/src/modules/staff/widgets/injection/timer-sidebar-indicator/widget.tsx +6 -5
  803. package/src/modules/staff/widgets/notifications/TimeProjectAccessRequestedRenderer.tsx +138 -0
  804. package/src/modules/staff/widgets/notifications/index.ts +1 -0
  805. package/src/modules/staff/workers/timesheets-reapply-rounding.ts +59 -0
  806. package/dist/modules/staff/backend/staff/timesheets/projects/projectFormConfig.js +0 -123
  807. package/dist/modules/staff/backend/staff/timesheets/projects/projectFormConfig.js.map +0 -7
  808. package/src/modules/staff/backend/staff/timesheets/projects/projectFormConfig.ts +0 -138
@@ -0,0 +1,228 @@
1
+ import { NextResponse } from "next/server";
2
+ import { resolveFeatureAccess } from "../../../../../lib/time-tracking/featureAccess.js";
3
+ import { z } from "zod";
4
+ import { createRequestContainer } from "@open-mercato/shared/lib/di/container";
5
+ import { getAuthFromRequest } from "@open-mercato/shared/lib/auth/server";
6
+ import { resolveOrganizationScopeForRequest } from "@open-mercato/core/modules/directory/utils/organizationScope";
7
+ import { CrudHttpError, forbidden, isCrudHttpError } from "@open-mercato/shared/lib/crud/errors";
8
+ import { runRouteMutationGuards } from "@open-mercato/shared/lib/crud/route-mutation-guard";
9
+ import { resolveTranslations } from "@open-mercato/shared/lib/i18n/server";
10
+ import { readJsonSafe } from "@open-mercato/shared/lib/http/readJsonSafe";
11
+ import { findOneWithDecryption } from "@open-mercato/shared/lib/encryption/find";
12
+ import { createLogger } from "@open-mercato/shared/lib/logger";
13
+ import { emitStaffEvent } from "../../../../../events.js";
14
+ import { getCommandInterceptorHttpRejection } from "@open-mercato/shared/lib/commands/errors";
15
+ import { StaffTimeEntry } from "../../../../../data/entities.js";
16
+ import { staffTimeEntryCommandIds } from "../../../../../commands/timesheets-entries.js";
17
+ import { resolveProjectAccess } from "../../../../../lib/time-tracking/access.js";
18
+ import { readTimeTrackingSettings } from "../../../../../lib/time-tracking/settings.js";
19
+ import { STAFF_TIME_TRACKING_RESOURCE_KINDS } from "../../../../guards.js";
20
+ import { runTimesheetInterceptors } from "../../../_shared/withTimesheetInterceptors.js";
21
+ const logger = createLogger("staff").child({ component: "api/timesheets/time-entries/duplicate" });
22
+ const MANAGE_OWN_FEATURE = "staff.timesheets.manage_own";
23
+ const RESOURCE_KIND = STAFF_TIME_TRACKING_RESOURCE_KINDS.timeEntry;
24
+ const metadata = {
25
+ POST: { requireAuth: true, requireFeatures: [MANAGE_OWN_FEATURE] }
26
+ };
27
+ const duplicateRequestSchema = z.object({
28
+ date: z.coerce.date().optional(),
29
+ durationMinutes: z.number().int().min(0).max(1440).optional(),
30
+ tagIds: z.array(z.string().uuid()).max(50).optional()
31
+ });
32
+ const duplicateResponseSchema = z.object({
33
+ id: z.string().uuid(),
34
+ sourceId: z.string().uuid()
35
+ });
36
+ const lockedConflictSchema = z.object({
37
+ code: z.literal("time_entry_locked"),
38
+ error: z.string(),
39
+ lockedReportId: z.string().uuid().nullable(),
40
+ lockedEntryIds: z.array(z.string().uuid()),
41
+ lockedReportIds: z.array(z.string().uuid())
42
+ });
43
+ function extractEntryIdFromUrl(request) {
44
+ if (!request?.url) return null;
45
+ try {
46
+ const url = new URL(request.url);
47
+ const match = url.pathname.match(/\/time-entries\/([^/]+)\/duplicate/);
48
+ return match?.[1] ?? null;
49
+ } catch {
50
+ return null;
51
+ }
52
+ }
53
+ async function resolveAssignmentGraceDays(container, tenantId) {
54
+ try {
55
+ const configService = container.resolve("moduleConfigService");
56
+ const settings = await readTimeTrackingSettings(configService, { tenantId });
57
+ return settings.access.assignmentGraceDays;
58
+ } catch {
59
+ return null;
60
+ }
61
+ }
62
+ async function loadProjectAccess(container, userId, tenantId, organizationId, grantedFeatures) {
63
+ const em = container.resolve("em");
64
+ return resolveProjectAccess({
65
+ em: em.fork(),
66
+ userId,
67
+ tenantId,
68
+ organizationId,
69
+ userFeatures: grantedFeatures,
70
+ assignmentGraceDays: await resolveAssignmentGraceDays(container, tenantId)
71
+ });
72
+ }
73
+ function isTimeEntryVisible(entry, access) {
74
+ if (access.canManageAll) return true;
75
+ const projectId = entry.timeProjectId ?? null;
76
+ if (projectId) return access.projectIds.includes(projectId);
77
+ return access.staffMemberId !== null && access.staffMemberId === entry.staffMemberId;
78
+ }
79
+ async function POST(req) {
80
+ try {
81
+ const container = await createRequestContainer();
82
+ const auth = await getAuthFromRequest(req);
83
+ const { translate } = await resolveTranslations();
84
+ if (!auth) {
85
+ throw new CrudHttpError(401, { error: translate("staff.errors.unauthorized", "Unauthorized") });
86
+ }
87
+ const scope = await resolveOrganizationScopeForRequest({ container, auth, request: req });
88
+ const tenantId = scope?.tenantId ?? auth.tenantId ?? null;
89
+ const organizationId = scope?.selectedId ?? auth.orgId ?? null;
90
+ if (!tenantId || !organizationId) {
91
+ throw new CrudHttpError(400, {
92
+ error: translate("staff.errors.missingScope", "Missing tenant or organization scope.")
93
+ });
94
+ }
95
+ const timeEntryId = extractEntryIdFromUrl(req);
96
+ if (!timeEntryId) {
97
+ throw new CrudHttpError(400, {
98
+ error: translate("staff.timesheets.errors.missingEntryId", "Missing entry ID.")
99
+ });
100
+ }
101
+ const actorId = auth.sub ?? "";
102
+ const manageOwn = await resolveFeatureAccess(container, actorId, [MANAGE_OWN_FEATURE], { tenantId, organizationId });
103
+ if (!manageOwn.allowed) {
104
+ throw forbidden(translate("staff.errors.forbidden", "Forbidden"));
105
+ }
106
+ const grantedFeatures = manageOwn.grantedFeatures;
107
+ const interceptors = await runTimesheetInterceptors({
108
+ request: req,
109
+ method: "POST",
110
+ scope: { container, userId: actorId, tenantId, organizationId, userFeatures: grantedFeatures },
111
+ body: await readJsonSafe(req, {})
112
+ });
113
+ if (!interceptors.ok) return interceptors.response;
114
+ const { session } = interceptors;
115
+ const parsed = duplicateRequestSchema.parse(session.body);
116
+ const em = container.resolve("em").fork();
117
+ const source = await findOneWithDecryption(
118
+ em,
119
+ StaffTimeEntry,
120
+ { id: timeEntryId, tenantId, organizationId, deletedAt: null },
121
+ {},
122
+ { tenantId, organizationId }
123
+ );
124
+ const access = await loadProjectAccess(container, actorId || null, tenantId, organizationId, grantedFeatures);
125
+ if (!source || !isTimeEntryVisible(source, access)) {
126
+ throw new CrudHttpError(404, {
127
+ error: translate("staff.timesheets.errors.entryNotFound", "Time entry not found, deleted, or not owned by you.")
128
+ });
129
+ }
130
+ const guardResult = await runRouteMutationGuards({
131
+ container,
132
+ req,
133
+ auth: {
134
+ userId: actorId,
135
+ tenantId,
136
+ organizationId,
137
+ userFeatures: grantedFeatures
138
+ },
139
+ input: {
140
+ resourceKind: RESOURCE_KIND,
141
+ resourceId: timeEntryId,
142
+ operation: "create",
143
+ mutationPayload: parsed
144
+ }
145
+ });
146
+ if (!guardResult.ok) return guardResult.response;
147
+ const effective = guardResult.modifiedPayload ? duplicateRequestSchema.parse({ ...parsed, ...guardResult.modifiedPayload }) : parsed;
148
+ const ctx = {
149
+ container,
150
+ auth,
151
+ organizationScope: scope,
152
+ selectedOrganizationId: scope?.selectedId ?? auth.orgId ?? null,
153
+ organizationIds: scope?.filterIds ?? (auth.orgId ? [auth.orgId] : null),
154
+ request: req
155
+ };
156
+ const commandBus = container.resolve("commandBus");
157
+ const { result } = await commandBus.execute(
158
+ staffTimeEntryCommandIds.duplicate,
159
+ { input: { ...effective, id: timeEntryId }, ctx }
160
+ );
161
+ await guardResult.runAfterSuccess();
162
+ if (result?.timeEntryId) {
163
+ void emitStaffEvent("staff.timesheets.time_entry.copied", {
164
+ id: result.timeEntryId,
165
+ sourceId: timeEntryId,
166
+ tenantId,
167
+ organizationId
168
+ }, { persistent: true }).catch((err) => {
169
+ logger.error("staff.timesheets emit time_entry.copied failed", { err });
170
+ });
171
+ }
172
+ return session.respond(201, { id: result?.timeEntryId ?? null, sourceId: timeEntryId });
173
+ } catch (err) {
174
+ if (isCrudHttpError(err)) {
175
+ return NextResponse.json(err.body, { status: err.status });
176
+ }
177
+ const interceptorRejection = getCommandInterceptorHttpRejection(err);
178
+ if (interceptorRejection) {
179
+ return NextResponse.json(interceptorRejection.body, { status: interceptorRejection.status });
180
+ }
181
+ const { translate } = await resolveTranslations();
182
+ if (err instanceof z.ZodError) {
183
+ return NextResponse.json(
184
+ { error: translate("staff.errors.invalid_request", "Invalid request"), details: err.issues },
185
+ { status: 400 }
186
+ );
187
+ }
188
+ logger.error("staff.timesheets.time-entries.duplicate failed", { err });
189
+ return NextResponse.json(
190
+ { error: translate("staff.errors.internal", "Internal server error") },
191
+ { status: 500 }
192
+ );
193
+ }
194
+ }
195
+ const openApi = {
196
+ tag: "Staff",
197
+ summary: "Duplicate a time entry",
198
+ methods: {
199
+ POST: {
200
+ summary: "Duplicate a time entry",
201
+ description: "US-D6. Copies an existing time entry, carrying its task, project, description, billable flag and rate override across. `date` and `durationMinutes` override the copy without a second request; `tagIds` sets the copy tag set (the caller passes the source tags it already holds from the list response). The copy is written through the create command, so ownership, project access, the billable and currency defaults, and the tenant rounding rule apply to it exactly as they do to a hand-made entry. A source locked into a closed report is refused with 409 `time_entry_locked`; a source the caller cannot see answers 404.",
202
+ requestBody: {
203
+ contentType: "application/json",
204
+ schema: duplicateRequestSchema
205
+ },
206
+ responses: [
207
+ { status: 201, description: "Copy created", schema: duplicateResponseSchema }
208
+ ],
209
+ errors: [
210
+ { status: 400, description: "Invalid body or missing scope" },
211
+ { status: 401, description: "Unauthorized" },
212
+ { status: 403, description: "Missing staff.timesheets.manage_own" },
213
+ { status: 404, description: "Time entry not found, deleted, or outside the caller project access" },
214
+ {
215
+ status: 409,
216
+ description: "The source entry is locked into a closed report; nothing was copied",
217
+ schema: lockedConflictSchema
218
+ }
219
+ ]
220
+ }
221
+ }
222
+ };
223
+ export {
224
+ POST,
225
+ metadata,
226
+ openApi
227
+ };
228
+ //# sourceMappingURL=route.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../../../../src/modules/staff/api/timesheets/time-entries/%5Bid%5D/duplicate/route.ts"],
4
+ "sourcesContent": ["/**\n * US-D6 \u2014 \"repeat yesterday\", for one entry.\n *\n * The row action on screen 10 and the per-row copy button on screen 1 both land\n * here. The route is deliberately thin: T4.2 already put the whole copy in the\n * `staff.timesheets.time_entries.duplicate` command, which resolves the SOURCE,\n * runs it past the lock gate and then delegates the write to the create command\n * so the copy inherits ownership enforcement, project access, the billable and\n * currency defaults, rounding (D-7), tag syncing and the audit entry unchanged.\n * Nothing here re-implements any of that.\n *\n * Two things the route owns that the command cannot:\n *\n * 1. **Existence is decided by the caller's project access, not by the row.** The\n * source is looked up through the same intersection the list route applies \u2014\n * entries on projects the caller belongs to, plus their own project-less\n * entries \u2014 and anything outside it answers `404`, never `403`. A `403` on a\n * project the caller cannot see would confirm the entry exists and name the\n * project it belongs to.\n * 2. **`date` and `durationMinutes` overrides.** US-D6 ends with \"I adjust the\n * duration and the date\", so both travel with the copy request instead of\n * costing a follow-up PUT that would briefly show the wrong duration.\n *\n * `tagIds` is passed in by the caller rather than read off the junction: the\n * command writes tags only through the tag commands and never touches\n * `staff_time_entry_tags`, and the client already holds the source's `tags[]`\n * from the list response.\n */\n\nimport { NextResponse } from 'next/server'\nimport { resolveFeatureAccess } from '../../../../../lib/time-tracking/featureAccess'\nimport { z } from 'zod'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { resolveOrganizationScopeForRequest } from '@open-mercato/core/modules/directory/utils/organizationScope'\nimport { CrudHttpError, forbidden, isCrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport { runRouteMutationGuards } from '@open-mercato/shared/lib/crud/route-mutation-guard'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport { readJsonSafe } from '@open-mercato/shared/lib/http/readJsonSafe'\nimport { findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\nimport { emitStaffEvent } from '../../../../../events'\nimport type { CommandBus, CommandRuntimeContext } from '@open-mercato/shared/lib/commands'\nimport { getCommandInterceptorHttpRejection } from '@open-mercato/shared/lib/commands/errors'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport type { ModuleConfigService } from '@open-mercato/core/modules/configs/lib/module-config-service'\nimport { StaffTimeEntry } from '../../../../../data/entities'\nimport { staffTimeEntryCommandIds } from '../../../../../commands/timesheets-entries'\nimport { resolveProjectAccess, type ProjectAccess } from '../../../../../lib/time-tracking/access'\nimport { readTimeTrackingSettings } from '../../../../../lib/time-tracking/settings'\nimport { STAFF_TIME_TRACKING_RESOURCE_KINDS } from '../../../../guards'\nimport { runTimesheetInterceptors } from '../../../_shared/withTimesheetInterceptors'\n\nconst logger = createLogger('staff').child({ component: 'api/timesheets/time-entries/duplicate' })\n\nconst MANAGE_OWN_FEATURE = 'staff.timesheets.manage_own'\nconst RESOURCE_KIND = STAFF_TIME_TRACKING_RESOURCE_KINDS.timeEntry\n\nexport const metadata = {\n POST: { requireAuth: true, requireFeatures: [MANAGE_OWN_FEATURE] },\n}\n\nconst duplicateRequestSchema = z.object({\n date: z.coerce.date().optional(),\n durationMinutes: z.number().int().min(0).max(1440).optional(),\n tagIds: z.array(z.string().uuid()).max(50).optional(),\n})\n\nconst duplicateResponseSchema = z.object({\n id: z.string().uuid(),\n sourceId: z.string().uuid(),\n})\n\nconst lockedConflictSchema = z.object({\n code: z.literal('time_entry_locked'),\n error: z.string(),\n lockedReportId: z.string().uuid().nullable(),\n lockedEntryIds: z.array(z.string().uuid()),\n lockedReportIds: z.array(z.string().uuid()),\n})\n\ntype ContainerLike = { resolve: (name: string) => unknown }\n\nfunction extractEntryIdFromUrl(request?: Request): string | null {\n if (!request?.url) return null\n try {\n const url = new URL(request.url)\n const match = url.pathname.match(/\\/time-entries\\/([^/]+)\\/duplicate/)\n return match?.[1] ?? null\n } catch {\n return null\n }\n}\n\nasync function resolveAssignmentGraceDays(container: ContainerLike, tenantId: string): Promise<number | null> {\n try {\n const configService = container.resolve('moduleConfigService') as ModuleConfigService\n const settings = await readTimeTrackingSettings(configService, { tenantId })\n return settings.access.assignmentGraceDays\n } catch {\n // Fail safe to the documented default rather than widening the window.\n return null\n }\n}\n\nasync function loadProjectAccess(\n container: ContainerLike,\n userId: string | null,\n tenantId: string,\n organizationId: string,\n grantedFeatures: readonly string[],\n): Promise<ProjectAccess> {\n const em = container.resolve('em') as EntityManager\n return resolveProjectAccess({\n em: em.fork(),\n userId,\n tenantId,\n organizationId,\n userFeatures: grantedFeatures,\n assignmentGraceDays: await resolveAssignmentGraceDays(container, tenantId),\n })\n}\n\n/**\n * The per-row form of the list route's `$or` intersection: an entry on a project\n * is visible to the members of that project, and a project-less entry is visible\n * only to the member who logged it.\n */\nfunction isTimeEntryVisible(\n entry: { staffMemberId: string; timeProjectId?: string | null },\n access: ProjectAccess,\n): boolean {\n if (access.canManageAll) return true\n const projectId = entry.timeProjectId ?? null\n if (projectId) return access.projectIds.includes(projectId)\n return access.staffMemberId !== null && access.staffMemberId === entry.staffMemberId\n}\n\nexport async function POST(req: Request) {\n try {\n const container = await createRequestContainer()\n const auth = await getAuthFromRequest(req)\n const { translate } = await resolveTranslations()\n if (!auth) {\n throw new CrudHttpError(401, { error: translate('staff.errors.unauthorized', 'Unauthorized') })\n }\n\n const scope = await resolveOrganizationScopeForRequest({ container, auth, request: req })\n const tenantId = scope?.tenantId ?? auth.tenantId ?? null\n const organizationId = scope?.selectedId ?? auth.orgId ?? null\n if (!tenantId || !organizationId) {\n throw new CrudHttpError(400, {\n error: translate('staff.errors.missingScope', 'Missing tenant or organization scope.'),\n })\n }\n\n const timeEntryId = extractEntryIdFromUrl(req)\n if (!timeEntryId) {\n throw new CrudHttpError(400, {\n error: translate('staff.timesheets.errors.missingEntryId', 'Missing entry ID.'),\n })\n }\n\n const actorId = auth.sub ?? ''\n // One lookup through the module's single RBAC authority: the decision is\n // checked unconditionally and fails closed, and the grant list the plumbing\n // takes comes from the same answer instead of a second, nullable read.\n const manageOwn = await resolveFeatureAccess(container, actorId, [MANAGE_OWN_FEATURE], { tenantId, organizationId })\n if (!manageOwn.allowed) {\n throw forbidden(translate('staff.errors.forbidden', 'Forbidden'))\n }\n const grantedFeatures = manageOwn.grantedFeatures\n\n const interceptors = await runTimesheetInterceptors({\n request: req,\n method: 'POST',\n scope: { container, userId: actorId, tenantId, organizationId, userFeatures: grantedFeatures },\n body: await readJsonSafe<Record<string, unknown>>(req, {}),\n })\n if (!interceptors.ok) return interceptors.response\n const { session } = interceptors\n\n const parsed = duplicateRequestSchema.parse(session.body)\n\n // The source is resolved here, before the command, purely so an entry the\n // caller cannot see answers 404 instead of the command's 403 \u2014 which would\n // confirm the entry exists and name the project that hides it.\n const em = (container.resolve('em') as EntityManager).fork()\n const source = await findOneWithDecryption(\n em,\n StaffTimeEntry,\n { id: timeEntryId, tenantId, organizationId, deletedAt: null },\n {},\n { tenantId, organizationId },\n )\n const access = await loadProjectAccess(container, actorId || null, tenantId, organizationId, grantedFeatures)\n if (!source || !isTimeEntryVisible(source, access)) {\n throw new CrudHttpError(404, {\n error: translate('staff.timesheets.errors.entryNotFound', 'Time entry not found, deleted, or not owned by you.'),\n })\n }\n\n const guardResult = await runRouteMutationGuards({\n container,\n req,\n auth: {\n userId: actorId,\n tenantId,\n organizationId,\n userFeatures: grantedFeatures,\n },\n input: {\n resourceKind: RESOURCE_KIND,\n resourceId: timeEntryId,\n operation: 'create',\n mutationPayload: parsed,\n },\n })\n if (!guardResult.ok) return guardResult.response\n\n const effective = guardResult.modifiedPayload\n ? duplicateRequestSchema.parse({ ...parsed, ...guardResult.modifiedPayload })\n : parsed\n\n const ctx: CommandRuntimeContext = {\n container,\n auth,\n organizationScope: scope,\n selectedOrganizationId: scope?.selectedId ?? auth.orgId ?? null,\n organizationIds: scope?.filterIds ?? (auth.orgId ? [auth.orgId] : null),\n request: req,\n }\n\n const commandBus = container.resolve('commandBus') as CommandBus\n const { result } = await commandBus.execute<unknown, { timeEntryId: string }>(\n staffTimeEntryCommandIds.duplicate,\n { input: { ...effective, id: timeEntryId }, ctx },\n )\n\n await guardResult.runAfterSuccess()\n\n if (result?.timeEntryId) {\n void emitStaffEvent('staff.timesheets.time_entry.copied', {\n id: result.timeEntryId,\n sourceId: timeEntryId,\n tenantId,\n organizationId,\n }, { persistent: true }).catch((err) => {\n logger.error('staff.timesheets emit time_entry.copied failed', { err })\n })\n }\n\n return session.respond(201, { id: result?.timeEntryId ?? null, sourceId: timeEntryId })\n } catch (err) {\n if (isCrudHttpError(err)) {\n return NextResponse.json(err.body, { status: err.status })\n }\n const interceptorRejection = getCommandInterceptorHttpRejection(err)\n if (interceptorRejection) {\n return NextResponse.json(interceptorRejection.body, { status: interceptorRejection.status })\n }\n const { translate } = await resolveTranslations()\n if (err instanceof z.ZodError) {\n return NextResponse.json(\n { error: translate('staff.errors.invalid_request', 'Invalid request'), details: err.issues },\n { status: 400 },\n )\n }\n logger.error('staff.timesheets.time-entries.duplicate failed', { err })\n return NextResponse.json(\n { error: translate('staff.errors.internal', 'Internal server error') },\n { status: 500 },\n )\n }\n}\n\nexport const openApi: OpenApiRouteDoc = {\n tag: 'Staff',\n summary: 'Duplicate a time entry',\n methods: {\n POST: {\n summary: 'Duplicate a time entry',\n description:\n 'US-D6. Copies an existing time entry, carrying its task, project, description, billable flag and rate override across. `date` and `durationMinutes` override the copy without a second request; `tagIds` sets the copy tag set (the caller passes the source tags it already holds from the list response). The copy is written through the create command, so ownership, project access, the billable and currency defaults, and the tenant rounding rule apply to it exactly as they do to a hand-made entry. A source locked into a closed report is refused with 409 `time_entry_locked`; a source the caller cannot see answers 404.',\n requestBody: {\n contentType: 'application/json',\n schema: duplicateRequestSchema,\n },\n responses: [\n { status: 201, description: 'Copy created', schema: duplicateResponseSchema },\n ],\n errors: [\n { status: 400, description: 'Invalid body or missing scope' },\n { status: 401, description: 'Unauthorized' },\n { status: 403, description: 'Missing staff.timesheets.manage_own' },\n { status: 404, description: 'Time entry not found, deleted, or outside the caller project access' },\n {\n status: 409,\n description: 'The source entry is locked into a closed report; nothing was copied',\n schema: lockedConflictSchema,\n },\n ],\n },\n },\n}\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
@@ -10,11 +10,15 @@ import { LockMode } from "@mikro-orm/core";
10
10
  import { StaffTimeEntry, StaffTimeEntrySegment } from "../../../../../../data/entities.js";
11
11
  import { staffTimeEntrySegmentUpdateSchema } from "../../../../../../data/validators.js";
12
12
  import { getStaffMemberByUserId } from "../../../../../../lib/staffMemberResolver.js";
13
+ import { emitStaffEvent } from "../../../../../../events.js";
14
+ import { runTimesheetInterceptors } from "../../../../_shared/withTimesheetInterceptors.js";
15
+ import { createLogger } from "@open-mercato/shared/lib/logger";
13
16
  import {
14
- resolveUserFeatures,
17
+ STAFF_TIME_TRACKING_RESOURCE_KINDS,
15
18
  runStaffMutationGuardAfterSuccess,
16
19
  runStaffMutationGuards
17
20
  } from "../../../../../guards.js";
21
+ const logger = createLogger("staff").child({ component: "api/timesheets/time-entries/segments" });
18
22
  const routeMetadata = {
19
23
  PATCH: { requireAuth: true, requireFeatures: ["staff.timesheets.manage_own"] }
20
24
  };
@@ -43,10 +47,6 @@ async function PATCH(req) {
43
47
  if (!rawBody) {
44
48
  return NextResponse.json({ error: "Invalid payload" }, { status: 400 });
45
49
  }
46
- const parsed = staffTimeEntrySegmentUpdateSchema.safeParse({ ...rawBody, id: ids.segmentId });
47
- if (!parsed.success) {
48
- return NextResponse.json({ error: "Invalid payload", details: parsed.error.flatten() }, { status: 400 });
49
- }
50
50
  const container = await createRequestContainer();
51
51
  const scope = await resolveOrganizationScopeForRequest({ container, auth, request: req });
52
52
  const tenantId = scope?.tenantId ?? auth.tenantId ?? null;
@@ -54,6 +54,18 @@ async function PATCH(req) {
54
54
  if (!tenantId || !organizationId) {
55
55
  return NextResponse.json({ error: "Missing tenant or organization scope." }, { status: 400 });
56
56
  }
57
+ const interceptors = await runTimesheetInterceptors({
58
+ request: req,
59
+ method: "PATCH",
60
+ scope: { container, userId: auth.sub, tenantId, organizationId },
61
+ body: rawBody
62
+ });
63
+ if (!interceptors.ok) return interceptors.response;
64
+ const { session } = interceptors;
65
+ const parsed = staffTimeEntrySegmentUpdateSchema.safeParse({ ...session.body, id: ids.segmentId });
66
+ if (!parsed.success) {
67
+ return NextResponse.json({ error: "Invalid payload", details: parsed.error.flatten() }, { status: 400 });
68
+ }
57
69
  const em = container.resolve("em").fork();
58
70
  const scopeCtx = { tenantId, organizationId };
59
71
  const entry = await findOneWithDecryption(em, StaffTimeEntry, { id: ids.entryId, tenantId, organizationId, deletedAt: null }, {}, scopeCtx);
@@ -80,14 +92,13 @@ async function PATCH(req) {
80
92
  tenantId,
81
93
  organizationId,
82
94
  userId: auth.sub ?? "",
83
- resourceKind: "staff.timesheets.time_entry_segment",
95
+ resourceKind: STAFF_TIME_TRACKING_RESOURCE_KINDS.timeEntrySegment,
84
96
  resourceId: segment.id,
85
97
  operation: "update",
86
98
  requestMethod: req.method,
87
99
  requestHeaders: req.headers,
88
100
  mutationPayload: parsed.data
89
- },
90
- resolveUserFeatures(auth)
101
+ }
91
102
  );
92
103
  if (!guardResult.ok) {
93
104
  return NextResponse.json(
@@ -141,14 +152,22 @@ async function PATCH(req) {
141
152
  tenantId,
142
153
  organizationId,
143
154
  userId: auth.sub ?? "",
144
- resourceKind: "staff.timesheets.time_entry_segment",
155
+ resourceKind: STAFF_TIME_TRACKING_RESOURCE_KINDS.timeEntrySegment,
145
156
  resourceId: updatedSegment.id,
146
157
  operation: "update",
147
158
  requestMethod: req.method,
148
159
  requestHeaders: req.headers
149
160
  });
150
161
  }
151
- return NextResponse.json({
162
+ void emitStaffEvent("staff.timesheets.time_entry_segment.updated", {
163
+ id: updatedSegment.id,
164
+ timeEntryId: updatedSegment.timeEntryId,
165
+ tenantId,
166
+ organizationId
167
+ }, { persistent: true }).catch((err) => {
168
+ logger.error("staff.timesheets emit time_entry_segment.updated failed", { err });
169
+ });
170
+ return session.respond(200, {
152
171
  ok: true,
153
172
  item: {
154
173
  id: updatedSegment.id,
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../../../../../src/modules/staff/api/timesheets/time-entries/%5Bid%5D/segments/%5BsegmentId%5D/route.ts"],
4
- "sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { resolveOrganizationScopeForRequest } from '@open-mercato/core/modules/directory/utils/organizationScope'\nimport { findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport { readJsonSafe } from '@open-mercato/shared/lib/http/readJsonSafe'\nimport { CrudHttpError, isCrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { LockMode } from '@mikro-orm/core'\nimport { StaffTimeEntry, StaffTimeEntrySegment } from '../../../../../../data/entities'\nimport { staffTimeEntrySegmentUpdateSchema } from '../../../../../../data/validators'\nimport { getStaffMemberByUserId } from '../../../../../../lib/staffMemberResolver'\nimport {\n resolveUserFeatures,\n runStaffMutationGuardAfterSuccess,\n runStaffMutationGuards,\n} from '../../../../../guards'\n\nconst routeMetadata = {\n PATCH: { requireAuth: true, requireFeatures: ['staff.timesheets.manage_own'] },\n}\n\nexport const metadata = routeMetadata\n\nfunction extractIdsFromUrl(request?: Request): { entryId: string; segmentId: string } | null {\n if (!request?.url) return null\n try {\n const url = new URL(request.url)\n const match = url.pathname.match(/\\/time-entries\\/([^/]+)\\/segments\\/([^/]+)/)\n if (!match?.[1] || !match?.[2]) return null\n return { entryId: match[1], segmentId: match[2] }\n } catch {\n return null\n }\n}\n\nexport async function PATCH(req: Request) {\n const auth = await getAuthFromRequest(req)\n if (!auth) {\n return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n }\n\n const ids = extractIdsFromUrl(req)\n if (!ids) {\n return NextResponse.json({ error: 'Segment id is required' }, { status: 400 })\n }\n\n const rawBody = await readJsonSafe<Record<string, unknown>>(req, null)\n if (!rawBody) {\n return NextResponse.json({ error: 'Invalid payload' }, { status: 400 })\n }\n\n const parsed = staffTimeEntrySegmentUpdateSchema.safeParse({ ...rawBody, id: ids.segmentId })\n if (!parsed.success) {\n return NextResponse.json({ error: 'Invalid payload', details: parsed.error.flatten() }, { status: 400 })\n }\n\n const container = await createRequestContainer()\n const scope = await resolveOrganizationScopeForRequest({ container, auth, request: req })\n const tenantId = scope?.tenantId ?? auth.tenantId ?? null\n const organizationId = scope?.selectedId ?? auth.orgId ?? null\n if (!tenantId || !organizationId) {\n return NextResponse.json({ error: 'Missing tenant or organization scope.' }, { status: 400 })\n }\n\n const em = (container.resolve('em') as EntityManager).fork()\n const scopeCtx = { tenantId, organizationId }\n\n const entry = await findOneWithDecryption(em, StaffTimeEntry, { id: ids.entryId, tenantId, organizationId, deletedAt: null }, {}, scopeCtx)\n if (!entry) {\n return NextResponse.json({ error: 'Time entry not found' }, { status: 404 })\n }\n\n const staffMember = await getStaffMemberByUserId(em, auth.sub, tenantId, organizationId)\n if (!staffMember || entry.staffMemberId !== staffMember.id) {\n return NextResponse.json({ error: 'You can only manage your own time entries.' }, { status: 403 })\n }\n\n const segment = await findOneWithDecryption(em, StaffTimeEntrySegment, {\n id: ids.segmentId,\n timeEntryId: ids.entryId,\n tenantId,\n organizationId,\n deletedAt: null,\n }, {}, scopeCtx)\n\n if (!segment) {\n return NextResponse.json({ error: 'Segment not found' }, { status: 404 })\n }\n\n const guardResult = await runStaffMutationGuards(\n container,\n {\n tenantId,\n organizationId,\n userId: auth.sub ?? '',\n resourceKind: 'staff.timesheets.time_entry_segment',\n resourceId: segment.id,\n operation: 'update',\n requestMethod: req.method,\n requestHeaders: req.headers,\n mutationPayload: parsed.data as unknown as Record<string, unknown>,\n },\n resolveUserFeatures(auth),\n )\n if (!guardResult.ok) {\n return NextResponse.json(\n guardResult.errorBody ?? { error: 'Operation blocked by guard' },\n { status: guardResult.errorStatus ?? 422 },\n )\n }\n\n // Apply the segment edit inside a single transaction with a PESSIMISTIC_WRITE\n // lock on the parent time entry row, re-loading the segment under the lock so\n // concurrent segment edits / timer-stop recomputes on the same entry serialize\n // instead of racing on a shared in-memory snapshot (issue #2416).\n let updatedSegment: StaffTimeEntrySegment\n try {\n updatedSegment = await em.transactional(async (trx) => {\n const lockedEntry = await findOneWithDecryption(\n trx,\n StaffTimeEntry,\n { id: ids.entryId, tenantId, organizationId, deletedAt: null },\n { lockMode: LockMode.PESSIMISTIC_WRITE },\n scopeCtx,\n )\n if (!lockedEntry) {\n throw new CrudHttpError(404, { error: 'Time entry not found' })\n }\n\n const lockedSegment = await findOneWithDecryption(\n trx,\n StaffTimeEntrySegment,\n { id: ids.segmentId, timeEntryId: ids.entryId, tenantId, organizationId, deletedAt: null },\n {},\n scopeCtx,\n )\n if (!lockedSegment) {\n throw new CrudHttpError(404, { error: 'Segment not found' })\n }\n\n if (parsed.data.startedAt !== undefined) {\n lockedSegment.startedAt = parsed.data.startedAt\n }\n if (parsed.data.endedAt !== undefined) {\n lockedSegment.endedAt = parsed.data.endedAt ?? null\n }\n if (parsed.data.segmentType !== undefined) {\n lockedSegment.segmentType = parsed.data.segmentType\n }\n\n await trx.flush()\n return lockedSegment\n })\n } catch (err) {\n if (isCrudHttpError(err)) {\n return NextResponse.json(err.body, { status: err.status })\n }\n throw err\n }\n\n if (guardResult.afterSuccessCallbacks.length) {\n await runStaffMutationGuardAfterSuccess(guardResult.afterSuccessCallbacks, {\n tenantId,\n organizationId,\n userId: auth.sub ?? '',\n resourceKind: 'staff.timesheets.time_entry_segment',\n resourceId: updatedSegment.id,\n operation: 'update',\n requestMethod: req.method,\n requestHeaders: req.headers,\n })\n }\n\n return NextResponse.json({\n ok: true,\n item: {\n id: updatedSegment.id,\n timeEntryId: updatedSegment.timeEntryId,\n startedAt: updatedSegment.startedAt,\n endedAt: updatedSegment.endedAt,\n segmentType: updatedSegment.segmentType,\n createdAt: updatedSegment.createdAt,\n updatedAt: updatedSegment.updatedAt,\n },\n })\n}\n\nconst errorSchema = z.object({ error: z.string() })\nconst segmentResponseSchema = z.object({\n ok: z.literal(true),\n item: z.object({\n id: z.string(),\n timeEntryId: z.string(),\n startedAt: z.string(),\n endedAt: z.string().nullable(),\n segmentType: z.enum(['work', 'break']),\n createdAt: z.string(),\n updatedAt: z.string(),\n }),\n})\n\nexport const openApi: OpenApiRouteDoc = {\n tag: 'Staff',\n summary: 'Time entry segment management',\n methods: {\n PATCH: {\n summary: 'Update a time entry segment',\n description: 'Updates fields on an existing time entry segment (startedAt, endedAt, segmentType).',\n requestBody: {\n contentType: 'application/json',\n schema: staffTimeEntrySegmentUpdateSchema.omit({ id: true }),\n },\n responses: [\n { status: 200, description: 'Segment updated successfully', schema: segmentResponseSchema },\n ],\n errors: [\n { status: 400, description: 'Invalid payload or missing segment id', schema: errorSchema },\n { status: 401, description: 'Unauthorized', schema: errorSchema },\n { status: 404, description: 'Segment not found', schema: errorSchema },\n ],\n },\n },\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { resolveOrganizationScopeForRequest } from '@open-mercato/core/modules/directory/utils/organizationScope'\nimport { findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport { readJsonSafe } from '@open-mercato/shared/lib/http/readJsonSafe'\nimport { CrudHttpError, isCrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { LockMode } from '@mikro-orm/core'\nimport { StaffTimeEntry, StaffTimeEntrySegment } from '../../../../../../data/entities'\nimport { staffTimeEntrySegmentUpdateSchema } from '../../../../../../data/validators'\nimport { getStaffMemberByUserId } from '../../../../../../lib/staffMemberResolver'\nimport { emitStaffEvent } from '../../../../../../events'\nimport { runTimesheetInterceptors } from '../../../../_shared/withTimesheetInterceptors'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\nimport {\n STAFF_TIME_TRACKING_RESOURCE_KINDS,\n runStaffMutationGuardAfterSuccess,\n runStaffMutationGuards,\n} from '../../../../../guards'\n\nconst logger = createLogger('staff').child({ component: 'api/timesheets/time-entries/segments' })\n\nconst routeMetadata = {\n PATCH: { requireAuth: true, requireFeatures: ['staff.timesheets.manage_own'] },\n}\n\nexport const metadata = routeMetadata\n\nfunction extractIdsFromUrl(request?: Request): { entryId: string; segmentId: string } | null {\n if (!request?.url) return null\n try {\n const url = new URL(request.url)\n const match = url.pathname.match(/\\/time-entries\\/([^/]+)\\/segments\\/([^/]+)/)\n if (!match?.[1] || !match?.[2]) return null\n return { entryId: match[1], segmentId: match[2] }\n } catch {\n return null\n }\n}\n\nexport async function PATCH(req: Request) {\n const auth = await getAuthFromRequest(req)\n if (!auth) {\n return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n }\n\n const ids = extractIdsFromUrl(req)\n if (!ids) {\n return NextResponse.json({ error: 'Segment id is required' }, { status: 400 })\n }\n\n const rawBody = await readJsonSafe<Record<string, unknown>>(req, null)\n if (!rawBody) {\n return NextResponse.json({ error: 'Invalid payload' }, { status: 400 })\n }\n\n const container = await createRequestContainer()\n const scope = await resolveOrganizationScopeForRequest({ container, auth, request: req })\n const tenantId = scope?.tenantId ?? auth.tenantId ?? null\n const organizationId = scope?.selectedId ?? auth.orgId ?? null\n if (!tenantId || !organizationId) {\n return NextResponse.json({ error: 'Missing tenant or organization scope.' }, { status: 400 })\n }\n\n const interceptors = await runTimesheetInterceptors({\n request: req,\n method: 'PATCH',\n scope: { container, userId: auth.sub, tenantId, organizationId },\n body: rawBody,\n })\n if (!interceptors.ok) return interceptors.response\n const { session } = interceptors\n\n const parsed = staffTimeEntrySegmentUpdateSchema.safeParse({ ...session.body, id: ids.segmentId })\n if (!parsed.success) {\n return NextResponse.json({ error: 'Invalid payload', details: parsed.error.flatten() }, { status: 400 })\n }\n\n const em = (container.resolve('em') as EntityManager).fork()\n const scopeCtx = { tenantId, organizationId }\n\n const entry = await findOneWithDecryption(em, StaffTimeEntry, { id: ids.entryId, tenantId, organizationId, deletedAt: null }, {}, scopeCtx)\n if (!entry) {\n return NextResponse.json({ error: 'Time entry not found' }, { status: 404 })\n }\n\n const staffMember = await getStaffMemberByUserId(em, auth.sub, tenantId, organizationId)\n if (!staffMember || entry.staffMemberId !== staffMember.id) {\n return NextResponse.json({ error: 'You can only manage your own time entries.' }, { status: 403 })\n }\n\n const segment = await findOneWithDecryption(em, StaffTimeEntrySegment, {\n id: ids.segmentId,\n timeEntryId: ids.entryId,\n tenantId,\n organizationId,\n deletedAt: null,\n }, {}, scopeCtx)\n\n if (!segment) {\n return NextResponse.json({ error: 'Segment not found' }, { status: 404 })\n }\n\n const guardResult = await runStaffMutationGuards(\n container,\n {\n tenantId,\n organizationId,\n userId: auth.sub ?? '',\n resourceKind: STAFF_TIME_TRACKING_RESOURCE_KINDS.timeEntrySegment,\n resourceId: segment.id,\n operation: 'update',\n requestMethod: req.method,\n requestHeaders: req.headers,\n mutationPayload: parsed.data as unknown as Record<string, unknown>,\n },\n )\n if (!guardResult.ok) {\n return NextResponse.json(\n guardResult.errorBody ?? { error: 'Operation blocked by guard' },\n { status: guardResult.errorStatus ?? 422 },\n )\n }\n\n // Apply the segment edit inside a single transaction with a PESSIMISTIC_WRITE\n // lock on the parent time entry row, re-loading the segment under the lock so\n // concurrent segment edits / timer-stop recomputes on the same entry serialize\n // instead of racing on a shared in-memory snapshot (issue #2416).\n let updatedSegment: StaffTimeEntrySegment\n try {\n updatedSegment = await em.transactional(async (trx) => {\n const lockedEntry = await findOneWithDecryption(\n trx,\n StaffTimeEntry,\n { id: ids.entryId, tenantId, organizationId, deletedAt: null },\n { lockMode: LockMode.PESSIMISTIC_WRITE },\n scopeCtx,\n )\n if (!lockedEntry) {\n throw new CrudHttpError(404, { error: 'Time entry not found' })\n }\n\n const lockedSegment = await findOneWithDecryption(\n trx,\n StaffTimeEntrySegment,\n { id: ids.segmentId, timeEntryId: ids.entryId, tenantId, organizationId, deletedAt: null },\n {},\n scopeCtx,\n )\n if (!lockedSegment) {\n throw new CrudHttpError(404, { error: 'Segment not found' })\n }\n\n if (parsed.data.startedAt !== undefined) {\n lockedSegment.startedAt = parsed.data.startedAt\n }\n if (parsed.data.endedAt !== undefined) {\n lockedSegment.endedAt = parsed.data.endedAt ?? null\n }\n if (parsed.data.segmentType !== undefined) {\n lockedSegment.segmentType = parsed.data.segmentType\n }\n\n await trx.flush()\n return lockedSegment\n })\n } catch (err) {\n if (isCrudHttpError(err)) {\n return NextResponse.json(err.body, { status: err.status })\n }\n throw err\n }\n\n if (guardResult.afterSuccessCallbacks.length) {\n await runStaffMutationGuardAfterSuccess(guardResult.afterSuccessCallbacks, {\n tenantId,\n organizationId,\n userId: auth.sub ?? '',\n resourceKind: STAFF_TIME_TRACKING_RESOURCE_KINDS.timeEntrySegment,\n resourceId: updatedSegment.id,\n operation: 'update',\n requestMethod: req.method,\n requestHeaders: req.headers,\n })\n }\n\n void emitStaffEvent('staff.timesheets.time_entry_segment.updated', {\n id: updatedSegment.id,\n timeEntryId: updatedSegment.timeEntryId,\n tenantId,\n organizationId,\n }, { persistent: true }).catch((err) => {\n logger.error('staff.timesheets emit time_entry_segment.updated failed', { err })\n })\n\n return session.respond(200, {\n ok: true,\n item: {\n id: updatedSegment.id,\n timeEntryId: updatedSegment.timeEntryId,\n startedAt: updatedSegment.startedAt,\n endedAt: updatedSegment.endedAt,\n segmentType: updatedSegment.segmentType,\n createdAt: updatedSegment.createdAt,\n updatedAt: updatedSegment.updatedAt,\n },\n })\n}\n\nconst errorSchema = z.object({ error: z.string() })\nconst segmentResponseSchema = z.object({\n ok: z.literal(true),\n item: z.object({\n id: z.string(),\n timeEntryId: z.string(),\n startedAt: z.string(),\n endedAt: z.string().nullable(),\n segmentType: z.enum(['work', 'break']),\n createdAt: z.string(),\n updatedAt: z.string(),\n }),\n})\n\nexport const openApi: OpenApiRouteDoc = {\n tag: 'Staff',\n summary: 'Time entry segment management',\n methods: {\n PATCH: {\n summary: 'Update a time entry segment',\n description: 'Updates fields on an existing time entry segment (startedAt, endedAt, segmentType).',\n requestBody: {\n contentType: 'application/json',\n schema: staffTimeEntrySegmentUpdateSchema.omit({ id: true }),\n },\n responses: [\n { status: 200, description: 'Segment updated successfully', schema: segmentResponseSchema },\n ],\n errors: [\n { status: 400, description: 'Invalid payload or missing segment id', schema: errorSchema },\n { status: 401, description: 'Unauthorized', schema: errorSchema },\n { status: 404, description: 'Segment not found', schema: errorSchema },\n ],\n },\n },\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -13,11 +13,13 @@ import { StaffTimeEntry, StaffTimeEntrySegment } from "../../../../../data/entit
13
13
  import { staffTimeEntrySegmentCreateSchema } from "../../../../../data/validators.js";
14
14
  import { getStaffMemberByUserId } from "../../../../../lib/staffMemberResolver.js";
15
15
  import {
16
- resolveUserFeatures,
16
+ STAFF_TIME_TRACKING_RESOURCE_KINDS,
17
17
  runStaffMutationGuardAfterSuccess,
18
18
  runStaffMutationGuards
19
19
  } from "../../../../guards.js";
20
20
  import { createLogger } from "@open-mercato/shared/lib/logger";
21
+ import { emitStaffEvent } from "../../../../../events.js";
22
+ import { runTimesheetInterceptors } from "../../../_shared/withTimesheetInterceptors.js";
21
23
  const logger = createLogger("staff");
22
24
  function extractEntryIdFromUrl(request) {
23
25
  if (!request?.url) return null;
@@ -64,10 +66,17 @@ async function POST(req) {
64
66
  if (!staffMember || entry.staffMemberId !== staffMember.id) {
65
67
  throw new CrudHttpError(403, { error: translate("staff.timesheets.errors.notOwner", "You can only manage your own time entries.") });
66
68
  }
67
- const body = await readJsonSafe(req, {});
69
+ const interceptors = await runTimesheetInterceptors({
70
+ request: req,
71
+ method: "POST",
72
+ scope: { container, userId: auth.sub, tenantId, organizationId },
73
+ body: await readJsonSafe(req, {})
74
+ });
75
+ if (!interceptors.ok) return interceptors.response;
76
+ const { session } = interceptors;
68
77
  const input = parseScopedCommandInput(
69
78
  staffTimeEntrySegmentCreateSchema,
70
- { ...body, timeEntryId: entryId },
79
+ { ...session.body, timeEntryId: entryId },
71
80
  {
72
81
  container,
73
82
  auth,
@@ -84,14 +93,13 @@ async function POST(req) {
84
93
  tenantId,
85
94
  organizationId,
86
95
  userId: auth.sub ?? "",
87
- resourceKind: "staff.timesheets.time_entry_segment",
96
+ resourceKind: STAFF_TIME_TRACKING_RESOURCE_KINDS.timeEntrySegment,
88
97
  resourceId: entry.id,
89
98
  operation: "create",
90
99
  requestMethod: req.method,
91
100
  requestHeaders: req.headers,
92
101
  mutationPayload: input
93
- },
94
- resolveUserFeatures(auth)
102
+ }
95
103
  );
96
104
  if (!guardResult.ok) {
97
105
  return NextResponse.json(
@@ -127,24 +135,29 @@ async function POST(req) {
127
135
  tenantId,
128
136
  organizationId,
129
137
  userId: auth.sub ?? "",
130
- resourceKind: "staff.timesheets.time_entry_segment",
138
+ resourceKind: STAFF_TIME_TRACKING_RESOURCE_KINDS.timeEntrySegment,
131
139
  resourceId: segment.id,
132
140
  operation: "create",
133
141
  requestMethod: req.method,
134
142
  requestHeaders: req.headers
135
143
  });
136
144
  }
137
- return NextResponse.json(
138
- {
139
- id: segment.id,
140
- timeEntryId: segment.timeEntryId,
141
- startedAt: segment.startedAt,
142
- endedAt: segment.endedAt ?? null,
143
- segmentType: segment.segmentType,
144
- createdAt: segment.createdAt
145
- },
146
- { status: 201 }
147
- );
145
+ void emitStaffEvent("staff.timesheets.time_entry_segment.created", {
146
+ id: segment.id,
147
+ timeEntryId: segment.timeEntryId,
148
+ tenantId,
149
+ organizationId
150
+ }, { persistent: true }).catch((err) => {
151
+ logger.error("staff.timesheets emit time_entry_segment.created failed", { err });
152
+ });
153
+ return session.respond(201, {
154
+ id: segment.id,
155
+ timeEntryId: segment.timeEntryId,
156
+ startedAt: segment.startedAt,
157
+ endedAt: segment.endedAt ?? null,
158
+ segmentType: segment.segmentType,
159
+ createdAt: segment.createdAt
160
+ });
148
161
  } catch (err) {
149
162
  if (err instanceof CrudHttpError) {
150
163
  return NextResponse.json(err.body, { status: err.status });
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../../../../src/modules/staff/api/timesheets/time-entries/%5Bid%5D/segments/route.ts"],
4
- "sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { resolveOrganizationScopeForRequest } from '@open-mercato/core/modules/directory/utils/organizationScope'\nimport { CrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport { parseScopedCommandInput } from '@open-mercato/shared/lib/api/scoped'\nimport { findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport { readJsonSafe } from '@open-mercato/shared/lib/http/readJsonSafe'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { LockMode } from '@mikro-orm/core'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport { StaffTimeEntry, StaffTimeEntrySegment } from '../../../../../data/entities'\nimport { staffTimeEntrySegmentCreateSchema } from '../../../../../data/validators'\nimport { getStaffMemberByUserId } from '../../../../../lib/staffMemberResolver'\nimport {\n resolveUserFeatures,\n runStaffMutationGuardAfterSuccess,\n runStaffMutationGuards,\n} from '../../../../guards'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\n\nconst logger = createLogger('staff')\n\nfunction extractEntryIdFromUrl(request?: Request): string | null {\n if (!request?.url) return null\n try {\n const url = new URL(request.url)\n const match = url.pathname.match(/\\/time-entries\\/([^/]+)\\/segments/)\n return match?.[1] ?? null\n } catch {\n return null\n }\n}\n\nexport const metadata = {\n POST: { requireAuth: true, requireFeatures: ['staff.timesheets.manage_own'] },\n}\n\nexport async function POST(req: Request) {\n try {\n const container = await createRequestContainer()\n const auth = await getAuthFromRequest(req)\n const { translate } = await resolveTranslations()\n if (!auth) throw new CrudHttpError(401, { error: translate('staff.errors.unauthorized', 'Unauthorized') })\n\n const scope = await resolveOrganizationScopeForRequest({ container, auth, request: req })\n const tenantId = scope?.tenantId ?? auth.tenantId ?? null\n const organizationId = scope?.selectedId ?? auth.orgId ?? null\n if (!tenantId || !organizationId) {\n throw new CrudHttpError(400, { error: translate('staff.errors.missingScope', 'Missing tenant or organization scope.') })\n }\n\n const em = (container.resolve('em') as EntityManager).fork()\n const scopeCtx = { tenantId, organizationId }\n\n const entryId = extractEntryIdFromUrl(req)\n if (!entryId) {\n throw new CrudHttpError(400, { error: translate('staff.timesheets.errors.missingEntryId', 'Missing entry ID.') })\n }\n\n const entry = await findOneWithDecryption(\n em,\n StaffTimeEntry,\n { id: entryId, tenantId, organizationId, deletedAt: null },\n {},\n scopeCtx,\n )\n if (!entry) {\n throw new CrudHttpError(404, { error: translate('staff.timesheets.errors.entryNotFound', 'Time entry not found.') })\n }\n\n const staffMember = await getStaffMemberByUserId(em, auth.sub, tenantId, organizationId)\n if (!staffMember || entry.staffMemberId !== staffMember.id) {\n throw new CrudHttpError(403, { error: translate('staff.timesheets.errors.notOwner', 'You can only manage your own time entries.') })\n }\n\n const body = await readJsonSafe(req, {})\n const input = parseScopedCommandInput(\n staffTimeEntrySegmentCreateSchema,\n { ...body, timeEntryId: entryId },\n {\n container,\n auth,\n organizationScope: scope,\n selectedOrganizationId: organizationId,\n organizationIds: scope?.filterIds ?? (auth.orgId ? [auth.orgId] : null),\n request: req,\n },\n translate,\n )\n\n const guardResult = await runStaffMutationGuards(\n container,\n {\n tenantId,\n organizationId,\n userId: auth.sub ?? '',\n resourceKind: 'staff.timesheets.time_entry_segment',\n resourceId: entry.id,\n operation: 'create',\n requestMethod: req.method,\n requestHeaders: req.headers,\n mutationPayload: input as unknown as Record<string, unknown>,\n },\n resolveUserFeatures(auth),\n )\n if (!guardResult.ok) {\n return NextResponse.json(\n guardResult.errorBody ?? { error: 'Operation blocked by guard' },\n { status: guardResult.errorStatus ?? 422 },\n )\n }\n\n // Create the segment inside a single transaction with a PESSIMISTIC_WRITE\n // lock on the parent time entry row, so segment writes serialize against\n // concurrent timer-stop / segment mutations that recompute the entry from a\n // shared snapshot (issue #2416).\n const segment = await em.transactional(async (trx) => {\n const lockedEntry = await findOneWithDecryption(\n trx,\n StaffTimeEntry,\n { id: entryId, tenantId, organizationId, deletedAt: null },\n { lockMode: LockMode.PESSIMISTIC_WRITE },\n scopeCtx,\n )\n if (!lockedEntry) {\n throw new CrudHttpError(404, { error: translate('staff.timesheets.errors.entryNotFound', 'Time entry not found.') })\n }\n\n const segmentData = {\n tenantId: input.tenantId,\n organizationId: input.organizationId,\n timeEntryId: input.timeEntryId,\n startedAt: input.startedAt,\n endedAt: input.endedAt ?? null,\n segmentType: input.segmentType,\n }\n const created = trx.create(StaffTimeEntrySegment, segmentData as never)\n\n await trx.flush()\n return created\n })\n\n if (guardResult.afterSuccessCallbacks.length) {\n await runStaffMutationGuardAfterSuccess(guardResult.afterSuccessCallbacks, {\n tenantId,\n organizationId,\n userId: auth.sub ?? '',\n resourceKind: 'staff.timesheets.time_entry_segment',\n resourceId: segment.id,\n operation: 'create',\n requestMethod: req.method,\n requestHeaders: req.headers,\n })\n }\n\n return NextResponse.json(\n {\n id: segment.id,\n timeEntryId: segment.timeEntryId,\n startedAt: segment.startedAt,\n endedAt: segment.endedAt ?? null,\n segmentType: segment.segmentType,\n createdAt: segment.createdAt,\n },\n { status: 201 },\n )\n } catch (err) {\n if (err instanceof CrudHttpError) {\n return NextResponse.json(err.body, { status: err.status })\n }\n const { translate } = await resolveTranslations()\n logger.error('staff.timesheets.time-entries.segments.create failed', { err })\n return NextResponse.json(\n { error: translate('staff.timesheets.errors.segmentCreate', 'Failed to create time entry segment.') },\n { status: 400 },\n )\n }\n}\n\nexport const openApi: OpenApiRouteDoc = {\n tag: 'Staff',\n summary: 'Add a segment to a time entry',\n methods: {\n POST: {\n summary: 'Add a segment to a time entry',\n description: 'Creates a new work or break segment for the specified time entry.',\n requestBody: {\n contentType: 'application/json',\n schema: staffTimeEntrySegmentCreateSchema,\n },\n responses: [\n {\n status: 201,\n description: 'Segment created',\n schema: z.object({\n id: z.string().uuid(),\n timeEntryId: z.string().uuid(),\n startedAt: z.string(),\n endedAt: z.string().nullable(),\n segmentType: z.enum(['work', 'break']),\n createdAt: z.string(),\n }),\n },\n { status: 404, description: 'Time entry not found', schema: z.object({ error: z.string() }) },\n { status: 400, description: 'Invalid payload', schema: z.object({ error: z.string() }) },\n { status: 401, description: 'Unauthorized', schema: z.object({ error: z.string() }) },\n ],\n },\n },\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { resolveOrganizationScopeForRequest } from '@open-mercato/core/modules/directory/utils/organizationScope'\nimport { CrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport { parseScopedCommandInput } from '@open-mercato/shared/lib/api/scoped'\nimport { findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport { readJsonSafe } from '@open-mercato/shared/lib/http/readJsonSafe'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { LockMode } from '@mikro-orm/core'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport { StaffTimeEntry, StaffTimeEntrySegment } from '../../../../../data/entities'\nimport { staffTimeEntrySegmentCreateSchema } from '../../../../../data/validators'\nimport { getStaffMemberByUserId } from '../../../../../lib/staffMemberResolver'\nimport {\n STAFF_TIME_TRACKING_RESOURCE_KINDS,\n runStaffMutationGuardAfterSuccess,\n runStaffMutationGuards,\n} from '../../../../guards'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\nimport { emitStaffEvent } from '../../../../../events'\nimport { runTimesheetInterceptors } from '../../../_shared/withTimesheetInterceptors'\n\nconst logger = createLogger('staff')\n\nfunction extractEntryIdFromUrl(request?: Request): string | null {\n if (!request?.url) return null\n try {\n const url = new URL(request.url)\n const match = url.pathname.match(/\\/time-entries\\/([^/]+)\\/segments/)\n return match?.[1] ?? null\n } catch {\n return null\n }\n}\n\nexport const metadata = {\n POST: { requireAuth: true, requireFeatures: ['staff.timesheets.manage_own'] },\n}\n\nexport async function POST(req: Request) {\n try {\n const container = await createRequestContainer()\n const auth = await getAuthFromRequest(req)\n const { translate } = await resolveTranslations()\n if (!auth) throw new CrudHttpError(401, { error: translate('staff.errors.unauthorized', 'Unauthorized') })\n\n const scope = await resolveOrganizationScopeForRequest({ container, auth, request: req })\n const tenantId = scope?.tenantId ?? auth.tenantId ?? null\n const organizationId = scope?.selectedId ?? auth.orgId ?? null\n if (!tenantId || !organizationId) {\n throw new CrudHttpError(400, { error: translate('staff.errors.missingScope', 'Missing tenant or organization scope.') })\n }\n\n const em = (container.resolve('em') as EntityManager).fork()\n const scopeCtx = { tenantId, organizationId }\n\n const entryId = extractEntryIdFromUrl(req)\n if (!entryId) {\n throw new CrudHttpError(400, { error: translate('staff.timesheets.errors.missingEntryId', 'Missing entry ID.') })\n }\n\n const entry = await findOneWithDecryption(\n em,\n StaffTimeEntry,\n { id: entryId, tenantId, organizationId, deletedAt: null },\n {},\n scopeCtx,\n )\n if (!entry) {\n throw new CrudHttpError(404, { error: translate('staff.timesheets.errors.entryNotFound', 'Time entry not found.') })\n }\n\n const staffMember = await getStaffMemberByUserId(em, auth.sub, tenantId, organizationId)\n if (!staffMember || entry.staffMemberId !== staffMember.id) {\n throw new CrudHttpError(403, { error: translate('staff.timesheets.errors.notOwner', 'You can only manage your own time entries.') })\n }\n\n const interceptors = await runTimesheetInterceptors({\n request: req,\n method: 'POST',\n scope: { container, userId: auth.sub, tenantId, organizationId },\n body: await readJsonSafe<Record<string, unknown>>(req, {}),\n })\n if (!interceptors.ok) return interceptors.response\n const { session } = interceptors\n\n const input = parseScopedCommandInput(\n staffTimeEntrySegmentCreateSchema,\n { ...session.body, timeEntryId: entryId },\n {\n container,\n auth,\n organizationScope: scope,\n selectedOrganizationId: organizationId,\n organizationIds: scope?.filterIds ?? (auth.orgId ? [auth.orgId] : null),\n request: req,\n },\n translate,\n )\n\n const guardResult = await runStaffMutationGuards(\n container,\n {\n tenantId,\n organizationId,\n userId: auth.sub ?? '',\n resourceKind: STAFF_TIME_TRACKING_RESOURCE_KINDS.timeEntrySegment,\n resourceId: entry.id,\n operation: 'create',\n requestMethod: req.method,\n requestHeaders: req.headers,\n mutationPayload: input as unknown as Record<string, unknown>,\n },\n )\n if (!guardResult.ok) {\n return NextResponse.json(\n guardResult.errorBody ?? { error: 'Operation blocked by guard' },\n { status: guardResult.errorStatus ?? 422 },\n )\n }\n\n // Create the segment inside a single transaction with a PESSIMISTIC_WRITE\n // lock on the parent time entry row, so segment writes serialize against\n // concurrent timer-stop / segment mutations that recompute the entry from a\n // shared snapshot (issue #2416).\n const segment = await em.transactional(async (trx) => {\n const lockedEntry = await findOneWithDecryption(\n trx,\n StaffTimeEntry,\n { id: entryId, tenantId, organizationId, deletedAt: null },\n { lockMode: LockMode.PESSIMISTIC_WRITE },\n scopeCtx,\n )\n if (!lockedEntry) {\n throw new CrudHttpError(404, { error: translate('staff.timesheets.errors.entryNotFound', 'Time entry not found.') })\n }\n\n const segmentData = {\n tenantId: input.tenantId,\n organizationId: input.organizationId,\n timeEntryId: input.timeEntryId,\n startedAt: input.startedAt,\n endedAt: input.endedAt ?? null,\n segmentType: input.segmentType,\n }\n const created = trx.create(StaffTimeEntrySegment, segmentData as never)\n\n await trx.flush()\n return created\n })\n\n if (guardResult.afterSuccessCallbacks.length) {\n await runStaffMutationGuardAfterSuccess(guardResult.afterSuccessCallbacks, {\n tenantId,\n organizationId,\n userId: auth.sub ?? '',\n resourceKind: STAFF_TIME_TRACKING_RESOURCE_KINDS.timeEntrySegment,\n resourceId: segment.id,\n operation: 'create',\n requestMethod: req.method,\n requestHeaders: req.headers,\n })\n }\n\n void emitStaffEvent('staff.timesheets.time_entry_segment.created', {\n id: segment.id,\n timeEntryId: segment.timeEntryId,\n tenantId,\n organizationId,\n }, { persistent: true }).catch((err) => {\n logger.error('staff.timesheets emit time_entry_segment.created failed', { err })\n })\n\n return session.respond(201, {\n id: segment.id,\n timeEntryId: segment.timeEntryId,\n startedAt: segment.startedAt,\n endedAt: segment.endedAt ?? null,\n segmentType: segment.segmentType,\n createdAt: segment.createdAt,\n })\n } catch (err) {\n if (err instanceof CrudHttpError) {\n return NextResponse.json(err.body, { status: err.status })\n }\n const { translate } = await resolveTranslations()\n logger.error('staff.timesheets.time-entries.segments.create failed', { err })\n return NextResponse.json(\n { error: translate('staff.timesheets.errors.segmentCreate', 'Failed to create time entry segment.') },\n { status: 400 },\n )\n }\n}\n\nexport const openApi: OpenApiRouteDoc = {\n tag: 'Staff',\n summary: 'Add a segment to a time entry',\n methods: {\n POST: {\n summary: 'Add a segment to a time entry',\n description: 'Creates a new work or break segment for the specified time entry.',\n requestBody: {\n contentType: 'application/json',\n schema: staffTimeEntrySegmentCreateSchema,\n },\n responses: [\n {\n status: 201,\n description: 'Segment created',\n schema: z.object({\n id: z.string().uuid(),\n timeEntryId: z.string().uuid(),\n startedAt: z.string(),\n endedAt: z.string().nullable(),\n segmentType: z.enum(['work', 'break']),\n createdAt: z.string(),\n }),\n },\n { status: 404, description: 'Time entry not found', schema: z.object({ error: z.string() }) },\n { status: 400, description: 'Invalid payload', schema: z.object({ error: z.string() }) },\n { status: 401, description: 'Unauthorized', schema: z.object({ error: z.string() }) },\n ],\n },\n },\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }