@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.
- package/.turbo/turbo-build.log +2 -2
- package/dist/generated/entities/staff_time_entry/index.js +14 -0
- package/dist/generated/entities/staff_time_entry/index.js.map +2 -2
- package/dist/generated/entities/staff_time_entry_tag/index.js +15 -0
- package/dist/generated/entities/staff_time_entry_tag/index.js.map +7 -0
- package/dist/generated/entities/staff_time_project/index.js +16 -0
- package/dist/generated/entities/staff_time_project/index.js.map +2 -2
- package/dist/generated/entities/staff_time_report/index.js +57 -0
- package/dist/generated/entities/staff_time_report/index.js.map +7 -0
- package/dist/generated/entities/staff_time_report_entry/index.js +27 -0
- package/dist/generated/entities/staff_time_report_entry/index.js.map +7 -0
- package/dist/generated/entities/staff_time_report_event/index.js +21 -0
- package/dist/generated/entities/staff_time_report_event/index.js.map +7 -0
- package/dist/generated/entities/staff_time_report_project/index.js +15 -0
- package/dist/generated/entities/staff_time_report_project/index.js.map +7 -0
- package/dist/generated/entities/staff_time_tag/index.js +21 -0
- package/dist/generated/entities/staff_time_tag/index.js.map +7 -0
- package/dist/generated/entities/staff_time_task/index.js +37 -0
- package/dist/generated/entities/staff_time_task/index.js.map +7 -0
- package/dist/generated/entities/staff_time_task_comment/index.js +21 -0
- package/dist/generated/entities/staff_time_task_comment/index.js.map +7 -0
- package/dist/generated/entities/staff_time_task_status/index.js +29 -0
- package/dist/generated/entities/staff_time_task_status/index.js.map +7 -0
- package/dist/generated/entities/staff_time_task_tag/index.js +15 -0
- package/dist/generated/entities/staff_time_task_tag/index.js.map +7 -0
- package/dist/generated/entities.ids.generated.js +11 -1
- package/dist/generated/entities.ids.generated.js.map +2 -2
- package/dist/generated/entity-fields-registry.js +149 -0
- package/dist/generated/entity-fields-registry.js.map +2 -2
- package/dist/helpers/integration/timesheetFixtures.js +7 -0
- package/dist/helpers/integration/timesheetFixtures.js.map +2 -2
- package/dist/modules/auth/cli.js +16 -0
- package/dist/modules/auth/cli.js.map +2 -2
- package/dist/modules/auth/lib/backendChrome.js +1 -0
- package/dist/modules/auth/lib/backendChrome.js.map +2 -2
- package/dist/modules/customer_accounts/lib/customerRoleAcls.js +44 -0
- package/dist/modules/customer_accounts/lib/customerRoleAcls.js.map +7 -0
- package/dist/modules/customer_accounts/setup.js +1 -28
- package/dist/modules/customer_accounts/setup.js.map +2 -2
- package/dist/modules/data_sync/api/options.js +2 -0
- package/dist/modules/data_sync/api/options.js.map +2 -2
- package/dist/modules/data_sync/backend/data-sync/page.js +39 -17
- package/dist/modules/data_sync/backend/data-sync/page.js.map +2 -2
- package/dist/modules/data_sync/lib/start-controls.js +42 -0
- package/dist/modules/data_sync/lib/start-controls.js.map +7 -0
- package/dist/modules/staff/acl.js +9 -1
- package/dist/modules/staff/acl.js.map +2 -2
- package/dist/modules/staff/ai-agents.js +142 -0
- package/dist/modules/staff/ai-agents.js.map +7 -0
- package/dist/modules/staff/ai-tools/time-tracking-pack.js +348 -0
- package/dist/modules/staff/ai-tools/time-tracking-pack.js.map +7 -0
- package/dist/modules/staff/ai-tools/types.js +54 -0
- package/dist/modules/staff/ai-tools/types.js.map +7 -0
- package/dist/modules/staff/ai-tools.js +8 -0
- package/dist/modules/staff/ai-tools.js.map +7 -0
- package/dist/modules/staff/analytics.js +84 -1
- package/dist/modules/staff/analytics.js.map +2 -2
- package/dist/modules/staff/api/guards.js +37 -3
- package/dist/modules/staff/api/guards.js.map +2 -2
- package/dist/modules/staff/api/leave-requests/accept/route.js +1 -3
- package/dist/modules/staff/api/leave-requests/accept/route.js.map +2 -2
- package/dist/modules/staff/api/leave-requests/reject/route.js +1 -3
- package/dist/modules/staff/api/leave-requests/reject/route.js.map +2 -2
- package/dist/modules/staff/api/portal/time-reports/[id]/route.js +131 -0
- package/dist/modules/staff/api/portal/time-reports/[id]/route.js.map +7 -0
- package/dist/modules/staff/api/portal/time-reports/route.js +179 -0
- package/dist/modules/staff/api/portal/time-reports/route.js.map +7 -0
- package/dist/modules/staff/api/team-members/self/route.js +1 -3
- package/dist/modules/staff/api/team-members/self/route.js.map +2 -2
- package/dist/modules/staff/api/team-members/tags/assign/route.js +1 -3
- package/dist/modules/staff/api/team-members/tags/assign/route.js.map +2 -2
- package/dist/modules/staff/api/team-members/tags/unassign/route.js +1 -3
- package/dist/modules/staff/api/team-members/tags/unassign/route.js.map +2 -2
- package/dist/modules/staff/api/timesheets/_shared/withTimesheetInterceptors.js +174 -0
- package/dist/modules/staff/api/timesheets/_shared/withTimesheetInterceptors.js.map +7 -0
- package/dist/modules/staff/api/timesheets/access-requests/route.js +226 -0
- package/dist/modules/staff/api/timesheets/access-requests/route.js.map +7 -0
- package/dist/modules/staff/api/timesheets/my-projects/[projectId]/route.js +15 -8
- package/dist/modules/staff/api/timesheets/my-projects/[projectId]/route.js.map +2 -2
- package/dist/modules/staff/api/timesheets/my-projects/route.js +13 -1
- package/dist/modules/staff/api/timesheets/my-projects/route.js.map +2 -2
- package/dist/modules/staff/api/timesheets/my-work/myWorkAggregate.js +42 -0
- package/dist/modules/staff/api/timesheets/my-work/myWorkAggregate.js.map +7 -0
- package/dist/modules/staff/api/timesheets/my-work/route.js +383 -0
- package/dist/modules/staff/api/timesheets/my-work/route.js.map +7 -0
- package/dist/modules/staff/api/timesheets/projects/kpis/route.js +14 -2
- package/dist/modules/staff/api/timesheets/projects/kpis/route.js.map +2 -2
- package/dist/modules/staff/api/timesheets/reports/[id]/close/route.js +168 -0
- package/dist/modules/staff/api/timesheets/reports/[id]/close/route.js.map +7 -0
- package/dist/modules/staff/api/timesheets/reports/[id]/export/route.js +246 -0
- package/dist/modules/staff/api/timesheets/reports/[id]/export/route.js.map +7 -0
- package/dist/modules/staff/api/timesheets/reports/[id]/sheet/route.js +183 -0
- package/dist/modules/staff/api/timesheets/reports/[id]/sheet/route.js.map +7 -0
- package/dist/modules/staff/api/timesheets/reports/[id]/unlock/route.js +164 -0
- package/dist/modules/staff/api/timesheets/reports/[id]/unlock/route.js.map +7 -0
- package/dist/modules/staff/api/timesheets/reports/preview/route.js +360 -0
- package/dist/modules/staff/api/timesheets/reports/preview/route.js.map +7 -0
- package/dist/modules/staff/api/timesheets/reports/route.js +316 -0
- package/dist/modules/staff/api/timesheets/reports/route.js.map +7 -0
- package/dist/modules/staff/api/timesheets/reports/shared.js +63 -0
- package/dist/modules/staff/api/timesheets/reports/shared.js.map +7 -0
- package/dist/modules/staff/api/timesheets/settings/reapply-rounding/route.js +164 -0
- package/dist/modules/staff/api/timesheets/settings/reapply-rounding/route.js.map +7 -0
- package/dist/modules/staff/api/timesheets/settings/rounding-impact/route.js +168 -0
- package/dist/modules/staff/api/timesheets/settings/rounding-impact/route.js.map +7 -0
- package/dist/modules/staff/api/timesheets/settings/route.js +199 -0
- package/dist/modules/staff/api/timesheets/settings/route.js.map +7 -0
- package/dist/modules/staff/api/timesheets/tags/access.js +76 -0
- package/dist/modules/staff/api/timesheets/tags/access.js.map +7 -0
- package/dist/modules/staff/api/timesheets/tags/entry-assignments/route.js +235 -0
- package/dist/modules/staff/api/timesheets/tags/entry-assignments/route.js.map +7 -0
- package/dist/modules/staff/api/timesheets/tags/route.js +160 -0
- package/dist/modules/staff/api/timesheets/tags/route.js.map +7 -0
- package/dist/modules/staff/api/timesheets/tags/task-assignments/route.js +233 -0
- package/dist/modules/staff/api/timesheets/tags/task-assignments/route.js.map +7 -0
- package/dist/modules/staff/api/timesheets/task-statuses/route.js +188 -0
- package/dist/modules/staff/api/timesheets/task-statuses/route.js.map +7 -0
- package/dist/modules/staff/api/timesheets/tasks/[id]/comments/route.js +399 -0
- package/dist/modules/staff/api/timesheets/tasks/[id]/comments/route.js.map +7 -0
- package/dist/modules/staff/api/timesheets/tasks/[id]/status/route.js +210 -0
- package/dist/modules/staff/api/timesheets/tasks/[id]/status/route.js.map +7 -0
- package/dist/modules/staff/api/timesheets/tasks/route.js +333 -0
- package/dist/modules/staff/api/timesheets/tasks/route.js.map +7 -0
- package/dist/modules/staff/api/timesheets/time-entries/[id]/duplicate/route.js +228 -0
- package/dist/modules/staff/api/timesheets/time-entries/[id]/duplicate/route.js.map +7 -0
- package/dist/modules/staff/api/timesheets/time-entries/[id]/segments/[segmentId]/route.js +29 -10
- package/dist/modules/staff/api/timesheets/time-entries/[id]/segments/[segmentId]/route.js.map +2 -2
- package/dist/modules/staff/api/timesheets/time-entries/[id]/segments/route.js +31 -18
- package/dist/modules/staff/api/timesheets/time-entries/[id]/segments/route.js.map +2 -2
- package/dist/modules/staff/api/timesheets/time-entries/[id]/timer-start/route.js +21 -7
- package/dist/modules/staff/api/timesheets/time-entries/[id]/timer-start/route.js.map +2 -2
- package/dist/modules/staff/api/timesheets/time-entries/[id]/timer-stop/route.js +27 -7
- package/dist/modules/staff/api/timesheets/time-entries/[id]/timer-stop/route.js.map +2 -2
- package/dist/modules/staff/api/timesheets/time-entries/bulk/route.js +215 -32
- package/dist/modules/staff/api/timesheets/time-entries/bulk/route.js.map +2 -2
- package/dist/modules/staff/api/timesheets/time-entries/copy-day/route.js +378 -0
- package/dist/modules/staff/api/timesheets/time-entries/copy-day/route.js.map +7 -0
- package/dist/modules/staff/api/timesheets/time-entries/overlaps/route.js +340 -0
- package/dist/modules/staff/api/timesheets/time-entries/overlaps/route.js.map +7 -0
- package/dist/modules/staff/api/timesheets/time-entries/route.js +238 -26
- package/dist/modules/staff/api/timesheets/time-entries/route.js.map +2 -2
- package/dist/modules/staff/api/timesheets/time-entries/start-timer/route.js +23 -8
- package/dist/modules/staff/api/timesheets/time-entries/start-timer/route.js.map +2 -2
- package/dist/modules/staff/api/timesheets/time-projects/[id]/change-currency/route.js +209 -0
- package/dist/modules/staff/api/timesheets/time-projects/[id]/change-currency/route.js.map +7 -0
- package/dist/modules/staff/api/timesheets/time-projects/[id]/employees/route.js +59 -2
- package/dist/modules/staff/api/timesheets/time-projects/[id]/employees/route.js.map +2 -2
- package/dist/modules/staff/api/timesheets/time-projects/route.js +264 -22
- package/dist/modules/staff/api/timesheets/time-projects/route.js.map +2 -2
- package/dist/modules/staff/backend/staff/time-tracking/board/page.js +110 -0
- package/dist/modules/staff/backend/staff/time-tracking/board/page.js.map +7 -0
- package/dist/modules/staff/backend/staff/time-tracking/board/page.meta.js +18 -0
- package/dist/modules/staff/backend/staff/time-tracking/board/page.meta.js.map +7 -0
- package/dist/modules/staff/backend/staff/time-tracking/entries/page.js +981 -0
- package/dist/modules/staff/backend/staff/time-tracking/entries/page.js.map +7 -0
- package/dist/modules/staff/backend/staff/time-tracking/entries/page.meta.js +18 -0
- package/dist/modules/staff/backend/staff/time-tracking/entries/page.meta.js.map +7 -0
- package/dist/modules/staff/backend/staff/time-tracking/page.js +583 -0
- package/dist/modules/staff/backend/staff/time-tracking/page.js.map +7 -0
- package/dist/modules/staff/backend/staff/time-tracking/page.meta.js +15 -0
- package/dist/modules/staff/backend/staff/time-tracking/page.meta.js.map +7 -0
- package/dist/modules/staff/backend/staff/time-tracking/projects/[id]/board/page.js +65 -0
- package/dist/modules/staff/backend/staff/time-tracking/projects/[id]/board/page.js.map +7 -0
- package/dist/modules/staff/backend/staff/time-tracking/projects/[id]/board/page.meta.js +16 -0
- package/dist/modules/staff/backend/staff/time-tracking/projects/[id]/board/page.meta.js.map +7 -0
- package/dist/modules/staff/backend/staff/time-tracking/projects/[id]/edit/page.js +229 -0
- package/dist/modules/staff/backend/staff/time-tracking/projects/[id]/edit/page.js.map +7 -0
- package/dist/modules/staff/backend/staff/time-tracking/projects/[id]/edit/page.meta.js +16 -0
- package/dist/modules/staff/backend/staff/time-tracking/projects/[id]/edit/page.meta.js.map +7 -0
- package/dist/modules/staff/backend/staff/time-tracking/projects/[id]/page.js +1014 -0
- package/dist/modules/staff/backend/staff/time-tracking/projects/[id]/page.js.map +7 -0
- package/dist/modules/staff/backend/staff/time-tracking/projects/[id]/page.meta.js +16 -0
- package/dist/modules/staff/backend/staff/time-tracking/projects/[id]/page.meta.js.map +7 -0
- package/dist/modules/staff/backend/staff/time-tracking/projects/create/page.js +97 -0
- package/dist/modules/staff/backend/staff/time-tracking/projects/create/page.js.map +7 -0
- package/dist/modules/staff/backend/staff/time-tracking/projects/create/page.meta.js +16 -0
- package/dist/modules/staff/backend/staff/time-tracking/projects/create/page.meta.js.map +7 -0
- package/dist/modules/staff/backend/staff/time-tracking/projects/page.js +1040 -0
- package/dist/modules/staff/backend/staff/time-tracking/projects/page.js.map +7 -0
- package/dist/modules/staff/backend/staff/time-tracking/projects/page.meta.js +19 -0
- package/dist/modules/staff/backend/staff/time-tracking/projects/page.meta.js.map +7 -0
- package/dist/modules/staff/backend/staff/time-tracking/projects/projectFormConfig.js +353 -0
- package/dist/modules/staff/backend/staff/time-tracking/projects/projectFormConfig.js.map +7 -0
- package/dist/modules/staff/backend/staff/time-tracking/reports/[id]/page.js +457 -0
- package/dist/modules/staff/backend/staff/time-tracking/reports/[id]/page.js.map +7 -0
- package/dist/modules/staff/backend/staff/time-tracking/reports/[id]/page.meta.js +16 -0
- package/dist/modules/staff/backend/staff/time-tracking/reports/[id]/page.meta.js.map +7 -0
- package/dist/modules/staff/backend/staff/time-tracking/reports/create/page.js +697 -0
- package/dist/modules/staff/backend/staff/time-tracking/reports/create/page.js.map +7 -0
- package/dist/modules/staff/backend/staff/time-tracking/reports/create/page.meta.js +16 -0
- package/dist/modules/staff/backend/staff/time-tracking/reports/create/page.meta.js.map +7 -0
- package/dist/modules/staff/backend/staff/time-tracking/reports/page.js +174 -0
- package/dist/modules/staff/backend/staff/time-tracking/reports/page.js.map +7 -0
- package/dist/modules/staff/backend/staff/time-tracking/reports/page.meta.js +18 -0
- package/dist/modules/staff/backend/staff/time-tracking/reports/page.meta.js.map +7 -0
- package/dist/modules/staff/backend/staff/time-tracking/settings/page.js +546 -0
- package/dist/modules/staff/backend/staff/time-tracking/settings/page.js.map +7 -0
- package/dist/modules/staff/backend/staff/time-tracking/settings/page.meta.js +18 -0
- package/dist/modules/staff/backend/staff/time-tracking/settings/page.meta.js.map +7 -0
- package/dist/modules/staff/backend/staff/time-tracking/timesheet/GridView.js +710 -0
- package/dist/modules/staff/backend/staff/time-tracking/timesheet/GridView.js.map +7 -0
- package/dist/modules/staff/backend/staff/time-tracking/timesheet/page.js +646 -0
- package/dist/modules/staff/backend/staff/time-tracking/timesheet/page.js.map +7 -0
- package/dist/modules/staff/backend/staff/time-tracking/timesheet/page.meta.js +18 -0
- package/dist/modules/staff/backend/staff/time-tracking/timesheet/page.meta.js.map +7 -0
- package/dist/modules/staff/backend/staff/timesheets/page.js +4 -793
- package/dist/modules/staff/backend/staff/timesheets/page.js.map +2 -2
- package/dist/modules/staff/backend/staff/timesheets/page.meta.js +2 -7
- package/dist/modules/staff/backend/staff/timesheets/page.meta.js.map +2 -2
- package/dist/modules/staff/backend/staff/timesheets/projects/[id]/edit/page.js +5 -136
- package/dist/modules/staff/backend/staff/timesheets/projects/[id]/edit/page.js.map +2 -2
- package/dist/modules/staff/backend/staff/timesheets/projects/[id]/edit/page.meta.js +1 -7
- package/dist/modules/staff/backend/staff/timesheets/projects/[id]/edit/page.meta.js.map +2 -2
- package/dist/modules/staff/backend/staff/timesheets/projects/[id]/page.js +5 -445
- package/dist/modules/staff/backend/staff/timesheets/projects/[id]/page.js.map +3 -3
- package/dist/modules/staff/backend/staff/timesheets/projects/[id]/page.meta.js +1 -7
- package/dist/modules/staff/backend/staff/timesheets/projects/[id]/page.meta.js.map +2 -2
- package/dist/modules/staff/backend/staff/timesheets/projects/create/page.js +4 -76
- package/dist/modules/staff/backend/staff/timesheets/projects/create/page.js.map +2 -2
- package/dist/modules/staff/backend/staff/timesheets/projects/create/page.meta.js +1 -7
- package/dist/modules/staff/backend/staff/timesheets/projects/create/page.meta.js.map +2 -2
- package/dist/modules/staff/backend/staff/timesheets/projects/page.js +4 -613
- package/dist/modules/staff/backend/staff/timesheets/projects/page.js.map +2 -2
- package/dist/modules/staff/backend/staff/timesheets/projects/page.meta.js +2 -17
- package/dist/modules/staff/backend/staff/timesheets/projects/page.meta.js.map +2 -2
- package/dist/modules/staff/ce.js +40 -0
- package/dist/modules/staff/ce.js.map +2 -2
- package/dist/modules/staff/cli.js +166 -1
- package/dist/modules/staff/cli.js.map +2 -2
- package/dist/modules/staff/commands/index.js +5 -0
- package/dist/modules/staff/commands/index.js.map +2 -2
- package/dist/modules/staff/commands/timesheets-entries.js +569 -21
- package/dist/modules/staff/commands/timesheets-entries.js.map +3 -3
- package/dist/modules/staff/commands/timesheets-projects.js +450 -7
- package/dist/modules/staff/commands/timesheets-projects.js.map +2 -2
- package/dist/modules/staff/commands/timesheets-reports.js +1036 -0
- package/dist/modules/staff/commands/timesheets-reports.js.map +7 -0
- package/dist/modules/staff/commands/timesheets-tags.js +1015 -0
- package/dist/modules/staff/commands/timesheets-tags.js.map +7 -0
- package/dist/modules/staff/commands/timesheets-task-comments.js +438 -0
- package/dist/modules/staff/commands/timesheets-task-comments.js.map +7 -0
- package/dist/modules/staff/commands/timesheets-task-statuses.js +723 -0
- package/dist/modules/staff/commands/timesheets-task-statuses.js.map +7 -0
- package/dist/modules/staff/commands/timesheets-tasks.js +834 -0
- package/dist/modules/staff/commands/timesheets-tasks.js.map +7 -0
- package/dist/modules/staff/data/enrichers.js +426 -12
- package/dist/modules/staff/data/enrichers.js.map +2 -2
- package/dist/modules/staff/data/entities.js +552 -3
- package/dist/modules/staff/data/entities.js.map +2 -2
- package/dist/modules/staff/data/extensions.js +41 -0
- package/dist/modules/staff/data/extensions.js.map +7 -0
- package/dist/modules/staff/data/validators.js +254 -9
- package/dist/modules/staff/data/validators.js.map +2 -2
- package/dist/modules/staff/di.js +42 -1
- package/dist/modules/staff/di.js.map +2 -2
- package/dist/modules/staff/events.js +84 -6
- package/dist/modules/staff/events.js.map +2 -2
- package/dist/modules/staff/events.payloads.js +158 -0
- package/dist/modules/staff/events.payloads.js.map +7 -0
- package/dist/modules/staff/extension-points.js +261 -0
- package/dist/modules/staff/extension-points.js.map +7 -0
- package/dist/modules/staff/frontend/[orgSlug]/portal/time-reports/[id]/page.js +138 -0
- package/dist/modules/staff/frontend/[orgSlug]/portal/time-reports/[id]/page.js.map +7 -0
- package/dist/modules/staff/frontend/[orgSlug]/portal/time-reports/[id]/page.meta.js +12 -0
- package/dist/modules/staff/frontend/[orgSlug]/portal/time-reports/[id]/page.meta.js.map +7 -0
- package/dist/modules/staff/frontend/[orgSlug]/portal/time-reports/page.js +133 -0
- package/dist/modules/staff/frontend/[orgSlug]/portal/time-reports/page.js.map +7 -0
- package/dist/modules/staff/frontend/[orgSlug]/portal/time-reports/page.meta.js +19 -0
- package/dist/modules/staff/frontend/[orgSlug]/portal/time-reports/page.meta.js.map +7 -0
- package/dist/modules/staff/lib/crud.js +11 -1
- package/dist/modules/staff/lib/crud.js.map +2 -2
- package/dist/modules/staff/lib/time-tracking/access.js +92 -0
- package/dist/modules/staff/lib/time-tracking/access.js.map +7 -0
- package/dist/modules/staff/lib/time-tracking/billability.js +39 -0
- package/dist/modules/staff/lib/time-tracking/billability.js.map +7 -0
- package/dist/modules/staff/lib/time-tracking/capacity.js +68 -0
- package/dist/modules/staff/lib/time-tracking/capacity.js.map +7 -0
- package/dist/modules/staff/lib/time-tracking/componentContracts.js +15 -0
- package/dist/modules/staff/lib/time-tracking/componentContracts.js.map +7 -0
- package/dist/modules/staff/lib/time-tracking/cost.js +82 -0
- package/dist/modules/staff/lib/time-tracking/cost.js.map +7 -0
- package/dist/modules/staff/lib/time-tracking/duration.js +55 -0
- package/dist/modules/staff/lib/time-tracking/duration.js.map +7 -0
- package/dist/modules/staff/lib/time-tracking/featureAccess.js +41 -0
- package/dist/modules/staff/lib/time-tracking/featureAccess.js.map +7 -0
- package/dist/modules/staff/lib/time-tracking/interval.js +72 -0
- package/dist/modules/staff/lib/time-tracking/interval.js.map +7 -0
- package/dist/modules/staff/lib/time-tracking/migrateProjectCodes.js +88 -0
- package/dist/modules/staff/lib/time-tracking/migrateProjectCodes.js.map +7 -0
- package/dist/modules/staff/lib/time-tracking/moneyVisibility.js +10 -0
- package/dist/modules/staff/lib/time-tracking/moneyVisibility.js.map +7 -0
- package/dist/modules/staff/lib/time-tracking/overlap.js +87 -0
- package/dist/modules/staff/lib/time-tracking/overlap.js.map +7 -0
- package/dist/modules/staff/lib/time-tracking/portalRecipients.js +27 -0
- package/dist/modules/staff/lib/time-tracking/portalRecipients.js.map +7 -0
- package/dist/modules/staff/lib/time-tracking/portalReports.js +16 -0
- package/dist/modules/staff/lib/time-tracking/portalReports.js.map +7 -0
- package/dist/modules/staff/lib/time-tracking/projectCode.js +135 -0
- package/dist/modules/staff/lib/time-tracking/projectCode.js.map +7 -0
- package/dist/modules/staff/lib/time-tracking/reapplyRounding.js +187 -0
- package/dist/modules/staff/lib/time-tracking/reapplyRounding.js.map +7 -0
- package/dist/modules/staff/lib/time-tracking/recalculationRunner.js +80 -0
- package/dist/modules/staff/lib/time-tracking/recalculationRunner.js.map +7 -0
- package/dist/modules/staff/lib/time-tracking/recalculations.js +56 -0
- package/dist/modules/staff/lib/time-tracking/recalculations.js.map +7 -0
- package/dist/modules/staff/lib/time-tracking/registries/invoke.js +63 -0
- package/dist/modules/staff/lib/time-tracking/registries/invoke.js.map +7 -0
- package/dist/modules/staff/lib/time-tracking/registries/registry.js +62 -0
- package/dist/modules/staff/lib/time-tracking/registries/registry.js.map +7 -0
- package/dist/modules/staff/lib/time-tracking/registries/scope.js +21 -0
- package/dist/modules/staff/lib/time-tracking/registries/scope.js.map +7 -0
- package/dist/modules/staff/lib/time-tracking/rollup.js +34 -0
- package/dist/modules/staff/lib/time-tracking/rollup.js.map +7 -0
- package/dist/modules/staff/lib/time-tracking/rounding.js +66 -0
- package/dist/modules/staff/lib/time-tracking/rounding.js.map +7 -0
- package/dist/modules/staff/lib/time-tracking/roundingImpact.js +39 -0
- package/dist/modules/staff/lib/time-tracking/roundingImpact.js.map +7 -0
- package/dist/modules/staff/lib/time-tracking/settingKeys.js +214 -0
- package/dist/modules/staff/lib/time-tracking/settingKeys.js.map +7 -0
- package/dist/modules/staff/lib/time-tracking/settings.js +87 -0
- package/dist/modules/staff/lib/time-tracking/settings.js.map +7 -0
- package/dist/modules/staff/lib/time-tracking/sqlInClause.js +9 -0
- package/dist/modules/staff/lib/time-tracking/sqlInClause.js.map +7 -0
- package/dist/modules/staff/lib/time-tracking/timeEntrySources.js +48 -0
- package/dist/modules/staff/lib/time-tracking/timeEntrySources.js.map +7 -0
- package/dist/modules/staff/lib/time-tracking-ui/BoardFilterChips.js +283 -0
- package/dist/modules/staff/lib/time-tracking-ui/BoardFilterChips.js.map +7 -0
- package/dist/modules/staff/lib/time-tracking-ui/CustomerPicker.js +324 -0
- package/dist/modules/staff/lib/time-tracking-ui/CustomerPicker.js.map +7 -0
- package/dist/modules/staff/lib/time-tracking-ui/DurationInput.js +121 -0
- package/dist/modules/staff/lib/time-tracking-ui/DurationInput.js.map +7 -0
- package/dist/modules/staff/lib/time-tracking-ui/EntryDetailsFields.js +188 -0
- package/dist/modules/staff/lib/time-tracking-ui/EntryDetailsFields.js.map +7 -0
- package/dist/modules/staff/lib/time-tracking-ui/EntryFilterBar.js +89 -0
- package/dist/modules/staff/lib/time-tracking-ui/EntryFilterBar.js.map +7 -0
- package/dist/modules/staff/lib/time-tracking-ui/KanbanBoard.js +855 -0
- package/dist/modules/staff/lib/time-tracking-ui/KanbanBoard.js.map +7 -0
- package/dist/modules/staff/lib/time-tracking-ui/KanbanCard.js +360 -0
- package/dist/modules/staff/lib/time-tracking-ui/KanbanCard.js.map +7 -0
- package/dist/modules/staff/lib/time-tracking-ui/KanbanColumn.js +259 -0
- package/dist/modules/staff/lib/time-tracking-ui/KanbanColumn.js.map +7 -0
- package/dist/modules/staff/lib/time-tracking-ui/NewTaskDialog.js +238 -0
- package/dist/modules/staff/lib/time-tracking-ui/NewTaskDialog.js.map +7 -0
- package/dist/modules/staff/lib/time-tracking-ui/NoProjectAccess.js +130 -0
- package/dist/modules/staff/lib/time-tracking-ui/NoProjectAccess.js.map +7 -0
- package/dist/modules/staff/lib/time-tracking-ui/PeriodSelector.js +98 -0
- package/dist/modules/staff/lib/time-tracking-ui/PeriodSelector.js.map +7 -0
- package/dist/modules/staff/lib/time-tracking-ui/PhasePlaceholder.js +27 -0
- package/dist/modules/staff/lib/time-tracking-ui/PhasePlaceholder.js.map +7 -0
- package/dist/modules/staff/lib/time-tracking-ui/ProjectCodeField.js +166 -0
- package/dist/modules/staff/lib/time-tracking-ui/ProjectCodeField.js.map +7 -0
- package/dist/modules/staff/lib/time-tracking-ui/ProjectCurrencyField.js +295 -0
- package/dist/modules/staff/lib/time-tracking-ui/ProjectCurrencyField.js.map +7 -0
- package/dist/modules/staff/lib/time-tracking-ui/ProjectFormSections.js +212 -0
- package/dist/modules/staff/lib/time-tracking-ui/ProjectFormSections.js.map +7 -0
- package/dist/modules/staff/lib/time-tracking-ui/ProjectTeamDrawer.js +729 -0
- package/dist/modules/staff/lib/time-tracking-ui/ProjectTeamDrawer.js.map +7 -0
- package/dist/modules/staff/lib/time-tracking-ui/ReportSheet.js +217 -0
- package/dist/modules/staff/lib/time-tracking-ui/ReportSheet.js.map +7 -0
- package/dist/modules/staff/lib/time-tracking-ui/TagPicker.js +158 -0
- package/dist/modules/staff/lib/time-tracking-ui/TagPicker.js.map +7 -0
- package/dist/modules/staff/lib/time-tracking-ui/TaskBoardScreen.js +224 -0
- package/dist/modules/staff/lib/time-tracking-ui/TaskBoardScreen.js.map +7 -0
- package/dist/modules/staff/lib/time-tracking-ui/TaskCommentThread.js +93 -0
- package/dist/modules/staff/lib/time-tracking-ui/TaskCommentThread.js.map +7 -0
- package/dist/modules/staff/lib/time-tracking-ui/TaskDrawer.js +1196 -0
- package/dist/modules/staff/lib/time-tracking-ui/TaskDrawer.js.map +7 -0
- package/dist/modules/staff/lib/time-tracking-ui/TaskListView.js +176 -0
- package/dist/modules/staff/lib/time-tracking-ui/TaskListView.js.map +7 -0
- package/dist/modules/staff/lib/time-tracking-ui/TaskPicker.js +307 -0
- package/dist/modules/staff/lib/time-tracking-ui/TaskPicker.js.map +7 -0
- package/dist/modules/staff/lib/time-tracking-ui/TaskQuickLog.js +284 -0
- package/dist/modules/staff/lib/time-tracking-ui/TaskQuickLog.js.map +7 -0
- package/dist/modules/staff/lib/time-tracking-ui/TimeEntriesSummaryFooter.js +73 -0
- package/dist/modules/staff/lib/time-tracking-ui/TimeEntriesSummaryFooter.js.map +7 -0
- package/dist/modules/staff/lib/time-tracking-ui/TimeEntryDialog.js +1499 -0
- package/dist/modules/staff/lib/time-tracking-ui/TimeEntryDialog.js.map +7 -0
- package/dist/modules/staff/lib/time-tracking-ui/TimeEntryDurationCell.js +95 -0
- package/dist/modules/staff/lib/time-tracking-ui/TimeEntryDurationCell.js.map +7 -0
- package/dist/modules/staff/lib/time-tracking-ui/TimesheetCalendar.js +188 -0
- package/dist/modules/staff/lib/time-tracking-ui/TimesheetCalendar.js.map +7 -0
- package/dist/modules/staff/lib/time-tracking-ui/TimesheetPeriodFooter.js +61 -0
- package/dist/modules/staff/lib/time-tracking-ui/TimesheetPeriodFooter.js.map +7 -0
- package/dist/modules/staff/lib/time-tracking-ui/TimesheetViewSwitch.js +45 -0
- package/dist/modules/staff/lib/time-tracking-ui/TimesheetViewSwitch.js.map +7 -0
- package/dist/modules/staff/lib/time-tracking-ui/boardDirectory.js +176 -0
- package/dist/modules/staff/lib/time-tracking-ui/boardDirectory.js.map +7 -0
- package/dist/modules/staff/lib/time-tracking-ui/boardFilters.js +82 -0
- package/dist/modules/staff/lib/time-tracking-ui/boardFilters.js.map +7 -0
- package/dist/modules/staff/lib/time-tracking-ui/boardSummary.js +5 -0
- package/dist/modules/staff/lib/time-tracking-ui/boardSummary.js.map +7 -0
- package/dist/modules/staff/lib/time-tracking-ui/kanbanBoardData.js +152 -0
- package/dist/modules/staff/lib/time-tracking-ui/kanbanBoardData.js.map +7 -0
- package/dist/modules/staff/lib/time-tracking-ui/projectTeamAssignment.js +92 -0
- package/dist/modules/staff/lib/time-tracking-ui/projectTeamAssignment.js.map +7 -0
- package/dist/modules/staff/lib/time-tracking-ui/taskDrawerData.js +111 -0
- package/dist/modules/staff/lib/time-tracking-ui/taskDrawerData.js.map +7 -0
- package/dist/modules/staff/lib/time-tracking-ui/timeEntryDialogState.js +243 -0
- package/dist/modules/staff/lib/time-tracking-ui/timeEntryDialogState.js.map +7 -0
- package/dist/modules/staff/lib/time-tracking-ui/timeEntryListData.js +151 -0
- package/dist/modules/staff/lib/time-tracking-ui/timeEntryListData.js.map +7 -0
- package/dist/modules/staff/lib/time-tracking-ui/timeTrackingSettingsForm.js +105 -0
- package/dist/modules/staff/lib/time-tracking-ui/timeTrackingSettingsForm.js.map +7 -0
- package/dist/modules/staff/lib/time-tracking-ui/timesheetData.js +132 -0
- package/dist/modules/staff/lib/time-tracking-ui/timesheetData.js.map +7 -0
- package/dist/modules/staff/lib/time-tracking-ui/timesheetPeriod.js +205 -0
- package/dist/modules/staff/lib/time-tracking-ui/timesheetPeriod.js.map +7 -0
- package/dist/modules/staff/lib/time-tracking-ui/timesheetTargets.js +53 -0
- package/dist/modules/staff/lib/time-tracking-ui/timesheetTargets.js.map +7 -0
- package/dist/modules/staff/lib/time-tracking-ui/useBoardFilters.js +77 -0
- package/dist/modules/staff/lib/time-tracking-ui/useBoardFilters.js.map +7 -0
- package/dist/modules/staff/lib/time-tracking-ui/useTimesheetPreferences.js +151 -0
- package/dist/modules/staff/lib/time-tracking-ui/useTimesheetPreferences.js.map +7 -0
- package/dist/modules/staff/lib/timeTrackingSeeds.js +809 -0
- package/dist/modules/staff/lib/timeTrackingSeeds.js.map +7 -0
- package/dist/modules/staff/lib/timesheets/timeEntryCacheInvalidation.js +2 -3
- package/dist/modules/staff/lib/timesheets/timeEntryCacheInvalidation.js.map +2 -2
- package/dist/modules/staff/lib/timesheets/timeEntryDecoration.js +152 -0
- package/dist/modules/staff/lib/timesheets/timeEntryDecoration.js.map +7 -0
- package/dist/modules/staff/lib/timesheets-projects/budgetBurn.js +36 -0
- package/dist/modules/staff/lib/timesheets-projects/budgetBurn.js.map +7 -0
- package/dist/modules/staff/lib/timesheets-projects/budgetThreshold.js +66 -0
- package/dist/modules/staff/lib/timesheets-projects/budgetThreshold.js.map +7 -0
- package/dist/modules/staff/lib/timesheets-projects/budgetThresholdState.js +78 -0
- package/dist/modules/staff/lib/timesheets-projects/budgetThresholdState.js.map +7 -0
- package/dist/modules/staff/lib/timesheets-projects/computeProjectFinancials.js +81 -0
- package/dist/modules/staff/lib/timesheets-projects/computeProjectFinancials.js.map +7 -0
- package/dist/modules/staff/lib/timesheets-projects/reportSelection.js +44 -0
- package/dist/modules/staff/lib/timesheets-projects/reportSelection.js.map +7 -0
- package/dist/modules/staff/lib/timesheets-projects-ui/AssignMembersDialog.js +169 -0
- package/dist/modules/staff/lib/timesheets-projects-ui/AssignMembersDialog.js.map +7 -0
- package/dist/modules/staff/lib/timesheets-projects-ui/ProjectBudgetCell.js +22 -0
- package/dist/modules/staff/lib/timesheets-projects-ui/ProjectBudgetCell.js.map +7 -0
- package/dist/modules/staff/lib/timesheets-projects-ui/ProjectCard.js +51 -1
- package/dist/modules/staff/lib/timesheets-projects-ui/ProjectCard.js.map +2 -2
- package/dist/modules/staff/lib/timesheets-projects-ui/ProjectsBulkActionsBar.js +70 -0
- package/dist/modules/staff/lib/timesheets-projects-ui/ProjectsBulkActionsBar.js.map +7 -0
- package/dist/modules/staff/lib/timesheets-reports/buildReportSheet.js +63 -0
- package/dist/modules/staff/lib/timesheets-reports/buildReportSheet.js.map +7 -0
- package/dist/modules/staff/lib/timesheets-reports/loadReportData.js +170 -0
- package/dist/modules/staff/lib/timesheets-reports/loadReportData.js.map +7 -0
- package/dist/modules/staff/lib/timesheets-reports/pdf.js +205 -0
- package/dist/modules/staff/lib/timesheets-reports/pdf.js.map +7 -0
- package/dist/modules/staff/lib/timesheets-reports/reportApprovalPolicies.js +68 -0
- package/dist/modules/staff/lib/timesheets-reports/reportApprovalPolicies.js.map +7 -0
- package/dist/modules/staff/lib/timesheets-reports/reportConfigData.js +135 -0
- package/dist/modules/staff/lib/timesheets-reports/reportConfigData.js.map +7 -0
- package/dist/modules/staff/lib/timesheets-reports/reportConfigState.js +48 -0
- package/dist/modules/staff/lib/timesheets-reports/reportConfigState.js.map +7 -0
- package/dist/modules/staff/lib/timesheets-reports/reportExport.js +262 -0
- package/dist/modules/staff/lib/timesheets-reports/reportExport.js.map +7 -0
- package/dist/modules/staff/lib/timesheets-reports/reportExportFormats.js +34 -0
- package/dist/modules/staff/lib/timesheets-reports/reportExportFormats.js.map +7 -0
- package/dist/modules/staff/lib/timesheets-reports/reportGroupings.js +79 -0
- package/dist/modules/staff/lib/timesheets-reports/reportGroupings.js.map +7 -0
- package/dist/modules/staff/lib/timesheets-reports/reportLabels.js +11 -0
- package/dist/modules/staff/lib/timesheets-reports/reportLabels.js.map +7 -0
- package/dist/modules/staff/lib/timesheets-reports/reportReference.js +83 -0
- package/dist/modules/staff/lib/timesheets-reports/reportReference.js.map +7 -0
- package/dist/modules/staff/lib/timesheets-reports/reportRows.js +42 -0
- package/dist/modules/staff/lib/timesheets-reports/reportRows.js.map +7 -0
- package/dist/modules/staff/lib/timesheets-reports/reportSheetData.js +107 -0
- package/dist/modules/staff/lib/timesheets-reports/reportSheetData.js.map +7 -0
- package/dist/modules/staff/lib/timesheets-reports/reportTotals.js +289 -0
- package/dist/modules/staff/lib/timesheets-reports/reportTotals.js.map +7 -0
- package/dist/modules/staff/lib/timesheets-reports/xlsx.js +145 -0
- package/dist/modules/staff/lib/timesheets-reports/xlsx.js.map +7 -0
- package/dist/modules/staff/lib/timesheets-tasks/computeTaskRollups.js +116 -0
- package/dist/modules/staff/lib/timesheets-tasks/computeTaskRollups.js.map +7 -0
- package/dist/modules/staff/lib/timesheets-tasks/defaultStatusTemplate.js +50 -0
- package/dist/modules/staff/lib/timesheets-tasks/defaultStatusTemplate.js.map +7 -0
- package/dist/modules/staff/lib/timesheets-tasks/seedProjectStatuses.js +27 -0
- package/dist/modules/staff/lib/timesheets-tasks/seedProjectStatuses.js.map +7 -0
- package/dist/modules/staff/lib/timesheets-tasks/statusPositions.js +65 -0
- package/dist/modules/staff/lib/timesheets-tasks/statusPositions.js.map +7 -0
- package/dist/modules/staff/lib/timesheets-tasks/statusSlug.js +31 -0
- package/dist/modules/staff/lib/timesheets-tasks/statusSlug.js.map +7 -0
- package/dist/modules/staff/lib/timesheets-tasks/taskHierarchy.js +31 -0
- package/dist/modules/staff/lib/timesheets-tasks/taskHierarchy.js.map +7 -0
- package/dist/modules/staff/lib/timesheets-tasks/taskHoursTotals.js +17 -0
- package/dist/modules/staff/lib/timesheets-tasks/taskHoursTotals.js.map +7 -0
- package/dist/modules/staff/lib/timesheets-tasks/taskReference.js +51 -0
- package/dist/modules/staff/lib/timesheets-tasks/taskReference.js.map +7 -0
- package/dist/modules/staff/lib/timesheets-ui/CreateProjectDialog.js +11 -4
- package/dist/modules/staff/lib/timesheets-ui/CreateProjectDialog.js.map +2 -2
- package/dist/modules/staff/lib/timesheets-ui/ListView.js +369 -111
- package/dist/modules/staff/lib/timesheets-ui/ListView.js.map +2 -2
- package/dist/modules/staff/lib/timesheets-ui/TimerBar.js +47 -2
- package/dist/modules/staff/lib/timesheets-ui/TimerBar.js.map +2 -2
- package/dist/modules/staff/migrations/Migration20260813073131_staff.js +79 -0
- package/dist/modules/staff/migrations/Migration20260813073131_staff.js.map +7 -0
- package/dist/modules/staff/migrations/Migration20260824143357_staff.js +15 -0
- package/dist/modules/staff/migrations/Migration20260824143357_staff.js.map +7 -0
- package/dist/modules/staff/notifications.client.js +16 -0
- package/dist/modules/staff/notifications.client.js.map +7 -0
- package/dist/modules/staff/notifications.handlers.js +80 -0
- package/dist/modules/staff/notifications.handlers.js.map +7 -0
- package/dist/modules/staff/notifications.js +168 -0
- package/dist/modules/staff/notifications.js.map +2 -2
- package/dist/modules/staff/search.js +159 -1
- package/dist/modules/staff/search.js.map +2 -2
- package/dist/modules/staff/setup.js +20 -1
- package/dist/modules/staff/setup.js.map +2 -2
- package/dist/modules/staff/subscribers/time-project-access-requested-notification.js +68 -0
- package/dist/modules/staff/subscribers/time-project-access-requested-notification.js.map +7 -0
- package/dist/modules/staff/subscribers/time-project-budget-threshold-notification.js +149 -0
- package/dist/modules/staff/subscribers/time-project-budget-threshold-notification.js.map +7 -0
- package/dist/modules/staff/subscribers/time-report-approved-notification.js +78 -0
- package/dist/modules/staff/subscribers/time-report-approved-notification.js.map +7 -0
- package/dist/modules/staff/subscribers/time-report-portal-broadcast.js +78 -0
- package/dist/modules/staff/subscribers/time-report-portal-broadcast.js.map +7 -0
- package/dist/modules/staff/translations.js +4 -1
- package/dist/modules/staff/translations.js.map +2 -2
- package/dist/modules/staff/widgets/components.js +7 -0
- package/dist/modules/staff/widgets/components.js.map +7 -0
- package/dist/modules/staff/widgets/injection/timer-sidebar-indicator/widget.js +5 -4
- package/dist/modules/staff/widgets/injection/timer-sidebar-indicator/widget.js.map +2 -2
- package/dist/modules/staff/widgets/notifications/TimeProjectAccessRequestedRenderer.js +135 -0
- package/dist/modules/staff/widgets/notifications/TimeProjectAccessRequestedRenderer.js.map +7 -0
- package/dist/modules/staff/widgets/notifications/index.js +5 -0
- package/dist/modules/staff/widgets/notifications/index.js.map +7 -0
- package/dist/modules/staff/workers/timesheets-reapply-rounding.js +43 -0
- package/dist/modules/staff/workers/timesheets-reapply-rounding.js.map +7 -0
- package/generated/entities/staff_time_entry/index.ts +7 -0
- package/generated/entities/staff_time_entry_tag/index.ts +6 -0
- package/generated/entities/staff_time_project/index.ts +8 -0
- package/generated/entities/staff_time_report/index.ts +27 -0
- package/generated/entities/staff_time_report_entry/index.ts +12 -0
- package/generated/entities/staff_time_report_event/index.ts +9 -0
- package/generated/entities/staff_time_report_project/index.ts +6 -0
- package/generated/entities/staff_time_tag/index.ts +9 -0
- package/generated/entities/staff_time_task/index.ts +17 -0
- package/generated/entities/staff_time_task_comment/index.ts +9 -0
- package/generated/entities/staff_time_task_status/index.ts +13 -0
- package/generated/entities/staff_time_task_tag/index.ts +6 -0
- package/generated/entities.ids.generated.ts +11 -1
- package/generated/entity-fields-registry.ts +149 -0
- package/package.json +7 -7
- package/src/helpers/integration/timesheetFixtures.ts +31 -1
- package/src/modules/auth/README.md +1 -1
- package/src/modules/auth/cli.ts +35 -1
- package/src/modules/auth/i18n/de.json +7 -0
- package/src/modules/auth/i18n/en.json +7 -0
- package/src/modules/auth/i18n/es.json +7 -0
- package/src/modules/auth/i18n/ko.json +7 -0
- package/src/modules/auth/i18n/pl.json +7 -0
- package/src/modules/auth/lib/backendChrome.tsx +1 -0
- package/src/modules/customer_accounts/lib/customerRoleAcls.ts +79 -0
- package/src/modules/customer_accounts/setup.ts +1 -45
- package/src/modules/data_sync/AGENTS.md +34 -0
- package/src/modules/data_sync/api/options.ts +2 -0
- package/src/modules/data_sync/backend/data-sync/page.tsx +82 -33
- package/src/modules/data_sync/i18n/de.json +1 -0
- package/src/modules/data_sync/i18n/en.json +1 -0
- package/src/modules/data_sync/i18n/es.json +1 -0
- package/src/modules/data_sync/i18n/ko.json +1 -0
- package/src/modules/data_sync/i18n/pl.json +1 -0
- package/src/modules/data_sync/lib/adapter.ts +45 -0
- package/src/modules/data_sync/lib/start-controls.ts +89 -0
- package/src/modules/staff/AGENTS.md +769 -1
- package/src/modules/staff/acl.ts +9 -0
- package/src/modules/staff/ai-agents.ts +189 -0
- package/src/modules/staff/ai-tools/time-tracking-pack.ts +494 -0
- package/src/modules/staff/ai-tools/types.ts +82 -0
- package/src/modules/staff/ai-tools.ts +13 -0
- package/src/modules/staff/analytics.ts +113 -0
- package/src/modules/staff/api/guards.ts +85 -3
- package/src/modules/staff/api/leave-requests/accept/route.ts +0 -2
- package/src/modules/staff/api/leave-requests/reject/route.ts +0 -2
- package/src/modules/staff/api/portal/time-reports/[id]/route.ts +182 -0
- package/src/modules/staff/api/portal/time-reports/route.ts +270 -0
- package/src/modules/staff/api/team-members/self/route.ts +0 -2
- package/src/modules/staff/api/team-members/tags/assign/route.ts +0 -2
- package/src/modules/staff/api/team-members/tags/unassign/route.ts +0 -2
- package/src/modules/staff/api/timesheets/_shared/withTimesheetInterceptors.ts +292 -0
- package/src/modules/staff/api/timesheets/access-requests/route.ts +299 -0
- package/src/modules/staff/api/timesheets/my-projects/[projectId]/route.ts +15 -7
- package/src/modules/staff/api/timesheets/my-projects/route.ts +14 -1
- package/src/modules/staff/api/timesheets/my-work/myWorkAggregate.ts +90 -0
- package/src/modules/staff/api/timesheets/my-work/route.ts +475 -0
- package/src/modules/staff/api/timesheets/projects/kpis/route.ts +15 -2
- package/src/modules/staff/api/timesheets/reports/[id]/close/route.ts +203 -0
- package/src/modules/staff/api/timesheets/reports/[id]/export/route.ts +315 -0
- package/src/modules/staff/api/timesheets/reports/[id]/sheet/route.ts +220 -0
- package/src/modules/staff/api/timesheets/reports/[id]/unlock/route.ts +208 -0
- package/src/modules/staff/api/timesheets/reports/preview/route.ts +449 -0
- package/src/modules/staff/api/timesheets/reports/route.ts +386 -0
- package/src/modules/staff/api/timesheets/reports/shared.ts +121 -0
- package/src/modules/staff/api/timesheets/settings/reapply-rounding/route.ts +205 -0
- package/src/modules/staff/api/timesheets/settings/rounding-impact/route.ts +226 -0
- package/src/modules/staff/api/timesheets/settings/route.ts +242 -0
- package/src/modules/staff/api/timesheets/tags/access.ts +134 -0
- package/src/modules/staff/api/timesheets/tags/entry-assignments/route.ts +290 -0
- package/src/modules/staff/api/timesheets/tags/route.ts +177 -0
- package/src/modules/staff/api/timesheets/tags/task-assignments/route.ts +294 -0
- package/src/modules/staff/api/timesheets/task-statuses/route.ts +232 -0
- package/src/modules/staff/api/timesheets/tasks/[id]/comments/route.ts +532 -0
- package/src/modules/staff/api/timesheets/tasks/[id]/status/route.ts +271 -0
- package/src/modules/staff/api/timesheets/tasks/route.ts +443 -0
- package/src/modules/staff/api/timesheets/time-entries/[id]/duplicate/route.ts +306 -0
- package/src/modules/staff/api/timesheets/time-entries/[id]/segments/[segmentId]/route.ts +32 -10
- package/src/modules/staff/api/timesheets/time-entries/[id]/segments/route.ts +32 -17
- package/src/modules/staff/api/timesheets/time-entries/[id]/timer-start/route.ts +27 -6
- package/src/modules/staff/api/timesheets/time-entries/[id]/timer-stop/route.ts +36 -6
- package/src/modules/staff/api/timesheets/time-entries/bulk/route.ts +331 -34
- package/src/modules/staff/api/timesheets/time-entries/copy-day/route.ts +533 -0
- package/src/modules/staff/api/timesheets/time-entries/overlaps/route.ts +457 -0
- package/src/modules/staff/api/timesheets/time-entries/route.ts +378 -24
- package/src/modules/staff/api/timesheets/time-entries/start-timer/route.ts +31 -9
- package/src/modules/staff/api/timesheets/time-projects/[id]/change-currency/route.ts +246 -0
- package/src/modules/staff/api/timesheets/time-projects/[id]/employees/route.ts +62 -2
- package/src/modules/staff/api/timesheets/time-projects/route.ts +397 -22
- package/src/modules/staff/backend/staff/time-tracking/board/page.meta.ts +14 -0
- package/src/modules/staff/backend/staff/time-tracking/board/page.tsx +151 -0
- package/src/modules/staff/backend/staff/time-tracking/entries/page.meta.ts +14 -0
- package/src/modules/staff/backend/staff/time-tracking/entries/page.tsx +1229 -0
- package/src/modules/staff/backend/staff/time-tracking/page.meta.ts +11 -0
- package/src/modules/staff/backend/staff/time-tracking/page.tsx +823 -0
- package/src/modules/staff/backend/staff/time-tracking/projects/[id]/board/page.meta.ts +12 -0
- package/src/modules/staff/backend/staff/time-tracking/projects/[id]/board/page.tsx +81 -0
- package/src/modules/staff/backend/staff/time-tracking/projects/[id]/edit/page.meta.ts +12 -0
- package/src/modules/staff/backend/staff/time-tracking/projects/[id]/edit/page.tsx +275 -0
- package/src/modules/staff/backend/staff/time-tracking/projects/[id]/page.meta.ts +12 -0
- package/src/modules/staff/backend/staff/time-tracking/projects/[id]/page.tsx +1420 -0
- package/src/modules/staff/backend/staff/time-tracking/projects/create/page.meta.ts +12 -0
- package/src/modules/staff/backend/staff/time-tracking/projects/create/page.tsx +109 -0
- package/src/modules/staff/backend/staff/time-tracking/projects/page.meta.ts +15 -0
- package/src/modules/staff/backend/staff/time-tracking/projects/page.tsx +1254 -0
- package/src/modules/staff/backend/staff/time-tracking/projects/projectFormConfig.ts +466 -0
- package/src/modules/staff/backend/staff/time-tracking/reports/[id]/page.meta.ts +12 -0
- package/src/modules/staff/backend/staff/time-tracking/reports/[id]/page.tsx +632 -0
- package/src/modules/staff/backend/staff/time-tracking/reports/create/page.meta.ts +12 -0
- package/src/modules/staff/backend/staff/time-tracking/reports/create/page.tsx +867 -0
- package/src/modules/staff/backend/staff/time-tracking/reports/page.meta.ts +14 -0
- package/src/modules/staff/backend/staff/time-tracking/reports/page.tsx +246 -0
- package/src/modules/staff/backend/staff/time-tracking/settings/page.meta.ts +14 -0
- package/src/modules/staff/backend/staff/time-tracking/settings/page.tsx +701 -0
- package/src/modules/staff/backend/staff/time-tracking/timesheet/GridView.tsx +953 -0
- package/src/modules/staff/backend/staff/time-tracking/timesheet/page.meta.ts +14 -0
- package/src/modules/staff/backend/staff/time-tracking/timesheet/page.tsx +800 -0
- package/src/modules/staff/backend/staff/timesheets/page.meta.ts +6 -6
- package/src/modules/staff/backend/staff/timesheets/page.tsx +12 -1010
- package/src/modules/staff/backend/staff/timesheets/projects/[id]/edit/page.meta.ts +0 -6
- package/src/modules/staff/backend/staff/timesheets/projects/[id]/edit/page.tsx +4 -158
- package/src/modules/staff/backend/staff/timesheets/projects/[id]/page.meta.ts +0 -6
- package/src/modules/staff/backend/staff/timesheets/projects/[id]/page.tsx +4 -609
- package/src/modules/staff/backend/staff/timesheets/projects/create/page.meta.ts +0 -6
- package/src/modules/staff/backend/staff/timesheets/projects/create/page.tsx +3 -89
- package/src/modules/staff/backend/staff/timesheets/projects/page.meta.ts +2 -19
- package/src/modules/staff/backend/staff/timesheets/projects/page.tsx +3 -778
- package/src/modules/staff/ce.ts +63 -0
- package/src/modules/staff/cli.ts +207 -1
- package/src/modules/staff/commands/index.ts +5 -0
- package/src/modules/staff/commands/timesheets-entries.ts +1006 -28
- package/src/modules/staff/commands/timesheets-projects.ts +593 -7
- package/src/modules/staff/commands/timesheets-reports.ts +1329 -0
- package/src/modules/staff/commands/timesheets-tags.ts +1315 -0
- package/src/modules/staff/commands/timesheets-task-comments.ts +583 -0
- package/src/modules/staff/commands/timesheets-task-statuses.ts +889 -0
- package/src/modules/staff/commands/timesheets-tasks.ts +1121 -0
- package/src/modules/staff/data/enrichers.ts +675 -10
- package/src/modules/staff/data/entities.ts +494 -2
- package/src/modules/staff/data/extensions.ts +68 -0
- package/src/modules/staff/data/validators.ts +348 -3
- package/src/modules/staff/di.ts +96 -0
- package/src/modules/staff/events.payloads.ts +268 -0
- package/src/modules/staff/events.ts +87 -5
- package/src/modules/staff/extension-points.ts +308 -0
- package/src/modules/staff/frontend/[orgSlug]/portal/time-reports/[id]/page.meta.ts +10 -0
- package/src/modules/staff/frontend/[orgSlug]/portal/time-reports/[id]/page.tsx +225 -0
- package/src/modules/staff/frontend/[orgSlug]/portal/time-reports/page.meta.ts +17 -0
- package/src/modules/staff/frontend/[orgSlug]/portal/time-reports/page.tsx +182 -0
- package/src/modules/staff/i18n/de.json +899 -0
- package/src/modules/staff/i18n/en.json +899 -0
- package/src/modules/staff/i18n/es.json +899 -0
- package/src/modules/staff/i18n/ko.json +899 -0
- package/src/modules/staff/i18n/pl.json +899 -0
- package/src/modules/staff/lib/crud.ts +14 -3
- package/src/modules/staff/lib/time-tracking/access.ts +155 -0
- package/src/modules/staff/lib/time-tracking/billability.ts +68 -0
- package/src/modules/staff/lib/time-tracking/capacity.ts +124 -0
- package/src/modules/staff/lib/time-tracking/componentContracts.ts +26 -0
- package/src/modules/staff/lib/time-tracking/cost.ts +144 -0
- package/src/modules/staff/lib/time-tracking/duration.ts +68 -0
- package/src/modules/staff/lib/time-tracking/featureAccess.ts +115 -0
- package/src/modules/staff/lib/time-tracking/interval.ts +90 -0
- package/src/modules/staff/lib/time-tracking/migrateProjectCodes.ts +161 -0
- package/src/modules/staff/lib/time-tracking/moneyVisibility.ts +37 -0
- package/src/modules/staff/lib/time-tracking/overlap.ts +159 -0
- package/src/modules/staff/lib/time-tracking/portalRecipients.ts +76 -0
- package/src/modules/staff/lib/time-tracking/portalReports.ts +31 -0
- package/src/modules/staff/lib/time-tracking/projectCode.ts +221 -0
- package/src/modules/staff/lib/time-tracking/reapplyRounding.ts +313 -0
- package/src/modules/staff/lib/time-tracking/recalculationRunner.ts +118 -0
- package/src/modules/staff/lib/time-tracking/recalculations.ts +135 -0
- package/src/modules/staff/lib/time-tracking/registries/invoke.ts +122 -0
- package/src/modules/staff/lib/time-tracking/registries/registry.ts +124 -0
- package/src/modules/staff/lib/time-tracking/registries/scope.ts +56 -0
- package/src/modules/staff/lib/time-tracking/rollup.ts +59 -0
- package/src/modules/staff/lib/time-tracking/rounding.ts +126 -0
- package/src/modules/staff/lib/time-tracking/roundingImpact.ts +75 -0
- package/src/modules/staff/lib/time-tracking/settingKeys.ts +274 -0
- package/src/modules/staff/lib/time-tracking/settings.ts +168 -0
- package/src/modules/staff/lib/time-tracking/sqlInClause.ts +22 -0
- package/src/modules/staff/lib/time-tracking/timeEntrySources.ts +85 -0
- package/src/modules/staff/lib/time-tracking-ui/BoardFilterChips.tsx +360 -0
- package/src/modules/staff/lib/time-tracking-ui/CustomerPicker.tsx +427 -0
- package/src/modules/staff/lib/time-tracking-ui/DurationInput.tsx +192 -0
- package/src/modules/staff/lib/time-tracking-ui/EntryDetailsFields.tsx +224 -0
- package/src/modules/staff/lib/time-tracking-ui/EntryFilterBar.tsx +144 -0
- package/src/modules/staff/lib/time-tracking-ui/KanbanBoard.tsx +1065 -0
- package/src/modules/staff/lib/time-tracking-ui/KanbanCard.tsx +435 -0
- package/src/modules/staff/lib/time-tracking-ui/KanbanColumn.tsx +316 -0
- package/src/modules/staff/lib/time-tracking-ui/NewTaskDialog.tsx +291 -0
- package/src/modules/staff/lib/time-tracking-ui/NoProjectAccess.tsx +162 -0
- package/src/modules/staff/lib/time-tracking-ui/PeriodSelector.tsx +149 -0
- package/src/modules/staff/lib/time-tracking-ui/PhasePlaceholder.tsx +31 -0
- package/src/modules/staff/lib/time-tracking-ui/ProjectCodeField.tsx +230 -0
- package/src/modules/staff/lib/time-tracking-ui/ProjectCurrencyField.tsx +432 -0
- package/src/modules/staff/lib/time-tracking-ui/ProjectFormSections.tsx +323 -0
- package/src/modules/staff/lib/time-tracking-ui/ProjectTeamDrawer.tsx +930 -0
- package/src/modules/staff/lib/time-tracking-ui/ReportSheet.tsx +357 -0
- package/src/modules/staff/lib/time-tracking-ui/TagPicker.tsx +195 -0
- package/src/modules/staff/lib/time-tracking-ui/TaskBoardScreen.tsx +243 -0
- package/src/modules/staff/lib/time-tracking-ui/TaskCommentThread.tsx +137 -0
- package/src/modules/staff/lib/time-tracking-ui/TaskDrawer.tsx +1405 -0
- package/src/modules/staff/lib/time-tracking-ui/TaskListView.tsx +260 -0
- package/src/modules/staff/lib/time-tracking-ui/TaskPicker.tsx +435 -0
- package/src/modules/staff/lib/time-tracking-ui/TaskQuickLog.tsx +296 -0
- package/src/modules/staff/lib/time-tracking-ui/TimeEntriesSummaryFooter.tsx +97 -0
- package/src/modules/staff/lib/time-tracking-ui/TimeEntryDialog.tsx +1929 -0
- package/src/modules/staff/lib/time-tracking-ui/TimeEntryDurationCell.tsx +126 -0
- package/src/modules/staff/lib/time-tracking-ui/TimesheetCalendar.tsx +265 -0
- package/src/modules/staff/lib/time-tracking-ui/TimesheetPeriodFooter.tsx +78 -0
- package/src/modules/staff/lib/time-tracking-ui/TimesheetViewSwitch.tsx +70 -0
- package/src/modules/staff/lib/time-tracking-ui/boardDirectory.ts +231 -0
- package/src/modules/staff/lib/time-tracking-ui/boardFilters.ts +128 -0
- package/src/modules/staff/lib/time-tracking-ui/boardSummary.ts +20 -0
- package/src/modules/staff/lib/time-tracking-ui/kanbanBoardData.ts +228 -0
- package/src/modules/staff/lib/time-tracking-ui/projectTeamAssignment.ts +143 -0
- package/src/modules/staff/lib/time-tracking-ui/taskDrawerData.ts +194 -0
- package/src/modules/staff/lib/time-tracking-ui/timeEntryDialogState.ts +400 -0
- package/src/modules/staff/lib/time-tracking-ui/timeEntryListData.ts +271 -0
- package/src/modules/staff/lib/time-tracking-ui/timeTrackingSettingsForm.ts +168 -0
- package/src/modules/staff/lib/time-tracking-ui/timesheetData.ts +225 -0
- package/src/modules/staff/lib/time-tracking-ui/timesheetPeriod.ts +278 -0
- package/src/modules/staff/lib/time-tracking-ui/timesheetTargets.ts +93 -0
- package/src/modules/staff/lib/time-tracking-ui/useBoardFilters.ts +107 -0
- package/src/modules/staff/lib/time-tracking-ui/useTimesheetPreferences.ts +245 -0
- package/src/modules/staff/lib/timeTrackingSeeds.ts +1037 -0
- package/src/modules/staff/lib/timesheets/timeEntryCacheInvalidation.ts +11 -10
- package/src/modules/staff/lib/timesheets/timeEntryDecoration.ts +256 -0
- package/src/modules/staff/lib/timesheets-projects/budgetBurn.ts +60 -0
- package/src/modules/staff/lib/timesheets-projects/budgetThreshold.ts +135 -0
- package/src/modules/staff/lib/timesheets-projects/budgetThresholdState.ts +137 -0
- package/src/modules/staff/lib/timesheets-projects/computeProjectFinancials.ts +142 -0
- package/src/modules/staff/lib/timesheets-projects/reportSelection.ts +82 -0
- package/src/modules/staff/lib/timesheets-projects-ui/AssignMembersDialog.tsx +237 -0
- package/src/modules/staff/lib/timesheets-projects-ui/ProjectBudgetCell.tsx +33 -0
- package/src/modules/staff/lib/timesheets-projects-ui/ProjectCard.tsx +56 -1
- package/src/modules/staff/lib/timesheets-projects-ui/ProjectsBulkActionsBar.tsx +90 -0
- package/src/modules/staff/lib/timesheets-reports/buildReportSheet.ts +125 -0
- package/src/modules/staff/lib/timesheets-reports/loadReportData.ts +251 -0
- package/src/modules/staff/lib/timesheets-reports/pdf.ts +263 -0
- package/src/modules/staff/lib/timesheets-reports/reportApprovalPolicies.ts +137 -0
- package/src/modules/staff/lib/timesheets-reports/reportConfigData.ts +247 -0
- package/src/modules/staff/lib/timesheets-reports/reportConfigState.ts +89 -0
- package/src/modules/staff/lib/timesheets-reports/reportExport.ts +373 -0
- package/src/modules/staff/lib/timesheets-reports/reportExportFormats.ts +71 -0
- package/src/modules/staff/lib/timesheets-reports/reportGroupings.ts +153 -0
- package/src/modules/staff/lib/timesheets-reports/reportLabels.ts +23 -0
- package/src/modules/staff/lib/timesheets-reports/reportReference.ts +146 -0
- package/src/modules/staff/lib/timesheets-reports/reportRows.ts +89 -0
- package/src/modules/staff/lib/timesheets-reports/reportSheetData.ts +196 -0
- package/src/modules/staff/lib/timesheets-reports/reportTotals.ts +544 -0
- package/src/modules/staff/lib/timesheets-reports/xlsx.ts +207 -0
- package/src/modules/staff/lib/timesheets-tasks/computeTaskRollups.ts +238 -0
- package/src/modules/staff/lib/timesheets-tasks/defaultStatusTemplate.ts +90 -0
- package/src/modules/staff/lib/timesheets-tasks/seedProjectStatuses.ts +43 -0
- package/src/modules/staff/lib/timesheets-tasks/statusPositions.ts +132 -0
- package/src/modules/staff/lib/timesheets-tasks/statusSlug.ts +43 -0
- package/src/modules/staff/lib/timesheets-tasks/taskHierarchy.ts +99 -0
- package/src/modules/staff/lib/timesheets-tasks/taskHoursTotals.ts +42 -0
- package/src/modules/staff/lib/timesheets-tasks/taskReference.ts +108 -0
- package/src/modules/staff/lib/timesheets-ui/CreateProjectDialog.tsx +18 -4
- package/src/modules/staff/lib/timesheets-ui/ListView.tsx +504 -149
- package/src/modules/staff/lib/timesheets-ui/TimerBar.tsx +50 -2
- package/src/modules/staff/migrations/.snapshot-open-mercato.json +3244 -228
- package/src/modules/staff/migrations/Migration20260813073131_staff.ts +94 -0
- package/src/modules/staff/migrations/Migration20260824143357_staff.ts +17 -0
- package/src/modules/staff/notifications.client.ts +16 -0
- package/src/modules/staff/notifications.handlers.ts +88 -0
- package/src/modules/staff/notifications.ts +163 -0
- package/src/modules/staff/search.ts +191 -1
- package/src/modules/staff/setup.ts +22 -0
- package/src/modules/staff/subscribers/time-project-access-requested-notification.ts +95 -0
- package/src/modules/staff/subscribers/time-project-budget-threshold-notification.ts +226 -0
- package/src/modules/staff/subscribers/time-report-approved-notification.ts +116 -0
- package/src/modules/staff/subscribers/time-report-portal-broadcast.ts +120 -0
- package/src/modules/staff/translations.ts +17 -0
- package/src/modules/staff/widgets/components.ts +34 -0
- package/src/modules/staff/widgets/injection/timer-sidebar-indicator/widget.tsx +6 -5
- package/src/modules/staff/widgets/notifications/TimeProjectAccessRequestedRenderer.tsx +138 -0
- package/src/modules/staff/widgets/notifications/index.ts +1 -0
- package/src/modules/staff/workers/timesheets-reapply-rounding.ts +59 -0
- package/dist/modules/staff/backend/staff/timesheets/projects/projectFormConfig.js +0 -123
- package/dist/modules/staff/backend/staff/timesheets/projects/projectFormConfig.js.map +0 -7
- package/src/modules/staff/backend/staff/timesheets/projects/projectFormConfig.ts +0 -138
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../../../../../src/modules/staff/backend/staff/time-tracking/projects/%5Bid%5D/page.tsx"],
|
|
4
|
+
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { Page, PageBody } from '@open-mercato/ui/backend/Page'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { Badge } from '@open-mercato/ui/primitives/badge'\nimport { Spinner } from '@open-mercato/ui/primitives/spinner'\nimport { Input } from '@open-mercato/ui/primitives/input'\nimport { Dialog, DialogContent, DialogHeader, DialogTitle } from '@open-mercato/ui/primitives/dialog'\nimport { LookupSelect } from '@open-mercato/ui/backend/inputs/LookupSelect'\nimport { Tabs, TabsContent, TabsList, TabsTrigger } from '@open-mercato/ui/primitives/tabs'\nimport { apiCall, readApiResultOrThrow } from '@open-mercato/ui/backend/utils/apiCall'\nimport { deleteCrud, createCrud } from '@open-mercato/ui/backend/utils/crud'\nimport { useConfirmDialog } from '@open-mercato/ui/backend/confirm-dialog'\nimport { surfaceRecordConflict } from '@open-mercato/ui/backend/conflicts'\nimport { useGuardedMutation } from '@open-mercato/ui/backend/injection/useGuardedMutation'\nimport { InjectionSpot, useInjectionWidgets } from '@open-mercato/ui/backend/injection/InjectionSpot'\nimport { extensionPoints } from '@open-mercato/core/modules/staff/extension-points'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { useOrganizationScopeVersion } from '@open-mercato/shared/lib/frontend/useOrganizationScope'\nimport { LoadingMessage } from '@open-mercato/ui/backend/detail'\nimport { ErrorMessage, RecordNotFoundState } from '@open-mercato/ui/backend/detail'\nimport { ArrowLeft, Plus, Pencil, ChevronDown, Trash2, User, Users } from 'lucide-react'\nimport Link from 'next/link'\nimport { usePathname, useRouter, useSearchParams } from 'next/navigation'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\nimport { formatCurrency } from '@open-mercato/ui/utils/format'\nimport { KpiCard, Sparkline } from '@open-mercato/ui/backend/charts'\nimport { ProjectTeamDrawer } from '../../../../../lib/time-tracking-ui/ProjectTeamDrawer'\nimport { NoProjectAccess } from '../../../../../lib/time-tracking-ui/NoProjectAccess'\nimport { ProjectBudgetCell } from '../../../../../lib/timesheets-projects-ui/ProjectBudgetCell'\nimport { computeBudgetBurn } from '../../../../../lib/timesheets-projects/budgetBurn'\nimport { resolveProjectColorHex } from '../../../../../lib/timesheets-ui/colors'\n\nconst logger = createLogger('staff')\n\nconst BACK_HREF = '/backend/staff/time-tracking/projects'\n\nconst EMPLOYEE_MUTATION_CONTEXT_ID = 'staff-time-project-detail:employees'\nconst EMPLOYEE_RESOURCE_KIND = 'staff.time_project_member'\n\ntype EmployeeMutationContext = {\n formId: string\n resourceKind: string\n resourceId: string\n retryLastMutation: () => Promise<boolean>\n}\n\n/**\n * Mirrors `NO_PROJECT_ACCESS_REASON` from\n * `api/timesheets/time-projects/route.ts`. The route module is server-only, so\n * the literal is restated here rather than imported into the client bundle;\n * `__tests__/no-project-access-contract.test.ts` pins the two together.\n */\nconst NO_PROJECT_ACCESS_REASON = 'no_project_access'\n\nconst PROJECT_ENTITY_ID = 'staff:staff_time_project'\n\ntype ProjectDetailInjectionContext = {\n entityId: string\n recordId: string | null\n projectId: string | null\n path: string | null\n retryLastMutation: () => Promise<boolean>\n}\n\ntype ProjectRecord = {\n id: string\n name: string\n code: string\n description?: string | null\n projectType?: string | null\n project_type?: string | null\n startDate?: string | null\n start_date?: string | null\n costCenter?: string | null\n cost_center?: string | null\n status?: string | null\n customerId?: string | null\n customer_id?: string | null\n} & Record<string, unknown>\n\ntype ProjectResponse = {\n items?: ProjectRecord[]\n}\n\nfunction readDenialReason(payload: unknown): string | null {\n if (!payload || typeof payload !== 'object') return null\n const reason = (payload as { reason?: unknown }).reason\n return typeof reason === 'string' ? reason : null\n}\n\ntype EmployeeAssignment = {\n id: string\n staffMemberId: string\n role: string | null\n status: string | null\n assignedStartDate: string | null\n assignedEndDate: string | null\n displayName: string | null\n teamName: string | null\n}\n\ntype EmployeesResponse = {\n items?: Array<Record<string, unknown>>\n total?: number\n}\n\ntype StaffMemberRecord = {\n id: string\n display_name?: string\n displayName?: string\n team?: { id: string; name: string } | null\n}\n\ntype StaffMembersResponse = {\n items?: StaffMemberRecord[]\n}\n\ntype ProjectEntryRow = {\n id: string\n date: string\n description: string | null\n durationMinutes: number\n isBillable: boolean\n taskId: string | null\n staffMemberId: string | null\n}\n\ntype ProjectTaskRow = {\n id: string\n reference: string | null\n title: string\n statusId: string | null\n assigneeStaffMemberId: string | null\n loggedMinutes: number | null\n}\n\n/**\n * A group heading inside the reference rail. The rail replaces three full-width\n * cards, so the groups carry the separation those card borders used to.\n */\nfunction RailGroup({ label, first }: { label: string; first?: boolean }) {\n return (\n <div\n className={`border-b pb-1.5 text-overline font-semibold uppercase tracking-widest text-muted-foreground ${\n first ? '' : 'pt-4'\n }`}\n >\n {label}\n </div>\n )\n}\n\nfunction RailFact({ label, value }: { label: string; value: React.ReactNode }) {\n return (\n <div className=\"flex items-baseline justify-between gap-3 border-b py-2 text-sm last:border-b-0\">\n <dt className=\"shrink-0 text-muted-foreground\">{label}</dt>\n <dd className=\"text-right font-medium tabular-nums\">{value}</dd>\n </div>\n )\n}\n\nexport default function TimesheetProjectDetailPage({ params }: { params?: { id?: string } }) {\n const projectId = params?.id\n const t = useT()\n const scopeVersion = useOrganizationScopeVersion()\n const { confirm, ConfirmDialogElement } = useConfirmDialog()\n const router = useRouter()\n const pathname = usePathname()\n const searchParams = useSearchParams()\n\n const [project, setProject] = React.useState<ProjectRecord | null>(null)\n const [loading, setLoading] = React.useState(true)\n const [error, setError] = React.useState<string | null>(null)\n const [isNotFound, setIsNotFound] = React.useState(false)\n const [accessDenied, setAccessDenied] = React.useState(false)\n\n const [employees, setEmployees] = React.useState<EmployeeAssignment[]>([])\n const [employeesLoading, setEmployeesLoading] = React.useState(false)\n const [expandedCards, setExpandedCards] = React.useState<Set<string>>(new Set())\n const [reloadToken, setReloadToken] = React.useState(0)\n\n // Tabs govern the working column only \u2014 the details rail stays visible on every\n // tab, because reference facts are what you check *while* reading the tab you\n // are on, not a destination of their own.\n const [activeTab, setActiveTab] = React.useState<string>('team')\n const [recentEntries, setRecentEntries] = React.useState<ProjectEntryRow[]>([])\n const [recentEntriesLoading, setRecentEntriesLoading] = React.useState(false)\n const [projectTasks, setProjectTasks] = React.useState<ProjectTaskRow[]>([])\n const [projectTasksLoading, setProjectTasksLoading] = React.useState(false)\n\n const [canManageProjects, setCanManageProjects] = React.useState(false)\n // Money is gated on the same feature the portfolio gates it on; a rate hidden\n // in the list must not be readable one click deeper.\n const [canSeeMoney, setCanSeeMoney] = React.useState(false)\n\n React.useEffect(() => {\n let cancelled = false\n void (async () => {\n try {\n const res = await apiCall<{ ok: boolean; granted: string[] }>('/api/auth/feature-check', {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({\n features: ['staff.timesheets.projects.manage', 'staff.timesheets.rates.view'],\n }),\n })\n if (!cancelled) {\n const granted = new Set(res.result?.granted ?? [])\n setCanManageProjects(granted.has('staff.timesheets.projects.manage'))\n setCanSeeMoney(granted.has('staff.timesheets.rates.view'))\n }\n } catch {\n // default: no manage access, no money\n }\n })()\n return () => { cancelled = true }\n }, [])\n\n const [addDialogOpen, setAddDialogOpen] = React.useState(false)\n const [addStaffMemberId, setAddStaffMemberId] = React.useState<string | null>(null)\n const [addRole, setAddRole] = React.useState('')\n const [addStartDate, setAddStartDate] = React.useState('')\n const [addSaving, setAddSaving] = React.useState(false)\n const [staffLookupError, setStaffLookupError] = React.useState<string | null>(null)\n\n // Fetched per tab rather than up front: a project page opened to check a rate\n // should not pay for two lists nobody looked at.\n React.useEffect(() => {\n if (!projectId || activeTab !== 'time') return\n let cancelled = false\n void (async () => {\n setRecentEntriesLoading(true)\n try {\n const res = await apiCall<{ items?: Array<Record<string, unknown>> }>(\n `/api/staff/timesheets/time-entries?projectId=${encodeURIComponent(projectId)}&page=1&pageSize=25&sortField=date&sortDir=desc`,\n )\n if (cancelled || !res.ok) return\n const items = Array.isArray(res.result?.items) ? res.result.items : []\n setRecentEntries(\n items\n .map((row): ProjectEntryRow | null => {\n const id = typeof row.id === 'string' ? row.id : null\n if (!id) return null\n const raw = row as Record<string, unknown>\n const minutes = raw.duration_minutes ?? raw.durationMinutes\n return {\n id,\n date: typeof raw.date === 'string' ? raw.date.slice(0, 10) : '',\n description:\n typeof raw.description === 'string'\n ? raw.description\n : typeof raw.notes === 'string'\n ? raw.notes\n : null,\n durationMinutes: typeof minutes === 'number' ? minutes : Number(minutes ?? 0) || 0,\n isBillable: raw.is_billable === true || raw.isBillable === true,\n taskId: typeof (raw.task_id ?? raw.taskId) === 'string' ? String(raw.task_id ?? raw.taskId) : null,\n staffMemberId:\n typeof (raw.staff_member_id ?? raw.staffMemberId) === 'string'\n ? String(raw.staff_member_id ?? raw.staffMemberId)\n : null,\n }\n })\n .filter((row): row is ProjectEntryRow => row !== null),\n )\n } catch (err) {\n logger.warn('staff.timesheets.projects.detail recent entries failed', { err })\n } finally {\n if (!cancelled) setRecentEntriesLoading(false)\n }\n })()\n return () => { cancelled = true }\n }, [projectId, activeTab, reloadToken])\n\n React.useEffect(() => {\n if (!projectId || activeTab !== 'tasks') return\n let cancelled = false\n void (async () => {\n setProjectTasksLoading(true)\n try {\n const res = await apiCall<{ items?: Array<Record<string, unknown>> }>(\n // `timeProjectId`, not `projectId`: the tasks list schema declares the\n // former and is `.passthrough()`, so the wrong name is accepted, never\n // read, and the tab silently lists every task in the tenant.\n `/api/staff/timesheets/tasks?timeProjectId=${encodeURIComponent(projectId)}&page=1&pageSize=50&sortField=reference&sortDir=asc`,\n )\n if (cancelled || !res.ok) return\n const items = Array.isArray(res.result?.items) ? res.result.items : []\n setProjectTasks(\n items\n .map((row): ProjectTaskRow | null => {\n const id = typeof row.id === 'string' ? row.id : null\n const title = typeof row.title === 'string' ? row.title : null\n if (!id || !title) return null\n const raw = row as Record<string, unknown>\n const logged = raw.loggedMinutes ?? raw.logged_minutes\n return {\n id,\n reference: typeof raw.reference === 'string' ? raw.reference : null,\n title,\n statusId:\n typeof (raw.task_status_id ?? raw.taskStatusId) === 'string'\n ? String(raw.task_status_id ?? raw.taskStatusId)\n : null,\n assigneeStaffMemberId:\n typeof (raw.assignee_staff_member_id ?? raw.assigneeStaffMemberId) === 'string'\n ? String(raw.assignee_staff_member_id ?? raw.assigneeStaffMemberId)\n : null,\n loggedMinutes: typeof logged === 'number' ? logged : null,\n }\n })\n .filter((row): row is ProjectTaskRow => row !== null),\n )\n } catch (err) {\n logger.warn('staff.timesheets.projects.detail tasks failed', { err })\n } finally {\n if (!cancelled) setProjectTasksLoading(false)\n }\n })()\n return () => { cancelled = true }\n }, [projectId, activeTab, reloadToken])\n\n const employeeNameById = React.useMemo(\n () => new Map(employees.map((emp) => [emp.staffMemberId, emp.displayName ?? ''])),\n [employees],\n )\n const activeCount = employees.filter((emp) => emp.status === 'active').length\n const inactiveCount = employees.length - activeCount\n\n // --- Load project ---\n React.useEffect(() => {\n if (!projectId) return\n let cancelled = false\n async function loadProject() {\n setLoading(true)\n setError(null)\n setIsNotFound(false)\n setAccessDenied(false)\n try {\n const queryParams = new URLSearchParams({ page: '1', pageSize: '1', ids: projectId! })\n const call = await apiCall<ProjectResponse>(\n `/api/staff/timesheets/time-projects?${queryParams.toString()}`,\n )\n // Screen 17: the route answers 404 with this discriminator for a project\n // the caller is not a member of, without naming it.\n if (call.status === 404 && readDenialReason(call.result) === NO_PROJECT_ACCESS_REASON) {\n if (!cancelled) setAccessDenied(true)\n return\n }\n if (!call.ok) {\n throw new Error(t('staff.timesheets.projects.errors.load', 'Failed to load project.'))\n }\n const payload = call.result\n const record = Array.isArray(payload?.items) ? payload.items[0] : null\n if (!record) {\n if (!cancelled) setIsNotFound(true)\n return\n }\n if (!cancelled) setProject(record)\n } catch (loadError) {\n if (!cancelled) {\n setError(loadError instanceof Error ? loadError.message : t('staff.timesheets.projects.errors.load', 'Failed to load project.'))\n }\n } finally {\n if (!cancelled) setLoading(false)\n }\n }\n loadProject()\n return () => { cancelled = true }\n }, [projectId, t, scopeVersion])\n\n // --- Load employees with name resolution ---\n const loadEmployees = React.useCallback(async () => {\n if (!projectId || accessDenied) return\n setEmployeesLoading(true)\n try {\n const payload = await readApiResultOrThrow<EmployeesResponse>(\n `/api/staff/timesheets/time-projects/${projectId}/employees?page=1&pageSize=100`,\n undefined,\n { errorMessage: t('staff.timesheets.projects.employees.empty', 'No employees assigned yet.'), fallback: { items: [], total: 0 } },\n )\n const items = Array.isArray(payload.items) ? payload.items : []\n\n const staffMemberIds = items\n .map((item) => String(item.staff_member_id ?? item.staffMemberId ?? ''))\n .filter((id) => id.length > 0)\n\n let staffMap = new Map<string, StaffMemberRecord>()\n if (staffMemberIds.length > 0) {\n try {\n const staffRes = await apiCall<StaffMembersResponse>(\n `/api/staff/team-members?ids=${staffMemberIds.join(',')}&pageSize=100`,\n )\n if (staffRes.ok) {\n const staffItems = Array.isArray(staffRes.result?.items) ? staffRes.result.items : []\n staffMap = new Map(staffItems.map((member) => [member.id, member]))\n }\n } catch {\n // name resolution failed (e.g. 403 for employees) \u2014 show IDs as fallback\n }\n }\n\n const mapped: EmployeeAssignment[] = items.map((item) => {\n const staffMemberId = String(item.staff_member_id ?? item.staffMemberId ?? '')\n const staff = staffMap.get(staffMemberId)\n return {\n id: String(item.id ?? ''),\n staffMemberId,\n role: typeof item.role === 'string' ? item.role : null,\n status: typeof item.status === 'string' ? item.status : null,\n assignedStartDate: String(item.assigned_start_date ?? item.assignedStartDate ?? ''),\n assignedEndDate: typeof (item.assigned_end_date ?? item.assignedEndDate) === 'string'\n ? String(item.assigned_end_date ?? item.assignedEndDate)\n : null,\n displayName: staff?.display_name ?? staff?.displayName ?? null,\n teamName: staff?.team?.name ?? null,\n }\n })\n\n setEmployees(mapped)\n } catch (loadError) {\n logger.error('staff.timesheets.projects.employees.list', { err: loadError })\n } finally {\n setEmployeesLoading(false)\n }\n }, [projectId, accessDenied, t])\n\n React.useEffect(() => {\n void loadEmployees()\n }, [loadEmployees, reloadToken, scopeVersion])\n\n // --- Toggle card expand/collapse ---\n const toggleCard = React.useCallback((employeeId: string) => {\n setExpandedCards((prev) => {\n const next = new Set(prev)\n if (next.has(employeeId)) next.delete(employeeId)\n else next.add(employeeId)\n return next\n })\n }, [])\n\n const { runMutation, retryLastMutation } = useGuardedMutation<EmployeeMutationContext>({\n contextId: EMPLOYEE_MUTATION_CONTEXT_ID,\n blockedMessage: t('ui.forms.flash.saveBlocked', 'Save blocked by validation'),\n })\n\n const projectInjectionContext = React.useMemo<ProjectDetailInjectionContext>(\n () => ({\n entityId: PROJECT_ENTITY_ID,\n recordId: projectId ?? null,\n projectId: projectId ?? null,\n path: pathname ?? null,\n retryLastMutation,\n }),\n [pathname, projectId, retryLastMutation],\n )\n\n const { widgets: injectedTabWidgets } = useInjectionWidgets<ProjectDetailInjectionContext>(\n extensionPoints.hosts.projectDetailTabs.spotId,\n { context: projectInjectionContext, triggerOnLoad: true },\n )\n\n const employeeMutationContext = React.useCallback(\n (resourceId: string): EmployeeMutationContext => ({\n formId: EMPLOYEE_MUTATION_CONTEXT_ID,\n resourceKind: EMPLOYEE_RESOURCE_KIND,\n resourceId,\n retryLastMutation,\n }),\n [retryLastMutation],\n )\n\n // optimistic-lock-exempt: the employee add/remove calls below mutate the\n // time-project \u2194 staff-member assignment junction (createCrud/deleteCrud of a\n // membership row), not a shared editable aggregate. Junction add/remove is exempt\n // from OSS optimistic locking per the data-integrity contract.\n // --- Remove employee ---\n const handleRemoveEmployee = React.useCallback(async (emp: EmployeeAssignment) => {\n const confirmed = await confirm({\n title: t('staff.timesheets.projects.employees.remove', 'Remove'),\n text: t('staff.timesheets.projects.employees.removeConfirm', 'Remove this employee from the project?'),\n variant: 'destructive',\n })\n if (!confirmed) return\n try {\n await runMutation({\n operation: () =>\n deleteCrud(\n `staff/timesheets/time-projects/${projectId}/employees`,\n emp.id,\n { errorMessage: t('staff.timesheets.projects.employees.removeError', 'Failed to remove employee.') },\n ),\n context: employeeMutationContext(emp.id),\n mutationPayload: { id: emp.id },\n })\n flash(t('staff.timesheets.projects.employees.removed', 'Employee removed.'), 'success')\n setReloadToken((token) => token + 1)\n } catch (removeError) {\n if (surfaceRecordConflict(removeError, t)) return\n flash(t('staff.timesheets.projects.employees.removeError', 'Failed to remove employee.'), 'error')\n }\n }, [projectId, confirm, employeeMutationContext, runMutation, t])\n\n // --- Add employee dialog ---\n const openAddDialog = React.useCallback(() => {\n setAddStaffMemberId(null)\n setAddRole('')\n setAddStartDate(new Date().toISOString().slice(0, 10))\n setStaffLookupError(null)\n setAddDialogOpen(true)\n }, [])\n\n // A failed lookup is not an empty roster: swallowing it here left the picker\n // claiming \"No team members found\" while the request had actually errored.\n const staffLookupErrorMessage = t(\n 'staff.timesheets.projects.employees.loadError',\n 'Could not load team members. Check your connection and try again.',\n )\n\n const fetchStaffMembers = React.useCallback(async (query: string) => {\n const params = new URLSearchParams({ search: query, pageSize: '20', isActive: 'true' })\n try {\n const payload = await readApiResultOrThrow<StaffMembersResponse>(\n `/api/staff/team-members?${params.toString()}`,\n undefined,\n { errorMessage: staffLookupErrorMessage, fallback: { items: [] } },\n )\n const items = Array.isArray(payload.items) ? payload.items : []\n setStaffLookupError(null)\n return items.map((member) => ({\n id: member.id,\n title: member.display_name ?? member.displayName ?? member.id,\n subtitle: member.team?.name ?? null,\n }))\n } catch (lookupError) {\n logger.error('staff.timesheets.projects.employees.lookup', { err: lookupError })\n setStaffLookupError(staffLookupErrorMessage)\n throw lookupError\n }\n }, [staffLookupErrorMessage])\n\n const handleAddEmployee = React.useCallback(async () => {\n if (!projectId || !addStaffMemberId || !addStartDate) return\n const payload = {\n staffMemberId: addStaffMemberId,\n timeProjectId: projectId,\n role: addRole.trim() || null,\n assignedStartDate: addStartDate,\n }\n setAddSaving(true)\n try {\n await runMutation({\n operation: () =>\n createCrud(`staff/timesheets/time-projects/${projectId}/employees`, payload, {\n errorMessage: t('staff.timesheets.projects.employees.addError', 'Failed to add employee.'),\n }),\n context: employeeMutationContext(addStaffMemberId),\n mutationPayload: payload,\n })\n flash(t('staff.timesheets.projects.employees.added', 'Employee added.'), 'success')\n setAddDialogOpen(false)\n setReloadToken((token) => token + 1)\n } catch (addError) {\n if (surfaceRecordConflict(addError, t)) return\n flash(addError instanceof Error ? addError.message : t('staff.timesheets.projects.employees.addError', 'Failed to add employee.'), 'error')\n } finally {\n setAddSaving(false)\n }\n }, [projectId, addStaffMemberId, addRole, addStartDate, employeeMutationContext, runMutation, t])\n\n // Screen 5: the team drawer lives behind `?panel=team` so the project form\n // link and the access-request notification (D-6, `&requesterUserId=`) can both\n // deep-link into it. Assignment is a Team-Leader affordance, so the panel only\n // mounts for holders of `staff.timesheets.projects.manage`.\n const teamPanelRequested = searchParams?.get('panel') === 'team'\n const teamDrawerOpen = teamPanelRequested && canManageProjects\n const requesterUserId = searchParams?.get('requesterUserId') ?? null\n\n const openTeamDrawer = React.useCallback(() => {\n router.replace(`${pathname}?panel=team`)\n }, [pathname, router])\n\n const handleTeamDrawerOpenChange = React.useCallback((next: boolean) => {\n if (next) openTeamDrawer()\n else router.replace(pathname ?? BACK_HREF)\n }, [openTeamDrawer, pathname, router])\n\n const handleTeamSaved = React.useCallback(() => {\n setReloadToken((token) => token + 1)\n }, [])\n\n const handleAddDialogKeyDown = React.useCallback((event: React.KeyboardEvent) => {\n if (event.key === 'Enter' && (event.metaKey || event.ctrlKey)) {\n event.preventDefault()\n void handleAddEmployee()\n }\n }, [handleAddEmployee])\n\n // --- Render ---\n if (loading) {\n return (\n <Page>\n <PageBody>\n <LoadingMessage label={t('staff.timesheets.projects.loading', 'Loading project...')} />\n </PageBody>\n </Page>\n )\n }\n\n // Screen 17 \u2014 the caller may not open this project. The guard state names\n // neither the customer nor the project (note 1), and the surrounding shell\n // (sidebar, topbar) is untouched because the caller still has other projects\n // (note 3).\n if (accessDenied) {\n return <NoProjectAccess timeProjectId={projectId} />\n }\n\n if (isNotFound) {\n return (\n <Page>\n <PageBody>\n <RecordNotFoundState\n label={t('staff.timesheets.projects.errors.notFound', 'Project not found.')}\n backHref={BACK_HREF}\n backLabel={t('staff.timesheets.projects.actions.backToList', 'Back to projects')}\n />\n </PageBody>\n </Page>\n )\n }\n\n if (error || !project) {\n return (\n <Page>\n <PageBody>\n <ErrorMessage label={error ?? t('staff.timesheets.projects.errors.load', 'Failed to load project.')} />\n </PageBody>\n </Page>\n )\n }\n\n const projectStatus = project.status ?? 'active'\n const projectType = project.projectType ?? project.project_type ?? null\n const projectStartDate = project.startDate ?? project.start_date ?? null\n const projectCode = project.code\n\n // Everything below already arrives in the list projection this page reads with\n // `?ids=`; the card used to render five of the ~22 columns and drop the rest,\n // so rate, budget, customer and cost centre were only visible by opening Edit.\n const readString = (...keys: string[]): string | null => {\n for (const key of keys) {\n const raw = (project as Record<string, unknown>)[key]\n if (typeof raw === 'string' && raw.trim().length > 0) return raw.trim()\n if (typeof raw === 'number' && Number.isFinite(raw)) return String(raw)\n }\n return null\n }\n const readNumber = (...keys: string[]): number | null => {\n for (const key of keys) {\n const raw = (project as Record<string, unknown>)[key]\n const parsed = typeof raw === 'number' ? raw : typeof raw === 'string' ? Number(raw) : NaN\n if (Number.isFinite(parsed)) return parsed\n }\n return null\n }\n const readBool = (...keys: string[]): boolean | null => {\n for (const key of keys) {\n const raw = (project as Record<string, unknown>)[key]\n if (typeof raw === 'boolean') return raw\n }\n return null\n }\n\n const enrichment = ((project as Record<string, unknown>)._staff ?? {}) as Record<string, unknown>\n const readEnriched = (key: string): number | null => {\n const raw = enrichment[key]\n return typeof raw === 'number' && Number.isFinite(raw) ? raw : null\n }\n\n const customerSnapshot = ((project as Record<string, unknown>).customerSnapshot\n ?? (project as Record<string, unknown>).customer_snapshot) as Record<string, unknown> | null | undefined\n const customerName =\n (typeof customerSnapshot?.name === 'string' ? customerSnapshot.name : null)\n ?? (typeof enrichment.customerName === 'string' ? enrichment.customerName : null)\n const customerId = readString('customerId', 'customer_id')\n const projectColor = readString('color')\n const costCenter = readString('costCenter', 'cost_center')\n const currencyCode = readString('currencyCode', 'currency_code')\n const hourlyRate = readNumber('hourlyRate', 'hourly_rate')\n const billableByDefault = readBool('billableByDefault', 'billable_by_default')\n const createdAt = readString('createdAt', 'created_at')\n const updatedAt = readString('updatedAt', 'updated_at')\n const entryCount = readNumber('entryCount')\n const currencyLocked = readBool('currencyLocked') ?? false\n const lockedEntryCount = readNumber('lockedEntryCount')\n const totalMinutes = readEnriched('totalMinutes') ?? 0\n const billableMinutes = readEnriched('billableMinutes')\n const cost = readEnriched('cost')\n const nonBillableMinutes = billableMinutes === null ? null : Math.max(0, totalMinutes - billableMinutes)\n const billablePercent =\n billableMinutes === null || totalMinutes <= 0 ? null : Math.round((billableMinutes / totalMinutes) * 100)\n const hoursTrend = Array.isArray(enrichment.hoursTrend)\n ? (enrichment.hoursTrend as unknown[]).filter((value): value is number => typeof value === 'number')\n : []\n\n const budgetKind = readString('budgetKind', 'budget_kind')\n const budgetValue = readNumber('budgetValue', 'budget_value')\n const budgetWarnAt = readNumber('budgetWarnAtPercent', 'budget_warn_at_percent')\n const budgetBurn = computeBudgetBurn({\n kind: budgetKind as 'none' | 'hours' | 'amount' | null,\n budgetValue,\n warnAtPercent: budgetWarnAt,\n totalMinutes,\n cost,\n })\n\n const formatHours = (minutes: number | null): string => {\n if (minutes === null) return '\u2014'\n const sign = minutes < 0 ? '-' : ''\n const abs = Math.abs(minutes)\n return `${sign}${Math.floor(abs / 60)}:${String(abs % 60).padStart(2, '0')}`\n }\n const formatDateTime = (value: string | null): string => {\n if (!value) return '\u2014'\n // The query engine returns `2026-05-24 00:00:00+00` \u2014 a space separator and a\n // two-digit offset, neither of which `Date` accepts, so both need widening\n // before parsing or every timestamp renders raw.\n //\n // The offset is only widened when a clock precedes it: anchoring on the\n // trailing `-24` alone also matches the day of a date-only `2026-05-24`,\n // which turns a valid date into `2026-05-24:00` and renders it raw.\n const normalized = value\n .replace(' ', 'T')\n .replace(/(T\\d{2}:\\d{2}(?::\\d{2}(?:\\.\\d+)?)?)([+-]\\d{2})$/, '$1$2:00')\n const parsed = new Date(normalized)\n return Number.isNaN(parsed.getTime()) ? value : parsed.toLocaleDateString()\n }\n const budgetUsageLabel = budgetBurn\n ? budgetBurn.kind === 'hours'\n ? t('staff.timesheets.projects.detail.budgetHours', '{percent}% of {value} h', {\n percent: budgetBurn.percent,\n value: budgetBurn.budgetValue,\n })\n : t('staff.timesheets.projects.detail.budgetAmount', '{percent}% of {value}', {\n percent: budgetBurn.percent,\n value: formatCurrency(budgetBurn.budgetValue, currencyCode ?? undefined) ?? String(budgetBurn.budgetValue),\n })\n : null\n\n const injectedTabs = injectedTabWidgets\n .filter((widget) => (widget.placement?.kind ?? 'tab') === 'tab')\n .map((widget) => {\n const tabId = widget.placement?.groupId ?? widget.widgetId\n const groupLabel = widget.placement?.groupLabel\n const label = groupLabel ? t(groupLabel, groupLabel) : widget.module.metadata.title ?? tabId\n const priority = typeof widget.placement?.priority === 'number' ? widget.placement.priority : 0\n return { tabId, label, priority, Widget: widget.module.Widget }\n })\n .sort((left, right) => right.priority - left.priority)\n\n return (\n <Page>\n <PageBody>\n <div className=\"space-y-6\">\n {/*\n * Identity first: colour, name, and the two states worth knowing before\n * anything else \u2014 is it running, and is the budget in trouble. The code,\n * customer, type and start date sit under it as one meta line rather than\n * four labelled rows, because none of them is a decision on its own.\n */}\n <div className=\"flex flex-wrap items-start justify-between gap-4\">\n <div className=\"flex min-w-0 items-start gap-3\">\n <Button variant=\"ghost\" size=\"icon\" asChild>\n <Link href={BACK_HREF}>\n <ArrowLeft className=\"h-4 w-4\" aria-hidden />\n </Link>\n </Button>\n <div className=\"flex min-w-0 flex-col gap-1.5\">\n <div className=\"flex flex-wrap items-center gap-2\">\n <span\n className=\"size-3 shrink-0 rounded-sm border border-border\"\n style={{ backgroundColor: resolveProjectColorHex(projectColor, project.name) }}\n aria-hidden\n />\n <h1 className=\"text-xl font-semibold\">{project.name}</h1>\n <Badge\n variant={projectStatus === 'active' ? 'success' : projectStatus === 'on_hold' ? 'warning' : 'neutral'}\n className=\"capitalize\"\n >\n {projectStatus}\n </Badge>\n {canSeeMoney && budgetBurn && budgetBurn.tone !== 'accent' ? (\n <Badge variant={budgetBurn.tone === 'destructive' ? 'destructive' : 'warning'}>\n {t('staff.timesheets.projects.detail.budgetBadge', 'Budget {percent}%', {\n percent: budgetBurn.percent,\n })}\n </Badge>\n ) : null}\n <InjectionSpot\n spotId={extensionPoints.hosts.projectDetailStatusBadges.spotId}\n context={projectInjectionContext}\n data={project}\n />\n </div>\n <div className=\"flex flex-wrap items-center gap-2 text-sm text-muted-foreground\">\n <span className=\"rounded-sm border border-border bg-muted px-1.5 py-0.5 font-mono text-xs text-foreground\">\n {projectCode}\n </span>\n {customerName ? (\n <>\n <span aria-hidden>\u00B7</span>\n {customerId ? (\n <Link className=\"underline underline-offset-2\" href={`/backend/customers/companies/${customerId}`}>\n {customerName}\n </Link>\n ) : (\n <span>{customerName}</span>\n )}\n </>\n ) : null}\n {projectType ? (<><span aria-hidden>\u00B7</span><span>{projectType}</span></>) : null}\n {projectStartDate ? (\n <>\n <span aria-hidden>\u00B7</span>\n <span>\n {t('staff.timesheets.projects.detail.startedOn', 'Started {date}', {\n date: formatDateTime(projectStartDate),\n })}\n </span>\n </>\n ) : null}\n </div>\n </div>\n </div>\n {canManageProjects && (\n <div className=\"flex items-center gap-2\">\n <Button variant=\"outline\" size=\"sm\" onClick={openTeamDrawer}>\n <Users className=\"mr-2 h-4 w-4\" aria-hidden />\n {t('staff.time_tracking.projects.team.manage', 'Manage team')}\n </Button>\n <Button variant=\"outline\" size=\"sm\" asChild>\n <Link href={`/backend/staff/time-tracking/projects/${projectId}/edit`}>\n <Pencil className=\"mr-2 h-4 w-4\" aria-hidden />\n {t('staff.timesheets.projects.form.actions.edit', 'Edit Project')}\n </Link>\n </Button>\n </div>\n )}\n <InjectionSpot\n spotId={extensionPoints.hosts.projectDetailHeader.spotId}\n context={projectInjectionContext}\n data={project}\n />\n </div>\n\n {/*\n * Summary before detail. Four figures across the full width, in the order\n * somebody asks them: how much time, how much of it billable, what it\n * cost, how close the budget is. The three stacked label/value cards this\n * replaces gave all fourteen fields identical weight.\n */}\n <div className=\"grid gap-3 sm:grid-cols-2 xl:grid-cols-4\">\n <KpiCard\n title={t('staff.timesheets.projects.detail.loggedHours', 'Logged hours')}\n value={totalMinutes / 60}\n formatValue={() => formatHours(totalMinutes)}\n footer={\n hoursTrend.length > 0 ? (\n <Sparkline\n values={hoursTrend}\n ariaLabel={t('staff.timesheets.projects.detail.trendAria', 'Hours per week, last 7 weeks')}\n />\n ) : undefined\n }\n headerAction={\n entryCount !== null ? (\n <span className=\"text-xs text-muted-foreground\">\n {entryCount === 1\n ? t('staff.timesheets.projects.detail.entryCountOne', '1 entry')\n : t('staff.timesheets.projects.detail.entryCountShort', '{count} entries', { count: entryCount })}\n </span>\n ) : undefined\n }\n />\n <KpiCard\n title={t('staff.timesheets.projects.detail.billableHours', 'Billable hours')}\n value={(billableMinutes ?? 0) / 60}\n formatValue={() => formatHours(billableMinutes)}\n headerAction={\n billablePercent !== null ? (\n <Badge variant={billablePercent >= 80 ? 'success' : 'neutral'}>{`${billablePercent}%`}</Badge>\n ) : undefined\n }\n footer={\n nonBillableMinutes !== null ? (\n <span className=\"text-xs text-muted-foreground\">\n {t('staff.timesheets.projects.detail.nonBillable', '{hours} non-billable', {\n hours: formatHours(nonBillableMinutes),\n })}\n </span>\n ) : undefined\n }\n />\n {canSeeMoney ? (\n <KpiCard\n title={t('staff.timesheets.projects.detail.cost', 'Cost to date')}\n value={cost}\n formatValue={(value) => formatCurrency(value, currencyCode ?? undefined) ?? String(value)}\n footer={\n hourlyRate !== null ? (\n <span className=\"text-xs text-muted-foreground\">\n {t('staff.timesheets.projects.detail.costBasis', '{hours} \u00D7 {rate}', {\n hours: formatHours(billableMinutes),\n rate: `${formatCurrency(hourlyRate, currencyCode ?? undefined) ?? hourlyRate}/h`,\n })}\n </span>\n ) : undefined\n }\n />\n ) : null}\n {canSeeMoney ? (\n <div className=\"rounded-lg border bg-card p-4\">\n <div className=\"flex items-center justify-between gap-2\">\n <span className=\"text-sm font-medium text-muted-foreground\">\n {t('staff.timesheets.projects.form.budget', 'Budget')}\n </span>\n {budgetBurn ? (\n <Badge\n variant={\n budgetBurn.tone === 'destructive'\n ? 'destructive'\n : budgetBurn.tone === 'warning'\n ? 'warning'\n : 'neutral'\n }\n >\n {`${budgetBurn.percent}%`}\n </Badge>\n ) : (\n <Badge variant=\"neutral\">{t('staff.timesheets.projects.detail.noBudget', 'No budget')}</Badge>\n )}\n </div>\n <div className=\"mt-2 space-y-2\">\n <p className=\"text-2xl font-semibold tabular-nums\">\n {budgetBurn ? `${budgetBurn.percent}%` : '\u2014'}\n </p>\n <ProjectBudgetCell\n burn={budgetBurn}\n usageLabel={budgetUsageLabel}\n noBudgetLabel={t('staff.timesheets.projects.detail.budgetHint', 'Set one to track burn')}\n ariaLabel={t('staff.timesheets.projects.detail.budgetUsage', 'Budget usage')}\n />\n </div>\n </div>\n ) : null}\n </div>\n\n {/*\n * Working content left, reference right. Everything static \u2014 rate,\n * currency, cost centre, timestamps \u2014 collapses into one rail instead of\n * three full-width cards, which is what left the right half of a wide\n * screen carrying nothing.\n */}\n <div className=\"grid items-start gap-4 lg:grid-cols-[minmax(0,2fr)_minmax(280px,1fr)]\">\n <Tabs\n value={activeTab}\n onValueChange={setActiveTab}\n variant=\"underline\"\n className=\"min-w-0\"\n >\n <TabsList aria-label={t('staff.timesheets.projects.detail.tabs', 'Project sections')}>\n <TabsTrigger value=\"team\" count={activeCount}>\n {t('staff.timesheets.projects.detail.tabTeam', 'Team')}\n </TabsTrigger>\n <TabsTrigger value=\"time\" count={entryCount ?? undefined}>\n {t('staff.timesheets.projects.detail.tabTime', 'Time')}\n </TabsTrigger>\n <TabsTrigger value=\"tasks\">\n {t('staff.timesheets.projects.detail.tabTasks', 'Tasks')}\n </TabsTrigger>\n {injectedTabs.map((tab) => (\n <TabsTrigger key={tab.tabId} value={tab.tabId}>\n {tab.label}\n </TabsTrigger>\n ))}\n </TabsList>\n\n <TabsContent value=\"team\">\n {/* Assigned Employees \u2014 collapsible cards */}\n <div className=\"space-y-3\">\n <div className=\"flex items-center justify-between\">\n <h2 className=\"sr-only\">\n {t('staff.timesheets.projects.employees.title', 'Assigned Employees')}\n </h2>\n <span />\n {canManageProjects && (\n <Button size=\"sm\" onClick={openAddDialog}>\n <Plus className=\"mr-2 h-4 w-4\" aria-hidden />\n {t('staff.timesheets.projects.add_employee', 'Add Employee')}\n </Button>\n )}\n </div>\n\n {employeesLoading ? (\n <LoadingMessage label={t('staff.timesheets.projects.employees.loading', 'Loading employees...')} />\n ) : employees.length === 0 ? (\n <div className=\"rounded-lg border border-dashed p-8 text-center\">\n <User className=\"mx-auto h-8 w-8 text-muted-foreground/50\" aria-hidden />\n <p className=\"mt-2 text-sm text-muted-foreground\">\n {t('staff.timesheets.projects.employees.empty', 'No employees assigned yet.')}\n </p>\n </div>\n ) : (\n <div className=\"space-y-2\">\n {employees.map((emp) => {\n const isExpanded = expandedCards.has(emp.id)\n const isInactive = emp.status !== 'active'\n return (\n <div\n key={emp.id}\n className={`rounded-lg border ${isInactive ? 'opacity-60' : ''}`}\n >\n {/* Collapsed view: name, role, status badge, start date, expand toggle */}\n <button\n type=\"button\"\n className=\"flex w-full items-center justify-between p-4 text-left hover:bg-muted/50 transition-colors\"\n onClick={() => toggleCard(emp.id)}\n >\n <div className=\"flex items-center gap-3 min-w-0\">\n <div className=\"flex h-8 w-8 shrink-0 items-center justify-center rounded-full bg-muted\">\n <User className=\"h-4 w-4 text-muted-foreground\" aria-hidden />\n </div>\n <div className=\"min-w-0\">\n <p className=\"text-sm font-medium truncate\">\n {emp.displayName ?? emp.staffMemberId}\n </p>\n {emp.role ? (\n <p className=\"text-xs text-muted-foreground truncate\">{emp.role}</p>\n ) : null}\n </div>\n </div>\n <div className=\"flex items-center gap-3 shrink-0\">\n <Badge\n variant={(emp.status ?? 'active') === 'active' ? 'success' : 'neutral'}\n className=\"capitalize\"\n >\n {emp.status ?? 'active'}\n </Badge>\n {emp.assignedStartDate ? (\n <span className=\"text-xs text-muted-foreground hidden sm:inline\">\n {emp.assignedStartDate}\n </span>\n ) : null}\n <ChevronDown className={`h-4 w-4 text-muted-foreground transition-transform ${isExpanded ? 'rotate-180' : ''}`} aria-hidden />\n </div>\n </button>\n\n {/* Expanded view: assignment details */}\n {isExpanded ? (\n <div className=\"border-t px-4 pb-4 pt-3 space-y-3\">\n <dl className=\"grid grid-cols-2 gap-3 text-sm\">\n <div>\n <dt className=\"font-medium text-muted-foreground\">\n {t('staff.timesheets.projects.employees.role', 'Role')}\n </dt>\n <dd>{emp.role || '-'}</dd>\n </div>\n <div>\n <dt className=\"font-medium text-muted-foreground\">\n {t('staff.timesheets.projects.employees.status', 'Status')}\n </dt>\n <dd className=\"capitalize\">{emp.status ?? 'active'}</dd>\n </div>\n <div>\n <dt className=\"font-medium text-muted-foreground\">\n {t('staff.timesheets.projects.employees.startDate', 'Assignment start')}\n </dt>\n <dd>{emp.assignedStartDate || '-'}</dd>\n </div>\n <div>\n <dt className=\"font-medium text-muted-foreground\">\n {t('staff.timesheets.projects.employees.endDate', 'Assignment end')}\n </dt>\n <dd>{emp.assignedEndDate || '-'}</dd>\n </div>\n {emp.teamName ? (\n <div>\n <dt className=\"font-medium text-muted-foreground\">\n {t('staff.timesheets.projects.employees.department', 'Department')}\n </dt>\n <dd>{emp.teamName}</dd>\n </div>\n ) : null}\n </dl>\n {canManageProjects && (\n <div className=\"flex justify-end\">\n <Button\n variant=\"outline\"\n size=\"sm\"\n className=\"text-destructive hover:text-destructive\"\n onClick={() => { void handleRemoveEmployee(emp) }}\n >\n <Trash2 className=\"mr-2 h-3.5 w-3.5\" aria-hidden />\n {t('staff.timesheets.projects.employees.remove', 'Remove')}\n </Button>\n </div>\n )}\n </div>\n ) : null}\n </div>\n )\n })}\n </div>\n )}\n </div>\n </TabsContent>\n\n <TabsContent value=\"time\">\n {recentEntriesLoading ? (\n <LoadingMessage label={t('staff.timesheets.projects.detail.loadingTime', 'Loading time entries\u2026')} />\n ) : recentEntries.length === 0 ? (\n <div className=\"rounded-lg border border-dashed p-8 text-center\">\n <p className=\"text-sm font-medium\">\n {t('staff.timesheets.projects.detail.noTime', 'No time logged yet')}\n </p>\n <p className=\"mt-1 text-xs text-muted-foreground\">\n {t('staff.timesheets.projects.detail.noTimeHint', 'Hours logged against this project appear here.')}\n </p>\n </div>\n ) : (\n <div className=\"overflow-x-auto rounded-lg border\">\n <table className=\"w-full text-sm\">\n <thead>\n <tr className=\"border-b bg-muted/40 text-left\">\n <th className=\"px-3 py-2 font-medium text-muted-foreground\">\n {t('staff.timesheets.projects.detail.colDate', 'Date')}\n </th>\n <th className=\"px-3 py-2 font-medium text-muted-foreground\">\n {t('staff.timesheets.projects.detail.colWho', 'Person')}\n </th>\n <th className=\"px-3 py-2 font-medium text-muted-foreground\">\n {t('staff.timesheets.projects.detail.colDescription', 'Description')}\n </th>\n <th className=\"px-3 py-2 text-right font-medium text-muted-foreground\">\n {t('staff.timesheets.projects.detail.colHours', 'Hours')}\n </th>\n </tr>\n </thead>\n <tbody>\n {recentEntries.map((entry) => (\n <tr key={entry.id} className=\"border-b last:border-b-0\">\n <td className=\"whitespace-nowrap px-3 py-2\">{entry.date}</td>\n <td className=\"whitespace-nowrap px-3 py-2\">\n {employeeNameById.get(entry.staffMemberId ?? '') ?? '\u2014'}\n </td>\n <td className=\"px-3 py-2\">\n <span className=\"flex items-center gap-2\">\n <span className=\"truncate\">{entry.description ?? '\u2014'}</span>\n {entry.isBillable ? null : (\n <Badge variant=\"neutral\">\n {t('staff.timesheets.projects.detail.nonBillableTag', 'non-billable')}\n </Badge>\n )}\n </span>\n </td>\n <td className=\"whitespace-nowrap px-3 py-2 text-right font-mono tabular-nums\">\n {formatHours(entry.durationMinutes)}\n </td>\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n )}\n </TabsContent>\n\n <TabsContent value=\"tasks\">\n {projectTasksLoading ? (\n <LoadingMessage label={t('staff.timesheets.projects.detail.loadingTasks', 'Loading tasks\u2026')} />\n ) : projectTasks.length === 0 ? (\n <div className=\"rounded-lg border border-dashed p-8 text-center\">\n <p className=\"text-sm font-medium\">\n {t('staff.timesheets.projects.detail.noTasks', 'No tasks yet')}\n </p>\n </div>\n ) : (\n <div className=\"divide-y rounded-lg border\">\n {projectTasks.map((task) => (\n <div key={task.id} className=\"flex items-center gap-3 px-3 py-2 text-sm\">\n {task.reference ? (\n <span className=\"shrink-0 rounded-sm border border-border bg-muted px-1.5 py-0.5 font-mono text-xs\">\n {task.reference}\n </span>\n ) : null}\n <span className=\"min-w-0 flex-1 truncate\">{task.title}</span>\n <span className=\"shrink-0 text-xs text-muted-foreground\">\n {employeeNameById.get(task.assigneeStaffMemberId ?? '') ?? ''}\n </span>\n {task.loggedMinutes !== null ? (\n <span className=\"shrink-0 font-mono text-xs tabular-nums text-muted-foreground\">\n {formatHours(task.loggedMinutes)}\n </span>\n ) : null}\n </div>\n ))}\n </div>\n )}\n </TabsContent>\n\n {injectedTabs.map((tab) => (\n <TabsContent key={tab.tabId} value={tab.tabId}>\n <tab.Widget context={projectInjectionContext} data={project} />\n </TabsContent>\n ))}\n </Tabs>\n\n {/* Reference rail \u2014 one card, grouped, no longer three stacked sections. */}\n <div className=\"rounded-lg border bg-card\">\n <div className=\"border-b px-4 py-3\">\n <h2 className=\"text-sm font-semibold\">\n {t('staff.timesheets.projects.detail.details', 'Details')}\n </h2>\n </div>\n <div className=\"px-4 py-3\">\n {canSeeMoney ? (\n <>\n <RailGroup label={t('staff.timesheets.projects.detail.commercials', 'Billing')} first />\n <dl>\n <RailFact\n label={t('staff.timesheets.projects.form.hourlyRate', 'Hourly rate')}\n value={\n hourlyRate === null\n ? '\u2014'\n : `${formatCurrency(hourlyRate, currencyCode ?? undefined) ?? hourlyRate} / h`\n }\n />\n <RailFact\n label={t('staff.timesheets.projects.form.currency', 'Currency')}\n value={\n <span className=\"inline-flex items-center gap-1.5\">\n {currencyCode ?? '\u2014'}\n {currencyLocked ? (\n <Badge\n variant=\"neutral\"\n title={t(\n 'staff.timesheets.projects.detail.currencyLockedHint',\n 'Locked because hours are already logged against this project.',\n )}\n >\n {t('staff.timesheets.projects.detail.locked', 'locked')}\n </Badge>\n ) : null}\n </span>\n }\n />\n <RailFact\n label={t('staff.timesheets.projects.form.billableByDefault', 'Billable by default')}\n value={\n billableByDefault === null ? (\n '\u2014'\n ) : (\n <Badge variant={billableByDefault ? 'success' : 'neutral'}>\n {billableByDefault\n ? t('staff.timesheets.projects.detail.yes', 'Yes')\n : t('staff.timesheets.projects.detail.no', 'No')}\n </Badge>\n )\n }\n />\n </dl>\n </>\n ) : null}\n\n <RailGroup\n label={t('staff.timesheets.projects.detail.delivery', 'Delivery')}\n first={!canSeeMoney}\n />\n <dl>\n <RailFact\n label={t('staff.timesheets.projects.detail.entryCount', 'Time entries')}\n value={entryCount ?? '\u2014'}\n />\n <RailFact\n label={t('staff.timesheets.projects.detail.lockedEntries', 'Locked in reports')}\n value={lockedEntryCount ?? '\u2014'}\n />\n </dl>\n\n <RailGroup label={t('staff.timesheets.projects.detail.administration', 'Administration')} />\n <dl>\n <RailFact\n label={t('staff.timesheets.projects.form.costCenter', 'Cost center')}\n value={costCenter ?? '\u2014'}\n />\n <RailFact\n label={t('staff.timesheets.projects.form.color', 'Project color')}\n value={\n <span className=\"inline-flex items-center gap-1.5 capitalize\">\n <span\n className=\"size-2.5 rounded-full border border-border\"\n style={{ backgroundColor: resolveProjectColorHex(projectColor, project.name) }}\n aria-hidden\n />\n {projectColor ?? t('staff.timesheets.projects.detail.autoColor', 'Auto')}\n </span>\n }\n />\n <RailFact\n label={t('staff.timesheets.projects.detail.createdAt', 'Created')}\n value={formatDateTime(createdAt)}\n />\n <RailFact\n label={t('staff.timesheets.projects.detail.updatedAt', 'Last updated')}\n value={formatDateTime(updatedAt)}\n />\n </dl>\n\n {project.description ? (\n <>\n <RailGroup label={t('staff.timesheets.projects.form.description', 'Description')} />\n <p className=\"whitespace-pre-wrap pt-2 text-sm text-muted-foreground\">{project.description}</p>\n </>\n ) : null}\n <InjectionSpot\n spotId={extensionPoints.hosts.projectDetailSidebar.spotId}\n context={projectInjectionContext}\n data={project}\n />\n </div>\n </div>\n </div>\n <InjectionSpot\n spotId={extensionPoints.hosts.projectDetailFooter.spotId}\n context={projectInjectionContext}\n data={project}\n />\n </div>\n\n {ConfirmDialogElement}\n\n {projectId && teamDrawerOpen ? (\n <ProjectTeamDrawer\n projectId={projectId}\n projectName={project.name}\n open={teamDrawerOpen}\n onOpenChange={handleTeamDrawerOpenChange}\n preselectUserId={requesterUserId}\n onSaved={handleTeamSaved}\n />\n ) : null}\n\n {/* Add Employee Dialog */}\n <Dialog open={addDialogOpen} onOpenChange={setAddDialogOpen}>\n <DialogContent className=\"sm:max-w-md\" onKeyDown={handleAddDialogKeyDown}>\n <DialogHeader>\n <DialogTitle>{t('staff.timesheets.projects.add_employee', 'Add Employee')}</DialogTitle>\n </DialogHeader>\n <div className=\"space-y-4 pt-2\">\n <div className=\"space-y-2\">\n <label className=\"text-sm font-medium\">\n {t('staff.timesheets.projects.employees.selectEmployee', 'Select Employee')}\n </label>\n <LookupSelect\n value={addStaffMemberId}\n onChange={setAddStaffMemberId}\n fetchItems={fetchStaffMembers}\n searchPlaceholder={t('staff.timesheets.projects.employees.searchEmployee', 'Search team members...')}\n emptyLabel={t('staff.timesheets.projects.employees.noResults', 'No team members found')}\n />\n {staffLookupError ? (\n <p className=\"text-xs text-status-error-text\" role=\"alert\">\n {staffLookupError}\n </p>\n ) : null}\n </div>\n <div className=\"space-y-2\">\n <label className=\"text-sm font-medium\" htmlFor=\"add-role\">\n {t('staff.timesheets.projects.employees.roleOnProject', 'Role on Project')}\n </label>\n <Input\n id=\"add-role\"\n value={addRole}\n onChange={(event) => setAddRole(event.target.value)}\n placeholder={t('staff.timesheets.projects.employees.rolePlaceholder', 'e.g. Developer, Designer...')}\n />\n </div>\n <div className=\"space-y-2\">\n <label className=\"text-sm font-medium\" htmlFor=\"add-start-date\">\n {t('staff.timesheets.projects.employees.assignmentStartDate', 'Assignment Start Date')}\n </label>\n <Input\n id=\"add-start-date\"\n type=\"date\"\n value={addStartDate}\n onChange={(event) => setAddStartDate(event.target.value)}\n />\n </div>\n <div className=\"flex justify-end gap-2 pt-2\">\n <Button variant=\"outline\" size=\"sm\" onClick={() => setAddDialogOpen(false)}>\n {t('staff.timesheets.projects.form.actions.cancel', 'Cancel')}\n </Button>\n <Button\n size=\"sm\"\n disabled={addSaving || !addStaffMemberId || !addStartDate}\n onClick={handleAddEmployee}\n >\n {addSaving ? <Spinner className=\"size-4\" /> : null}\n {t('staff.timesheets.projects.add_employee', 'Add Employee')}\n </Button>\n </div>\n </div>\n </DialogContent>\n </Dialog>\n </PageBody>\n </Page>\n )\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["params"]
|
|
7
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
const metadata = {
|
|
2
|
+
requireAuth: true,
|
|
3
|
+
requireFeatures: ["staff.timesheets.projects.view"],
|
|
4
|
+
navHidden: true,
|
|
5
|
+
pageTitle: "Project Details",
|
|
6
|
+
pageTitleKey: "staff.timesheets.nav.project_details",
|
|
7
|
+
breadcrumb: [
|
|
8
|
+
{ label: "Time tracking", labelKey: "staff.time_tracking.nav.group", href: "/backend/staff/time-tracking" },
|
|
9
|
+
{ label: "Projects", labelKey: "staff.time_tracking.nav.projects", href: "/backend/staff/time-tracking/projects" },
|
|
10
|
+
{ label: "Details", labelKey: "staff.timesheets.nav.project_details" }
|
|
11
|
+
]
|
|
12
|
+
};
|
|
13
|
+
export {
|
|
14
|
+
metadata
|
|
15
|
+
};
|
|
16
|
+
//# sourceMappingURL=page.meta.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../../../../../src/modules/staff/backend/staff/time-tracking/projects/%5Bid%5D/page.meta.ts"],
|
|
4
|
+
"sourcesContent": ["export const metadata = {\n requireAuth: true,\n requireFeatures: ['staff.timesheets.projects.view'],\n navHidden: true,\n pageTitle: 'Project Details',\n pageTitleKey: 'staff.timesheets.nav.project_details',\n breadcrumb: [\n { label: 'Time tracking', labelKey: 'staff.time_tracking.nav.group', href: '/backend/staff/time-tracking' },\n { label: 'Projects', labelKey: 'staff.time_tracking.nav.projects', href: '/backend/staff/time-tracking/projects' },\n { label: 'Details', labelKey: 'staff.timesheets.nav.project_details' },\n ],\n}\n"],
|
|
5
|
+
"mappings": "AAAO,MAAM,WAAW;AAAA,EACtB,aAAa;AAAA,EACb,iBAAiB,CAAC,gCAAgC;AAAA,EAClD,WAAW;AAAA,EACX,WAAW;AAAA,EACX,cAAc;AAAA,EACd,YAAY;AAAA,IACV,EAAE,OAAO,iBAAiB,UAAU,iCAAiC,MAAM,+BAA+B;AAAA,IAC1G,EAAE,OAAO,YAAY,UAAU,oCAAoC,MAAM,wCAAwC;AAAA,IACjH,EAAE,OAAO,WAAW,UAAU,uCAAuC;AAAA,EACvE;AACF;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx } from "react/jsx-runtime";
|
|
3
|
+
import { extensionPoints } from "@open-mercato/core/modules/staff/extension-points";
|
|
4
|
+
import * as React from "react";
|
|
5
|
+
import { useRouter } from "next/navigation";
|
|
6
|
+
import { Page, PageBody } from "@open-mercato/ui/backend/Page";
|
|
7
|
+
import { CrudForm } from "@open-mercato/ui/backend/CrudForm";
|
|
8
|
+
import { createCrud } from "@open-mercato/ui/backend/utils/crud";
|
|
9
|
+
import { apiCall } from "@open-mercato/ui/backend/utils/apiCall";
|
|
10
|
+
import { createCrudFormError } from "@open-mercato/ui/backend/utils/serverErrors";
|
|
11
|
+
import { E } from "../../../../../../../generated/entities.ids.generated.js";
|
|
12
|
+
import { flash } from "@open-mercato/ui/backend/FlashMessages";
|
|
13
|
+
import { useT } from "@open-mercato/shared/lib/i18n/context";
|
|
14
|
+
import {
|
|
15
|
+
buildProjectPayload,
|
|
16
|
+
createProjectFormFields,
|
|
17
|
+
createProjectFormGroups,
|
|
18
|
+
createProjectFormSchema
|
|
19
|
+
} from "../projectFormConfig.js";
|
|
20
|
+
const BACK_HREF = "/backend/staff/time-tracking/projects";
|
|
21
|
+
const INITIAL_VALUES = {
|
|
22
|
+
status: "active",
|
|
23
|
+
billableByDefault: true,
|
|
24
|
+
budgetEnabled: false,
|
|
25
|
+
budgetKind: "hours",
|
|
26
|
+
budgetWarnAtPercent: "80",
|
|
27
|
+
codeManual: false
|
|
28
|
+
};
|
|
29
|
+
function TimeTrackingProjectCreatePage() {
|
|
30
|
+
const t = useT();
|
|
31
|
+
const router = useRouter();
|
|
32
|
+
const formSchema = React.useMemo(() => createProjectFormSchema(), []);
|
|
33
|
+
const fields = React.useMemo(() => createProjectFormFields(t), [t]);
|
|
34
|
+
const groups = React.useMemo(() => createProjectFormGroups(t), [t]);
|
|
35
|
+
return /* @__PURE__ */ jsx(Page, { children: /* @__PURE__ */ jsx(PageBody, { children: /* @__PURE__ */ jsx(
|
|
36
|
+
CrudForm,
|
|
37
|
+
{
|
|
38
|
+
title: t("staff.time_tracking.projects.form.createTitle", "New project"),
|
|
39
|
+
titleHeadingLevel: 1,
|
|
40
|
+
backHref: BACK_HREF,
|
|
41
|
+
cancelHref: BACK_HREF,
|
|
42
|
+
fields,
|
|
43
|
+
groups,
|
|
44
|
+
schema: formSchema,
|
|
45
|
+
initialValues: INITIAL_VALUES,
|
|
46
|
+
entityIds: [E.staff.staff_time_project],
|
|
47
|
+
injectionSpotId: extensionPoints.hosts.projectForm.spotId,
|
|
48
|
+
submitLabel: t("staff.timesheets.projects.form.actions.create", "Create"),
|
|
49
|
+
onSubmit: async (values) => {
|
|
50
|
+
if (!values.name?.trim() || !values.code?.trim() || !values.customerId) {
|
|
51
|
+
const fieldErrors = {};
|
|
52
|
+
if (!values.name?.trim()) {
|
|
53
|
+
fieldErrors.name = t("staff.time_tracking.projects.validation.nameRequired", "A project name is required.");
|
|
54
|
+
}
|
|
55
|
+
if (!values.code?.trim()) {
|
|
56
|
+
fieldErrors.code = t("staff.time_tracking.projects.validation.codeRequired", "A project code is required.");
|
|
57
|
+
}
|
|
58
|
+
if (!values.customerId) {
|
|
59
|
+
fieldErrors.customerId = t("staff.time_tracking.projects.validation.customerRequired", "A customer is required.");
|
|
60
|
+
}
|
|
61
|
+
throw createCrudFormError(
|
|
62
|
+
t("staff.time_tracking.projects.errors.required", "Fill in the required fields."),
|
|
63
|
+
fieldErrors
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
const payload = buildProjectPayload(values);
|
|
67
|
+
const { result: created } = await createCrud(
|
|
68
|
+
"staff/timesheets/time-projects",
|
|
69
|
+
payload,
|
|
70
|
+
{ errorMessage: t("staff.timesheets.projects.errors.save", "Failed to save project.") }
|
|
71
|
+
);
|
|
72
|
+
const newId = created?.id;
|
|
73
|
+
if (newId) {
|
|
74
|
+
try {
|
|
75
|
+
const selfRes = await apiCall("/api/staff/team-members/self");
|
|
76
|
+
const staffMemberId = selfRes.result?.member?.id;
|
|
77
|
+
if (staffMemberId) {
|
|
78
|
+
await createCrud("staff/timesheets/time-projects/" + newId + "/employees", {
|
|
79
|
+
staffMemberId,
|
|
80
|
+
assignedStartDate: (/* @__PURE__ */ new Date()).toISOString().slice(0, 10),
|
|
81
|
+
status: "active"
|
|
82
|
+
}, { errorMessage: "" });
|
|
83
|
+
}
|
|
84
|
+
} catch {
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
flash(t("staff.timesheets.projects.messages.saved", "Project saved."), "success");
|
|
88
|
+
if (newId) router.push(`/backend/staff/time-tracking/projects/${newId}`);
|
|
89
|
+
else router.push(BACK_HREF);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
) }) });
|
|
93
|
+
}
|
|
94
|
+
export {
|
|
95
|
+
TimeTrackingProjectCreatePage as default
|
|
96
|
+
};
|
|
97
|
+
//# sourceMappingURL=page.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../../../../../src/modules/staff/backend/staff/time-tracking/projects/create/page.tsx"],
|
|
4
|
+
"sourcesContent": ["\"use client\"\n\nimport { extensionPoints } from '@open-mercato/core/modules/staff/extension-points'\nimport * as React from 'react'\nimport { useRouter } from 'next/navigation'\nimport { Page, PageBody } from '@open-mercato/ui/backend/Page'\nimport { CrudForm } from '@open-mercato/ui/backend/CrudForm'\nimport { createCrud } from '@open-mercato/ui/backend/utils/crud'\nimport { apiCall } from '@open-mercato/ui/backend/utils/apiCall'\nimport { createCrudFormError } from '@open-mercato/ui/backend/utils/serverErrors'\nimport { E } from '#generated/entities.ids.generated'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport {\n buildProjectPayload,\n createProjectFormFields,\n createProjectFormGroups,\n createProjectFormSchema,\n type ProjectFormValues,\n} from '../projectFormConfig'\n\nconst BACK_HREF = '/backend/staff/time-tracking/projects'\n\nconst INITIAL_VALUES: Partial<ProjectFormValues> = {\n status: 'active',\n billableByDefault: true,\n budgetEnabled: false,\n budgetKind: 'hours',\n budgetWarnAtPercent: '80',\n codeManual: false,\n}\n\nexport default function TimeTrackingProjectCreatePage() {\n const t = useT()\n const router = useRouter()\n\n const formSchema = React.useMemo(() => createProjectFormSchema(), [])\n const fields = React.useMemo(() => createProjectFormFields(t), [t])\n const groups = React.useMemo(() => createProjectFormGroups(t), [t])\n\n return (\n <Page>\n <PageBody>\n <CrudForm<ProjectFormValues>\n title={t('staff.time_tracking.projects.form.createTitle', 'New project')}\n titleHeadingLevel={1}\n backHref={BACK_HREF}\n cancelHref={BACK_HREF}\n fields={fields}\n groups={groups}\n schema={formSchema}\n initialValues={INITIAL_VALUES}\n entityIds={[E.staff.staff_time_project]}\n injectionSpotId={extensionPoints.hosts.projectForm.spotId}\n submitLabel={t('staff.timesheets.projects.form.actions.create', 'Create')}\n onSubmit={async (values) => {\n if (!values.name?.trim() || !values.code?.trim() || !values.customerId) {\n const fieldErrors: Record<string, string> = {}\n if (!values.name?.trim()) {\n fieldErrors.name = t('staff.time_tracking.projects.validation.nameRequired', 'A project name is required.')\n }\n if (!values.code?.trim()) {\n fieldErrors.code = t('staff.time_tracking.projects.validation.codeRequired', 'A project code is required.')\n }\n if (!values.customerId) {\n fieldErrors.customerId = t('staff.time_tracking.projects.validation.customerRequired', 'A customer is required.')\n }\n throw createCrudFormError(\n t('staff.time_tracking.projects.errors.required', 'Fill in the required fields.'),\n fieldErrors,\n )\n }\n\n const payload = buildProjectPayload(values)\n\n const { result: created } = await createCrud<{ id?: string }>(\n 'staff/timesheets/time-projects',\n payload,\n { errorMessage: t('staff.timesheets.projects.errors.save', 'Failed to save project.') },\n )\n\n const newId = created?.id\n\n // Auto-assign creator to the project\n if (newId) {\n try {\n const selfRes = await apiCall<{ member?: { id?: string } | null }>('/api/staff/team-members/self')\n const staffMemberId = selfRes.result?.member?.id\n if (staffMemberId) {\n await createCrud('staff/timesheets/time-projects/' + newId + '/employees', {\n staffMemberId,\n assignedStartDate: new Date().toISOString().slice(0, 10),\n status: 'active',\n }, { errorMessage: '' })\n }\n } catch {\n // non-critical \u2014 project created, self-assignment is best-effort\n }\n }\n\n flash(t('staff.timesheets.projects.messages.saved', 'Project saved.'), 'success')\n if (newId) router.push(`/backend/staff/time-tracking/projects/${newId}`)\n else router.push(BACK_HREF)\n }}\n />\n </PageBody>\n </Page>\n )\n}\n"],
|
|
5
|
+
"mappings": ";AA2CQ;AAzCR,SAAS,uBAAuB;AAChC,YAAY,WAAW;AACvB,SAAS,iBAAiB;AAC1B,SAAS,MAAM,gBAAgB;AAC/B,SAAS,gBAAgB;AACzB,SAAS,kBAAkB;AAC3B,SAAS,eAAe;AACxB,SAAS,2BAA2B;AACpC,SAAS,SAAS;AAClB,SAAS,aAAa;AACtB,SAAS,YAAY;AACrB;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAEK;AAEP,MAAM,YAAY;AAElB,MAAM,iBAA6C;AAAA,EACjD,QAAQ;AAAA,EACR,mBAAmB;AAAA,EACnB,eAAe;AAAA,EACf,YAAY;AAAA,EACZ,qBAAqB;AAAA,EACrB,YAAY;AACd;AAEe,SAAR,gCAAiD;AACtD,QAAM,IAAI,KAAK;AACf,QAAM,SAAS,UAAU;AAEzB,QAAM,aAAa,MAAM,QAAQ,MAAM,wBAAwB,GAAG,CAAC,CAAC;AACpE,QAAM,SAAS,MAAM,QAAQ,MAAM,wBAAwB,CAAC,GAAG,CAAC,CAAC,CAAC;AAClE,QAAM,SAAS,MAAM,QAAQ,MAAM,wBAAwB,CAAC,GAAG,CAAC,CAAC,CAAC;AAElE,SACE,oBAAC,QACC,8BAAC,YACC;AAAA,IAAC;AAAA;AAAA,MACC,OAAO,EAAE,iDAAiD,aAAa;AAAA,MACvE,mBAAmB;AAAA,MACnB,UAAU;AAAA,MACV,YAAY;AAAA,MACZ;AAAA,MACA;AAAA,MACA,QAAQ;AAAA,MACR,eAAe;AAAA,MACf,WAAW,CAAC,EAAE,MAAM,kBAAkB;AAAA,MACtC,iBAAiB,gBAAgB,MAAM,YAAY;AAAA,MACnD,aAAa,EAAE,iDAAiD,QAAQ;AAAA,MACxE,UAAU,OAAO,WAAW;AAC1B,YAAI,CAAC,OAAO,MAAM,KAAK,KAAK,CAAC,OAAO,MAAM,KAAK,KAAK,CAAC,OAAO,YAAY;AACtE,gBAAM,cAAsC,CAAC;AAC7C,cAAI,CAAC,OAAO,MAAM,KAAK,GAAG;AACxB,wBAAY,OAAO,EAAE,wDAAwD,6BAA6B;AAAA,UAC5G;AACA,cAAI,CAAC,OAAO,MAAM,KAAK,GAAG;AACxB,wBAAY,OAAO,EAAE,wDAAwD,6BAA6B;AAAA,UAC5G;AACA,cAAI,CAAC,OAAO,YAAY;AACtB,wBAAY,aAAa,EAAE,4DAA4D,yBAAyB;AAAA,UAClH;AACA,gBAAM;AAAA,YACJ,EAAE,gDAAgD,8BAA8B;AAAA,YAChF;AAAA,UACF;AAAA,QACF;AAEA,cAAM,UAAU,oBAAoB,MAAM;AAE1C,cAAM,EAAE,QAAQ,QAAQ,IAAI,MAAM;AAAA,UAChC;AAAA,UACA;AAAA,UACA,EAAE,cAAc,EAAE,yCAAyC,yBAAyB,EAAE;AAAA,QACxF;AAEA,cAAM,QAAQ,SAAS;AAGvB,YAAI,OAAO;AACT,cAAI;AACF,kBAAM,UAAU,MAAM,QAA6C,8BAA8B;AACjG,kBAAM,gBAAgB,QAAQ,QAAQ,QAAQ;AAC9C,gBAAI,eAAe;AACjB,oBAAM,WAAW,oCAAoC,QAAQ,cAAc;AAAA,gBACzE;AAAA,gBACA,oBAAmB,oBAAI,KAAK,GAAE,YAAY,EAAE,MAAM,GAAG,EAAE;AAAA,gBACvD,QAAQ;AAAA,cACV,GAAG,EAAE,cAAc,GAAG,CAAC;AAAA,YACzB;AAAA,UACF,QAAQ;AAAA,UAER;AAAA,QACF;AAEA,cAAM,EAAE,4CAA4C,gBAAgB,GAAG,SAAS;AAChF,YAAI,MAAO,QAAO,KAAK,yCAAyC,KAAK,EAAE;AAAA,YAClE,QAAO,KAAK,SAAS;AAAA,MAC5B;AAAA;AAAA,EACF,GACF,GACF;AAEJ;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
const metadata = {
|
|
2
|
+
requireAuth: true,
|
|
3
|
+
requireFeatures: ["staff.timesheets.projects.manage"],
|
|
4
|
+
navHidden: true,
|
|
5
|
+
pageTitle: "Create Project",
|
|
6
|
+
pageTitleKey: "staff.timesheets.nav.create_project",
|
|
7
|
+
breadcrumb: [
|
|
8
|
+
{ label: "Time tracking", labelKey: "staff.time_tracking.nav.group", href: "/backend/staff/time-tracking" },
|
|
9
|
+
{ label: "Projects", labelKey: "staff.time_tracking.nav.projects", href: "/backend/staff/time-tracking/projects" },
|
|
10
|
+
{ label: "Create", labelKey: "staff.timesheets.nav.create_project" }
|
|
11
|
+
]
|
|
12
|
+
};
|
|
13
|
+
export {
|
|
14
|
+
metadata
|
|
15
|
+
};
|
|
16
|
+
//# sourceMappingURL=page.meta.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../../../../../src/modules/staff/backend/staff/time-tracking/projects/create/page.meta.ts"],
|
|
4
|
+
"sourcesContent": ["export const metadata = {\n requireAuth: true,\n requireFeatures: ['staff.timesheets.projects.manage'],\n navHidden: true,\n pageTitle: 'Create Project',\n pageTitleKey: 'staff.timesheets.nav.create_project',\n breadcrumb: [\n { label: 'Time tracking', labelKey: 'staff.time_tracking.nav.group', href: '/backend/staff/time-tracking' },\n { label: 'Projects', labelKey: 'staff.time_tracking.nav.projects', href: '/backend/staff/time-tracking/projects' },\n { label: 'Create', labelKey: 'staff.timesheets.nav.create_project' },\n ],\n}\n"],
|
|
5
|
+
"mappings": "AAAO,MAAM,WAAW;AAAA,EACtB,aAAa;AAAA,EACb,iBAAiB,CAAC,kCAAkC;AAAA,EACpD,WAAW;AAAA,EACX,WAAW;AAAA,EACX,cAAc;AAAA,EACd,YAAY;AAAA,IACV,EAAE,OAAO,iBAAiB,UAAU,iCAAiC,MAAM,+BAA+B;AAAA,IAC1G,EAAE,OAAO,YAAY,UAAU,oCAAoC,MAAM,wCAAwC;AAAA,IACjH,EAAE,OAAO,UAAU,UAAU,sCAAsC;AAAA,EACrE;AACF;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|