@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,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../src/modules/staff/lib/time-tracking-ui/kanbanBoardData.ts"],
4
+ "sourcesContent": ["/**\n * Row parsing and board arithmetic for the Kanban board of screen 6 (T3.6).\n *\n * Kept apart from `KanbanBoard.tsx` so the parts that decide what a card says \u2014\n * which rows become cards, what a column's hours are, where a dropped card lands \u2014\n * are unit-testable without a DOM, and so the board component stays about drag,\n * optimistic writes and rollback.\n *\n * The column total deliberately delegates to `sumTaskLoggedMinutes` rather than\n * folding `loggedMinutes` inline: that helper drops any row whose parent is present\n * in the same set, which is the only guard against risk R10 (a parent's rollup\n * already contains its children's minutes, so adding both double-counts).\n */\n\nimport { formatDuration } from '../time-tracking/duration'\nimport { sumTaskLoggedMinutes } from '../timesheets-tasks/taskHoursTotals'\nimport { STATUS_POSITION_GAP } from '../timesheets-tasks/statusPositions'\nimport { resolveProjectColorHex } from '../timesheets-ui/colors'\n\nexport type BoardApiRow = Record<string, unknown>\n\nexport type BoardStatus = {\n id: string\n name: string\n slug: string | null\n color: string | null\n position: number\n isDefault: boolean\n isDone: boolean\n}\n\nexport type BoardTask = {\n id: string\n title: string\n reference: string | null\n timeProjectId: string | null\n parentTaskId: string | null\n taskStatusId: string | null\n assigneeStaffMemberId: string | null\n position: number\n ownMinutes: number\n loggedMinutes: number\n childCount: number\n closedAt: string | null\n updatedAt: string | null\n tagIds: string[]\n}\n\nexport type SubtaskProgress = {\n total: number\n done: number\n}\n\nexport function readString(row: BoardApiRow, ...keys: string[]): string | null {\n for (const key of keys) {\n const value = row[key]\n if (typeof value === 'string' && value.length > 0) return value\n }\n return null\n}\n\nexport function readNumber(row: BoardApiRow, ...keys: string[]): number | null {\n for (const key of keys) {\n const value = row[key]\n if (typeof value === 'number' && Number.isFinite(value)) return value\n if (typeof value === 'string' && value.trim().length > 0) {\n const parsed = Number(value)\n if (Number.isFinite(parsed)) return parsed\n }\n }\n return null\n}\n\nexport function readBoolean(row: BoardApiRow, ...keys: string[]): boolean {\n for (const key of keys) {\n const value = row[key]\n if (typeof value === 'boolean') return value\n if (value === 'true') return true\n if (value === 'false') return false\n }\n return false\n}\n\nfunction readStringArray(row: BoardApiRow, ...keys: string[]): string[] {\n for (const key of keys) {\n const value = row[key]\n if (!Array.isArray(value)) continue\n const ids = value.filter((entry): entry is string => typeof entry === 'string' && entry.length > 0)\n if (ids.length > 0) return ids\n }\n return []\n}\n\nexport function toBoardStatus(row: BoardApiRow): BoardStatus | null {\n const id = readString(row, 'id')\n if (!id) return null\n return {\n id,\n name: readString(row, 'name') ?? id,\n slug: readString(row, 'slug'),\n color: readString(row, 'color'),\n position: readNumber(row, 'position') ?? 0,\n isDefault: readBoolean(row, 'is_default', 'isDefault'),\n isDone: readBoolean(row, 'is_done', 'isDone'),\n }\n}\n\nexport function toBoardTask(row: BoardApiRow): BoardTask | null {\n const id = readString(row, 'id')\n if (!id) return null\n return {\n id,\n title: readString(row, 'title') ?? id,\n reference: readString(row, 'reference'),\n timeProjectId: readString(row, 'time_project_id', 'timeProjectId'),\n parentTaskId: readString(row, 'parent_task_id', 'parentTaskId'),\n taskStatusId: readString(row, 'task_status_id', 'taskStatusId'),\n assigneeStaffMemberId: readString(row, 'assignee_staff_member_id', 'assigneeStaffMemberId'),\n position: readNumber(row, 'position') ?? 0,\n ownMinutes: readNumber(row, 'ownMinutes', 'own_minutes') ?? 0,\n loggedMinutes: readNumber(row, 'loggedMinutes', 'logged_minutes') ?? 0,\n childCount: readNumber(row, 'childCount', 'child_count') ?? 0,\n closedAt: readString(row, 'closed_at', 'closedAt'),\n updatedAt: readString(row, 'updated_at', 'updatedAt'),\n tagIds: readStringArray(row, 'tagIds', 'tag_ids'),\n }\n}\n\nexport function readItems(payload: unknown): BoardApiRow[] {\n if (!payload || typeof payload !== 'object') return []\n const items = (payload as { items?: unknown }).items\n if (!Array.isArray(items)) return []\n return items.filter((item): item is BoardApiRow => !!item && typeof item === 'object')\n}\n\nexport function readTotal(payload: unknown, fallback: number): number {\n if (!payload || typeof payload !== 'object') return fallback\n const total = (payload as { total?: unknown }).total\n return typeof total === 'number' && Number.isFinite(total) ? total : fallback\n}\n\n/** `22:45` \u2014 the card and column-header hours format of the mockup. */\nexport function formatBoardMinutes(minutes: number): string {\n return formatDuration(Number.isFinite(minutes) ? minutes : 0, 'clock')\n}\n\n/**\n * A column's summed hours (mockup note 4). Never fold `loggedMinutes` by hand \u2014\n * see the module docblock and `taskHoursTotals.ts` on risk R10.\n */\nexport function columnLoggedMinutes(tasks: readonly BoardTask[]): number {\n return sumTaskLoggedMinutes(\n tasks.map((task) => ({\n id: task.id,\n parentTaskId: task.parentTaskId,\n loggedMinutes: task.loggedMinutes,\n })),\n )\n}\n\n/**\n * Subtask progress per parent (`3/5 podzada\u0144`).\n *\n * The board only ever renders top-level cards, so children arrive from a separate\n * sweep. A child counts as done when it sits in a column flagged `is_done`, with\n * `closed_at` as the fallback for a child whose status is no longer on the board.\n */\nexport function computeSubtaskProgress(\n children: readonly BoardTask[],\n doneStatusIds: ReadonlySet<string>,\n): Map<string, SubtaskProgress> {\n const progress = new Map<string, SubtaskProgress>()\n for (const child of children) {\n if (!child.parentTaskId) continue\n const entry = progress.get(child.parentTaskId) ?? { total: 0, done: 0 }\n entry.total += 1\n const isDone = (child.taskStatusId && doneStatusIds.has(child.taskStatusId)) || !!child.closedAt\n if (isDone) entry.done += 1\n progress.set(child.parentTaskId, entry)\n }\n return progress\n}\n\n/**\n * The slot a card dropped on a column takes.\n *\n * Cards land at the top of the target column, matching where the optimistic update\n * puts them, so the server order and the rendered order agree without a refetch.\n * Positions stay on the gap grid the server seeds, so a drop writes one row.\n */\nexport function nextPositionAtColumnTop(tasks: readonly BoardTask[]): number {\n let lowest: number | null = null\n for (const task of tasks) {\n if (!Number.isFinite(task.position)) continue\n if (lowest === null || task.position < lowest) lowest = task.position\n }\n if (lowest === null) return STATUS_POSITION_GAP\n const candidate = Math.floor(lowest / 2)\n return candidate > 0 ? candidate : 1\n}\n\n/** Two-letter avatar initials, matching the mockup's `AN` / `PZ` chips. */\nexport function initialsFromName(name: string | null | undefined): string {\n const safe = (name ?? '').trim()\n if (!safe) return '?'\n const parts = safe.split(/\\s+/).filter((part) => part.length > 0)\n if (parts.length === 0) return '?'\n if (parts.length === 1) return parts[0].slice(0, 2).toUpperCase()\n return `${parts[0][0]}${parts[parts.length - 1][0]}`.toUpperCase()\n}\n\n/**\n * The column accent bar colour.\n *\n * Statuses store a `PROJECT_COLOR_KEYS` token, never raw CSS, so the board resolves\n * the key through the shared palette and paints the 6px bar with an inline\n * background. No status-colour Tailwind class is involved.\n */\nexport function statusAccentColor(status: BoardStatus): string {\n return resolveProjectColorHex(status.color, status.name)\n}\n\nexport function sortByPosition<T extends { position: number; id: string }>(rows: readonly T[]): T[] {\n return [...rows].sort((left, right) => {\n if (left.position !== right.position) return left.position - right.position\n return left.id < right.id ? -1 : left.id > right.id ? 1 : 0\n })\n}\n"],
5
+ "mappings": "AAcA,SAAS,sBAAsB;AAC/B,SAAS,4BAA4B;AACrC,SAAS,2BAA2B;AACpC,SAAS,8BAA8B;AAoChC,SAAS,WAAW,QAAqB,MAA+B;AAC7E,aAAW,OAAO,MAAM;AACtB,UAAM,QAAQ,IAAI,GAAG;AACrB,QAAI,OAAO,UAAU,YAAY,MAAM,SAAS,EAAG,QAAO;AAAA,EAC5D;AACA,SAAO;AACT;AAEO,SAAS,WAAW,QAAqB,MAA+B;AAC7E,aAAW,OAAO,MAAM;AACtB,UAAM,QAAQ,IAAI,GAAG;AACrB,QAAI,OAAO,UAAU,YAAY,OAAO,SAAS,KAAK,EAAG,QAAO;AAChE,QAAI,OAAO,UAAU,YAAY,MAAM,KAAK,EAAE,SAAS,GAAG;AACxD,YAAM,SAAS,OAAO,KAAK;AAC3B,UAAI,OAAO,SAAS,MAAM,EAAG,QAAO;AAAA,IACtC;AAAA,EACF;AACA,SAAO;AACT;AAEO,SAAS,YAAY,QAAqB,MAAyB;AACxE,aAAW,OAAO,MAAM;AACtB,UAAM,QAAQ,IAAI,GAAG;AACrB,QAAI,OAAO,UAAU,UAAW,QAAO;AACvC,QAAI,UAAU,OAAQ,QAAO;AAC7B,QAAI,UAAU,QAAS,QAAO;AAAA,EAChC;AACA,SAAO;AACT;AAEA,SAAS,gBAAgB,QAAqB,MAA0B;AACtE,aAAW,OAAO,MAAM;AACtB,UAAM,QAAQ,IAAI,GAAG;AACrB,QAAI,CAAC,MAAM,QAAQ,KAAK,EAAG;AAC3B,UAAM,MAAM,MAAM,OAAO,CAAC,UAA2B,OAAO,UAAU,YAAY,MAAM,SAAS,CAAC;AAClG,QAAI,IAAI,SAAS,EAAG,QAAO;AAAA,EAC7B;AACA,SAAO,CAAC;AACV;AAEO,SAAS,cAAc,KAAsC;AAClE,QAAM,KAAK,WAAW,KAAK,IAAI;AAC/B,MAAI,CAAC,GAAI,QAAO;AAChB,SAAO;AAAA,IACL;AAAA,IACA,MAAM,WAAW,KAAK,MAAM,KAAK;AAAA,IACjC,MAAM,WAAW,KAAK,MAAM;AAAA,IAC5B,OAAO,WAAW,KAAK,OAAO;AAAA,IAC9B,UAAU,WAAW,KAAK,UAAU,KAAK;AAAA,IACzC,WAAW,YAAY,KAAK,cAAc,WAAW;AAAA,IACrD,QAAQ,YAAY,KAAK,WAAW,QAAQ;AAAA,EAC9C;AACF;AAEO,SAAS,YAAY,KAAoC;AAC9D,QAAM,KAAK,WAAW,KAAK,IAAI;AAC/B,MAAI,CAAC,GAAI,QAAO;AAChB,SAAO;AAAA,IACL;AAAA,IACA,OAAO,WAAW,KAAK,OAAO,KAAK;AAAA,IACnC,WAAW,WAAW,KAAK,WAAW;AAAA,IACtC,eAAe,WAAW,KAAK,mBAAmB,eAAe;AAAA,IACjE,cAAc,WAAW,KAAK,kBAAkB,cAAc;AAAA,IAC9D,cAAc,WAAW,KAAK,kBAAkB,cAAc;AAAA,IAC9D,uBAAuB,WAAW,KAAK,4BAA4B,uBAAuB;AAAA,IAC1F,UAAU,WAAW,KAAK,UAAU,KAAK;AAAA,IACzC,YAAY,WAAW,KAAK,cAAc,aAAa,KAAK;AAAA,IAC5D,eAAe,WAAW,KAAK,iBAAiB,gBAAgB,KAAK;AAAA,IACrE,YAAY,WAAW,KAAK,cAAc,aAAa,KAAK;AAAA,IAC5D,UAAU,WAAW,KAAK,aAAa,UAAU;AAAA,IACjD,WAAW,WAAW,KAAK,cAAc,WAAW;AAAA,IACpD,QAAQ,gBAAgB,KAAK,UAAU,SAAS;AAAA,EAClD;AACF;AAEO,SAAS,UAAU,SAAiC;AACzD,MAAI,CAAC,WAAW,OAAO,YAAY,SAAU,QAAO,CAAC;AACrD,QAAM,QAAS,QAAgC;AAC/C,MAAI,CAAC,MAAM,QAAQ,KAAK,EAAG,QAAO,CAAC;AACnC,SAAO,MAAM,OAAO,CAAC,SAA8B,CAAC,CAAC,QAAQ,OAAO,SAAS,QAAQ;AACvF;AAEO,SAAS,UAAU,SAAkB,UAA0B;AACpE,MAAI,CAAC,WAAW,OAAO,YAAY,SAAU,QAAO;AACpD,QAAM,QAAS,QAAgC;AAC/C,SAAO,OAAO,UAAU,YAAY,OAAO,SAAS,KAAK,IAAI,QAAQ;AACvE;AAGO,SAAS,mBAAmB,SAAyB;AAC1D,SAAO,eAAe,OAAO,SAAS,OAAO,IAAI,UAAU,GAAG,OAAO;AACvE;AAMO,SAAS,oBAAoB,OAAqC;AACvE,SAAO;AAAA,IACL,MAAM,IAAI,CAAC,UAAU;AAAA,MACnB,IAAI,KAAK;AAAA,MACT,cAAc,KAAK;AAAA,MACnB,eAAe,KAAK;AAAA,IACtB,EAAE;AAAA,EACJ;AACF;AASO,SAAS,uBACd,UACA,eAC8B;AAC9B,QAAM,WAAW,oBAAI,IAA6B;AAClD,aAAW,SAAS,UAAU;AAC5B,QAAI,CAAC,MAAM,aAAc;AACzB,UAAM,QAAQ,SAAS,IAAI,MAAM,YAAY,KAAK,EAAE,OAAO,GAAG,MAAM,EAAE;AACtE,UAAM,SAAS;AACf,UAAM,SAAU,MAAM,gBAAgB,cAAc,IAAI,MAAM,YAAY,KAAM,CAAC,CAAC,MAAM;AACxF,QAAI,OAAQ,OAAM,QAAQ;AAC1B,aAAS,IAAI,MAAM,cAAc,KAAK;AAAA,EACxC;AACA,SAAO;AACT;AASO,SAAS,wBAAwB,OAAqC;AAC3E,MAAI,SAAwB;AAC5B,aAAW,QAAQ,OAAO;AACxB,QAAI,CAAC,OAAO,SAAS,KAAK,QAAQ,EAAG;AACrC,QAAI,WAAW,QAAQ,KAAK,WAAW,OAAQ,UAAS,KAAK;AAAA,EAC/D;AACA,MAAI,WAAW,KAAM,QAAO;AAC5B,QAAM,YAAY,KAAK,MAAM,SAAS,CAAC;AACvC,SAAO,YAAY,IAAI,YAAY;AACrC;AAGO,SAAS,iBAAiB,MAAyC;AACxE,QAAM,QAAQ,QAAQ,IAAI,KAAK;AAC/B,MAAI,CAAC,KAAM,QAAO;AAClB,QAAM,QAAQ,KAAK,MAAM,KAAK,EAAE,OAAO,CAAC,SAAS,KAAK,SAAS,CAAC;AAChE,MAAI,MAAM,WAAW,EAAG,QAAO;AAC/B,MAAI,MAAM,WAAW,EAAG,QAAO,MAAM,CAAC,EAAE,MAAM,GAAG,CAAC,EAAE,YAAY;AAChE,SAAO,GAAG,MAAM,CAAC,EAAE,CAAC,CAAC,GAAG,MAAM,MAAM,SAAS,CAAC,EAAE,CAAC,CAAC,GAAG,YAAY;AACnE;AASO,SAAS,kBAAkB,QAA6B;AAC7D,SAAO,uBAAuB,OAAO,OAAO,OAAO,IAAI;AACzD;AAEO,SAAS,eAA2D,MAAyB;AAClG,SAAO,CAAC,GAAG,IAAI,EAAE,KAAK,CAAC,MAAM,UAAU;AACrC,QAAI,KAAK,aAAa,MAAM,SAAU,QAAO,KAAK,WAAW,MAAM;AACnE,WAAO,KAAK,KAAK,MAAM,KAAK,KAAK,KAAK,KAAK,MAAM,KAAK,IAAI;AAAA,EAC5D,CAAC;AACH;",
6
+ "names": []
7
+ }
@@ -0,0 +1,92 @@
1
+ import { formatDuration } from "../time-tracking/duration.js";
2
+ import { DEFAULT_ASSIGNMENT_GRACE_DAYS, MAX_ASSIGNMENT_GRACE_DAYS } from "../time-tracking/settings.js";
3
+ const MS_PER_DAY = 864e5;
4
+ const ISO_DATE_PREFIX = /^(\d{4})-(\d{2})-(\d{2})/;
5
+ function toDayIndex(value) {
6
+ if (value == null) return null;
7
+ if (value instanceof Date) {
8
+ if (Number.isNaN(value.getTime())) return null;
9
+ return Date.UTC(value.getFullYear(), value.getMonth(), value.getDate()) / MS_PER_DAY;
10
+ }
11
+ const match = ISO_DATE_PREFIX.exec(String(value).trim());
12
+ if (!match) return null;
13
+ return Date.UTC(Number(match[1]), Number(match[2]) - 1, Number(match[3])) / MS_PER_DAY;
14
+ }
15
+ function toIsoDate(value) {
16
+ if (value == null) return null;
17
+ if (value instanceof Date) {
18
+ if (Number.isNaN(value.getTime())) return null;
19
+ const month = String(value.getMonth() + 1).padStart(2, "0");
20
+ const day = String(value.getDate()).padStart(2, "0");
21
+ return `${value.getFullYear()}-${month}-${day}`;
22
+ }
23
+ const match = ISO_DATE_PREFIX.exec(String(value).trim());
24
+ return match ? `${match[1]}-${match[2]}-${match[3]}` : null;
25
+ }
26
+ function resolveGraceDays(value) {
27
+ if (typeof value !== "number" || !Number.isInteger(value)) return DEFAULT_ASSIGNMENT_GRACE_DAYS;
28
+ if (value < 0 || value > MAX_ASSIGNMENT_GRACE_DAYS) return DEFAULT_ASSIGNMENT_GRACE_DAYS;
29
+ return value;
30
+ }
31
+ function resolveAssignmentState(member, graceDays, now = /* @__PURE__ */ new Date()) {
32
+ const todayIndex = toDayIndex(now);
33
+ if (todayIndex === null) return "active";
34
+ const startIndex = toDayIndex(member.startDate);
35
+ if (startIndex !== null && todayIndex < startIndex) return "scheduled";
36
+ const endIndex = toDayIndex(member.endDate);
37
+ if (endIndex === null) return "active";
38
+ return todayIndex > endIndex + resolveGraceDays(graceDays) ? "expired" : "active";
39
+ }
40
+ function formatProjectMinutes(minutes) {
41
+ return formatDuration(Number.isFinite(minutes) ? minutes : 0, "clock");
42
+ }
43
+ function diffTeamSelection(assigned, draft, lockedStaffMemberIds = /* @__PURE__ */ new Set()) {
44
+ const assignedByStaffMemberId = /* @__PURE__ */ new Map();
45
+ for (const member of assigned) {
46
+ if (!assignedByStaffMemberId.has(member.staffMemberId)) {
47
+ assignedByStaffMemberId.set(member.staffMemberId, member);
48
+ }
49
+ }
50
+ const additions = [];
51
+ for (const staffMemberId of draft.selectedStaffMemberIds) {
52
+ if (assignedByStaffMemberId.has(staffMemberId)) continue;
53
+ if (lockedStaffMemberIds.has(staffMemberId)) continue;
54
+ additions.push(staffMemberId);
55
+ }
56
+ const removals = [];
57
+ const endDateUpdates = [];
58
+ for (const member of assignedByStaffMemberId.values()) {
59
+ if (lockedStaffMemberIds.has(member.staffMemberId)) continue;
60
+ if (!draft.selectedStaffMemberIds.has(member.staffMemberId)) {
61
+ removals.push(member);
62
+ continue;
63
+ }
64
+ if (!draft.endDateByStaffMemberId.has(member.staffMemberId)) continue;
65
+ const nextEndDate = draft.endDateByStaffMemberId.get(member.staffMemberId) ?? null;
66
+ if (toIsoDate(nextEndDate) === toIsoDate(member.endDate)) continue;
67
+ endDateUpdates.push({ member, endDate: toIsoDate(nextEndDate) });
68
+ }
69
+ return { additions, removals, endDateUpdates };
70
+ }
71
+ function countPendingChanges(changes) {
72
+ return changes.additions.length + changes.removals.length + changes.endDateUpdates.length;
73
+ }
74
+ function selectChangeCountKey(count) {
75
+ const safe = Math.abs(Math.trunc(Number.isFinite(count) ? count : 0));
76
+ if (safe === 1) return "one";
77
+ const lastTwo = safe % 100;
78
+ if (lastTwo >= 12 && lastTwo <= 14) return "many";
79
+ const last = safe % 10;
80
+ return last >= 2 && last <= 4 ? "few" : "many";
81
+ }
82
+ export {
83
+ countPendingChanges,
84
+ diffTeamSelection,
85
+ formatProjectMinutes,
86
+ resolveAssignmentState,
87
+ resolveGraceDays,
88
+ selectChangeCountKey,
89
+ toDayIndex,
90
+ toIsoDate
91
+ };
92
+ //# sourceMappingURL=projectTeamAssignment.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../src/modules/staff/lib/time-tracking-ui/projectTeamAssignment.ts"],
4
+ "sourcesContent": ["import { formatDuration } from '../time-tracking/duration'\nimport { DEFAULT_ASSIGNMENT_GRACE_DAYS, MAX_ASSIGNMENT_GRACE_DAYS } from '../time-tracking/settings'\n\nconst MS_PER_DAY = 86_400_000\nconst ISO_DATE_PREFIX = /^(\\d{4})-(\\d{2})-(\\d{2})/\n\nexport type AssignedMember = {\n membershipId: string\n staffMemberId: string\n role: string | null\n status: string | null\n startDate: string | null\n endDate: string | null\n}\n\nexport type TeamDraft = {\n selectedStaffMemberIds: ReadonlySet<string>\n endDateByStaffMemberId: ReadonlyMap<string, string | null>\n}\n\nexport type TeamChangeSet = {\n additions: string[]\n removals: AssignedMember[]\n endDateUpdates: Array<{ member: AssignedMember; endDate: string | null }>\n}\n\nexport type AssignmentState = 'active' | 'scheduled' | 'expired'\n\n/**\n * Client-side twin of the server day comparison in\n * `lib/time-tracking/access.ts`. `assigned_start_date` / `assigned_end_date` are\n * `date` columns, so both bounds are whole days, never timestamps.\n */\nexport function toDayIndex(value: string | Date | null | undefined): number | null {\n if (value == null) return null\n if (value instanceof Date) {\n if (Number.isNaN(value.getTime())) return null\n return Date.UTC(value.getFullYear(), value.getMonth(), value.getDate()) / MS_PER_DAY\n }\n const match = ISO_DATE_PREFIX.exec(String(value).trim())\n if (!match) return null\n return Date.UTC(Number(match[1]), Number(match[2]) - 1, Number(match[3])) / MS_PER_DAY\n}\n\n/** Normalizes a `date` column value into the `YYYY-MM-DD` a date input expects. */\nexport function toIsoDate(value: string | Date | null | undefined): string | null {\n if (value == null) return null\n if (value instanceof Date) {\n if (Number.isNaN(value.getTime())) return null\n const month = String(value.getMonth() + 1).padStart(2, '0')\n const day = String(value.getDate()).padStart(2, '0')\n return `${value.getFullYear()}-${month}-${day}`\n }\n const match = ISO_DATE_PREFIX.exec(String(value).trim())\n return match ? `${match[1]}-${match[2]}-${match[3]}` : null\n}\n\n/**\n * Mirrors `normalizeAssignmentGraceDays` for the browser. The server helper\n * cannot be imported here because it ships with the ORM-backed access resolver.\n */\nexport function resolveGraceDays(value: unknown): number {\n if (typeof value !== 'number' || !Number.isInteger(value)) return DEFAULT_ASSIGNMENT_GRACE_DAYS\n if (value < 0 || value > MAX_ASSIGNMENT_GRACE_DAYS) return DEFAULT_ASSIGNMENT_GRACE_DAYS\n return value\n}\n\n/**\n * D-12: a membership only grants access from `assigned_start_date` until\n * `assigned_end_date` plus the tenant's grace days. A row outside that window\n * still exists but no longer opens the project, so the drawer must not draw it\n * as an ordinary assignment.\n */\nexport function resolveAssignmentState(\n member: Pick<AssignedMember, 'startDate' | 'endDate'>,\n graceDays: number,\n now: Date = new Date(),\n): AssignmentState {\n const todayIndex = toDayIndex(now)\n if (todayIndex === null) return 'active'\n const startIndex = toDayIndex(member.startDate)\n if (startIndex !== null && todayIndex < startIndex) return 'scheduled'\n const endIndex = toDayIndex(member.endDate)\n if (endIndex === null) return 'active'\n return todayIndex > endIndex + resolveGraceDays(graceDays) ? 'expired' : 'active'\n}\n\nexport function formatProjectMinutes(minutes: number): string {\n return formatDuration(Number.isFinite(minutes) ? minutes : 0, 'clock')\n}\n\nexport function diffTeamSelection(\n assigned: readonly AssignedMember[],\n draft: TeamDraft,\n lockedStaffMemberIds: ReadonlySet<string> = new Set<string>(),\n): TeamChangeSet {\n const assignedByStaffMemberId = new Map<string, AssignedMember>()\n for (const member of assigned) {\n if (!assignedByStaffMemberId.has(member.staffMemberId)) {\n assignedByStaffMemberId.set(member.staffMemberId, member)\n }\n }\n\n const additions: string[] = []\n for (const staffMemberId of draft.selectedStaffMemberIds) {\n if (assignedByStaffMemberId.has(staffMemberId)) continue\n if (lockedStaffMemberIds.has(staffMemberId)) continue\n additions.push(staffMemberId)\n }\n\n const removals: AssignedMember[] = []\n const endDateUpdates: Array<{ member: AssignedMember; endDate: string | null }> = []\n for (const member of assignedByStaffMemberId.values()) {\n if (lockedStaffMemberIds.has(member.staffMemberId)) continue\n if (!draft.selectedStaffMemberIds.has(member.staffMemberId)) {\n removals.push(member)\n continue\n }\n if (!draft.endDateByStaffMemberId.has(member.staffMemberId)) continue\n const nextEndDate = draft.endDateByStaffMemberId.get(member.staffMemberId) ?? null\n if (toIsoDate(nextEndDate) === toIsoDate(member.endDate)) continue\n endDateUpdates.push({ member, endDate: toIsoDate(nextEndDate) })\n }\n\n return { additions, removals, endDateUpdates }\n}\n\nexport function countPendingChanges(changes: TeamChangeSet): number {\n return changes.additions.length + changes.removals.length + changes.endDateUpdates.length\n}\n\n/**\n * Slavic plural buckets (Polish is the mockup's source language); locales with\n * fewer forms simply translate `few` and `many` to the same string.\n */\nexport function selectChangeCountKey(count: number): 'one' | 'few' | 'many' {\n const safe = Math.abs(Math.trunc(Number.isFinite(count) ? count : 0))\n if (safe === 1) return 'one'\n const lastTwo = safe % 100\n if (lastTwo >= 12 && lastTwo <= 14) return 'many'\n const last = safe % 10\n return last >= 2 && last <= 4 ? 'few' : 'many'\n}\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
@@ -0,0 +1,111 @@
1
+ import { formatDuration } from "../time-tracking/duration.js";
2
+ import { readNumber, readString } from "./kanbanBoardData.js";
3
+ function toDrawerComment(row) {
4
+ const id = readString(row, "id");
5
+ const body = readString(row, "body");
6
+ if (!id || !body) return null;
7
+ return {
8
+ id,
9
+ body,
10
+ authorUserId: readString(row, "authorUserId", "author_user_id"),
11
+ authorName: readString(row, "authorName", "author_name"),
12
+ authorEmail: readString(row, "authorEmail", "author_email"),
13
+ createdAt: readString(row, "createdAt", "created_at")
14
+ };
15
+ }
16
+ function toDrawerEntry(row) {
17
+ const id = readString(row, "id");
18
+ if (!id) return null;
19
+ const billable = row.is_billable ?? row.isBillable;
20
+ const cost = readNumber(row, "cost");
21
+ return {
22
+ id,
23
+ taskId: readString(row, "task_id", "taskId"),
24
+ date: readString(row, "date"),
25
+ description: readString(row, "description", "notes"),
26
+ durationMinutes: readNumber(row, "duration_minutes", "durationMinutes") ?? 0,
27
+ isBillable: billable !== false,
28
+ cost: cost === null ? null : cost,
29
+ currencyCode: readString(row, "currencyCode", "rate_currency_code")
30
+ };
31
+ }
32
+ function summarizeEntries(entries, partial) {
33
+ let billableMinutes = 0;
34
+ let nonBillableMinutes = 0;
35
+ let cost = null;
36
+ let currencyCode = null;
37
+ for (const entry of entries) {
38
+ if (entry.isBillable) billableMinutes += entry.durationMinutes;
39
+ else nonBillableMinutes += entry.durationMinutes;
40
+ if (entry.cost === null) continue;
41
+ cost = (cost ?? 0) + entry.cost;
42
+ currencyCode = currencyCode ?? entry.currencyCode;
43
+ }
44
+ return { billableMinutes, nonBillableMinutes, cost, currencyCode, partial };
45
+ }
46
+ function resolveDoneStatusId(statuses) {
47
+ return statuses.find((status) => status.isDone)?.id ?? null;
48
+ }
49
+ function resolveDefaultStatusId(statuses) {
50
+ const flagged = statuses.find((status) => status.isDefault);
51
+ if (flagged) return flagged.id;
52
+ return statuses.find((status) => !status.isDone)?.id ?? null;
53
+ }
54
+ function formatElapsed(startedAt, now) {
55
+ if (!startedAt) return "00:00:00";
56
+ const started = Date.parse(startedAt);
57
+ if (!Number.isFinite(started)) return "00:00:00";
58
+ const seconds = Math.max(0, Math.floor((now - started) / 1e3));
59
+ const hours = Math.floor(seconds / 3600);
60
+ const minutes = Math.floor(seconds % 3600 / 60);
61
+ const rest = seconds % 60;
62
+ return [hours, minutes, rest].map((part) => String(part).padStart(2, "0")).join(":");
63
+ }
64
+ function formatSplitMinutes(minutes) {
65
+ return formatDuration(Number.isFinite(minutes) && minutes > 0 ? minutes : 0, "hm");
66
+ }
67
+ function childMinutes(loggedMinutes, ownMinutes) {
68
+ const delta = loggedMinutes - ownMinutes;
69
+ return delta > 0 ? delta : 0;
70
+ }
71
+ function todayIsoDate(now = /* @__PURE__ */ new Date()) {
72
+ const month = String(now.getMonth() + 1).padStart(2, "0");
73
+ const day = String(now.getDate()).padStart(2, "0");
74
+ return `${now.getFullYear()}-${month}-${day}`;
75
+ }
76
+ function clockToMinutes(clock) {
77
+ const match = /^(\d{1,2}):(\d{2})$/.exec(clock.trim());
78
+ if (!match) return null;
79
+ const hours = Number(match[1]);
80
+ const mins = Number(match[2]);
81
+ if (!Number.isFinite(hours) || !Number.isFinite(mins) || hours > 23 || mins > 59) return null;
82
+ return hours * 60 + mins;
83
+ }
84
+ function minutesToClock(total) {
85
+ const wrapped = (total % 1440 + 1440) % 1440;
86
+ return `${String(Math.floor(wrapped / 60)).padStart(2, "0")}:${String(wrapped % 60).padStart(2, "0")}`;
87
+ }
88
+ function buildEntryClocks({ date, startClock, endClock, minutes }) {
89
+ const start = startClock ? clockToMinutes(startClock) : null;
90
+ const end = endClock ? clockToMinutes(endClock) : null;
91
+ if (start === null && end === null) return {};
92
+ const resolvedStart = start !== null ? start : end - minutes;
93
+ const resolvedEnd = end !== null ? end : start + minutes;
94
+ return {
95
+ startedAt: `${date}T${minutesToClock(resolvedStart)}`,
96
+ endedAt: `${date}T${minutesToClock(resolvedEnd)}`
97
+ };
98
+ }
99
+ export {
100
+ buildEntryClocks,
101
+ childMinutes,
102
+ formatElapsed,
103
+ formatSplitMinutes,
104
+ resolveDefaultStatusId,
105
+ resolveDoneStatusId,
106
+ summarizeEntries,
107
+ toDrawerComment,
108
+ toDrawerEntry,
109
+ todayIsoDate
110
+ };
111
+ //# sourceMappingURL=taskDrawerData.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../src/modules/staff/lib/time-tracking-ui/taskDrawerData.ts"],
4
+ "sourcesContent": ["/**\n * Row parsing and arithmetic for the task drawer of screen 7 (T3.7).\n *\n * Split from `TaskDrawer.tsx` for the same reason `kanbanBoardData.ts` is split\n * from the board: the decisions that shape what the drawer *says* \u2014 which column\n * a ticked subtask moves to, how the billable split is summed, whether that sum\n * is complete \u2014 are unit-testable without a DOM, and the component stays about\n * fetching, mutating and rendering.\n *\n * Board helpers are reused rather than re-derived (`toBoardTask`,\n * `formatBoardMinutes`, `computeSubtaskProgress`), so a task row means the same\n * thing on the card and inside the drawer.\n */\n\nimport { formatDuration } from '../time-tracking/duration'\nimport { readNumber, readString, type BoardApiRow, type BoardStatus } from './kanbanBoardData'\n\nexport type DrawerComment = {\n id: string\n body: string\n authorUserId: string | null\n authorName: string | null\n authorEmail: string | null\n createdAt: string | null\n}\n\nexport type DrawerEntry = {\n id: string\n taskId: string | null\n date: string | null\n description: string | null\n durationMinutes: number\n isBillable: boolean\n /** Present only for a caller holding `staff.timesheets.rates.view`. */\n cost: number | null\n currencyCode: string | null\n}\n\nexport type EntryTotals = {\n billableMinutes: number\n nonBillableMinutes: number\n /** `null` when the caller may not see money, or when no entry carried a cost. */\n cost: number | null\n currencyCode: string | null\n /** The entries scan hit its page bound, so the split is a lower bound. */\n partial: boolean\n}\n\nexport function toDrawerComment(row: BoardApiRow): DrawerComment | null {\n const id = readString(row, 'id')\n const body = readString(row, 'body')\n if (!id || !body) return null\n return {\n id,\n body,\n authorUserId: readString(row, 'authorUserId', 'author_user_id'),\n authorName: readString(row, 'authorName', 'author_name'),\n authorEmail: readString(row, 'authorEmail', 'author_email'),\n createdAt: readString(row, 'createdAt', 'created_at'),\n }\n}\n\nexport function toDrawerEntry(row: BoardApiRow): DrawerEntry | null {\n const id = readString(row, 'id')\n if (!id) return null\n const billable = row.is_billable ?? row.isBillable\n const cost = readNumber(row, 'cost')\n return {\n id,\n taskId: readString(row, 'task_id', 'taskId'),\n date: readString(row, 'date'),\n description: readString(row, 'description', 'notes'),\n durationMinutes: readNumber(row, 'duration_minutes', 'durationMinutes') ?? 0,\n isBillable: billable !== false,\n cost: cost === null ? null : cost,\n currencyCode: readString(row, 'currencyCode', 'rate_currency_code'),\n }\n}\n\n/**\n * The billable / non-billable / cost split of screen 7 (note 5).\n *\n * Money only exists in the rows when the caller holds\n * `staff.timesheets.rates.view` \u2014 the entries route omits the keys entirely for\n * everyone else \u2014 so an absent `cost` stays `null` here instead of becoming a\n * zero that would read as \"this work is free\".\n */\nexport function summarizeEntries(entries: readonly DrawerEntry[], partial: boolean): EntryTotals {\n let billableMinutes = 0\n let nonBillableMinutes = 0\n let cost: number | null = null\n let currencyCode: string | null = null\n for (const entry of entries) {\n if (entry.isBillable) billableMinutes += entry.durationMinutes\n else nonBillableMinutes += entry.durationMinutes\n if (entry.cost === null) continue\n cost = (cost ?? 0) + entry.cost\n currencyCode = currencyCode ?? entry.currencyCode\n }\n return { billableMinutes, nonBillableMinutes, cost, currencyCode, partial }\n}\n\n/**\n * Ticking a subtask is a status move, not a boolean (D-2): the child goes to the\n * project's first done column, and un-ticking returns it to the default one.\n * A project with no done column cannot express \"done\" at all, which is why the\n * checkbox is disabled rather than silently writing nothing.\n */\nexport function resolveDoneStatusId(statuses: readonly BoardStatus[]): string | null {\n return statuses.find((status) => status.isDone)?.id ?? null\n}\n\nexport function resolveDefaultStatusId(statuses: readonly BoardStatus[]): string | null {\n const flagged = statuses.find((status) => status.isDefault)\n if (flagged) return flagged.id\n return statuses.find((status) => !status.isDone)?.id ?? null\n}\n\n/** `01:14:02` \u2014 the running-timer label of the mockup's stop button. */\nexport function formatElapsed(startedAt: string | null | undefined, now: number): string {\n if (!startedAt) return '00:00:00'\n const started = Date.parse(startedAt)\n if (!Number.isFinite(started)) return '00:00:00'\n const seconds = Math.max(0, Math.floor((now - started) / 1000))\n const hours = Math.floor(seconds / 3600)\n const minutes = Math.floor((seconds % 3600) / 60)\n const rest = seconds % 60\n return [hours, minutes, rest].map((part) => String(part).padStart(2, '0')).join(':')\n}\n\n/** `1h 40m` \u2014 the own/children split reads as a duration, not as a clock. */\nexport function formatSplitMinutes(minutes: number): string {\n return formatDuration(Number.isFinite(minutes) && minutes > 0 ? minutes : 0, 'hm')\n}\n\n/** The minutes a task's children contributed to its inclusive rollup (D-2). */\nexport function childMinutes(loggedMinutes: number, ownMinutes: number): number {\n const delta = loggedMinutes - ownMinutes\n return delta > 0 ? delta : 0\n}\n\nexport function todayIsoDate(now: Date = new Date()): string {\n const month = String(now.getMonth() + 1).padStart(2, '0')\n const day = String(now.getDate()).padStart(2, '0')\n return `${now.getFullYear()}-${month}-${day}`\n}\n\nexport type EntryClockInput = {\n date: string\n startClock: string | null\n endClock: string | null\n minutes: number\n}\n\nexport type EntryClocks = { startedAt?: string; endedAt?: string }\n\nfunction clockToMinutes(clock: string): number | null {\n const match = /^(\\d{1,2}):(\\d{2})$/.exec(clock.trim())\n if (!match) return null\n const hours = Number(match[1])\n const mins = Number(match[2])\n if (!Number.isFinite(hours) || !Number.isFinite(mins) || hours > 23 || mins > 59) return null\n return hours * 60 + mins\n}\n\nfunction minutesToClock(total: number): string {\n const wrapped = ((total % 1440) + 1440) % 1440\n return `${String(Math.floor(wrapped / 60)).padStart(2, '0')}:${String(wrapped % 60).padStart(2, '0')}`\n}\n\n/**\n * Resolves the quick log's optional clocks into a pair, or nothing at all.\n *\n * A lone `started_at` is not a partial record \u2014 it is a *running timer*, because\n * `started_at IS NOT NULL AND ended_at IS NULL` is exactly how the entries list\n * identifies one. An entry saved that way reads as a timer running since that\n * moment and cannot be stopped, since no timer segment was ever opened for it.\n *\n * So the missing half is derived from the duration, which is authoritative in the\n * quick log. Both blank means a duration-only entry, which is a legitimate shape.\n */\nexport function buildEntryClocks({ date, startClock, endClock, minutes }: EntryClockInput): EntryClocks {\n const start = startClock ? clockToMinutes(startClock) : null\n const end = endClock ? clockToMinutes(endClock) : null\n if (start === null && end === null) return {}\n\n const resolvedStart = start !== null ? start : (end as number) - minutes\n const resolvedEnd = end !== null ? end : (start as number) + minutes\n\n return {\n startedAt: `${date}T${minutesToClock(resolvedStart)}`,\n endedAt: `${date}T${minutesToClock(resolvedEnd)}`,\n }\n}\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
@@ -0,0 +1,243 @@
1
+ import {
2
+ deriveInterval,
3
+ formatClock,
4
+ parseClock
5
+ } from "../time-tracking/interval.js";
6
+ const ANCHOR_PRIORITY = ["start", "end", "duration"];
7
+ function hasValue(raw, field) {
8
+ if (field === "duration") return raw.durationMinutes !== null;
9
+ return parseClock(field === "start" ? raw.startText : raw.endText) !== null;
10
+ }
11
+ function chooseAnchors(raw, lastEdited) {
12
+ const present = ANCHOR_PRIORITY.filter((field) => hasValue(raw, field));
13
+ if (present.length <= 2) return present;
14
+ if (!lastEdited) return present.slice(0, 2);
15
+ const second = ANCHOR_PRIORITY.find((field) => field !== lastEdited && hasValue(raw, field));
16
+ return second ? [lastEdited, second] : [lastEdited];
17
+ }
18
+ function buildInput(raw, anchors) {
19
+ const input = {};
20
+ if (anchors.includes("start")) input.start = raw.startText;
21
+ if (anchors.includes("end")) input.end = raw.endText;
22
+ if (anchors.includes("duration") || anchors.includes("start") && anchors.includes("end")) {
23
+ input.durationMinutes = raw.durationMinutes;
24
+ }
25
+ return input;
26
+ }
27
+ function applyDerivation(raw, previous, lastEdited, anchors) {
28
+ const derived = deriveInterval(buildInput(raw, anchors));
29
+ const durationMinutes = derived.computed === "duration" ? derived.durationMinutes : raw.durationMinutes;
30
+ const durationChanged = derived.computed === "duration" && durationMinutes !== previous.durationMinutes;
31
+ return {
32
+ startText: derived.computed === "start" && derived.start ? derived.start : raw.startText,
33
+ endText: derived.computed === "end" && derived.end ? derived.end : raw.endText,
34
+ durationMinutes,
35
+ durationInvalid: derived.computed === "duration" ? false : previous.durationInvalid,
36
+ computed: derived.computed,
37
+ crossesMidnight: derived.crossesMidnight,
38
+ durationEpoch: durationChanged ? previous.durationEpoch + 1 : previous.durationEpoch,
39
+ lastEdited
40
+ };
41
+ }
42
+ const EMPTY_STATE = {
43
+ startText: "",
44
+ endText: "",
45
+ durationMinutes: null,
46
+ durationInvalid: false,
47
+ computed: null,
48
+ crossesMidnight: false,
49
+ durationEpoch: 0,
50
+ lastEdited: null
51
+ };
52
+ function createIntervalState(seed) {
53
+ const raw = {
54
+ startText: seed.start ?? "",
55
+ endText: seed.end ?? "",
56
+ durationMinutes: seed.durationMinutes ?? null
57
+ };
58
+ return applyDerivation(raw, EMPTY_STATE, null, chooseAnchors(raw, null));
59
+ }
60
+ function resetIntervalState(previous, seed) {
61
+ const next = createIntervalState(seed);
62
+ return { ...next, durationEpoch: previous.durationEpoch + 1 };
63
+ }
64
+ function reduceIntervalState(state, edit) {
65
+ if (edit.field === "snapStart") {
66
+ const raw2 = { ...toRaw(state), startText: edit.value };
67
+ const anchors = raw2.durationMinutes !== null ? ["start", "duration"] : ["start", "end"];
68
+ return applyDerivation(raw2, state, "start", anchors);
69
+ }
70
+ if (edit.field === "duration") {
71
+ const raw2 = { ...toRaw(state), durationMinutes: edit.minutes };
72
+ const next = applyDerivation(raw2, state, "duration", chooseAnchors(raw2, "duration"));
73
+ return { ...next, durationInvalid: edit.invalid };
74
+ }
75
+ const raw = edit.field === "start" ? { ...toRaw(state), startText: edit.value } : { ...toRaw(state), endText: edit.value };
76
+ return applyDerivation(raw, state, edit.field, chooseAnchors(raw, edit.field));
77
+ }
78
+ function toRaw(state) {
79
+ return { startText: state.startText, endText: state.endText, durationMinutes: state.durationMinutes };
80
+ }
81
+ const DATE_PATTERN = /^(\d{4})-(\d{2})-(\d{2})$/;
82
+ function shiftIsoDate(date, days) {
83
+ const match = DATE_PATTERN.exec(date);
84
+ if (!match) return date;
85
+ const shifted = new Date(Date.UTC(Number(match[1]), Number(match[2]) - 1, Number(match[3]) + days));
86
+ const month = String(shifted.getUTCMonth() + 1).padStart(2, "0");
87
+ const day = String(shifted.getUTCDate()).padStart(2, "0");
88
+ return `${shifted.getUTCFullYear()}-${month}-${day}`;
89
+ }
90
+ function todayIsoDate(now = /* @__PURE__ */ new Date()) {
91
+ const month = String(now.getMonth() + 1).padStart(2, "0");
92
+ const day = String(now.getDate()).padStart(2, "0");
93
+ return `${now.getFullYear()}-${month}-${day}`;
94
+ }
95
+ function combineDateAndClock(date, clock, plusDays = 0) {
96
+ const match = DATE_PATTERN.exec(date);
97
+ const minutesOfDay = parseClock(clock);
98
+ if (!match || minutesOfDay === null) return null;
99
+ const composed = new Date(
100
+ Number(match[1]),
101
+ Number(match[2]) - 1,
102
+ Number(match[3]) + plusDays,
103
+ Math.floor(minutesOfDay / 60),
104
+ minutesOfDay % 60,
105
+ 0,
106
+ 0
107
+ );
108
+ if (Number.isNaN(composed.getTime())) return null;
109
+ return composed.toISOString();
110
+ }
111
+ function clockFromApiValue(value) {
112
+ if (typeof value !== "string" || value.trim().length === 0) return "";
113
+ const trimmed = value.trim();
114
+ const direct = parseClock(trimmed);
115
+ if (direct !== null) return formatClock(direct);
116
+ const parsed = new Date(trimmed);
117
+ if (Number.isNaN(parsed.getTime())) return "";
118
+ return formatClock(parsed.getHours() * 60 + parsed.getMinutes());
119
+ }
120
+ function dateFromApiValue(value) {
121
+ if (typeof value !== "string" || value.trim().length === 0) return "";
122
+ const match = /^(\d{4}-\d{2}-\d{2})/.exec(value.trim());
123
+ if (match) return match[1];
124
+ const parsed = new Date(value);
125
+ if (Number.isNaN(parsed.getTime())) return "";
126
+ return todayIsoDate(parsed);
127
+ }
128
+ function readRowString(row, ...keys) {
129
+ for (const key of keys) {
130
+ const value = row[key];
131
+ if (typeof value === "string" && value.trim().length > 0) return value;
132
+ }
133
+ return null;
134
+ }
135
+ function readRowNumber(row, ...keys) {
136
+ for (const key of keys) {
137
+ const value = row[key];
138
+ if (typeof value === "number" && Number.isFinite(value)) return value;
139
+ if (typeof value === "string" && value.trim().length > 0) {
140
+ const parsed = Number(value);
141
+ if (Number.isFinite(parsed)) return parsed;
142
+ }
143
+ }
144
+ return null;
145
+ }
146
+ function readRowBoolean(row, fallback, ...keys) {
147
+ for (const key of keys) {
148
+ const value = row[key];
149
+ if (typeof value === "boolean") return value;
150
+ }
151
+ return fallback;
152
+ }
153
+ function readRowItems(payload) {
154
+ const items = payload?.items;
155
+ return Array.isArray(items) ? items.filter((item) => !!item && typeof item === "object") : [];
156
+ }
157
+ function toTimeEntryRecord(row) {
158
+ const id = readRowString(row, "id");
159
+ if (!id) return null;
160
+ const tags = Array.isArray(row.tags) ? row.tags : [];
161
+ return {
162
+ id,
163
+ date: dateFromApiValue(readRowString(row, "date")),
164
+ startText: clockFromApiValue(readRowString(row, "started_at", "startedAt")),
165
+ endText: clockFromApiValue(readRowString(row, "ended_at", "endedAt")),
166
+ durationMinutes: readRowNumber(row, "duration_minutes", "durationMinutes"),
167
+ description: readRowString(row, "description", "notes") ?? "",
168
+ taskId: readRowString(row, "task_id", "taskId"),
169
+ timeProjectId: readRowString(row, "time_project_id", "timeProjectId"),
170
+ isBillable: readRowBoolean(row, true, "is_billable", "isBillable"),
171
+ rateOverrideAmount: readRowNumber(row, "rate_override_amount", "rateOverrideAmount"),
172
+ currencyCode: readRowString(row, "currencyCode", "rate_currency_code", "rateCurrencyCode"),
173
+ tagIds: tags.map((tag) => tag && typeof tag === "object" ? readRowString(tag, "id") : null).filter((value) => value !== null),
174
+ isLocked: readRowBoolean(row, false, "isLocked", "is_locked"),
175
+ lockedReportId: readRowString(row, "lockedReportId", "locked_report_id"),
176
+ updatedAt: readRowString(row, "updated_at", "updatedAt")
177
+ };
178
+ }
179
+ function toTaskOption(row) {
180
+ const id = readRowString(row, "id");
181
+ const title = readRowString(row, "title");
182
+ if (!id || !title) return null;
183
+ const logged = row.loggedMinutes ?? row.logged_minutes;
184
+ return {
185
+ id,
186
+ title,
187
+ reference: readRowString(row, "reference"),
188
+ timeProjectId: readRowString(row, "time_project_id", "timeProjectId"),
189
+ taskStatusId: readRowString(row, "task_status_id", "taskStatusId"),
190
+ loggedMinutes: typeof logged === "number" ? logged : null
191
+ };
192
+ }
193
+ function toProjectOption(row) {
194
+ const id = readRowString(row, "id");
195
+ if (!id) return null;
196
+ const snapshot = row.customer_snapshot ?? row.customerSnapshot;
197
+ const customerName = snapshot && typeof snapshot === "object" ? readRowString(snapshot, "name", "displayName") : null;
198
+ return {
199
+ id,
200
+ name: readRowString(row, "name") ?? id,
201
+ customerName,
202
+ hourlyRate: readRowNumber(row, "hourly_rate", "hourlyRate"),
203
+ currencyCode: readRowString(row, "currency_code", "currencyCode"),
204
+ billableByDefault: typeof row.billable_by_default === "boolean" ? row.billable_by_default : typeof row.billableByDefault === "boolean" ? row.billableByDefault : null
205
+ };
206
+ }
207
+ function describeTaskOption(task, project) {
208
+ const label = [task.title, project?.customerName ?? null, project?.name ?? null].filter((part) => !!part && part.trim().length > 0).join(" \u2014 ");
209
+ return task.reference ? `${task.reference} \xB7 ${label}` : label;
210
+ }
211
+ function toOverlapEntry(row) {
212
+ const id = readRowString(row, "id");
213
+ if (!id) return null;
214
+ return {
215
+ id,
216
+ startText: clockFromApiValue(readRowString(row, "started_at", "startedAt")),
217
+ endText: clockFromApiValue(readRowString(row, "ended_at", "endedAt")),
218
+ durationMinutes: readRowNumber(row, "duration_minutes", "durationMinutes") ?? 0,
219
+ taskTitle: readRowString(row, "task_title", "taskTitle"),
220
+ projectName: readRowString(row, "project_name", "projectName"),
221
+ description: readRowString(row, "notes", "description")
222
+ };
223
+ }
224
+ export {
225
+ clockFromApiValue,
226
+ combineDateAndClock,
227
+ createIntervalState,
228
+ dateFromApiValue,
229
+ describeTaskOption,
230
+ readRowBoolean,
231
+ readRowItems,
232
+ readRowNumber,
233
+ readRowString,
234
+ reduceIntervalState,
235
+ resetIntervalState,
236
+ shiftIsoDate,
237
+ toOverlapEntry,
238
+ toProjectOption,
239
+ toTaskOption,
240
+ toTimeEntryRecord,
241
+ todayIsoDate
242
+ };
243
+ //# sourceMappingURL=timeEntryDialogState.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../src/modules/staff/lib/time-tracking-ui/timeEntryDialogState.ts"],
4
+ "sourcesContent": ["import {\n deriveInterval,\n formatClock,\n parseClock,\n type ComputedIntervalField,\n type IntervalInput,\n} from '../time-tracking/interval'\n\nexport type IntervalField = 'start' | 'end' | 'duration'\n\n/**\n * The 2-of-3 state of screen 8. It keeps the three fields as the user left them\n * plus the answer `deriveInterval` gave for the third one, so the badge and the\n * hint can be read straight off the state instead of being recomputed in the\n * render path.\n */\nexport type TimeIntervalState = {\n /** Raw clock text, so half-typed input (`15:1`) is never rewritten. */\n startText: string\n endText: string\n durationMinutes: number | null\n /** `DurationInput` said the text does not parse \u2014 Save is blocked on this. */\n durationInvalid: boolean\n computed: ComputedIntervalField\n crossesMidnight: boolean\n /**\n * Bumped only when the *derived* duration changes. The dialog uses it as the\n * `key` of `DurationInput`, so a machine-filled duration re-renders while the\n * text the user is typing there is left alone.\n */\n durationEpoch: number\n lastEdited: IntervalField | null\n}\n\nexport type TimeIntervalEdit =\n | { field: 'start'; value: string }\n | { field: 'end'; value: string }\n | { field: 'duration'; minutes: number | null; invalid: boolean }\n /** \"Snap start to 13:30\" \u2014 moves the start and keeps the duration. */\n | { field: 'snapStart'; value: string }\n\ntype RawInterval = {\n startText: string\n endText: string\n durationMinutes: number | null\n}\n\n/**\n * Which field yields when all three are filled. `start` is the anchor a person\n * thinks in, so editing the end restates the duration (screen 8 note 1) rather\n * than sliding the start backwards.\n */\nconst ANCHOR_PRIORITY: readonly IntervalField[] = ['start', 'end', 'duration']\n\nfunction hasValue(raw: RawInterval, field: IntervalField): boolean {\n if (field === 'duration') return raw.durationMinutes !== null\n return parseClock(field === 'start' ? raw.startText : raw.endText) !== null\n}\n\nfunction chooseAnchors(raw: RawInterval, lastEdited: IntervalField | null): IntervalField[] {\n const present = ANCHOR_PRIORITY.filter((field) => hasValue(raw, field))\n if (present.length <= 2) return present\n if (!lastEdited) return present.slice(0, 2)\n const second = ANCHOR_PRIORITY.find((field) => field !== lastEdited && hasValue(raw, field))\n return second ? [lastEdited, second] : [lastEdited]\n}\n\nfunction buildInput(raw: RawInterval, anchors: readonly IntervalField[]): IntervalInput {\n const input: IntervalInput = {}\n if (anchors.includes('start')) input.start = raw.startText\n if (anchors.includes('end')) input.end = raw.endText\n // With both clocks anchored the duration is still handed over: `deriveInterval`\n // then answers `computed: null` for values that already agree, which is what\n // keeps a freshly loaded entry from opening with a spurious badge.\n if (anchors.includes('duration') || (anchors.includes('start') && anchors.includes('end'))) {\n input.durationMinutes = raw.durationMinutes\n }\n return input\n}\n\nfunction applyDerivation(\n raw: RawInterval,\n previous: TimeIntervalState,\n lastEdited: IntervalField | null,\n anchors: readonly IntervalField[],\n): TimeIntervalState {\n const derived = deriveInterval(buildInput(raw, anchors))\n const durationMinutes = derived.computed === 'duration' ? derived.durationMinutes : raw.durationMinutes\n const durationChanged = derived.computed === 'duration' && durationMinutes !== previous.durationMinutes\n return {\n startText: derived.computed === 'start' && derived.start ? derived.start : raw.startText,\n endText: derived.computed === 'end' && derived.end ? derived.end : raw.endText,\n durationMinutes,\n durationInvalid: derived.computed === 'duration' ? false : previous.durationInvalid,\n computed: derived.computed,\n crossesMidnight: derived.crossesMidnight,\n durationEpoch: durationChanged ? previous.durationEpoch + 1 : previous.durationEpoch,\n lastEdited,\n }\n}\n\nconst EMPTY_STATE: TimeIntervalState = {\n startText: '',\n endText: '',\n durationMinutes: null,\n durationInvalid: false,\n computed: null,\n crossesMidnight: false,\n durationEpoch: 0,\n lastEdited: null,\n}\n\nexport function createIntervalState(seed: {\n start?: string | null\n end?: string | null\n durationMinutes?: number | null\n}): TimeIntervalState {\n const raw: RawInterval = {\n startText: seed.start ?? '',\n endText: seed.end ?? '',\n durationMinutes: seed.durationMinutes ?? null,\n }\n return applyDerivation(raw, EMPTY_STATE, null, chooseAnchors(raw, null))\n}\n\n/**\n * Re-seeds the three fields for a different entry \u2014 or for the next one after\n * \"save and add another\" \u2014 and always advances `durationEpoch`, so the duration\n * field remounts and drops whatever text was left in it.\n */\nexport function resetIntervalState(\n previous: TimeIntervalState,\n seed: { start?: string | null; end?: string | null; durationMinutes?: number | null },\n): TimeIntervalState {\n const next = createIntervalState(seed)\n return { ...next, durationEpoch: previous.durationEpoch + 1 }\n}\n\nexport function reduceIntervalState(state: TimeIntervalState, edit: TimeIntervalEdit): TimeIntervalState {\n if (edit.field === 'snapStart') {\n const raw: RawInterval = { ...toRaw(state), startText: edit.value }\n // The suggestion moves the start and keeps the length of the work, so the\n // end is the field that follows \u2014 never the duration the user already typed.\n const anchors: IntervalField[] = raw.durationMinutes !== null ? ['start', 'duration'] : ['start', 'end']\n return applyDerivation(raw, state, 'start', anchors)\n }\n if (edit.field === 'duration') {\n const raw: RawInterval = { ...toRaw(state), durationMinutes: edit.minutes }\n const next = applyDerivation(raw, state, 'duration', chooseAnchors(raw, 'duration'))\n return { ...next, durationInvalid: edit.invalid }\n }\n const raw: RawInterval =\n edit.field === 'start'\n ? { ...toRaw(state), startText: edit.value }\n : { ...toRaw(state), endText: edit.value }\n return applyDerivation(raw, state, edit.field, chooseAnchors(raw, edit.field))\n}\n\nfunction toRaw(state: TimeIntervalState): RawInterval {\n return { startText: state.startText, endText: state.endText, durationMinutes: state.durationMinutes }\n}\n\nconst DATE_PATTERN = /^(\\d{4})-(\\d{2})-(\\d{2})$/\n\nexport function shiftIsoDate(date: string, days: number): string {\n const match = DATE_PATTERN.exec(date)\n if (!match) return date\n const shifted = new Date(Date.UTC(Number(match[1]), Number(match[2]) - 1, Number(match[3]) + days))\n const month = String(shifted.getUTCMonth() + 1).padStart(2, '0')\n const day = String(shifted.getUTCDate()).padStart(2, '0')\n return `${shifted.getUTCFullYear()}-${month}-${day}`\n}\n\nexport function todayIsoDate(now: Date = new Date()): string {\n const month = String(now.getMonth() + 1).padStart(2, '0')\n const day = String(now.getDate()).padStart(2, '0')\n return `${now.getFullYear()}-${month}-${day}`\n}\n\n/**\n * Wall clock plus calendar day into the instant the API stores. `plusDays` is 1\n * for the end of an entry that crosses midnight (D-8): the work started on\n * `date` and finished the following calendar day.\n */\nexport function combineDateAndClock(date: string, clock: string, plusDays = 0): string | null {\n const match = DATE_PATTERN.exec(date)\n const minutesOfDay = parseClock(clock)\n if (!match || minutesOfDay === null) return null\n const composed = new Date(\n Number(match[1]),\n Number(match[2]) - 1,\n Number(match[3]) + plusDays,\n Math.floor(minutesOfDay / 60),\n minutesOfDay % 60,\n 0,\n 0,\n )\n if (Number.isNaN(composed.getTime())) return null\n return composed.toISOString()\n}\n\n/** Accepts the wall clock the form sends and the timestamp the timer persists. */\nexport function clockFromApiValue(value: unknown): string {\n if (typeof value !== 'string' || value.trim().length === 0) return ''\n const trimmed = value.trim()\n const direct = parseClock(trimmed)\n if (direct !== null) return formatClock(direct)\n const parsed = new Date(trimmed)\n if (Number.isNaN(parsed.getTime())) return ''\n return formatClock(parsed.getHours() * 60 + parsed.getMinutes())\n}\n\nexport function dateFromApiValue(value: unknown): string {\n if (typeof value !== 'string' || value.trim().length === 0) return ''\n const match = /^(\\d{4}-\\d{2}-\\d{2})/.exec(value.trim())\n if (match) return match[1]\n const parsed = new Date(value)\n if (Number.isNaN(parsed.getTime())) return ''\n return todayIsoDate(parsed)\n}\n\nexport type TimeEntryRecord = {\n id: string\n date: string\n startText: string\n endText: string\n durationMinutes: number | null\n description: string\n taskId: string | null\n timeProjectId: string | null\n isBillable: boolean\n rateOverrideAmount: number | null\n currencyCode: string | null\n tagIds: string[]\n isLocked: boolean\n lockedReportId: string | null\n updatedAt: string | null\n}\n\nexport type ApiRow = Record<string, unknown>\n\nexport function readRowString(row: ApiRow, ...keys: string[]): string | null {\n for (const key of keys) {\n const value = row[key]\n if (typeof value === 'string' && value.trim().length > 0) return value\n }\n return null\n}\n\nexport function readRowNumber(row: ApiRow, ...keys: string[]): number | null {\n for (const key of keys) {\n const value = row[key]\n if (typeof value === 'number' && Number.isFinite(value)) return value\n if (typeof value === 'string' && value.trim().length > 0) {\n const parsed = Number(value)\n if (Number.isFinite(parsed)) return parsed\n }\n }\n return null\n}\n\nexport function readRowBoolean(row: ApiRow, fallback: boolean, ...keys: string[]): boolean {\n for (const key of keys) {\n const value = row[key]\n if (typeof value === 'boolean') return value\n }\n return fallback\n}\n\nexport function readRowItems(payload: unknown): ApiRow[] {\n const items = (payload as { items?: unknown } | null)?.items\n return Array.isArray(items) ? (items.filter((item) => !!item && typeof item === 'object') as ApiRow[]) : []\n}\n\nexport function toTimeEntryRecord(row: ApiRow): TimeEntryRecord | null {\n const id = readRowString(row, 'id')\n if (!id) return null\n const tags = Array.isArray(row.tags) ? row.tags : []\n return {\n id,\n date: dateFromApiValue(readRowString(row, 'date')),\n startText: clockFromApiValue(readRowString(row, 'started_at', 'startedAt')),\n endText: clockFromApiValue(readRowString(row, 'ended_at', 'endedAt')),\n durationMinutes: readRowNumber(row, 'duration_minutes', 'durationMinutes'),\n description: readRowString(row, 'description', 'notes') ?? '',\n taskId: readRowString(row, 'task_id', 'taskId'),\n timeProjectId: readRowString(row, 'time_project_id', 'timeProjectId'),\n isBillable: readRowBoolean(row, true, 'is_billable', 'isBillable'),\n rateOverrideAmount: readRowNumber(row, 'rate_override_amount', 'rateOverrideAmount'),\n currencyCode: readRowString(row, 'currencyCode', 'rate_currency_code', 'rateCurrencyCode'),\n tagIds: tags\n .map((tag) => (tag && typeof tag === 'object' ? readRowString(tag as ApiRow, 'id') : null))\n .filter((value): value is string => value !== null),\n isLocked: readRowBoolean(row, false, 'isLocked', 'is_locked'),\n lockedReportId: readRowString(row, 'lockedReportId', 'locked_report_id'),\n updatedAt: readRowString(row, 'updated_at', 'updatedAt'),\n }\n}\n\nexport type TaskOption = {\n id: string\n title: string\n /** `<project code>-<n>` \u2014 frozen at creation, quoted in reports. */\n reference: string | null\n timeProjectId: string | null\n /** Drives the picker's status dot. */\n taskStatusId: string | null\n /** Inclusive rollup from the tasks enricher; shown as context, never edited. */\n loggedMinutes: number | null\n}\n\nexport type TagOption = {\n id: string\n label: string\n /** A `PROJECT_COLOR_KEYS` key, resolved to a hex at render time. */\n color: string | null\n}\n\nexport type ProjectOption = {\n id: string\n name: string\n customerName: string | null\n hourlyRate: number | null\n currencyCode: string | null\n billableByDefault: boolean | null\n}\n\nexport function toTaskOption(row: ApiRow): TaskOption | null {\n const id = readRowString(row, 'id')\n const title = readRowString(row, 'title')\n if (!id || !title) return null\n const logged = row.loggedMinutes ?? row.logged_minutes\n return {\n id,\n title,\n reference: readRowString(row, 'reference'),\n timeProjectId: readRowString(row, 'time_project_id', 'timeProjectId'),\n taskStatusId: readRowString(row, 'task_status_id', 'taskStatusId'),\n loggedMinutes: typeof logged === 'number' ? logged : null,\n }\n}\n\nexport function toProjectOption(row: ApiRow): ProjectOption | null {\n const id = readRowString(row, 'id')\n if (!id) return null\n const snapshot = row.customer_snapshot ?? row.customerSnapshot\n const customerName =\n snapshot && typeof snapshot === 'object' ? readRowString(snapshot as ApiRow, 'name', 'displayName') : null\n return {\n id,\n name: readRowString(row, 'name') ?? id,\n customerName,\n hourlyRate: readRowNumber(row, 'hourly_rate', 'hourlyRate'),\n currencyCode: readRowString(row, 'currency_code', 'currencyCode'),\n billableByDefault:\n typeof row.billable_by_default === 'boolean'\n ? row.billable_by_default\n : typeof row.billableByDefault === 'boolean'\n ? row.billableByDefault\n : null,\n }\n}\n\n/** `Migracja koszyka B2B \u2014 Nordvik \u2014 migracja B2B` (screen 8's task option). */\n/**\n * The reference leads, because it is what people actually say to each other about\n * a task (\"I put two hours on APOLLO-14\") and what a client-facing report quotes.\n * A list keyed on the title alone makes the picker unusable the moment two\n * projects both have a \"Consulting / workshops\".\n */\nexport function describeTaskOption(task: TaskOption, project: ProjectOption | null | undefined): string {\n const label = [task.title, project?.customerName ?? null, project?.name ?? null]\n .filter((part): part is string => !!part && part.trim().length > 0)\n .join(' \u2014 ')\n return task.reference ? `${task.reference} \u00B7 ${label}` : label\n}\n\nexport type OverlapEntry = {\n id: string\n startText: string\n endText: string\n durationMinutes: number\n taskTitle: string | null\n projectName: string | null\n description: string | null\n}\n\nexport function toOverlapEntry(row: ApiRow): OverlapEntry | null {\n const id = readRowString(row, 'id')\n if (!id) return null\n return {\n id,\n startText: clockFromApiValue(readRowString(row, 'started_at', 'startedAt')),\n endText: clockFromApiValue(readRowString(row, 'ended_at', 'endedAt')),\n durationMinutes: readRowNumber(row, 'duration_minutes', 'durationMinutes') ?? 0,\n taskTitle: readRowString(row, 'task_title', 'taskTitle'),\n projectName: readRowString(row, 'project_name', 'projectName'),\n description: readRowString(row, 'notes', 'description'),\n }\n}\n"],
5
+ "mappings": "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",
6
+ "names": ["raw"]
7
+ }