@open-mercato/core 0.6.4-develop.4217.1.c9aa050183 → 0.6.4-develop.4236.1.9fa6806b34
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 +37 -0
- package/dist/generated/entities/staff_time_entry/index.js.map +7 -0
- package/dist/generated/entities/staff_time_entry_segment/index.js +23 -0
- package/dist/generated/entities/staff_time_entry_segment/index.js.map +7 -0
- package/dist/generated/entities/staff_time_project/index.js +35 -0
- package/dist/generated/entities/staff_time_project/index.js.map +7 -0
- package/dist/generated/entities/staff_time_project_member/index.js +29 -0
- package/dist/generated/entities/staff_time_project_member/index.js.map +7 -0
- package/dist/generated/entities.ids.generated.js +5 -1
- package/dist/generated/entities.ids.generated.js.map +2 -2
- package/dist/generated/entity-fields-registry.js +64 -0
- package/dist/generated/entity-fields-registry.js.map +2 -2
- package/dist/helpers/integration/timesheetFixtures.js +50 -0
- package/dist/helpers/integration/timesheetFixtures.js.map +7 -0
- package/dist/modules/attachments/api/library/[id]/route.js +20 -16
- package/dist/modules/attachments/api/library/[id]/route.js.map +2 -2
- package/dist/modules/attachments/api/route.js +18 -14
- package/dist/modules/attachments/api/route.js.map +2 -2
- package/dist/modules/auth/api/roles/acl/route.js +10 -4
- package/dist/modules/auth/api/roles/acl/route.js.map +2 -2
- package/dist/modules/auth/api/sidebar/preferences/route.js +27 -20
- package/dist/modules/auth/api/sidebar/preferences/route.js.map +2 -2
- package/dist/modules/auth/api/users/acl/route.js +16 -11
- package/dist/modules/auth/api/users/acl/route.js.map +2 -2
- package/dist/modules/auth/commands/users.js +87 -71
- package/dist/modules/auth/commands/users.js.map +2 -2
- package/dist/modules/auth/services/sidebarPreferencesService.js +39 -30
- package/dist/modules/auth/services/sidebarPreferencesService.js.map +2 -2
- package/dist/modules/catalog/commands/categories.js +61 -12
- package/dist/modules/catalog/commands/categories.js.map +2 -2
- package/dist/modules/catalog/commands/products.js +79 -54
- package/dist/modules/catalog/commands/products.js.map +2 -2
- package/dist/modules/catalog/commands/variants.js +29 -16
- package/dist/modules/catalog/commands/variants.js.map +2 -2
- package/dist/modules/currencies/commands/currencies.js +15 -8
- package/dist/modules/currencies/commands/currencies.js.map +2 -2
- package/dist/modules/customer_accounts/api/admin/users.js +27 -26
- package/dist/modules/customer_accounts/api/admin/users.js.map +2 -2
- package/dist/modules/customer_accounts/api/password/reset-confirm.js +5 -5
- package/dist/modules/customer_accounts/api/password/reset-confirm.js.map +2 -2
- package/dist/modules/customer_accounts/api/portal/users/[id]/roles.js +11 -10
- package/dist/modules/customer_accounts/api/portal/users/[id]/roles.js.map +2 -2
- package/dist/modules/customers/commands/addresses.js +35 -21
- package/dist/modules/customers/commands/addresses.js.map +2 -2
- package/dist/modules/customers/commands/companies.js +163 -162
- package/dist/modules/customers/commands/companies.js.map +2 -2
- package/dist/modules/customers/commands/deals.js +3 -4
- package/dist/modules/customers/commands/deals.js.map +2 -2
- package/dist/modules/customers/commands/interactions.js +19 -22
- package/dist/modules/customers/commands/interactions.js.map +2 -2
- package/dist/modules/customers/commands/people.js +18 -15
- package/dist/modules/customers/commands/people.js.map +2 -2
- package/dist/modules/customers/commands/personCompanyLinks.js +105 -94
- package/dist/modules/customers/commands/personCompanyLinks.js.map +2 -2
- package/dist/modules/customers/commands/pipeline-stages.js +30 -23
- package/dist/modules/customers/commands/pipeline-stages.js.map +2 -2
- package/dist/modules/customers/commands/pipelines.js +27 -20
- package/dist/modules/customers/commands/pipelines.js.map +2 -2
- package/dist/modules/customers/commands/tags.js +13 -5
- package/dist/modules/customers/commands/tags.js.map +2 -2
- package/dist/modules/dashboards/api/users/widgets/route.js +0 -1
- package/dist/modules/dashboards/api/users/widgets/route.js.map +2 -2
- package/dist/modules/dashboards/api/widgets/data/route.js +29 -1
- package/dist/modules/dashboards/api/widgets/data/route.js.map +2 -2
- package/dist/modules/data_sync/lib/sync-engine.js +4 -4
- package/dist/modules/data_sync/lib/sync-engine.js.map +2 -2
- package/dist/modules/data_sync/lib/sync-run-service.js +51 -27
- package/dist/modules/data_sync/lib/sync-run-service.js.map +2 -2
- package/dist/modules/directory/commands/organizations.js +192 -158
- package/dist/modules/directory/commands/organizations.js.map +3 -3
- package/dist/modules/inbox_ops/api/emails/[id]/reprocess/route.js +22 -16
- package/dist/modules/inbox_ops/api/emails/[id]/reprocess/route.js.map +2 -2
- package/dist/modules/messages/commands/messages.js +77 -75
- package/dist/modules/messages/commands/messages.js.map +2 -2
- package/dist/modules/messages/commands/shared.js +132 -132
- package/dist/modules/messages/commands/shared.js.map +2 -2
- package/dist/modules/perspectives/api/[tableId]/route.js +37 -26
- package/dist/modules/perspectives/api/[tableId]/route.js.map +2 -2
- package/dist/modules/resources/commands/resources.js +125 -117
- package/dist/modules/resources/commands/resources.js.map +2 -2
- package/dist/modules/resources/commands/tags.js +7 -3
- package/dist/modules/resources/commands/tags.js.map +2 -2
- package/dist/modules/sales/api/quotes/send/route.js +12 -11
- package/dist/modules/sales/api/quotes/send/route.js.map +2 -2
- package/dist/modules/sales/commands/documents.js +629 -478
- package/dist/modules/sales/commands/documents.js.map +2 -2
- package/dist/modules/sales/commands/payments.js +146 -146
- package/dist/modules/sales/commands/payments.js.map +2 -2
- package/dist/modules/sales/commands/returns.js +68 -60
- package/dist/modules/sales/commands/returns.js.map +2 -2
- package/dist/modules/staff/acl.js +10 -1
- package/dist/modules/staff/acl.js.map +2 -2
- package/dist/modules/staff/analytics.js +33 -0
- package/dist/modules/staff/analytics.js.map +7 -0
- package/dist/modules/staff/api/guards.js +31 -0
- package/dist/modules/staff/api/guards.js.map +7 -0
- package/dist/modules/staff/api/interceptors.js +96 -0
- package/dist/modules/staff/api/interceptors.js.map +7 -0
- package/dist/modules/staff/api/timesheets/my-projects/[projectId]/route.js +170 -0
- package/dist/modules/staff/api/timesheets/my-projects/[projectId]/route.js.map +7 -0
- package/dist/modules/staff/api/timesheets/my-projects/route.js +103 -0
- package/dist/modules/staff/api/timesheets/my-projects/route.js.map +7 -0
- package/dist/modules/staff/api/timesheets/projects/kpis/route.js +147 -0
- package/dist/modules/staff/api/timesheets/projects/kpis/route.js.map +7 -0
- package/dist/modules/staff/api/timesheets/time-entries/[id]/segments/[segmentId]/route.js +171 -0
- package/dist/modules/staff/api/timesheets/time-entries/[id]/segments/[segmentId]/route.js.map +7 -0
- package/dist/modules/staff/api/timesheets/time-entries/[id]/segments/route.js +180 -0
- package/dist/modules/staff/api/timesheets/time-entries/[id]/segments/route.js.map +7 -0
- package/dist/modules/staff/api/timesheets/time-entries/[id]/timer-start/route.js +155 -0
- package/dist/modules/staff/api/timesheets/time-entries/[id]/timer-start/route.js.map +7 -0
- package/dist/modules/staff/api/timesheets/time-entries/[id]/timer-stop/route.js +173 -0
- package/dist/modules/staff/api/timesheets/time-entries/[id]/timer-stop/route.js.map +7 -0
- package/dist/modules/staff/api/timesheets/time-entries/bulk/route.js +260 -0
- package/dist/modules/staff/api/timesheets/time-entries/bulk/route.js.map +7 -0
- package/dist/modules/staff/api/timesheets/time-entries/route.js +188 -0
- package/dist/modules/staff/api/timesheets/time-entries/route.js.map +7 -0
- package/dist/modules/staff/api/timesheets/time-projects/[id]/employees/route.js +159 -0
- package/dist/modules/staff/api/timesheets/time-projects/[id]/employees/route.js.map +7 -0
- package/dist/modules/staff/api/timesheets/time-projects/route.js +230 -0
- package/dist/modules/staff/api/timesheets/time-projects/route.js.map +7 -0
- package/dist/modules/staff/backend/staff/timesheets/page.js +710 -0
- package/dist/modules/staff/backend/staff/timesheets/page.js.map +7 -0
- package/dist/modules/staff/backend/staff/timesheets/page.meta.js +22 -0
- package/dist/modules/staff/backend/staff/timesheets/page.meta.js.map +7 -0
- package/dist/modules/staff/backend/staff/timesheets/projects/[id]/edit/page.js +125 -0
- package/dist/modules/staff/backend/staff/timesheets/projects/[id]/edit/page.js.map +7 -0
- package/dist/modules/staff/backend/staff/timesheets/projects/[id]/edit/page.meta.js +16 -0
- package/dist/modules/staff/backend/staff/timesheets/projects/[id]/edit/page.meta.js.map +7 -0
- package/dist/modules/staff/backend/staff/timesheets/projects/[id]/page.js +418 -0
- package/dist/modules/staff/backend/staff/timesheets/projects/[id]/page.js.map +7 -0
- package/dist/modules/staff/backend/staff/timesheets/projects/[id]/page.meta.js +16 -0
- package/dist/modules/staff/backend/staff/timesheets/projects/[id]/page.meta.js.map +7 -0
- package/dist/modules/staff/backend/staff/timesheets/projects/create/page.js +79 -0
- package/dist/modules/staff/backend/staff/timesheets/projects/create/page.js.map +7 -0
- package/dist/modules/staff/backend/staff/timesheets/projects/create/page.meta.js +16 -0
- package/dist/modules/staff/backend/staff/timesheets/projects/create/page.meta.js.map +7 -0
- package/dist/modules/staff/backend/staff/timesheets/projects/page.js +602 -0
- package/dist/modules/staff/backend/staff/timesheets/projects/page.js.map +7 -0
- package/dist/modules/staff/backend/staff/timesheets/projects/page.meta.js +25 -0
- package/dist/modules/staff/backend/staff/timesheets/projects/page.meta.js.map +7 -0
- package/dist/modules/staff/backend/staff/timesheets/projects/projectFormConfig.js +123 -0
- package/dist/modules/staff/backend/staff/timesheets/projects/projectFormConfig.js.map +7 -0
- package/dist/modules/staff/cli.js +38 -1
- package/dist/modules/staff/cli.js.map +2 -2
- package/dist/modules/staff/commands/index.js +2 -0
- package/dist/modules/staff/commands/index.js.map +2 -2
- package/dist/modules/staff/commands/leave-requests.js +30 -28
- package/dist/modules/staff/commands/leave-requests.js.map +3 -3
- package/dist/modules/staff/commands/team-members.js +21 -20
- package/dist/modules/staff/commands/team-members.js.map +2 -2
- package/dist/modules/staff/commands/timesheets-entries.js +409 -0
- package/dist/modules/staff/commands/timesheets-entries.js.map +7 -0
- package/dist/modules/staff/commands/timesheets-projects.js +618 -0
- package/dist/modules/staff/commands/timesheets-projects.js.map +7 -0
- package/dist/modules/staff/data/enrichers.js +104 -0
- package/dist/modules/staff/data/enrichers.js.map +7 -0
- package/dist/modules/staff/data/entities.js +226 -1
- package/dist/modules/staff/data/entities.js.map +2 -2
- package/dist/modules/staff/data/validators.js +113 -1
- package/dist/modules/staff/data/validators.js.map +2 -2
- package/dist/modules/staff/events.js +13 -1
- package/dist/modules/staff/events.js.map +2 -2
- package/dist/modules/staff/lib/crud.js +7 -1
- package/dist/modules/staff/lib/crud.js.map +2 -2
- package/dist/modules/staff/lib/staffMemberResolver.js +15 -0
- package/dist/modules/staff/lib/staffMemberResolver.js.map +7 -0
- package/dist/modules/staff/lib/timesheets-projects/computeProjectHoursTrend.js +60 -0
- package/dist/modules/staff/lib/timesheets-projects/computeProjectHoursTrend.js.map +7 -0
- package/dist/modules/staff/lib/timesheets-projects/computeProjectsKpis.js +260 -0
- package/dist/modules/staff/lib/timesheets-projects/computeProjectsKpis.js.map +7 -0
- package/dist/modules/staff/lib/timesheets-projects/dateBuckets.js +41 -0
- package/dist/modules/staff/lib/timesheets-projects/dateBuckets.js.map +7 -0
- package/dist/modules/staff/lib/timesheets-projects/initials.js +10 -0
- package/dist/modules/staff/lib/timesheets-projects/initials.js.map +7 -0
- package/dist/modules/staff/lib/timesheets-projects/kpiMath.js +12 -0
- package/dist/modules/staff/lib/timesheets-projects/kpiMath.js.map +7 -0
- package/dist/modules/staff/lib/timesheets-projects/listProjectMembersPreview.js +55 -0
- package/dist/modules/staff/lib/timesheets-projects/listProjectMembersPreview.js.map +7 -0
- package/dist/modules/staff/lib/timesheets-projects-ui/HoursSparkline.js +66 -0
- package/dist/modules/staff/lib/timesheets-projects-ui/HoursSparkline.js.map +7 -0
- package/dist/modules/staff/lib/timesheets-projects-ui/ProjectCard.js +81 -0
- package/dist/modules/staff/lib/timesheets-projects-ui/ProjectCard.js.map +7 -0
- package/dist/modules/staff/lib/timesheets-projects-ui/ProjectMembersAvatarStack.js +58 -0
- package/dist/modules/staff/lib/timesheets-projects-ui/ProjectMembersAvatarStack.js.map +7 -0
- package/dist/modules/staff/lib/timesheets-projects-ui/ProjectsKpiStrip.js +152 -0
- package/dist/modules/staff/lib/timesheets-projects-ui/ProjectsKpiStrip.js.map +7 -0
- package/dist/modules/staff/lib/timesheets-projects-ui/SavedViewTabs.js +37 -0
- package/dist/modules/staff/lib/timesheets-projects-ui/SavedViewTabs.js.map +7 -0
- package/dist/modules/staff/lib/timesheets-projects-ui/ViewModeToggle.js +57 -0
- package/dist/modules/staff/lib/timesheets-projects-ui/ViewModeToggle.js.map +7 -0
- package/dist/modules/staff/lib/timesheets-projects-ui/useProjectsViewMode.js +50 -0
- package/dist/modules/staff/lib/timesheets-projects-ui/useProjectsViewMode.js.map +7 -0
- package/dist/modules/staff/lib/timesheets-ui/AddRowDropdown.js +163 -0
- package/dist/modules/staff/lib/timesheets-ui/AddRowDropdown.js.map +7 -0
- package/dist/modules/staff/lib/timesheets-ui/CalendarPicker.js +209 -0
- package/dist/modules/staff/lib/timesheets-ui/CalendarPicker.js.map +7 -0
- package/dist/modules/staff/lib/timesheets-ui/ColorPicker.js +52 -0
- package/dist/modules/staff/lib/timesheets-ui/ColorPicker.js.map +7 -0
- package/dist/modules/staff/lib/timesheets-ui/CreateProjectDialog.js +77 -0
- package/dist/modules/staff/lib/timesheets-ui/CreateProjectDialog.js.map +7 -0
- package/dist/modules/staff/lib/timesheets-ui/ListView.js +173 -0
- package/dist/modules/staff/lib/timesheets-ui/ListView.js.map +7 -0
- package/dist/modules/staff/lib/timesheets-ui/ProjectColorDot.js +32 -0
- package/dist/modules/staff/lib/timesheets-ui/ProjectColorDot.js.map +7 -0
- package/dist/modules/staff/lib/timesheets-ui/TimerBar.js +270 -0
- package/dist/modules/staff/lib/timesheets-ui/TimerBar.js.map +7 -0
- package/dist/modules/staff/lib/timesheets-ui/ViewSwitcher.js +57 -0
- package/dist/modules/staff/lib/timesheets-ui/ViewSwitcher.js.map +7 -0
- package/dist/modules/staff/lib/timesheets-ui/colors.js +43 -0
- package/dist/modules/staff/lib/timesheets-ui/colors.js.map +7 -0
- package/dist/modules/staff/migrations/Migration20260326135612.js +24 -0
- package/dist/modules/staff/migrations/Migration20260326135612.js.map +7 -0
- package/dist/modules/staff/migrations/Migration20260413102715.js +23 -0
- package/dist/modules/staff/migrations/Migration20260413102715.js.map +7 -0
- package/dist/modules/staff/migrations/Migration20260413111602.js +13 -0
- package/dist/modules/staff/migrations/Migration20260413111602.js.map +7 -0
- package/dist/modules/staff/migrations/Migration20260511112759.js +19 -0
- package/dist/modules/staff/migrations/Migration20260511112759.js.map +7 -0
- package/dist/modules/staff/search.js +35 -0
- package/dist/modules/staff/search.js.map +2 -2
- package/dist/modules/staff/setup.js +15 -1
- package/dist/modules/staff/setup.js.map +2 -2
- package/dist/modules/staff/widgets/dashboard/timesheets-hours-by-project/config.js +16 -0
- package/dist/modules/staff/widgets/dashboard/timesheets-hours-by-project/config.js.map +7 -0
- package/dist/modules/staff/widgets/dashboard/timesheets-hours-by-project/widget.client.js +126 -0
- package/dist/modules/staff/widgets/dashboard/timesheets-hours-by-project/widget.client.js.map +7 -0
- package/dist/modules/staff/widgets/dashboard/timesheets-hours-by-project/widget.js +26 -0
- package/dist/modules/staff/widgets/dashboard/timesheets-hours-by-project/widget.js.map +7 -0
- package/dist/modules/staff/widgets/dashboard/timesheets-time-reporting/config.js +15 -0
- package/dist/modules/staff/widgets/dashboard/timesheets-time-reporting/config.js.map +7 -0
- package/dist/modules/staff/widgets/dashboard/timesheets-time-reporting/widget.client.js +238 -0
- package/dist/modules/staff/widgets/dashboard/timesheets-time-reporting/widget.client.js.map +7 -0
- package/dist/modules/staff/widgets/dashboard/timesheets-time-reporting/widget.js +26 -0
- package/dist/modules/staff/widgets/dashboard/timesheets-time-reporting/widget.js.map +7 -0
- package/dist/modules/staff/widgets/injection/timer-sidebar-indicator/widget.js +145 -0
- package/dist/modules/staff/widgets/injection/timer-sidebar-indicator/widget.js.map +7 -0
- package/dist/modules/staff/widgets/injection-table.js +12 -0
- package/dist/modules/staff/widgets/injection-table.js.map +7 -0
- package/dist/modules/sync_excel/api/import/route.js +19 -17
- package/dist/modules/sync_excel/api/import/route.js.map +2 -2
- package/dist/modules/translations/commands/translations.js +22 -19
- package/dist/modules/translations/commands/translations.js.map +2 -2
- package/generated/entities/staff_time_entry/index.ts +17 -0
- package/generated/entities/staff_time_entry_segment/index.ts +10 -0
- package/generated/entities/staff_time_project/index.ts +16 -0
- package/generated/entities/staff_time_project_member/index.ts +13 -0
- package/generated/entities.ids.generated.ts +5 -1
- package/generated/entity-fields-registry.ts +64 -0
- package/package.json +7 -7
- package/src/helpers/integration/timesheetFixtures.ts +61 -0
- package/src/modules/attachments/api/library/[id]/route.ts +24 -17
- package/src/modules/attachments/api/route.ts +20 -14
- package/src/modules/auth/api/roles/acl/route.ts +11 -5
- package/src/modules/auth/api/sidebar/preferences/route.ts +33 -24
- package/src/modules/auth/api/users/acl/route.ts +17 -12
- package/src/modules/auth/commands/users.ts +96 -80
- package/src/modules/auth/services/sidebarPreferencesService.ts +40 -32
- package/src/modules/catalog/commands/categories.ts +61 -12
- package/src/modules/catalog/commands/products.ts +93 -60
- package/src/modules/catalog/commands/variants.ts +29 -16
- package/src/modules/currencies/commands/currencies.ts +27 -14
- package/src/modules/customer_accounts/api/admin/users.ts +31 -26
- package/src/modules/customer_accounts/api/password/reset-confirm.ts +5 -6
- package/src/modules/customer_accounts/api/portal/users/[id]/roles.ts +14 -13
- package/src/modules/customers/commands/addresses.ts +35 -23
- package/src/modules/customers/commands/companies.ts +166 -165
- package/src/modules/customers/commands/deals.ts +2 -4
- package/src/modules/customers/commands/interactions.ts +20 -26
- package/src/modules/customers/commands/people.ts +18 -15
- package/src/modules/customers/commands/personCompanyLinks.ts +109 -100
- package/src/modules/customers/commands/pipeline-stages.ts +31 -27
- package/src/modules/customers/commands/pipelines.ts +29 -23
- package/src/modules/customers/commands/tags.ts +13 -5
- package/src/modules/dashboards/api/users/widgets/route.ts +0 -1
- package/src/modules/dashboards/api/widgets/data/route.ts +36 -1
- package/src/modules/data_sync/lib/sync-engine.ts +4 -5
- package/src/modules/data_sync/lib/sync-run-service.ts +57 -28
- package/src/modules/directory/commands/organizations.ts +203 -166
- package/src/modules/inbox_ops/api/emails/[id]/reprocess/route.ts +26 -18
- package/src/modules/messages/commands/messages.ts +82 -80
- package/src/modules/messages/commands/shared.ts +138 -133
- package/src/modules/perspectives/api/[tableId]/route.ts +38 -27
- package/src/modules/resources/commands/resources.ts +127 -117
- package/src/modules/resources/commands/tags.ts +7 -3
- package/src/modules/sales/api/quotes/send/route.ts +17 -12
- package/src/modules/sales/commands/documents.ts +673 -481
- package/src/modules/sales/commands/payments.ts +158 -152
- package/src/modules/sales/commands/returns.ts +74 -63
- package/src/modules/staff/acl.ts +11 -0
- package/src/modules/staff/analytics.ts +30 -0
- package/src/modules/staff/api/guards.ts +59 -0
- package/src/modules/staff/api/interceptors.ts +122 -0
- package/src/modules/staff/api/timesheets/my-projects/[projectId]/route.ts +191 -0
- package/src/modules/staff/api/timesheets/my-projects/route.ts +115 -0
- package/src/modules/staff/api/timesheets/projects/kpis/route.ts +159 -0
- package/src/modules/staff/api/timesheets/time-entries/[id]/segments/[segmentId]/route.ts +187 -0
- package/src/modules/staff/api/timesheets/time-entries/[id]/segments/route.ts +191 -0
- package/src/modules/staff/api/timesheets/time-entries/[id]/timer-start/route.ts +168 -0
- package/src/modules/staff/api/timesheets/time-entries/[id]/timer-stop/route.ts +191 -0
- package/src/modules/staff/api/timesheets/time-entries/bulk/route.ts +292 -0
- package/src/modules/staff/api/timesheets/time-entries/route.ts +193 -0
- package/src/modules/staff/api/timesheets/time-projects/[id]/employees/route.ts +167 -0
- package/src/modules/staff/api/timesheets/time-projects/route.ts +244 -0
- package/src/modules/staff/backend/staff/timesheets/page.meta.ts +20 -0
- package/src/modules/staff/backend/staff/timesheets/page.tsx +899 -0
- package/src/modules/staff/backend/staff/timesheets/projects/[id]/edit/page.meta.ts +12 -0
- package/src/modules/staff/backend/staff/timesheets/projects/[id]/edit/page.tsx +141 -0
- package/src/modules/staff/backend/staff/timesheets/projects/[id]/page.meta.ts +12 -0
- package/src/modules/staff/backend/staff/timesheets/projects/[id]/page.tsx +579 -0
- package/src/modules/staff/backend/staff/timesheets/projects/create/page.meta.ts +12 -0
- package/src/modules/staff/backend/staff/timesheets/projects/create/page.tsx +90 -0
- package/src/modules/staff/backend/staff/timesheets/projects/page.meta.ts +23 -0
- package/src/modules/staff/backend/staff/timesheets/projects/page.tsx +765 -0
- package/src/modules/staff/backend/staff/timesheets/projects/projectFormConfig.ts +138 -0
- package/src/modules/staff/cli.ts +40 -1
- package/src/modules/staff/commands/index.ts +2 -0
- package/src/modules/staff/commands/leave-requests.ts +37 -29
- package/src/modules/staff/commands/team-members.ts +25 -20
- package/src/modules/staff/commands/timesheets-entries.ts +504 -0
- package/src/modules/staff/commands/timesheets-projects.ts +699 -0
- package/src/modules/staff/data/enrichers.ts +134 -0
- package/src/modules/staff/data/entities.ts +198 -0
- package/src/modules/staff/data/validators.ts +129 -0
- package/src/modules/staff/events.ts +13 -0
- package/src/modules/staff/i18n/de.json +209 -1
- package/src/modules/staff/i18n/en.json +209 -1
- package/src/modules/staff/i18n/es.json +209 -1
- package/src/modules/staff/i18n/pl.json +209 -1
- package/src/modules/staff/lib/crud.ts +8 -0
- package/src/modules/staff/lib/staffMemberResolver.ts +22 -0
- package/src/modules/staff/lib/timesheets-projects/computeProjectHoursTrend.ts +89 -0
- package/src/modules/staff/lib/timesheets-projects/computeProjectsKpis.ts +311 -0
- package/src/modules/staff/lib/timesheets-projects/dateBuckets.ts +37 -0
- package/src/modules/staff/lib/timesheets-projects/initials.ts +6 -0
- package/src/modules/staff/lib/timesheets-projects/kpiMath.ts +8 -0
- package/src/modules/staff/lib/timesheets-projects/listProjectMembersPreview.ts +83 -0
- package/src/modules/staff/lib/timesheets-projects-ui/HoursSparkline.tsx +75 -0
- package/src/modules/staff/lib/timesheets-projects-ui/ProjectCard.tsx +110 -0
- package/src/modules/staff/lib/timesheets-projects-ui/ProjectMembersAvatarStack.tsx +73 -0
- package/src/modules/staff/lib/timesheets-projects-ui/ProjectsKpiStrip.tsx +185 -0
- package/src/modules/staff/lib/timesheets-projects-ui/SavedViewTabs.tsx +53 -0
- package/src/modules/staff/lib/timesheets-projects-ui/ViewModeToggle.tsx +63 -0
- package/src/modules/staff/lib/timesheets-projects-ui/useProjectsViewMode.ts +63 -0
- package/src/modules/staff/lib/timesheets-ui/AddRowDropdown.tsx +188 -0
- package/src/modules/staff/lib/timesheets-ui/CalendarPicker.tsx +229 -0
- package/src/modules/staff/lib/timesheets-ui/ColorPicker.tsx +65 -0
- package/src/modules/staff/lib/timesheets-ui/CreateProjectDialog.tsx +99 -0
- package/src/modules/staff/lib/timesheets-ui/ListView.tsx +230 -0
- package/src/modules/staff/lib/timesheets-ui/ProjectColorDot.tsx +40 -0
- package/src/modules/staff/lib/timesheets-ui/TimerBar.tsx +327 -0
- package/src/modules/staff/lib/timesheets-ui/ViewSwitcher.tsx +60 -0
- package/src/modules/staff/lib/timesheets-ui/colors.ts +58 -0
- package/src/modules/staff/migrations/.snapshot-open-mercato.json +1148 -0
- package/src/modules/staff/migrations/Migration20260326135612.ts +26 -0
- package/src/modules/staff/migrations/Migration20260413102715.ts +25 -0
- package/src/modules/staff/migrations/Migration20260413111602.ts +13 -0
- package/src/modules/staff/migrations/Migration20260511112759.ts +21 -0
- package/src/modules/staff/search.ts +35 -0
- package/src/modules/staff/setup.ts +15 -0
- package/src/modules/staff/widgets/dashboard/timesheets-hours-by-project/config.ts +17 -0
- package/src/modules/staff/widgets/dashboard/timesheets-hours-by-project/widget.client.tsx +158 -0
- package/src/modules/staff/widgets/dashboard/timesheets-hours-by-project/widget.ts +25 -0
- package/src/modules/staff/widgets/dashboard/timesheets-time-reporting/config.ts +15 -0
- package/src/modules/staff/widgets/dashboard/timesheets-time-reporting/widget.client.tsx +297 -0
- package/src/modules/staff/widgets/dashboard/timesheets-time-reporting/widget.ts +25 -0
- package/src/modules/staff/widgets/injection/timer-sidebar-indicator/widget.tsx +161 -0
- package/src/modules/staff/widgets/injection-table.ts +10 -0
- package/src/modules/sync_excel/api/import/route.ts +23 -18
- package/src/modules/translations/commands/translations.ts +49 -41
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import { apiCall, readApiResultOrThrow } from "@open-mercato/ui/backend/utils/apiCall";
|
|
5
|
+
import { Button } from "@open-mercato/ui/primitives/button";
|
|
6
|
+
import { Input } from "@open-mercato/ui/primitives/input";
|
|
7
|
+
import { useT } from "@open-mercato/shared/lib/i18n/context";
|
|
8
|
+
import { DEFAULT_SETTINGS, hydrateSettings } from "./config.js";
|
|
9
|
+
function formatElapsed(startedAt) {
|
|
10
|
+
const elapsed = Math.max(0, Date.now() - new Date(startedAt).getTime());
|
|
11
|
+
const totalSeconds = Math.floor(elapsed / 1e3);
|
|
12
|
+
const hours = Math.floor(totalSeconds / 3600);
|
|
13
|
+
const minutes = Math.floor(totalSeconds % 3600 / 60);
|
|
14
|
+
const seconds = totalSeconds % 60;
|
|
15
|
+
return `${String(hours).padStart(2, "0")}:${String(minutes).padStart(2, "0")}:${String(seconds).padStart(2, "0")}`;
|
|
16
|
+
}
|
|
17
|
+
const TimeReportingWidget = ({
|
|
18
|
+
mode,
|
|
19
|
+
settings = DEFAULT_SETTINGS,
|
|
20
|
+
onSettingsChange,
|
|
21
|
+
refreshToken,
|
|
22
|
+
onRefreshStateChange
|
|
23
|
+
}) => {
|
|
24
|
+
const t = useT();
|
|
25
|
+
const hydrated = React.useMemo(() => hydrateSettings(settings), [settings]);
|
|
26
|
+
const [projects, setProjects] = React.useState([]);
|
|
27
|
+
const [selectedProjectId, setSelectedProjectId] = React.useState(hydrated.lastProjectId);
|
|
28
|
+
const [notes, setNotes] = React.useState("");
|
|
29
|
+
const [timer, setTimer] = React.useState({ entryId: null, running: false, startedAt: null, projectId: null });
|
|
30
|
+
const [elapsed, setElapsed] = React.useState("00:00:00");
|
|
31
|
+
const [staffMemberId, setStaffMemberId] = React.useState(null);
|
|
32
|
+
const [loading, setLoading] = React.useState(true);
|
|
33
|
+
const [actionLoading, setActionLoading] = React.useState(false);
|
|
34
|
+
const [error, setError] = React.useState(null);
|
|
35
|
+
const loadState = React.useCallback(async () => {
|
|
36
|
+
onRefreshStateChange?.(true);
|
|
37
|
+
setLoading(true);
|
|
38
|
+
setError(null);
|
|
39
|
+
try {
|
|
40
|
+
const assignmentsRes = await readApiResultOrThrow(
|
|
41
|
+
"/api/staff/timesheets/my-projects?pageSize=100",
|
|
42
|
+
void 0,
|
|
43
|
+
{ errorMessage: "", fallback: { items: [] } }
|
|
44
|
+
);
|
|
45
|
+
const assignmentItems = Array.isArray(assignmentsRes.items) ? assignmentsRes.items : [];
|
|
46
|
+
const projectIds = assignmentItems.map((item) => String(item.time_project_id ?? item.timeProjectId ?? "")).filter((id) => id.length > 0);
|
|
47
|
+
if (projectIds.length > 0) {
|
|
48
|
+
const projectsRes = await readApiResultOrThrow(
|
|
49
|
+
`/api/staff/timesheets/time-projects?ids=${projectIds.join(",")}&pageSize=100`,
|
|
50
|
+
void 0,
|
|
51
|
+
{ errorMessage: "", fallback: { items: [] } }
|
|
52
|
+
);
|
|
53
|
+
const items = Array.isArray(projectsRes.items) ? projectsRes.items : [];
|
|
54
|
+
setProjects(items.map((item) => ({
|
|
55
|
+
id: String(item.id ?? ""),
|
|
56
|
+
name: String(item.name ?? ""),
|
|
57
|
+
code: typeof item.code === "string" ? item.code : null
|
|
58
|
+
})));
|
|
59
|
+
} else {
|
|
60
|
+
setProjects([]);
|
|
61
|
+
}
|
|
62
|
+
const selfRes = await readApiResultOrThrow(
|
|
63
|
+
"/api/staff/team-members/self",
|
|
64
|
+
void 0,
|
|
65
|
+
{ errorMessage: "", fallback: { member: null } }
|
|
66
|
+
);
|
|
67
|
+
const memberId = selfRes.member?.id ?? null;
|
|
68
|
+
setStaffMemberId(memberId);
|
|
69
|
+
if (memberId) {
|
|
70
|
+
const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
71
|
+
const entriesRes = await readApiResultOrThrow(
|
|
72
|
+
`/api/staff/timesheets/time-entries?staffMemberId=${memberId}&from=${today}&to=${today}&pageSize=100`,
|
|
73
|
+
void 0,
|
|
74
|
+
{ errorMessage: "", fallback: { items: [] } }
|
|
75
|
+
);
|
|
76
|
+
const entries = Array.isArray(entriesRes.items) ? entriesRes.items : [];
|
|
77
|
+
const running = entries.find((e) => {
|
|
78
|
+
const startedAt = e.started_at ?? e.startedAt;
|
|
79
|
+
const endedAt = e.ended_at ?? e.endedAt;
|
|
80
|
+
return startedAt != null && endedAt == null;
|
|
81
|
+
});
|
|
82
|
+
if (running) {
|
|
83
|
+
setTimer({
|
|
84
|
+
entryId: String(running.id ?? ""),
|
|
85
|
+
running: true,
|
|
86
|
+
startedAt: String(running.started_at ?? running.startedAt ?? ""),
|
|
87
|
+
projectId: String(running.time_project_id ?? running.timeProjectId ?? "")
|
|
88
|
+
});
|
|
89
|
+
} else {
|
|
90
|
+
setTimer({ entryId: null, running: false, startedAt: null, projectId: null });
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
} catch (err) {
|
|
94
|
+
console.error("staff.timesheets.timeReporting.load", err);
|
|
95
|
+
setError(t("staff.timesheets.widgets.timeReporting.error", "Failed to load timer state"));
|
|
96
|
+
} finally {
|
|
97
|
+
setLoading(false);
|
|
98
|
+
onRefreshStateChange?.(false);
|
|
99
|
+
}
|
|
100
|
+
}, [onRefreshStateChange, t]);
|
|
101
|
+
React.useEffect(() => {
|
|
102
|
+
void loadState();
|
|
103
|
+
}, [loadState, refreshToken]);
|
|
104
|
+
React.useEffect(() => {
|
|
105
|
+
if (!timer.running || !timer.startedAt) return;
|
|
106
|
+
const tick = () => setElapsed(formatElapsed(timer.startedAt));
|
|
107
|
+
tick();
|
|
108
|
+
const interval = setInterval(tick, 1e3);
|
|
109
|
+
return () => clearInterval(interval);
|
|
110
|
+
}, [timer.running, timer.startedAt]);
|
|
111
|
+
const handleStart = React.useCallback(async () => {
|
|
112
|
+
if (!selectedProjectId || !staffMemberId) return;
|
|
113
|
+
if (timer.running) return;
|
|
114
|
+
setActionLoading(true);
|
|
115
|
+
try {
|
|
116
|
+
const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
117
|
+
const createRes = await apiCall("/api/staff/timesheets/time-entries", {
|
|
118
|
+
method: "POST",
|
|
119
|
+
headers: { "Content-Type": "application/json" },
|
|
120
|
+
body: JSON.stringify({
|
|
121
|
+
staffMemberId,
|
|
122
|
+
date: today,
|
|
123
|
+
timeProjectId: selectedProjectId,
|
|
124
|
+
durationMinutes: 0,
|
|
125
|
+
notes: notes.trim() || null,
|
|
126
|
+
source: "timer"
|
|
127
|
+
})
|
|
128
|
+
});
|
|
129
|
+
if (!createRes.ok) throw new Error("Failed to create entry");
|
|
130
|
+
const body = createRes.result;
|
|
131
|
+
const entryId = String(body?.id ?? body?.item?.id ?? "");
|
|
132
|
+
if (!entryId) throw new Error("Failed to extract entry ID");
|
|
133
|
+
await apiCall(`/api/staff/timesheets/time-entries/${entryId}/timer-start`, { method: "POST" });
|
|
134
|
+
onSettingsChange({ ...hydrated, lastProjectId: selectedProjectId });
|
|
135
|
+
await loadState();
|
|
136
|
+
} catch (err) {
|
|
137
|
+
console.error("staff.timesheets.timeReporting.start", err);
|
|
138
|
+
setError(t("staff.timesheets.widgets.timeReporting.startError", "Failed to start timer"));
|
|
139
|
+
} finally {
|
|
140
|
+
setActionLoading(false);
|
|
141
|
+
}
|
|
142
|
+
}, [selectedProjectId, staffMemberId, timer.running, notes, hydrated, onSettingsChange, loadState, t]);
|
|
143
|
+
const handleStop = React.useCallback(async () => {
|
|
144
|
+
if (!timer.entryId) return;
|
|
145
|
+
setActionLoading(true);
|
|
146
|
+
try {
|
|
147
|
+
await apiCall(`/api/staff/timesheets/time-entries/${timer.entryId}/timer-stop`, { method: "POST" });
|
|
148
|
+
await loadState();
|
|
149
|
+
} catch (err) {
|
|
150
|
+
console.error("staff.timesheets.timeReporting.stop", err);
|
|
151
|
+
setError(t("staff.timesheets.widgets.timeReporting.stopError", "Failed to stop timer"));
|
|
152
|
+
} finally {
|
|
153
|
+
setActionLoading(false);
|
|
154
|
+
}
|
|
155
|
+
}, [timer.entryId, loadState, t]);
|
|
156
|
+
if (mode === "settings") {
|
|
157
|
+
return /* @__PURE__ */ jsx("div", { className: "space-y-2 text-sm", children: /* @__PURE__ */ jsx("p", { className: "text-muted-foreground", children: t("staff.timesheets.widgets.timeReporting.settings.description", "No additional settings. Select a project and start tracking from the widget.") }) });
|
|
158
|
+
}
|
|
159
|
+
if (loading) {
|
|
160
|
+
return /* @__PURE__ */ jsx("div", { className: "flex h-full items-center justify-center py-8", children: /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: t("staff.timesheets.widgets.timeReporting.loading", "Loading...") }) });
|
|
161
|
+
}
|
|
162
|
+
if (error) {
|
|
163
|
+
return /* @__PURE__ */ jsx("div", { className: "flex h-full items-center justify-center py-8", children: /* @__PURE__ */ jsx("p", { className: "text-sm text-destructive", children: error }) });
|
|
164
|
+
}
|
|
165
|
+
if (projects.length === 0) {
|
|
166
|
+
return /* @__PURE__ */ jsx("div", { className: "flex h-full items-center justify-center py-8", children: /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: t("staff.timesheets.widgets.timeReporting.noProjects", "No projects assigned.") }) });
|
|
167
|
+
}
|
|
168
|
+
if (timer.running) {
|
|
169
|
+
const runningProject = projects.find((p) => p.id === timer.projectId);
|
|
170
|
+
return /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
|
|
171
|
+
/* @__PURE__ */ jsx("div", { className: "text-sm text-muted-foreground", children: runningProject?.name ?? t("staff.timesheets.widgets.timeReporting.unknownProject", "Unknown project") }),
|
|
172
|
+
/* @__PURE__ */ jsxs("div", { className: "text-center", children: [
|
|
173
|
+
/* @__PURE__ */ jsx("p", { className: "font-mono text-3xl font-bold tabular-nums", children: elapsed }),
|
|
174
|
+
/* @__PURE__ */ jsx("p", { className: "mt-1 text-xs text-muted-foreground", children: t("staff.timesheets.widgets.timeReporting.running", "Timer running") })
|
|
175
|
+
] }),
|
|
176
|
+
/* @__PURE__ */ jsx(
|
|
177
|
+
Button,
|
|
178
|
+
{
|
|
179
|
+
type: "button",
|
|
180
|
+
variant: "destructive",
|
|
181
|
+
className: "w-full",
|
|
182
|
+
onClick: handleStop,
|
|
183
|
+
disabled: actionLoading,
|
|
184
|
+
children: t("staff.timesheets.widgets.timeReporting.stop", "Stop Timer")
|
|
185
|
+
}
|
|
186
|
+
)
|
|
187
|
+
] });
|
|
188
|
+
}
|
|
189
|
+
return /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
|
|
190
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
|
|
191
|
+
/* @__PURE__ */ jsx("label", { className: "text-xs font-medium text-muted-foreground", htmlFor: "timer-project", children: t("staff.timesheets.widgets.timeReporting.project", "Project") }),
|
|
192
|
+
/* @__PURE__ */ jsxs(
|
|
193
|
+
"select",
|
|
194
|
+
{
|
|
195
|
+
id: "timer-project",
|
|
196
|
+
className: "w-full rounded-md border bg-background px-3 py-2 text-sm",
|
|
197
|
+
value: selectedProjectId ?? "",
|
|
198
|
+
onChange: (e) => setSelectedProjectId(e.target.value || null),
|
|
199
|
+
children: [
|
|
200
|
+
/* @__PURE__ */ jsx("option", { value: "", children: t("staff.timesheets.widgets.timeReporting.selectProject", "Select project...") }),
|
|
201
|
+
projects.map((project) => /* @__PURE__ */ jsxs("option", { value: project.id, children: [
|
|
202
|
+
project.name,
|
|
203
|
+
project.code ? ` (${project.code})` : ""
|
|
204
|
+
] }, project.id))
|
|
205
|
+
]
|
|
206
|
+
}
|
|
207
|
+
)
|
|
208
|
+
] }),
|
|
209
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
|
|
210
|
+
/* @__PURE__ */ jsx("label", { className: "text-xs font-medium text-muted-foreground", htmlFor: "timer-notes", children: t("staff.timesheets.widgets.timeReporting.taskNote", "Task / Note") }),
|
|
211
|
+
/* @__PURE__ */ jsx(
|
|
212
|
+
Input,
|
|
213
|
+
{
|
|
214
|
+
id: "timer-notes",
|
|
215
|
+
value: notes,
|
|
216
|
+
onChange: (e) => setNotes(e.target.value),
|
|
217
|
+
placeholder: t("staff.timesheets.widgets.timeReporting.notesPlaceholder", "What are you working on?")
|
|
218
|
+
}
|
|
219
|
+
)
|
|
220
|
+
] }),
|
|
221
|
+
/* @__PURE__ */ jsx("div", { className: "text-center", children: /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: t("staff.timesheets.widgets.timeReporting.notRunning", "Not running") }) }),
|
|
222
|
+
/* @__PURE__ */ jsx(
|
|
223
|
+
Button,
|
|
224
|
+
{
|
|
225
|
+
type: "button",
|
|
226
|
+
className: "w-full",
|
|
227
|
+
onClick: handleStart,
|
|
228
|
+
disabled: actionLoading || !selectedProjectId,
|
|
229
|
+
children: t("staff.timesheets.widgets.timeReporting.start", "Start Timer")
|
|
230
|
+
}
|
|
231
|
+
)
|
|
232
|
+
] });
|
|
233
|
+
};
|
|
234
|
+
var widget_client_default = TimeReportingWidget;
|
|
235
|
+
export {
|
|
236
|
+
widget_client_default as default
|
|
237
|
+
};
|
|
238
|
+
//# sourceMappingURL=widget.client.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../../../src/modules/staff/widgets/dashboard/timesheets-time-reporting/widget.client.tsx"],
|
|
4
|
+
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport type { DashboardWidgetComponentProps } from '@open-mercato/shared/modules/dashboard/widgets'\nimport { apiCall, readApiResultOrThrow } from '@open-mercato/ui/backend/utils/apiCall'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { Input } from '@open-mercato/ui/primitives/input'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { DEFAULT_SETTINGS, hydrateSettings, type TimeReportingSettings } from './config'\n\ntype ProjectOption = { id: string; name: string; code: string | null }\n\ntype TimerState = {\n entryId: string | null\n running: boolean\n startedAt: string | null\n projectId: string | null\n}\n\nfunction formatElapsed(startedAt: string): string {\n const elapsed = Math.max(0, Date.now() - new Date(startedAt).getTime())\n const totalSeconds = Math.floor(elapsed / 1000)\n const hours = Math.floor(totalSeconds / 3600)\n const minutes = Math.floor((totalSeconds % 3600) / 60)\n const seconds = totalSeconds % 60\n return `${String(hours).padStart(2, '0')}:${String(minutes).padStart(2, '0')}:${String(seconds).padStart(2, '0')}`\n}\n\nconst TimeReportingWidget: React.FC<DashboardWidgetComponentProps<TimeReportingSettings>> = ({\n mode,\n settings = DEFAULT_SETTINGS,\n onSettingsChange,\n refreshToken,\n onRefreshStateChange,\n}) => {\n const t = useT()\n const hydrated = React.useMemo(() => hydrateSettings(settings), [settings])\n\n const [projects, setProjects] = React.useState<ProjectOption[]>([])\n const [selectedProjectId, setSelectedProjectId] = React.useState<string | null>(hydrated.lastProjectId)\n const [notes, setNotes] = React.useState('')\n const [timer, setTimer] = React.useState<TimerState>({ entryId: null, running: false, startedAt: null, projectId: null })\n const [elapsed, setElapsed] = React.useState('00:00:00')\n const [staffMemberId, setStaffMemberId] = React.useState<string | null>(null)\n const [loading, setLoading] = React.useState(true)\n const [actionLoading, setActionLoading] = React.useState(false)\n const [error, setError] = React.useState<string | null>(null)\n\n const loadState = React.useCallback(async () => {\n onRefreshStateChange?.(true)\n setLoading(true)\n setError(null)\n try {\n // Load assigned projects\n const assignmentsRes = await readApiResultOrThrow<{ items?: Array<Record<string, unknown>> }>(\n '/api/staff/timesheets/my-projects?pageSize=100',\n undefined,\n { errorMessage: '', fallback: { items: [] } },\n )\n const assignmentItems = Array.isArray(assignmentsRes.items) ? assignmentsRes.items : []\n const projectIds = assignmentItems\n .map((item) => String(item.time_project_id ?? item.timeProjectId ?? ''))\n .filter((id) => id.length > 0)\n\n if (projectIds.length > 0) {\n const projectsRes = await readApiResultOrThrow<{ items?: Array<Record<string, unknown>> }>(\n `/api/staff/timesheets/time-projects?ids=${projectIds.join(',')}&pageSize=100`,\n undefined,\n { errorMessage: '', fallback: { items: [] } },\n )\n const items = Array.isArray(projectsRes.items) ? projectsRes.items : []\n setProjects(items.map((item) => ({\n id: String(item.id ?? ''),\n name: String(item.name ?? ''),\n code: typeof item.code === 'string' ? item.code : null,\n })))\n } else {\n setProjects([])\n }\n\n // Check for active timer \u2014 look for today's entries with startedAt set and endedAt null\n const selfRes = await readApiResultOrThrow<{ member?: { id: string } | null }>(\n '/api/staff/team-members/self',\n undefined,\n { errorMessage: '', fallback: { member: null } },\n )\n const memberId = selfRes.member?.id ?? null\n setStaffMemberId(memberId)\n if (memberId) {\n const today = new Date().toISOString().slice(0, 10)\n const entriesRes = await readApiResultOrThrow<{ items?: Array<Record<string, unknown>> }>(\n `/api/staff/timesheets/time-entries?staffMemberId=${memberId}&from=${today}&to=${today}&pageSize=100`,\n undefined,\n { errorMessage: '', fallback: { items: [] } },\n )\n const entries = Array.isArray(entriesRes.items) ? entriesRes.items : []\n const running = entries.find((e) => {\n const startedAt = e.started_at ?? e.startedAt\n const endedAt = e.ended_at ?? e.endedAt\n return startedAt != null && endedAt == null\n })\n if (running) {\n setTimer({\n entryId: String(running.id ?? ''),\n running: true,\n startedAt: String(running.started_at ?? running.startedAt ?? ''),\n projectId: String(running.time_project_id ?? running.timeProjectId ?? ''),\n })\n } else {\n setTimer({ entryId: null, running: false, startedAt: null, projectId: null })\n }\n }\n } catch (err) {\n console.error('staff.timesheets.timeReporting.load', err)\n setError(t('staff.timesheets.widgets.timeReporting.error', 'Failed to load timer state'))\n } finally {\n setLoading(false)\n onRefreshStateChange?.(false)\n }\n }, [onRefreshStateChange, t])\n\n React.useEffect(() => {\n void loadState()\n }, [loadState, refreshToken])\n\n // Tick elapsed time\n React.useEffect(() => {\n if (!timer.running || !timer.startedAt) return\n const tick = () => setElapsed(formatElapsed(timer.startedAt!))\n tick()\n const interval = setInterval(tick, 1000)\n return () => clearInterval(interval)\n }, [timer.running, timer.startedAt])\n\n const handleStart = React.useCallback(async () => {\n if (!selectedProjectId || !staffMemberId) return\n if (timer.running) return\n setActionLoading(true)\n try {\n const today = new Date().toISOString().slice(0, 10)\n // Create entry + start timer\n const createRes = await apiCall<Record<string, unknown>>('/api/staff/timesheets/time-entries', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n staffMemberId,\n date: today,\n timeProjectId: selectedProjectId,\n durationMinutes: 0,\n notes: notes.trim() || null,\n source: 'timer',\n }),\n })\n if (!createRes.ok) throw new Error('Failed to create entry')\n const body = createRes.result as Record<string, unknown> | null\n const entryId = String(body?.id ?? (body?.item as Record<string, unknown> | undefined)?.id ?? '')\n if (!entryId) throw new Error('Failed to extract entry ID')\n\n await apiCall(`/api/staff/timesheets/time-entries/${entryId}/timer-start`, { method: 'POST' })\n\n onSettingsChange({ ...hydrated, lastProjectId: selectedProjectId })\n await loadState()\n } catch (err) {\n console.error('staff.timesheets.timeReporting.start', err)\n setError(t('staff.timesheets.widgets.timeReporting.startError', 'Failed to start timer'))\n } finally {\n setActionLoading(false)\n }\n }, [selectedProjectId, staffMemberId, timer.running, notes, hydrated, onSettingsChange, loadState, t])\n\n const handleStop = React.useCallback(async () => {\n if (!timer.entryId) return\n setActionLoading(true)\n try {\n await apiCall(`/api/staff/timesheets/time-entries/${timer.entryId}/timer-stop`, { method: 'POST' })\n await loadState()\n } catch (err) {\n console.error('staff.timesheets.timeReporting.stop', err)\n setError(t('staff.timesheets.widgets.timeReporting.stopError', 'Failed to stop timer'))\n } finally {\n setActionLoading(false)\n }\n }, [timer.entryId, loadState, t])\n\n if (mode === 'settings') {\n return (\n <div className=\"space-y-2 text-sm\">\n <p className=\"text-muted-foreground\">\n {t('staff.timesheets.widgets.timeReporting.settings.description', 'No additional settings. Select a project and start tracking from the widget.')}\n </p>\n </div>\n )\n }\n\n if (loading) {\n return (\n <div className=\"flex h-full items-center justify-center py-8\">\n <p className=\"text-sm text-muted-foreground\">{t('staff.timesheets.widgets.timeReporting.loading', 'Loading...')}</p>\n </div>\n )\n }\n\n if (error) {\n return (\n <div className=\"flex h-full items-center justify-center py-8\">\n <p className=\"text-sm text-destructive\">{error}</p>\n </div>\n )\n }\n\n if (projects.length === 0) {\n return (\n <div className=\"flex h-full items-center justify-center py-8\">\n <p className=\"text-sm text-muted-foreground\">\n {t('staff.timesheets.widgets.timeReporting.noProjects', 'No projects assigned.')}\n </p>\n </div>\n )\n }\n\n // Timer is running\n if (timer.running) {\n const runningProject = projects.find((p) => p.id === timer.projectId)\n return (\n <div className=\"space-y-3\">\n <div className=\"text-sm text-muted-foreground\">\n {runningProject?.name ?? t('staff.timesheets.widgets.timeReporting.unknownProject', 'Unknown project')}\n </div>\n <div className=\"text-center\">\n <p className=\"font-mono text-3xl font-bold tabular-nums\">{elapsed}</p>\n <p className=\"mt-1 text-xs text-muted-foreground\">\n {t('staff.timesheets.widgets.timeReporting.running', 'Timer running')}\n </p>\n </div>\n <Button\n type=\"button\"\n variant=\"destructive\"\n className=\"w-full\"\n onClick={handleStop}\n disabled={actionLoading}\n >\n {t('staff.timesheets.widgets.timeReporting.stop', 'Stop Timer')}\n </Button>\n </div>\n )\n }\n\n // Timer not running \u2014 show start form\n return (\n <div className=\"space-y-3\">\n <div className=\"space-y-1.5\">\n <label className=\"text-xs font-medium text-muted-foreground\" htmlFor=\"timer-project\">\n {t('staff.timesheets.widgets.timeReporting.project', 'Project')}\n </label>\n <select\n id=\"timer-project\"\n className=\"w-full rounded-md border bg-background px-3 py-2 text-sm\"\n value={selectedProjectId ?? ''}\n onChange={(e) => setSelectedProjectId(e.target.value || null)}\n >\n <option value=\"\">{t('staff.timesheets.widgets.timeReporting.selectProject', 'Select project...')}</option>\n {projects.map((project) => (\n <option key={project.id} value={project.id}>\n {project.name}{project.code ? ` (${project.code})` : ''}\n </option>\n ))}\n </select>\n </div>\n <div className=\"space-y-1.5\">\n <label className=\"text-xs font-medium text-muted-foreground\" htmlFor=\"timer-notes\">\n {t('staff.timesheets.widgets.timeReporting.taskNote', 'Task / Note')}\n </label>\n <Input\n id=\"timer-notes\"\n value={notes}\n onChange={(e) => setNotes(e.target.value)}\n placeholder={t('staff.timesheets.widgets.timeReporting.notesPlaceholder', 'What are you working on?')}\n />\n </div>\n <div className=\"text-center\">\n <p className=\"text-sm text-muted-foreground\">\n {t('staff.timesheets.widgets.timeReporting.notRunning', 'Not running')}\n </p>\n </div>\n <Button\n type=\"button\"\n className=\"w-full\"\n onClick={handleStart}\n disabled={actionLoading || !selectedProjectId}\n >\n {t('staff.timesheets.widgets.timeReporting.start', 'Start Timer')}\n </Button>\n </div>\n )\n}\n\nexport default TimeReportingWidget\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { lazyDashboardWidget } from "@open-mercato/shared/modules/dashboard/widgets";
|
|
2
|
+
import { DEFAULT_SETTINGS, hydrateSettings } from "./config.js";
|
|
3
|
+
const TimeReportingWidget = lazyDashboardWidget(() => import("./widget.client.js"));
|
|
4
|
+
const widget = {
|
|
5
|
+
metadata: {
|
|
6
|
+
id: "staff.timesheets.timeReporting",
|
|
7
|
+
title: "Time Reporting",
|
|
8
|
+
description: "Quick start/stop timer for the current work item",
|
|
9
|
+
features: ["dashboards.view", "staff.timesheets.manage_own"],
|
|
10
|
+
defaultSize: "sm",
|
|
11
|
+
defaultEnabled: false,
|
|
12
|
+
defaultSettings: DEFAULT_SETTINGS,
|
|
13
|
+
tags: ["staff", "timesheets", "timer"],
|
|
14
|
+
category: "productivity",
|
|
15
|
+
icon: "timer",
|
|
16
|
+
supportsRefresh: true
|
|
17
|
+
},
|
|
18
|
+
Widget: TimeReportingWidget,
|
|
19
|
+
hydrateSettings,
|
|
20
|
+
dehydrateSettings: (s) => ({ lastProjectId: s.lastProjectId })
|
|
21
|
+
};
|
|
22
|
+
var widget_default = widget;
|
|
23
|
+
export {
|
|
24
|
+
widget_default as default
|
|
25
|
+
};
|
|
26
|
+
//# sourceMappingURL=widget.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../../../src/modules/staff/widgets/dashboard/timesheets-time-reporting/widget.ts"],
|
|
4
|
+
"sourcesContent": ["import { lazyDashboardWidget, type DashboardWidgetModule } from '@open-mercato/shared/modules/dashboard/widgets'\nimport { DEFAULT_SETTINGS, hydrateSettings, type TimeReportingSettings } from './config'\n\nconst TimeReportingWidget = lazyDashboardWidget(() => import('./widget.client'))\n\nconst widget: DashboardWidgetModule<TimeReportingSettings> = {\n metadata: {\n id: 'staff.timesheets.timeReporting',\n title: 'Time Reporting',\n description: 'Quick start/stop timer for the current work item',\n features: ['dashboards.view', 'staff.timesheets.manage_own'],\n defaultSize: 'sm',\n defaultEnabled: false,\n defaultSettings: DEFAULT_SETTINGS,\n tags: ['staff', 'timesheets', 'timer'],\n category: 'productivity',\n icon: 'timer',\n supportsRefresh: true,\n },\n Widget: TimeReportingWidget,\n hydrateSettings,\n dehydrateSettings: (s) => ({ lastProjectId: s.lastProjectId }),\n}\n\nexport default widget\n"],
|
|
5
|
+
"mappings": "AAAA,SAAS,2BAAuD;AAChE,SAAS,kBAAkB,uBAAmD;AAE9E,MAAM,sBAAsB,oBAAoB,MAAM,OAAO,iBAAiB,CAAC;AAE/E,MAAM,SAAuD;AAAA,EAC3D,UAAU;AAAA,IACR,IAAI;AAAA,IACJ,OAAO;AAAA,IACP,aAAa;AAAA,IACb,UAAU,CAAC,mBAAmB,6BAA6B;AAAA,IAC3D,aAAa;AAAA,IACb,gBAAgB;AAAA,IAChB,iBAAiB;AAAA,IACjB,MAAM,CAAC,SAAS,cAAc,OAAO;AAAA,IACrC,UAAU;AAAA,IACV,MAAM;AAAA,IACN,iBAAiB;AAAA,EACnB;AAAA,EACA,QAAQ;AAAA,EACR;AAAA,EACA,mBAAmB,CAAC,OAAO,EAAE,eAAe,EAAE,cAAc;AAC9D;AAEA,IAAO,iBAAQ;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import { apiCall } from "@open-mercato/ui/backend/utils/apiCall";
|
|
5
|
+
import { useT } from "@open-mercato/shared/lib/i18n/context";
|
|
6
|
+
import { ProjectColorDot } from "../../../lib/timesheets-ui/ProjectColorDot.js";
|
|
7
|
+
const STORAGE_KEY = "om:timesheets:activeTimer";
|
|
8
|
+
function saveToSession(state) {
|
|
9
|
+
try {
|
|
10
|
+
if (state) sessionStorage.setItem(STORAGE_KEY, JSON.stringify(state));
|
|
11
|
+
else sessionStorage.removeItem(STORAGE_KEY);
|
|
12
|
+
} catch {
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
function loadFromSession() {
|
|
16
|
+
try {
|
|
17
|
+
const raw = sessionStorage.getItem(STORAGE_KEY);
|
|
18
|
+
if (!raw) return null;
|
|
19
|
+
const parsed = JSON.parse(raw);
|
|
20
|
+
if (parsed?.startedAt && parsed?.entryId) return parsed;
|
|
21
|
+
} catch {
|
|
22
|
+
}
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
function formatElapsed(seconds) {
|
|
26
|
+
const h = Math.floor(seconds / 3600);
|
|
27
|
+
const m = Math.floor(seconds % 3600 / 60);
|
|
28
|
+
const s = seconds % 60;
|
|
29
|
+
return `${h}:${String(m).padStart(2, "0")}:${String(s).padStart(2, "0")}`;
|
|
30
|
+
}
|
|
31
|
+
function getToday() {
|
|
32
|
+
const now = /* @__PURE__ */ new Date();
|
|
33
|
+
return `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, "0")}-${String(now.getDate()).padStart(2, "0")}`;
|
|
34
|
+
}
|
|
35
|
+
function TimerSidebarIndicator() {
|
|
36
|
+
const t = useT();
|
|
37
|
+
const [timer, setTimer] = React.useState(loadFromSession);
|
|
38
|
+
const [elapsed, setElapsed] = React.useState(0);
|
|
39
|
+
const intervalRef = React.useRef(null);
|
|
40
|
+
const updateTimer = React.useCallback((next) => {
|
|
41
|
+
setTimer(next);
|
|
42
|
+
saveToSession(next);
|
|
43
|
+
}, []);
|
|
44
|
+
const pollTimer = React.useCallback(async () => {
|
|
45
|
+
try {
|
|
46
|
+
const selfRes = await apiCall("/api/staff/team-members/self");
|
|
47
|
+
const memberId = selfRes.result?.member?.id;
|
|
48
|
+
if (!memberId) {
|
|
49
|
+
updateTimer(null);
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
const today = getToday();
|
|
53
|
+
const res = await apiCall(
|
|
54
|
+
`/api/staff/timesheets/time-entries?staffMemberId=${memberId}&from=${today}&to=${today}&pageSize=50`
|
|
55
|
+
);
|
|
56
|
+
const items = res.result?.items ?? [];
|
|
57
|
+
const active = items.find((e) => e.started_at && !e.ended_at);
|
|
58
|
+
if (!active) {
|
|
59
|
+
updateTimer(null);
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
const projectId = String(active.time_project_id ?? "");
|
|
63
|
+
let projectName = "";
|
|
64
|
+
let projectColor = null;
|
|
65
|
+
if (projectId) {
|
|
66
|
+
const projRes = await apiCall(
|
|
67
|
+
`/api/staff/timesheets/time-projects?ids=${projectId}&pageSize=1`
|
|
68
|
+
);
|
|
69
|
+
const proj = (projRes.result?.items ?? [])[0];
|
|
70
|
+
if (proj) {
|
|
71
|
+
projectName = String(proj.name ?? "");
|
|
72
|
+
projectColor = typeof proj.color === "string" ? proj.color : null;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
updateTimer({
|
|
76
|
+
entryId: String(active.id ?? ""),
|
|
77
|
+
projectName,
|
|
78
|
+
projectColor,
|
|
79
|
+
startedAt: String(active.started_at ?? "")
|
|
80
|
+
});
|
|
81
|
+
} catch {
|
|
82
|
+
}
|
|
83
|
+
}, [updateTimer]);
|
|
84
|
+
React.useEffect(() => {
|
|
85
|
+
void pollTimer();
|
|
86
|
+
const poll = setInterval(() => {
|
|
87
|
+
void pollTimer();
|
|
88
|
+
}, 3e4);
|
|
89
|
+
return () => clearInterval(poll);
|
|
90
|
+
}, [pollTimer]);
|
|
91
|
+
React.useEffect(() => {
|
|
92
|
+
if (!timer) {
|
|
93
|
+
if (intervalRef.current) {
|
|
94
|
+
clearInterval(intervalRef.current);
|
|
95
|
+
intervalRef.current = null;
|
|
96
|
+
}
|
|
97
|
+
setElapsed(0);
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
const startTime = new Date(timer.startedAt).getTime();
|
|
101
|
+
const calcElapsed = () => Math.max(0, Math.floor((Date.now() - startTime) / 1e3));
|
|
102
|
+
setElapsed(calcElapsed());
|
|
103
|
+
intervalRef.current = setInterval(() => setElapsed(calcElapsed()), 1e3);
|
|
104
|
+
return () => {
|
|
105
|
+
if (intervalRef.current) {
|
|
106
|
+
clearInterval(intervalRef.current);
|
|
107
|
+
intervalRef.current = null;
|
|
108
|
+
}
|
|
109
|
+
};
|
|
110
|
+
}, [timer]);
|
|
111
|
+
if (!timer) return null;
|
|
112
|
+
return /* @__PURE__ */ jsxs(
|
|
113
|
+
"a",
|
|
114
|
+
{
|
|
115
|
+
href: "/backend/staff/timesheets",
|
|
116
|
+
className: "flex items-center gap-2 rounded-md px-2 py-1.5 text-xs hover:bg-muted transition-colors cursor-pointer",
|
|
117
|
+
title: t("staff.timesheets.sidebar.timerRunning", "Timer running \u2014 click to view"),
|
|
118
|
+
children: [
|
|
119
|
+
/* @__PURE__ */ jsxs("span", { className: "relative flex h-2.5 w-2.5 shrink-0", children: [
|
|
120
|
+
/* @__PURE__ */ jsx("span", { className: "absolute inline-flex h-full w-full animate-ping rounded-full bg-red-400 opacity-75" }),
|
|
121
|
+
/* @__PURE__ */ jsx("span", { className: "relative inline-flex h-2.5 w-2.5 rounded-full bg-red-500" })
|
|
122
|
+
] }),
|
|
123
|
+
timer.projectName ? /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-1 truncate", children: [
|
|
124
|
+
/* @__PURE__ */ jsx(ProjectColorDot, { colorKey: timer.projectColor, projectName: timer.projectName, size: "xs" }),
|
|
125
|
+
/* @__PURE__ */ jsx("span", { className: "truncate", children: timer.projectName })
|
|
126
|
+
] }) : null,
|
|
127
|
+
/* @__PURE__ */ jsx("span", { className: "ml-auto font-mono tabular-nums shrink-0", children: formatElapsed(elapsed) })
|
|
128
|
+
]
|
|
129
|
+
}
|
|
130
|
+
);
|
|
131
|
+
}
|
|
132
|
+
const widget = {
|
|
133
|
+
metadata: {
|
|
134
|
+
id: "staff.injection.timer-sidebar-indicator",
|
|
135
|
+
title: "Active timer indicator",
|
|
136
|
+
description: "Shows a pulsing indicator in the sidebar when a timesheet timer is running.",
|
|
137
|
+
features: ["staff.timesheets.manage_own"]
|
|
138
|
+
},
|
|
139
|
+
Widget: TimerSidebarIndicator
|
|
140
|
+
};
|
|
141
|
+
var widget_default = widget;
|
|
142
|
+
export {
|
|
143
|
+
widget_default as default
|
|
144
|
+
};
|
|
145
|
+
//# sourceMappingURL=widget.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../../../src/modules/staff/widgets/injection/timer-sidebar-indicator/widget.tsx"],
|
|
4
|
+
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { apiCall } from '@open-mercato/ui/backend/utils/apiCall'\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'\n\ntype TimerState = {\n entryId: string\n projectName: string\n projectColor: string | null\n startedAt: string\n}\n\nconst STORAGE_KEY = 'om:timesheets:activeTimer'\n\nfunction saveToSession(state: TimerState | null) {\n try {\n if (state) sessionStorage.setItem(STORAGE_KEY, JSON.stringify(state))\n else sessionStorage.removeItem(STORAGE_KEY)\n } catch { /* private browsing */ }\n}\n\nfunction loadFromSession(): TimerState | null {\n try {\n const raw = sessionStorage.getItem(STORAGE_KEY)\n if (!raw) return null\n const parsed = JSON.parse(raw)\n if (parsed?.startedAt && parsed?.entryId) return parsed as TimerState\n } catch { /* corrupt or private browsing */ }\n return null\n}\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 getToday(): string {\n const now = new Date()\n return `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}-${String(now.getDate()).padStart(2, '0')}`\n}\n\nfunction TimerSidebarIndicator() {\n const t = useT()\n // Initialise from sessionStorage so the indicator doesn't flash away on navigation\n const [timer, setTimer] = React.useState<TimerState | null>(loadFromSession)\n const [elapsed, setElapsed] = React.useState(0)\n const intervalRef = React.useRef<ReturnType<typeof setInterval> | null>(null)\n\n // Wrap setTimer to also persist to sessionStorage\n const updateTimer = React.useCallback((next: TimerState | null) => {\n setTimer(next)\n saveToSession(next)\n }, [])\n\n const pollTimer = React.useCallback(async () => {\n try {\n const selfRes = await apiCall<{ member?: { id: string } | null }>('/api/staff/team-members/self')\n const memberId = selfRes.result?.member?.id\n if (!memberId) { updateTimer(null); return }\n\n const today = getToday()\n const res = await apiCall<{ items?: Array<Record<string, unknown>> }>(\n `/api/staff/timesheets/time-entries?staffMemberId=${memberId}&from=${today}&to=${today}&pageSize=50`,\n )\n const items = (res.result?.items ?? []) as Array<Record<string, unknown>>\n const active = items.find((e) => e.started_at && !e.ended_at)\n\n if (!active) {\n updateTimer(null)\n return\n }\n\n const projectId = String(active.time_project_id ?? '')\n let projectName = ''\n let projectColor: string | null = null\n if (projectId) {\n const projRes = await apiCall<{ items?: Array<Record<string, unknown>> }>(\n `/api/staff/timesheets/time-projects?ids=${projectId}&pageSize=1`,\n )\n const proj = (projRes.result?.items ?? [])[0]\n if (proj) {\n projectName = String(proj.name ?? '')\n projectColor = typeof proj.color === 'string' ? proj.color : null\n }\n }\n\n updateTimer({\n entryId: String(active.id ?? ''),\n projectName,\n projectColor,\n startedAt: String(active.started_at ?? ''),\n })\n } catch {\n // Silent \u2014 don't crash the sidebar\n }\n }, [updateTimer])\n\n // Poll for active timer on mount + every 30s\n React.useEffect(() => {\n void pollTimer()\n const poll = setInterval(() => { void pollTimer() }, 30000)\n return () => clearInterval(poll)\n }, [pollTimer])\n\n // Tick the elapsed counter locally every second\n React.useEffect(() => {\n if (!timer) {\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])\n\n if (!timer) return null\n\n return (\n <a\n href=\"/backend/staff/timesheets\"\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-red-400 opacity-75\" />\n <span className=\"relative inline-flex h-2.5 w-2.5 rounded-full bg-red-500\" />\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 </a>\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": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
const injectionTable = {
|
|
2
|
+
"backend:sidebar:nav:footer": {
|
|
3
|
+
widgetId: "staff.injection.timer-sidebar-indicator",
|
|
4
|
+
priority: 90
|
|
5
|
+
}
|
|
6
|
+
};
|
|
7
|
+
var injection_table_default = injectionTable;
|
|
8
|
+
export {
|
|
9
|
+
injection_table_default as default,
|
|
10
|
+
injectionTable
|
|
11
|
+
};
|
|
12
|
+
//# sourceMappingURL=injection-table.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../src/modules/staff/widgets/injection-table.ts"],
|
|
4
|
+
"sourcesContent": ["import type { ModuleInjectionTable } from '@open-mercato/shared/modules/widgets/injection'\n\nexport const injectionTable: ModuleInjectionTable = {\n 'backend:sidebar:nav:footer': {\n widgetId: 'staff.injection.timer-sidebar-indicator',\n priority: 90,\n },\n}\n\nexport default injectionTable\n"],
|
|
5
|
+
"mappings": "AAEO,MAAM,iBAAuC;AAAA,EAClD,8BAA8B;AAAA,IAC5B,UAAU;AAAA,IACV,UAAU;AAAA,EACZ;AACF;AAEA,IAAO,0BAAQ;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -108,20 +108,21 @@ async function POST(request) {
|
|
|
108
108
|
void 0,
|
|
109
109
|
scope
|
|
110
110
|
);
|
|
111
|
-
|
|
112
|
-
existingMapping
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
111
|
+
await em.transactional(async () => {
|
|
112
|
+
if (existingMapping) {
|
|
113
|
+
existingMapping.mapping = parsedPayload.data.mapping;
|
|
114
|
+
} else {
|
|
115
|
+
em.persist(em.create(SyncMapping, {
|
|
116
|
+
integrationId: "sync_excel",
|
|
117
|
+
entityType: parsedPayload.data.entityType,
|
|
118
|
+
mapping: parsedPayload.data.mapping,
|
|
119
|
+
organizationId: scope.organizationId,
|
|
120
|
+
tenantId: scope.tenantId
|
|
121
|
+
}));
|
|
122
|
+
}
|
|
123
|
+
await credentialsService.save("sync_excel", {}, scope);
|
|
124
|
+
await integrationStateService.upsert("sync_excel", { isEnabled: true }, scope);
|
|
125
|
+
});
|
|
125
126
|
const { run, progressJob } = await startDataSyncRun({
|
|
126
127
|
syncRunService,
|
|
127
128
|
progressService,
|
|
@@ -148,9 +149,10 @@ async function POST(request) {
|
|
|
148
149
|
}
|
|
149
150
|
}
|
|
150
151
|
});
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
152
|
+
await em.transactional(async () => {
|
|
153
|
+
upload.syncRunId = run.id;
|
|
154
|
+
upload.status = "importing";
|
|
155
|
+
});
|
|
154
156
|
return NextResponse.json(syncExcelImportResponseSchema.parse({
|
|
155
157
|
runId: run.id,
|
|
156
158
|
progressJobId: progressJob?.id ?? null,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../src/modules/sync_excel/api/import/route.ts"],
|
|
4
|
-
"sourcesContent": ["import type { EntityManager } from '@mikro-orm/postgresql'\nimport { NextResponse } from 'next/server'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { readJsonSafe } from '@open-mercato/shared/lib/http/readJsonSafe'\nimport { findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport { z } from 'zod'\nimport type { ProgressService } from '@open-mercato/core/modules/progress/lib/progressService'\nimport type { SyncRunService } from '@open-mercato/core/modules/data_sync/lib/sync-run-service'\nimport { startDataSyncRun } from '@open-mercato/core/modules/data_sync/lib/start-run'\nimport { SyncMapping } from '@open-mercato/core/modules/data_sync/data/entities'\nimport type { CredentialsService } from '@open-mercato/core/modules/integrations/lib/credentials-service'\nimport type { IntegrationStateService } from '@open-mercato/core/modules/integrations/lib/state-service'\nimport { SyncExcelUpload } from '../../data/entities'\nimport { Attachment } from '../../../attachments/data/entities'\nimport { createCursor } from '../../lib/adapters/customers'\nimport {\n syncExcelImportRequestSchema,\n syncExcelImportResponseSchema,\n} from '../../data/validators'\nimport { resolveSyncExcelConcreteScope } from '../../lib/scope'\n\nexport const metadata = {\n POST: { requireAuth: true, requireFeatures: ['sync_excel.run'] },\n}\n\nconst errorSchema = z.object({\n error: z.string(),\n})\n\nexport const openApi = {\n tags: ['SyncExcel'],\n summary: 'Start a CSV import run for a stored sync_excel upload',\n methods: {\n POST: {\n summary: 'Start CSV import',\n responses: [\n { status: 201, description: 'Import run started', schema: syncExcelImportResponseSchema },\n { status: 401, description: 'Unauthorized', schema: errorSchema },\n { status: 404, description: 'Upload not found', schema: errorSchema },\n { status: 409, description: 'Import overlap detected', schema: errorSchema },\n { status: 422, description: 'Invalid import payload', schema: errorSchema },\n ],\n },\n },\n}\n\nexport async function POST(request: Request) {\n try {\n const auth = await getAuthFromRequest(request)\n if (!auth?.tenantId) {\n return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n }\n\n const payload = await readJsonSafe(request)\n const parsedPayload = syncExcelImportRequestSchema.safeParse(payload)\n if (!parsedPayload.success) {\n return NextResponse.json({ error: 'Invalid import payload.' }, { status: 422 })\n }\n\n const container = await createRequestContainer()\n const em = container.resolve('em') as EntityManager\n const syncRunService = container.resolve('dataSyncRunService') as SyncRunService\n const progressService = container.resolve('progressService') as ProgressService\n const credentialsService = container.resolve('integrationCredentialsService') as CredentialsService\n const integrationStateService = container.resolve('integrationStateService') as IntegrationStateService\n const scopeResult = await resolveSyncExcelConcreteScope({ auth, container, request })\n if (!scopeResult.ok) {\n return NextResponse.json({ error: scopeResult.error }, { status: scopeResult.status })\n }\n const { scope } = scopeResult\n\n const upload = await findOneWithDecryption(\n em,\n SyncExcelUpload,\n {\n id: parsedPayload.data.uploadId,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n },\n undefined,\n scope,\n )\n\n if (!upload) {\n return NextResponse.json({ error: 'Upload preview not found.' }, { status: 404 })\n }\n\n const attachment = await findOneWithDecryption(\n em,\n Attachment,\n {\n id: upload.attachmentId,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n },\n undefined,\n scope,\n )\n\n if (!attachment) {\n return NextResponse.json({ error: 'Upload attachment not found.' }, { status: 404 })\n }\n\n if (upload.entityType !== parsedPayload.data.entityType) {\n return NextResponse.json({ error: 'Upload entity type does not match requested import target.' }, { status: 422 })\n }\n\n if (parsedPayload.data.mapping.entityType !== parsedPayload.data.entityType) {\n return NextResponse.json({ error: 'Mapping entity type does not match requested import target.' }, { status: 422 })\n }\n\n const overlap = await syncRunService.findRunningOverlap('sync_excel', parsedPayload.data.entityType, 'import', scope)\n if (overlap) {\n return NextResponse.json({ error: 'A sync_excel import is already in progress for this entity type.' }, { status: 409 })\n }\n\n const existingMapping = await findOneWithDecryption(\n em,\n SyncMapping,\n {\n integrationId: 'sync_excel',\n entityType: parsedPayload.data.entityType,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n },\n undefined,\n scope,\n )\n\n if (existingMapping) {\n
|
|
5
|
-
"mappings": "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
|
|
4
|
+
"sourcesContent": ["import type { EntityManager } from '@mikro-orm/postgresql'\nimport { NextResponse } from 'next/server'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { readJsonSafe } from '@open-mercato/shared/lib/http/readJsonSafe'\nimport { findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport { z } from 'zod'\nimport type { ProgressService } from '@open-mercato/core/modules/progress/lib/progressService'\nimport type { SyncRunService } from '@open-mercato/core/modules/data_sync/lib/sync-run-service'\nimport { startDataSyncRun } from '@open-mercato/core/modules/data_sync/lib/start-run'\nimport { SyncMapping } from '@open-mercato/core/modules/data_sync/data/entities'\nimport type { CredentialsService } from '@open-mercato/core/modules/integrations/lib/credentials-service'\nimport type { IntegrationStateService } from '@open-mercato/core/modules/integrations/lib/state-service'\nimport { SyncExcelUpload } from '../../data/entities'\nimport { Attachment } from '../../../attachments/data/entities'\nimport { createCursor } from '../../lib/adapters/customers'\nimport {\n syncExcelImportRequestSchema,\n syncExcelImportResponseSchema,\n} from '../../data/validators'\nimport { resolveSyncExcelConcreteScope } from '../../lib/scope'\n\nexport const metadata = {\n POST: { requireAuth: true, requireFeatures: ['sync_excel.run'] },\n}\n\nconst errorSchema = z.object({\n error: z.string(),\n})\n\nexport const openApi = {\n tags: ['SyncExcel'],\n summary: 'Start a CSV import run for a stored sync_excel upload',\n methods: {\n POST: {\n summary: 'Start CSV import',\n responses: [\n { status: 201, description: 'Import run started', schema: syncExcelImportResponseSchema },\n { status: 401, description: 'Unauthorized', schema: errorSchema },\n { status: 404, description: 'Upload not found', schema: errorSchema },\n { status: 409, description: 'Import overlap detected', schema: errorSchema },\n { status: 422, description: 'Invalid import payload', schema: errorSchema },\n ],\n },\n },\n}\n\nexport async function POST(request: Request) {\n try {\n const auth = await getAuthFromRequest(request)\n if (!auth?.tenantId) {\n return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n }\n\n const payload = await readJsonSafe(request)\n const parsedPayload = syncExcelImportRequestSchema.safeParse(payload)\n if (!parsedPayload.success) {\n return NextResponse.json({ error: 'Invalid import payload.' }, { status: 422 })\n }\n\n const container = await createRequestContainer()\n const em = container.resolve('em') as EntityManager\n const syncRunService = container.resolve('dataSyncRunService') as SyncRunService\n const progressService = container.resolve('progressService') as ProgressService\n const credentialsService = container.resolve('integrationCredentialsService') as CredentialsService\n const integrationStateService = container.resolve('integrationStateService') as IntegrationStateService\n const scopeResult = await resolveSyncExcelConcreteScope({ auth, container, request })\n if (!scopeResult.ok) {\n return NextResponse.json({ error: scopeResult.error }, { status: scopeResult.status })\n }\n const { scope } = scopeResult\n\n const upload = await findOneWithDecryption(\n em,\n SyncExcelUpload,\n {\n id: parsedPayload.data.uploadId,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n },\n undefined,\n scope,\n )\n\n if (!upload) {\n return NextResponse.json({ error: 'Upload preview not found.' }, { status: 404 })\n }\n\n const attachment = await findOneWithDecryption(\n em,\n Attachment,\n {\n id: upload.attachmentId,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n },\n undefined,\n scope,\n )\n\n if (!attachment) {\n return NextResponse.json({ error: 'Upload attachment not found.' }, { status: 404 })\n }\n\n if (upload.entityType !== parsedPayload.data.entityType) {\n return NextResponse.json({ error: 'Upload entity type does not match requested import target.' }, { status: 422 })\n }\n\n if (parsedPayload.data.mapping.entityType !== parsedPayload.data.entityType) {\n return NextResponse.json({ error: 'Mapping entity type does not match requested import target.' }, { status: 422 })\n }\n\n const overlap = await syncRunService.findRunningOverlap('sync_excel', parsedPayload.data.entityType, 'import', scope)\n if (overlap) {\n return NextResponse.json({ error: 'A sync_excel import is already in progress for this entity type.' }, { status: 409 })\n }\n\n const existingMapping = await findOneWithDecryption(\n em,\n SyncMapping,\n {\n integrationId: 'sync_excel',\n entityType: parsedPayload.data.entityType,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n },\n undefined,\n scope,\n )\n\n // Persist the mapping, credentials, and integration-state config atomically.\n // credentialsService / integrationStateService are request-scoped and share\n // this request `em`, so a single transaction covers all of their writes.\n await em.transactional(async () => {\n if (existingMapping) {\n existingMapping.mapping = parsedPayload.data.mapping\n } else {\n em.persist(em.create(SyncMapping, {\n integrationId: 'sync_excel',\n entityType: parsedPayload.data.entityType,\n mapping: parsedPayload.data.mapping,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n }))\n }\n\n await credentialsService.save('sync_excel', {}, scope)\n await integrationStateService.upsert('sync_excel', { isEnabled: true }, scope)\n })\n\n const { run, progressJob } = await startDataSyncRun({\n syncRunService,\n progressService,\n scope: {\n ...scope,\n userId: auth.sub,\n },\n input: {\n integrationId: 'sync_excel',\n entityType: parsedPayload.data.entityType,\n direction: 'import',\n cursor: createCursor(upload.id, 0),\n triggeredBy: auth.sub,\n batchSize: parsedPayload.data.batchSize ?? 100,\n progressJob: {\n jobType: 'sync_excel:import',\n name: `CSV import \u2014 ${parsedPayload.data.entityType}`,\n description: upload.filename,\n meta: {\n integrationId: 'sync_excel',\n uploadId: upload.id,\n hiddenFromTopBar: false,\n },\n },\n },\n })\n\n await em.transactional(async () => {\n upload.syncRunId = run.id\n upload.status = 'importing'\n })\n\n return NextResponse.json(syncExcelImportResponseSchema.parse({\n runId: run.id,\n progressJobId: progressJob?.id ?? null,\n status: run.status,\n }), { status: 201 })\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n const stack = error instanceof Error ? error.stack : undefined\n console.error('[sync_excel.import] unhandled error', { message, stack })\n return NextResponse.json(\n { error: 'Failed to start sync_excel import.', message, stack },\n { status: 500 },\n )\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|