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