@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,68 @@
|
|
|
1
|
+
import { createLogger } from "@open-mercato/shared/lib/logger";
|
|
2
|
+
import { resolveNotificationService } from "../../notifications/lib/notificationService.js";
|
|
3
|
+
import { buildFeatureNotificationFromType } from "../../notifications/lib/notificationBuilder.js";
|
|
4
|
+
import { notificationTypes } from "../notifications.js";
|
|
5
|
+
import { MANAGE_PROJECTS_FEATURE } from "../lib/time-tracking/access.js";
|
|
6
|
+
const logger = createLogger("staff").child({ component: "subscribers/time-project-access-requested" });
|
|
7
|
+
const NOTIFICATION_TYPE = "staff.timesheets.project_access.requested";
|
|
8
|
+
const PROJECTS_LIST_HREF = "/backend/staff/time-tracking/projects";
|
|
9
|
+
const metadata = {
|
|
10
|
+
event: "staff.timesheets.project_access.requested",
|
|
11
|
+
persistent: true,
|
|
12
|
+
id: "staff:time-project-access-requested-notification"
|
|
13
|
+
};
|
|
14
|
+
function buildProjectAccessRequestLinkHref(input) {
|
|
15
|
+
if (!input.timeProjectId) return PROJECTS_LIST_HREF;
|
|
16
|
+
const projectSegment = encodeURIComponent(input.timeProjectId);
|
|
17
|
+
const requesterParam = encodeURIComponent(input.requesterUserId);
|
|
18
|
+
return `${PROJECTS_LIST_HREF}/${projectSegment}?panel=team&requesterUserId=${requesterParam}`;
|
|
19
|
+
}
|
|
20
|
+
function buildProjectAccessRequestGroupKey(input) {
|
|
21
|
+
return `staff:time-project-access:${input.requesterUserId}:${input.timeProjectId ?? "general"}`;
|
|
22
|
+
}
|
|
23
|
+
async function handle(payload, ctx) {
|
|
24
|
+
try {
|
|
25
|
+
if (!payload?.tenantId || !payload?.requesterUserId) return;
|
|
26
|
+
const typeDef = notificationTypes.find((type) => type.type === NOTIFICATION_TYPE);
|
|
27
|
+
if (!typeDef) return;
|
|
28
|
+
const notificationService = resolveNotificationService(ctx);
|
|
29
|
+
const requesterName = payload.requesterName?.trim() || payload.requesterUserId;
|
|
30
|
+
const timeProjectId = payload.timeProjectId ?? null;
|
|
31
|
+
const notificationInput = buildFeatureNotificationFromType(typeDef, {
|
|
32
|
+
requiredFeature: MANAGE_PROJECTS_FEATURE,
|
|
33
|
+
bodyVariables: {
|
|
34
|
+
requesterName,
|
|
35
|
+
...payload.timeProjectName ? { projectName: payload.timeProjectName } : {}
|
|
36
|
+
},
|
|
37
|
+
sourceEntityType: "staff:time_project",
|
|
38
|
+
sourceEntityId: timeProjectId ?? void 0,
|
|
39
|
+
linkHref: buildProjectAccessRequestLinkHref({
|
|
40
|
+
requesterUserId: payload.requesterUserId,
|
|
41
|
+
timeProjectId
|
|
42
|
+
}),
|
|
43
|
+
groupKey: buildProjectAccessRequestGroupKey({
|
|
44
|
+
requesterUserId: payload.requesterUserId,
|
|
45
|
+
timeProjectId
|
|
46
|
+
})
|
|
47
|
+
});
|
|
48
|
+
await notificationService.createForFeature(
|
|
49
|
+
{ ...notificationInput, restrictRecipientsToOrganization: true },
|
|
50
|
+
{
|
|
51
|
+
tenantId: payload.tenantId,
|
|
52
|
+
organizationId: payload.organizationId ?? null
|
|
53
|
+
}
|
|
54
|
+
);
|
|
55
|
+
} catch (err) {
|
|
56
|
+
logger.error("Failed to create the project access request notification", {
|
|
57
|
+
subscriber: metadata.id,
|
|
58
|
+
err
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
export {
|
|
63
|
+
buildProjectAccessRequestGroupKey,
|
|
64
|
+
buildProjectAccessRequestLinkHref,
|
|
65
|
+
handle as default,
|
|
66
|
+
metadata
|
|
67
|
+
};
|
|
68
|
+
//# sourceMappingURL=time-project-access-requested-notification.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../src/modules/staff/subscribers/time-project-access-requested-notification.ts"],
|
|
4
|
+
"sourcesContent": ["import { createLogger } from '@open-mercato/shared/lib/logger'\nimport { resolveNotificationService } from '../../notifications/lib/notificationService'\nimport { buildFeatureNotificationFromType } from '../../notifications/lib/notificationBuilder'\nimport { notificationTypes } from '../notifications'\nimport { MANAGE_PROJECTS_FEATURE } from '../lib/time-tracking/access'\n\nconst logger = createLogger('staff').child({ component: 'subscribers/time-project-access-requested' })\n\nconst NOTIFICATION_TYPE = 'staff.timesheets.project_access.requested'\nconst PROJECTS_LIST_HREF = '/backend/staff/time-tracking/projects'\n\nexport const metadata = {\n event: 'staff.timesheets.project_access.requested',\n persistent: true,\n id: 'staff:time-project-access-requested-notification',\n}\n\nexport type ProjectAccessRequestedPayload = {\n tenantId: string\n organizationId?: string | null\n requesterUserId: string\n requesterName?: string | null\n timeProjectId?: string | null\n timeProjectName?: string | null\n requestedAt?: string\n}\n\ntype ResolverContext = {\n resolve: <T = unknown>(name: string) => T\n}\n\n/**\n * Spec D-6: the notification action opens the project team drawer with the\n * requester pre-selected, so a Team Leader grants access without hunting for\n * the row. A general request carries no project, so it lands on the list.\n */\nexport function buildProjectAccessRequestLinkHref(input: {\n requesterUserId: string\n timeProjectId?: string | null\n}): string {\n if (!input.timeProjectId) return PROJECTS_LIST_HREF\n const projectSegment = encodeURIComponent(input.timeProjectId)\n const requesterParam = encodeURIComponent(input.requesterUserId)\n return `${PROJECTS_LIST_HREF}/${projectSegment}?panel=team&requesterUserId=${requesterParam}`\n}\n\nexport function buildProjectAccessRequestGroupKey(input: {\n requesterUserId: string\n timeProjectId?: string | null\n}): string {\n return `staff:time-project-access:${input.requesterUserId}:${input.timeProjectId ?? 'general'}`\n}\n\nexport default async function handle(payload: ProjectAccessRequestedPayload, ctx: ResolverContext) {\n try {\n if (!payload?.tenantId || !payload?.requesterUserId) return\n const typeDef = notificationTypes.find((type) => type.type === NOTIFICATION_TYPE)\n if (!typeDef) return\n\n const notificationService = resolveNotificationService(ctx)\n const requesterName = payload.requesterName?.trim() || payload.requesterUserId\n const timeProjectId = payload.timeProjectId ?? null\n\n const notificationInput = buildFeatureNotificationFromType(typeDef, {\n requiredFeature: MANAGE_PROJECTS_FEATURE,\n bodyVariables: {\n requesterName,\n ...(payload.timeProjectName ? { projectName: payload.timeProjectName } : {}),\n },\n sourceEntityType: 'staff:time_project',\n sourceEntityId: timeProjectId ?? undefined,\n linkHref: buildProjectAccessRequestLinkHref({\n requesterUserId: payload.requesterUserId,\n timeProjectId,\n }),\n groupKey: buildProjectAccessRequestGroupKey({\n requesterUserId: payload.requesterUserId,\n timeProjectId,\n }),\n })\n\n await notificationService.createForFeature(\n { ...notificationInput, restrictRecipientsToOrganization: true },\n {\n tenantId: payload.tenantId,\n organizationId: payload.organizationId ?? null,\n },\n )\n } catch (err) {\n logger.error('Failed to create the project access request notification', {\n subscriber: metadata.id,\n err,\n })\n }\n}\n"],
|
|
5
|
+
"mappings": "AAAA,SAAS,oBAAoB;AAC7B,SAAS,kCAAkC;AAC3C,SAAS,wCAAwC;AACjD,SAAS,yBAAyB;AAClC,SAAS,+BAA+B;AAExC,MAAM,SAAS,aAAa,OAAO,EAAE,MAAM,EAAE,WAAW,4CAA4C,CAAC;AAErG,MAAM,oBAAoB;AAC1B,MAAM,qBAAqB;AAEpB,MAAM,WAAW;AAAA,EACtB,OAAO;AAAA,EACP,YAAY;AAAA,EACZ,IAAI;AACN;AAqBO,SAAS,kCAAkC,OAGvC;AACT,MAAI,CAAC,MAAM,cAAe,QAAO;AACjC,QAAM,iBAAiB,mBAAmB,MAAM,aAAa;AAC7D,QAAM,iBAAiB,mBAAmB,MAAM,eAAe;AAC/D,SAAO,GAAG,kBAAkB,IAAI,cAAc,+BAA+B,cAAc;AAC7F;AAEO,SAAS,kCAAkC,OAGvC;AACT,SAAO,6BAA6B,MAAM,eAAe,IAAI,MAAM,iBAAiB,SAAS;AAC/F;AAEA,eAAO,OAA8B,SAAwC,KAAsB;AACjG,MAAI;AACF,QAAI,CAAC,SAAS,YAAY,CAAC,SAAS,gBAAiB;AACrD,UAAM,UAAU,kBAAkB,KAAK,CAAC,SAAS,KAAK,SAAS,iBAAiB;AAChF,QAAI,CAAC,QAAS;AAEd,UAAM,sBAAsB,2BAA2B,GAAG;AAC1D,UAAM,gBAAgB,QAAQ,eAAe,KAAK,KAAK,QAAQ;AAC/D,UAAM,gBAAgB,QAAQ,iBAAiB;AAE/C,UAAM,oBAAoB,iCAAiC,SAAS;AAAA,MAClE,iBAAiB;AAAA,MACjB,eAAe;AAAA,QACb;AAAA,QACA,GAAI,QAAQ,kBAAkB,EAAE,aAAa,QAAQ,gBAAgB,IAAI,CAAC;AAAA,MAC5E;AAAA,MACA,kBAAkB;AAAA,MAClB,gBAAgB,iBAAiB;AAAA,MACjC,UAAU,kCAAkC;AAAA,QAC1C,iBAAiB,QAAQ;AAAA,QACzB;AAAA,MACF,CAAC;AAAA,MACD,UAAU,kCAAkC;AAAA,QAC1C,iBAAiB,QAAQ;AAAA,QACzB;AAAA,MACF,CAAC;AAAA,IACH,CAAC;AAED,UAAM,oBAAoB;AAAA,MACxB,EAAE,GAAG,mBAAmB,kCAAkC,KAAK;AAAA,MAC/D;AAAA,QACE,UAAU,QAAQ;AAAA,QAClB,gBAAgB,QAAQ,kBAAkB;AAAA,MAC5C;AAAA,IACF;AAAA,EACF,SAAS,KAAK;AACZ,WAAO,MAAM,4DAA4D;AAAA,MACvE,YAAY,SAAS;AAAA,MACrB;AAAA,IACF,CAAC;AAAA,EACH;AACF;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import { createLogger } from "@open-mercato/shared/lib/logger";
|
|
2
|
+
import { resolveNotificationService } from "../../notifications/lib/notificationService.js";
|
|
3
|
+
import {
|
|
4
|
+
buildFeatureNotificationFromType,
|
|
5
|
+
buildNotificationFromType
|
|
6
|
+
} from "../../notifications/lib/notificationBuilder.js";
|
|
7
|
+
import { notificationTypes } from "../notifications.js";
|
|
8
|
+
import { emitStaffEvent } from "../events.js";
|
|
9
|
+
import { MANAGE_PROJECTS_FEATURE } from "../lib/time-tracking/access.js";
|
|
10
|
+
import { computeProjectFinancials } from "../lib/timesheets-projects/computeProjectFinancials.js";
|
|
11
|
+
import { FULL_BUDGET_PERCENT, evaluateBudgetThreshold } from "../lib/timesheets-projects/budgetThreshold.js";
|
|
12
|
+
import {
|
|
13
|
+
claimBudgetThresholdAlert,
|
|
14
|
+
loadTimeProjectBudgetStateForEntry
|
|
15
|
+
} from "../lib/timesheets-projects/budgetThresholdState.js";
|
|
16
|
+
const logger = createLogger("staff").child({ component: "subscribers/time-project-budget-threshold" });
|
|
17
|
+
const NOTIFICATION_TYPE = "staff.timesheets.time_project.budget_threshold_reached";
|
|
18
|
+
const PROJECTS_LIST_HREF = "/backend/staff/time-tracking/projects";
|
|
19
|
+
const metadata = {
|
|
20
|
+
event: "staff.timesheets.time_entry.*",
|
|
21
|
+
persistent: true,
|
|
22
|
+
id: "staff:time-project-budget-threshold-notification"
|
|
23
|
+
};
|
|
24
|
+
function buildBudgetThresholdLinkHref(timeProjectId) {
|
|
25
|
+
return `${PROJECTS_LIST_HREF}/${encodeURIComponent(timeProjectId)}`;
|
|
26
|
+
}
|
|
27
|
+
function buildBudgetThresholdGroupKey(input) {
|
|
28
|
+
return `staff:time-project-budget:${input.timeProjectId}:${input.thresholdPercent}`;
|
|
29
|
+
}
|
|
30
|
+
function resolveBudgetThresholdSeverity(thresholdPercent) {
|
|
31
|
+
return thresholdPercent >= FULL_BUDGET_PERCENT ? "error" : "warning";
|
|
32
|
+
}
|
|
33
|
+
async function handle(payload, ctx) {
|
|
34
|
+
try {
|
|
35
|
+
const timeEntryId = payload?.id ?? null;
|
|
36
|
+
const tenantId = payload?.tenantId ?? null;
|
|
37
|
+
const organizationId = payload?.organizationId ?? null;
|
|
38
|
+
if (!timeEntryId || !tenantId || !organizationId) return;
|
|
39
|
+
const typeDef = notificationTypes.find((type) => type.type === NOTIFICATION_TYPE);
|
|
40
|
+
if (!typeDef) return;
|
|
41
|
+
const em = ctx.resolve("em").fork();
|
|
42
|
+
const project = await loadTimeProjectBudgetStateForEntry({ em, tenantId, organizationId, timeEntryId });
|
|
43
|
+
if (!project) return;
|
|
44
|
+
if (project.budgetKind === "none" && project.budgetAlertedAtPercent === null) return;
|
|
45
|
+
const financials = await computeProjectFinancials({
|
|
46
|
+
em,
|
|
47
|
+
tenantId,
|
|
48
|
+
organizationId,
|
|
49
|
+
projectIds: [project.timeProjectId],
|
|
50
|
+
hourlyRateByProjectId: /* @__PURE__ */ new Map([[project.timeProjectId, project.hourlyRate]])
|
|
51
|
+
});
|
|
52
|
+
const totals = financials.get(project.timeProjectId);
|
|
53
|
+
const evaluation = evaluateBudgetThreshold({
|
|
54
|
+
budgetKind: project.budgetKind,
|
|
55
|
+
budgetValue: project.budgetValue,
|
|
56
|
+
budgetWarnAtPercent: project.budgetWarnAtPercent,
|
|
57
|
+
budgetAlertedAtPercent: project.budgetAlertedAtPercent,
|
|
58
|
+
totalMinutes: totals?.totalMinutes ?? 0,
|
|
59
|
+
cost: totals?.cost ?? null,
|
|
60
|
+
currencyCode: project.currencyCode
|
|
61
|
+
});
|
|
62
|
+
if (!evaluation.shouldPersistAlertedAt) return;
|
|
63
|
+
const claimed = await claimBudgetThresholdAlert({
|
|
64
|
+
em,
|
|
65
|
+
tenantId,
|
|
66
|
+
organizationId,
|
|
67
|
+
timeProjectId: project.timeProjectId,
|
|
68
|
+
expectedAlertedAtPercent: project.budgetAlertedAtPercent,
|
|
69
|
+
nextAlertedAtPercent: evaluation.nextAlertedAtPercent
|
|
70
|
+
});
|
|
71
|
+
if (!claimed) return;
|
|
72
|
+
const thresholdPercent = evaluation.crossedThresholdPercent;
|
|
73
|
+
if (thresholdPercent === null) return;
|
|
74
|
+
const budgetIsMoney = evaluation.kind === "amount";
|
|
75
|
+
await emitStaffEvent(
|
|
76
|
+
"staff.timesheets.time_project.budget_threshold_reached",
|
|
77
|
+
{
|
|
78
|
+
id: project.timeProjectId,
|
|
79
|
+
timeProjectId: project.timeProjectId,
|
|
80
|
+
tenantId,
|
|
81
|
+
organizationId,
|
|
82
|
+
thresholdPercent,
|
|
83
|
+
percent: evaluation.percent,
|
|
84
|
+
budgetKind: evaluation.kind,
|
|
85
|
+
...budgetIsMoney ? {} : { budgetValue: evaluation.budgetValue, usedValue: evaluation.usedValue }
|
|
86
|
+
},
|
|
87
|
+
{ persistent: true }
|
|
88
|
+
);
|
|
89
|
+
const notificationService = resolveNotificationService(ctx);
|
|
90
|
+
const scope = { tenantId, organizationId };
|
|
91
|
+
const severity = resolveBudgetThresholdSeverity(thresholdPercent);
|
|
92
|
+
const commonOptions = {
|
|
93
|
+
bodyVariables: {
|
|
94
|
+
projectName: project.name || project.timeProjectId,
|
|
95
|
+
percent: String(evaluation.percent ?? 0),
|
|
96
|
+
thresholdPercent: String(thresholdPercent)
|
|
97
|
+
},
|
|
98
|
+
sourceEntityType: "staff:time_project",
|
|
99
|
+
sourceEntityId: project.timeProjectId,
|
|
100
|
+
linkHref: buildBudgetThresholdLinkHref(project.timeProjectId),
|
|
101
|
+
groupKey: buildBudgetThresholdGroupKey({ timeProjectId: project.timeProjectId, thresholdPercent })
|
|
102
|
+
};
|
|
103
|
+
const featureNotifications = await notificationService.createForFeature(
|
|
104
|
+
{
|
|
105
|
+
...buildFeatureNotificationFromType(typeDef, {
|
|
106
|
+
...commonOptions,
|
|
107
|
+
requiredFeature: MANAGE_PROJECTS_FEATURE
|
|
108
|
+
}),
|
|
109
|
+
severity,
|
|
110
|
+
restrictRecipientsToOrganization: true
|
|
111
|
+
},
|
|
112
|
+
scope
|
|
113
|
+
);
|
|
114
|
+
const ownerUserId = project.ownerUserId;
|
|
115
|
+
if (!ownerUserId) return;
|
|
116
|
+
const ownerAlreadyNotified = featureNotifications.some(
|
|
117
|
+
(notification) => notification?.recipientUserId === ownerUserId
|
|
118
|
+
);
|
|
119
|
+
if (ownerAlreadyNotified) return;
|
|
120
|
+
try {
|
|
121
|
+
await notificationService.create(
|
|
122
|
+
{
|
|
123
|
+
...buildNotificationFromType(typeDef, { ...commonOptions, recipientUserId: ownerUserId }),
|
|
124
|
+
severity
|
|
125
|
+
},
|
|
126
|
+
scope
|
|
127
|
+
);
|
|
128
|
+
} catch (err) {
|
|
129
|
+
logger.warn("Skipped the budget threshold notification for an out-of-scope project owner", {
|
|
130
|
+
subscriber: metadata.id,
|
|
131
|
+
timeProjectId: project.timeProjectId,
|
|
132
|
+
err
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
} catch (err) {
|
|
136
|
+
logger.error("Failed to evaluate the time project budget threshold", {
|
|
137
|
+
subscriber: metadata.id,
|
|
138
|
+
err
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
export {
|
|
143
|
+
buildBudgetThresholdGroupKey,
|
|
144
|
+
buildBudgetThresholdLinkHref,
|
|
145
|
+
handle as default,
|
|
146
|
+
metadata,
|
|
147
|
+
resolveBudgetThresholdSeverity
|
|
148
|
+
};
|
|
149
|
+
//# sourceMappingURL=time-project-budget-threshold-notification.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../src/modules/staff/subscribers/time-project-budget-threshold-notification.ts"],
|
|
4
|
+
"sourcesContent": ["import type { EntityManager } from '@mikro-orm/postgresql'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\nimport type { NotificationSeverity } from '@open-mercato/shared/modules/notifications/types'\nimport { resolveNotificationService } from '../../notifications/lib/notificationService'\nimport {\n buildFeatureNotificationFromType,\n buildNotificationFromType,\n} from '../../notifications/lib/notificationBuilder'\nimport { notificationTypes } from '../notifications'\nimport { emitStaffEvent } from '../events'\nimport { MANAGE_PROJECTS_FEATURE } from '../lib/time-tracking/access'\nimport { computeProjectFinancials } from '../lib/timesheets-projects/computeProjectFinancials'\nimport { FULL_BUDGET_PERCENT, evaluateBudgetThreshold } from '../lib/timesheets-projects/budgetThreshold'\nimport {\n claimBudgetThresholdAlert,\n loadTimeProjectBudgetStateForEntry,\n} from '../lib/timesheets-projects/budgetThresholdState'\n\nconst logger = createLogger('staff').child({ component: 'subscribers/time-project-budget-threshold' })\n\nconst NOTIFICATION_TYPE = 'staff.timesheets.time_project.budget_threshold_reached'\nconst PROJECTS_LIST_HREF = '/backend/staff/time-tracking/projects'\n\n/**\n * Every write that can move a project's logged minutes or cost travels as a\n * `staff.timesheets.time_entry.*` event, but not all of them by the same route:\n *\n * - create / update / delete (soft) are emitted by the time-entry **commands**\n * through `staffTimeEntryCrudEvents` (`lib/crud.ts`), which the CRUD route and\n * `/copy-day` and `/[id]/duplicate` all drive;\n * - `/bulk` emits the same config itself, once per changed row, because it writes\n * through the EntityManager instead of the command bus;\n * - the timer transitions emit `\u2026timer_started` / `\u2026timer_stopped` explicitly.\n *\n * All of those carry the `{ id, tenantId, organizationId }` payload this handler\n * needs. The batch-level members of the family \u2014 `\u2026bulk_updated`, `\u2026locked`,\n * `\u2026unlocked` \u2014 deliberately carry no `id` and fall out at the guard below, because\n * the per-row events of the same write already covered every affected project.\n *\n * Subscribing to the family rather than three exact ids is what keeps a future write\n * path from silently skipping the budget check.\n *\n * `persistent: true`: this ends in a user-visible alert about money and hours\n * already spent. It has to survive a worker restart and be retried on failure \u2014 an\n * ephemeral subscription would drop the alert whenever the process handling the\n * entry write went away, and nothing would ever raise it again.\n */\nexport const metadata = {\n event: 'staff.timesheets.time_entry.*',\n persistent: true,\n id: 'staff:time-project-budget-threshold-notification',\n}\n\nexport type TimeEntryWritePayload = {\n id?: string | null\n tenantId?: string | null\n organizationId?: string | null\n}\n\ntype ResolverContext = {\n resolve: <T = unknown>(name: string) => T\n}\n\nexport function buildBudgetThresholdLinkHref(timeProjectId: string): string {\n return `${PROJECTS_LIST_HREF}/${encodeURIComponent(timeProjectId)}`\n}\n\n/**\n * One group key per project AND threshold, so the 80% alert and the 100% alert are\n * two separate items in the bell, while a retry of the same crossing refreshes the\n * existing item instead of stacking a duplicate.\n */\nexport function buildBudgetThresholdGroupKey(input: {\n timeProjectId: string\n thresholdPercent: number\n}): string {\n return `staff:time-project-budget:${input.timeProjectId}:${input.thresholdPercent}`\n}\n\nexport function resolveBudgetThresholdSeverity(thresholdPercent: number): NotificationSeverity {\n return thresholdPercent >= FULL_BUDGET_PERCENT ? 'error' : 'warning'\n}\n\nexport default async function handle(payload: TimeEntryWritePayload, ctx: ResolverContext) {\n try {\n const timeEntryId = payload?.id ?? null\n const tenantId = payload?.tenantId ?? null\n const organizationId = payload?.organizationId ?? null\n if (!timeEntryId || !tenantId || !organizationId) return\n\n const typeDef = notificationTypes.find((type) => type.type === NOTIFICATION_TYPE)\n if (!typeDef) return\n\n const em = (ctx.resolve('em') as EntityManager).fork()\n const project = await loadTimeProjectBudgetStateForEntry({ em, tenantId, organizationId, timeEntryId })\n if (!project) return\n // A project without a budget never notifies. It is also the common case, so it\n // exits before the aggregate query \u2014 unless a stale marker from a budget that\n // has since been switched off still needs clearing.\n if (project.budgetKind === 'none' && project.budgetAlertedAtPercent === null) return\n\n const financials = await computeProjectFinancials({\n em,\n tenantId,\n organizationId,\n projectIds: [project.timeProjectId],\n hourlyRateByProjectId: new Map([[project.timeProjectId, project.hourlyRate]]),\n })\n const totals = financials.get(project.timeProjectId)\n\n const evaluation = evaluateBudgetThreshold({\n budgetKind: project.budgetKind,\n budgetValue: project.budgetValue,\n budgetWarnAtPercent: project.budgetWarnAtPercent,\n budgetAlertedAtPercent: project.budgetAlertedAtPercent,\n totalMinutes: totals?.totalMinutes ?? 0,\n cost: totals?.cost ?? null,\n currencyCode: project.currencyCode,\n })\n\n if (!evaluation.shouldPersistAlertedAt) return\n\n // Compare-and-swap on the marker: the loser of a race between two concurrent\n // entry writes stops here, so a threshold is announced exactly once. The marker\n // is written before the alert, which makes this at-most-once by design \u2014 a\n // repeated alert about the same crossing is worse than a missed retry.\n const claimed = await claimBudgetThresholdAlert({\n em,\n tenantId,\n organizationId,\n timeProjectId: project.timeProjectId,\n expectedAlertedAtPercent: project.budgetAlertedAtPercent,\n nextAlertedAtPercent: evaluation.nextAlertedAtPercent,\n })\n if (!claimed) return\n\n const thresholdPercent = evaluation.crossedThresholdPercent\n // The marker moved down (usage fell back under the band it was raised for) or\n // the budget was switched off. Nothing to announce; the reset is the point.\n if (thresholdPercent === null) return\n\n // This event is `clientBroadcast: true`, and the DOM Event Bridge filters only by\n // tenant and organization \u2014 every signed-in user of the organization receives the\n // payload, whether or not they hold `staff.timesheets.rates.view`. An `amount`\n // budget states money (`budgetValue` is the contract value, `usedValue` the cost\n // burned against it), so those two are carried only for an `hours` budget, exactly\n // as `/my-work` withholds an amount budget from a caller who may not see money.\n // The percentages are not money and are what a live budget badge actually renders.\n const budgetIsMoney = evaluation.kind === 'amount'\n await emitStaffEvent(\n 'staff.timesheets.time_project.budget_threshold_reached',\n {\n id: project.timeProjectId,\n timeProjectId: project.timeProjectId,\n tenantId,\n organizationId,\n thresholdPercent,\n percent: evaluation.percent,\n budgetKind: evaluation.kind,\n ...(budgetIsMoney ? {} : { budgetValue: evaluation.budgetValue, usedValue: evaluation.usedValue }),\n },\n { persistent: true },\n )\n\n const notificationService = resolveNotificationService(ctx)\n const scope = { tenantId, organizationId }\n const severity = resolveBudgetThresholdSeverity(thresholdPercent)\n const commonOptions = {\n bodyVariables: {\n projectName: project.name || project.timeProjectId,\n percent: String(evaluation.percent ?? 0),\n thresholdPercent: String(thresholdPercent),\n },\n sourceEntityType: 'staff:time_project',\n sourceEntityId: project.timeProjectId,\n linkHref: buildBudgetThresholdLinkHref(project.timeProjectId),\n groupKey: buildBudgetThresholdGroupKey({ timeProjectId: project.timeProjectId, thresholdPercent }),\n }\n\n // Manage-feature holders are resolved through the notification service's feature\n // fan-out, which matches grants with the shared feature policy \u2014 a `staff.*`\n // wildcard counts \u2014 and then re-checks each candidate against this organization\n // through the RBAC service.\n const featureNotifications = await notificationService.createForFeature(\n {\n ...buildFeatureNotificationFromType(typeDef, {\n ...commonOptions,\n requiredFeature: MANAGE_PROJECTS_FEATURE,\n }),\n severity,\n restrictRecipientsToOrganization: true,\n },\n scope,\n )\n\n const ownerUserId = project.ownerUserId\n if (!ownerUserId) return\n const ownerAlreadyNotified = featureNotifications.some(\n (notification) => notification?.recipientUserId === ownerUserId,\n )\n if (ownerAlreadyNotified) return\n\n // The owner is notified even without the manage feature \u2014 it is their project's\n // budget. An owner who has left the scope is not an error worth failing on.\n try {\n await notificationService.create(\n {\n ...buildNotificationFromType(typeDef, { ...commonOptions, recipientUserId: ownerUserId }),\n severity,\n },\n scope,\n )\n } catch (err) {\n logger.warn('Skipped the budget threshold notification for an out-of-scope project owner', {\n subscriber: metadata.id,\n timeProjectId: project.timeProjectId,\n err,\n })\n }\n } catch (err) {\n logger.error('Failed to evaluate the time project budget threshold', {\n subscriber: metadata.id,\n err,\n })\n }\n}\n"],
|
|
5
|
+
"mappings": "AACA,SAAS,oBAAoB;AAE7B,SAAS,kCAAkC;AAC3C;AAAA,EACE;AAAA,EACA;AAAA,OACK;AACP,SAAS,yBAAyB;AAClC,SAAS,sBAAsB;AAC/B,SAAS,+BAA+B;AACxC,SAAS,gCAAgC;AACzC,SAAS,qBAAqB,+BAA+B;AAC7D;AAAA,EACE;AAAA,EACA;AAAA,OACK;AAEP,MAAM,SAAS,aAAa,OAAO,EAAE,MAAM,EAAE,WAAW,4CAA4C,CAAC;AAErG,MAAM,oBAAoB;AAC1B,MAAM,qBAAqB;AA0BpB,MAAM,WAAW;AAAA,EACtB,OAAO;AAAA,EACP,YAAY;AAAA,EACZ,IAAI;AACN;AAYO,SAAS,6BAA6B,eAA+B;AAC1E,SAAO,GAAG,kBAAkB,IAAI,mBAAmB,aAAa,CAAC;AACnE;AAOO,SAAS,6BAA6B,OAGlC;AACT,SAAO,6BAA6B,MAAM,aAAa,IAAI,MAAM,gBAAgB;AACnF;AAEO,SAAS,+BAA+B,kBAAgD;AAC7F,SAAO,oBAAoB,sBAAsB,UAAU;AAC7D;AAEA,eAAO,OAA8B,SAAgC,KAAsB;AACzF,MAAI;AACF,UAAM,cAAc,SAAS,MAAM;AACnC,UAAM,WAAW,SAAS,YAAY;AACtC,UAAM,iBAAiB,SAAS,kBAAkB;AAClD,QAAI,CAAC,eAAe,CAAC,YAAY,CAAC,eAAgB;AAElD,UAAM,UAAU,kBAAkB,KAAK,CAAC,SAAS,KAAK,SAAS,iBAAiB;AAChF,QAAI,CAAC,QAAS;AAEd,UAAM,KAAM,IAAI,QAAQ,IAAI,EAAoB,KAAK;AACrD,UAAM,UAAU,MAAM,mCAAmC,EAAE,IAAI,UAAU,gBAAgB,YAAY,CAAC;AACtG,QAAI,CAAC,QAAS;AAId,QAAI,QAAQ,eAAe,UAAU,QAAQ,2BAA2B,KAAM;AAE9E,UAAM,aAAa,MAAM,yBAAyB;AAAA,MAChD;AAAA,MACA;AAAA,MACA;AAAA,MACA,YAAY,CAAC,QAAQ,aAAa;AAAA,MAClC,uBAAuB,oBAAI,IAAI,CAAC,CAAC,QAAQ,eAAe,QAAQ,UAAU,CAAC,CAAC;AAAA,IAC9E,CAAC;AACD,UAAM,SAAS,WAAW,IAAI,QAAQ,aAAa;AAEnD,UAAM,aAAa,wBAAwB;AAAA,MACzC,YAAY,QAAQ;AAAA,MACpB,aAAa,QAAQ;AAAA,MACrB,qBAAqB,QAAQ;AAAA,MAC7B,wBAAwB,QAAQ;AAAA,MAChC,cAAc,QAAQ,gBAAgB;AAAA,MACtC,MAAM,QAAQ,QAAQ;AAAA,MACtB,cAAc,QAAQ;AAAA,IACxB,CAAC;AAED,QAAI,CAAC,WAAW,uBAAwB;AAMxC,UAAM,UAAU,MAAM,0BAA0B;AAAA,MAC9C;AAAA,MACA;AAAA,MACA;AAAA,MACA,eAAe,QAAQ;AAAA,MACvB,0BAA0B,QAAQ;AAAA,MAClC,sBAAsB,WAAW;AAAA,IACnC,CAAC;AACD,QAAI,CAAC,QAAS;AAEd,UAAM,mBAAmB,WAAW;AAGpC,QAAI,qBAAqB,KAAM;AAS/B,UAAM,gBAAgB,WAAW,SAAS;AAC1C,UAAM;AAAA,MACJ;AAAA,MACA;AAAA,QACE,IAAI,QAAQ;AAAA,QACZ,eAAe,QAAQ;AAAA,QACvB;AAAA,QACA;AAAA,QACA;AAAA,QACA,SAAS,WAAW;AAAA,QACpB,YAAY,WAAW;AAAA,QACvB,GAAI,gBAAgB,CAAC,IAAI,EAAE,aAAa,WAAW,aAAa,WAAW,WAAW,UAAU;AAAA,MAClG;AAAA,MACA,EAAE,YAAY,KAAK;AAAA,IACrB;AAEA,UAAM,sBAAsB,2BAA2B,GAAG;AAC1D,UAAM,QAAQ,EAAE,UAAU,eAAe;AACzC,UAAM,WAAW,+BAA+B,gBAAgB;AAChE,UAAM,gBAAgB;AAAA,MACpB,eAAe;AAAA,QACb,aAAa,QAAQ,QAAQ,QAAQ;AAAA,QACrC,SAAS,OAAO,WAAW,WAAW,CAAC;AAAA,QACvC,kBAAkB,OAAO,gBAAgB;AAAA,MAC3C;AAAA,MACA,kBAAkB;AAAA,MAClB,gBAAgB,QAAQ;AAAA,MACxB,UAAU,6BAA6B,QAAQ,aAAa;AAAA,MAC5D,UAAU,6BAA6B,EAAE,eAAe,QAAQ,eAAe,iBAAiB,CAAC;AAAA,IACnG;AAMA,UAAM,uBAAuB,MAAM,oBAAoB;AAAA,MACrD;AAAA,QACE,GAAG,iCAAiC,SAAS;AAAA,UAC3C,GAAG;AAAA,UACH,iBAAiB;AAAA,QACnB,CAAC;AAAA,QACD;AAAA,QACA,kCAAkC;AAAA,MACpC;AAAA,MACA;AAAA,IACF;AAEA,UAAM,cAAc,QAAQ;AAC5B,QAAI,CAAC,YAAa;AAClB,UAAM,uBAAuB,qBAAqB;AAAA,MAChD,CAAC,iBAAiB,cAAc,oBAAoB;AAAA,IACtD;AACA,QAAI,qBAAsB;AAI1B,QAAI;AACF,YAAM,oBAAoB;AAAA,QACxB;AAAA,UACE,GAAG,0BAA0B,SAAS,EAAE,GAAG,eAAe,iBAAiB,YAAY,CAAC;AAAA,UACxF;AAAA,QACF;AAAA,QACA;AAAA,MACF;AAAA,IACF,SAAS,KAAK;AACZ,aAAO,KAAK,+EAA+E;AAAA,QACzF,YAAY,SAAS;AAAA,QACrB,eAAe,QAAQ;AAAA,QACvB;AAAA,MACF,CAAC;AAAA,IACH;AAAA,EACF,SAAS,KAAK;AACZ,WAAO,MAAM,wDAAwD;AAAA,MACnE,YAAY,SAAS;AAAA,MACrB;AAAA,IACF,CAAC;AAAA,EACH;AACF;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { createLogger } from "@open-mercato/shared/lib/logger";
|
|
2
|
+
import { findOneWithDecryption } from "@open-mercato/shared/lib/encryption/find";
|
|
3
|
+
import { resolveNotificationService } from "../../notifications/lib/notificationService.js";
|
|
4
|
+
import { buildNotificationFromType } from "../../notifications/lib/notificationBuilder.js";
|
|
5
|
+
import { notificationTypes } from "../notifications.js";
|
|
6
|
+
import { StaffTimeReport } from "../data/entities.js";
|
|
7
|
+
const logger = createLogger("staff").child({ component: "subscribers/time-report-approved" });
|
|
8
|
+
const NOTIFICATION_TYPE = "staff.timesheets.time_report.approved";
|
|
9
|
+
const REPORTS_LIST_HREF = "/backend/staff/time-tracking/reports";
|
|
10
|
+
const metadata = {
|
|
11
|
+
event: "staff.timesheets.time_report.closed",
|
|
12
|
+
persistent: true,
|
|
13
|
+
id: "staff:time-report-approved-notification"
|
|
14
|
+
};
|
|
15
|
+
function buildTimeReportLinkHref(reportId) {
|
|
16
|
+
return `${REPORTS_LIST_HREF}/${encodeURIComponent(reportId)}`;
|
|
17
|
+
}
|
|
18
|
+
function buildTimeReportApprovedGroupKey(reportId) {
|
|
19
|
+
return `staff:time-report-approved:${reportId}`;
|
|
20
|
+
}
|
|
21
|
+
function readId(...candidates) {
|
|
22
|
+
for (const candidate of candidates) {
|
|
23
|
+
if (typeof candidate !== "string") continue;
|
|
24
|
+
const trimmed = candidate.trim();
|
|
25
|
+
if (trimmed.length > 0) return trimmed;
|
|
26
|
+
}
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
async function handle(payload, ctx) {
|
|
30
|
+
try {
|
|
31
|
+
const reportId = readId(payload?.reportId, payload?.id);
|
|
32
|
+
const tenantId = readId(payload?.tenantId);
|
|
33
|
+
const organizationId = readId(payload?.organizationId);
|
|
34
|
+
if (!reportId || !tenantId || !organizationId) return;
|
|
35
|
+
const typeDef = notificationTypes.find((entry) => entry.type === NOTIFICATION_TYPE);
|
|
36
|
+
if (!typeDef) return;
|
|
37
|
+
const em = ctx.resolve("em").fork();
|
|
38
|
+
const scope = { tenantId, organizationId };
|
|
39
|
+
const report = await findOneWithDecryption(
|
|
40
|
+
em,
|
|
41
|
+
StaffTimeReport,
|
|
42
|
+
{ id: reportId, tenantId, organizationId, deletedAt: null },
|
|
43
|
+
{},
|
|
44
|
+
scope
|
|
45
|
+
);
|
|
46
|
+
if (!report) return;
|
|
47
|
+
const recipientUserId = readId(report.createdByUserId);
|
|
48
|
+
if (!recipientUserId) return;
|
|
49
|
+
if (recipientUserId === readId(report.closedByUserId, payload?.closedByUserId)) return;
|
|
50
|
+
const notificationService = resolveNotificationService(ctx);
|
|
51
|
+
await notificationService.create(
|
|
52
|
+
buildNotificationFromType(typeDef, {
|
|
53
|
+
recipientUserId,
|
|
54
|
+
bodyVariables: {
|
|
55
|
+
reference: report.reference,
|
|
56
|
+
title: report.title
|
|
57
|
+
},
|
|
58
|
+
sourceEntityType: "staff:time_report",
|
|
59
|
+
sourceEntityId: report.id,
|
|
60
|
+
linkHref: buildTimeReportLinkHref(report.id),
|
|
61
|
+
groupKey: buildTimeReportApprovedGroupKey(report.id)
|
|
62
|
+
}),
|
|
63
|
+
scope
|
|
64
|
+
);
|
|
65
|
+
} catch (err) {
|
|
66
|
+
logger.error("Failed to raise the time report approved notification", {
|
|
67
|
+
subscriber: metadata.id,
|
|
68
|
+
err
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
export {
|
|
73
|
+
buildTimeReportApprovedGroupKey,
|
|
74
|
+
buildTimeReportLinkHref,
|
|
75
|
+
handle as default,
|
|
76
|
+
metadata
|
|
77
|
+
};
|
|
78
|
+
//# sourceMappingURL=time-report-approved-notification.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../src/modules/staff/subscribers/time-report-approved-notification.ts"],
|
|
4
|
+
"sourcesContent": ["import type { EntityManager } from '@mikro-orm/postgresql'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\nimport { findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport { resolveNotificationService } from '../../notifications/lib/notificationService'\nimport { buildNotificationFromType } from '../../notifications/lib/notificationBuilder'\nimport { notificationTypes } from '../notifications'\nimport { StaffTimeReport } from '../data/entities'\n\nconst logger = createLogger('staff').child({ component: 'subscribers/time-report-approved' })\n\nconst NOTIFICATION_TYPE = 'staff.timesheets.time_report.approved'\nconst REPORTS_LIST_HREF = '/backend/staff/time-tracking/reports'\n\n/**\n * EP-48. Closing a report IS its approval in this module: the close freezes every\n * per-entry rate and amount, locks the entries against further edits, and is\n * gated on `staff.timesheets.lock`. There is no separate \"approved\" transition to\n * subscribe to, and adding one would be a behaviour change, not an extension\n * point \u2014 so this subscribes to the close and announces it to the one person who\n * otherwise finds out by reopening the screen: whoever drafted the report.\n *\n * The closer is deliberately not notified about their own action, and a report\n * with no recorded author is a normal outcome (a seeded or imported report), not\n * an error.\n *\n * `persistent: true` \u2014 a report closing is a billing milestone; the alert has to\n * survive a worker restart and be retried, the same reasoning as the budget\n * threshold subscriber next to it.\n */\nexport const metadata = {\n event: 'staff.timesheets.time_report.closed',\n persistent: true,\n id: 'staff:time-report-approved-notification',\n}\n\nexport type TimeReportClosedPayload = {\n id?: string | null\n reportId?: string | null\n reference?: string | null\n tenantId?: string | null\n organizationId?: string | null\n closedByUserId?: string | null\n}\n\ntype ResolverContext = {\n resolve: <T = unknown>(name: string) => T\n}\n\nexport function buildTimeReportLinkHref(reportId: string): string {\n return `${REPORTS_LIST_HREF}/${encodeURIComponent(reportId)}`\n}\n\n/** One item per report, so a re-close after an unlock refreshes rather than stacks. */\nexport function buildTimeReportApprovedGroupKey(reportId: string): string {\n return `staff:time-report-approved:${reportId}`\n}\n\nfunction readId(...candidates: Array<unknown>): string | null {\n for (const candidate of candidates) {\n if (typeof candidate !== 'string') continue\n const trimmed = candidate.trim()\n if (trimmed.length > 0) return trimmed\n }\n return null\n}\n\nexport default async function handle(\n payload: TimeReportClosedPayload,\n ctx: ResolverContext,\n): Promise<void> {\n try {\n const reportId = readId(payload?.reportId, payload?.id)\n const tenantId = readId(payload?.tenantId)\n const organizationId = readId(payload?.organizationId)\n if (!reportId || !tenantId || !organizationId) return\n\n const typeDef = notificationTypes.find((entry) => entry.type === NOTIFICATION_TYPE)\n if (!typeDef) return\n\n const em = ctx.resolve<EntityManager>('em').fork()\n const scope = { tenantId, organizationId }\n const report = await findOneWithDecryption(\n em,\n StaffTimeReport,\n { id: reportId, tenantId, organizationId, deletedAt: null },\n {},\n scope,\n )\n if (!report) return\n\n const recipientUserId = readId(report.createdByUserId)\n if (!recipientUserId) return\n if (recipientUserId === readId(report.closedByUserId, payload?.closedByUserId)) return\n\n const notificationService = resolveNotificationService(ctx)\n await notificationService.create(\n buildNotificationFromType(typeDef, {\n recipientUserId,\n bodyVariables: {\n reference: report.reference,\n title: report.title,\n },\n sourceEntityType: 'staff:time_report',\n sourceEntityId: report.id,\n linkHref: buildTimeReportLinkHref(report.id),\n groupKey: buildTimeReportApprovedGroupKey(report.id),\n }),\n scope,\n )\n } catch (err) {\n logger.error('Failed to raise the time report approved notification', {\n subscriber: metadata.id,\n err,\n })\n }\n}\n"],
|
|
5
|
+
"mappings": "AACA,SAAS,oBAAoB;AAC7B,SAAS,6BAA6B;AACtC,SAAS,kCAAkC;AAC3C,SAAS,iCAAiC;AAC1C,SAAS,yBAAyB;AAClC,SAAS,uBAAuB;AAEhC,MAAM,SAAS,aAAa,OAAO,EAAE,MAAM,EAAE,WAAW,mCAAmC,CAAC;AAE5F,MAAM,oBAAoB;AAC1B,MAAM,oBAAoB;AAkBnB,MAAM,WAAW;AAAA,EACtB,OAAO;AAAA,EACP,YAAY;AAAA,EACZ,IAAI;AACN;AAeO,SAAS,wBAAwB,UAA0B;AAChE,SAAO,GAAG,iBAAiB,IAAI,mBAAmB,QAAQ,CAAC;AAC7D;AAGO,SAAS,gCAAgC,UAA0B;AACxE,SAAO,8BAA8B,QAAQ;AAC/C;AAEA,SAAS,UAAU,YAA2C;AAC5D,aAAW,aAAa,YAAY;AAClC,QAAI,OAAO,cAAc,SAAU;AACnC,UAAM,UAAU,UAAU,KAAK;AAC/B,QAAI,QAAQ,SAAS,EAAG,QAAO;AAAA,EACjC;AACA,SAAO;AACT;AAEA,eAAO,OACL,SACA,KACe;AACf,MAAI;AACF,UAAM,WAAW,OAAO,SAAS,UAAU,SAAS,EAAE;AACtD,UAAM,WAAW,OAAO,SAAS,QAAQ;AACzC,UAAM,iBAAiB,OAAO,SAAS,cAAc;AACrD,QAAI,CAAC,YAAY,CAAC,YAAY,CAAC,eAAgB;AAE/C,UAAM,UAAU,kBAAkB,KAAK,CAAC,UAAU,MAAM,SAAS,iBAAiB;AAClF,QAAI,CAAC,QAAS;AAEd,UAAM,KAAK,IAAI,QAAuB,IAAI,EAAE,KAAK;AACjD,UAAM,QAAQ,EAAE,UAAU,eAAe;AACzC,UAAM,SAAS,MAAM;AAAA,MACnB;AAAA,MACA;AAAA,MACA,EAAE,IAAI,UAAU,UAAU,gBAAgB,WAAW,KAAK;AAAA,MAC1D,CAAC;AAAA,MACD;AAAA,IACF;AACA,QAAI,CAAC,OAAQ;AAEb,UAAM,kBAAkB,OAAO,OAAO,eAAe;AACrD,QAAI,CAAC,gBAAiB;AACtB,QAAI,oBAAoB,OAAO,OAAO,gBAAgB,SAAS,cAAc,EAAG;AAEhF,UAAM,sBAAsB,2BAA2B,GAAG;AAC1D,UAAM,oBAAoB;AAAA,MACxB,0BAA0B,SAAS;AAAA,QACjC;AAAA,QACA,eAAe;AAAA,UACb,WAAW,OAAO;AAAA,UAClB,OAAO,OAAO;AAAA,QAChB;AAAA,QACA,kBAAkB;AAAA,QAClB,gBAAgB,OAAO;AAAA,QACvB,UAAU,wBAAwB,OAAO,EAAE;AAAA,QAC3C,UAAU,gCAAgC,OAAO,EAAE;AAAA,MACrD,CAAC;AAAA,MACD;AAAA,IACF;AAAA,EACF,SAAS,KAAK;AACZ,WAAO,MAAM,yDAAyD;AAAA,MACpE,YAAY,SAAS;AAAA,MACrB;AAAA,IACF,CAAC;AAAA,EACH;AACF;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { createLogger } from "@open-mercato/shared/lib/logger";
|
|
2
|
+
import { findOneWithDecryption } from "@open-mercato/shared/lib/encryption/find";
|
|
3
|
+
import { emitStaffEvent } from "../events.js";
|
|
4
|
+
import { StaffTimeReport } from "../data/entities.js";
|
|
5
|
+
import { resolvePortalRecipientUserIds } from "../lib/time-tracking/portalRecipients.js";
|
|
6
|
+
const logger = createLogger("staff").child({ component: "subscribers/time-report-portal-broadcast" });
|
|
7
|
+
const metadata = {
|
|
8
|
+
event: "staff.timesheets.time_report.closed",
|
|
9
|
+
persistent: true,
|
|
10
|
+
id: "staff:time-report-portal-broadcast"
|
|
11
|
+
};
|
|
12
|
+
function readId(...candidates) {
|
|
13
|
+
for (const candidate of candidates) {
|
|
14
|
+
if (typeof candidate !== "string") continue;
|
|
15
|
+
const trimmed = candidate.trim();
|
|
16
|
+
if (trimmed.length > 0) return trimmed;
|
|
17
|
+
}
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
20
|
+
function toIsoDate(value) {
|
|
21
|
+
if (value instanceof Date) {
|
|
22
|
+
if (Number.isNaN(value.getTime())) return null;
|
|
23
|
+
return value.toISOString().slice(0, 10);
|
|
24
|
+
}
|
|
25
|
+
if (typeof value === "string" && value.trim().length > 0) return value.trim().slice(0, 10);
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
async function handle(payload, ctx) {
|
|
29
|
+
try {
|
|
30
|
+
const reportId = readId(payload?.reportId, payload?.id);
|
|
31
|
+
const tenantId = readId(payload?.tenantId);
|
|
32
|
+
const organizationId = readId(payload?.organizationId);
|
|
33
|
+
if (!reportId || !tenantId || !organizationId) return;
|
|
34
|
+
const em = ctx.resolve("em").fork();
|
|
35
|
+
const report = await findOneWithDecryption(
|
|
36
|
+
em,
|
|
37
|
+
StaffTimeReport,
|
|
38
|
+
{ id: reportId, tenantId, organizationId, deletedAt: null },
|
|
39
|
+
{},
|
|
40
|
+
{ tenantId, organizationId }
|
|
41
|
+
);
|
|
42
|
+
if (!report || report.status !== "closed") return;
|
|
43
|
+
const customerId = readId(report.customerId);
|
|
44
|
+
if (!customerId) return;
|
|
45
|
+
const recipientUserIds = await resolvePortalRecipientUserIds(em, {
|
|
46
|
+
tenantId,
|
|
47
|
+
organizationId,
|
|
48
|
+
customerId
|
|
49
|
+
});
|
|
50
|
+
if (recipientUserIds.length === 0) return;
|
|
51
|
+
const periodFrom = toIsoDate(report.periodFrom);
|
|
52
|
+
const periodTo = toIsoDate(report.periodTo);
|
|
53
|
+
if (!periodFrom || !periodTo) return;
|
|
54
|
+
await emitStaffEvent(
|
|
55
|
+
"staff.timesheets.time_report.portal_published",
|
|
56
|
+
{
|
|
57
|
+
id: report.id,
|
|
58
|
+
tenantId,
|
|
59
|
+
organizationId,
|
|
60
|
+
reference: report.reference,
|
|
61
|
+
periodFrom,
|
|
62
|
+
periodTo,
|
|
63
|
+
recipientUserIds
|
|
64
|
+
},
|
|
65
|
+
{ persistent: true }
|
|
66
|
+
);
|
|
67
|
+
} catch (err) {
|
|
68
|
+
logger.error("Failed to publish a closed time report to the customer portal", {
|
|
69
|
+
subscriber: metadata.id,
|
|
70
|
+
err
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
export {
|
|
75
|
+
handle as default,
|
|
76
|
+
metadata
|
|
77
|
+
};
|
|
78
|
+
//# sourceMappingURL=time-report-portal-broadcast.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../src/modules/staff/subscribers/time-report-portal-broadcast.ts"],
|
|
4
|
+
"sourcesContent": ["import type { EntityManager } from '@mikro-orm/postgresql'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\nimport { findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport { emitStaffEvent } from '../events'\nimport { StaffTimeReport } from '../data/entities'\nimport { resolvePortalRecipientUserIds } from '../lib/time-tracking/portalRecipients'\n\nconst logger = createLogger('staff').child({ component: 'subscribers/time-report-portal-broadcast' })\n\n/**\n * EP-06 / EP-50. Mirrors a closed report to the customer portal \u2014 and is the only\n * emitter of `staff.timesheets.time_report.portal_published`.\n *\n * The glue lives here, in an optional consumer, rather than in the close command,\n * because resolving \"which portal users belong to this customer\" reads a\n * `customer_accounts` table. As a subscriber it is skippable: if the query fails\n * the recipient list is empty and nothing is emitted, so a deployment without the\n * portal installed behaves exactly as it did before.\n *\n * Two invariants this file exists to hold:\n *\n * 1. **No recipients, no event.** The portal SSE stream falls back to an\n * organization-wide audience for a payload with no `recipientUserIds`, and one\n * organization serves many customers. Emitting an unpinned report event would\n * be a cross-customer disclosure.\n * 2. **No money on the wire.** The payload carries the reference and the period\n * only. Amounts are gated on `staff.timesheets.rates.view`, which SSE cannot\n * apply, and a portal user never holds it in any case.\n *\n * `persistent: true`: a client-visible publication should survive a worker restart.\n */\nexport const metadata = {\n event: 'staff.timesheets.time_report.closed',\n persistent: true,\n id: 'staff:time-report-portal-broadcast',\n}\n\nexport type TimeReportClosedPayload = {\n id?: string | null\n reportId?: string | null\n tenantId?: string | null\n organizationId?: string | null\n}\n\ntype ResolverContext = {\n resolve: <T = unknown>(name: string) => T\n}\n\nfunction readId(...candidates: Array<unknown>): string | null {\n for (const candidate of candidates) {\n if (typeof candidate !== 'string') continue\n const trimmed = candidate.trim()\n if (trimmed.length > 0) return trimmed\n }\n return null\n}\n\nfunction toIsoDate(value: unknown): string | null {\n if (value instanceof Date) {\n if (Number.isNaN(value.getTime())) return null\n return value.toISOString().slice(0, 10)\n }\n if (typeof value === 'string' && value.trim().length > 0) return value.trim().slice(0, 10)\n return null\n}\n\nexport default async function handle(\n payload: TimeReportClosedPayload,\n ctx: ResolverContext,\n): Promise<void> {\n try {\n const reportId = readId(payload?.reportId, payload?.id)\n const tenantId = readId(payload?.tenantId)\n const organizationId = readId(payload?.organizationId)\n if (!reportId || !tenantId || !organizationId) return\n\n const em = ctx.resolve<EntityManager>('em').fork()\n const report = await findOneWithDecryption(\n em,\n StaffTimeReport,\n { id: reportId, tenantId, organizationId, deletedAt: null },\n {},\n { tenantId, organizationId },\n )\n if (!report || report.status !== 'closed') return\n\n const customerId = readId(report.customerId)\n if (!customerId) return\n\n const recipientUserIds = await resolvePortalRecipientUserIds(em, {\n tenantId,\n organizationId,\n customerId,\n })\n if (recipientUserIds.length === 0) return\n\n const periodFrom = toIsoDate(report.periodFrom)\n const periodTo = toIsoDate(report.periodTo)\n if (!periodFrom || !periodTo) return\n\n await emitStaffEvent(\n 'staff.timesheets.time_report.portal_published',\n {\n id: report.id,\n tenantId,\n organizationId,\n reference: report.reference,\n periodFrom,\n periodTo,\n recipientUserIds,\n },\n { persistent: true },\n )\n } catch (err) {\n logger.error('Failed to publish a closed time report to the customer portal', {\n subscriber: metadata.id,\n err,\n })\n }\n}\n"],
|
|
5
|
+
"mappings": "AACA,SAAS,oBAAoB;AAC7B,SAAS,6BAA6B;AACtC,SAAS,sBAAsB;AAC/B,SAAS,uBAAuB;AAChC,SAAS,qCAAqC;AAE9C,MAAM,SAAS,aAAa,OAAO,EAAE,MAAM,EAAE,WAAW,2CAA2C,CAAC;AAwB7F,MAAM,WAAW;AAAA,EACtB,OAAO;AAAA,EACP,YAAY;AAAA,EACZ,IAAI;AACN;AAaA,SAAS,UAAU,YAA2C;AAC5D,aAAW,aAAa,YAAY;AAClC,QAAI,OAAO,cAAc,SAAU;AACnC,UAAM,UAAU,UAAU,KAAK;AAC/B,QAAI,QAAQ,SAAS,EAAG,QAAO;AAAA,EACjC;AACA,SAAO;AACT;AAEA,SAAS,UAAU,OAA+B;AAChD,MAAI,iBAAiB,MAAM;AACzB,QAAI,OAAO,MAAM,MAAM,QAAQ,CAAC,EAAG,QAAO;AAC1C,WAAO,MAAM,YAAY,EAAE,MAAM,GAAG,EAAE;AAAA,EACxC;AACA,MAAI,OAAO,UAAU,YAAY,MAAM,KAAK,EAAE,SAAS,EAAG,QAAO,MAAM,KAAK,EAAE,MAAM,GAAG,EAAE;AACzF,SAAO;AACT;AAEA,eAAO,OACL,SACA,KACe;AACf,MAAI;AACF,UAAM,WAAW,OAAO,SAAS,UAAU,SAAS,EAAE;AACtD,UAAM,WAAW,OAAO,SAAS,QAAQ;AACzC,UAAM,iBAAiB,OAAO,SAAS,cAAc;AACrD,QAAI,CAAC,YAAY,CAAC,YAAY,CAAC,eAAgB;AAE/C,UAAM,KAAK,IAAI,QAAuB,IAAI,EAAE,KAAK;AACjD,UAAM,SAAS,MAAM;AAAA,MACnB;AAAA,MACA;AAAA,MACA,EAAE,IAAI,UAAU,UAAU,gBAAgB,WAAW,KAAK;AAAA,MAC1D,CAAC;AAAA,MACD,EAAE,UAAU,eAAe;AAAA,IAC7B;AACA,QAAI,CAAC,UAAU,OAAO,WAAW,SAAU;AAE3C,UAAM,aAAa,OAAO,OAAO,UAAU;AAC3C,QAAI,CAAC,WAAY;AAEjB,UAAM,mBAAmB,MAAM,8BAA8B,IAAI;AAAA,MAC/D;AAAA,MACA;AAAA,MACA;AAAA,IACF,CAAC;AACD,QAAI,iBAAiB,WAAW,EAAG;AAEnC,UAAM,aAAa,UAAU,OAAO,UAAU;AAC9C,UAAM,WAAW,UAAU,OAAO,QAAQ;AAC1C,QAAI,CAAC,cAAc,CAAC,SAAU;AAE9B,UAAM;AAAA,MACJ;AAAA,MACA;AAAA,QACE,IAAI,OAAO;AAAA,QACX;AAAA,QACA;AAAA,QACA,WAAW,OAAO;AAAA,QAClB;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,MACA,EAAE,YAAY,KAAK;AAAA,IACrB;AAAA,EACF,SAAS,KAAK;AACZ,WAAO,MAAM,iEAAiE;AAAA,MAC5E,YAAY,SAAS;AAAA,MACrB;AAAA,IACF,CAAC;AAAA,EACH;AACF;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
const translatableFields = {
|
|
2
|
-
"staff:staff_team_member": ["display_name", "description"]
|
|
2
|
+
"staff:staff_team_member": ["display_name", "description"],
|
|
3
|
+
"staff:staff_time_project": ["name", "description"],
|
|
4
|
+
"staff:staff_time_task_status": ["name"],
|
|
5
|
+
"staff:staff_time_tag": ["label"]
|
|
3
6
|
};
|
|
4
7
|
var translations_default = translatableFields;
|
|
5
8
|
export {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/modules/staff/translations.ts"],
|
|
4
|
-
"sourcesContent": ["
|
|
5
|
-
"mappings": "
|
|
4
|
+
"sourcesContent": ["/**\n * EP-45 \u2014 entity fields a tenant may translate per locale.\n *\n * Keys are entity ids, values are **database column** names (snake_case), matching the\n * `staff_team_member` entry this file shipped with. Adding a field here does not change\n * any rendered output on its own: the translation overlay only substitutes a value when\n * a translation row exists for the request locale, and there are none until a tenant\n * writes one.\n *\n * Only columns that actually exist are listed. `staff_time_task_statuses` has no\n * description column and `staff_time_tags` names its label `label`, so the spec's\n * \"name / description\" reads as \"the human-readable columns of each\" rather than two\n * literal column names.\n */\nexport const translatableFields: Record<string, string[]> = {\n 'staff:staff_team_member': ['display_name', 'description'],\n 'staff:staff_time_project': ['name', 'description'],\n 'staff:staff_time_task_status': ['name'],\n 'staff:staff_time_tag': ['label'],\n}\n\nexport default translatableFields\n"],
|
|
5
|
+
"mappings": "AAcO,MAAM,qBAA+C;AAAA,EAC1D,2BAA2B,CAAC,gBAAgB,aAAa;AAAA,EACzD,4BAA4B,CAAC,QAAQ,aAAa;AAAA,EAClD,gCAAgC,CAAC,MAAM;AAAA,EACvC,wBAAwB,CAAC,OAAO;AAClC;AAEA,IAAO,uBAAQ;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../src/modules/staff/widgets/components.ts"],
|
|
4
|
+
"sourcesContent": ["import type { ComponentOverride } from '@open-mercato/shared/modules/widgets/component-registry'\n\n/**\n * Component replacement handles the staff module publishes (EP-31 of\n * `.ai/specs/2026-08-24-time-tracking-umes-extension-points.md`).\n *\n * Each handle is registered by the component's own module through\n * `registerComponent`, and the shipping component resolves itself through\n * `useRegisteredComponent`, so another module can `replace`, `wrapper` or\n * `props`-transform it without forking the screen that renders it. Every entry\n * publishes a zod `propsSchema` \u2014 in development `useRegisteredComponent` parses\n * the resolved props against it and falls back to the original component when a\n * replacement does not satisfy it, so the schema is the contract, not a comment.\n *\n * | Handle | Registered by | Props |\n * |---|---|---|\n * | `staff.time_entry_dialog` | `lib/time-tracking-ui/TimeEntryDialog.tsx` | `TimeEntryDialogProps` |\n * | `staff.timer_bar` | `lib/timesheets-ui/TimerBar.tsx` | `TimerBarProps` |\n * | `staff.kanban_card` | `lib/time-tracking-ui/KanbanCard.tsx` | `KanbanCardProps` |\n * | `staff.kanban_column` | `lib/time-tracking-ui/KanbanColumn.tsx` | `KanbanColumnProps` |\n * | `staff.timesheet_grid` | `backend/staff/time-tracking/timesheet/GridView.tsx`| `GridViewProps` |\n * | `staff.timesheet_list` | `lib/timesheets-ui/ListView.tsx` | `ListViewProps` |\n * | `staff.timesheet_calendar` | `lib/time-tracking-ui/TimesheetCalendar.tsx` | `TimesheetCalendarProps` |\n * | `staff.report_sheet` | `lib/time-tracking-ui/ReportSheet.tsx` | `ReportSheetProps` |\n * | `staff.project_card` | `lib/timesheets-projects-ui/ProjectCard.tsx` | `ProjectCardProps` |\n * | `staff.entries_summary_footer` | `lib/time-tracking-ui/TimeEntriesSummaryFooter.tsx` | `TimeEntriesSummaryFooterProps` |\n *\n * The array below is the staff module's own *outgoing* overrides \u2014 it targets no\n * other module's components today, so it is empty. Adding an entry here would\n * override somebody else's component, not one of the handles above.\n */\nexport const componentOverrides: ComponentOverride[] = []\n\nexport default componentOverrides\n"],
|
|
5
|
+
"mappings": "AA+BO,MAAM,qBAA0C,CAAC;AAExD,IAAO,qBAAQ;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
3
|
import * as React from "react";
|
|
4
|
+
import Link from "next/link";
|
|
4
5
|
import { useT } from "@open-mercato/shared/lib/i18n/context";
|
|
5
6
|
import { ProjectColorDot } from "../../../lib/timesheets-ui/ProjectColorDot.js";
|
|
6
7
|
import { useActiveTimesheetTimer } from "../../../lib/timesheets-ui/useActiveTimesheetTimer.js";
|
|
@@ -37,15 +38,15 @@ function TimerSidebarIndicator() {
|
|
|
37
38
|
}, [timer.running, timer.startedAt]);
|
|
38
39
|
if (!timer.running || !timer.entryId) return null;
|
|
39
40
|
return /* @__PURE__ */ jsxs(
|
|
40
|
-
|
|
41
|
+
Link,
|
|
41
42
|
{
|
|
42
|
-
href: "/backend/staff/
|
|
43
|
+
href: "/backend/staff/time-tracking/timesheet",
|
|
43
44
|
className: "flex items-center gap-2 rounded-md px-2 py-1.5 text-xs hover:bg-muted transition-colors cursor-pointer",
|
|
44
45
|
title: t("staff.timesheets.sidebar.timerRunning", "Timer running \u2014 click to view"),
|
|
45
46
|
children: [
|
|
46
47
|
/* @__PURE__ */ jsxs("span", { className: "relative flex h-2.5 w-2.5 shrink-0", children: [
|
|
47
|
-
/* @__PURE__ */ jsx("span", { className: "absolute inline-flex h-full w-full animate-ping rounded-full bg-
|
|
48
|
-
/* @__PURE__ */ jsx("span", { className: "relative inline-flex h-2.5 w-2.5 rounded-full bg-
|
|
48
|
+
/* @__PURE__ */ jsx("span", { className: "absolute inline-flex h-full w-full animate-ping rounded-full bg-status-error-icon opacity-75" }),
|
|
49
|
+
/* @__PURE__ */ jsx("span", { className: "relative inline-flex h-2.5 w-2.5 rounded-full bg-status-error-icon" })
|
|
49
50
|
] }),
|
|
50
51
|
timer.projectName ? /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-1 truncate", children: [
|
|
51
52
|
/* @__PURE__ */ jsx(ProjectColorDot, { colorKey: timer.projectColor, projectName: timer.projectName, size: "xs" }),
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../../src/modules/staff/widgets/injection/timer-sidebar-indicator/widget.tsx"],
|
|
4
|
-
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport type { InjectionWidgetModule } from '@open-mercato/shared/modules/widgets/injection'\nimport { ProjectColorDot } from '../../../lib/timesheets-ui/ProjectColorDot'\nimport { useActiveTimesheetTimer } from '../../../lib/timesheets-ui/useActiveTimesheetTimer'\n\nfunction formatElapsed(seconds: number): string {\n const h = Math.floor(seconds / 3600)\n const m = Math.floor((seconds % 3600) / 60)\n const s = seconds % 60\n return `${h}:${String(m).padStart(2, '0')}:${String(s).padStart(2, '0')}`\n}\n\nfunction TimerSidebarIndicator() {\n const t = useT()\n const timer = useActiveTimesheetTimer()\n const [elapsed, setElapsed] = React.useState(0)\n const intervalRef = React.useRef<ReturnType<typeof setInterval> | null>(null)\n\n // Tick the elapsed counter locally every second\n React.useEffect(() => {\n if (!timer.running || !timer.startedAt) {\n if (intervalRef.current) { clearInterval(intervalRef.current); intervalRef.current = null }\n setElapsed(0)\n return\n }\n\n const startTime = new Date(timer.startedAt).getTime()\n const calcElapsed = () => Math.max(0, Math.floor((Date.now() - startTime) / 1000))\n setElapsed(calcElapsed())\n\n intervalRef.current = setInterval(() => setElapsed(calcElapsed()), 1000)\n return () => {\n if (intervalRef.current) { clearInterval(intervalRef.current); intervalRef.current = null }\n }\n }, [timer.running, timer.startedAt])\n\n if (!timer.running || !timer.entryId) return null\n\n return (\n <
|
|
5
|
-
"mappings": ";
|
|
4
|
+
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport Link from 'next/link'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport type { InjectionWidgetModule } from '@open-mercato/shared/modules/widgets/injection'\nimport { ProjectColorDot } from '../../../lib/timesheets-ui/ProjectColorDot'\nimport { useActiveTimesheetTimer } from '../../../lib/timesheets-ui/useActiveTimesheetTimer'\n\nfunction formatElapsed(seconds: number): string {\n const h = Math.floor(seconds / 3600)\n const m = Math.floor((seconds % 3600) / 60)\n const s = seconds % 60\n return `${h}:${String(m).padStart(2, '0')}:${String(s).padStart(2, '0')}`\n}\n\nfunction TimerSidebarIndicator() {\n const t = useT()\n const timer = useActiveTimesheetTimer()\n const [elapsed, setElapsed] = React.useState(0)\n const intervalRef = React.useRef<ReturnType<typeof setInterval> | null>(null)\n\n // Tick the elapsed counter locally every second\n React.useEffect(() => {\n if (!timer.running || !timer.startedAt) {\n if (intervalRef.current) { clearInterval(intervalRef.current); intervalRef.current = null }\n setElapsed(0)\n return\n }\n\n const startTime = new Date(timer.startedAt).getTime()\n const calcElapsed = () => Math.max(0, Math.floor((Date.now() - startTime) / 1000))\n setElapsed(calcElapsed())\n\n intervalRef.current = setInterval(() => setElapsed(calcElapsed()), 1000)\n return () => {\n if (intervalRef.current) { clearInterval(intervalRef.current); intervalRef.current = null }\n }\n }, [timer.running, timer.startedAt])\n\n if (!timer.running || !timer.entryId) return null\n\n return (\n <Link\n href=\"/backend/staff/time-tracking/timesheet\"\n className=\"flex items-center gap-2 rounded-md px-2 py-1.5 text-xs hover:bg-muted transition-colors cursor-pointer\"\n title={t('staff.timesheets.sidebar.timerRunning', 'Timer running \u2014 click to view')}\n >\n <span className=\"relative flex h-2.5 w-2.5 shrink-0\">\n <span className=\"absolute inline-flex h-full w-full animate-ping rounded-full bg-status-error-icon opacity-75\" />\n <span className=\"relative inline-flex h-2.5 w-2.5 rounded-full bg-status-error-icon\" />\n </span>\n {timer.projectName ? (\n <span className=\"inline-flex items-center gap-1 truncate\">\n <ProjectColorDot colorKey={timer.projectColor} projectName={timer.projectName} size=\"xs\" />\n <span className=\"truncate\">{timer.projectName}</span>\n </span>\n ) : null}\n <span className=\"ml-auto font-mono tabular-nums shrink-0\">{formatElapsed(elapsed)}</span>\n </Link>\n )\n}\n\nconst widget: InjectionWidgetModule = {\n metadata: {\n id: 'staff.injection.timer-sidebar-indicator',\n title: 'Active timer indicator',\n description: 'Shows a pulsing indicator in the sidebar when a timesheet timer is running.',\n features: ['staff.timesheets.manage_own'],\n },\n Widget: TimerSidebarIndicator,\n}\n\nexport default widget\n"],
|
|
5
|
+
"mappings": ";AAgDM,SACE,KADF;AA9CN,YAAY,WAAW;AACvB,OAAO,UAAU;AACjB,SAAS,YAAY;AAErB,SAAS,uBAAuB;AAChC,SAAS,+BAA+B;AAExC,SAAS,cAAc,SAAyB;AAC9C,QAAM,IAAI,KAAK,MAAM,UAAU,IAAI;AACnC,QAAM,IAAI,KAAK,MAAO,UAAU,OAAQ,EAAE;AAC1C,QAAM,IAAI,UAAU;AACpB,SAAO,GAAG,CAAC,IAAI,OAAO,CAAC,EAAE,SAAS,GAAG,GAAG,CAAC,IAAI,OAAO,CAAC,EAAE,SAAS,GAAG,GAAG,CAAC;AACzE;AAEA,SAAS,wBAAwB;AAC/B,QAAM,IAAI,KAAK;AACf,QAAM,QAAQ,wBAAwB;AACtC,QAAM,CAAC,SAAS,UAAU,IAAI,MAAM,SAAS,CAAC;AAC9C,QAAM,cAAc,MAAM,OAA8C,IAAI;AAG5E,QAAM,UAAU,MAAM;AACpB,QAAI,CAAC,MAAM,WAAW,CAAC,MAAM,WAAW;AACtC,UAAI,YAAY,SAAS;AAAE,sBAAc,YAAY,OAAO;AAAG,oBAAY,UAAU;AAAA,MAAK;AAC1F,iBAAW,CAAC;AACZ;AAAA,IACF;AAEA,UAAM,YAAY,IAAI,KAAK,MAAM,SAAS,EAAE,QAAQ;AACpD,UAAM,cAAc,MAAM,KAAK,IAAI,GAAG,KAAK,OAAO,KAAK,IAAI,IAAI,aAAa,GAAI,CAAC;AACjF,eAAW,YAAY,CAAC;AAExB,gBAAY,UAAU,YAAY,MAAM,WAAW,YAAY,CAAC,GAAG,GAAI;AACvE,WAAO,MAAM;AACX,UAAI,YAAY,SAAS;AAAE,sBAAc,YAAY,OAAO;AAAG,oBAAY,UAAU;AAAA,MAAK;AAAA,IAC5F;AAAA,EACF,GAAG,CAAC,MAAM,SAAS,MAAM,SAAS,CAAC;AAEnC,MAAI,CAAC,MAAM,WAAW,CAAC,MAAM,QAAS,QAAO;AAE7C,SACE;AAAA,IAAC;AAAA;AAAA,MACC,MAAK;AAAA,MACL,WAAU;AAAA,MACV,OAAO,EAAE,yCAAyC,oCAA+B;AAAA,MAEjF;AAAA,6BAAC,UAAK,WAAU,sCACd;AAAA,8BAAC,UAAK,WAAU,gGAA+F;AAAA,UAC/G,oBAAC,UAAK,WAAU,sEAAqE;AAAA,WACvF;AAAA,QACC,MAAM,cACL,qBAAC,UAAK,WAAU,2CACd;AAAA,8BAAC,mBAAgB,UAAU,MAAM,cAAc,aAAa,MAAM,aAAa,MAAK,MAAK;AAAA,UACzF,oBAAC,UAAK,WAAU,YAAY,gBAAM,aAAY;AAAA,WAChD,IACE;AAAA,QACJ,oBAAC,UAAK,WAAU,2CAA2C,wBAAc,OAAO,GAAE;AAAA;AAAA;AAAA,EACpF;AAEJ;AAEA,MAAM,SAAgC;AAAA,EACpC,UAAU;AAAA,IACR,IAAI;AAAA,IACJ,OAAO;AAAA,IACP,aAAa;AAAA,IACb,UAAU,CAAC,6BAA6B;AAAA,EAC1C;AAAA,EACA,QAAQ;AACV;AAEA,IAAO,iBAAQ;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|