@open-mercato/core 0.6.4-develop.4210.1.d412061cfe → 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
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
} from "../data/validators.js";
|
|
8
8
|
import { ensureOrganizationScope, ensureTenantScope } from "./shared.js";
|
|
9
9
|
import { CrudHttpError } from "@open-mercato/shared/lib/crud/errors";
|
|
10
|
+
import { withAtomicFlush } from "@open-mercato/shared/lib/commands/flush";
|
|
10
11
|
const createPipelineCommand = {
|
|
11
12
|
id: "customers.pipelines.create",
|
|
12
13
|
async execute(rawInput, ctx) {
|
|
@@ -14,13 +15,6 @@ const createPipelineCommand = {
|
|
|
14
15
|
ensureTenantScope(ctx, parsed.tenantId);
|
|
15
16
|
ensureOrganizationScope(ctx, parsed.organizationId);
|
|
16
17
|
const em = ctx.container.resolve("em").fork();
|
|
17
|
-
if (parsed.isDefault) {
|
|
18
|
-
await em.nativeUpdate(
|
|
19
|
-
CustomerPipeline,
|
|
20
|
-
{ organizationId: parsed.organizationId, tenantId: parsed.tenantId, isDefault: true },
|
|
21
|
-
{ isDefault: false }
|
|
22
|
-
);
|
|
23
|
-
}
|
|
24
18
|
const pipeline = em.create(CustomerPipeline, {
|
|
25
19
|
organizationId: parsed.organizationId,
|
|
26
20
|
tenantId: parsed.tenantId,
|
|
@@ -29,8 +23,18 @@ const createPipelineCommand = {
|
|
|
29
23
|
createdAt: /* @__PURE__ */ new Date(),
|
|
30
24
|
updatedAt: /* @__PURE__ */ new Date()
|
|
31
25
|
});
|
|
32
|
-
em
|
|
33
|
-
|
|
26
|
+
await withAtomicFlush(em, [
|
|
27
|
+
async () => {
|
|
28
|
+
if (parsed.isDefault) {
|
|
29
|
+
await em.nativeUpdate(
|
|
30
|
+
CustomerPipeline,
|
|
31
|
+
{ organizationId: parsed.organizationId, tenantId: parsed.tenantId, isDefault: true },
|
|
32
|
+
{ isDefault: false }
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
em.persist(pipeline);
|
|
36
|
+
}
|
|
37
|
+
], { transaction: true });
|
|
34
38
|
return { pipelineId: pipeline.id };
|
|
35
39
|
}
|
|
36
40
|
};
|
|
@@ -43,17 +47,20 @@ const updatePipelineCommand = {
|
|
|
43
47
|
if (!pipeline) throw new CrudHttpError(404, { error: "Pipeline not found" });
|
|
44
48
|
ensureTenantScope(ctx, pipeline.tenantId);
|
|
45
49
|
ensureOrganizationScope(ctx, pipeline.organizationId);
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
50
|
+
await withAtomicFlush(em, [
|
|
51
|
+
async () => {
|
|
52
|
+
if (parsed.isDefault && !pipeline.isDefault) {
|
|
53
|
+
await em.nativeUpdate(
|
|
54
|
+
CustomerPipeline,
|
|
55
|
+
{ organizationId: pipeline.organizationId, tenantId: pipeline.tenantId, isDefault: true },
|
|
56
|
+
{ isDefault: false }
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
if (parsed.name !== void 0) pipeline.name = parsed.name;
|
|
60
|
+
if (parsed.isDefault !== void 0) pipeline.isDefault = parsed.isDefault;
|
|
61
|
+
pipeline.updatedAt = /* @__PURE__ */ new Date();
|
|
62
|
+
}
|
|
63
|
+
], { transaction: true });
|
|
57
64
|
}
|
|
58
65
|
};
|
|
59
66
|
const deletePipelineCommand = {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/modules/customers/commands/pipelines.ts"],
|
|
4
|
-
"sourcesContent": ["import { registerCommand } from '@open-mercato/shared/lib/commands'\nimport type { CommandHandler } from '@open-mercato/shared/lib/commands'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { CustomerPipeline, CustomerDeal } from '../data/entities'\nimport {\n pipelineCreateSchema,\n pipelineUpdateSchema,\n pipelineDeleteSchema,\n type PipelineCreateInput,\n type PipelineUpdateInput,\n type PipelineDeleteInput,\n} from '../data/validators'\nimport { ensureOrganizationScope, ensureTenantScope } from './shared'\nimport { CrudHttpError } from '@open-mercato/shared/lib/crud/errors'\n\nconst createPipelineCommand: CommandHandler<PipelineCreateInput, { pipelineId: string }> = {\n id: 'customers.pipelines.create',\n async execute(rawInput, ctx) {\n const parsed = pipelineCreateSchema.parse(rawInput)\n ensureTenantScope(ctx, parsed.tenantId)\n ensureOrganizationScope(ctx, parsed.organizationId)\n\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n\n
|
|
5
|
-
"mappings": "AAAA,SAAS,uBAAuB;AAGhC,SAAS,kBAAkB,oBAAoB;AAC/C;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,OAIK;AACP,SAAS,yBAAyB,yBAAyB;AAC3D,SAAS,qBAAqB;
|
|
4
|
+
"sourcesContent": ["import { registerCommand } from '@open-mercato/shared/lib/commands'\nimport type { CommandHandler } from '@open-mercato/shared/lib/commands'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { CustomerPipeline, CustomerDeal } from '../data/entities'\nimport {\n pipelineCreateSchema,\n pipelineUpdateSchema,\n pipelineDeleteSchema,\n type PipelineCreateInput,\n type PipelineUpdateInput,\n type PipelineDeleteInput,\n} from '../data/validators'\nimport { ensureOrganizationScope, ensureTenantScope } from './shared'\nimport { CrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport { withAtomicFlush } from '@open-mercato/shared/lib/commands/flush'\n\nconst createPipelineCommand: CommandHandler<PipelineCreateInput, { pipelineId: string }> = {\n id: 'customers.pipelines.create',\n async execute(rawInput, ctx) {\n const parsed = pipelineCreateSchema.parse(rawInput)\n ensureTenantScope(ctx, parsed.tenantId)\n ensureOrganizationScope(ctx, parsed.organizationId)\n\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n\n const pipeline = em.create(CustomerPipeline, {\n organizationId: parsed.organizationId,\n tenantId: parsed.tenantId,\n name: parsed.name,\n isDefault: parsed.isDefault ?? false,\n createdAt: new Date(),\n updatedAt: new Date(),\n })\n\n await withAtomicFlush(em, [\n async () => {\n if (parsed.isDefault) {\n await em.nativeUpdate(\n CustomerPipeline,\n { organizationId: parsed.organizationId, tenantId: parsed.tenantId, isDefault: true },\n { isDefault: false }\n )\n }\n em.persist(pipeline)\n },\n ], { transaction: true })\n\n return { pipelineId: pipeline.id }\n },\n}\n\nconst updatePipelineCommand: CommandHandler<PipelineUpdateInput, void> = {\n id: 'customers.pipelines.update',\n async execute(rawInput, ctx) {\n const parsed = pipelineUpdateSchema.parse(rawInput)\n\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const pipeline = await em.findOne(CustomerPipeline, { id: parsed.id })\n if (!pipeline) throw new CrudHttpError(404, { error: 'Pipeline not found' })\n\n ensureTenantScope(ctx, pipeline.tenantId)\n ensureOrganizationScope(ctx, pipeline.organizationId)\n\n await withAtomicFlush(em, [\n async () => {\n if (parsed.isDefault && !pipeline.isDefault) {\n await em.nativeUpdate(\n CustomerPipeline,\n { organizationId: pipeline.organizationId, tenantId: pipeline.tenantId, isDefault: true },\n { isDefault: false }\n )\n }\n\n if (parsed.name !== undefined) pipeline.name = parsed.name\n if (parsed.isDefault !== undefined) pipeline.isDefault = parsed.isDefault\n pipeline.updatedAt = new Date()\n },\n ], { transaction: true })\n },\n}\n\nconst deletePipelineCommand: CommandHandler<PipelineDeleteInput, void> = {\n id: 'customers.pipelines.delete',\n async execute(rawInput, ctx) {\n const parsed = pipelineDeleteSchema.parse(rawInput)\n\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const pipeline = await em.findOne(CustomerPipeline, { id: parsed.id })\n if (!pipeline) throw new CrudHttpError(404, { error: 'Pipeline not found' })\n\n ensureTenantScope(ctx, pipeline.tenantId)\n ensureOrganizationScope(ctx, pipeline.organizationId)\n\n const activeDealsCount = await em.count(CustomerDeal, {\n pipelineId: parsed.id,\n deletedAt: null,\n })\n if (activeDealsCount > 0) {\n throw new CrudHttpError(409, { error: 'Cannot delete pipeline with active deals' })\n }\n\n em.remove(pipeline)\n await em.flush()\n },\n}\n\nregisterCommand(createPipelineCommand)\nregisterCommand(updatePipelineCommand)\nregisterCommand(deletePipelineCommand)\n\nexport { createPipelineCommand, updatePipelineCommand, deletePipelineCommand }\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -18,6 +18,7 @@ import { resolveTranslations } from "@open-mercato/shared/lib/i18n/server";
|
|
|
18
18
|
import { CrudHttpError } from "@open-mercato/shared/lib/crud/errors";
|
|
19
19
|
import { findOneWithDecryption } from "@open-mercato/shared/lib/encryption/find";
|
|
20
20
|
import { emitCustomersEvent } from "../events.js";
|
|
21
|
+
import { withAtomicFlush } from "@open-mercato/shared/lib/commands/flush";
|
|
21
22
|
const tagCrudEvents = {
|
|
22
23
|
module: "customers",
|
|
23
24
|
entity: "tag",
|
|
@@ -236,9 +237,12 @@ const deleteTagCommand = {
|
|
|
236
237
|
if (!tag) throw new CrudHttpError(404, { error: "Tag not found" });
|
|
237
238
|
ensureTenantScope(ctx, tag.tenantId);
|
|
238
239
|
ensureOrganizationScope(ctx, tag.organizationId);
|
|
239
|
-
await em
|
|
240
|
-
|
|
241
|
-
|
|
240
|
+
await withAtomicFlush(em, [
|
|
241
|
+
async () => {
|
|
242
|
+
await em.nativeDelete(CustomerTagAssignment, { tag });
|
|
243
|
+
em.remove(tag);
|
|
244
|
+
}
|
|
245
|
+
], { transaction: true });
|
|
242
246
|
const de = ctx.container.resolve("dataEngine");
|
|
243
247
|
await emitCrudSideEffects({
|
|
244
248
|
dataEngine: de,
|
|
@@ -294,7 +298,12 @@ const deleteTagCommand = {
|
|
|
294
298
|
tag.color = before.color;
|
|
295
299
|
tag.description = before.description;
|
|
296
300
|
}
|
|
297
|
-
|
|
301
|
+
const restoredTag = tag;
|
|
302
|
+
await withAtomicFlush(em, [
|
|
303
|
+
() => {
|
|
304
|
+
em.persist(restoredTag);
|
|
305
|
+
}
|
|
306
|
+
], { transaction: true });
|
|
298
307
|
const de = ctx.container.resolve("dataEngine");
|
|
299
308
|
await emitCrudUndoSideEffects({
|
|
300
309
|
dataEngine: de,
|
|
@@ -411,7 +420,6 @@ const unassignTagCommand = {
|
|
|
411
420
|
});
|
|
412
421
|
if (!existing) throw new CrudHttpError(404, { error: "Tag assignment not found" });
|
|
413
422
|
await em.remove(existing).flush();
|
|
414
|
-
await em.flush();
|
|
415
423
|
const de = ctx.container.resolve("dataEngine");
|
|
416
424
|
await emitCrudSideEffects({
|
|
417
425
|
dataEngine: de,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/modules/customers/commands/tags.ts"],
|
|
4
|
-
"sourcesContent": ["import { registerCommand } from '@open-mercato/shared/lib/commands'\nimport type { CommandHandler } from '@open-mercato/shared/lib/commands'\nimport { emitCrudSideEffects, emitCrudUndoSideEffects, buildChanges, requireId } from '@open-mercato/shared/lib/commands/helpers'\nimport type { DataEngine } from '@open-mercato/shared/lib/data/engine'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { CustomerTag, CustomerTagAssignment } from '../data/entities'\nimport {\n tagCreateSchema,\n tagUpdateSchema,\n tagAssignmentSchema,\n type TagCreateInput,\n type TagUpdateInput,\n type TagAssignmentInput,\n} from '../data/validators'\nimport {\n ensureOrganizationScope,\n ensureTenantScope,\n extractUndoPayload,\n requireCustomerEntity,\n ensureSameScope,\n loadEntityTagIds,\n} from './shared'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport { CrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport type { CrudEventsConfig } from '@open-mercato/shared/lib/crud/types'\nimport { findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport { emitCustomersEvent } from '../events'\n\nconst tagCrudEvents: CrudEventsConfig = {\n module: 'customers',\n entity: 'tag',\n persistent: true,\n buildPayload: (ctx) => ({\n id: ctx.identifiers.id,\n organizationId: ctx.identifiers.organizationId,\n tenantId: ctx.identifiers.tenantId,\n }),\n}\n\ntype TagSnapshot = {\n id: string\n organizationId: string\n tenantId: string\n slug: string\n label: string\n color: string | null\n description: string | null\n}\n\ntype TagUndoPayload = {\n before?: TagSnapshot | null\n after?: TagSnapshot | null\n}\n\ntype TagAssignmentSnapshot = {\n tagId: string\n entityId: string\n organizationId: string\n tenantId: string\n}\n\ntype TagAssignmentUndoPayload = {\n before?: TagAssignmentSnapshot | null\n}\n\nasync function loadTagSnapshot(em: EntityManager, id: string): Promise<TagSnapshot | null> {\n const tag = await em.findOne(CustomerTag, { id })\n if (!tag) return null\n return {\n id: tag.id,\n organizationId: tag.organizationId,\n tenantId: tag.tenantId,\n slug: tag.slug,\n label: tag.label,\n color: tag.color ?? null,\n description: tag.description ?? null,\n }\n}\n\nconst createTagCommand: CommandHandler<TagCreateInput, { tagId: string }> = {\n id: 'customers.tags.create',\n async execute(rawInput, ctx) {\n const parsed = tagCreateSchema.parse(rawInput)\n ensureTenantScope(ctx, parsed.tenantId)\n ensureOrganizationScope(ctx, parsed.organizationId)\n\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const duplicate = await em.findOne(CustomerTag, {\n slug: parsed.slug,\n organizationId: parsed.organizationId,\n tenantId: parsed.tenantId,\n })\n if (duplicate) throw new CrudHttpError(409, { error: 'Tag slug already exists' })\n\n const tag = em.create(CustomerTag, {\n organizationId: parsed.organizationId,\n tenantId: parsed.tenantId,\n slug: parsed.slug,\n label: parsed.label,\n color: parsed.color ?? null,\n description: parsed.description ?? null,\n })\n em.persist(tag)\n await em.flush()\n\n const de = (ctx.container.resolve('dataEngine') as DataEngine)\n await emitCrudSideEffects({\n dataEngine: de,\n action: 'created',\n entity: tag,\n identifiers: {\n id: tag.id,\n organizationId: tag.organizationId,\n tenantId: tag.tenantId,\n },\n events: tagCrudEvents,\n })\n\n return { tagId: tag.id }\n },\n captureAfter: async (_input, result, ctx) => {\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n return await loadTagSnapshot(em, result.tagId)\n },\n buildLog: async ({ result, ctx }) => {\n const { translate } = await resolveTranslations()\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const snapshot = await loadTagSnapshot(em, result.tagId)\n return {\n actionLabel: translate('customers.audit.tags.create', 'Create tag'),\n resourceKind: 'customers.tag',\n resourceId: result.tagId,\n tenantId: snapshot?.tenantId ?? null,\n organizationId: snapshot?.organizationId ?? null,\n snapshotAfter: snapshot ?? null,\n payload: {\n undo: {\n after: snapshot ?? null,\n } satisfies TagUndoPayload,\n },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const tagId = logEntry?.resourceId ?? null\n if (!tagId) return\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const tag = await em.findOne(CustomerTag, { id: tagId })\n if (tag) {\n await em.nativeDelete(CustomerTagAssignment, { tag })\n em.remove(tag)\n await em.flush()\n }\n },\n}\n\nconst updateTagCommand: CommandHandler<TagUpdateInput, { tagId: string }> = {\n id: 'customers.tags.update',\n async prepare(rawInput, ctx) {\n const parsed = tagUpdateSchema.parse(rawInput)\n const em = (ctx.container.resolve('em') as EntityManager)\n const snapshot = await loadTagSnapshot(em, parsed.id)\n return snapshot ? { before: snapshot } : {}\n },\n async execute(rawInput, ctx) {\n const parsed = tagUpdateSchema.parse(rawInput)\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const tag = await em.findOne(CustomerTag, { id: parsed.id })\n if (!tag) throw new CrudHttpError(404, { error: 'Tag not found' })\n ensureTenantScope(ctx, tag.tenantId)\n ensureOrganizationScope(ctx, tag.organizationId)\n\n if (parsed.slug !== undefined && parsed.slug !== tag.slug) {\n const duplicate = await em.findOne(CustomerTag, {\n slug: parsed.slug,\n organizationId: tag.organizationId,\n tenantId: tag.tenantId,\n id: { $ne: tag.id },\n })\n if (duplicate) throw new CrudHttpError(409, { error: 'Tag slug already exists' })\n tag.slug = parsed.slug\n }\n if (parsed.label !== undefined) tag.label = parsed.label\n if (parsed.color !== undefined) tag.color = parsed.color ?? null\n if (parsed.description !== undefined) tag.description = parsed.description ?? null\n\n await em.flush()\n\n const de = (ctx.container.resolve('dataEngine') as DataEngine)\n await emitCrudSideEffects({\n dataEngine: de,\n action: 'updated',\n entity: tag,\n identifiers: {\n id: tag.id,\n organizationId: tag.organizationId,\n tenantId: tag.tenantId,\n },\n events: tagCrudEvents,\n })\n\n return { tagId: tag.id }\n },\n buildLog: async ({ snapshots, ctx }) => {\n const { translate } = await resolveTranslations()\n const before = snapshots.before as TagSnapshot | undefined\n if (!before) return null\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const afterSnapshot = await loadTagSnapshot(em, before.id)\n const changes =\n afterSnapshot && before\n ? buildChanges(\n before as unknown as Record<string, unknown>,\n afterSnapshot as unknown as Record<string, unknown>,\n ['slug', 'label', 'color', 'description']\n )\n : {}\n return {\n actionLabel: translate('customers.audit.tags.update', 'Update tag'),\n resourceKind: 'customers.tag',\n resourceId: before.id,\n tenantId: before.tenantId,\n organizationId: before.organizationId,\n snapshotBefore: before,\n snapshotAfter: afterSnapshot ?? null,\n changes,\n payload: {\n undo: {\n before,\n after: afterSnapshot ?? null,\n } satisfies TagUndoPayload,\n },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const payload = extractUndoPayload<TagUndoPayload>(logEntry)\n const before = payload?.before\n if (!before) return\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n let tag = await em.findOne(CustomerTag, { id: before.id })\n if (!tag) {\n tag = em.create(CustomerTag, {\n id: before.id,\n organizationId: before.organizationId,\n tenantId: before.tenantId,\n slug: before.slug,\n label: before.label,\n color: before.color,\n description: before.description,\n })\n em.persist(tag)\n } else {\n tag.slug = before.slug\n tag.label = before.label\n tag.color = before.color\n tag.description = before.description\n }\n await em.flush()\n\n const de = (ctx.container.resolve('dataEngine') as DataEngine)\n await emitCrudUndoSideEffects({\n dataEngine: de,\n action: 'updated',\n entity: tag,\n identifiers: {\n id: tag.id,\n organizationId: tag.organizationId,\n tenantId: tag.tenantId,\n },\n events: tagCrudEvents,\n })\n },\n}\n\nconst deleteTagCommand: CommandHandler<{ body?: Record<string, unknown>; query?: Record<string, unknown> }, { tagId: string }> = {\n id: 'customers.tags.delete',\n async prepare(input, ctx) {\n const id = requireId(input, 'Tag id required')\n const em = (ctx.container.resolve('em') as EntityManager)\n const snapshot = await loadTagSnapshot(em, id)\n return snapshot ? { before: snapshot } : {}\n },\n async execute(input, ctx) {\n const id = requireId(input, 'Tag id required')\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const tag = await em.findOne(CustomerTag, { id })\n if (!tag) throw new CrudHttpError(404, { error: 'Tag not found' })\n ensureTenantScope(ctx, tag.tenantId)\n ensureOrganizationScope(ctx, tag.organizationId)\n await em.nativeDelete(CustomerTagAssignment, { tag })\n em.remove(tag)\n await em.flush()\n\n const de = (ctx.container.resolve('dataEngine') as DataEngine)\n await emitCrudSideEffects({\n dataEngine: de,\n action: 'deleted',\n entity: tag,\n identifiers: {\n id: tag.id,\n organizationId: tag.organizationId,\n tenantId: tag.tenantId,\n },\n events: tagCrudEvents,\n })\n return { tagId: tag.id }\n },\n buildLog: async ({ snapshots }) => {\n const before = snapshots.before as TagSnapshot | undefined\n if (!before) return null\n const { translate } = await resolveTranslations()\n return {\n actionLabel: translate('customers.audit.tags.delete', 'Delete tag'),\n resourceKind: 'customers.tag',\n resourceId: before.id,\n tenantId: before.tenantId,\n organizationId: before.organizationId,\n snapshotBefore: before,\n payload: {\n undo: {\n before,\n } satisfies TagUndoPayload,\n },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const payload = extractUndoPayload<TagUndoPayload>(logEntry)\n const before = payload?.before\n if (!before) return\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n let tag = await em.findOne(CustomerTag, { id: before.id })\n if (!tag) {\n tag = em.create(CustomerTag, {\n id: before.id,\n organizationId: before.organizationId,\n tenantId: before.tenantId,\n slug: before.slug,\n label: before.label,\n color: before.color,\n description: before.description,\n })\n em.persist(tag)\n } else {\n tag.slug = before.slug\n tag.label = before.label\n tag.color = before.color\n tag.description = before.description\n }\n await em.flush()\n\n const de = (ctx.container.resolve('dataEngine') as DataEngine)\n await emitCrudUndoSideEffects({\n dataEngine: de,\n action: 'created',\n entity: tag,\n identifiers: {\n id: tag.id,\n organizationId: tag.organizationId,\n tenantId: tag.tenantId,\n },\n events: tagCrudEvents,\n })\n },\n}\n\nconst assignTagCommand: CommandHandler<TagAssignmentInput, { assignmentId: string }> = {\n id: 'customers.tags.assign',\n async execute(rawInput, ctx) {\n const parsed = tagAssignmentSchema.parse(rawInput)\n ensureTenantScope(ctx, parsed.tenantId)\n ensureOrganizationScope(ctx, parsed.organizationId)\n\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const tag = await em.findOne(CustomerTag, { id: parsed.tagId, tenantId: parsed.tenantId, organizationId: parsed.organizationId })\n if (!tag) throw new CrudHttpError(404, { error: 'Tag not found' })\n const entity = await requireCustomerEntity(em, parsed.entityId, undefined, 'Customer not found')\n ensureSameScope(entity, parsed.organizationId, parsed.tenantId)\n const tagIds = await loadEntityTagIds(em, entity)\n if (tagIds.includes(parsed.tagId)) {\n throw new CrudHttpError(409, { error: 'Tag already assigned' })\n }\n\n const assignment = em.create(CustomerTagAssignment, {\n tag,\n entity,\n organizationId: parsed.organizationId,\n tenantId: parsed.tenantId,\n })\n em.persist(assignment)\n await em.flush()\n\n const de = (ctx.container.resolve('dataEngine') as DataEngine)\n await emitCrudSideEffects({\n dataEngine: de,\n action: 'updated',\n entity: assignment,\n identifiers: {\n id: String(assignment.id),\n organizationId: parsed.organizationId,\n tenantId: parsed.tenantId,\n },\n })\n\n await emitCustomersEvent('customers.tag.assigned', {\n id: String(assignment.id),\n tagId: parsed.tagId,\n entityId: parsed.entityId,\n organizationId: parsed.organizationId,\n tenantId: parsed.tenantId,\n }, { persistent: true })\n\n return { assignmentId: assignment.id }\n },\n buildLog: async ({ result, ctx }) => {\n const { translate } = await resolveTranslations()\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const assignment = await findOneWithDecryption(\n em,\n CustomerTagAssignment,\n { id: result.assignmentId },\n { populate: ['tag', 'entity'] },\n { tenantId: ctx.auth?.tenantId ?? null, organizationId: ctx.selectedOrganizationId ?? ctx.auth?.orgId ?? null },\n )\n if (!assignment) return null\n const tagId = typeof assignment.tag === 'string' ? assignment.tag : assignment.tag.id\n const entityId = typeof assignment.entity === 'string' ? assignment.entity : assignment.entity.id\n return {\n actionLabel: translate('customers.audit.tags.assign', 'Assign tag'),\n resourceKind: 'customers.tagAssignment',\n resourceId: result.assignmentId,\n tenantId: assignment.tenantId,\n organizationId: assignment.organizationId,\n payload: {\n undo: {\n before: {\n tagId,\n entityId,\n tenantId: assignment.tenantId,\n organizationId: assignment.organizationId,\n } satisfies TagAssignmentSnapshot,\n } satisfies TagAssignmentUndoPayload,\n },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const payload = extractUndoPayload<TagAssignmentUndoPayload>(logEntry)\n const before = payload?.before\n if (!before) return\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n await em.nativeDelete(CustomerTagAssignment, {\n tag: before.tagId,\n entity: before.entityId,\n tenantId: before.tenantId,\n organizationId: before.organizationId,\n })\n },\n}\n\nconst unassignTagCommand: CommandHandler<TagAssignmentInput, { assignmentId: string | null }> = {\n id: 'customers.tags.unassign',\n async execute(rawInput, ctx) {\n const parsed = tagAssignmentSchema.parse(rawInput)\n ensureTenantScope(ctx, parsed.tenantId)\n ensureOrganizationScope(ctx, parsed.organizationId)\n\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const existing = await em.findOne(CustomerTagAssignment, {\n tag: parsed.tagId,\n entity: parsed.entityId,\n tenantId: parsed.tenantId,\n organizationId: parsed.organizationId,\n })\n if (!existing) throw new CrudHttpError(404, { error: 'Tag assignment not found' })\n await em.remove(existing).flush()\n await em.flush()\n\n const de = (ctx.container.resolve('dataEngine') as DataEngine)\n await emitCrudSideEffects({\n dataEngine: de,\n action: 'updated',\n entity: existing,\n identifiers: {\n id: String(existing.id),\n organizationId: existing.organizationId,\n tenantId: existing.tenantId,\n },\n })\n\n await emitCustomersEvent('customers.tag.removed', {\n id: String(existing.id),\n tagId: parsed.tagId,\n entityId: parsed.entityId,\n organizationId: parsed.organizationId,\n tenantId: parsed.tenantId,\n }, { persistent: true })\n\n return { assignmentId: existing.id ?? null }\n },\n buildLog: async ({ snapshots, input }) => {\n const { translate } = await resolveTranslations()\n const parsed = tagAssignmentSchema.parse(input)\n return {\n actionLabel: translate('customers.audit.tags.unassign', 'Unassign tag'),\n resourceKind: 'customers.tagAssignment',\n resourceId: parsed.tagId,\n tenantId: parsed.tenantId,\n organizationId: parsed.organizationId,\n payload: {\n undo: {\n before: {\n tagId: parsed.tagId,\n entityId: parsed.entityId,\n tenantId: parsed.tenantId,\n organizationId: parsed.organizationId,\n } satisfies TagAssignmentSnapshot,\n } satisfies TagAssignmentUndoPayload,\n },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const payload = extractUndoPayload<TagAssignmentUndoPayload>(logEntry)\n const before = payload?.before\n if (!before) return\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const tag = await em.findOne(CustomerTag, { id: before.tagId })\n const entity = await requireCustomerEntity(em, before.entityId, undefined, 'Customer not found')\n ensureSameScope(entity, before.organizationId, before.tenantId)\n if (!tag) throw new CrudHttpError(404, { error: 'Tag not found' })\n const existing = await em.findOne(CustomerTagAssignment, {\n tag,\n entity,\n tenantId: before.tenantId,\n organizationId: before.organizationId,\n })\n if (!existing) {\n const assignment = em.create(CustomerTagAssignment, {\n tag,\n entity,\n tenantId: before.tenantId,\n organizationId: before.organizationId,\n })\n em.persist(assignment)\n await em.flush()\n }\n },\n}\n\nregisterCommand(createTagCommand)\nregisterCommand(updateTagCommand)\nregisterCommand(deleteTagCommand)\nregisterCommand(assignTagCommand)\nregisterCommand(unassignTagCommand)\n"],
|
|
5
|
-
"mappings": "AAAA,SAAS,uBAAuB;AAEhC,SAAS,qBAAqB,yBAAyB,cAAc,iBAAiB;AAGtF,SAAS,aAAa,6BAA6B;AACnD;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,OAIK;AACP;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,2BAA2B;AACpC,SAAS,qBAAqB;AAE9B,SAAS,6BAA6B;AACtC,SAAS,0BAA0B;AAEnC,MAAM,gBAAkC;AAAA,EACtC,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,YAAY;AAAA,EACZ,cAAc,CAAC,SAAS;AAAA,IACtB,IAAI,IAAI,YAAY;AAAA,IACpB,gBAAgB,IAAI,YAAY;AAAA,IAChC,UAAU,IAAI,YAAY;AAAA,EAC5B;AACF;AA4BA,eAAe,gBAAgB,IAAmB,IAAyC;AACzF,QAAM,MAAM,MAAM,GAAG,QAAQ,aAAa,EAAE,GAAG,CAAC;AAChD,MAAI,CAAC,IAAK,QAAO;AACjB,SAAO;AAAA,IACL,IAAI,IAAI;AAAA,IACR,gBAAgB,IAAI;AAAA,IACpB,UAAU,IAAI;AAAA,IACd,MAAM,IAAI;AAAA,IACV,OAAO,IAAI;AAAA,IACX,OAAO,IAAI,SAAS;AAAA,IACpB,aAAa,IAAI,eAAe;AAAA,EAClC;AACF;AAEA,MAAM,mBAAsE;AAAA,EAC1E,IAAI;AAAA,EACJ,MAAM,QAAQ,UAAU,KAAK;AAC3B,UAAM,SAAS,gBAAgB,MAAM,QAAQ;AAC7C,sBAAkB,KAAK,OAAO,QAAQ;AACtC,4BAAwB,KAAK,OAAO,cAAc;AAElD,UAAM,KAAM,IAAI,UAAU,QAAQ,IAAI,EAAoB,KAAK;AAC/D,UAAM,YAAY,MAAM,GAAG,QAAQ,aAAa;AAAA,MAC9C,MAAM,OAAO;AAAA,MACb,gBAAgB,OAAO;AAAA,MACvB,UAAU,OAAO;AAAA,IACnB,CAAC;AACD,QAAI,UAAW,OAAM,IAAI,cAAc,KAAK,EAAE,OAAO,0BAA0B,CAAC;AAEhF,UAAM,MAAM,GAAG,OAAO,aAAa;AAAA,MACjC,gBAAgB,OAAO;AAAA,MACvB,UAAU,OAAO;AAAA,MACjB,MAAM,OAAO;AAAA,MACb,OAAO,OAAO;AAAA,MACd,OAAO,OAAO,SAAS;AAAA,MACvB,aAAa,OAAO,eAAe;AAAA,IACrC,CAAC;AACD,OAAG,QAAQ,GAAG;AACd,UAAM,GAAG,MAAM;AAEf,UAAM,KAAM,IAAI,UAAU,QAAQ,YAAY;AAC9C,UAAM,oBAAoB;AAAA,MACxB,YAAY;AAAA,MACZ,QAAQ;AAAA,MACR,QAAQ;AAAA,MACR,aAAa;AAAA,QACX,IAAI,IAAI;AAAA,QACR,gBAAgB,IAAI;AAAA,QACpB,UAAU,IAAI;AAAA,MAChB;AAAA,MACA,QAAQ;AAAA,IACV,CAAC;AAED,WAAO,EAAE,OAAO,IAAI,GAAG;AAAA,EACzB;AAAA,EACA,cAAc,OAAO,QAAQ,QAAQ,QAAQ;AAC3C,UAAM,KAAM,IAAI,UAAU,QAAQ,IAAI,EAAoB,KAAK;AAC/D,WAAO,MAAM,gBAAgB,IAAI,OAAO,KAAK;AAAA,EAC/C;AAAA,EACA,UAAU,OAAO,EAAE,QAAQ,IAAI,MAAM;AACnC,UAAM,EAAE,UAAU,IAAI,MAAM,oBAAoB;AAChD,UAAM,KAAM,IAAI,UAAU,QAAQ,IAAI,EAAoB,KAAK;AAC/D,UAAM,WAAW,MAAM,gBAAgB,IAAI,OAAO,KAAK;AACvD,WAAO;AAAA,MACL,aAAa,UAAU,+BAA+B,YAAY;AAAA,MAClE,cAAc;AAAA,MACd,YAAY,OAAO;AAAA,MACnB,UAAU,UAAU,YAAY;AAAA,MAChC,gBAAgB,UAAU,kBAAkB;AAAA,MAC5C,eAAe,YAAY;AAAA,MAC3B,SAAS;AAAA,QACP,MAAM;AAAA,UACJ,OAAO,YAAY;AAAA,QACrB;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA,EACA,MAAM,OAAO,EAAE,UAAU,IAAI,MAAM;AACjC,UAAM,QAAQ,UAAU,cAAc;AACtC,QAAI,CAAC,MAAO;AACZ,UAAM,KAAM,IAAI,UAAU,QAAQ,IAAI,EAAoB,KAAK;AAC/D,UAAM,MAAM,MAAM,GAAG,QAAQ,aAAa,EAAE,IAAI,MAAM,CAAC;AACvD,QAAI,KAAK;AACP,YAAM,GAAG,aAAa,uBAAuB,EAAE,IAAI,CAAC;AACpD,SAAG,OAAO,GAAG;AACb,YAAM,GAAG,MAAM;AAAA,IACjB;AAAA,EACF;AACF;AAEA,MAAM,mBAAsE;AAAA,EAC1E,IAAI;AAAA,EACJ,MAAM,QAAQ,UAAU,KAAK;AAC3B,UAAM,SAAS,gBAAgB,MAAM,QAAQ;AAC7C,UAAM,KAAM,IAAI,UAAU,QAAQ,IAAI;AACtC,UAAM,WAAW,MAAM,gBAAgB,IAAI,OAAO,EAAE;AACpD,WAAO,WAAW,EAAE,QAAQ,SAAS,IAAI,CAAC;AAAA,EAC5C;AAAA,EACA,MAAM,QAAQ,UAAU,KAAK;AAC3B,UAAM,SAAS,gBAAgB,MAAM,QAAQ;AAC7C,UAAM,KAAM,IAAI,UAAU,QAAQ,IAAI,EAAoB,KAAK;AAC/D,UAAM,MAAM,MAAM,GAAG,QAAQ,aAAa,EAAE,IAAI,OAAO,GAAG,CAAC;AAC3D,QAAI,CAAC,IAAK,OAAM,IAAI,cAAc,KAAK,EAAE,OAAO,gBAAgB,CAAC;AACjE,sBAAkB,KAAK,IAAI,QAAQ;AACnC,4BAAwB,KAAK,IAAI,cAAc;AAE/C,QAAI,OAAO,SAAS,UAAa,OAAO,SAAS,IAAI,MAAM;AACzD,YAAM,YAAY,MAAM,GAAG,QAAQ,aAAa;AAAA,QAC9C,MAAM,OAAO;AAAA,QACb,gBAAgB,IAAI;AAAA,QACpB,UAAU,IAAI;AAAA,QACd,IAAI,EAAE,KAAK,IAAI,GAAG;AAAA,MACpB,CAAC;AACD,UAAI,UAAW,OAAM,IAAI,cAAc,KAAK,EAAE,OAAO,0BAA0B,CAAC;AAChF,UAAI,OAAO,OAAO;AAAA,IACpB;AACA,QAAI,OAAO,UAAU,OAAW,KAAI,QAAQ,OAAO;AACnD,QAAI,OAAO,UAAU,OAAW,KAAI,QAAQ,OAAO,SAAS;AAC5D,QAAI,OAAO,gBAAgB,OAAW,KAAI,cAAc,OAAO,eAAe;AAE9E,UAAM,GAAG,MAAM;AAEf,UAAM,KAAM,IAAI,UAAU,QAAQ,YAAY;AAC9C,UAAM,oBAAoB;AAAA,MACxB,YAAY;AAAA,MACZ,QAAQ;AAAA,MACR,QAAQ;AAAA,MACR,aAAa;AAAA,QACX,IAAI,IAAI;AAAA,QACR,gBAAgB,IAAI;AAAA,QACpB,UAAU,IAAI;AAAA,MAChB;AAAA,MACA,QAAQ;AAAA,IACV,CAAC;AAED,WAAO,EAAE,OAAO,IAAI,GAAG;AAAA,EACzB;AAAA,EACA,UAAU,OAAO,EAAE,WAAW,IAAI,MAAM;AACtC,UAAM,EAAE,UAAU,IAAI,MAAM,oBAAoB;AAChD,UAAM,SAAS,UAAU;AACzB,QAAI,CAAC,OAAQ,QAAO;AACpB,UAAM,KAAM,IAAI,UAAU,QAAQ,IAAI,EAAoB,KAAK;AAC/D,UAAM,gBAAgB,MAAM,gBAAgB,IAAI,OAAO,EAAE;AACzD,UAAM,UACJ,iBAAiB,SACb;AAAA,MACE;AAAA,MACA;AAAA,MACA,CAAC,QAAQ,SAAS,SAAS,aAAa;AAAA,IAC1C,IACA,CAAC;AACP,WAAO;AAAA,MACL,aAAa,UAAU,+BAA+B,YAAY;AAAA,MAClE,cAAc;AAAA,MACd,YAAY,OAAO;AAAA,MACnB,UAAU,OAAO;AAAA,MACjB,gBAAgB,OAAO;AAAA,MACvB,gBAAgB;AAAA,MAChB,eAAe,iBAAiB;AAAA,MAChC;AAAA,MACA,SAAS;AAAA,QACP,MAAM;AAAA,UACJ;AAAA,UACA,OAAO,iBAAiB;AAAA,QAC1B;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA,EACA,MAAM,OAAO,EAAE,UAAU,IAAI,MAAM;AACjC,UAAM,UAAU,mBAAmC,QAAQ;AAC3D,UAAM,SAAS,SAAS;AACxB,QAAI,CAAC,OAAQ;AACb,UAAM,KAAM,IAAI,UAAU,QAAQ,IAAI,EAAoB,KAAK;AAC/D,QAAI,MAAM,MAAM,GAAG,QAAQ,aAAa,EAAE,IAAI,OAAO,GAAG,CAAC;AACzD,QAAI,CAAC,KAAK;AACR,YAAM,GAAG,OAAO,aAAa;AAAA,QAC3B,IAAI,OAAO;AAAA,QACX,gBAAgB,OAAO;AAAA,QACvB,UAAU,OAAO;AAAA,QACjB,MAAM,OAAO;AAAA,QACb,OAAO,OAAO;AAAA,QACd,OAAO,OAAO;AAAA,QACd,aAAa,OAAO;AAAA,MACtB,CAAC;AACD,SAAG,QAAQ,GAAG;AAAA,IAChB,OAAO;AACL,UAAI,OAAO,OAAO;AAClB,UAAI,QAAQ,OAAO;AACnB,UAAI,QAAQ,OAAO;AACnB,UAAI,cAAc,OAAO;AAAA,IAC3B;AACA,UAAM,GAAG,MAAM;AAEf,UAAM,KAAM,IAAI,UAAU,QAAQ,YAAY;AAC9C,UAAM,wBAAwB;AAAA,MAC5B,YAAY;AAAA,MACZ,QAAQ;AAAA,MACR,QAAQ;AAAA,MACR,aAAa;AAAA,QACX,IAAI,IAAI;AAAA,QACR,gBAAgB,IAAI;AAAA,QACpB,UAAU,IAAI;AAAA,MAChB;AAAA,MACA,QAAQ;AAAA,IACV,CAAC;AAAA,EACH;AACF;AAEA,MAAM,mBAA2H;AAAA,EAC/H,IAAI;AAAA,EACJ,MAAM,QAAQ,OAAO,KAAK;AACxB,UAAM,KAAK,UAAU,OAAO,iBAAiB;AAC7C,UAAM,KAAM,IAAI,UAAU,QAAQ,IAAI;AACtC,UAAM,WAAW,MAAM,gBAAgB,IAAI,EAAE;AAC7C,WAAO,WAAW,EAAE,QAAQ,SAAS,IAAI,CAAC;AAAA,EAC5C;AAAA,EACA,MAAM,QAAQ,OAAO,KAAK;AACxB,UAAM,KAAK,UAAU,OAAO,iBAAiB;AAC7C,UAAM,KAAM,IAAI,UAAU,QAAQ,IAAI,EAAoB,KAAK;AAC/D,UAAM,MAAM,MAAM,GAAG,QAAQ,aAAa,EAAE,GAAG,CAAC;AAChD,QAAI,CAAC,IAAK,OAAM,IAAI,cAAc,KAAK,EAAE,OAAO,gBAAgB,CAAC;AACjE,sBAAkB,KAAK,IAAI,QAAQ;AACnC,4BAAwB,KAAK,IAAI,cAAc;AAC/C,UAAM,GAAG,aAAa,uBAAuB,EAAE,IAAI,CAAC;AACpD,OAAG,OAAO,GAAG;AACb,UAAM,GAAG,MAAM;AAEf,UAAM,KAAM,IAAI,UAAU,QAAQ,YAAY;AAC9C,UAAM,oBAAoB;AAAA,MACxB,YAAY;AAAA,MACZ,QAAQ;AAAA,MACR,QAAQ;AAAA,MACR,aAAa;AAAA,QACX,IAAI,IAAI;AAAA,QACR,gBAAgB,IAAI;AAAA,QACpB,UAAU,IAAI;AAAA,MAChB;AAAA,MACA,QAAQ;AAAA,IACV,CAAC;AACD,WAAO,EAAE,OAAO,IAAI,GAAG;AAAA,EACzB;AAAA,EACA,UAAU,OAAO,EAAE,UAAU,MAAM;AACjC,UAAM,SAAS,UAAU;AACzB,QAAI,CAAC,OAAQ,QAAO;AACpB,UAAM,EAAE,UAAU,IAAI,MAAM,oBAAoB;AAChD,WAAO;AAAA,MACL,aAAa,UAAU,+BAA+B,YAAY;AAAA,MAClE,cAAc;AAAA,MACd,YAAY,OAAO;AAAA,MACnB,UAAU,OAAO;AAAA,MACjB,gBAAgB,OAAO;AAAA,MACvB,gBAAgB;AAAA,MAChB,SAAS;AAAA,QACP,MAAM;AAAA,UACJ;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA,EACA,MAAM,OAAO,EAAE,UAAU,IAAI,MAAM;AACjC,UAAM,UAAU,mBAAmC,QAAQ;AAC3D,UAAM,SAAS,SAAS;AACxB,QAAI,CAAC,OAAQ;AACb,UAAM,KAAM,IAAI,UAAU,QAAQ,IAAI,EAAoB,KAAK;AAC/D,QAAI,MAAM,MAAM,GAAG,QAAQ,aAAa,EAAE,IAAI,OAAO,GAAG,CAAC;AACzD,QAAI,CAAC,KAAK;AACR,YAAM,GAAG,OAAO,aAAa;AAAA,QAC3B,IAAI,OAAO;AAAA,QACX,gBAAgB,OAAO;AAAA,QACvB,UAAU,OAAO;AAAA,QACjB,MAAM,OAAO;AAAA,QACb,OAAO,OAAO;AAAA,QACd,OAAO,OAAO;AAAA,QACd,aAAa,OAAO;AAAA,MACtB,CAAC;AACD,SAAG,QAAQ,GAAG;AAAA,IAChB,OAAO;AACL,UAAI,OAAO,OAAO;AAClB,UAAI,QAAQ,OAAO;AACnB,UAAI,QAAQ,OAAO;AACnB,UAAI,cAAc,OAAO;AAAA,IAC3B;AACA,UAAM,GAAG,MAAM;AAEf,UAAM,KAAM,IAAI,UAAU,QAAQ,YAAY;AAC9C,UAAM,wBAAwB;AAAA,MAC5B,YAAY;AAAA,MACZ,QAAQ;AAAA,MACR,QAAQ;AAAA,MACR,aAAa;AAAA,QACX,IAAI,IAAI;AAAA,QACR,gBAAgB,IAAI;AAAA,QACpB,UAAU,IAAI;AAAA,MAChB;AAAA,MACA,QAAQ;AAAA,IACV,CAAC;AAAA,EACH;AACF;AAEA,MAAM,mBAAiF;AAAA,EACrF,IAAI;AAAA,EACJ,MAAM,QAAQ,UAAU,KAAK;AAC3B,UAAM,SAAS,oBAAoB,MAAM,QAAQ;AACjD,sBAAkB,KAAK,OAAO,QAAQ;AACtC,4BAAwB,KAAK,OAAO,cAAc;AAElD,UAAM,KAAM,IAAI,UAAU,QAAQ,IAAI,EAAoB,KAAK;AACjE,UAAM,MAAM,MAAM,GAAG,QAAQ,aAAa,EAAE,IAAI,OAAO,OAAO,UAAU,OAAO,UAAU,gBAAgB,OAAO,eAAe,CAAC;AAChI,QAAI,CAAC,IAAK,OAAM,IAAI,cAAc,KAAK,EAAE,OAAO,gBAAgB,CAAC;AACjE,UAAM,SAAS,MAAM,sBAAsB,IAAI,OAAO,UAAU,QAAW,oBAAoB;AAC/F,oBAAgB,QAAQ,OAAO,gBAAgB,OAAO,QAAQ;AAC9D,UAAM,SAAS,MAAM,iBAAiB,IAAI,MAAM;AAC9C,QAAI,OAAO,SAAS,OAAO,KAAK,GAAG;AACjC,YAAM,IAAI,cAAc,KAAK,EAAE,OAAO,uBAAuB,CAAC;AAAA,IAChE;AAEA,UAAM,aAAa,GAAG,OAAO,uBAAuB;AAAA,MAClD;AAAA,MACA;AAAA,MACA,gBAAgB,OAAO;AAAA,MACvB,UAAU,OAAO;AAAA,IACnB,CAAC;AACD,OAAG,QAAQ,UAAU;AACrB,UAAM,GAAG,MAAM;AAEf,UAAM,KAAM,IAAI,UAAU,QAAQ,YAAY;AAC9C,UAAM,oBAAoB;AAAA,MACxB,YAAY;AAAA,MACZ,QAAQ;AAAA,MACR,QAAQ;AAAA,MACR,aAAa;AAAA,QACX,IAAI,OAAO,WAAW,EAAE;AAAA,QACxB,gBAAgB,OAAO;AAAA,QACvB,UAAU,OAAO;AAAA,MACnB;AAAA,IACF,CAAC;AAED,UAAM,mBAAmB,0BAA0B;AAAA,MACjD,IAAI,OAAO,WAAW,EAAE;AAAA,MACxB,OAAO,OAAO;AAAA,MACd,UAAU,OAAO;AAAA,MACjB,gBAAgB,OAAO;AAAA,MACvB,UAAU,OAAO;AAAA,IACnB,GAAG,EAAE,YAAY,KAAK,CAAC;AAEvB,WAAO,EAAE,cAAc,WAAW,GAAG;AAAA,EACvC;AAAA,EACA,UAAU,OAAO,EAAE,QAAQ,IAAI,MAAM;AACnC,UAAM,EAAE,UAAU,IAAI,MAAM,oBAAoB;AAChD,UAAM,KAAM,IAAI,UAAU,QAAQ,IAAI,EAAoB,KAAK;AAC/D,UAAM,aAAa,MAAM;AAAA,MACvB;AAAA,MACA;AAAA,MACA,EAAE,IAAI,OAAO,aAAa;AAAA,MAC1B,EAAE,UAAU,CAAC,OAAO,QAAQ,EAAE;AAAA,MAC9B,EAAE,UAAU,IAAI,MAAM,YAAY,MAAM,gBAAgB,IAAI,0BAA0B,IAAI,MAAM,SAAS,KAAK;AAAA,IAChH;AACA,QAAI,CAAC,WAAY,QAAO;AACxB,UAAM,QAAQ,OAAO,WAAW,QAAQ,WAAW,WAAW,MAAM,WAAW,IAAI;AACnF,UAAM,WAAW,OAAO,WAAW,WAAW,WAAW,WAAW,SAAS,WAAW,OAAO;AAC/F,WAAO;AAAA,MACL,aAAa,UAAU,+BAA+B,YAAY;AAAA,MAClE,cAAc;AAAA,MACd,YAAY,OAAO;AAAA,MACnB,UAAU,WAAW;AAAA,MACrB,gBAAgB,WAAW;AAAA,MAC3B,SAAS;AAAA,QACP,MAAM;AAAA,UACJ,QAAQ;AAAA,YACN;AAAA,YACA;AAAA,YACA,UAAU,WAAW;AAAA,YACrB,gBAAgB,WAAW;AAAA,UAC7B;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA,EACA,MAAM,OAAO,EAAE,UAAU,IAAI,MAAM;AACjC,UAAM,UAAU,mBAA6C,QAAQ;AACrE,UAAM,SAAS,SAAS;AACxB,QAAI,CAAC,OAAQ;AACb,UAAM,KAAM,IAAI,UAAU,QAAQ,IAAI,EAAoB,KAAK;AAC/D,UAAM,GAAG,aAAa,uBAAuB;AAAA,MAC3C,KAAK,OAAO;AAAA,MACZ,QAAQ,OAAO;AAAA,MACf,UAAU,OAAO;AAAA,MACjB,gBAAgB,OAAO;AAAA,IACzB,CAAC;AAAA,EACH;AACF;AAEA,MAAM,qBAA0F;AAAA,EAC9F,IAAI;AAAA,EACJ,MAAM,QAAQ,UAAU,KAAK;AAC3B,UAAM,SAAS,oBAAoB,MAAM,QAAQ;AACjD,sBAAkB,KAAK,OAAO,QAAQ;AACtC,4BAAwB,KAAK,OAAO,cAAc;AAElD,UAAM,KAAM,IAAI,UAAU,QAAQ,IAAI,EAAoB,KAAK;AAC/D,UAAM,WAAW,MAAM,GAAG,QAAQ,uBAAuB;AAAA,MACvD,KAAK,OAAO;AAAA,MACZ,QAAQ,OAAO;AAAA,MACf,UAAU,OAAO;AAAA,MACjB,gBAAgB,OAAO;AAAA,IACzB,CAAC;AACD,QAAI,CAAC,SAAU,OAAM,IAAI,cAAc,KAAK,EAAE,OAAO,2BAA2B,CAAC;AACjF,UAAM,GAAG,OAAO,QAAQ,EAAE,MAAM;AAChC,UAAM,GAAG,MAAM;AAEf,UAAM,KAAM,IAAI,UAAU,QAAQ,YAAY;AAC9C,UAAM,oBAAoB;AAAA,MACxB,YAAY;AAAA,MACZ,QAAQ;AAAA,MACR,QAAQ;AAAA,MACR,aAAa;AAAA,QACX,IAAI,OAAO,SAAS,EAAE;AAAA,QACtB,gBAAgB,SAAS;AAAA,QACzB,UAAU,SAAS;AAAA,MACrB;AAAA,IACF,CAAC;AAED,UAAM,mBAAmB,yBAAyB;AAAA,MAChD,IAAI,OAAO,SAAS,EAAE;AAAA,MACtB,OAAO,OAAO;AAAA,MACd,UAAU,OAAO;AAAA,MACjB,gBAAgB,OAAO;AAAA,MACvB,UAAU,OAAO;AAAA,IACnB,GAAG,EAAE,YAAY,KAAK,CAAC;AAEvB,WAAO,EAAE,cAAc,SAAS,MAAM,KAAK;AAAA,EAC7C;AAAA,EACA,UAAU,OAAO,EAAE,WAAW,MAAM,MAAM;AACxC,UAAM,EAAE,UAAU,IAAI,MAAM,oBAAoB;AAChD,UAAM,SAAS,oBAAoB,MAAM,KAAK;AAC9C,WAAO;AAAA,MACL,aAAa,UAAU,iCAAiC,cAAc;AAAA,MACtE,cAAc;AAAA,MACd,YAAY,OAAO;AAAA,MACnB,UAAU,OAAO;AAAA,MACjB,gBAAgB,OAAO;AAAA,MACvB,SAAS;AAAA,QACP,MAAM;AAAA,UACJ,QAAQ;AAAA,YACN,OAAO,OAAO;AAAA,YACd,UAAU,OAAO;AAAA,YACjB,UAAU,OAAO;AAAA,YACjB,gBAAgB,OAAO;AAAA,UACzB;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA,EACA,MAAM,OAAO,EAAE,UAAU,IAAI,MAAM;AACjC,UAAM,UAAU,mBAA6C,QAAQ;AACrE,UAAM,SAAS,SAAS;AACxB,QAAI,CAAC,OAAQ;AACb,UAAM,KAAM,IAAI,UAAU,QAAQ,IAAI,EAAoB,KAAK;AAC/D,UAAM,MAAM,MAAM,GAAG,QAAQ,aAAa,EAAE,IAAI,OAAO,MAAM,CAAC;AAC9D,UAAM,SAAS,MAAM,sBAAsB,IAAI,OAAO,UAAU,QAAW,oBAAoB;AAC/F,oBAAgB,QAAQ,OAAO,gBAAgB,OAAO,QAAQ;AAC9D,QAAI,CAAC,IAAK,OAAM,IAAI,cAAc,KAAK,EAAE,OAAO,gBAAgB,CAAC;AACjE,UAAM,WAAW,MAAM,GAAG,QAAQ,uBAAuB;AAAA,MACvD;AAAA,MACA;AAAA,MACA,UAAU,OAAO;AAAA,MACjB,gBAAgB,OAAO;AAAA,IACzB,CAAC;AACD,QAAI,CAAC,UAAU;AACb,YAAM,aAAa,GAAG,OAAO,uBAAuB;AAAA,QAClD;AAAA,QACA;AAAA,QACA,UAAU,OAAO;AAAA,QACjB,gBAAgB,OAAO;AAAA,MACzB,CAAC;AACD,SAAG,QAAQ,UAAU;AACrB,YAAM,GAAG,MAAM;AAAA,IACjB;AAAA,EACF;AACF;AAEA,gBAAgB,gBAAgB;AAChC,gBAAgB,gBAAgB;AAChC,gBAAgB,gBAAgB;AAChC,gBAAgB,gBAAgB;AAChC,gBAAgB,kBAAkB;",
|
|
4
|
+
"sourcesContent": ["import { registerCommand } from '@open-mercato/shared/lib/commands'\nimport type { CommandHandler } from '@open-mercato/shared/lib/commands'\nimport { emitCrudSideEffects, emitCrudUndoSideEffects, buildChanges, requireId } from '@open-mercato/shared/lib/commands/helpers'\nimport type { DataEngine } from '@open-mercato/shared/lib/data/engine'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { CustomerTag, CustomerTagAssignment } from '../data/entities'\nimport {\n tagCreateSchema,\n tagUpdateSchema,\n tagAssignmentSchema,\n type TagCreateInput,\n type TagUpdateInput,\n type TagAssignmentInput,\n} from '../data/validators'\nimport {\n ensureOrganizationScope,\n ensureTenantScope,\n extractUndoPayload,\n requireCustomerEntity,\n ensureSameScope,\n loadEntityTagIds,\n} from './shared'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport { CrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport type { CrudEventsConfig } from '@open-mercato/shared/lib/crud/types'\nimport { findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport { emitCustomersEvent } from '../events'\nimport { withAtomicFlush } from '@open-mercato/shared/lib/commands/flush'\n\nconst tagCrudEvents: CrudEventsConfig = {\n module: 'customers',\n entity: 'tag',\n persistent: true,\n buildPayload: (ctx) => ({\n id: ctx.identifiers.id,\n organizationId: ctx.identifiers.organizationId,\n tenantId: ctx.identifiers.tenantId,\n }),\n}\n\ntype TagSnapshot = {\n id: string\n organizationId: string\n tenantId: string\n slug: string\n label: string\n color: string | null\n description: string | null\n}\n\ntype TagUndoPayload = {\n before?: TagSnapshot | null\n after?: TagSnapshot | null\n}\n\ntype TagAssignmentSnapshot = {\n tagId: string\n entityId: string\n organizationId: string\n tenantId: string\n}\n\ntype TagAssignmentUndoPayload = {\n before?: TagAssignmentSnapshot | null\n}\n\nasync function loadTagSnapshot(em: EntityManager, id: string): Promise<TagSnapshot | null> {\n const tag = await em.findOne(CustomerTag, { id })\n if (!tag) return null\n return {\n id: tag.id,\n organizationId: tag.organizationId,\n tenantId: tag.tenantId,\n slug: tag.slug,\n label: tag.label,\n color: tag.color ?? null,\n description: tag.description ?? null,\n }\n}\n\nconst createTagCommand: CommandHandler<TagCreateInput, { tagId: string }> = {\n id: 'customers.tags.create',\n async execute(rawInput, ctx) {\n const parsed = tagCreateSchema.parse(rawInput)\n ensureTenantScope(ctx, parsed.tenantId)\n ensureOrganizationScope(ctx, parsed.organizationId)\n\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const duplicate = await em.findOne(CustomerTag, {\n slug: parsed.slug,\n organizationId: parsed.organizationId,\n tenantId: parsed.tenantId,\n })\n if (duplicate) throw new CrudHttpError(409, { error: 'Tag slug already exists' })\n\n const tag = em.create(CustomerTag, {\n organizationId: parsed.organizationId,\n tenantId: parsed.tenantId,\n slug: parsed.slug,\n label: parsed.label,\n color: parsed.color ?? null,\n description: parsed.description ?? null,\n })\n em.persist(tag)\n await em.flush()\n\n const de = (ctx.container.resolve('dataEngine') as DataEngine)\n await emitCrudSideEffects({\n dataEngine: de,\n action: 'created',\n entity: tag,\n identifiers: {\n id: tag.id,\n organizationId: tag.organizationId,\n tenantId: tag.tenantId,\n },\n events: tagCrudEvents,\n })\n\n return { tagId: tag.id }\n },\n captureAfter: async (_input, result, ctx) => {\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n return await loadTagSnapshot(em, result.tagId)\n },\n buildLog: async ({ result, ctx }) => {\n const { translate } = await resolveTranslations()\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const snapshot = await loadTagSnapshot(em, result.tagId)\n return {\n actionLabel: translate('customers.audit.tags.create', 'Create tag'),\n resourceKind: 'customers.tag',\n resourceId: result.tagId,\n tenantId: snapshot?.tenantId ?? null,\n organizationId: snapshot?.organizationId ?? null,\n snapshotAfter: snapshot ?? null,\n payload: {\n undo: {\n after: snapshot ?? null,\n } satisfies TagUndoPayload,\n },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const tagId = logEntry?.resourceId ?? null\n if (!tagId) return\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const tag = await em.findOne(CustomerTag, { id: tagId })\n if (tag) {\n await em.nativeDelete(CustomerTagAssignment, { tag })\n em.remove(tag)\n await em.flush()\n }\n },\n}\n\nconst updateTagCommand: CommandHandler<TagUpdateInput, { tagId: string }> = {\n id: 'customers.tags.update',\n async prepare(rawInput, ctx) {\n const parsed = tagUpdateSchema.parse(rawInput)\n const em = (ctx.container.resolve('em') as EntityManager)\n const snapshot = await loadTagSnapshot(em, parsed.id)\n return snapshot ? { before: snapshot } : {}\n },\n async execute(rawInput, ctx) {\n const parsed = tagUpdateSchema.parse(rawInput)\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const tag = await em.findOne(CustomerTag, { id: parsed.id })\n if (!tag) throw new CrudHttpError(404, { error: 'Tag not found' })\n ensureTenantScope(ctx, tag.tenantId)\n ensureOrganizationScope(ctx, tag.organizationId)\n\n if (parsed.slug !== undefined && parsed.slug !== tag.slug) {\n const duplicate = await em.findOne(CustomerTag, {\n slug: parsed.slug,\n organizationId: tag.organizationId,\n tenantId: tag.tenantId,\n id: { $ne: tag.id },\n })\n if (duplicate) throw new CrudHttpError(409, { error: 'Tag slug already exists' })\n tag.slug = parsed.slug\n }\n if (parsed.label !== undefined) tag.label = parsed.label\n if (parsed.color !== undefined) tag.color = parsed.color ?? null\n if (parsed.description !== undefined) tag.description = parsed.description ?? null\n\n await em.flush()\n\n const de = (ctx.container.resolve('dataEngine') as DataEngine)\n await emitCrudSideEffects({\n dataEngine: de,\n action: 'updated',\n entity: tag,\n identifiers: {\n id: tag.id,\n organizationId: tag.organizationId,\n tenantId: tag.tenantId,\n },\n events: tagCrudEvents,\n })\n\n return { tagId: tag.id }\n },\n buildLog: async ({ snapshots, ctx }) => {\n const { translate } = await resolveTranslations()\n const before = snapshots.before as TagSnapshot | undefined\n if (!before) return null\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const afterSnapshot = await loadTagSnapshot(em, before.id)\n const changes =\n afterSnapshot && before\n ? buildChanges(\n before as unknown as Record<string, unknown>,\n afterSnapshot as unknown as Record<string, unknown>,\n ['slug', 'label', 'color', 'description']\n )\n : {}\n return {\n actionLabel: translate('customers.audit.tags.update', 'Update tag'),\n resourceKind: 'customers.tag',\n resourceId: before.id,\n tenantId: before.tenantId,\n organizationId: before.organizationId,\n snapshotBefore: before,\n snapshotAfter: afterSnapshot ?? null,\n changes,\n payload: {\n undo: {\n before,\n after: afterSnapshot ?? null,\n } satisfies TagUndoPayload,\n },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const payload = extractUndoPayload<TagUndoPayload>(logEntry)\n const before = payload?.before\n if (!before) return\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n let tag = await em.findOne(CustomerTag, { id: before.id })\n if (!tag) {\n tag = em.create(CustomerTag, {\n id: before.id,\n organizationId: before.organizationId,\n tenantId: before.tenantId,\n slug: before.slug,\n label: before.label,\n color: before.color,\n description: before.description,\n })\n em.persist(tag)\n } else {\n tag.slug = before.slug\n tag.label = before.label\n tag.color = before.color\n tag.description = before.description\n }\n await em.flush()\n\n const de = (ctx.container.resolve('dataEngine') as DataEngine)\n await emitCrudUndoSideEffects({\n dataEngine: de,\n action: 'updated',\n entity: tag,\n identifiers: {\n id: tag.id,\n organizationId: tag.organizationId,\n tenantId: tag.tenantId,\n },\n events: tagCrudEvents,\n })\n },\n}\n\nconst deleteTagCommand: CommandHandler<{ body?: Record<string, unknown>; query?: Record<string, unknown> }, { tagId: string }> = {\n id: 'customers.tags.delete',\n async prepare(input, ctx) {\n const id = requireId(input, 'Tag id required')\n const em = (ctx.container.resolve('em') as EntityManager)\n const snapshot = await loadTagSnapshot(em, id)\n return snapshot ? { before: snapshot } : {}\n },\n async execute(input, ctx) {\n const id = requireId(input, 'Tag id required')\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const tag = await em.findOne(CustomerTag, { id })\n if (!tag) throw new CrudHttpError(404, { error: 'Tag not found' })\n ensureTenantScope(ctx, tag.tenantId)\n ensureOrganizationScope(ctx, tag.organizationId)\n await withAtomicFlush(em, [\n async () => {\n await em.nativeDelete(CustomerTagAssignment, { tag })\n em.remove(tag)\n },\n ], { transaction: true })\n\n const de = (ctx.container.resolve('dataEngine') as DataEngine)\n await emitCrudSideEffects({\n dataEngine: de,\n action: 'deleted',\n entity: tag,\n identifiers: {\n id: tag.id,\n organizationId: tag.organizationId,\n tenantId: tag.tenantId,\n },\n events: tagCrudEvents,\n })\n return { tagId: tag.id }\n },\n buildLog: async ({ snapshots }) => {\n const before = snapshots.before as TagSnapshot | undefined\n if (!before) return null\n const { translate } = await resolveTranslations()\n return {\n actionLabel: translate('customers.audit.tags.delete', 'Delete tag'),\n resourceKind: 'customers.tag',\n resourceId: before.id,\n tenantId: before.tenantId,\n organizationId: before.organizationId,\n snapshotBefore: before,\n payload: {\n undo: {\n before,\n } satisfies TagUndoPayload,\n },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const payload = extractUndoPayload<TagUndoPayload>(logEntry)\n const before = payload?.before\n if (!before) return\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n let tag = await em.findOne(CustomerTag, { id: before.id })\n if (!tag) {\n tag = em.create(CustomerTag, {\n id: before.id,\n organizationId: before.organizationId,\n tenantId: before.tenantId,\n slug: before.slug,\n label: before.label,\n color: before.color,\n description: before.description,\n })\n em.persist(tag)\n } else {\n tag.slug = before.slug\n tag.label = before.label\n tag.color = before.color\n tag.description = before.description\n }\n const restoredTag = tag\n await withAtomicFlush(em, [\n () => {\n em.persist(restoredTag)\n },\n ], { transaction: true })\n\n const de = (ctx.container.resolve('dataEngine') as DataEngine)\n await emitCrudUndoSideEffects({\n dataEngine: de,\n action: 'created',\n entity: tag,\n identifiers: {\n id: tag.id,\n organizationId: tag.organizationId,\n tenantId: tag.tenantId,\n },\n events: tagCrudEvents,\n })\n },\n}\n\nconst assignTagCommand: CommandHandler<TagAssignmentInput, { assignmentId: string }> = {\n id: 'customers.tags.assign',\n async execute(rawInput, ctx) {\n const parsed = tagAssignmentSchema.parse(rawInput)\n ensureTenantScope(ctx, parsed.tenantId)\n ensureOrganizationScope(ctx, parsed.organizationId)\n\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const tag = await em.findOne(CustomerTag, { id: parsed.tagId, tenantId: parsed.tenantId, organizationId: parsed.organizationId })\n if (!tag) throw new CrudHttpError(404, { error: 'Tag not found' })\n const entity = await requireCustomerEntity(em, parsed.entityId, undefined, 'Customer not found')\n ensureSameScope(entity, parsed.organizationId, parsed.tenantId)\n const tagIds = await loadEntityTagIds(em, entity)\n if (tagIds.includes(parsed.tagId)) {\n throw new CrudHttpError(409, { error: 'Tag already assigned' })\n }\n\n const assignment = em.create(CustomerTagAssignment, {\n tag,\n entity,\n organizationId: parsed.organizationId,\n tenantId: parsed.tenantId,\n })\n em.persist(assignment)\n await em.flush()\n\n const de = (ctx.container.resolve('dataEngine') as DataEngine)\n await emitCrudSideEffects({\n dataEngine: de,\n action: 'updated',\n entity: assignment,\n identifiers: {\n id: String(assignment.id),\n organizationId: parsed.organizationId,\n tenantId: parsed.tenantId,\n },\n })\n\n await emitCustomersEvent('customers.tag.assigned', {\n id: String(assignment.id),\n tagId: parsed.tagId,\n entityId: parsed.entityId,\n organizationId: parsed.organizationId,\n tenantId: parsed.tenantId,\n }, { persistent: true })\n\n return { assignmentId: assignment.id }\n },\n buildLog: async ({ result, ctx }) => {\n const { translate } = await resolveTranslations()\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const assignment = await findOneWithDecryption(\n em,\n CustomerTagAssignment,\n { id: result.assignmentId },\n { populate: ['tag', 'entity'] },\n { tenantId: ctx.auth?.tenantId ?? null, organizationId: ctx.selectedOrganizationId ?? ctx.auth?.orgId ?? null },\n )\n if (!assignment) return null\n const tagId = typeof assignment.tag === 'string' ? assignment.tag : assignment.tag.id\n const entityId = typeof assignment.entity === 'string' ? assignment.entity : assignment.entity.id\n return {\n actionLabel: translate('customers.audit.tags.assign', 'Assign tag'),\n resourceKind: 'customers.tagAssignment',\n resourceId: result.assignmentId,\n tenantId: assignment.tenantId,\n organizationId: assignment.organizationId,\n payload: {\n undo: {\n before: {\n tagId,\n entityId,\n tenantId: assignment.tenantId,\n organizationId: assignment.organizationId,\n } satisfies TagAssignmentSnapshot,\n } satisfies TagAssignmentUndoPayload,\n },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const payload = extractUndoPayload<TagAssignmentUndoPayload>(logEntry)\n const before = payload?.before\n if (!before) return\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n await em.nativeDelete(CustomerTagAssignment, {\n tag: before.tagId,\n entity: before.entityId,\n tenantId: before.tenantId,\n organizationId: before.organizationId,\n })\n },\n}\n\nconst unassignTagCommand: CommandHandler<TagAssignmentInput, { assignmentId: string | null }> = {\n id: 'customers.tags.unassign',\n async execute(rawInput, ctx) {\n const parsed = tagAssignmentSchema.parse(rawInput)\n ensureTenantScope(ctx, parsed.tenantId)\n ensureOrganizationScope(ctx, parsed.organizationId)\n\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const existing = await em.findOne(CustomerTagAssignment, {\n tag: parsed.tagId,\n entity: parsed.entityId,\n tenantId: parsed.tenantId,\n organizationId: parsed.organizationId,\n })\n if (!existing) throw new CrudHttpError(404, { error: 'Tag assignment not found' })\n await em.remove(existing).flush()\n\n const de = (ctx.container.resolve('dataEngine') as DataEngine)\n await emitCrudSideEffects({\n dataEngine: de,\n action: 'updated',\n entity: existing,\n identifiers: {\n id: String(existing.id),\n organizationId: existing.organizationId,\n tenantId: existing.tenantId,\n },\n })\n\n await emitCustomersEvent('customers.tag.removed', {\n id: String(existing.id),\n tagId: parsed.tagId,\n entityId: parsed.entityId,\n organizationId: parsed.organizationId,\n tenantId: parsed.tenantId,\n }, { persistent: true })\n\n return { assignmentId: existing.id ?? null }\n },\n buildLog: async ({ snapshots, input }) => {\n const { translate } = await resolveTranslations()\n const parsed = tagAssignmentSchema.parse(input)\n return {\n actionLabel: translate('customers.audit.tags.unassign', 'Unassign tag'),\n resourceKind: 'customers.tagAssignment',\n resourceId: parsed.tagId,\n tenantId: parsed.tenantId,\n organizationId: parsed.organizationId,\n payload: {\n undo: {\n before: {\n tagId: parsed.tagId,\n entityId: parsed.entityId,\n tenantId: parsed.tenantId,\n organizationId: parsed.organizationId,\n } satisfies TagAssignmentSnapshot,\n } satisfies TagAssignmentUndoPayload,\n },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const payload = extractUndoPayload<TagAssignmentUndoPayload>(logEntry)\n const before = payload?.before\n if (!before) return\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const tag = await em.findOne(CustomerTag, { id: before.tagId })\n const entity = await requireCustomerEntity(em, before.entityId, undefined, 'Customer not found')\n ensureSameScope(entity, before.organizationId, before.tenantId)\n if (!tag) throw new CrudHttpError(404, { error: 'Tag not found' })\n const existing = await em.findOne(CustomerTagAssignment, {\n tag,\n entity,\n tenantId: before.tenantId,\n organizationId: before.organizationId,\n })\n if (!existing) {\n const assignment = em.create(CustomerTagAssignment, {\n tag,\n entity,\n tenantId: before.tenantId,\n organizationId: before.organizationId,\n })\n em.persist(assignment)\n await em.flush()\n }\n },\n}\n\nregisterCommand(createTagCommand)\nregisterCommand(updateTagCommand)\nregisterCommand(deleteTagCommand)\nregisterCommand(assignTagCommand)\nregisterCommand(unassignTagCommand)\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../../src/modules/dashboards/api/users/widgets/route.ts"],
|
|
4
|
-
"sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { DashboardUserWidgets } from '@open-mercato/core/modules/dashboards/data/entities'\nimport { User } from '@open-mercato/core/modules/auth/data/entities'\nimport { userWidgetSettingsSchema } from '@open-mercato/core/modules/dashboards/data/validators'\nimport { loadAllWidgets } from '@open-mercato/core/modules/dashboards/lib/widgets'\nimport { resolveAllowedWidgetIds } from '@open-mercato/core/modules/dashboards/lib/access'\nimport { resolveWidgetAssignmentReadScope } from '@open-mercato/core/modules/dashboards/lib/widgetAssignmentScope'\nimport { hasFeature } from '@open-mercato/shared/security/features'\nimport type { OpenApiMethodDoc, OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport {\n dashboardsTag,\n dashboardsErrorSchema,\n dashboardUserWidgetsResponseSchema,\n dashboardUserWidgetsUpdateResponseSchema,\n} from '../../openapi'\n\nconst FEATURE = 'dashboards.admin.assign-widgets'\n\nexport const metadata = {\n GET: { requireAuth: true, requireFeatures: [FEATURE] },\n PUT: { requireAuth: true, requireFeatures: [FEATURE] },\n}\n\nexport async function GET(req: Request) {\n const auth = await getAuthFromRequest(req)\n if (!auth) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n\n const url = new URL(req.url)\n const userId = url.searchParams.get('userId')\n if (!userId) return NextResponse.json({ error: 'userId is required' }, { status: 400 })\n\n const container = await createRequestContainer()\n const em = container.resolve('em') as any\n const rbac = container.resolve('rbacService') as any\n const acl = await rbac.loadAcl(auth.sub, { tenantId: auth.tenantId ?? null, organizationId: auth.orgId ?? null })\n if (!acl.isSuperAdmin && !hasFeature(acl.features, FEATURE)) {\n return NextResponse.json({ error: 'Forbidden' }, { status: 403 })\n }\n\n const { tenantId, organizationId } = resolveWidgetAssignmentReadScope({\n auth: { tenantId: auth.tenantId ?? null, orgId: auth.orgId ?? null },\n isSuperAdmin: !!acl.isSuperAdmin,\n queryTenantId: url.searchParams.get('tenantId'),\n queryOrganizationId: url.searchParams.get('organizationId'),\n })\n\n const targetUserForRead = await em.findOne(User, { id: userId, deletedAt: null })\n if (!targetUserForRead || (tenantId && (targetUserForRead.tenantId ?? null) !== tenantId)) {\n return NextResponse.json({ error: 'User not found' }, { status: 404 })\n }\n\n const widgets = await loadAllWidgets()\n const targetAcl = await rbac.loadAcl(userId, { tenantId, organizationId })\n const allowed = await resolveAllowedWidgetIds(\n em,\n {\n userId,\n tenantId,\n organizationId,\n features: targetAcl.features ?? [],\n isSuperAdmin: !!targetAcl.isSuperAdmin,\n },\n widgets,\n )\n\n const record = await em.findOne(DashboardUserWidgets, {\n userId,\n tenantId,\n organizationId,\n deletedAt: null,\n })\n\n const response = {\n mode: record ? record.mode : 'inherit',\n widgetIds: record && record.mode === 'override' ? record.widgetIdsJson : [],\n hasCustom: !!record && record.mode === 'override',\n effectiveWidgetIds: allowed,\n scope: { tenantId, organizationId },\n }\n\n return NextResponse.json(response)\n}\n\nexport async function PUT(req: Request) {\n const auth = await getAuthFromRequest(req)\n if (!auth) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n\n let payload: unknown\n try {\n payload = await req.json()\n } catch {\n return NextResponse.json({ error: 'Invalid JSON body' }, { status: 400 })\n }\n const parsed = userWidgetSettingsSchema.safeParse(payload)\n if (!parsed.success) {\n return NextResponse.json({ error: 'Invalid payload', issues: parsed.error.issues }, { status: 400 })\n }\n\n const { resolve } = await createRequestContainer()\n const em = resolve('em') as any\n const rbac = resolve('rbacService') as any\n const acl = await rbac.loadAcl(auth.sub, { tenantId: auth.tenantId ?? null, organizationId: auth.orgId ?? null })\n if (!acl.isSuperAdmin && !hasFeature(acl.features, FEATURE)) {\n return NextResponse.json({ error: 'Forbidden' }, { status: 403 })\n }\n\n const widgets = await loadAllWidgets()\n const validWidgetIds = new Set(widgets.map((w) => w.metadata.id))\n const widgetIds = parsed.data.widgetIds.filter((id) => validWidgetIds.has(id))\n\n const tenantId = auth.tenantId ?? null\n const organizationId = auth.orgId ?? null\n\n const targetUser = await em.findOne(User, { id: parsed.data.userId, deletedAt: null })\n if (!targetUser || (tenantId && (targetUser.tenantId ?? null) !== tenantId)) {\n return NextResponse.json({ error: 'User not found' }, { status: 404 })\n }\n\n let record = await em.findOne(DashboardUserWidgets, {\n userId: parsed.data.userId,\n tenantId,\n organizationId,\n deletedAt: null,\n })\n\n if (parsed.data.mode === 'inherit') {\n if (record) {\n await em.remove(record).flush()\n
|
|
5
|
-
"mappings": "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
|
|
4
|
+
"sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { DashboardUserWidgets } from '@open-mercato/core/modules/dashboards/data/entities'\nimport { User } from '@open-mercato/core/modules/auth/data/entities'\nimport { userWidgetSettingsSchema } from '@open-mercato/core/modules/dashboards/data/validators'\nimport { loadAllWidgets } from '@open-mercato/core/modules/dashboards/lib/widgets'\nimport { resolveAllowedWidgetIds } from '@open-mercato/core/modules/dashboards/lib/access'\nimport { resolveWidgetAssignmentReadScope } from '@open-mercato/core/modules/dashboards/lib/widgetAssignmentScope'\nimport { hasFeature } from '@open-mercato/shared/security/features'\nimport type { OpenApiMethodDoc, OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport {\n dashboardsTag,\n dashboardsErrorSchema,\n dashboardUserWidgetsResponseSchema,\n dashboardUserWidgetsUpdateResponseSchema,\n} from '../../openapi'\n\nconst FEATURE = 'dashboards.admin.assign-widgets'\n\nexport const metadata = {\n GET: { requireAuth: true, requireFeatures: [FEATURE] },\n PUT: { requireAuth: true, requireFeatures: [FEATURE] },\n}\n\nexport async function GET(req: Request) {\n const auth = await getAuthFromRequest(req)\n if (!auth) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n\n const url = new URL(req.url)\n const userId = url.searchParams.get('userId')\n if (!userId) return NextResponse.json({ error: 'userId is required' }, { status: 400 })\n\n const container = await createRequestContainer()\n const em = container.resolve('em') as any\n const rbac = container.resolve('rbacService') as any\n const acl = await rbac.loadAcl(auth.sub, { tenantId: auth.tenantId ?? null, organizationId: auth.orgId ?? null })\n if (!acl.isSuperAdmin && !hasFeature(acl.features, FEATURE)) {\n return NextResponse.json({ error: 'Forbidden' }, { status: 403 })\n }\n\n const { tenantId, organizationId } = resolveWidgetAssignmentReadScope({\n auth: { tenantId: auth.tenantId ?? null, orgId: auth.orgId ?? null },\n isSuperAdmin: !!acl.isSuperAdmin,\n queryTenantId: url.searchParams.get('tenantId'),\n queryOrganizationId: url.searchParams.get('organizationId'),\n })\n\n const targetUserForRead = await em.findOne(User, { id: userId, deletedAt: null })\n if (!targetUserForRead || (tenantId && (targetUserForRead.tenantId ?? null) !== tenantId)) {\n return NextResponse.json({ error: 'User not found' }, { status: 404 })\n }\n\n const widgets = await loadAllWidgets()\n const targetAcl = await rbac.loadAcl(userId, { tenantId, organizationId })\n const allowed = await resolveAllowedWidgetIds(\n em,\n {\n userId,\n tenantId,\n organizationId,\n features: targetAcl.features ?? [],\n isSuperAdmin: !!targetAcl.isSuperAdmin,\n },\n widgets,\n )\n\n const record = await em.findOne(DashboardUserWidgets, {\n userId,\n tenantId,\n organizationId,\n deletedAt: null,\n })\n\n const response = {\n mode: record ? record.mode : 'inherit',\n widgetIds: record && record.mode === 'override' ? record.widgetIdsJson : [],\n hasCustom: !!record && record.mode === 'override',\n effectiveWidgetIds: allowed,\n scope: { tenantId, organizationId },\n }\n\n return NextResponse.json(response)\n}\n\nexport async function PUT(req: Request) {\n const auth = await getAuthFromRequest(req)\n if (!auth) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n\n let payload: unknown\n try {\n payload = await req.json()\n } catch {\n return NextResponse.json({ error: 'Invalid JSON body' }, { status: 400 })\n }\n const parsed = userWidgetSettingsSchema.safeParse(payload)\n if (!parsed.success) {\n return NextResponse.json({ error: 'Invalid payload', issues: parsed.error.issues }, { status: 400 })\n }\n\n const { resolve } = await createRequestContainer()\n const em = resolve('em') as any\n const rbac = resolve('rbacService') as any\n const acl = await rbac.loadAcl(auth.sub, { tenantId: auth.tenantId ?? null, organizationId: auth.orgId ?? null })\n if (!acl.isSuperAdmin && !hasFeature(acl.features, FEATURE)) {\n return NextResponse.json({ error: 'Forbidden' }, { status: 403 })\n }\n\n const widgets = await loadAllWidgets()\n const validWidgetIds = new Set(widgets.map((w) => w.metadata.id))\n const widgetIds = parsed.data.widgetIds.filter((id) => validWidgetIds.has(id))\n\n const tenantId = auth.tenantId ?? null\n const organizationId = auth.orgId ?? null\n\n const targetUser = await em.findOne(User, { id: parsed.data.userId, deletedAt: null })\n if (!targetUser || (tenantId && (targetUser.tenantId ?? null) !== tenantId)) {\n return NextResponse.json({ error: 'User not found' }, { status: 404 })\n }\n\n let record = await em.findOne(DashboardUserWidgets, {\n userId: parsed.data.userId,\n tenantId,\n organizationId,\n deletedAt: null,\n })\n\n if (parsed.data.mode === 'inherit') {\n if (record) {\n await em.remove(record).flush()\n }\n return NextResponse.json({ ok: true, mode: 'inherit', widgetIds: [] })\n }\n\n if (!record) {\n record = em.create(DashboardUserWidgets, {\n userId: parsed.data.userId,\n tenantId,\n organizationId,\n mode: 'override',\n widgetIdsJson: widgetIds,\n })\n em.persist(record)\n } else {\n record.mode = 'override'\n record.widgetIdsJson = widgetIds\n }\n await em.flush()\n\n return NextResponse.json({ ok: true, mode: 'override', widgetIds })\n}\n\nconst userWidgetsQuerySchema = z.object({\n userId: z.string().uuid(),\n tenantId: z.string().uuid().optional(),\n organizationId: z.string().uuid().optional(),\n})\n\nconst userWidgetsGetDoc: OpenApiMethodDoc = {\n summary: 'Read widget overrides for a user',\n description: 'Returns the widgets inherited and explicitly configured for the requested user within the current scope.',\n tags: [dashboardsTag],\n query: userWidgetsQuerySchema,\n responses: [\n { status: 200, description: 'Widget settings for the user.', schema: dashboardUserWidgetsResponseSchema },\n ],\n errors: [\n { status: 400, description: 'Missing user identifier', schema: dashboardsErrorSchema },\n { status: 401, description: 'Authentication required', schema: dashboardsErrorSchema },\n { status: 403, description: 'Insufficient permissions to manage user widgets', schema: dashboardsErrorSchema },\n ],\n}\n\nconst userWidgetsPutDoc: OpenApiMethodDoc = {\n summary: 'Update user-specific dashboard widgets',\n description: 'Sets the widget override mode and allowed widgets for a user. Passing `mode: inherit` clears overrides.',\n tags: [dashboardsTag],\n requestBody: {\n contentType: 'application/json',\n schema: userWidgetSettingsSchema,\n description: 'User identifier, override mode, and widget ids. Scope is derived from the authenticated session.',\n },\n responses: [\n { status: 200, description: 'Overrides saved.', schema: dashboardUserWidgetsUpdateResponseSchema },\n ],\n errors: [\n { status: 400, description: 'Invalid payload or unknown widgets', schema: dashboardsErrorSchema },\n { status: 401, description: 'Authentication required', schema: dashboardsErrorSchema },\n { status: 403, description: 'Insufficient permissions to manage user widgets', schema: dashboardsErrorSchema },\n ],\n}\n\nexport const openApi: OpenApiRouteDoc = {\n tag: dashboardsTag,\n summary: 'Manage user-level dashboard widgets',\n methods: {\n GET: userWidgetsGetDoc,\n PUT: userWidgetsPutDoc,\n },\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
createWidgetDataService,
|
|
7
7
|
WidgetDataValidationError
|
|
8
8
|
} from "../../../services/widgetDataService.js";
|
|
9
|
+
import { runApiInterceptorsBefore } from "@open-mercato/shared/lib/crud/interceptor-runner";
|
|
9
10
|
import { dashboardsTag, dashboardsErrorSchema } from "../../openapi.js";
|
|
10
11
|
import { widgetDataRequestSchema, widgetDataResponseSchema } from "./schema.js";
|
|
11
12
|
const metadata = {
|
|
@@ -59,10 +60,37 @@ async function POST(req) {
|
|
|
59
60
|
if (auth.orgId) return [auth.orgId];
|
|
60
61
|
return void 0;
|
|
61
62
|
})();
|
|
63
|
+
const userFeatures = Array.isArray(auth.features) ? auth.features.filter((value) => typeof value === "string") : [];
|
|
64
|
+
const headers = {};
|
|
65
|
+
req.headers.forEach((value, key) => {
|
|
66
|
+
headers[key] = value;
|
|
67
|
+
});
|
|
68
|
+
const interceptorResult = await runApiInterceptorsBefore({
|
|
69
|
+
routePath: "dashboards/widgets/data",
|
|
70
|
+
method: "POST",
|
|
71
|
+
request: {
|
|
72
|
+
method: "POST",
|
|
73
|
+
url: req.url,
|
|
74
|
+
headers,
|
|
75
|
+
body: parsed.data
|
|
76
|
+
},
|
|
77
|
+
context: {
|
|
78
|
+
em,
|
|
79
|
+
container,
|
|
80
|
+
userId: auth.sub ?? "",
|
|
81
|
+
tenantId,
|
|
82
|
+
organizationId: organizationIds?.[0] ?? auth.orgId ?? "",
|
|
83
|
+
userFeatures
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
if (!interceptorResult.ok) {
|
|
87
|
+
return NextResponse.json(interceptorResult.body, { status: interceptorResult.statusCode });
|
|
88
|
+
}
|
|
89
|
+
const requestData = interceptorResult.request.body ?? parsed.data;
|
|
62
90
|
try {
|
|
63
91
|
const cache = container.resolve("cache");
|
|
64
92
|
const service = createWidgetDataService(em, { tenantId, organizationIds }, analyticsRegistry, cache);
|
|
65
|
-
const result = await service.fetchWidgetData(
|
|
93
|
+
const result = await service.fetchWidgetData(requestData);
|
|
66
94
|
return NextResponse.json(result);
|
|
67
95
|
} catch (err) {
|
|
68
96
|
console.error("[widgets/data] Error:", err);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../../src/modules/dashboards/api/widgets/data/route.ts"],
|
|
4
|
-
"sourcesContent": ["import { NextResponse } from 'next/server'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport type { CacheStrategy } from '@open-mercato/cache'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { resolveOrganizationScopeForRequest } from '@open-mercato/core/modules/directory/utils/organizationScope'\nimport {\n createWidgetDataService,\n type WidgetDataRequest,\n WidgetDataValidationError,\n} from '../../../services/widgetDataService'\nimport type { AnalyticsRegistry } from '../../../services/analyticsRegistry'\nimport type { OpenApiMethodDoc, OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport { dashboardsTag, dashboardsErrorSchema } from '../../openapi'\nimport { widgetDataRequestSchema, widgetDataResponseSchema } from './schema'\n\nexport const metadata = {\n POST: { requireAuth: true, requireFeatures: ['analytics.view'] },\n}\n\nexport async function POST(req: Request) {\n const auth = await getAuthFromRequest(req)\n if (!auth) {\n return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n }\n\n let body: unknown\n try {\n body = await req.json()\n } catch {\n return NextResponse.json({ error: 'Invalid JSON body' }, { status: 400 })\n }\n\n const parsed = widgetDataRequestSchema.safeParse(body)\n if (!parsed.success) {\n return NextResponse.json(\n { error: 'Invalid request payload', issues: parsed.error.issues },\n { status: 400 },\n )\n }\n\n const container = await createRequestContainer()\n const analyticsRegistry = container.resolve<AnalyticsRegistry>('analyticsRegistry')\n\n const entityFeatures = analyticsRegistry.getRequiredFeatures(parsed.data.entityType)\n if (entityFeatures && entityFeatures.length > 0) {\n const rbacService = container.resolve<{\n userHasAllFeatures: (\n userId: string,\n features: string[],\n scope: { tenantId: string; organizationId?: string | null },\n ) => Promise<boolean>\n }>('rbacService')\n const hasAccess = await rbacService.userHasAllFeatures(auth.sub, entityFeatures, {\n tenantId: auth.tenantId!,\n organizationId: auth.orgId,\n })\n if (!hasAccess) {\n return NextResponse.json({ error: 'Forbidden' }, { status: 403 })\n }\n }\n\n const em = (container.resolve('em') as EntityManager).fork({\n clear: true,\n freshEventManager: true,\n useContext: true,\n })\n\n const tenantId = auth.tenantId ?? null\n if (!tenantId) {\n return NextResponse.json({ error: 'Tenant context is required' }, { status: 400 })\n }\n\n const scope = await resolveOrganizationScopeForRequest({ container, auth, request: req })\n\n const organizationIds = (() => {\n if (scope?.selectedId) return [scope.selectedId]\n if (Array.isArray(scope?.filterIds) && scope.filterIds.length > 0) return scope.filterIds\n if (scope?.allowedIds === null) return undefined\n if (auth.orgId) return [auth.orgId]\n return undefined\n })()\n\n try {\n const cache = container.resolve<CacheStrategy>('cache')\n const service = createWidgetDataService(em, { tenantId, organizationIds }, analyticsRegistry, cache)\n const result = await service.fetchWidgetData(
|
|
5
|
-
"mappings": "AAAA,SAAS,oBAAoB;AAG7B,SAAS,0BAA0B;AACnC,SAAS,8BAA8B;AACvC,SAAS,0CAA0C;AACnD;AAAA,EACE;AAAA,EAEA;AAAA,OACK;
|
|
4
|
+
"sourcesContent": ["import { NextResponse } from 'next/server'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport type { CacheStrategy } from '@open-mercato/cache'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { resolveOrganizationScopeForRequest } from '@open-mercato/core/modules/directory/utils/organizationScope'\nimport {\n createWidgetDataService,\n type WidgetDataRequest,\n WidgetDataValidationError,\n} from '../../../services/widgetDataService'\nimport type { AnalyticsRegistry } from '../../../services/analyticsRegistry'\nimport { runApiInterceptorsBefore } from '@open-mercato/shared/lib/crud/interceptor-runner'\nimport type { OpenApiMethodDoc, OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport { dashboardsTag, dashboardsErrorSchema } from '../../openapi'\nimport { widgetDataRequestSchema, widgetDataResponseSchema } from './schema'\n\nexport const metadata = {\n POST: { requireAuth: true, requireFeatures: ['analytics.view'] },\n}\n\nexport async function POST(req: Request) {\n const auth = await getAuthFromRequest(req)\n if (!auth) {\n return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n }\n\n let body: unknown\n try {\n body = await req.json()\n } catch {\n return NextResponse.json({ error: 'Invalid JSON body' }, { status: 400 })\n }\n\n const parsed = widgetDataRequestSchema.safeParse(body)\n if (!parsed.success) {\n return NextResponse.json(\n { error: 'Invalid request payload', issues: parsed.error.issues },\n { status: 400 },\n )\n }\n\n const container = await createRequestContainer()\n const analyticsRegistry = container.resolve<AnalyticsRegistry>('analyticsRegistry')\n\n const entityFeatures = analyticsRegistry.getRequiredFeatures(parsed.data.entityType)\n if (entityFeatures && entityFeatures.length > 0) {\n const rbacService = container.resolve<{\n userHasAllFeatures: (\n userId: string,\n features: string[],\n scope: { tenantId: string; organizationId?: string | null },\n ) => Promise<boolean>\n }>('rbacService')\n const hasAccess = await rbacService.userHasAllFeatures(auth.sub, entityFeatures, {\n tenantId: auth.tenantId!,\n organizationId: auth.orgId,\n })\n if (!hasAccess) {\n return NextResponse.json({ error: 'Forbidden' }, { status: 403 })\n }\n }\n\n const em = (container.resolve('em') as EntityManager).fork({\n clear: true,\n freshEventManager: true,\n useContext: true,\n })\n\n const tenantId = auth.tenantId ?? null\n if (!tenantId) {\n return NextResponse.json({ error: 'Tenant context is required' }, { status: 400 })\n }\n\n const scope = await resolveOrganizationScopeForRequest({ container, auth, request: req })\n\n const organizationIds = (() => {\n if (scope?.selectedId) return [scope.selectedId]\n if (Array.isArray(scope?.filterIds) && scope.filterIds.length > 0) return scope.filterIds\n if (scope?.allowedIds === null) return undefined\n if (auth.orgId) return [auth.orgId]\n return undefined\n })()\n\n const userFeatures = Array.isArray(auth.features)\n ? auth.features.filter((value): value is string => typeof value === 'string')\n : []\n\n const headers: Record<string, string> = {}\n req.headers.forEach((value, key) => {\n headers[key] = value\n })\n\n const interceptorResult = await runApiInterceptorsBefore({\n routePath: 'dashboards/widgets/data',\n method: 'POST',\n request: {\n method: 'POST',\n url: req.url,\n headers,\n body: parsed.data as unknown as Record<string, unknown>,\n },\n context: {\n em,\n container,\n userId: auth.sub ?? '',\n tenantId,\n organizationId: organizationIds?.[0] ?? auth.orgId ?? '',\n userFeatures,\n },\n })\n\n if (!interceptorResult.ok) {\n return NextResponse.json(interceptorResult.body, { status: interceptorResult.statusCode })\n }\n\n const requestData = (interceptorResult.request.body ?? parsed.data) as WidgetDataRequest\n\n try {\n const cache = container.resolve<CacheStrategy>('cache')\n const service = createWidgetDataService(em, { tenantId, organizationIds }, analyticsRegistry, cache)\n const result = await service.fetchWidgetData(requestData)\n return NextResponse.json(result)\n } catch (err) {\n console.error('[widgets/data] Error:', err)\n if (err instanceof WidgetDataValidationError) {\n return NextResponse.json({ error: err.message }, { status: 400 })\n }\n return NextResponse.json(\n { error: 'An error occurred while processing your request' },\n { status: 500 },\n )\n }\n}\n\nconst widgetDataPostDoc: OpenApiMethodDoc = {\n summary: 'Fetch aggregated data for dashboard widgets',\n description:\n 'Executes an aggregation query against the specified entity type and returns the result. Supports date range filtering, grouping, and period-over-period comparison.',\n tags: [dashboardsTag],\n requestBody: {\n contentType: 'application/json',\n schema: widgetDataRequestSchema,\n description: 'Widget data request configuration specifying entity type, metric, filters, and grouping.',\n },\n responses: [\n {\n status: 200,\n description: 'Aggregated data for the widget.',\n schema: widgetDataResponseSchema,\n },\n ],\n errors: [\n { status: 400, description: 'Invalid request payload', schema: dashboardsErrorSchema },\n { status: 401, description: 'Authentication required', schema: dashboardsErrorSchema },\n { status: 403, description: 'Missing analytics.view feature', schema: dashboardsErrorSchema },\n { status: 500, description: 'Internal server error', schema: dashboardsErrorSchema },\n ],\n}\n\nexport const openApi: OpenApiRouteDoc = {\n tag: dashboardsTag,\n summary: 'Widget data aggregation endpoint',\n methods: {\n POST: widgetDataPostDoc,\n },\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -347,15 +347,15 @@ function createSyncEngine(deps) {
|
|
|
347
347
|
const processedBatchCount = batch.processedCount ?? batch.items.length;
|
|
348
348
|
processedCount += processedBatchCount;
|
|
349
349
|
totalCount = batch.totalEstimate ?? totalCount;
|
|
350
|
-
await syncRunService.
|
|
350
|
+
await syncRunService.commitBatchProgress(
|
|
351
351
|
run.id,
|
|
352
352
|
{
|
|
353
353
|
...delta,
|
|
354
354
|
batchesCompleted: 1
|
|
355
355
|
},
|
|
356
|
+
batch.cursor,
|
|
356
357
|
scope
|
|
357
358
|
);
|
|
358
|
-
await syncRunService.updateCursor(run.id, batch.cursor, scope);
|
|
359
359
|
await updateProgress(run.progressJobId, processedCount, totalCount, scope);
|
|
360
360
|
await refreshCoverageSnapshots(batch.refreshCoverageEntityTypes, scope);
|
|
361
361
|
await logImportItemFailures(run.id, run.integrationId, batch.items, scope);
|
|
@@ -475,7 +475,7 @@ function createSyncEngine(deps) {
|
|
|
475
475
|
}
|
|
476
476
|
const delta = applyExportCounters(batch);
|
|
477
477
|
processedCount += delta.processedCount;
|
|
478
|
-
await syncRunService.
|
|
478
|
+
await syncRunService.commitBatchProgress(
|
|
479
479
|
run.id,
|
|
480
480
|
{
|
|
481
481
|
createdCount: 0,
|
|
@@ -484,9 +484,9 @@ function createSyncEngine(deps) {
|
|
|
484
484
|
failedCount: delta.failedCount,
|
|
485
485
|
batchesCompleted: 1
|
|
486
486
|
},
|
|
487
|
+
batch.cursor,
|
|
487
488
|
scope
|
|
488
489
|
);
|
|
489
|
-
await syncRunService.updateCursor(run.id, batch.cursor, scope);
|
|
490
490
|
await updateProgress(run.progressJobId, processedCount, null, scope);
|
|
491
491
|
await writeOperationalLog({
|
|
492
492
|
integrationId: run.integrationId,
|