@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,168 @@
1
+ import { NextResponse } from "next/server";
2
+ import { z } from "zod";
3
+ import { createRequestContainer } from "@open-mercato/shared/lib/di/container";
4
+ import { getAuthFromRequest } from "@open-mercato/shared/lib/auth/server";
5
+ import { resolveOrganizationScopeForRequest } from "@open-mercato/core/modules/directory/utils/organizationScope";
6
+ import { CrudHttpError } from "@open-mercato/shared/lib/crud/errors";
7
+ import { resolveTranslations } from "@open-mercato/shared/lib/i18n/server";
8
+ import { createLogger } from "@open-mercato/shared/lib/logger";
9
+ import { readTimeTrackingSettings } from "../../../../lib/time-tracking/settings.js";
10
+ import { projectRoundingImpact } from "../../../../lib/time-tracking/roundingImpact.js";
11
+ import { buildSqlInClause } from "../../../../lib/time-tracking/sqlInClause.js";
12
+ const logger = createLogger("staff").child({ component: "api/timesheets/settings/rounding-impact" });
13
+ const MANAGE_FEATURE = "staff.timesheets.settings.manage";
14
+ const DEFAULT_IMPACT_WINDOW_DAYS = 90;
15
+ const MAX_IMPACT_WINDOW_DAYS = 365;
16
+ const metadata = {
17
+ GET: { requireAuth: true, requireFeatures: [MANAGE_FEATURE] }
18
+ };
19
+ const querySchema = z.object({
20
+ unitMinutes: z.coerce.number().optional(),
21
+ direction: z.enum(["up", "nearest"]).optional(),
22
+ windowDays: z.coerce.number().int().min(1).max(MAX_IMPACT_WINDOW_DAYS).optional()
23
+ });
24
+ const impactSchema = z.object({
25
+ entryCount: z.number().int(),
26
+ rawMinutes: z.number().int(),
27
+ roundedMinutes: z.number().int(),
28
+ deltaMinutes: z.number().int()
29
+ });
30
+ const responseSchema = z.object({
31
+ windowDays: z.number().int(),
32
+ from: z.string(),
33
+ to: z.string(),
34
+ rounding: z.object({
35
+ unitMinutes: z.union([z.literal(0), z.literal(5), z.literal(10), z.literal(15)]),
36
+ direction: z.enum(["up", "nearest"])
37
+ }),
38
+ /** The projection under the requested (or stored) rule, locked entries excluded. */
39
+ projected: impactSchema,
40
+ /** The same window under the rule currently stored, so the screen can show the move. */
41
+ current: impactSchema,
42
+ /** Entries in the window that a rounding change can never restate. */
43
+ lockedEntryCount: z.number().int()
44
+ });
45
+ const ROUNDING_UNITS = [0, 5, 10, 15];
46
+ function toDateOnlyString(value) {
47
+ return value.toISOString().slice(0, 10);
48
+ }
49
+ async function GET(req) {
50
+ try {
51
+ const container = await createRequestContainer();
52
+ const auth = await getAuthFromRequest(req);
53
+ const { translate } = await resolveTranslations();
54
+ if (!auth) {
55
+ throw new CrudHttpError(401, { error: translate("staff.errors.unauthorized", "Unauthorized") });
56
+ }
57
+ const scope = await resolveOrganizationScopeForRequest({ container, auth, request: req });
58
+ const tenantId = scope?.tenantId ?? auth.tenantId ?? null;
59
+ if (!tenantId) {
60
+ throw new CrudHttpError(400, {
61
+ error: translate("staff.errors.missingScope", "Missing tenant or organization scope.")
62
+ });
63
+ }
64
+ const url = new URL(req.url);
65
+ const parsedQuery = querySchema.safeParse(Object.fromEntries(url.searchParams.entries()));
66
+ if (!parsedQuery.success) {
67
+ throw new CrudHttpError(400, {
68
+ error: translate("staff.errors.invalid_request", "Invalid request"),
69
+ details: parsedQuery.error.issues
70
+ });
71
+ }
72
+ const configService = container.resolve("moduleConfigService");
73
+ const stored = await readTimeTrackingSettings(configService, { tenantId });
74
+ const requestedUnit = parsedQuery.data.unitMinutes;
75
+ const candidate = {
76
+ unitMinutes: typeof requestedUnit === "number" && ROUNDING_UNITS.includes(requestedUnit) ? requestedUnit : stored.rounding.unitMinutes,
77
+ direction: parsedQuery.data.direction ?? stored.rounding.direction
78
+ };
79
+ const windowDays = parsedQuery.data.windowDays ?? DEFAULT_IMPACT_WINDOW_DAYS;
80
+ const to = /* @__PURE__ */ new Date();
81
+ const from = new Date(to.getTime() - (windowDays - 1) * 24 * 60 * 60 * 1e3);
82
+ const fromDate = toDateOnlyString(from);
83
+ const toDate = toDateOnlyString(to);
84
+ const organizationIds = scope?.filterIds ?? null;
85
+ const orgIn = organizationIds ? buildSqlInClause("organization_id", organizationIds) : null;
86
+ const orgClause = orgIn ? ` AND ${orgIn.sql}` : "";
87
+ const orgParams = orgIn ? orgIn.params : [];
88
+ const em = container.resolve("em");
89
+ const connection = em.getConnection();
90
+ const bucketRows = await connection.execute(
91
+ `
92
+ SELECT duration_minutes, COUNT(*)::bigint AS entry_count
93
+ FROM staff_time_entries
94
+ WHERE tenant_id = ?
95
+ AND deleted_at IS NULL
96
+ AND locked_report_id IS NULL
97
+ AND date >= ?::date
98
+ AND date <= ?::date${orgClause}
99
+ GROUP BY duration_minutes
100
+ `,
101
+ [tenantId, fromDate, toDate, ...orgParams]
102
+ );
103
+ const lockedRows = await connection.execute(
104
+ `
105
+ SELECT COUNT(*)::bigint AS locked_count
106
+ FROM staff_time_entries
107
+ WHERE tenant_id = ?
108
+ AND deleted_at IS NULL
109
+ AND locked_report_id IS NOT NULL
110
+ AND date >= ?::date
111
+ AND date <= ?::date${orgClause}
112
+ `,
113
+ [tenantId, fromDate, toDate, ...orgParams]
114
+ );
115
+ const buckets = (bucketRows ?? []).map((row) => ({
116
+ durationMinutes: Number(row.duration_minutes ?? 0),
117
+ entryCount: Number(row.entry_count ?? 0)
118
+ }));
119
+ const lockedRaw = Number(lockedRows?.[0]?.locked_count ?? 0);
120
+ return NextResponse.json(
121
+ {
122
+ windowDays,
123
+ from: fromDate,
124
+ to: toDate,
125
+ rounding: candidate,
126
+ projected: projectRoundingImpact(buckets, candidate),
127
+ current: projectRoundingImpact(buckets, stored.rounding),
128
+ lockedEntryCount: Number.isFinite(lockedRaw) ? lockedRaw : 0
129
+ },
130
+ { status: 200 }
131
+ );
132
+ } catch (err) {
133
+ if (err instanceof CrudHttpError) {
134
+ return NextResponse.json(err.body, { status: err.status });
135
+ }
136
+ logger.error("staff.timesheets.settings.rounding-impact failed", { err });
137
+ const { translate } = await resolveTranslations();
138
+ return NextResponse.json(
139
+ { error: translate("staff.errors.internal", "Internal server error") },
140
+ { status: 500 }
141
+ );
142
+ }
143
+ }
144
+ const openApi = {
145
+ tag: "Staff",
146
+ summary: "Rounding impact preview",
147
+ methods: {
148
+ GET: {
149
+ summary: "Project the billing impact of a rounding rule over a recent window",
150
+ description: "Answers what the last `windowDays` (default 90) would have billed under a candidate rounding rule, before it is saved. Entries already locked into a closed report are excluded from the projection \u2014 they cannot be restated \u2014 and reported separately as `lockedEntryCount`. Rounding is applied per entry (D-7) through the same helper the write path uses.",
151
+ query: querySchema,
152
+ responses: [
153
+ { status: 200, description: "Rounding impact projection", schema: responseSchema },
154
+ { status: 400, description: "Invalid request or missing scope", schema: z.object({ error: z.string() }) },
155
+ { status: 401, description: "Unauthorized", schema: z.object({ error: z.string() }) },
156
+ { status: 403, description: "Missing staff.timesheets.settings.manage" },
157
+ { status: 500, description: "Projection failure", schema: z.object({ error: z.string() }) }
158
+ ]
159
+ }
160
+ }
161
+ };
162
+ export {
163
+ DEFAULT_IMPACT_WINDOW_DAYS,
164
+ GET,
165
+ metadata,
166
+ openApi
167
+ };
168
+ //# sourceMappingURL=route.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../../../src/modules/staff/api/timesheets/settings/rounding-impact/route.ts"],
4
+ "sourcesContent": ["/**\n * `GET /api/staff/timesheets/settings/rounding-impact` \u2014 screen 16's 90-day preview (T7.2).\n *\n * Rounding is the one setting on that screen that moves money, and its effect is\n * invisible until an invoice goes out. This route answers \"what would the last 90\n * days have billed under the rule I am about to save\" before the Save button is\n * pressed, so changing it stops being a blind shot at client amounts.\n *\n * Three deliberate properties:\n *\n * * **Locked entries are excluded.** An entry frozen into a closed report cannot\n * be restated, so counting it in the projection would promise a change that the\n * retro-rounding job would then refuse to make. They are counted separately and\n * returned as `lockedEntryCount`, which is what lets the screen say \"closed\n * reports stay as they are\" from data rather than as boilerplate.\n * * **The projection is per entry** (D-7) \u2014 the database groups identical\n * durations and `projectRoundingImpact` rounds each distinct duration once,\n * through the same `roundMinutes` the write path uses.\n * * **The candidate rule comes from the query string**, defaulting to what is\n * stored, so the card recomputes as the toggles move without saving anything.\n */\n\nimport { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { resolveOrganizationScopeForRequest } from '@open-mercato/core/modules/directory/utils/organizationScope'\nimport { CrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport type { ModuleConfigService } from '@open-mercato/core/modules/configs/lib/module-config-service'\nimport { readTimeTrackingSettings } from '../../../../lib/time-tracking/settings'\nimport type { RoundingDirection, RoundingSettings, RoundingUnitMinutes } from '../../../../lib/time-tracking/rounding'\nimport { projectRoundingImpact, type DurationBucket } from '../../../../lib/time-tracking/roundingImpact'\nimport { buildSqlInClause } from '../../../../lib/time-tracking/sqlInClause'\n\nconst logger = createLogger('staff').child({ component: 'api/timesheets/settings/rounding-impact' })\n\nconst MANAGE_FEATURE = 'staff.timesheets.settings.manage'\n\nexport const DEFAULT_IMPACT_WINDOW_DAYS = 90\nconst MAX_IMPACT_WINDOW_DAYS = 365\n\nexport const metadata = {\n GET: { requireAuth: true, requireFeatures: [MANAGE_FEATURE] },\n}\n\nconst querySchema = z.object({\n unitMinutes: z.coerce.number().optional(),\n direction: z.enum(['up', 'nearest']).optional(),\n windowDays: z.coerce.number().int().min(1).max(MAX_IMPACT_WINDOW_DAYS).optional(),\n})\n\nconst impactSchema = z.object({\n entryCount: z.number().int(),\n rawMinutes: z.number().int(),\n roundedMinutes: z.number().int(),\n deltaMinutes: z.number().int(),\n})\n\nconst responseSchema = z.object({\n windowDays: z.number().int(),\n from: z.string(),\n to: z.string(),\n rounding: z.object({\n unitMinutes: z.union([z.literal(0), z.literal(5), z.literal(10), z.literal(15)]),\n direction: z.enum(['up', 'nearest']),\n }),\n /** The projection under the requested (or stored) rule, locked entries excluded. */\n projected: impactSchema,\n /** The same window under the rule currently stored, so the screen can show the move. */\n current: impactSchema,\n /** Entries in the window that a rounding change can never restate. */\n lockedEntryCount: z.number().int(),\n})\n\nconst ROUNDING_UNITS: readonly number[] = [0, 5, 10, 15]\n\ntype BucketRow = {\n duration_minutes: string | number | null\n entry_count: string | number | null\n}\n\ntype LockedRow = {\n locked_count: string | number | null\n}\n\nfunction toDateOnlyString(value: Date): string {\n return value.toISOString().slice(0, 10)\n}\n\nexport async function GET(req: Request) {\n try {\n const container = await createRequestContainer()\n const auth = await getAuthFromRequest(req)\n const { translate } = await resolveTranslations()\n if (!auth) {\n throw new CrudHttpError(401, { error: translate('staff.errors.unauthorized', 'Unauthorized') })\n }\n\n const scope = await resolveOrganizationScopeForRequest({ container, auth, request: req })\n const tenantId = scope?.tenantId ?? auth.tenantId ?? null\n if (!tenantId) {\n throw new CrudHttpError(400, {\n error: translate('staff.errors.missingScope', 'Missing tenant or organization scope.'),\n })\n }\n\n const url = new URL(req.url)\n const parsedQuery = querySchema.safeParse(Object.fromEntries(url.searchParams.entries()))\n if (!parsedQuery.success) {\n throw new CrudHttpError(400, {\n error: translate('staff.errors.invalid_request', 'Invalid request'),\n details: parsedQuery.error.issues,\n })\n }\n\n const configService = container.resolve('moduleConfigService') as ModuleConfigService\n const stored = await readTimeTrackingSettings(configService, { tenantId })\n\n const requestedUnit = parsedQuery.data.unitMinutes\n const candidate: RoundingSettings = {\n unitMinutes:\n typeof requestedUnit === 'number' && ROUNDING_UNITS.includes(requestedUnit)\n ? (requestedUnit as RoundingUnitMinutes)\n : stored.rounding.unitMinutes,\n direction: (parsedQuery.data.direction ?? stored.rounding.direction) as RoundingDirection,\n }\n\n const windowDays = parsedQuery.data.windowDays ?? DEFAULT_IMPACT_WINDOW_DAYS\n const to = new Date()\n const from = new Date(to.getTime() - (windowDays - 1) * 24 * 60 * 60 * 1000)\n const fromDate = toDateOnlyString(from)\n const toDate = toDateOnlyString(to)\n\n // Organizations narrow the same way every other read in the module does; a null\n // filter list means \"every organization of this tenant\", which is what a\n // tenant-global setting is asking about.\n const organizationIds = scope?.filterIds ?? null\n const orgIn = organizationIds ? buildSqlInClause('organization_id', organizationIds) : null\n const orgClause = orgIn ? ` AND ${orgIn.sql}` : ''\n const orgParams = orgIn ? orgIn.params : []\n\n const em = container.resolve('em') as EntityManager\n const connection = em.getConnection()\n\n const bucketRows = (await connection.execute(\n `\n SELECT duration_minutes, COUNT(*)::bigint AS entry_count\n FROM staff_time_entries\n WHERE tenant_id = ?\n AND deleted_at IS NULL\n AND locked_report_id IS NULL\n AND date >= ?::date\n AND date <= ?::date${orgClause}\n GROUP BY duration_minutes\n `,\n [tenantId, fromDate, toDate, ...orgParams],\n )) as BucketRow[]\n\n const lockedRows = (await connection.execute(\n `\n SELECT COUNT(*)::bigint AS locked_count\n FROM staff_time_entries\n WHERE tenant_id = ?\n AND deleted_at IS NULL\n AND locked_report_id IS NOT NULL\n AND date >= ?::date\n AND date <= ?::date${orgClause}\n `,\n [tenantId, fromDate, toDate, ...orgParams],\n )) as LockedRow[]\n\n const buckets: DurationBucket[] = (bucketRows ?? []).map((row) => ({\n durationMinutes: Number(row.duration_minutes ?? 0),\n entryCount: Number(row.entry_count ?? 0),\n }))\n\n const lockedRaw = Number(lockedRows?.[0]?.locked_count ?? 0)\n\n return NextResponse.json(\n {\n windowDays,\n from: fromDate,\n to: toDate,\n rounding: candidate,\n projected: projectRoundingImpact(buckets, candidate),\n current: projectRoundingImpact(buckets, stored.rounding),\n lockedEntryCount: Number.isFinite(lockedRaw) ? lockedRaw : 0,\n },\n { status: 200 },\n )\n } catch (err) {\n if (err instanceof CrudHttpError) {\n return NextResponse.json(err.body, { status: err.status })\n }\n logger.error('staff.timesheets.settings.rounding-impact failed', { err })\n const { translate } = await resolveTranslations()\n return NextResponse.json(\n { error: translate('staff.errors.internal', 'Internal server error') },\n { status: 500 },\n )\n }\n}\n\nexport const openApi: OpenApiRouteDoc = {\n tag: 'Staff',\n summary: 'Rounding impact preview',\n methods: {\n GET: {\n summary: 'Project the billing impact of a rounding rule over a recent window',\n description:\n 'Answers what the last `windowDays` (default 90) would have billed under a candidate rounding rule, before it is saved. Entries already locked into a closed report are excluded from the projection \u2014 they cannot be restated \u2014 and reported separately as `lockedEntryCount`. Rounding is applied per entry (D-7) through the same helper the write path uses.',\n query: querySchema,\n responses: [\n { status: 200, description: 'Rounding impact projection', schema: responseSchema },\n { status: 400, description: 'Invalid request or missing scope', schema: z.object({ error: z.string() }) },\n { status: 401, description: 'Unauthorized', schema: z.object({ error: z.string() }) },\n { status: 403, description: 'Missing staff.timesheets.settings.manage' },\n { status: 500, description: 'Projection failure', schema: z.object({ error: z.string() }) },\n ],\n },\n },\n}\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
@@ -0,0 +1,199 @@
1
+ import { NextResponse } from "next/server";
2
+ import { resolveFeatureAccess } from "../../../lib/time-tracking/featureAccess.js";
3
+ import { z } from "zod";
4
+ import { createRequestContainer } from "@open-mercato/shared/lib/di/container";
5
+ import { getAuthFromRequest } from "@open-mercato/shared/lib/auth/server";
6
+ import { CrudHttpError, forbidden, isCrudHttpError } from "@open-mercato/shared/lib/crud/errors";
7
+ import { runRouteMutationGuards } from "@open-mercato/shared/lib/crud/route-mutation-guard";
8
+ import { resolveTranslations } from "@open-mercato/shared/lib/i18n/server";
9
+ import { createLogger } from "@open-mercato/shared/lib/logger";
10
+ import { emitStaffEvent } from "../../../events.js";
11
+ import { buildTimeTrackingSettingsSchema } from "../../../lib/time-tracking/settingKeys.js";
12
+ import { readJsonSafe } from "@open-mercato/shared/lib/http/readJsonSafe";
13
+ import { STAFF_TIME_TRACKING_RESOURCE_KINDS } from "../../guards.js";
14
+ import {
15
+ readSearchParamsRecord,
16
+ runTimesheetInterceptors
17
+ } from "../_shared/withTimesheetInterceptors.js";
18
+ import {
19
+ readTimeTrackingSettings,
20
+ writeTimeTrackingSettings
21
+ } from "../../../lib/time-tracking/settings.js";
22
+ const logger = createLogger("staff").child({ component: "api/timesheets/settings" });
23
+ const VIEW_FEATURE = "staff.timesheets.view";
24
+ const MANAGE_FEATURE = "staff.timesheets.settings.manage";
25
+ const RESOURCE_KIND = STAFF_TIME_TRACKING_RESOURCE_KINDS.settings;
26
+ const RESOURCE_ID = "time_tracking";
27
+ const metadata = {
28
+ GET: { requireAuth: true, requireFeatures: [VIEW_FEATURE] },
29
+ PUT: { requireAuth: true, requireFeatures: [MANAGE_FEATURE] }
30
+ };
31
+ function settingsResponseSchema() {
32
+ return buildTimeTrackingSettingsSchema();
33
+ }
34
+ async function resolveSettingsContext(req) {
35
+ const container = await createRequestContainer();
36
+ const auth = await getAuthFromRequest(req);
37
+ const { translate } = await resolveTranslations();
38
+ if (!auth || !auth.tenantId) {
39
+ throw new CrudHttpError(401, { error: translate("staff.errors.unauthorized", "Unauthorized") });
40
+ }
41
+ const actorId = typeof auth.sub === "string" && auth.sub.trim().length > 0 && auth.sub || typeof auth.userId === "string" && auth.userId.trim().length > 0 && auth.userId || typeof auth.keyId === "string" && auth.keyId.trim().length > 0 && auth.keyId || "system";
42
+ return {
43
+ container,
44
+ tenantId: auth.tenantId,
45
+ organizationId: auth.orgId ?? null,
46
+ actorId,
47
+ translate
48
+ };
49
+ }
50
+ function resolveConfigService(context) {
51
+ return context.container.resolve("moduleConfigService");
52
+ }
53
+ async function GET(req) {
54
+ try {
55
+ const context = await resolveSettingsContext(req);
56
+ const interceptors = await runTimesheetInterceptors({
57
+ request: req,
58
+ method: "GET",
59
+ scope: {
60
+ container: context.container,
61
+ userId: context.actorId,
62
+ tenantId: context.tenantId,
63
+ organizationId: context.organizationId,
64
+ tenantGlobal: true
65
+ },
66
+ query: readSearchParamsRecord(req.url)
67
+ });
68
+ if (!interceptors.ok) return interceptors.response;
69
+ const settings = await readTimeTrackingSettings(resolveConfigService(context), {
70
+ tenantId: context.tenantId
71
+ });
72
+ return interceptors.session.respond(200, settings);
73
+ } catch (err) {
74
+ return errorResponse(err, "staff.timesheets.settings.GET failed");
75
+ }
76
+ }
77
+ async function PUT(req) {
78
+ try {
79
+ const context = await resolveSettingsContext(req);
80
+ const access = await resolveFeatureAccess(context.container, context.actorId || null, [MANAGE_FEATURE], {
81
+ tenantId: context.tenantId,
82
+ organizationId: context.organizationId
83
+ });
84
+ if (!access.allowed) {
85
+ throw forbidden(context.translate("staff.errors.forbidden", "Forbidden"));
86
+ }
87
+ const grantedFeatures = access.grantedFeatures;
88
+ const interceptors = await runTimesheetInterceptors({
89
+ request: req,
90
+ method: "PUT",
91
+ scope: {
92
+ container: context.container,
93
+ userId: context.actorId,
94
+ tenantId: context.tenantId,
95
+ organizationId: context.organizationId,
96
+ userFeatures: grantedFeatures,
97
+ tenantGlobal: true
98
+ },
99
+ body: await readJsonSafe(req, {})
100
+ });
101
+ if (!interceptors.ok) return interceptors.response;
102
+ const { session } = interceptors;
103
+ const settingsSchema = buildTimeTrackingSettingsSchema();
104
+ const parsed = settingsSchema.parse(session.body);
105
+ const guardResult = await runRouteMutationGuards({
106
+ container: context.container,
107
+ req,
108
+ auth: {
109
+ userId: context.actorId,
110
+ tenantId: context.tenantId,
111
+ organizationId: context.organizationId,
112
+ userFeatures: grantedFeatures
113
+ },
114
+ input: {
115
+ resourceKind: RESOURCE_KIND,
116
+ resourceId: RESOURCE_ID,
117
+ operation: "update",
118
+ mutationPayload: parsed
119
+ }
120
+ });
121
+ if (!guardResult.ok) return guardResult.response;
122
+ const effective = guardResult.modifiedPayload ? settingsSchema.parse({ ...parsed, ...guardResult.modifiedPayload }) : parsed;
123
+ const settings = await writeTimeTrackingSettings(
124
+ resolveConfigService(context),
125
+ { tenantId: context.tenantId },
126
+ effective
127
+ );
128
+ await guardResult.runAfterSuccess();
129
+ void emitStaffEvent("staff.timesheets.time_tracking.settings_updated", {
130
+ tenantId: context.tenantId,
131
+ organizationId: context.organizationId,
132
+ settings
133
+ }, { persistent: true }).catch((err) => {
134
+ logger.error("staff.timesheets emit time_tracking.settings_updated failed", { err });
135
+ });
136
+ return session.respond(200, settings);
137
+ } catch (err) {
138
+ return errorResponse(err, "staff.timesheets.settings.PUT failed");
139
+ }
140
+ }
141
+ async function errorResponse(err, message) {
142
+ if (isCrudHttpError(err)) {
143
+ return NextResponse.json(err.body, { status: err.status });
144
+ }
145
+ const { translate } = await resolveTranslations();
146
+ if (err instanceof z.ZodError) {
147
+ return NextResponse.json(
148
+ { error: translate("staff.errors.invalid_request", "Invalid request"), details: err.issues },
149
+ { status: 400 }
150
+ );
151
+ }
152
+ logger.error(message, { err });
153
+ return NextResponse.json(
154
+ { error: translate("staff.errors.internal", "Internal server error") },
155
+ { status: 500 }
156
+ );
157
+ }
158
+ const getDoc = {
159
+ summary: "Read time tracking settings",
160
+ tags: ["Staff"],
161
+ get responses() {
162
+ return [
163
+ { status: 200, description: "Tenant time tracking settings", schema: settingsResponseSchema() },
164
+ { status: 401, description: "Unauthorized", schema: z.object({ error: z.string() }) }
165
+ ];
166
+ }
167
+ };
168
+ const putDoc = {
169
+ summary: "Update time tracking settings",
170
+ tags: ["Staff"],
171
+ get requestBody() {
172
+ return { schema: buildTimeTrackingSettingsSchema() };
173
+ },
174
+ get responses() {
175
+ return [
176
+ { status: 200, description: "Updated time tracking settings", schema: settingsResponseSchema() }
177
+ ];
178
+ },
179
+ errors: [
180
+ { status: 400, description: "Invalid request body" },
181
+ { status: 401, description: "Unauthorized" },
182
+ { status: 403, description: "Missing staff.timesheets.settings.manage" }
183
+ ]
184
+ };
185
+ const openApi = {
186
+ tag: "Staff",
187
+ summary: "Time tracking module settings",
188
+ methods: {
189
+ GET: getDoc,
190
+ PUT: putDoc
191
+ }
192
+ };
193
+ export {
194
+ GET,
195
+ PUT,
196
+ metadata,
197
+ openApi
198
+ };
199
+ //# sourceMappingURL=route.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../../src/modules/staff/api/timesheets/settings/route.ts"],
4
+ "sourcesContent": ["import { NextResponse } from 'next/server'\nimport { resolveFeatureAccess } from '../../../lib/time-tracking/featureAccess'\nimport { z } from 'zod'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { CrudHttpError, forbidden, isCrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport { runRouteMutationGuards } from '@open-mercato/shared/lib/crud/route-mutation-guard'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\nimport { emitStaffEvent } from '../../../events'\nimport type { OpenApiMethodDoc, OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport type { ModuleConfigService } from '@open-mercato/core/modules/configs/lib/module-config-service'\nimport { buildTimeTrackingSettingsSchema } from '../../../lib/time-tracking/settingKeys'\nimport { readJsonSafe } from '@open-mercato/shared/lib/http/readJsonSafe'\nimport { STAFF_TIME_TRACKING_RESOURCE_KINDS } from '../../guards'\nimport {\n readSearchParamsRecord,\n runTimesheetInterceptors,\n} from '../_shared/withTimesheetInterceptors'\nimport {\n readTimeTrackingSettings,\n writeTimeTrackingSettings,\n type TimeTrackingSettings,\n} from '../../../lib/time-tracking/settings'\n\nconst logger = createLogger('staff').child({ component: 'api/timesheets/settings' })\n\nconst VIEW_FEATURE = 'staff.timesheets.view'\nconst MANAGE_FEATURE = 'staff.timesheets.settings.manage'\nconst RESOURCE_KIND = STAFF_TIME_TRACKING_RESOURCE_KINDS.settings\nconst RESOURCE_ID = 'time_tracking'\n\nexport const metadata = {\n GET: { requireAuth: true, requireFeatures: [VIEW_FEATURE] },\n PUT: { requireAuth: true, requireFeatures: [MANAGE_FEATURE] },\n}\n\n/**\n * EP-42 \u2014 derived from the setting-key registry, so a contributed key is published in\n * the OpenAPI response shape instead of being invisible to it. Rebuilt per read for the\n * same reason the request schema is: a key may be registered after this module loads.\n */\nfunction settingsResponseSchema(): z.ZodTypeAny {\n return buildTimeTrackingSettingsSchema()\n}\n\ntype SettingsContext = {\n container: Awaited<ReturnType<typeof createRequestContainer>>\n tenantId: string\n organizationId: string | null\n actorId: string\n translate: (key: string, fallback?: string) => string\n}\n\nasync function resolveSettingsContext(req: Request): Promise<SettingsContext> {\n const container = await createRequestContainer()\n const auth = await getAuthFromRequest(req)\n const { translate } = await resolveTranslations()\n if (!auth || !auth.tenantId) {\n throw new CrudHttpError(401, { error: translate('staff.errors.unauthorized', 'Unauthorized') })\n }\n const actorId =\n (typeof auth.sub === 'string' && auth.sub.trim().length > 0 && auth.sub) ||\n (typeof auth.userId === 'string' && auth.userId.trim().length > 0 && auth.userId) ||\n (typeof auth.keyId === 'string' && auth.keyId.trim().length > 0 && auth.keyId) ||\n 'system'\n return {\n container,\n tenantId: auth.tenantId,\n organizationId: auth.orgId ?? null,\n actorId,\n translate,\n }\n}\n\nfunction resolveConfigService(context: SettingsContext): ModuleConfigService {\n return context.container.resolve('moduleConfigService') as ModuleConfigService\n}\n\nasync function GET(req: Request) {\n try {\n const context = await resolveSettingsContext(req)\n const interceptors = await runTimesheetInterceptors({\n request: req,\n method: 'GET',\n scope: {\n container: context.container,\n userId: context.actorId,\n tenantId: context.tenantId,\n organizationId: context.organizationId,\n tenantGlobal: true,\n },\n query: readSearchParamsRecord(req.url),\n })\n if (!interceptors.ok) return interceptors.response\n\n const settings = await readTimeTrackingSettings(resolveConfigService(context), {\n tenantId: context.tenantId,\n })\n return interceptors.session.respond(200, settings)\n } catch (err) {\n return errorResponse(err, 'staff.timesheets.settings.GET failed')\n }\n}\n\nasync function PUT(req: Request) {\n try {\n const context = await resolveSettingsContext(req)\n // One lookup through the module's single RBAC authority, answering both\n // questions this route asks: may the caller manage settings, and what grant\n // list do the interceptors and the mutation-guard registry gate on. The\n // decision is checked unconditionally and fails closed; the list is plumbing\n // and is empty \u2014 never absent \u2014 when RBAC could not produce it.\n const access = await resolveFeatureAccess(context.container, context.actorId || null, [MANAGE_FEATURE], {\n tenantId: context.tenantId,\n organizationId: context.organizationId,\n })\n if (!access.allowed) {\n throw forbidden(context.translate('staff.errors.forbidden', 'Forbidden'))\n }\n const grantedFeatures = access.grantedFeatures\n\n const interceptors = await runTimesheetInterceptors({\n request: req,\n method: 'PUT',\n scope: {\n container: context.container,\n userId: context.actorId,\n tenantId: context.tenantId,\n organizationId: context.organizationId,\n userFeatures: grantedFeatures,\n tenantGlobal: true,\n },\n body: await readJsonSafe<Record<string, unknown>>(req, {}),\n })\n if (!interceptors.ok) return interceptors.response\n const { session } = interceptors\n\n const settingsSchema = buildTimeTrackingSettingsSchema()\n const parsed = settingsSchema.parse(session.body)\n\n const guardResult = await runRouteMutationGuards({\n container: context.container,\n req,\n auth: {\n userId: context.actorId,\n tenantId: context.tenantId,\n organizationId: context.organizationId,\n userFeatures: grantedFeatures,\n },\n input: {\n resourceKind: RESOURCE_KIND,\n resourceId: RESOURCE_ID,\n operation: 'update',\n mutationPayload: parsed,\n },\n })\n if (!guardResult.ok) return guardResult.response\n\n const effective = guardResult.modifiedPayload\n ? settingsSchema.parse({ ...parsed, ...guardResult.modifiedPayload })\n : parsed\n\n const settings: TimeTrackingSettings = await writeTimeTrackingSettings(\n resolveConfigService(context),\n { tenantId: context.tenantId },\n effective,\n )\n\n await guardResult.runAfterSuccess()\n\n void emitStaffEvent('staff.timesheets.time_tracking.settings_updated', {\n tenantId: context.tenantId,\n organizationId: context.organizationId,\n settings,\n }, { persistent: true }).catch((err) => {\n logger.error('staff.timesheets emit time_tracking.settings_updated failed', { err })\n })\n\n return session.respond(200, settings)\n } catch (err) {\n return errorResponse(err, 'staff.timesheets.settings.PUT failed')\n }\n}\n\nasync function errorResponse(err: unknown, message: string) {\n if (isCrudHttpError(err)) {\n return NextResponse.json(err.body, { status: err.status })\n }\n const { translate } = await resolveTranslations()\n if (err instanceof z.ZodError) {\n return NextResponse.json(\n { error: translate('staff.errors.invalid_request', 'Invalid request'), details: err.issues },\n { status: 400 },\n )\n }\n logger.error(message, { err })\n return NextResponse.json(\n { error: translate('staff.errors.internal', 'Internal server error') },\n { status: 500 },\n )\n}\n\nconst getDoc: OpenApiMethodDoc = {\n summary: 'Read time tracking settings',\n tags: ['Staff'],\n get responses() {\n return [\n { status: 200, description: 'Tenant time tracking settings', schema: settingsResponseSchema() },\n { status: 401, description: 'Unauthorized', schema: z.object({ error: z.string() }) },\n ]\n },\n}\n\nconst putDoc: OpenApiMethodDoc = {\n summary: 'Update time tracking settings',\n tags: ['Staff'],\n get requestBody() {\n return { schema: buildTimeTrackingSettingsSchema() }\n },\n get responses() {\n return [\n { status: 200, description: 'Updated time tracking settings', schema: settingsResponseSchema() },\n ]\n },\n errors: [\n { status: 400, description: 'Invalid request body' },\n { status: 401, description: 'Unauthorized' },\n { status: 403, description: 'Missing staff.timesheets.settings.manage' },\n ],\n}\n\nexport const openApi: OpenApiRouteDoc = {\n tag: 'Staff',\n summary: 'Time tracking module settings',\n methods: {\n GET: getDoc,\n PUT: putDoc,\n },\n}\n\nexport { GET, PUT }\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
@@ -0,0 +1,76 @@
1
+ import { CrudHttpError } from "@open-mercato/shared/lib/crud/errors";
2
+ import { createLogger } from "@open-mercato/shared/lib/logger";
3
+ import { StaffTimeEntry, StaffTimeTask } from "../../../data/entities.js";
4
+ import { MANAGE_PROJECTS_FEATURE, resolveProjectAccess } from "../../../lib/time-tracking/access.js";
5
+ import { resolveFeatureAccess } from "../../../lib/time-tracking/featureAccess.js";
6
+ import { readTimeTrackingSettings } from "../../../lib/time-tracking/settings.js";
7
+ const logger = createLogger("staff").child({ component: "api/timesheets/tags" });
8
+ const DENIED_ACCESS = { canManageAll: false, projectIds: [], staffMemberId: null };
9
+ async function resolveAssignmentGraceDays(container, tenantId) {
10
+ try {
11
+ const configService = container.resolve("moduleConfigService");
12
+ const settings = await readTimeTrackingSettings(configService, { tenantId });
13
+ return settings.access.assignmentGraceDays;
14
+ } catch {
15
+ return null;
16
+ }
17
+ }
18
+ async function loadTagProjectAccess(container, scope) {
19
+ try {
20
+ const access = await resolveFeatureAccess(container, scope.userId, [MANAGE_PROJECTS_FEATURE], {
21
+ tenantId: scope.tenantId,
22
+ organizationId: scope.organizationId
23
+ });
24
+ const em = container.resolve("em");
25
+ return await resolveProjectAccess({
26
+ em: em.fork(),
27
+ userId: scope.userId,
28
+ tenantId: scope.tenantId,
29
+ organizationId: scope.organizationId,
30
+ canManageAll: access.allowed,
31
+ userFeatures: access.grantedFeatures,
32
+ assignmentGraceDays: await resolveAssignmentGraceDays(container, scope.tenantId)
33
+ });
34
+ } catch (err) {
35
+ logger.error("staff.timesheets.tags access resolution failed", { err });
36
+ return { ...DENIED_ACCESS };
37
+ }
38
+ }
39
+ function targetNotFound(translate, target) {
40
+ const message = target === "task" ? translate("staff.time_tracking.tags.errors.taskNotFound", "Task not found or not accessible.") : translate("staff.time_tracking.tags.errors.entryNotFound", "Time entry not found or not accessible.");
41
+ return new CrudHttpError(404, { error: message });
42
+ }
43
+ async function requireAccessibleTask(em, taskId, scope, access, translate) {
44
+ const task = await em.findOne(StaffTimeTask, {
45
+ id: taskId,
46
+ tenantId: scope.tenantId,
47
+ organizationId: scope.organizationId,
48
+ deletedAt: null
49
+ });
50
+ if (!task) throw targetNotFound(translate, "task");
51
+ if (!access.canManageAll && !access.projectIds.includes(task.timeProjectId)) {
52
+ throw targetNotFound(translate, "task");
53
+ }
54
+ return { id: task.id, timeProjectId: task.timeProjectId };
55
+ }
56
+ async function requireAccessibleTimeEntry(em, timeEntryId, scope, access, translate) {
57
+ const entry = await em.findOne(StaffTimeEntry, {
58
+ id: timeEntryId,
59
+ tenantId: scope.tenantId,
60
+ organizationId: scope.organizationId,
61
+ deletedAt: null
62
+ });
63
+ if (!entry) throw targetNotFound(translate, "time_entry");
64
+ if (!access.canManageAll) {
65
+ const projectId = entry.timeProjectId ?? null;
66
+ const reachable = projectId ? access.projectIds.includes(projectId) : access.staffMemberId != null && entry.staffMemberId === access.staffMemberId;
67
+ if (!reachable) throw targetNotFound(translate, "time_entry");
68
+ }
69
+ return { id: entry.id, timeProjectId: entry.timeProjectId ?? null };
70
+ }
71
+ export {
72
+ loadTagProjectAccess,
73
+ requireAccessibleTask,
74
+ requireAccessibleTimeEntry
75
+ };
76
+ //# sourceMappingURL=access.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../../src/modules/staff/api/timesheets/tags/access.ts"],
4
+ "sourcesContent": ["/**\n * Shared access filtering for the two tag-assignment endpoints (T3.4).\n *\n * Tagging a task or a time entry is a write on a project-scoped row, so it goes\n * through the same resolver every other project-scoped read and write uses: a\n * caller without `staff.timesheets.projects.manage` only reaches the projects\n * they are an active member of. Skipping the resolver here would let anyone with\n * `tasks.manage` tag a task on a project they cannot even see \u2014 that is the R3\n * defect class, and it is why the target lookup below is never done without it.\n *\n * The refusal is a flat 404 with a generic message. \"Not yours\" and \"does not\n * exist\" are deliberately indistinguishable, so the endpoint cannot be used to\n * probe which task or entry ids exist.\n */\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { CrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\nimport type { ModuleConfigService } from '@open-mercato/core/modules/configs/lib/module-config-service'\nimport { StaffTimeEntry, StaffTimeTask } from '../../../data/entities'\nimport { MANAGE_PROJECTS_FEATURE, resolveProjectAccess, type ProjectAccess } from '../../../lib/time-tracking/access'\nimport { resolveFeatureAccess } from '../../../lib/time-tracking/featureAccess'\nimport { readTimeTrackingSettings } from '../../../lib/time-tracking/settings'\n\nconst logger = createLogger('staff').child({ component: 'api/timesheets/tags' })\n\nconst DENIED_ACCESS: ProjectAccess = { canManageAll: false, projectIds: [], staffMemberId: null }\n\nexport type ContainerLike = { resolve: (name: string) => unknown }\n\nexport type TagAssignmentScope = {\n tenantId: string\n organizationId: string\n userId: string\n}\n\nasync function resolveAssignmentGraceDays(container: ContainerLike, tenantId: string): Promise<number | null> {\n try {\n const configService = container.resolve('moduleConfigService') as ModuleConfigService\n const settings = await readTimeTrackingSettings(configService, { tenantId })\n return settings.access.assignmentGraceDays\n } catch {\n // Fail safe to the documented default rather than widening the window.\n return null\n }\n}\n\nexport async function loadTagProjectAccess(\n container: ContainerLike,\n scope: TagAssignmentScope,\n): Promise<ProjectAccess> {\n try {\n // One lookup, one authority, one failure direction. The hand-rolled grant read\n // this replaces could not tell \"denied\" from \"could not ask\", so an RBAC hiccup\n // silently demoted a manager to their own memberships.\n const access = await resolveFeatureAccess(container, scope.userId, [MANAGE_PROJECTS_FEATURE], {\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n })\n const em = container.resolve('em') as EntityManager\n return await resolveProjectAccess({\n em: em.fork(),\n userId: scope.userId,\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n canManageAll: access.allowed,\n userFeatures: access.grantedFeatures,\n assignmentGraceDays: await resolveAssignmentGraceDays(container, scope.tenantId),\n })\n } catch (err) {\n logger.error('staff.timesheets.tags access resolution failed', { err })\n return { ...DENIED_ACCESS }\n }\n}\n\nfunction targetNotFound(translate: (key: string, fallback: string) => string, target: 'task' | 'time_entry'): CrudHttpError {\n const message =\n target === 'task'\n ? translate('staff.time_tracking.tags.errors.taskNotFound', 'Task not found or not accessible.')\n : translate('staff.time_tracking.tags.errors.entryNotFound', 'Time entry not found or not accessible.')\n return new CrudHttpError(404, { error: message })\n}\n\n/**\n * Loads the task inside the caller's tenant/organization AND inside the projects\n * the resolver grants them. Both misses answer the same 404.\n */\nexport async function requireAccessibleTask(\n em: EntityManager,\n taskId: string,\n scope: TagAssignmentScope,\n access: ProjectAccess,\n translate: (key: string, fallback: string) => string,\n): Promise<{ id: string; timeProjectId: string }> {\n const task = await em.findOne(StaffTimeTask, {\n id: taskId,\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n deletedAt: null,\n })\n if (!task) throw targetNotFound(translate, 'task')\n if (!access.canManageAll && !access.projectIds.includes(task.timeProjectId)) {\n throw targetNotFound(translate, 'task')\n }\n return { id: task.id, timeProjectId: task.timeProjectId }\n}\n\n/**\n * Same rule for a time entry, with one extra branch: an entry that carries no\n * project is personal time, reachable only by the member who logged it (or by a\n * manager).\n */\nexport async function requireAccessibleTimeEntry(\n em: EntityManager,\n timeEntryId: string,\n scope: TagAssignmentScope,\n access: ProjectAccess,\n translate: (key: string, fallback: string) => string,\n): Promise<{ id: string; timeProjectId: string | null }> {\n const entry = await em.findOne(StaffTimeEntry, {\n id: timeEntryId,\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n deletedAt: null,\n })\n if (!entry) throw targetNotFound(translate, 'time_entry')\n if (!access.canManageAll) {\n const projectId = entry.timeProjectId ?? null\n const reachable = projectId\n ? access.projectIds.includes(projectId)\n : access.staffMemberId != null && entry.staffMemberId === access.staffMemberId\n if (!reachable) throw targetNotFound(translate, 'time_entry')\n }\n return { id: entry.id, timeProjectId: entry.timeProjectId ?? null }\n}\n"],
5
+ "mappings": "AAeA,SAAS,qBAAqB;AAC9B,SAAS,oBAAoB;AAE7B,SAAS,gBAAgB,qBAAqB;AAC9C,SAAS,yBAAyB,4BAAgD;AAClF,SAAS,4BAA4B;AACrC,SAAS,gCAAgC;AAEzC,MAAM,SAAS,aAAa,OAAO,EAAE,MAAM,EAAE,WAAW,sBAAsB,CAAC;AAE/E,MAAM,gBAA+B,EAAE,cAAc,OAAO,YAAY,CAAC,GAAG,eAAe,KAAK;AAUhG,eAAe,2BAA2B,WAA0B,UAA0C;AAC5G,MAAI;AACF,UAAM,gBAAgB,UAAU,QAAQ,qBAAqB;AAC7D,UAAM,WAAW,MAAM,yBAAyB,eAAe,EAAE,SAAS,CAAC;AAC3E,WAAO,SAAS,OAAO;AAAA,EACzB,QAAQ;AAEN,WAAO;AAAA,EACT;AACF;AAEA,eAAsB,qBACpB,WACA,OACwB;AACxB,MAAI;AAIF,UAAM,SAAS,MAAM,qBAAqB,WAAW,MAAM,QAAQ,CAAC,uBAAuB,GAAG;AAAA,MAC5F,UAAU,MAAM;AAAA,MAChB,gBAAgB,MAAM;AAAA,IACxB,CAAC;AACD,UAAM,KAAK,UAAU,QAAQ,IAAI;AACjC,WAAO,MAAM,qBAAqB;AAAA,MAChC,IAAI,GAAG,KAAK;AAAA,MACZ,QAAQ,MAAM;AAAA,MACd,UAAU,MAAM;AAAA,MAChB,gBAAgB,MAAM;AAAA,MACtB,cAAc,OAAO;AAAA,MACrB,cAAc,OAAO;AAAA,MACrB,qBAAqB,MAAM,2BAA2B,WAAW,MAAM,QAAQ;AAAA,IACjF,CAAC;AAAA,EACH,SAAS,KAAK;AACZ,WAAO,MAAM,kDAAkD,EAAE,IAAI,CAAC;AACtE,WAAO,EAAE,GAAG,cAAc;AAAA,EAC5B;AACF;AAEA,SAAS,eAAe,WAAsD,QAA8C;AAC1H,QAAM,UACJ,WAAW,SACP,UAAU,gDAAgD,mCAAmC,IAC7F,UAAU,iDAAiD,yCAAyC;AAC1G,SAAO,IAAI,cAAc,KAAK,EAAE,OAAO,QAAQ,CAAC;AAClD;AAMA,eAAsB,sBACpB,IACA,QACA,OACA,QACA,WACgD;AAChD,QAAM,OAAO,MAAM,GAAG,QAAQ,eAAe;AAAA,IAC3C,IAAI;AAAA,IACJ,UAAU,MAAM;AAAA,IAChB,gBAAgB,MAAM;AAAA,IACtB,WAAW;AAAA,EACb,CAAC;AACD,MAAI,CAAC,KAAM,OAAM,eAAe,WAAW,MAAM;AACjD,MAAI,CAAC,OAAO,gBAAgB,CAAC,OAAO,WAAW,SAAS,KAAK,aAAa,GAAG;AAC3E,UAAM,eAAe,WAAW,MAAM;AAAA,EACxC;AACA,SAAO,EAAE,IAAI,KAAK,IAAI,eAAe,KAAK,cAAc;AAC1D;AAOA,eAAsB,2BACpB,IACA,aACA,OACA,QACA,WACuD;AACvD,QAAM,QAAQ,MAAM,GAAG,QAAQ,gBAAgB;AAAA,IAC7C,IAAI;AAAA,IACJ,UAAU,MAAM;AAAA,IAChB,gBAAgB,MAAM;AAAA,IACtB,WAAW;AAAA,EACb,CAAC;AACD,MAAI,CAAC,MAAO,OAAM,eAAe,WAAW,YAAY;AACxD,MAAI,CAAC,OAAO,cAAc;AACxB,UAAM,YAAY,MAAM,iBAAiB;AACzC,UAAM,YAAY,YACd,OAAO,WAAW,SAAS,SAAS,IACpC,OAAO,iBAAiB,QAAQ,MAAM,kBAAkB,OAAO;AACnE,QAAI,CAAC,UAAW,OAAM,eAAe,WAAW,YAAY;AAAA,EAC9D;AACA,SAAO,EAAE,IAAI,MAAM,IAAI,eAAe,MAAM,iBAAiB,KAAK;AACpE;",
6
+ "names": []
7
+ }