@open-mercato/core 0.7.0 → 0.7.1-develop.7103.1.41ff100d93
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 +1 -1
- package/dist/helpers/integration/api.js +13 -1
- package/dist/helpers/integration/api.js.map +2 -2
- package/dist/helpers/integration/communicationChannelsFixtures.js +19 -0
- package/dist/helpers/integration/communicationChannelsFixtures.js.map +2 -2
- package/dist/modules/api_keys/api/keys/route.js +1 -1
- package/dist/modules/api_keys/api/keys/route.js.map +2 -2
- package/dist/modules/api_keys/backend/api-keys/page.js +3 -1
- package/dist/modules/api_keys/backend/api-keys/page.js.map +2 -2
- package/dist/modules/api_keys/di.js +9 -0
- package/dist/modules/api_keys/di.js.map +7 -0
- package/dist/modules/api_keys/services/principalService.js +31 -0
- package/dist/modules/api_keys/services/principalService.js.map +7 -0
- package/dist/modules/attachments/api/route.js +2 -2
- package/dist/modules/attachments/api/route.js.map +2 -2
- package/dist/modules/attachments/components/AttachmentLibrary.js +2 -0
- package/dist/modules/attachments/components/AttachmentLibrary.js.map +2 -2
- package/dist/modules/attachments/di.js +11 -1
- package/dist/modules/attachments/di.js.map +2 -2
- package/dist/modules/attachments/index.js.map +1 -1
- package/dist/modules/attachments/lib/attachment-service.js +313 -0
- package/dist/modules/attachments/lib/attachment-service.js.map +7 -0
- package/dist/modules/attachments/lib/scoped-upload-service.js +15 -2
- package/dist/modules/attachments/lib/scoped-upload-service.js.map +2 -2
- package/dist/modules/attachments/lib/upload-limits.js +6 -2
- package/dist/modules/attachments/lib/upload-limits.js.map +2 -2
- package/dist/modules/audit_logs/backend/audit-logs/page.js +6 -0
- package/dist/modules/audit_logs/backend/audit-logs/page.js.map +2 -2
- package/dist/modules/audit_logs/lib/display-helpers.js +19 -11
- package/dist/modules/audit_logs/lib/display-helpers.js.map +2 -2
- package/dist/modules/audit_logs/services/actionLogService.js +57 -12
- package/dist/modules/audit_logs/services/actionLogService.js.map +3 -3
- package/dist/modules/auth/acl.js +30 -5
- package/dist/modules/auth/acl.js.map +2 -2
- package/dist/modules/auth/api/feature-check.js +8 -2
- package/dist/modules/auth/api/feature-check.js.map +2 -2
- package/dist/modules/auth/api/login.js +4 -1
- package/dist/modules/auth/api/login.js.map +2 -2
- package/dist/modules/auth/api/reset/validate.js +55 -0
- package/dist/modules/auth/api/reset/validate.js.map +7 -0
- package/dist/modules/auth/api/roles/acl/route.js +28 -33
- package/dist/modules/auth/api/roles/acl/route.js.map +2 -2
- package/dist/modules/auth/api/session/refresh.js +25 -1
- package/dist/modules/auth/api/session/refresh.js.map +2 -2
- package/dist/modules/auth/api/users/acl/route.js +96 -51
- package/dist/modules/auth/api/users/acl/route.js.map +2 -2
- package/dist/modules/auth/api/users/route.js +13 -5
- package/dist/modules/auth/api/users/route.js.map +2 -2
- package/dist/modules/auth/backend/roles/[id]/edit/page.meta.js +5 -1
- package/dist/modules/auth/backend/roles/[id]/edit/page.meta.js.map +2 -2
- package/dist/modules/auth/backend/roles/page.js +3 -1
- package/dist/modules/auth/backend/roles/page.js.map +2 -2
- package/dist/modules/auth/backend/users/[id]/edit/page.meta.js +5 -1
- package/dist/modules/auth/backend/users/[id]/edit/page.meta.js.map +2 -2
- package/dist/modules/auth/backend/users/page.js +2 -1
- package/dist/modules/auth/backend/users/page.js.map +2 -2
- package/dist/modules/auth/commands/acl.js +254 -0
- package/dist/modules/auth/commands/acl.js.map +7 -0
- package/dist/modules/auth/components/AclEditor.js +39 -30
- package/dist/modules/auth/components/AclEditor.js.map +2 -2
- package/dist/modules/auth/data/entities.js.map +2 -2
- package/dist/modules/auth/data/validators.js +5 -1
- package/dist/modules/auth/data/validators.js.map +2 -2
- package/dist/modules/auth/di.js +35 -9
- package/dist/modules/auth/di.js.map +2 -2
- package/dist/modules/auth/frontend/reset/[token]/page.js +46 -0
- package/dist/modules/auth/frontend/reset/[token]/page.js.map +2 -2
- package/dist/modules/auth/lib/grantChecks.js.map +2 -2
- package/dist/modules/auth/lib/userIdFilter.js +16 -0
- package/dist/modules/auth/lib/userIdFilter.js.map +7 -0
- package/dist/modules/auth/services/authService.js +16 -2
- package/dist/modules/auth/services/authService.js.map +2 -2
- package/dist/modules/auth/services/principalService.js +200 -0
- package/dist/modules/auth/services/principalService.js.map +7 -0
- package/dist/modules/auth/services/rbacService.js +67 -24
- package/dist/modules/auth/services/rbacService.js.map +3 -3
- package/dist/modules/auth/services/roleOrganizationScope.js +41 -0
- package/dist/modules/auth/services/roleOrganizationScope.js.map +7 -0
- package/dist/modules/business_rules/backend/logs/page.js +3 -1
- package/dist/modules/business_rules/backend/logs/page.js.map +2 -2
- package/dist/modules/business_rules/backend/rules/page.js +3 -1
- package/dist/modules/business_rules/backend/rules/page.js.map +2 -2
- package/dist/modules/business_rules/backend/sets/page.js +3 -1
- package/dist/modules/business_rules/backend/sets/page.js.map +2 -2
- package/dist/modules/business_rules/lib/expression-evaluator.js +9 -9
- package/dist/modules/business_rules/lib/expression-evaluator.js.map +2 -2
- package/dist/modules/business_rules/lib/rule-evaluator.js +10 -9
- package/dist/modules/business_rules/lib/rule-evaluator.js.map +2 -2
- package/dist/modules/catalog/ai-agents.js +1 -1
- package/dist/modules/catalog/ai-agents.js.map +1 -1
- package/dist/modules/catalog/ai-tools/merchandising-pack.js +2 -2
- package/dist/modules/catalog/ai-tools/merchandising-pack.js.map +2 -2
- package/dist/modules/catalog/backend/catalog/products/MerchandisingAssistantSheet.js +5 -2
- package/dist/modules/catalog/backend/catalog/products/MerchandisingAssistantSheet.js.map +2 -2
- package/dist/modules/catalog/components/categories/CategoriesDataTable.js +2 -0
- package/dist/modules/catalog/components/categories/CategoriesDataTable.js.map +2 -2
- package/dist/modules/catalog/components/products/ProductsDataTable.js +7 -1
- package/dist/modules/catalog/components/products/ProductsDataTable.js.map +2 -2
- package/dist/modules/catalog/widgets/injection/merchandising-assistant-trigger/widget.client.js +2 -0
- package/dist/modules/catalog/widgets/injection/merchandising-assistant-trigger/widget.client.js.map +2 -2
- package/dist/modules/communication_channels/api/post/channels/[id]/test-send/route.js +9 -1
- package/dist/modules/communication_channels/api/post/channels/[id]/test-send/route.js.map +2 -2
- package/dist/modules/communication_channels/api/post/test-seed/route.js +95 -10
- package/dist/modules/communication_channels/api/post/test-seed/route.js.map +2 -2
- package/dist/modules/communication_channels/backend/communication_channels/channels/page.js +3 -0
- package/dist/modules/communication_channels/backend/communication_channels/channels/page.js.map +2 -2
- package/dist/modules/communication_channels/commands/ingest-inbound-message.js +14 -1
- package/dist/modules/communication_channels/commands/ingest-inbound-message.js.map +2 -2
- package/dist/modules/communication_channels/di.js +7 -1
- package/dist/modules/communication_channels/di.js.map +2 -2
- package/dist/modules/communication_channels/lib/email-capabilities.js +4 -1
- package/dist/modules/communication_channels/lib/email-capabilities.js.map +2 -2
- package/dist/modules/communication_channels/lib/outbound-recipient.js +33 -0
- package/dist/modules/communication_channels/lib/outbound-recipient.js.map +7 -0
- package/dist/modules/communication_channels/lib/resolve-channel-type.js +51 -0
- package/dist/modules/communication_channels/lib/resolve-channel-type.js.map +7 -0
- package/dist/modules/communication_channels/lib/test-seed.js +41 -2
- package/dist/modules/communication_channels/lib/test-seed.js.map +2 -2
- package/dist/modules/configs/lib/system-status.js +9 -0
- package/dist/modules/configs/lib/system-status.js.map +2 -2
- package/dist/modules/currencies/backend/currencies/page.js +3 -1
- package/dist/modules/currencies/backend/currencies/page.js.map +2 -2
- package/dist/modules/currencies/backend/exchange-rates/page.js +3 -1
- package/dist/modules/currencies/backend/exchange-rates/page.js.map +2 -2
- package/dist/modules/currencies/services/rateFetchingService.js +6 -1
- package/dist/modules/currencies/services/rateFetchingService.js.map +2 -2
- package/dist/modules/customer_accounts/api/admin/domain-mappings.js +9 -0
- package/dist/modules/customer_accounts/api/admin/domain-mappings.js.map +2 -2
- package/dist/modules/customer_accounts/backend/customer_accounts/roles/page.js +3 -1
- package/dist/modules/customer_accounts/backend/customer_accounts/roles/page.js.map +2 -2
- package/dist/modules/customer_accounts/backend/customer_accounts/users/PortalUsersPageClient.js +3 -1
- package/dist/modules/customer_accounts/backend/customer_accounts/users/PortalUsersPageClient.js.map +2 -2
- package/dist/modules/customer_accounts/services/domainMappingService.js +10 -0
- package/dist/modules/customer_accounts/services/domainMappingService.js.map +2 -2
- package/dist/modules/customers/ai-tools/companies-pack.js +4 -1
- package/dist/modules/customers/ai-tools/companies-pack.js.map +2 -2
- package/dist/modules/customers/ai-tools/deal-analyzer-pack.js +13 -2
- package/dist/modules/customers/ai-tools/deal-analyzer-pack.js.map +2 -2
- package/dist/modules/customers/ai-tools/deals-pack.js +52 -6
- package/dist/modules/customers/ai-tools/deals-pack.js.map +2 -2
- package/dist/modules/customers/ai-tools/people-pack.js +4 -1
- package/dist/modules/customers/ai-tools/people-pack.js.map +2 -2
- package/dist/modules/customers/api/activities/route.js +48 -6
- package/dist/modules/customers/api/activities/route.js.map +2 -2
- package/dist/modules/customers/api/companies/[id]/people/route.js +20 -64
- package/dist/modules/customers/api/companies/[id]/people/route.js.map +2 -2
- package/dist/modules/customers/api/companies/[id]/route.js +17 -94
- package/dist/modules/customers/api/companies/[id]/route.js.map +2 -2
- package/dist/modules/customers/api/dashboard/widgets/customer-todos/route.js +8 -5
- package/dist/modules/customers/api/dashboard/widgets/customer-todos/route.js.map +2 -2
- package/dist/modules/customers/api/deals/[id]/companies/route.js +1 -1
- package/dist/modules/customers/api/deals/[id]/companies/route.js.map +2 -2
- package/dist/modules/customers/api/deals/[id]/people/route.js +1 -1
- package/dist/modules/customers/api/deals/[id]/people/route.js.map +2 -2
- package/dist/modules/customers/api/deals/[id]/route.js +8 -25
- package/dist/modules/customers/api/deals/[id]/route.js.map +2 -2
- package/dist/modules/customers/api/deals/[id]/stats/route.js +3 -22
- package/dist/modules/customers/api/deals/[id]/stats/route.js.map +2 -2
- package/dist/modules/customers/api/deals/aggregate/route.js +14 -9
- package/dist/modules/customers/api/deals/aggregate/route.js.map +2 -2
- package/dist/modules/customers/api/deals/map/route.js +5 -5
- package/dist/modules/customers/api/deals/map/route.js.map +2 -2
- package/dist/modules/customers/api/deals/route.js +38 -1
- package/dist/modules/customers/api/deals/route.js.map +2 -2
- package/dist/modules/customers/api/entity-roles-factory.js +4 -4
- package/dist/modules/customers/api/entity-roles-factory.js.map +2 -2
- package/dist/modules/customers/api/interactions/route.js +3 -1
- package/dist/modules/customers/api/interactions/route.js.map +2 -2
- package/dist/modules/customers/api/interactions/tasks/route.js +27 -4
- package/dist/modules/customers/api/interactions/tasks/route.js.map +2 -2
- package/dist/modules/customers/api/people/[id]/companies/[linkId]/route.js +4 -3
- package/dist/modules/customers/api/people/[id]/companies/[linkId]/route.js.map +2 -2
- package/dist/modules/customers/api/people/[id]/companies/context.js +1 -1
- package/dist/modules/customers/api/people/[id]/companies/context.js.map +2 -2
- package/dist/modules/customers/api/people/[id]/companies/enriched/route.js +1 -1
- package/dist/modules/customers/api/people/[id]/companies/enriched/route.js.map +2 -2
- package/dist/modules/customers/api/people/[id]/route.js +11 -10
- package/dist/modules/customers/api/people/[id]/route.js.map +2 -2
- package/dist/modules/customers/api/people/check-phone/route.js +51 -9
- package/dist/modules/customers/api/people/check-phone/route.js.map +2 -2
- package/dist/modules/customers/api/todos/route.js +30 -5
- package/dist/modules/customers/api/todos/route.js.map +2 -2
- package/dist/modules/customers/backend/customers/companies/page.js +4 -2
- package/dist/modules/customers/backend/customers/companies/page.js.map +2 -2
- package/dist/modules/customers/backend/customers/deals/[id]/page.js +6 -1
- package/dist/modules/customers/backend/customers/deals/[id]/page.js.map +2 -2
- package/dist/modules/customers/backend/customers/deals/page.js +5 -2
- package/dist/modules/customers/backend/customers/deals/page.js.map +2 -2
- package/dist/modules/customers/backend/customers/deals/pipeline/components/Lane.js +4 -13
- package/dist/modules/customers/backend/customers/deals/pipeline/components/Lane.js.map +2 -2
- package/dist/modules/customers/backend/customers/deals/pipeline/components/StatusFilterPopover.js +85 -14
- package/dist/modules/customers/backend/customers/deals/pipeline/components/StatusFilterPopover.js.map +2 -2
- package/dist/modules/customers/backend/customers/deals/pipeline/components/toneClasses.js +18 -0
- package/dist/modules/customers/backend/customers/deals/pipeline/components/toneClasses.js.map +7 -0
- package/dist/modules/customers/backend/customers/people/page.js +4 -2
- package/dist/modules/customers/backend/customers/people/page.js.map +2 -2
- package/dist/modules/customers/backend/hooks/useEmailDuplicateCheck.js +9 -3
- package/dist/modules/customers/backend/hooks/useEmailDuplicateCheck.js.map +2 -2
- package/dist/modules/customers/commands/deals.js +21 -34
- package/dist/modules/customers/commands/deals.js.map +2 -2
- package/dist/modules/customers/commands/interactions.js.map +1 -1
- package/dist/modules/customers/commands/personCompanyLinks.js +131 -2
- package/dist/modules/customers/commands/personCompanyLinks.js.map +2 -2
- package/dist/modules/customers/components/CustomerTodosTable.js +1 -0
- package/dist/modules/customers/components/CustomerTodosTable.js.map +2 -2
- package/dist/modules/customers/components/DealsKpiStrip.js +7 -6
- package/dist/modules/customers/components/DealsKpiStrip.js.map +2 -2
- package/dist/modules/customers/components/calendar/AgendaList.js +2 -2
- package/dist/modules/customers/components/calendar/AgendaList.js.map +2 -2
- package/dist/modules/customers/components/calendar/CalendarEventEditor.js +1 -1
- package/dist/modules/customers/components/calendar/CalendarEventEditor.js.map +2 -2
- package/dist/modules/customers/components/calendar/CalendarScreen.js +1 -1
- package/dist/modules/customers/components/calendar/CalendarScreen.js.map +2 -2
- package/dist/modules/customers/components/calendar/CalendarSettingsModal.js +1 -1
- package/dist/modules/customers/components/calendar/CalendarSettingsModal.js.map +2 -2
- package/dist/modules/customers/components/calendar/EventBlock.js +1 -1
- package/dist/modules/customers/components/calendar/EventBlock.js.map +2 -2
- package/dist/modules/customers/components/calendar/EventPeekPopover.js +3 -2
- package/dist/modules/customers/components/calendar/EventPeekPopover.js.map +2 -2
- package/dist/modules/customers/components/calendar/editor/PeopleField.js +40 -5
- package/dist/modules/customers/components/calendar/editor/PeopleField.js.map +2 -2
- package/dist/modules/customers/components/calendar/editor/hooks.js +2 -1
- package/dist/modules/customers/components/calendar/editor/hooks.js.map +2 -2
- package/dist/modules/customers/components/calendar/types.js +1 -1
- package/dist/modules/customers/components/calendar/types.js.map +2 -2
- package/dist/modules/customers/components/detail/CompanyKpiBar.js +5 -4
- package/dist/modules/customers/components/detail/CompanyKpiBar.js.map +2 -2
- package/dist/modules/customers/components/detail/CompanyPeopleSection.js +3 -1
- package/dist/modules/customers/components/detail/CompanyPeopleSection.js.map +2 -2
- package/dist/modules/customers/components/detail/DealForm.js +19 -7
- package/dist/modules/customers/components/detail/DealForm.js.map +2 -2
- package/dist/modules/customers/components/detail/DealsSection.js +6 -5
- package/dist/modules/customers/components/detail/DealsSection.js.map +2 -2
- package/dist/modules/customers/components/detail/EntityTagsDialog.js +35 -11
- package/dist/modules/customers/components/detail/EntityTagsDialog.js.map +2 -2
- package/dist/modules/customers/components/detail/PersonCompaniesSection.js +3 -1
- package/dist/modules/customers/components/detail/PersonCompaniesSection.js.map +2 -2
- package/dist/modules/customers/components/detail/hooks/usePersonTasks.js +19 -13
- package/dist/modules/customers/components/detail/hooks/usePersonTasks.js.map +2 -2
- package/dist/modules/customers/components/detail/schedule/ParticipantsField.js +4 -4
- package/dist/modules/customers/components/detail/schedule/ParticipantsField.js.map +2 -2
- package/dist/modules/customers/components/detail/schedule/useScheduleFormState.js +3 -3
- package/dist/modules/customers/components/detail/schedule/useScheduleFormState.js.map +2 -2
- package/dist/modules/customers/components/detail/utils.js +5 -5
- package/dist/modules/customers/components/detail/utils.js.map +2 -2
- package/dist/modules/customers/data/entities.js.map +1 -1
- package/dist/modules/customers/data/validators.js +32 -3
- package/dist/modules/customers/data/validators.js.map +2 -2
- package/dist/modules/customers/events.js +5 -0
- package/dist/modules/customers/events.js.map +2 -2
- package/dist/modules/customers/lib/calendar/conflicts.js +8 -2
- package/dist/modules/customers/lib/calendar/conflicts.js.map +2 -2
- package/dist/modules/customers/lib/calendar/editorPayload.js +3 -2
- package/dist/modules/customers/lib/calendar/editorPayload.js.map +2 -2
- package/dist/modules/customers/lib/calendar/mapItem.js +8 -3
- package/dist/modules/customers/lib/calendar/mapItem.js.map +2 -2
- package/dist/modules/customers/lib/calendar/participantIdentity.js +9 -0
- package/dist/modules/customers/lib/calendar/participantIdentity.js.map +7 -0
- package/dist/modules/customers/lib/closureStage.js +45 -0
- package/dist/modules/customers/lib/closureStage.js.map +7 -0
- package/dist/modules/customers/lib/dealStatus.js +30 -0
- package/dist/modules/customers/lib/dealStatus.js.map +2 -2
- package/dist/modules/customers/lib/detailReadAccess.js +12 -0
- package/dist/modules/customers/lib/detailReadAccess.js.map +7 -0
- package/dist/modules/customers/lib/findPeopleByAddresses.js +1 -0
- package/dist/modules/customers/lib/findPeopleByAddresses.js.map +2 -2
- package/dist/modules/customers/lib/personCompanies.js +100 -2
- package/dist/modules/customers/lib/personCompanies.js.map +2 -2
- package/dist/modules/customers/lib/todoCompatibility.js +12 -0
- package/dist/modules/customers/lib/todoCompatibility.js.map +2 -2
- package/dist/modules/dashboards/lib/widgetScope.js +6 -1
- package/dist/modules/dashboards/lib/widgetScope.js.map +2 -2
- package/dist/modules/data_sync/backend/data-sync/page.js +3 -1
- package/dist/modules/data_sync/backend/data-sync/page.js.map +2 -2
- package/dist/modules/data_sync/lib/abandoned-run.js +19 -0
- package/dist/modules/data_sync/lib/abandoned-run.js.map +7 -0
- package/dist/modules/data_sync/lib/sync-engine.js +72 -6
- package/dist/modules/data_sync/lib/sync-engine.js.map +2 -2
- package/dist/modules/data_sync/workers/sync-export.js +5 -1
- package/dist/modules/data_sync/workers/sync-export.js.map +2 -2
- package/dist/modules/data_sync/workers/sync-import.js +5 -1
- package/dist/modules/data_sync/workers/sync-import.js.map +2 -2
- package/dist/modules/devices/acl.js +9 -1
- package/dist/modules/devices/acl.js.map +2 -2
- package/dist/modules/devices/api/admin/devices/[id]/route.js +5 -32
- package/dist/modules/devices/api/admin/devices/[id]/route.js.map +2 -2
- package/dist/modules/devices/api/admin/devices/route.js +14 -1
- package/dist/modules/devices/api/admin/devices/route.js.map +2 -2
- package/dist/modules/devices/api/deviceList.js +99 -12
- package/dist/modules/devices/api/deviceList.js.map +2 -2
- package/dist/modules/devices/api/deviceSerialization.js +46 -0
- package/dist/modules/devices/api/deviceSerialization.js.map +7 -0
- package/dist/modules/devices/api/openapi.js +3 -1
- package/dist/modules/devices/api/openapi.js.map +2 -2
- package/dist/modules/devices/api/route.js +9 -1
- package/dist/modules/devices/api/route.js.map +2 -2
- package/dist/modules/devices/backend/devices/[id]/page.js +10 -26
- package/dist/modules/devices/backend/devices/[id]/page.js.map +2 -2
- package/dist/modules/devices/backend/devices/create/page.js +17 -1
- package/dist/modules/devices/backend/devices/create/page.js.map +2 -2
- package/dist/modules/devices/backend/devices/page.js +28 -34
- package/dist/modules/devices/backend/devices/page.js.map +2 -2
- package/dist/modules/devices/backend/devices/useDeviceUserLabels.js +34 -0
- package/dist/modules/devices/backend/devices/useDeviceUserLabels.js.map +7 -0
- package/dist/modules/devices/backend/devices/userOptions.js +52 -0
- package/dist/modules/devices/backend/devices/userOptions.js.map +7 -0
- package/dist/modules/devices/setup.js +6 -2
- package/dist/modules/devices/setup.js.map +2 -2
- package/dist/modules/directory/backend/directory/organizations/page.js +2 -1
- package/dist/modules/directory/backend/directory/organizations/page.js.map +2 -2
- package/dist/modules/directory/backend/directory/tenants/page.js +2 -1
- package/dist/modules/directory/backend/directory/tenants/page.js.map +2 -2
- package/dist/modules/directory/di.js +9 -0
- package/dist/modules/directory/di.js.map +2 -2
- package/dist/modules/directory/services/organizationHierarchyService.js +29 -0
- package/dist/modules/directory/services/organizationHierarchyService.js.map +7 -0
- package/dist/modules/directory/services/organizationScopeService.js +56 -0
- package/dist/modules/directory/services/organizationScopeService.js.map +7 -0
- package/dist/modules/directory/utils/organizationScope.js.map +2 -2
- package/dist/modules/entities/api/records.js +4 -2
- package/dist/modules/entities/api/records.js.map +2 -2
- package/dist/modules/entities/backend/entities/user/[entityId]/records/page.js +3 -1
- package/dist/modules/entities/backend/entities/user/[entityId]/records/page.js.map +2 -2
- package/dist/modules/eudr/api/plots/route.js +5 -1
- package/dist/modules/eudr/api/plots/route.js.map +2 -2
- package/dist/modules/eudr/backend/eudr/evidence-submissions/page.js +3 -0
- package/dist/modules/eudr/backend/eudr/evidence-submissions/page.js.map +2 -2
- package/dist/modules/eudr/backend/eudr/plots/page.js +3 -0
- package/dist/modules/eudr/backend/eudr/plots/page.js.map +2 -2
- package/dist/modules/eudr/backend/eudr/product-mappings/page.js +3 -0
- package/dist/modules/eudr/backend/eudr/product-mappings/page.js.map +2 -2
- package/dist/modules/eudr/backend/eudr/risk-assessments/page.js +3 -0
- package/dist/modules/eudr/backend/eudr/risk-assessments/page.js.map +2 -2
- package/dist/modules/eudr/backend/eudr/statements/page.js +3 -0
- package/dist/modules/eudr/backend/eudr/statements/page.js.map +2 -2
- package/dist/modules/eudr/commands/statements.js +0 -3
- package/dist/modules/eudr/commands/statements.js.map +2 -2
- package/dist/modules/eudr/data/validators.js +4 -1
- package/dist/modules/eudr/data/validators.js.map +2 -2
- package/dist/modules/feature_toggles/components/FeatureTogglesTable.js +1 -0
- package/dist/modules/feature_toggles/components/FeatureTogglesTable.js.map +2 -2
- package/dist/modules/feature_toggles/components/OverridesTable.js +1 -0
- package/dist/modules/feature_toggles/components/OverridesTable.js.map +2 -2
- package/dist/modules/feature_toggles/defaults.json +0 -8
- package/dist/modules/inbox_ops/backend/inbox-ops/log/page.js +3 -0
- package/dist/modules/inbox_ops/backend/inbox-ops/log/page.js.map +2 -2
- package/dist/modules/inbox_ops/backend/inbox-ops/page.js +3 -0
- package/dist/modules/inbox_ops/backend/inbox-ops/page.js.map +2 -2
- package/dist/modules/inbox_ops/lib/htmlToPlainText.js +1 -12
- package/dist/modules/inbox_ops/lib/htmlToPlainText.js.map +2 -2
- package/dist/modules/integrations/api/[id]/credentials/route.js +6 -1
- package/dist/modules/integrations/api/[id]/credentials/route.js.map +2 -2
- package/dist/modules/integrations/backend/integrations/[id]/page.js +38 -29
- package/dist/modules/integrations/backend/integrations/[id]/page.js.map +2 -2
- package/dist/modules/integrations/backend/integrations/bundle/[id]/page.js +36 -9
- package/dist/modules/integrations/backend/integrations/bundle/[id]/page.js.map +2 -2
- package/dist/modules/integrations/backend/integrations/credential-secret-fields.js +55 -0
- package/dist/modules/integrations/backend/integrations/credential-secret-fields.js.map +7 -0
- package/dist/modules/integrations/data/validators.js +11 -3
- package/dist/modules/integrations/data/validators.js.map +2 -2
- package/dist/modules/integrations/lib/credentials-masking.js +6 -2
- package/dist/modules/integrations/lib/credentials-masking.js.map +2 -2
- package/dist/modules/messages/api/[id]/route.js +29 -1
- package/dist/modules/messages/api/[id]/route.js.map +2 -2
- package/dist/modules/messages/api/openapi.js +17 -2
- package/dist/modules/messages/api/openapi.js.map +2 -2
- package/dist/modules/messages/api/route.js +17 -2
- package/dist/modules/messages/api/route.js.map +2 -2
- package/dist/modules/messages/components/MessagesInboxPageClient.js +7 -14
- package/dist/modules/messages/components/MessagesInboxPageClient.js.map +2 -2
- package/dist/modules/messages/components/inboxFilters.js +15 -0
- package/dist/modules/messages/components/inboxFilters.js.map +2 -2
- package/dist/modules/messages/components/message-detail/hooks/useMessageDetailsConversation.js +11 -1
- package/dist/modules/messages/components/message-detail/hooks/useMessageDetailsConversation.js.map +2 -2
- package/dist/modules/messages/components/message-detail/panels/MessageHeader.js +3 -1
- package/dist/modules/messages/components/message-detail/panels/MessageHeader.js.map +2 -2
- package/dist/modules/messages/components/message-detail/panels/thread-panel.js +2 -1
- package/dist/modules/messages/components/message-detail/panels/thread-panel.js.map +2 -2
- package/dist/modules/messages/components/messageListLabels.js +14 -2
- package/dist/modules/messages/components/messageListLabels.js.map +2 -2
- package/dist/modules/messages/data/validators.js +18 -4
- package/dist/modules/messages/data/validators.js.map +2 -2
- package/dist/modules/messages/lib/channel-sender-identity.js +26 -0
- package/dist/modules/messages/lib/channel-sender-identity.js.map +7 -0
- package/dist/modules/messages/lib/composeSourceChannelType.js +41 -0
- package/dist/modules/messages/lib/composeSourceChannelType.js.map +7 -0
- package/dist/modules/payment_gateways/api/webhook/[provider]/route.js +4 -6
- package/dist/modules/payment_gateways/api/webhook/[provider]/route.js.map +2 -2
- package/dist/modules/payment_gateways/backend/payment-gateways/page.js +3 -1
- package/dist/modules/payment_gateways/backend/payment-gateways/page.js.map +2 -2
- package/dist/modules/payment_gateways/lib/webhook-processor.js +7 -0
- package/dist/modules/payment_gateways/lib/webhook-processor.js.map +2 -2
- package/dist/modules/planner/backend/planner/availability-rulesets/page.js +3 -0
- package/dist/modules/planner/backend/planner/availability-rulesets/page.js.map +2 -2
- package/dist/modules/push_notifications/backend/push_notifications/page.js +3 -1
- package/dist/modules/push_notifications/backend/push_notifications/page.js.map +2 -2
- package/dist/modules/push_notifications/backend/push_notifications/send/page.js +1 -1
- package/dist/modules/push_notifications/backend/push_notifications/send/page.js.map +2 -2
- package/dist/modules/query_index/lib/engine.js +150 -51
- package/dist/modules/query_index/lib/engine.js.map +2 -2
- package/dist/modules/query_index/lib/search-entity-policy.js +14 -0
- package/dist/modules/query_index/lib/search-entity-policy.js.map +7 -0
- package/dist/modules/query_index/lib/search-tokens.js +79 -3
- package/dist/modules/query_index/lib/search-tokens.js.map +2 -2
- package/dist/modules/resources/backend/resources/resource-types/page.js +3 -1
- package/dist/modules/resources/backend/resources/resource-types/page.js.map +2 -2
- package/dist/modules/resources/backend/resources/resources/page.js +3 -1
- package/dist/modules/resources/backend/resources/resources/page.js.map +2 -2
- package/dist/modules/sales/backend/sales/channels/offers/page.js +3 -0
- package/dist/modules/sales/backend/sales/channels/offers/page.js.map +2 -2
- package/dist/modules/sales/backend/sales/channels/page.js +3 -0
- package/dist/modules/sales/backend/sales/channels/page.js.map +2 -2
- package/dist/modules/sales/backend/sales/documents/[id]/page.js +10 -8
- package/dist/modules/sales/backend/sales/documents/[id]/page.js.map +2 -2
- package/dist/modules/sales/commands/documents.js +79 -1
- package/dist/modules/sales/commands/documents.js.map +2 -2
- package/dist/modules/sales/components/PriceWithCurrency.js +7 -5
- package/dist/modules/sales/components/PriceWithCurrency.js.map +2 -2
- package/dist/modules/sales/components/channels/SalesChannelOffersPanel.js +3 -0
- package/dist/modules/sales/components/channels/SalesChannelOffersPanel.js.map +2 -2
- package/dist/modules/sales/components/documents/AdjustmentsSection.js +8 -6
- package/dist/modules/sales/components/documents/AdjustmentsSection.js.map +2 -2
- package/dist/modules/sales/components/documents/ItemsSection.js +51 -22
- package/dist/modules/sales/components/documents/ItemsSection.js.map +2 -2
- package/dist/modules/sales/components/documents/LineItemDialog.js +83 -18
- package/dist/modules/sales/components/documents/LineItemDialog.js.map +2 -2
- package/dist/modules/sales/components/documents/PaymentsSection.js +23 -9
- package/dist/modules/sales/components/documents/PaymentsSection.js.map +2 -2
- package/dist/modules/sales/components/documents/ReturnsSection.js +8 -6
- package/dist/modules/sales/components/documents/ReturnsSection.js.map +2 -2
- package/dist/modules/sales/components/documents/SalesDocumentsTable.js +68 -14
- package/dist/modules/sales/components/documents/SalesDocumentsTable.js.map +2 -2
- package/dist/modules/sales/components/documents/SalesOrderDraftLines.js +6 -5
- package/dist/modules/sales/components/documents/SalesOrderDraftLines.js.map +2 -2
- package/dist/modules/sales/components/documents/ShipmentDialog.js +5 -4
- package/dist/modules/sales/components/documents/ShipmentDialog.js.map +2 -2
- package/dist/modules/sales/components/documents/ShipmentsSection.js +8 -6
- package/dist/modules/sales/components/documents/ShipmentsSection.js.map +2 -2
- package/dist/modules/sales/components/documents/lineItemShipmentLock.js +54 -0
- package/dist/modules/sales/components/documents/lineItemShipmentLock.js.map +7 -0
- package/dist/modules/sales/components/documents/lineItemUtils.js +2 -2
- package/dist/modules/sales/components/documents/lineItemUtils.js.map +2 -2
- package/dist/modules/sales/data/entities.js +8 -2
- package/dist/modules/sales/data/entities.js.map +2 -2
- package/dist/modules/sales/extension-points.js +1 -0
- package/dist/modules/sales/extension-points.js.map +2 -2
- package/dist/modules/sales/lib/salesChannelsToggleSeed.js +33 -0
- package/dist/modules/sales/lib/salesChannelsToggleSeed.js.map +7 -0
- package/dist/modules/sales/migrations/Migration20260821120000_sales_notes_addresses_context_idx.js +32 -0
- package/dist/modules/sales/migrations/Migration20260821120000_sales_notes_addresses_context_idx.js.map +7 -0
- package/dist/modules/sales/setup.js +2 -0
- package/dist/modules/sales/setup.js.map +2 -2
- package/dist/modules/sales/widgets/injection/payment-gateway-status-column/widget.js +10 -2
- package/dist/modules/sales/widgets/injection/payment-gateway-status-column/widget.js.map +2 -2
- package/dist/modules/sales/widgets/injection-table.js +4 -7
- package/dist/modules/sales/widgets/injection-table.js.map +2 -2
- package/dist/modules/staff/backend/staff/leave-requests/page.js +3 -0
- package/dist/modules/staff/backend/staff/leave-requests/page.js.map +2 -2
- package/dist/modules/staff/backend/staff/my-leave-requests/page.js +3 -0
- package/dist/modules/staff/backend/staff/my-leave-requests/page.js.map +2 -2
- package/dist/modules/staff/backend/staff/team-members/page.js +3 -0
- package/dist/modules/staff/backend/staff/team-members/page.js.map +2 -2
- package/dist/modules/staff/backend/staff/team-roles/page.js +3 -0
- package/dist/modules/staff/backend/staff/team-roles/page.js.map +2 -2
- package/dist/modules/staff/backend/staff/teams/[id]/edit/page.js +3 -0
- package/dist/modules/staff/backend/staff/teams/[id]/edit/page.js.map +2 -2
- package/dist/modules/staff/backend/staff/teams/page.js +3 -0
- package/dist/modules/staff/backend/staff/teams/page.js.map +2 -2
- package/dist/modules/staff/backend/staff/timesheets/page.js +29 -18
- package/dist/modules/staff/backend/staff/timesheets/page.js.map +2 -2
- package/dist/modules/staff/backend/staff/timesheets/projects/page.js +3 -0
- package/dist/modules/staff/backend/staff/timesheets/projects/page.js.map +2 -2
- package/dist/modules/sync_excel/lib/adapters/customers.js +1 -0
- package/dist/modules/sync_excel/lib/adapters/customers.js.map +2 -2
- package/dist/modules/warranty_claims/api/assignees/route.js +3 -3
- package/dist/modules/warranty_claims/api/assignees/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/portal/attachments/route.js +2 -2
- package/dist/modules/warranty_claims/api/portal/attachments/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/portal/claims/route.js +12 -9
- package/dist/modules/warranty_claims/api/portal/claims/route.js.map +2 -2
- package/dist/modules/warranty_claims/backend/page.js +3 -0
- package/dist/modules/warranty_claims/backend/page.js.map +2 -2
- package/dist/modules/warranty_claims/backend/warranty_claims/registrations/page.js +3 -0
- package/dist/modules/warranty_claims/backend/warranty_claims/registrations/page.js.map +2 -2
- package/dist/modules/warranty_claims/backend/warranty_claims/troubleshooting-guides/page.js +3 -0
- package/dist/modules/warranty_claims/backend/warranty_claims/troubleshooting-guides/page.js.map +2 -2
- package/dist/modules/warranty_claims/backend/warranty_claims/vendor-policies/page.js +3 -0
- package/dist/modules/warranty_claims/backend/warranty_claims/vendor-policies/page.js.map +2 -2
- package/dist/modules/warranty_claims/frontend/[orgSlug]/portal/claims/page.js +3 -0
- package/dist/modules/warranty_claims/frontend/[orgSlug]/portal/claims/page.js.map +2 -2
- package/dist/modules/wms/api/warehouseSearch.js +21 -0
- package/dist/modules/wms/api/warehouseSearch.js.map +7 -0
- package/dist/modules/wms/api/warehouses/route.js +13 -9
- package/dist/modules/wms/api/warehouses/route.js.map +2 -2
- package/dist/modules/wms/api/zones/route.js +15 -2
- package/dist/modules/wms/api/zones/route.js.map +2 -2
- package/dist/modules/wms/commands/configuration.js +121 -9
- package/dist/modules/wms/commands/configuration.js.map +2 -2
- package/dist/modules/wms/commands/inventory-actions.js +3 -0
- package/dist/modules/wms/commands/inventory-actions.js.map +2 -2
- package/dist/modules/wms/commands/shared.js +9 -1
- package/dist/modules/wms/commands/shared.js.map +2 -2
- package/dist/modules/wms/components/backend/WarehouseEditDialog.js +158 -0
- package/dist/modules/wms/components/backend/WarehouseEditDialog.js.map +7 -0
- package/dist/modules/wms/components/backend/WmsConfigurationPage.js +76 -111
- package/dist/modules/wms/components/backend/WmsConfigurationPage.js.map +3 -3
- package/dist/modules/wms/components/backend/WmsInventoryConsolePage.js +1 -0
- package/dist/modules/wms/components/backend/WmsInventoryConsolePage.js.map +2 -2
- package/dist/modules/wms/components/backend/WmsLotsListPage.js +1 -0
- package/dist/modules/wms/components/backend/WmsLotsListPage.js.map +2 -2
- package/dist/modules/wms/components/backend/inventoryMutationLoaders.js +27 -20
- package/dist/modules/wms/components/backend/inventoryMutationLoaders.js.map +2 -2
- package/dist/modules/wms/components/backend/warehouseFormOptions.js +66 -0
- package/dist/modules/wms/components/backend/warehouseFormOptions.js.map +7 -0
- package/dist/modules/workflows/backend/definitions/page.js +3 -1
- package/dist/modules/workflows/backend/definitions/page.js.map +2 -2
- package/dist/modules/workflows/backend/events/page.js +3 -1
- package/dist/modules/workflows/backend/events/page.js.map +2 -2
- package/dist/modules/workflows/backend/instances/page.js +3 -1
- package/dist/modules/workflows/backend/instances/page.js.map +2 -2
- package/dist/modules/workflows/backend/tasks/page.js +3 -1
- package/dist/modules/workflows/backend/tasks/page.js.map +2 -2
- package/dist/modules/workflows/lib/activity-executor.js +4 -0
- package/dist/modules/workflows/lib/activity-executor.js.map +2 -2
- package/package.json +9 -10
- package/src/helpers/integration/api.ts +33 -1
- package/src/helpers/integration/communicationChannelsFixtures.ts +69 -1
- package/src/modules/api_keys/api/keys/route.ts +3 -1
- package/src/modules/api_keys/backend/api-keys/page.tsx +4 -1
- package/src/modules/api_keys/di.ts +7 -0
- package/src/modules/api_keys/services/principalService.ts +33 -0
- package/src/modules/attachments/AGENTS.md +15 -0
- package/src/modules/attachments/api/route.ts +2 -2
- package/src/modules/attachments/components/AttachmentLibrary.tsx +3 -0
- package/src/modules/attachments/di.ts +16 -0
- package/src/modules/attachments/i18n/de.json +5 -0
- package/src/modules/attachments/i18n/en.json +5 -0
- package/src/modules/attachments/i18n/es.json +5 -0
- package/src/modules/attachments/i18n/ko.json +5 -0
- package/src/modules/attachments/i18n/pl.json +5 -0
- package/src/modules/attachments/index.ts +10 -0
- package/src/modules/attachments/lib/attachment-service.ts +468 -0
- package/src/modules/attachments/lib/scoped-upload-service.ts +46 -0
- package/src/modules/attachments/lib/upload-limits.ts +6 -2
- package/src/modules/audit_logs/backend/audit-logs/page.tsx +8 -0
- package/src/modules/audit_logs/lib/display-helpers.tsx +42 -32
- package/src/modules/audit_logs/services/actionLogService.ts +74 -11
- package/src/modules/auth/acl.ts +30 -6
- package/src/modules/auth/api/feature-check.ts +10 -3
- package/src/modules/auth/api/login.ts +9 -1
- package/src/modules/auth/api/reset/validate.ts +57 -0
- package/src/modules/auth/api/roles/acl/route.ts +42 -39
- package/src/modules/auth/api/session/refresh.ts +32 -1
- package/src/modules/auth/api/users/acl/route.ts +177 -53
- package/src/modules/auth/api/users/route.ts +18 -5
- package/src/modules/auth/backend/roles/[id]/edit/page.meta.ts +5 -2
- package/src/modules/auth/backend/roles/page.tsx +4 -2
- package/src/modules/auth/backend/users/[id]/edit/page.meta.ts +5 -2
- package/src/modules/auth/backend/users/page.tsx +3 -2
- package/src/modules/auth/commands/acl.ts +495 -0
- package/src/modules/auth/components/AclEditor.tsx +63 -33
- package/src/modules/auth/data/entities.ts +4 -1
- package/src/modules/auth/data/validators.ts +5 -0
- package/src/modules/auth/di.ts +46 -16
- package/src/modules/auth/frontend/reset/[token]/page.tsx +74 -0
- package/src/modules/auth/i18n/de.json +389 -0
- package/src/modules/auth/i18n/en.json +389 -0
- package/src/modules/auth/i18n/es.json +389 -0
- package/src/modules/auth/i18n/ko.json +389 -0
- package/src/modules/auth/i18n/pl.json +391 -2
- package/src/modules/auth/lib/grantChecks.ts +6 -0
- package/src/modules/auth/lib/userIdFilter.ts +31 -0
- package/src/modules/auth/services/authService.ts +18 -2
- package/src/modules/auth/services/principalService.ts +266 -0
- package/src/modules/auth/services/rbacService.ts +110 -24
- package/src/modules/auth/services/roleOrganizationScope.ts +70 -0
- package/src/modules/business_rules/backend/logs/page.tsx +4 -1
- package/src/modules/business_rules/backend/rules/page.tsx +4 -1
- package/src/modules/business_rules/backend/sets/page.tsx +4 -1
- package/src/modules/business_rules/lib/expression-evaluator.ts +9 -9
- package/src/modules/business_rules/lib/rule-evaluator.ts +10 -9
- package/src/modules/catalog/ai-agents.ts +1 -1
- package/src/modules/catalog/ai-tools/merchandising-pack.ts +2 -2
- package/src/modules/catalog/backend/catalog/products/MerchandisingAssistantSheet.tsx +12 -5
- package/src/modules/catalog/components/categories/CategoriesDataTable.tsx +3 -0
- package/src/modules/catalog/components/products/ProductsDataTable.tsx +7 -0
- package/src/modules/catalog/i18n/de.json +1 -0
- package/src/modules/catalog/i18n/en.json +1 -0
- package/src/modules/catalog/i18n/es.json +1 -0
- package/src/modules/catalog/i18n/ko.json +1 -0
- package/src/modules/catalog/i18n/pl.json +1 -0
- package/src/modules/catalog/widgets/injection/merchandising-assistant-trigger/widget.client.tsx +4 -0
- package/src/modules/communication_channels/api/post/channels/[id]/test-send/route.ts +17 -1
- package/src/modules/communication_channels/api/post/test-seed/route.ts +155 -16
- package/src/modules/communication_channels/backend/communication_channels/channels/page.tsx +4 -0
- package/src/modules/communication_channels/commands/ingest-inbound-message.ts +38 -1
- package/src/modules/communication_channels/di.ts +7 -0
- package/src/modules/communication_channels/lib/adapter.ts +14 -0
- package/src/modules/communication_channels/lib/email-capabilities.ts +4 -0
- package/src/modules/communication_channels/lib/outbound-recipient.ts +71 -0
- package/src/modules/communication_channels/lib/resolve-channel-type.ts +99 -0
- package/src/modules/communication_channels/lib/test-seed.ts +64 -4
- package/src/modules/configs/i18n/de.json +2 -0
- package/src/modules/configs/i18n/en.json +2 -0
- package/src/modules/configs/i18n/es.json +2 -0
- package/src/modules/configs/i18n/ko.json +2 -0
- package/src/modules/configs/i18n/pl.json +2 -0
- package/src/modules/configs/lib/system-status.ts +9 -0
- package/src/modules/currencies/AGENTS.md +3 -0
- package/src/modules/currencies/backend/currencies/page.tsx +4 -1
- package/src/modules/currencies/backend/exchange-rates/page.tsx +4 -1
- package/src/modules/currencies/services/README.md +45 -0
- package/src/modules/currencies/services/rateFetchingService.ts +6 -1
- package/src/modules/customer_accounts/api/admin/domain-mappings.ts +7 -0
- package/src/modules/customer_accounts/backend/customer_accounts/roles/page.tsx +4 -1
- package/src/modules/customer_accounts/backend/customer_accounts/users/PortalUsersPageClient.tsx +4 -1
- package/src/modules/customer_accounts/services/domainMappingService.ts +10 -0
- package/src/modules/customers/ai-tools/companies-pack.ts +4 -1
- package/src/modules/customers/ai-tools/deal-analyzer-pack.ts +17 -2
- package/src/modules/customers/ai-tools/deals-pack.ts +66 -1
- package/src/modules/customers/ai-tools/people-pack.ts +4 -1
- package/src/modules/customers/api/activities/route.ts +45 -0
- package/src/modules/customers/api/companies/[id]/people/route.ts +24 -73
- package/src/modules/customers/api/companies/[id]/route.ts +24 -107
- package/src/modules/customers/api/dashboard/widgets/customer-todos/route.ts +16 -8
- package/src/modules/customers/api/deals/[id]/companies/route.ts +4 -1
- package/src/modules/customers/api/deals/[id]/people/route.ts +4 -1
- package/src/modules/customers/api/deals/[id]/route.ts +17 -28
- package/src/modules/customers/api/deals/[id]/stats/route.ts +10 -25
- package/src/modules/customers/api/deals/aggregate/route.ts +17 -9
- package/src/modules/customers/api/deals/map/route.ts +4 -4
- package/src/modules/customers/api/deals/route.ts +50 -1
- package/src/modules/customers/api/entity-roles-factory.ts +8 -4
- package/src/modules/customers/api/interactions/route.ts +8 -2
- package/src/modules/customers/api/interactions/tasks/route.ts +24 -1
- package/src/modules/customers/api/people/[id]/companies/[linkId]/route.ts +12 -4
- package/src/modules/customers/api/people/[id]/companies/context.ts +4 -1
- package/src/modules/customers/api/people/[id]/companies/enriched/route.ts +4 -1
- package/src/modules/customers/api/people/[id]/route.ts +16 -11
- package/src/modules/customers/api/people/check-phone/route.ts +75 -10
- package/src/modules/customers/api/todos/route.ts +27 -2
- package/src/modules/customers/backend/customers/companies/page.tsx +5 -2
- package/src/modules/customers/backend/customers/deals/[id]/page.tsx +7 -2
- package/src/modules/customers/backend/customers/deals/page.tsx +8 -2
- package/src/modules/customers/backend/customers/deals/pipeline/components/Lane.tsx +5 -14
- package/src/modules/customers/backend/customers/deals/pipeline/components/StatusFilterPopover.tsx +109 -27
- package/src/modules/customers/backend/customers/deals/pipeline/components/toneClasses.ts +21 -0
- package/src/modules/customers/backend/customers/people/page.tsx +5 -2
- package/src/modules/customers/backend/hooks/useEmailDuplicateCheck.ts +12 -4
- package/src/modules/customers/commands/deals.ts +34 -59
- package/src/modules/customers/commands/interactions.ts +1 -1
- package/src/modules/customers/commands/personCompanyLinks.ts +224 -8
- package/src/modules/customers/components/CustomerTodosTable.tsx +2 -0
- package/src/modules/customers/components/DealsKpiStrip.tsx +7 -7
- package/src/modules/customers/components/calendar/AgendaList.tsx +3 -3
- package/src/modules/customers/components/calendar/CalendarEventEditor.tsx +1 -1
- package/src/modules/customers/components/calendar/CalendarScreen.tsx +1 -1
- package/src/modules/customers/components/calendar/CalendarSettingsModal.tsx +1 -1
- package/src/modules/customers/components/calendar/EventBlock.tsx +2 -2
- package/src/modules/customers/components/calendar/EventPeekPopover.tsx +4 -2
- package/src/modules/customers/components/calendar/editor/PeopleField.tsx +61 -5
- package/src/modules/customers/components/calendar/editor/hooks.ts +7 -1
- package/src/modules/customers/components/calendar/types.ts +2 -2
- package/src/modules/customers/components/detail/CompanyKpiBar.tsx +5 -4
- package/src/modules/customers/components/detail/CompanyPeopleSection.tsx +8 -1
- package/src/modules/customers/components/detail/DealForm.tsx +22 -2
- package/src/modules/customers/components/detail/DealsSection.tsx +11 -5
- package/src/modules/customers/components/detail/EntityTagsDialog.tsx +58 -12
- package/src/modules/customers/components/detail/PersonCompaniesSection.tsx +8 -1
- package/src/modules/customers/components/detail/hooks/usePersonTasks.ts +23 -14
- package/src/modules/customers/components/detail/schedule/ParticipantsField.tsx +5 -5
- package/src/modules/customers/components/detail/schedule/useScheduleFormState.ts +5 -5
- package/src/modules/customers/components/detail/types.ts +1 -1
- package/src/modules/customers/components/detail/utils.ts +15 -5
- package/src/modules/customers/data/entities.ts +1 -1
- package/src/modules/customers/data/validators.ts +50 -9
- package/src/modules/customers/events.ts +5 -0
- package/src/modules/customers/i18n/de.json +9 -1
- package/src/modules/customers/i18n/en.json +9 -1
- package/src/modules/customers/i18n/es.json +9 -1
- package/src/modules/customers/i18n/ko.json +9 -1
- package/src/modules/customers/i18n/pl.json +9 -1
- package/src/modules/customers/lib/calendar/conflicts.ts +10 -2
- package/src/modules/customers/lib/calendar/editorPayload.ts +4 -3
- package/src/modules/customers/lib/calendar/mapItem.ts +8 -3
- package/src/modules/customers/lib/calendar/participantIdentity.ts +17 -0
- package/src/modules/customers/lib/closureStage.ts +76 -0
- package/src/modules/customers/lib/dealStatus.ts +42 -0
- package/src/modules/customers/lib/detailReadAccess.ts +35 -0
- package/src/modules/customers/lib/findPeopleByAddresses.ts +8 -6
- package/src/modules/customers/lib/personCompanies.ts +136 -1
- package/src/modules/customers/lib/todoCompatibility.ts +19 -1
- package/src/modules/dashboards/i18n/es.json +4 -4
- package/src/modules/dashboards/i18n/pl.json +7 -7
- package/src/modules/dashboards/lib/widgetScope.ts +6 -0
- package/src/modules/data_sync/AGENTS.md +1 -1
- package/src/modules/data_sync/backend/data-sync/page.tsx +4 -1
- package/src/modules/data_sync/lib/abandoned-run.ts +56 -0
- package/src/modules/data_sync/lib/adapter.ts +36 -0
- package/src/modules/data_sync/lib/sync-engine.ts +113 -5
- package/src/modules/data_sync/workers/sync-export.ts +4 -0
- package/src/modules/data_sync/workers/sync-import.ts +4 -0
- package/src/modules/devices/AGENTS.md +20 -1
- package/src/modules/devices/acl.ts +9 -1
- package/src/modules/devices/api/admin/devices/[id]/route.ts +5 -34
- package/src/modules/devices/api/admin/devices/route.ts +14 -1
- package/src/modules/devices/api/deviceList.ts +135 -11
- package/src/modules/devices/api/deviceSerialization.ts +50 -0
- package/src/modules/devices/api/openapi.ts +9 -1
- package/src/modules/devices/api/route.ts +9 -1
- package/src/modules/devices/backend/devices/[id]/page.tsx +18 -32
- package/src/modules/devices/backend/devices/create/page.tsx +17 -1
- package/src/modules/devices/backend/devices/page.tsx +44 -45
- package/src/modules/devices/backend/devices/useDeviceUserLabels.ts +45 -0
- package/src/modules/devices/backend/devices/userOptions.ts +99 -0
- package/src/modules/devices/i18n/de.json +3 -2
- package/src/modules/devices/i18n/en.json +3 -2
- package/src/modules/devices/i18n/es.json +3 -2
- package/src/modules/devices/i18n/ko.json +3 -2
- package/src/modules/devices/i18n/pl.json +3 -2
- package/src/modules/devices/setup.ts +6 -2
- package/src/modules/directory/backend/directory/organizations/page.tsx +3 -1
- package/src/modules/directory/backend/directory/tenants/page.tsx +3 -1
- package/src/modules/directory/di.ts +14 -2
- package/src/modules/directory/services/organizationHierarchyService.ts +34 -0
- package/src/modules/directory/services/organizationScopeService.ts +85 -0
- package/src/modules/directory/utils/organizationScope.ts +4 -15
- package/src/modules/entities/api/records.ts +2 -0
- package/src/modules/entities/backend/entities/user/[entityId]/records/page.tsx +4 -1
- package/src/modules/eudr/api/plots/route.ts +4 -0
- package/src/modules/eudr/backend/eudr/evidence-submissions/page.tsx +4 -0
- package/src/modules/eudr/backend/eudr/plots/page.tsx +4 -0
- package/src/modules/eudr/backend/eudr/product-mappings/page.tsx +4 -0
- package/src/modules/eudr/backend/eudr/risk-assessments/page.tsx +4 -0
- package/src/modules/eudr/backend/eudr/statements/page.tsx +4 -0
- package/src/modules/eudr/commands/statements.ts +0 -3
- package/src/modules/eudr/data/validators.ts +16 -1
- package/src/modules/feature_toggles/components/FeatureTogglesTable.tsx +2 -1
- package/src/modules/feature_toggles/components/OverridesTable.tsx +2 -1
- package/src/modules/feature_toggles/defaults.json +0 -8
- package/src/modules/inbox_ops/backend/inbox-ops/log/page.tsx +4 -0
- package/src/modules/inbox_ops/backend/inbox-ops/page.tsx +4 -0
- package/src/modules/inbox_ops/lib/htmlToPlainText.ts +1 -17
- package/src/modules/integrations/api/[id]/credentials/route.ts +6 -4
- package/src/modules/integrations/backend/integrations/[id]/page.tsx +61 -31
- package/src/modules/integrations/backend/integrations/bundle/[id]/page.tsx +44 -9
- package/src/modules/integrations/backend/integrations/credential-secret-fields.ts +84 -0
- package/src/modules/integrations/data/validators.ts +12 -2
- package/src/modules/integrations/i18n/de.json +1 -0
- package/src/modules/integrations/i18n/en.json +1 -0
- package/src/modules/integrations/i18n/es.json +1 -0
- package/src/modules/integrations/i18n/ko.json +1 -0
- package/src/modules/integrations/i18n/pl.json +1 -0
- package/src/modules/integrations/lib/credentials-masking.ts +10 -6
- package/src/modules/messages/api/[id]/route.ts +55 -1
- package/src/modules/messages/api/openapi.ts +20 -0
- package/src/modules/messages/api/route.ts +31 -2
- package/src/modules/messages/components/MessagesInboxPageClient.tsx +9 -16
- package/src/modules/messages/components/inboxFilters.ts +36 -0
- package/src/modules/messages/components/message-detail/hooks/useMessageDetailsConversation.ts +14 -1
- package/src/modules/messages/components/message-detail/panels/MessageHeader.tsx +3 -1
- package/src/modules/messages/components/message-detail/panels/thread-panel.tsx +2 -1
- package/src/modules/messages/components/message-detail/types.ts +3 -0
- package/src/modules/messages/components/messageListLabels.ts +52 -3
- package/src/modules/messages/data/validators.ts +43 -4
- package/src/modules/messages/lib/channel-sender-identity.ts +55 -0
- package/src/modules/messages/lib/composeSourceChannelType.ts +95 -0
- package/src/modules/payment_gateways/api/webhook/[provider]/route.ts +4 -6
- package/src/modules/payment_gateways/backend/payment-gateways/page.tsx +4 -1
- package/src/modules/payment_gateways/lib/webhook-processor.ts +11 -0
- package/src/modules/planner/backend/planner/availability-rulesets/page.tsx +4 -0
- package/src/modules/push_notifications/backend/push_notifications/page.tsx +4 -1
- package/src/modules/push_notifications/backend/push_notifications/send/page.tsx +4 -4
- package/src/modules/query_index/lib/engine.ts +249 -61
- package/src/modules/query_index/lib/search-entity-policy.ts +46 -0
- package/src/modules/query_index/lib/search-tokens.ts +135 -3
- package/src/modules/resources/backend/resources/resource-types/page.tsx +4 -1
- package/src/modules/resources/backend/resources/resources/page.tsx +4 -1
- package/src/modules/sales/backend/sales/channels/offers/page.tsx +4 -0
- package/src/modules/sales/backend/sales/channels/page.tsx +4 -0
- package/src/modules/sales/backend/sales/documents/[id]/page.tsx +14 -8
- package/src/modules/sales/commands/documents.ts +93 -0
- package/src/modules/sales/components/PriceWithCurrency.tsx +17 -5
- package/src/modules/sales/components/channels/SalesChannelOffersPanel.tsx +4 -0
- package/src/modules/sales/components/documents/AdjustmentsSection.tsx +6 -5
- package/src/modules/sales/components/documents/ItemsSection.tsx +56 -18
- package/src/modules/sales/components/documents/LineItemDialog.tsx +137 -20
- package/src/modules/sales/components/documents/PaymentsSection.tsx +20 -9
- package/src/modules/sales/components/documents/ReturnsSection.tsx +6 -5
- package/src/modules/sales/components/documents/SalesDocumentsTable.tsx +106 -14
- package/src/modules/sales/components/documents/SalesOrderDraftLines.tsx +6 -5
- package/src/modules/sales/components/documents/ShipmentDialog.tsx +5 -4
- package/src/modules/sales/components/documents/ShipmentsSection.tsx +6 -5
- package/src/modules/sales/components/documents/lineItemShipmentLock.ts +74 -0
- package/src/modules/sales/components/documents/lineItemUtils.ts +7 -2
- package/src/modules/sales/data/entities.ts +6 -0
- package/src/modules/sales/extension-points.ts +1 -0
- package/src/modules/sales/i18n/de.json +8 -0
- package/src/modules/sales/i18n/en.json +8 -0
- package/src/modules/sales/i18n/es.json +8 -0
- package/src/modules/sales/i18n/ko.json +8 -0
- package/src/modules/sales/i18n/pl.json +8 -0
- package/src/modules/sales/lib/salesChannelsToggleSeed.ts +42 -0
- package/src/modules/sales/migrations/.snapshot-open-mercato.json +60 -0
- package/src/modules/sales/migrations/Migration20260821120000_sales_notes_addresses_context_idx.ts +87 -0
- package/src/modules/sales/setup.ts +2 -0
- package/src/modules/sales/widgets/injection/payment-gateway-status-column/widget.ts +9 -1
- package/src/modules/sales/widgets/injection-table.ts +4 -7
- package/src/modules/staff/backend/staff/leave-requests/page.tsx +4 -0
- package/src/modules/staff/backend/staff/my-leave-requests/page.tsx +4 -0
- package/src/modules/staff/backend/staff/team-members/page.tsx +6 -0
- package/src/modules/staff/backend/staff/team-roles/page.tsx +4 -0
- package/src/modules/staff/backend/staff/teams/[id]/edit/page.tsx +4 -0
- package/src/modules/staff/backend/staff/teams/page.tsx +4 -0
- package/src/modules/staff/backend/staff/timesheets/page.tsx +12 -1
- package/src/modules/staff/backend/staff/timesheets/projects/page.tsx +4 -0
- package/src/modules/sync_excel/lib/adapters/customers.ts +5 -0
- package/src/modules/warranty_claims/api/assignees/route.ts +3 -3
- package/src/modules/warranty_claims/api/portal/attachments/route.ts +2 -2
- package/src/modules/warranty_claims/api/portal/claims/route.ts +12 -9
- package/src/modules/warranty_claims/backend/page.tsx +4 -0
- package/src/modules/warranty_claims/backend/warranty_claims/registrations/page.tsx +4 -0
- package/src/modules/warranty_claims/backend/warranty_claims/troubleshooting-guides/page.tsx +4 -0
- package/src/modules/warranty_claims/backend/warranty_claims/vendor-policies/page.tsx +4 -0
- package/src/modules/warranty_claims/frontend/[orgSlug]/portal/claims/page.tsx +4 -0
- package/src/modules/wms/api/warehouseSearch.ts +23 -0
- package/src/modules/wms/api/warehouses/route.ts +13 -9
- package/src/modules/wms/api/zones/route.ts +16 -0
- package/src/modules/wms/commands/configuration.ts +159 -10
- package/src/modules/wms/commands/inventory-actions.ts +3 -0
- package/src/modules/wms/commands/shared.ts +17 -0
- package/src/modules/wms/components/backend/WarehouseEditDialog.tsx +216 -0
- package/src/modules/wms/components/backend/WmsConfigurationPage.tsx +95 -141
- package/src/modules/wms/components/backend/WmsInventoryConsolePage.tsx +2 -0
- package/src/modules/wms/components/backend/WmsLotsListPage.tsx +2 -0
- package/src/modules/wms/components/backend/inventoryMutationLoaders.ts +36 -20
- package/src/modules/wms/components/backend/warehouseFormOptions.ts +72 -0
- package/src/modules/wms/i18n/de.json +2 -0
- package/src/modules/wms/i18n/en.json +2 -0
- package/src/modules/wms/i18n/es.json +2 -0
- package/src/modules/wms/i18n/ko.json +2 -0
- package/src/modules/wms/i18n/pl.json +2 -0
- package/src/modules/workflows/backend/definitions/page.tsx +4 -1
- package/src/modules/workflows/backend/events/page.tsx +4 -1
- package/src/modules/workflows/backend/instances/page.tsx +4 -1
- package/src/modules/workflows/backend/tasks/page.tsx +4 -1
- package/src/modules/workflows/lib/activity-executor.ts +9 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../src/modules/data_sync/backend/data-sync/page.tsx"],
|
|
4
|
-
"sourcesContent": ["\"use client\"\nimport * as React from 'react'\nimport { extensionPoints } from '@open-mercato/core/modules/data_sync/extension-points'\nimport Link from 'next/link'\nimport { useRouter } from 'next/navigation'\nimport { Page, PageBody } from '@open-mercato/ui/backend/Page'\nimport { DataTable } from '@open-mercato/ui/backend/DataTable'\nimport type { LegacyColumnDef as ColumnDef } from '@tanstack/react-table/legacy'\nimport type { FilterDef, FilterValues } from '@open-mercato/ui/backend/FilterBar'\nimport { useGuardedMutation } from '@open-mercato/ui/backend/injection/useGuardedMutation'\nimport { Badge } from '@open-mercato/ui/primitives/badge'\nimport { StatusBadge } from '@open-mercato/ui/primitives/status-badge'\nimport { Card, CardContent, CardHeader, CardTitle } from '@open-mercato/ui/primitives/card'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { Input } from '@open-mercato/ui/primitives/input'\nimport { Label } from '@open-mercato/ui/primitives/label'\nimport { Alert, AlertDescription } from '@open-mercato/ui/primitives/alert'\nimport {\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from '@open-mercato/ui/primitives/select'\nimport { Separator } from '@open-mercato/ui/primitives/separator'\nimport { Switch } from '@open-mercato/ui/primitives/switch'\nimport { RowActions } from '@open-mercato/ui/backend/RowActions'\nimport { apiCall, withScopedApiRequestHeaders } from '@open-mercato/ui/backend/utils/apiCall'\nimport { buildOptimisticLockHeader } from '@open-mercato/ui/backend/utils/optimisticLock'\nimport { surfaceRecordConflict } from '@open-mercato/ui/backend/conflicts'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { useOrganizationScopeVersion } from '@open-mercato/shared/lib/frontend/useOrganizationScope'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport {\n ArrowRightLeft,\n Boxes,\n CalendarClock,\n Clock3,\n Gauge,\n Play,\n PlugZap,\n Repeat,\n Settings2,\n ShieldCheck,\n} from 'lucide-react'\nimport { getSyncRunStatusVariant, getSyncSummaryVariant } from '../../lib/syncRunStatus'\nimport type { RunParameter } from '../../lib/adapter'\nimport { getApplicableRunParameters } from '../../lib/run-parameters'\nimport {\n RunParameterFields,\n buildDefaultRunParameterValues,\n buildRetryFailureMessage,\n buildRunFailureMessage,\n buildRunParametersPayload,\n type RetryFailureBody,\n type RunFailureBody,\n type RunParameterFormValue,\n} from '../../components/RunParameterFields'\n\ntype SyncRunRow = {\n id: string\n integrationId: string\n entityType: string\n direction: 'import' | 'export'\n status: 'pending' | 'running' | 'completed' | 'failed' | 'cancelled' | 'paused'\n createdCount: number\n updatedCount: number\n failedCount: number\n createdAt: string\n}\n\ntype ResponsePayload = {\n items: SyncRunRow[]\n total: number\n page: number\n totalPages: number\n}\n\ntype SyncOption = {\n integrationId: string\n title: string\n description?: string | null\n providerKey?: string | null\n direction: 'import' | 'export' | 'bidirectional'\n runMode?: 'generic' | 'provider'\n canStartRun?: boolean\n supportedEntities: string[]\n runParameters?: RunParameter[]\n hasCredentials: boolean\n isEnabled: boolean\n settingsPath: string\n}\n\ntype SyncOptionsResponse = {\n items: SyncOption[]\n}\n\ntype SyncScheduleRecord = {\n id: string\n integrationId: string\n entityType: string\n direction: 'import' | 'export'\n scheduleType: 'cron' | 'interval'\n scheduleValue: string\n timezone: string\n fullSync: boolean\n isEnabled: boolean\n lastRunAt: string | null\n updatedAt?: string | null\n}\n\ntype SyncSchedulesResponse = {\n items?: SyncScheduleRecord[]\n}\n\ntype SyncScheduleEditorState = {\n id?: string\n scheduleType: 'cron' | 'interval'\n scheduleValue: string\n timezone: string\n fullSync: boolean\n isEnabled: boolean\n lastRunAt: string | null\n updatedAt?: string | null\n}\n\nconst DEFAULT_TIMEZONE = Intl.DateTimeFormat().resolvedOptions().timeZone || 'UTC'\n\nfunction formatEntityTypeLabel(entityType: string): string {\n return entityType\n .replace(/[_-]+/g, ' ')\n .replace(/\\b\\w/g, (letter) => letter.toUpperCase())\n}\n\nfunction buildDefaultScheduleState(entityType: string): SyncScheduleEditorState {\n const normalized = entityType.trim().toLowerCase()\n const longerInterval = normalized === 'categories' || normalized === 'attributes'\n return {\n scheduleType: 'interval',\n scheduleValue: longerInterval ? '6h' : '1h',\n timezone: DEFAULT_TIMEZONE,\n fullSync: normalized !== 'products',\n isEnabled: true,\n lastRunAt: null,\n updatedAt: null,\n }\n}\n\nexport default function SyncRunsDashboardPage() {\n const router = useRouter()\n const [rows, setRows] = React.useState<SyncRunRow[]>([])\n const [options, setOptions] = React.useState<SyncOption[]>([])\n const [page, setPage] = React.useState(1)\n const [total, setTotal] = React.useState(0)\n const [totalPages, setTotalPages] = React.useState(1)\n const [search, setSearch] = React.useState('')\n const [filterValues, setFilterValues] = React.useState<FilterValues>({})\n const [isLoading, setIsLoading] = React.useState(true)\n const [isLoadingOptions, setIsLoadingOptions] = React.useState(true)\n const [selectedIntegrationId, setSelectedIntegrationId] = React.useState('')\n const [selectedEntityType, setSelectedEntityType] = React.useState('')\n const [selectedDirection, setSelectedDirection] = React.useState<'import' | 'export'>('import')\n const [batchSize, setBatchSize] = React.useState('100')\n const [fullSync, setFullSync] = React.useState(false)\n const [paramValues, setParamValues] = React.useState<Record<string, RunParameterFormValue>>({})\n const [scheduleEditor, setScheduleEditor] = React.useState<SyncScheduleEditorState>(() => buildDefaultScheduleState(''))\n const [isLoadingSchedule, setIsLoadingSchedule] = React.useState(false)\n const [isSavingSchedule, setIsSavingSchedule] = React.useState(false)\n const [isDeletingSchedule, setIsDeletingSchedule] = React.useState(false)\n const [reloadToken, setReloadToken] = React.useState(0)\n const scopeVersion = useOrganizationScopeVersion()\n const t = useT()\n const { runMutation } = useGuardedMutation<Record<string, unknown>>({\n contextId: 'data_sync.dashboard',\n })\n\n React.useEffect(() => {\n let cancelled = false\n async function load() {\n setIsLoading(true)\n const params = new URLSearchParams()\n params.set('page', String(page))\n params.set('pageSize', '20')\n if (filterValues.status) params.set('status', filterValues.status as string)\n if (filterValues.direction) params.set('direction', filterValues.direction as string)\n if (search.trim()) params.set('search', search.trim())\n const fallback: ResponsePayload = { items: [], total: 0, page, totalPages: 1 }\n const call = await apiCall<ResponsePayload>(\n `/api/data_sync/runs?${params.toString()}`,\n undefined,\n { fallback },\n )\n if (!call.ok) {\n flash(t('data_sync.dashboard.loadError'), 'error')\n if (!cancelled) setIsLoading(false)\n return\n }\n const payload = call.result ?? fallback\n if (!cancelled) {\n setRows(Array.isArray(payload.items) ? payload.items : [])\n setTotal(payload.total || 0)\n setTotalPages(payload.totalPages || 1)\n setIsLoading(false)\n }\n }\n load()\n return () => { cancelled = true }\n }, [page, filterValues, search, reloadToken, scopeVersion, t])\n\n React.useEffect(() => {\n let cancelled = false\n async function loadOptions() {\n setIsLoadingOptions(true)\n const fallback: SyncOptionsResponse = { items: [] }\n const call = await apiCall<SyncOptionsResponse>('/api/data_sync/options', undefined, { fallback })\n if (!cancelled) {\n if (!call.ok) {\n flash(t('data_sync.dashboard.loadError'), 'error')\n setOptions([])\n setIsLoadingOptions(false)\n return\n }\n\n const nextItems = Array.isArray(call.result?.items) ? call.result.items : []\n setOptions(nextItems)\n setSelectedIntegrationId((current) => {\n if (current && nextItems.some((item) => item.integrationId === current)) return current\n return nextItems[0]?.integrationId ?? ''\n })\n setIsLoadingOptions(false)\n }\n }\n\n void loadOptions()\n return () => { cancelled = true }\n }, [scopeVersion, t])\n\n const selectedIntegration = React.useMemo(\n () => options.find((item) => item.integrationId === selectedIntegrationId) ?? null,\n [options, selectedIntegrationId],\n )\n\n const entityOptions = React.useMemo(\n () => selectedIntegration?.supportedEntities ?? [],\n [selectedIntegration],\n )\n\n const runParameters = React.useMemo(\n () => getApplicableRunParameters(\n selectedIntegration?.runParameters,\n selectedDirection,\n // Pass the state through as-is. Before an entity is chosen it is '',\n // which matches no `entityType` and so hides scoped parameters \u2014 the\n // wanted outcome. Mapping '' to `undefined` would mean \"skip entity\n // scoping\" and show every scoped parameter instead.\n selectedEntityType,\n ),\n [selectedIntegration, selectedDirection, selectedEntityType],\n )\n\n React.useEffect(() => {\n setParamValues(buildDefaultRunParameterValues(runParameters))\n }, [runParameters])\n\n const updateParamValue = React.useCallback((key: string, value: RunParameterFormValue) => {\n setParamValues((current) => ({ ...current, [key]: value }))\n }, [])\n\n React.useEffect(() => {\n if (!selectedIntegration) {\n setSelectedEntityType('')\n return\n }\n setSelectedEntityType((current) => (\n current && selectedIntegration.supportedEntities.includes(current)\n ? current\n : (selectedIntegration.supportedEntities[0] ?? '')\n ))\n setSelectedDirection(selectedIntegration.direction === 'export' ? 'export' : 'import')\n }, [selectedIntegration])\n\n React.useEffect(() => {\n if (!selectedIntegration || !selectedEntityType) {\n setScheduleEditor(buildDefaultScheduleState(selectedEntityType))\n return\n }\n\n const currentIntegration = selectedIntegration\n let cancelled = false\n async function loadSchedule() {\n setIsLoadingSchedule(true)\n const integrationId = currentIntegration.integrationId\n const params = new URLSearchParams({\n integrationId,\n entityType: selectedEntityType,\n direction: selectedDirection,\n page: '1',\n pageSize: '1',\n })\n const fallback: SyncSchedulesResponse = { items: [] }\n const call = await apiCall<SyncSchedulesResponse>(`/api/data_sync/schedules?${params.toString()}`, undefined, { fallback })\n\n if (cancelled) return\n\n if (!call.ok) {\n setScheduleEditor(buildDefaultScheduleState(selectedEntityType))\n setIsLoadingSchedule(false)\n return\n }\n\n const record = Array.isArray(call.result?.items) ? call.result?.items[0] : undefined\n if (!record) {\n setScheduleEditor(buildDefaultScheduleState(selectedEntityType))\n setIsLoadingSchedule(false)\n return\n }\n\n setScheduleEditor({\n id: record.id,\n scheduleType: record.scheduleType,\n scheduleValue: record.scheduleValue,\n timezone: record.timezone,\n fullSync: record.fullSync,\n isEnabled: record.isEnabled,\n lastRunAt: record.lastRunAt,\n updatedAt: record.updatedAt ?? null,\n })\n setIsLoadingSchedule(false)\n }\n\n void loadSchedule()\n return () => { cancelled = true }\n }, [selectedDirection, selectedEntityType, selectedIntegration, scopeVersion])\n\n const updateScheduleEditor = React.useCallback((changes: Partial<SyncScheduleEditorState>) => {\n setScheduleEditor((current) => ({ ...current, ...changes }))\n }, [])\n\n const handleCancel = React.useCallback(async (row: SyncRunRow) => {\n // optimistic-lock-exempt: run lifecycle action endpoint (cancel), not a concurrent record edit\n const call = await apiCall(`/api/data_sync/runs/${encodeURIComponent(row.id)}/cancel`, {\n method: 'POST',\n }, { fallback: null })\n if (call.ok) {\n flash(t('data_sync.runs.detail.cancelSuccess'), 'success')\n setReloadToken((token) => token + 1)\n } else {\n flash(t('data_sync.runs.detail.cancelError'), 'error')\n }\n }, [t])\n\n const handleRetry = React.useCallback(async (row: SyncRunRow) => {\n // optimistic-lock-exempt: run lifecycle action endpoint (retry), not a concurrent record edit\n const call = await apiCall(`/api/data_sync/runs/${encodeURIComponent(row.id)}/retry`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ fromBeginning: false }),\n }, { fallback: null })\n if (call.ok) {\n flash(t('data_sync.runs.detail.retrySuccess'), 'success')\n setReloadToken((token) => token + 1)\n } else {\n flash(buildRetryFailureMessage(call.result as RetryFailureBody | null, t), 'error')\n }\n }, [t])\n\n const handleFiltersApply = React.useCallback((values: FilterValues) => {\n const next: FilterValues = {}\n Object.entries(values).forEach(([key, value]) => {\n if (value !== undefined && value !== '') next[key] = value\n })\n setFilterValues(next)\n setPage(1)\n }, [])\n\n const handleFiltersClear = React.useCallback(() => {\n setFilterValues({})\n setPage(1)\n }, [])\n\n const handleStartSync = React.useCallback(async () => {\n if (!selectedIntegration || !selectedEntityType) return\n\n const parsedBatchSize = Number.parseInt(batchSize, 10)\n if (!Number.isFinite(parsedBatchSize) || parsedBatchSize < 1 || parsedBatchSize > 1000) {\n flash(t('data_sync.dashboard.start.invalidBatchSize', 'Batch size must be between 1 and 1000.'), 'error')\n return\n }\n\n const parameters = buildRunParametersPayload(runParameters, paramValues)\n const requestBody: Record<string, unknown> = {\n integrationId: selectedIntegration.integrationId,\n entityType: selectedEntityType,\n direction: selectedDirection,\n batchSize: parsedBatchSize,\n fullSync,\n }\n if (runParameters.length > 0) requestBody.parameters = parameters\n\n try {\n const call = await runMutation({\n // optimistic-lock-exempt: starts a new sync run (create), not a concurrent record edit\n operation: () => apiCall<{ id: string }>('/api/data_sync/run', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(requestBody),\n }, { fallback: null }),\n mutationPayload: requestBody,\n context: {\n operation: 'create',\n actionId: 'start-sync-run',\n integrationId: selectedIntegration.integrationId,\n },\n })\n\n if (!call.ok || !call.result?.id) {\n flash(buildRunFailureMessage(\n call.result as RunFailureBody | null,\n t('data_sync.dashboard.start.error', 'Failed to start sync run'),\n t,\n ), 'error')\n return\n }\n\n flash(t('data_sync.dashboard.start.success', 'Sync run started'), 'success')\n setReloadToken((token) => token + 1)\n router.push(`/backend/data-sync/runs/${encodeURIComponent(call.result.id)}`)\n } catch (error) {\n const message = error instanceof Error ? error.message : t('data_sync.dashboard.start.error', 'Failed to start sync run')\n flash(message, 'error')\n }\n }, [batchSize, fullSync, paramValues, router, runMutation, runParameters, selectedDirection, selectedEntityType, selectedIntegration, t])\n\n const handleSaveSchedule = React.useCallback(async () => {\n if (!selectedIntegration || !selectedEntityType) return\n if (scheduleEditor.scheduleValue.trim().length === 0) {\n flash(t('data_sync.dashboard.schedule.invalidValue', 'Provide a schedule value before saving.'), 'error')\n return\n }\n\n setIsSavingSchedule(true)\n try {\n const call = await runMutation({\n // Keyed upsert (POST). When the editor holds an existing schedule's\n // `updatedAt`, the lock header version-checks the resolved row on the\n // server; a brand-new schedule has a null `updatedAt`, so the header is\n // empty and the create path stays unaffected.\n operation: () => withScopedApiRequestHeaders(\n buildOptimisticLockHeader(scheduleEditor.updatedAt),\n () => apiCall<SyncScheduleRecord>('/api/data_sync/schedules', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n integrationId: selectedIntegration.integrationId,\n entityType: selectedEntityType,\n direction: selectedDirection,\n scheduleType: scheduleEditor.scheduleType,\n scheduleValue: scheduleEditor.scheduleValue.trim(),\n timezone: scheduleEditor.timezone.trim() || DEFAULT_TIMEZONE,\n fullSync: scheduleEditor.fullSync,\n isEnabled: scheduleEditor.isEnabled,\n }),\n }, { fallback: null }),\n ),\n mutationPayload: {\n integrationId: selectedIntegration.integrationId,\n entityType: selectedEntityType,\n direction: selectedDirection,\n scheduleType: scheduleEditor.scheduleType,\n scheduleValue: scheduleEditor.scheduleValue.trim(),\n timezone: scheduleEditor.timezone.trim() || DEFAULT_TIMEZONE,\n fullSync: scheduleEditor.fullSync,\n isEnabled: scheduleEditor.isEnabled,\n },\n context: {\n operation: 'update',\n actionId: 'save-sync-schedule',\n integrationId: selectedIntegration.integrationId,\n },\n })\n\n if (!call.ok || !call.result) {\n const conflictError = Object.assign(\n new Error((call.result as { error?: string } | null)?.error ?? t('data_sync.dashboard.schedule.error', 'Failed to save recurring schedule')),\n {\n status: call.status,\n ...(call.result && typeof call.result === 'object' ? call.result : {}),\n },\n )\n if (surfaceRecordConflict(conflictError, t)) {\n return\n }\n flash((call.result as { error?: string } | null)?.error ?? t('data_sync.dashboard.schedule.error', 'Failed to save recurring schedule'), 'error')\n return\n }\n\n setScheduleEditor({\n id: call.result.id,\n scheduleType: call.result.scheduleType,\n scheduleValue: call.result.scheduleValue,\n timezone: call.result.timezone,\n fullSync: call.result.fullSync,\n isEnabled: call.result.isEnabled,\n lastRunAt: call.result.lastRunAt,\n updatedAt: call.result.updatedAt ?? null,\n })\n flash(t('data_sync.dashboard.schedule.success', 'Recurring schedule saved'), 'success')\n } catch (error) {\n const message = error instanceof Error ? error.message : t('data_sync.dashboard.schedule.error', 'Failed to save recurring schedule')\n flash(message, 'error')\n } finally {\n setIsSavingSchedule(false)\n }\n }, [runMutation, scheduleEditor, selectedDirection, selectedEntityType, selectedIntegration, t])\n\n const handleDeleteSchedule = React.useCallback(async () => {\n if (!scheduleEditor.id) return\n\n setIsDeletingSchedule(true)\n try {\n const call = await runMutation({\n operation: () => withScopedApiRequestHeaders(\n buildOptimisticLockHeader(scheduleEditor.updatedAt),\n () => apiCall(`/api/data_sync/schedules/${encodeURIComponent(scheduleEditor.id as string)}`, {\n method: 'DELETE',\n }, { fallback: null }),\n ),\n mutationPayload: {\n scheduleId: scheduleEditor.id,\n },\n context: {\n operation: 'delete',\n actionId: 'delete-sync-schedule',\n },\n })\n\n if (!call.ok) {\n flash((call.result as { error?: string } | null)?.error ?? t('data_sync.dashboard.schedule.deleteError', 'Failed to remove recurring schedule'), 'error')\n return\n }\n\n setScheduleEditor(buildDefaultScheduleState(selectedEntityType))\n flash(t('data_sync.dashboard.schedule.deleteSuccess', 'Recurring schedule removed'), 'success')\n } catch (error) {\n const message = error instanceof Error ? error.message : t('data_sync.dashboard.schedule.deleteError', 'Failed to remove recurring schedule')\n flash(message, 'error')\n } finally {\n setIsDeletingSchedule(false)\n }\n }, [runMutation, scheduleEditor.id, scheduleEditor.updatedAt, selectedEntityType, t])\n\n const filters: FilterDef[] = [\n {\n id: 'status',\n type: 'select',\n label: t('data_sync.dashboard.filters.status'),\n options: [\n { label: t('data_sync.dashboard.filters.allStatuses'), value: '' },\n { label: t('data_sync.dashboard.status.pending'), value: 'pending' },\n { label: t('data_sync.dashboard.status.running'), value: 'running' },\n { label: t('data_sync.dashboard.status.completed'), value: 'completed' },\n { label: t('data_sync.dashboard.status.failed'), value: 'failed' },\n { label: t('data_sync.dashboard.status.cancelled'), value: 'cancelled' },\n ],\n },\n {\n id: 'direction',\n type: 'select',\n label: t('data_sync.dashboard.columns.direction'),\n options: [\n { label: t('data_sync.dashboard.filters.allDirections'), value: '' },\n { label: t('data_sync.dashboard.direction.import'), value: 'import' },\n { label: t('data_sync.dashboard.direction.export'), value: 'export' },\n ],\n },\n ]\n\n const columns = React.useMemo<ColumnDef<SyncRunRow>[]>(() => [\n {\n accessorKey: 'integrationId',\n header: t('data_sync.dashboard.columns.integration'),\n cell: ({ row }) => <span className=\"font-medium text-sm\">{row.original.integrationId}</span>,\n },\n {\n accessorKey: 'entityType',\n header: t('data_sync.dashboard.columns.entityType'),\n },\n {\n accessorKey: 'direction',\n header: t('data_sync.dashboard.columns.direction'),\n cell: ({ row }) => (\n <Badge variant=\"outline\">\n {t(`data_sync.dashboard.direction.${row.original.direction}`)}\n </Badge>\n ),\n },\n {\n accessorKey: 'status',\n header: t('data_sync.dashboard.columns.status'),\n cell: ({ row }) => (\n <StatusBadge variant={getSyncRunStatusVariant(row.original.status)}>\n {t(`data_sync.dashboard.status.${row.original.status}`)}\n </StatusBadge>\n ),\n },\n {\n accessorKey: 'createdCount',\n header: t('data_sync.dashboard.columns.created'),\n },\n {\n accessorKey: 'updatedCount',\n header: t('data_sync.dashboard.columns.updated'),\n },\n {\n accessorKey: 'failedCount',\n header: t('data_sync.dashboard.columns.failed'),\n },\n {\n accessorKey: 'createdAt',\n header: t('data_sync.dashboard.columns.createdAt'),\n cell: ({ row }) => new Date(row.original.createdAt).toLocaleString(),\n },\n ], [t])\n\n const canStartSelectedIntegration = Boolean(\n selectedIntegration\n && selectedEntityType\n && selectedIntegration.isEnabled\n && selectedIntegration.canStartRun !== false\n && selectedIntegration.hasCredentials,\n )\n const hasSavedSchedule = Boolean(scheduleEditor.id)\n const selectedEntityLabel = selectedEntityType ? formatEntityTypeLabel(selectedEntityType) : t('data_sync.dashboard.columns.entityType')\n const integrationStateVariant = getSyncSummaryVariant(selectedIntegration?.isEnabled ? 'enabled' : 'disabled')\n const credentialsVariant = getSyncSummaryVariant(selectedIntegration?.hasCredentials ? 'ready' : 'missing')\n const scheduleVariant = getSyncSummaryVariant(\n hasSavedSchedule\n ? (scheduleEditor.isEnabled ? 'scheduled' : 'paused')\n : 'none',\n )\n\n return (\n <Page>\n <PageBody className=\"space-y-6\">\n <Card>\n <CardHeader className=\"space-y-4\">\n <div className=\"flex flex-col gap-4 lg:flex-row lg:items-start lg:justify-between\">\n <div className=\"space-y-2\">\n <div className=\"flex items-center gap-2 text-xs font-medium uppercase tracking-widest text-muted-foreground\">\n <Repeat className=\"size-4\" />\n <span>{t('data_sync.dashboard.start.eyebrow', 'Run once or keep it recurring')}</span>\n </div>\n <div className=\"space-y-1\">\n <CardTitle>{t('data_sync.dashboard.start.title', 'Start or schedule a sync')}</CardTitle>\n <p className=\"max-w-3xl text-sm text-muted-foreground\">\n {t('data_sync.dashboard.start.description', 'Pick a sync target, launch an ad-hoc run, or save a recurring schedule for the same entity and direction from this page.')}\n </p>\n </div>\n </div>\n {selectedIntegration ? (\n <Button asChild variant=\"outline\">\n <Link href={selectedIntegration.settingsPath}>\n <Settings2 className=\"mr-2 size-4\" />\n {t('integrations.marketplace.configure')}\n </Link>\n </Button>\n ) : null}\n </div>\n\n {selectedIntegration ? (\n <div className=\"flex flex-wrap gap-2\">\n <Badge variant=\"outline\" className=\"gap-1.5\">\n <PlugZap className=\"size-3.5\" />\n {selectedIntegration.title}\n </Badge>\n <Badge variant=\"outline\" className=\"gap-1.5\">\n <ArrowRightLeft className=\"size-3.5\" />\n {t(`data_sync.dashboard.direction.${selectedDirection}`)}\n </Badge>\n <Badge variant={integrationStateVariant} className=\"gap-1.5\">\n <ShieldCheck className=\"size-3.5\" />\n {selectedIntegration.isEnabled\n ? t('data_sync.dashboard.start.status.enabled', 'Integration enabled')\n : t('data_sync.dashboard.start.status.disabled', 'Integration disabled')}\n </Badge>\n <Badge variant={credentialsVariant} className=\"gap-1.5\">\n <PlugZap className=\"size-3.5\" />\n {selectedIntegration.hasCredentials\n ? t('data_sync.dashboard.start.status.credentialsReady', 'Credentials ready')\n : t('data_sync.dashboard.start.status.credentialsMissing', 'Credentials missing')}\n </Badge>\n <Badge variant={scheduleVariant} className=\"gap-1.5\">\n <CalendarClock className=\"size-3.5\" />\n {hasSavedSchedule\n ? (scheduleEditor.isEnabled\n ? t('data_sync.dashboard.schedule.status.enabled', 'Recurring schedule active')\n : t('data_sync.dashboard.schedule.status.disabled', 'Recurring schedule paused'))\n : t('data_sync.dashboard.schedule.status.none', 'No recurring schedule')}\n </Badge>\n </div>\n ) : null}\n </CardHeader>\n <CardContent className=\"space-y-6\">\n <div className=\"grid gap-4 xl:grid-cols-3\">\n <div className=\"space-y-2 xl:col-span-1\">\n <Label className=\"flex items-center gap-2 text-sm font-medium\">\n <PlugZap className=\"size-4 text-muted-foreground\" />\n <span>{t('data_sync.dashboard.columns.integration')}</span>\n </Label>\n <Select\n value={selectedIntegrationId || undefined}\n onValueChange={(value) => setSelectedIntegrationId(value ?? '')}\n disabled={isLoadingOptions || options.length === 0}\n >\n <SelectTrigger size=\"lg\">\n <SelectValue\n placeholder={\n options.length === 0\n ? t('integrations.marketplace.noResults', 'No integrations found')\n : undefined\n }\n />\n </SelectTrigger>\n <SelectContent>\n {options.map((item) => (\n <SelectItem key={item.integrationId} value={item.integrationId}>\n {item.title}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n <div className=\"space-y-2\">\n <Label className=\"flex items-center gap-2 text-sm font-medium\">\n <Boxes className=\"size-4 text-muted-foreground\" />\n <span>{t('data_sync.dashboard.columns.entityType')}</span>\n </Label>\n <Select\n value={selectedEntityType || undefined}\n onValueChange={(value) => setSelectedEntityType(value ?? '')}\n disabled={entityOptions.length === 0}\n >\n <SelectTrigger size=\"lg\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {entityOptions.map((entityType) => (\n <SelectItem key={entityType} value={entityType}>\n {formatEntityTypeLabel(entityType)}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n <div className=\"space-y-2\">\n <Label className=\"flex items-center gap-2 text-sm font-medium\">\n <ArrowRightLeft className=\"size-4 text-muted-foreground\" />\n <span>{t('data_sync.dashboard.columns.direction')}</span>\n </Label>\n <Select\n value={selectedDirection}\n onValueChange={(value) => setSelectedDirection(value === 'export' ? 'export' : 'import')}\n disabled={selectedIntegration?.direction !== 'bidirectional'}\n >\n <SelectTrigger size=\"lg\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"import\">{t('data_sync.dashboard.direction.import')}</SelectItem>\n {(selectedIntegration?.direction === 'bidirectional' || selectedIntegration?.direction === 'export') ? (\n <SelectItem value=\"export\">{t('data_sync.dashboard.direction.export')}</SelectItem>\n ) : null}\n </SelectContent>\n </Select>\n </div>\n </div>\n\n {selectedIntegration?.description ? (\n <p className=\"text-sm text-muted-foreground\">{selectedIntegration.description}</p>\n ) : null}\n\n <div className=\"grid gap-4 xl:grid-cols-2\">\n <div className=\"rounded-xl border bg-muted/30 p-4\">\n <div className=\"flex items-start justify-between gap-3\">\n <div className=\"space-y-1\">\n <div className=\"flex items-center gap-2\">\n <Play className=\"size-4 text-primary\" />\n <h3 className=\"text-sm font-semibold\">{t('data_sync.dashboard.start.runNowTitle', 'Run once now')}</h3>\n </div>\n <p className=\"text-sm text-muted-foreground\">\n {t('data_sync.dashboard.start.runNowDescription', 'Use this for the next immediate sync. Batch size and full-sync mode apply only to this manual run.')}\n </p>\n </div>\n <Badge variant=\"outline\">{selectedEntityLabel}</Badge>\n </div>\n\n <Separator className=\"my-4\" />\n\n <div className=\"grid gap-4 sm:grid-cols-[minmax(0,180px)_1fr]\">\n <div className=\"space-y-2\">\n <Label className=\"flex items-center gap-2 text-sm font-medium\">\n <Gauge className=\"size-4 text-muted-foreground\" />\n <span>{t('data_sync.dashboard.start.batchSize', 'Batch size')}</span>\n </Label>\n <Input\n value={batchSize}\n onChange={(event) => setBatchSize(event.target.value)}\n inputMode=\"numeric\"\n />\n </div>\n <div className=\"rounded-lg border bg-background p-3\">\n <div className=\"flex items-center justify-between gap-3\">\n <div className=\"space-y-1\">\n <Label className=\"text-sm font-medium\">{t('data_sync.dashboard.start.fullSync', 'Run as full sync')}</Label>\n <p className=\"text-xs text-muted-foreground\">\n {t('data_sync.dashboard.start.fullSyncHelp', 'Ignore the saved cursor and process the entire source again for this run.')}\n </p>\n </div>\n <Switch checked={fullSync} onCheckedChange={setFullSync} />\n </div>\n </div>\n </div>\n\n {runParameters.length > 0 ? (\n <div className=\"mt-4 space-y-3\">\n <div className=\"flex items-center gap-2\">\n <Settings2 className=\"size-4 text-muted-foreground\" />\n <h4 className=\"text-sm font-semibold\">\n {t('data_sync.dashboard.start.parameters', 'Run parameters')}\n </h4>\n </div>\n <p className=\"text-xs text-muted-foreground\">\n {t('data_sync.dashboard.start.parametersHelp', 'Optional values this integration accepts for the manual run.')}\n </p>\n <RunParameterFields\n params={runParameters}\n values={paramValues}\n onChange={updateParamValue}\n />\n </div>\n ) : null}\n\n <div className=\"mt-4 flex flex-wrap items-center justify-between gap-3\">\n <p className=\"text-xs text-muted-foreground\">\n {t('data_sync.dashboard.start.runNowFootnote', 'Manual runs show progress immediately and land on the run detail page after launch.')}\n </p>\n <Button\n type=\"button\"\n onClick={() => void handleStartSync()}\n disabled={!canStartSelectedIntegration}\n >\n <Play className=\"mr-2 size-4\" />\n {t('data_sync.dashboard.start.submit', 'Start sync')}\n </Button>\n </div>\n </div>\n\n <div className=\"rounded-xl border bg-muted/30 p-4\">\n <div className=\"flex items-start justify-between gap-3\">\n <div className=\"space-y-1\">\n <div className=\"flex items-center gap-2\">\n <CalendarClock className=\"size-4 text-primary\" />\n <h3 className=\"text-sm font-semibold\">{t('data_sync.dashboard.schedule.title', 'Recurring schedule')}</h3>\n </div>\n <p className=\"text-sm text-muted-foreground\">\n {t('data_sync.dashboard.schedule.description', 'Save a repeating schedule for the selected integration, entity, and direction without leaving this dashboard.')}\n </p>\n </div>\n <Badge variant=\"outline\">\n {hasSavedSchedule\n ? (scheduleEditor.isEnabled\n ? t('data_sync.dashboard.schedule.status.shortEnabled', 'Scheduled')\n : t('data_sync.dashboard.schedule.status.shortDisabled', 'Paused'))\n : t('data_sync.dashboard.schedule.status.shortNone', 'One-time only')}\n </Badge>\n </div>\n\n <Separator className=\"my-4\" />\n\n <div className=\"grid gap-4 sm:grid-cols-2\">\n <div className=\"space-y-2\">\n <Label className=\"flex items-center gap-2 text-sm font-medium\">\n <Clock3 className=\"size-4 text-muted-foreground\" />\n <span>{t('data_sync.dashboard.schedule.type', 'Schedule type')}</span>\n </Label>\n <Select\n value={scheduleEditor.scheduleType}\n onValueChange={(value) => updateScheduleEditor({\n scheduleType: value === 'cron' ? 'cron' : 'interval',\n })}\n disabled={isLoadingSchedule || isSavingSchedule || isDeletingSchedule || !selectedIntegration || !selectedEntityType}\n >\n <SelectTrigger size=\"lg\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"interval\">{t('data_sync.dashboard.schedule.interval', 'Interval')}</SelectItem>\n <SelectItem value=\"cron\">{t('data_sync.dashboard.schedule.cron', 'Cron')}</SelectItem>\n </SelectContent>\n </Select>\n </div>\n <div className=\"space-y-2\">\n <Label className=\"flex items-center gap-2 text-sm font-medium\">\n <CalendarClock className=\"size-4 text-muted-foreground\" />\n <span>\n {scheduleEditor.scheduleType === 'cron'\n ? t('data_sync.dashboard.schedule.cronValue', 'Cron expression')\n : t('data_sync.dashboard.schedule.intervalValue', 'Interval')}\n </span>\n </Label>\n <Input\n value={scheduleEditor.scheduleValue}\n onChange={(event) => updateScheduleEditor({ scheduleValue: event.target.value })}\n disabled={isLoadingSchedule || isSavingSchedule || isDeletingSchedule || !selectedIntegration || !selectedEntityType}\n placeholder={scheduleEditor.scheduleType === 'cron' ? '0 * * * *' : '1h'}\n />\n <p className=\"text-xs text-muted-foreground\">\n {scheduleEditor.scheduleType === 'cron'\n ? t('data_sync.dashboard.schedule.cronHelp', 'Example: `0 * * * *` runs at the start of every hour.')\n : t('data_sync.dashboard.schedule.intervalHelp', 'Example: `1h`, `6h`, or `24h` for repeating intervals.')}\n </p>\n </div>\n <div className=\"space-y-2 sm:col-span-2\">\n <Label className=\"flex items-center gap-2 text-sm font-medium\">\n <Clock3 className=\"size-4 text-muted-foreground\" />\n <span>{t('data_sync.dashboard.schedule.timezone', 'Timezone')}</span>\n </Label>\n <Input\n value={scheduleEditor.timezone}\n onChange={(event) => updateScheduleEditor({ timezone: event.target.value })}\n disabled={isLoadingSchedule || isSavingSchedule || isDeletingSchedule || !selectedIntegration || !selectedEntityType}\n />\n </div>\n </div>\n\n <div className=\"mt-4 grid gap-3\">\n <div className=\"rounded-lg border bg-background p-3\">\n <div className=\"flex items-center justify-between gap-3\">\n <div className=\"space-y-1\">\n <Label className=\"text-sm font-medium\">{t('data_sync.dashboard.schedule.fullSync', 'Run scheduled jobs as full sync')}</Label>\n <p className=\"text-xs text-muted-foreground\">\n {t('data_sync.dashboard.schedule.fullSyncHelp', 'When enabled, every recurring run starts from the beginning instead of the saved cursor.')}\n </p>\n </div>\n <Switch\n checked={scheduleEditor.fullSync}\n onCheckedChange={(checked) => updateScheduleEditor({ fullSync: checked })}\n disabled={isLoadingSchedule || isSavingSchedule || isDeletingSchedule || !selectedIntegration || !selectedEntityType}\n />\n </div>\n </div>\n <div className=\"rounded-lg border bg-background p-3\">\n <div className=\"flex items-center justify-between gap-3\">\n <div className=\"space-y-1\">\n <Label className=\"text-sm font-medium\">{t('data_sync.dashboard.schedule.enabled', 'Schedule enabled')}</Label>\n <p className=\"text-xs text-muted-foreground\">\n {t('data_sync.dashboard.schedule.enabledHelp', 'Pause the recurring job without deleting the schedule definition.')}\n </p>\n </div>\n <Switch\n checked={scheduleEditor.isEnabled}\n onCheckedChange={(checked) => updateScheduleEditor({ isEnabled: checked })}\n disabled={isLoadingSchedule || isSavingSchedule || isDeletingSchedule || !selectedIntegration || !selectedEntityType}\n />\n </div>\n </div>\n </div>\n\n <div className=\"mt-4 flex flex-wrap items-center justify-between gap-3\">\n <div className=\"space-y-1 text-xs text-muted-foreground\">\n <div>\n {hasSavedSchedule\n ? (scheduleEditor.lastRunAt\n ? t('data_sync.dashboard.schedule.lastRun', 'Last scheduled run: {value}', {\n value: new Date(scheduleEditor.lastRunAt).toLocaleString(),\n })\n : t('data_sync.dashboard.schedule.neverRun', 'Saved, but no scheduled execution has completed yet.'))\n : t('data_sync.dashboard.schedule.none', 'No recurring schedule saved for this target yet.')}\n </div>\n </div>\n <div className=\"flex flex-wrap gap-2\">\n <Button\n type=\"button\"\n variant=\"outline\"\n onClick={() => void handleDeleteSchedule()}\n disabled={!hasSavedSchedule || isDeletingSchedule}\n >\n {isDeletingSchedule\n ? t('data_sync.dashboard.schedule.deleting', 'Removing...')\n : t('data_sync.dashboard.schedule.delete', 'Remove schedule')}\n </Button>\n <Button\n type=\"button\"\n variant=\"outline\"\n onClick={() => void handleSaveSchedule()}\n disabled={isSavingSchedule || !selectedIntegration || !selectedEntityType}\n >\n <CalendarClock className=\"mr-2 size-4\" />\n {isSavingSchedule\n ? t('data_sync.dashboard.schedule.saving', 'Saving...')\n : t('data_sync.dashboard.schedule.save', 'Save recurring schedule')}\n </Button>\n </div>\n </div>\n </div>\n </div>\n\n {selectedIntegration && !selectedIntegration.isEnabled ? (\n <Alert status=\"warning\">\n <AlertDescription>\n {t('integrations.detail.state.disabled', 'This integration is disabled. Enable it on the integration settings page before starting a sync.')}\n </AlertDescription>\n </Alert>\n ) : null}\n {selectedIntegration && !selectedIntegration.hasCredentials ? (\n <Alert status=\"warning\">\n <AlertDescription>\n {t('integrations.detail.credentials.notConfigured', 'Credentials are not configured yet. Save the integration credentials before starting a sync.')}\n </AlertDescription>\n </Alert>\n ) : null}\n {selectedIntegration && selectedIntegration.canStartRun === false ? (\n <Alert status=\"information\">\n <AlertDescription>\n {t('data_sync.dashboard.start.providerManaged', 'This integration starts sync runs from its own setup flow. Open the integration settings page to continue.')}\n </AlertDescription>\n </Alert>\n ) : null}\n </CardContent>\n </Card>\n\n <DataTable\n stickyActionsColumn\n title={t('data_sync.dashboard.title')}\n columns={columns}\n data={rows}\n filters={filters}\n filterValues={filterValues}\n onFiltersApply={handleFiltersApply}\n onFiltersClear={handleFiltersClear}\n searchValue={search}\n onSearchChange={(value) => { setSearch(value); setPage(1) }}\n searchPlaceholder={t('data_sync.dashboard.searchPlaceholder')}\n perspective={{ tableId: extensionPoints.hosts.runsTable.tableId }}\n onRowClick={(row) => {\n router.push(`/backend/data-sync/runs/${encodeURIComponent(row.id)}`)\n }}\n rowActions={(row) => (\n <RowActions items={[\n {\n id: 'view',\n label: t('data_sync.dashboard.actions.view'),\n onSelect: () => { router.push(`/backend/data-sync/runs/${encodeURIComponent(row.id)}`) },\n },\n ...(row.status === 'running' ? [{\n id: 'cancel',\n label: t('data_sync.runs.detail.cancel'),\n destructive: true,\n onSelect: () => { void handleCancel(row) },\n }] : []),\n ...(row.status === 'failed' ? [{\n id: 'retry',\n label: t('data_sync.runs.detail.retry'),\n onSelect: () => { void handleRetry(row) },\n }] : []),\n ]} />\n )}\n pagination={{ page, pageSize: 20, total, totalPages, onPageChange: setPage }}\n isLoading={isLoading}\n />\n </PageBody>\n </Page>\n )\n}\n"],
|
|
5
|
-
"mappings": "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",
|
|
4
|
+
"sourcesContent": ["\"use client\"\nimport * as React from 'react'\nimport { extensionPoints } from '@open-mercato/core/modules/data_sync/extension-points'\nimport Link from 'next/link'\nimport { useRouter } from 'next/navigation'\nimport { Page, PageBody } from '@open-mercato/ui/backend/Page'\nimport { DataTable } from '@open-mercato/ui/backend/DataTable'\nimport type { LegacyColumnDef as ColumnDef } from '@tanstack/react-table/legacy'\nimport type { FilterDef, FilterValues } from '@open-mercato/ui/backend/FilterBar'\nimport { useGuardedMutation } from '@open-mercato/ui/backend/injection/useGuardedMutation'\nimport { Badge } from '@open-mercato/ui/primitives/badge'\nimport { StatusBadge } from '@open-mercato/ui/primitives/status-badge'\nimport { Card, CardContent, CardHeader, CardTitle } from '@open-mercato/ui/primitives/card'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { Input } from '@open-mercato/ui/primitives/input'\nimport { Label } from '@open-mercato/ui/primitives/label'\nimport { Alert, AlertDescription } from '@open-mercato/ui/primitives/alert'\nimport {\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from '@open-mercato/ui/primitives/select'\nimport { Separator } from '@open-mercato/ui/primitives/separator'\nimport { Switch } from '@open-mercato/ui/primitives/switch'\nimport { RowActions } from '@open-mercato/ui/backend/RowActions'\nimport { apiCall, withScopedApiRequestHeaders } from '@open-mercato/ui/backend/utils/apiCall'\nimport { buildOptimisticLockHeader } from '@open-mercato/ui/backend/utils/optimisticLock'\nimport { surfaceRecordConflict } from '@open-mercato/ui/backend/conflicts'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { useOrganizationScopeVersion } from '@open-mercato/shared/lib/frontend/useOrganizationScope'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport {\n ArrowRightLeft,\n Boxes,\n CalendarClock,\n Clock3,\n Gauge,\n Play,\n PlugZap,\n Repeat,\n Settings2,\n ShieldCheck,\n} from 'lucide-react'\nimport { getSyncRunStatusVariant, getSyncSummaryVariant } from '../../lib/syncRunStatus'\nimport type { RunParameter } from '../../lib/adapter'\nimport { getApplicableRunParameters } from '../../lib/run-parameters'\nimport {\n RunParameterFields,\n buildDefaultRunParameterValues,\n buildRetryFailureMessage,\n buildRunFailureMessage,\n buildRunParametersPayload,\n type RetryFailureBody,\n type RunFailureBody,\n type RunParameterFormValue,\n} from '../../components/RunParameterFields'\n\ntype SyncRunRow = {\n id: string\n integrationId: string\n entityType: string\n direction: 'import' | 'export'\n status: 'pending' | 'running' | 'completed' | 'failed' | 'cancelled' | 'paused'\n createdCount: number\n updatedCount: number\n failedCount: number\n createdAt: string\n}\n\ntype ResponsePayload = {\n items: SyncRunRow[]\n total: number\n page: number\n totalPages: number\n totalIsCapped?: boolean\n}\n\ntype SyncOption = {\n integrationId: string\n title: string\n description?: string | null\n providerKey?: string | null\n direction: 'import' | 'export' | 'bidirectional'\n runMode?: 'generic' | 'provider'\n canStartRun?: boolean\n supportedEntities: string[]\n runParameters?: RunParameter[]\n hasCredentials: boolean\n isEnabled: boolean\n settingsPath: string\n}\n\ntype SyncOptionsResponse = {\n items: SyncOption[]\n}\n\ntype SyncScheduleRecord = {\n id: string\n integrationId: string\n entityType: string\n direction: 'import' | 'export'\n scheduleType: 'cron' | 'interval'\n scheduleValue: string\n timezone: string\n fullSync: boolean\n isEnabled: boolean\n lastRunAt: string | null\n updatedAt?: string | null\n}\n\ntype SyncSchedulesResponse = {\n items?: SyncScheduleRecord[]\n}\n\ntype SyncScheduleEditorState = {\n id?: string\n scheduleType: 'cron' | 'interval'\n scheduleValue: string\n timezone: string\n fullSync: boolean\n isEnabled: boolean\n lastRunAt: string | null\n updatedAt?: string | null\n}\n\nconst DEFAULT_TIMEZONE = Intl.DateTimeFormat().resolvedOptions().timeZone || 'UTC'\n\nfunction formatEntityTypeLabel(entityType: string): string {\n return entityType\n .replace(/[_-]+/g, ' ')\n .replace(/\\b\\w/g, (letter) => letter.toUpperCase())\n}\n\nfunction buildDefaultScheduleState(entityType: string): SyncScheduleEditorState {\n const normalized = entityType.trim().toLowerCase()\n const longerInterval = normalized === 'categories' || normalized === 'attributes'\n return {\n scheduleType: 'interval',\n scheduleValue: longerInterval ? '6h' : '1h',\n timezone: DEFAULT_TIMEZONE,\n fullSync: normalized !== 'products',\n isEnabled: true,\n lastRunAt: null,\n updatedAt: null,\n }\n}\n\nexport default function SyncRunsDashboardPage() {\n const router = useRouter()\n const [rows, setRows] = React.useState<SyncRunRow[]>([])\n const [options, setOptions] = React.useState<SyncOption[]>([])\n const [page, setPage] = React.useState(1)\n const [total, setTotal] = React.useState(0)\n const [totalPages, setTotalPages] = React.useState(1)\n const [totalIsCapped, setTotalIsCapped] = React.useState(false)\n const [search, setSearch] = React.useState('')\n const [filterValues, setFilterValues] = React.useState<FilterValues>({})\n const [isLoading, setIsLoading] = React.useState(true)\n const [isLoadingOptions, setIsLoadingOptions] = React.useState(true)\n const [selectedIntegrationId, setSelectedIntegrationId] = React.useState('')\n const [selectedEntityType, setSelectedEntityType] = React.useState('')\n const [selectedDirection, setSelectedDirection] = React.useState<'import' | 'export'>('import')\n const [batchSize, setBatchSize] = React.useState('100')\n const [fullSync, setFullSync] = React.useState(false)\n const [paramValues, setParamValues] = React.useState<Record<string, RunParameterFormValue>>({})\n const [scheduleEditor, setScheduleEditor] = React.useState<SyncScheduleEditorState>(() => buildDefaultScheduleState(''))\n const [isLoadingSchedule, setIsLoadingSchedule] = React.useState(false)\n const [isSavingSchedule, setIsSavingSchedule] = React.useState(false)\n const [isDeletingSchedule, setIsDeletingSchedule] = React.useState(false)\n const [reloadToken, setReloadToken] = React.useState(0)\n const scopeVersion = useOrganizationScopeVersion()\n const t = useT()\n const { runMutation } = useGuardedMutation<Record<string, unknown>>({\n contextId: 'data_sync.dashboard',\n })\n\n React.useEffect(() => {\n let cancelled = false\n async function load() {\n setIsLoading(true)\n const params = new URLSearchParams()\n params.set('page', String(page))\n params.set('pageSize', '20')\n if (filterValues.status) params.set('status', filterValues.status as string)\n if (filterValues.direction) params.set('direction', filterValues.direction as string)\n if (search.trim()) params.set('search', search.trim())\n const fallback: ResponsePayload = { items: [], total: 0, page, totalPages: 1 }\n const call = await apiCall<ResponsePayload>(\n `/api/data_sync/runs?${params.toString()}`,\n undefined,\n { fallback },\n )\n if (!call.ok) {\n flash(t('data_sync.dashboard.loadError'), 'error')\n if (!cancelled) setIsLoading(false)\n return\n }\n const payload = call.result ?? fallback\n if (!cancelled) {\n setRows(Array.isArray(payload.items) ? payload.items : [])\n setTotal(payload.total || 0)\n setTotalPages(payload.totalPages || 1)\n setTotalIsCapped(payload?.totalIsCapped === true)\n setIsLoading(false)\n }\n }\n load()\n return () => { cancelled = true }\n }, [page, filterValues, search, reloadToken, scopeVersion, t])\n\n React.useEffect(() => {\n let cancelled = false\n async function loadOptions() {\n setIsLoadingOptions(true)\n const fallback: SyncOptionsResponse = { items: [] }\n const call = await apiCall<SyncOptionsResponse>('/api/data_sync/options', undefined, { fallback })\n if (!cancelled) {\n if (!call.ok) {\n flash(t('data_sync.dashboard.loadError'), 'error')\n setOptions([])\n setIsLoadingOptions(false)\n return\n }\n\n const nextItems = Array.isArray(call.result?.items) ? call.result.items : []\n setOptions(nextItems)\n setSelectedIntegrationId((current) => {\n if (current && nextItems.some((item) => item.integrationId === current)) return current\n return nextItems[0]?.integrationId ?? ''\n })\n setIsLoadingOptions(false)\n }\n }\n\n void loadOptions()\n return () => { cancelled = true }\n }, [scopeVersion, t])\n\n const selectedIntegration = React.useMemo(\n () => options.find((item) => item.integrationId === selectedIntegrationId) ?? null,\n [options, selectedIntegrationId],\n )\n\n const entityOptions = React.useMemo(\n () => selectedIntegration?.supportedEntities ?? [],\n [selectedIntegration],\n )\n\n const runParameters = React.useMemo(\n () => getApplicableRunParameters(\n selectedIntegration?.runParameters,\n selectedDirection,\n // Pass the state through as-is. Before an entity is chosen it is '',\n // which matches no `entityType` and so hides scoped parameters \u2014 the\n // wanted outcome. Mapping '' to `undefined` would mean \"skip entity\n // scoping\" and show every scoped parameter instead.\n selectedEntityType,\n ),\n [selectedIntegration, selectedDirection, selectedEntityType],\n )\n\n React.useEffect(() => {\n setParamValues(buildDefaultRunParameterValues(runParameters))\n }, [runParameters])\n\n const updateParamValue = React.useCallback((key: string, value: RunParameterFormValue) => {\n setParamValues((current) => ({ ...current, [key]: value }))\n }, [])\n\n React.useEffect(() => {\n if (!selectedIntegration) {\n setSelectedEntityType('')\n return\n }\n setSelectedEntityType((current) => (\n current && selectedIntegration.supportedEntities.includes(current)\n ? current\n : (selectedIntegration.supportedEntities[0] ?? '')\n ))\n setSelectedDirection(selectedIntegration.direction === 'export' ? 'export' : 'import')\n }, [selectedIntegration])\n\n React.useEffect(() => {\n if (!selectedIntegration || !selectedEntityType) {\n setScheduleEditor(buildDefaultScheduleState(selectedEntityType))\n return\n }\n\n const currentIntegration = selectedIntegration\n let cancelled = false\n async function loadSchedule() {\n setIsLoadingSchedule(true)\n const integrationId = currentIntegration.integrationId\n const params = new URLSearchParams({\n integrationId,\n entityType: selectedEntityType,\n direction: selectedDirection,\n page: '1',\n pageSize: '1',\n })\n const fallback: SyncSchedulesResponse = { items: [] }\n const call = await apiCall<SyncSchedulesResponse>(`/api/data_sync/schedules?${params.toString()}`, undefined, { fallback })\n\n if (cancelled) return\n\n if (!call.ok) {\n setScheduleEditor(buildDefaultScheduleState(selectedEntityType))\n setIsLoadingSchedule(false)\n return\n }\n\n const record = Array.isArray(call.result?.items) ? call.result?.items[0] : undefined\n if (!record) {\n setScheduleEditor(buildDefaultScheduleState(selectedEntityType))\n setIsLoadingSchedule(false)\n return\n }\n\n setScheduleEditor({\n id: record.id,\n scheduleType: record.scheduleType,\n scheduleValue: record.scheduleValue,\n timezone: record.timezone,\n fullSync: record.fullSync,\n isEnabled: record.isEnabled,\n lastRunAt: record.lastRunAt,\n updatedAt: record.updatedAt ?? null,\n })\n setIsLoadingSchedule(false)\n }\n\n void loadSchedule()\n return () => { cancelled = true }\n }, [selectedDirection, selectedEntityType, selectedIntegration, scopeVersion])\n\n const updateScheduleEditor = React.useCallback((changes: Partial<SyncScheduleEditorState>) => {\n setScheduleEditor((current) => ({ ...current, ...changes }))\n }, [])\n\n const handleCancel = React.useCallback(async (row: SyncRunRow) => {\n // optimistic-lock-exempt: run lifecycle action endpoint (cancel), not a concurrent record edit\n const call = await apiCall(`/api/data_sync/runs/${encodeURIComponent(row.id)}/cancel`, {\n method: 'POST',\n }, { fallback: null })\n if (call.ok) {\n flash(t('data_sync.runs.detail.cancelSuccess'), 'success')\n setReloadToken((token) => token + 1)\n } else {\n flash(t('data_sync.runs.detail.cancelError'), 'error')\n }\n }, [t])\n\n const handleRetry = React.useCallback(async (row: SyncRunRow) => {\n // optimistic-lock-exempt: run lifecycle action endpoint (retry), not a concurrent record edit\n const call = await apiCall(`/api/data_sync/runs/${encodeURIComponent(row.id)}/retry`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ fromBeginning: false }),\n }, { fallback: null })\n if (call.ok) {\n flash(t('data_sync.runs.detail.retrySuccess'), 'success')\n setReloadToken((token) => token + 1)\n } else {\n flash(buildRetryFailureMessage(call.result as RetryFailureBody | null, t), 'error')\n }\n }, [t])\n\n const handleFiltersApply = React.useCallback((values: FilterValues) => {\n const next: FilterValues = {}\n Object.entries(values).forEach(([key, value]) => {\n if (value !== undefined && value !== '') next[key] = value\n })\n setFilterValues(next)\n setPage(1)\n }, [])\n\n const handleFiltersClear = React.useCallback(() => {\n setFilterValues({})\n setPage(1)\n }, [])\n\n const handleStartSync = React.useCallback(async () => {\n if (!selectedIntegration || !selectedEntityType) return\n\n const parsedBatchSize = Number.parseInt(batchSize, 10)\n if (!Number.isFinite(parsedBatchSize) || parsedBatchSize < 1 || parsedBatchSize > 1000) {\n flash(t('data_sync.dashboard.start.invalidBatchSize', 'Batch size must be between 1 and 1000.'), 'error')\n return\n }\n\n const parameters = buildRunParametersPayload(runParameters, paramValues)\n const requestBody: Record<string, unknown> = {\n integrationId: selectedIntegration.integrationId,\n entityType: selectedEntityType,\n direction: selectedDirection,\n batchSize: parsedBatchSize,\n fullSync,\n }\n if (runParameters.length > 0) requestBody.parameters = parameters\n\n try {\n const call = await runMutation({\n // optimistic-lock-exempt: starts a new sync run (create), not a concurrent record edit\n operation: () => apiCall<{ id: string }>('/api/data_sync/run', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(requestBody),\n }, { fallback: null }),\n mutationPayload: requestBody,\n context: {\n operation: 'create',\n actionId: 'start-sync-run',\n integrationId: selectedIntegration.integrationId,\n },\n })\n\n if (!call.ok || !call.result?.id) {\n flash(buildRunFailureMessage(\n call.result as RunFailureBody | null,\n t('data_sync.dashboard.start.error', 'Failed to start sync run'),\n t,\n ), 'error')\n return\n }\n\n flash(t('data_sync.dashboard.start.success', 'Sync run started'), 'success')\n setReloadToken((token) => token + 1)\n router.push(`/backend/data-sync/runs/${encodeURIComponent(call.result.id)}`)\n } catch (error) {\n const message = error instanceof Error ? error.message : t('data_sync.dashboard.start.error', 'Failed to start sync run')\n flash(message, 'error')\n }\n }, [batchSize, fullSync, paramValues, router, runMutation, runParameters, selectedDirection, selectedEntityType, selectedIntegration, t])\n\n const handleSaveSchedule = React.useCallback(async () => {\n if (!selectedIntegration || !selectedEntityType) return\n if (scheduleEditor.scheduleValue.trim().length === 0) {\n flash(t('data_sync.dashboard.schedule.invalidValue', 'Provide a schedule value before saving.'), 'error')\n return\n }\n\n setIsSavingSchedule(true)\n try {\n const call = await runMutation({\n // Keyed upsert (POST). When the editor holds an existing schedule's\n // `updatedAt`, the lock header version-checks the resolved row on the\n // server; a brand-new schedule has a null `updatedAt`, so the header is\n // empty and the create path stays unaffected.\n operation: () => withScopedApiRequestHeaders(\n buildOptimisticLockHeader(scheduleEditor.updatedAt),\n () => apiCall<SyncScheduleRecord>('/api/data_sync/schedules', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n integrationId: selectedIntegration.integrationId,\n entityType: selectedEntityType,\n direction: selectedDirection,\n scheduleType: scheduleEditor.scheduleType,\n scheduleValue: scheduleEditor.scheduleValue.trim(),\n timezone: scheduleEditor.timezone.trim() || DEFAULT_TIMEZONE,\n fullSync: scheduleEditor.fullSync,\n isEnabled: scheduleEditor.isEnabled,\n }),\n }, { fallback: null }),\n ),\n mutationPayload: {\n integrationId: selectedIntegration.integrationId,\n entityType: selectedEntityType,\n direction: selectedDirection,\n scheduleType: scheduleEditor.scheduleType,\n scheduleValue: scheduleEditor.scheduleValue.trim(),\n timezone: scheduleEditor.timezone.trim() || DEFAULT_TIMEZONE,\n fullSync: scheduleEditor.fullSync,\n isEnabled: scheduleEditor.isEnabled,\n },\n context: {\n operation: 'update',\n actionId: 'save-sync-schedule',\n integrationId: selectedIntegration.integrationId,\n },\n })\n\n if (!call.ok || !call.result) {\n const conflictError = Object.assign(\n new Error((call.result as { error?: string } | null)?.error ?? t('data_sync.dashboard.schedule.error', 'Failed to save recurring schedule')),\n {\n status: call.status,\n ...(call.result && typeof call.result === 'object' ? call.result : {}),\n },\n )\n if (surfaceRecordConflict(conflictError, t)) {\n return\n }\n flash((call.result as { error?: string } | null)?.error ?? t('data_sync.dashboard.schedule.error', 'Failed to save recurring schedule'), 'error')\n return\n }\n\n setScheduleEditor({\n id: call.result.id,\n scheduleType: call.result.scheduleType,\n scheduleValue: call.result.scheduleValue,\n timezone: call.result.timezone,\n fullSync: call.result.fullSync,\n isEnabled: call.result.isEnabled,\n lastRunAt: call.result.lastRunAt,\n updatedAt: call.result.updatedAt ?? null,\n })\n flash(t('data_sync.dashboard.schedule.success', 'Recurring schedule saved'), 'success')\n } catch (error) {\n const message = error instanceof Error ? error.message : t('data_sync.dashboard.schedule.error', 'Failed to save recurring schedule')\n flash(message, 'error')\n } finally {\n setIsSavingSchedule(false)\n }\n }, [runMutation, scheduleEditor, selectedDirection, selectedEntityType, selectedIntegration, t])\n\n const handleDeleteSchedule = React.useCallback(async () => {\n if (!scheduleEditor.id) return\n\n setIsDeletingSchedule(true)\n try {\n const call = await runMutation({\n operation: () => withScopedApiRequestHeaders(\n buildOptimisticLockHeader(scheduleEditor.updatedAt),\n () => apiCall(`/api/data_sync/schedules/${encodeURIComponent(scheduleEditor.id as string)}`, {\n method: 'DELETE',\n }, { fallback: null }),\n ),\n mutationPayload: {\n scheduleId: scheduleEditor.id,\n },\n context: {\n operation: 'delete',\n actionId: 'delete-sync-schedule',\n },\n })\n\n if (!call.ok) {\n flash((call.result as { error?: string } | null)?.error ?? t('data_sync.dashboard.schedule.deleteError', 'Failed to remove recurring schedule'), 'error')\n return\n }\n\n setScheduleEditor(buildDefaultScheduleState(selectedEntityType))\n flash(t('data_sync.dashboard.schedule.deleteSuccess', 'Recurring schedule removed'), 'success')\n } catch (error) {\n const message = error instanceof Error ? error.message : t('data_sync.dashboard.schedule.deleteError', 'Failed to remove recurring schedule')\n flash(message, 'error')\n } finally {\n setIsDeletingSchedule(false)\n }\n }, [runMutation, scheduleEditor.id, scheduleEditor.updatedAt, selectedEntityType, t])\n\n const filters: FilterDef[] = [\n {\n id: 'status',\n type: 'select',\n label: t('data_sync.dashboard.filters.status'),\n options: [\n { label: t('data_sync.dashboard.filters.allStatuses'), value: '' },\n { label: t('data_sync.dashboard.status.pending'), value: 'pending' },\n { label: t('data_sync.dashboard.status.running'), value: 'running' },\n { label: t('data_sync.dashboard.status.completed'), value: 'completed' },\n { label: t('data_sync.dashboard.status.failed'), value: 'failed' },\n { label: t('data_sync.dashboard.status.cancelled'), value: 'cancelled' },\n ],\n },\n {\n id: 'direction',\n type: 'select',\n label: t('data_sync.dashboard.columns.direction'),\n options: [\n { label: t('data_sync.dashboard.filters.allDirections'), value: '' },\n { label: t('data_sync.dashboard.direction.import'), value: 'import' },\n { label: t('data_sync.dashboard.direction.export'), value: 'export' },\n ],\n },\n ]\n\n const columns = React.useMemo<ColumnDef<SyncRunRow>[]>(() => [\n {\n accessorKey: 'integrationId',\n header: t('data_sync.dashboard.columns.integration'),\n cell: ({ row }) => <span className=\"font-medium text-sm\">{row.original.integrationId}</span>,\n },\n {\n accessorKey: 'entityType',\n header: t('data_sync.dashboard.columns.entityType'),\n },\n {\n accessorKey: 'direction',\n header: t('data_sync.dashboard.columns.direction'),\n cell: ({ row }) => (\n <Badge variant=\"outline\">\n {t(`data_sync.dashboard.direction.${row.original.direction}`)}\n </Badge>\n ),\n },\n {\n accessorKey: 'status',\n header: t('data_sync.dashboard.columns.status'),\n cell: ({ row }) => (\n <StatusBadge variant={getSyncRunStatusVariant(row.original.status)}>\n {t(`data_sync.dashboard.status.${row.original.status}`)}\n </StatusBadge>\n ),\n },\n {\n accessorKey: 'createdCount',\n header: t('data_sync.dashboard.columns.created'),\n },\n {\n accessorKey: 'updatedCount',\n header: t('data_sync.dashboard.columns.updated'),\n },\n {\n accessorKey: 'failedCount',\n header: t('data_sync.dashboard.columns.failed'),\n },\n {\n accessorKey: 'createdAt',\n header: t('data_sync.dashboard.columns.createdAt'),\n cell: ({ row }) => new Date(row.original.createdAt).toLocaleString(),\n },\n ], [t])\n\n const canStartSelectedIntegration = Boolean(\n selectedIntegration\n && selectedEntityType\n && selectedIntegration.isEnabled\n && selectedIntegration.canStartRun !== false\n && selectedIntegration.hasCredentials,\n )\n const hasSavedSchedule = Boolean(scheduleEditor.id)\n const selectedEntityLabel = selectedEntityType ? formatEntityTypeLabel(selectedEntityType) : t('data_sync.dashboard.columns.entityType')\n const integrationStateVariant = getSyncSummaryVariant(selectedIntegration?.isEnabled ? 'enabled' : 'disabled')\n const credentialsVariant = getSyncSummaryVariant(selectedIntegration?.hasCredentials ? 'ready' : 'missing')\n const scheduleVariant = getSyncSummaryVariant(\n hasSavedSchedule\n ? (scheduleEditor.isEnabled ? 'scheduled' : 'paused')\n : 'none',\n )\n\n return (\n <Page>\n <PageBody className=\"space-y-6\">\n <Card>\n <CardHeader className=\"space-y-4\">\n <div className=\"flex flex-col gap-4 lg:flex-row lg:items-start lg:justify-between\">\n <div className=\"space-y-2\">\n <div className=\"flex items-center gap-2 text-xs font-medium uppercase tracking-widest text-muted-foreground\">\n <Repeat className=\"size-4\" />\n <span>{t('data_sync.dashboard.start.eyebrow', 'Run once or keep it recurring')}</span>\n </div>\n <div className=\"space-y-1\">\n <CardTitle>{t('data_sync.dashboard.start.title', 'Start or schedule a sync')}</CardTitle>\n <p className=\"max-w-3xl text-sm text-muted-foreground\">\n {t('data_sync.dashboard.start.description', 'Pick a sync target, launch an ad-hoc run, or save a recurring schedule for the same entity and direction from this page.')}\n </p>\n </div>\n </div>\n {selectedIntegration ? (\n <Button asChild variant=\"outline\">\n <Link href={selectedIntegration.settingsPath}>\n <Settings2 className=\"mr-2 size-4\" />\n {t('integrations.marketplace.configure')}\n </Link>\n </Button>\n ) : null}\n </div>\n\n {selectedIntegration ? (\n <div className=\"flex flex-wrap gap-2\">\n <Badge variant=\"outline\" className=\"gap-1.5\">\n <PlugZap className=\"size-3.5\" />\n {selectedIntegration.title}\n </Badge>\n <Badge variant=\"outline\" className=\"gap-1.5\">\n <ArrowRightLeft className=\"size-3.5\" />\n {t(`data_sync.dashboard.direction.${selectedDirection}`)}\n </Badge>\n <Badge variant={integrationStateVariant} className=\"gap-1.5\">\n <ShieldCheck className=\"size-3.5\" />\n {selectedIntegration.isEnabled\n ? t('data_sync.dashboard.start.status.enabled', 'Integration enabled')\n : t('data_sync.dashboard.start.status.disabled', 'Integration disabled')}\n </Badge>\n <Badge variant={credentialsVariant} className=\"gap-1.5\">\n <PlugZap className=\"size-3.5\" />\n {selectedIntegration.hasCredentials\n ? t('data_sync.dashboard.start.status.credentialsReady', 'Credentials ready')\n : t('data_sync.dashboard.start.status.credentialsMissing', 'Credentials missing')}\n </Badge>\n <Badge variant={scheduleVariant} className=\"gap-1.5\">\n <CalendarClock className=\"size-3.5\" />\n {hasSavedSchedule\n ? (scheduleEditor.isEnabled\n ? t('data_sync.dashboard.schedule.status.enabled', 'Recurring schedule active')\n : t('data_sync.dashboard.schedule.status.disabled', 'Recurring schedule paused'))\n : t('data_sync.dashboard.schedule.status.none', 'No recurring schedule')}\n </Badge>\n </div>\n ) : null}\n </CardHeader>\n <CardContent className=\"space-y-6\">\n <div className=\"grid gap-4 xl:grid-cols-3\">\n <div className=\"space-y-2 xl:col-span-1\">\n <Label className=\"flex items-center gap-2 text-sm font-medium\">\n <PlugZap className=\"size-4 text-muted-foreground\" />\n <span>{t('data_sync.dashboard.columns.integration')}</span>\n </Label>\n <Select\n value={selectedIntegrationId || undefined}\n onValueChange={(value) => setSelectedIntegrationId(value ?? '')}\n disabled={isLoadingOptions || options.length === 0}\n >\n <SelectTrigger size=\"lg\">\n <SelectValue\n placeholder={\n options.length === 0\n ? t('integrations.marketplace.noResults', 'No integrations found')\n : undefined\n }\n />\n </SelectTrigger>\n <SelectContent>\n {options.map((item) => (\n <SelectItem key={item.integrationId} value={item.integrationId}>\n {item.title}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n <div className=\"space-y-2\">\n <Label className=\"flex items-center gap-2 text-sm font-medium\">\n <Boxes className=\"size-4 text-muted-foreground\" />\n <span>{t('data_sync.dashboard.columns.entityType')}</span>\n </Label>\n <Select\n value={selectedEntityType || undefined}\n onValueChange={(value) => setSelectedEntityType(value ?? '')}\n disabled={entityOptions.length === 0}\n >\n <SelectTrigger size=\"lg\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {entityOptions.map((entityType) => (\n <SelectItem key={entityType} value={entityType}>\n {formatEntityTypeLabel(entityType)}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n <div className=\"space-y-2\">\n <Label className=\"flex items-center gap-2 text-sm font-medium\">\n <ArrowRightLeft className=\"size-4 text-muted-foreground\" />\n <span>{t('data_sync.dashboard.columns.direction')}</span>\n </Label>\n <Select\n value={selectedDirection}\n onValueChange={(value) => setSelectedDirection(value === 'export' ? 'export' : 'import')}\n disabled={selectedIntegration?.direction !== 'bidirectional'}\n >\n <SelectTrigger size=\"lg\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"import\">{t('data_sync.dashboard.direction.import')}</SelectItem>\n {(selectedIntegration?.direction === 'bidirectional' || selectedIntegration?.direction === 'export') ? (\n <SelectItem value=\"export\">{t('data_sync.dashboard.direction.export')}</SelectItem>\n ) : null}\n </SelectContent>\n </Select>\n </div>\n </div>\n\n {selectedIntegration?.description ? (\n <p className=\"text-sm text-muted-foreground\">{selectedIntegration.description}</p>\n ) : null}\n\n <div className=\"grid gap-4 xl:grid-cols-2\">\n <div className=\"rounded-xl border bg-muted/30 p-4\">\n <div className=\"flex items-start justify-between gap-3\">\n <div className=\"space-y-1\">\n <div className=\"flex items-center gap-2\">\n <Play className=\"size-4 text-primary\" />\n <h3 className=\"text-sm font-semibold\">{t('data_sync.dashboard.start.runNowTitle', 'Run once now')}</h3>\n </div>\n <p className=\"text-sm text-muted-foreground\">\n {t('data_sync.dashboard.start.runNowDescription', 'Use this for the next immediate sync. Batch size and full-sync mode apply only to this manual run.')}\n </p>\n </div>\n <Badge variant=\"outline\">{selectedEntityLabel}</Badge>\n </div>\n\n <Separator className=\"my-4\" />\n\n <div className=\"grid gap-4 sm:grid-cols-[minmax(0,180px)_1fr]\">\n <div className=\"space-y-2\">\n <Label className=\"flex items-center gap-2 text-sm font-medium\">\n <Gauge className=\"size-4 text-muted-foreground\" />\n <span>{t('data_sync.dashboard.start.batchSize', 'Batch size')}</span>\n </Label>\n <Input\n value={batchSize}\n onChange={(event) => setBatchSize(event.target.value)}\n inputMode=\"numeric\"\n />\n </div>\n <div className=\"rounded-lg border bg-background p-3\">\n <div className=\"flex items-center justify-between gap-3\">\n <div className=\"space-y-1\">\n <Label className=\"text-sm font-medium\">{t('data_sync.dashboard.start.fullSync', 'Run as full sync')}</Label>\n <p className=\"text-xs text-muted-foreground\">\n {t('data_sync.dashboard.start.fullSyncHelp', 'Ignore the saved cursor and process the entire source again for this run.')}\n </p>\n </div>\n <Switch checked={fullSync} onCheckedChange={setFullSync} />\n </div>\n </div>\n </div>\n\n {runParameters.length > 0 ? (\n <div className=\"mt-4 space-y-3\">\n <div className=\"flex items-center gap-2\">\n <Settings2 className=\"size-4 text-muted-foreground\" />\n <h4 className=\"text-sm font-semibold\">\n {t('data_sync.dashboard.start.parameters', 'Run parameters')}\n </h4>\n </div>\n <p className=\"text-xs text-muted-foreground\">\n {t('data_sync.dashboard.start.parametersHelp', 'Optional values this integration accepts for the manual run.')}\n </p>\n <RunParameterFields\n params={runParameters}\n values={paramValues}\n onChange={updateParamValue}\n />\n </div>\n ) : null}\n\n <div className=\"mt-4 flex flex-wrap items-center justify-between gap-3\">\n <p className=\"text-xs text-muted-foreground\">\n {t('data_sync.dashboard.start.runNowFootnote', 'Manual runs show progress immediately and land on the run detail page after launch.')}\n </p>\n <Button\n type=\"button\"\n onClick={() => void handleStartSync()}\n disabled={!canStartSelectedIntegration}\n >\n <Play className=\"mr-2 size-4\" />\n {t('data_sync.dashboard.start.submit', 'Start sync')}\n </Button>\n </div>\n </div>\n\n <div className=\"rounded-xl border bg-muted/30 p-4\">\n <div className=\"flex items-start justify-between gap-3\">\n <div className=\"space-y-1\">\n <div className=\"flex items-center gap-2\">\n <CalendarClock className=\"size-4 text-primary\" />\n <h3 className=\"text-sm font-semibold\">{t('data_sync.dashboard.schedule.title', 'Recurring schedule')}</h3>\n </div>\n <p className=\"text-sm text-muted-foreground\">\n {t('data_sync.dashboard.schedule.description', 'Save a repeating schedule for the selected integration, entity, and direction without leaving this dashboard.')}\n </p>\n </div>\n <Badge variant=\"outline\">\n {hasSavedSchedule\n ? (scheduleEditor.isEnabled\n ? t('data_sync.dashboard.schedule.status.shortEnabled', 'Scheduled')\n : t('data_sync.dashboard.schedule.status.shortDisabled', 'Paused'))\n : t('data_sync.dashboard.schedule.status.shortNone', 'One-time only')}\n </Badge>\n </div>\n\n <Separator className=\"my-4\" />\n\n <div className=\"grid gap-4 sm:grid-cols-2\">\n <div className=\"space-y-2\">\n <Label className=\"flex items-center gap-2 text-sm font-medium\">\n <Clock3 className=\"size-4 text-muted-foreground\" />\n <span>{t('data_sync.dashboard.schedule.type', 'Schedule type')}</span>\n </Label>\n <Select\n value={scheduleEditor.scheduleType}\n onValueChange={(value) => updateScheduleEditor({\n scheduleType: value === 'cron' ? 'cron' : 'interval',\n })}\n disabled={isLoadingSchedule || isSavingSchedule || isDeletingSchedule || !selectedIntegration || !selectedEntityType}\n >\n <SelectTrigger size=\"lg\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"interval\">{t('data_sync.dashboard.schedule.interval', 'Interval')}</SelectItem>\n <SelectItem value=\"cron\">{t('data_sync.dashboard.schedule.cron', 'Cron')}</SelectItem>\n </SelectContent>\n </Select>\n </div>\n <div className=\"space-y-2\">\n <Label className=\"flex items-center gap-2 text-sm font-medium\">\n <CalendarClock className=\"size-4 text-muted-foreground\" />\n <span>\n {scheduleEditor.scheduleType === 'cron'\n ? t('data_sync.dashboard.schedule.cronValue', 'Cron expression')\n : t('data_sync.dashboard.schedule.intervalValue', 'Interval')}\n </span>\n </Label>\n <Input\n value={scheduleEditor.scheduleValue}\n onChange={(event) => updateScheduleEditor({ scheduleValue: event.target.value })}\n disabled={isLoadingSchedule || isSavingSchedule || isDeletingSchedule || !selectedIntegration || !selectedEntityType}\n placeholder={scheduleEditor.scheduleType === 'cron' ? '0 * * * *' : '1h'}\n />\n <p className=\"text-xs text-muted-foreground\">\n {scheduleEditor.scheduleType === 'cron'\n ? t('data_sync.dashboard.schedule.cronHelp', 'Example: `0 * * * *` runs at the start of every hour.')\n : t('data_sync.dashboard.schedule.intervalHelp', 'Example: `1h`, `6h`, or `24h` for repeating intervals.')}\n </p>\n </div>\n <div className=\"space-y-2 sm:col-span-2\">\n <Label className=\"flex items-center gap-2 text-sm font-medium\">\n <Clock3 className=\"size-4 text-muted-foreground\" />\n <span>{t('data_sync.dashboard.schedule.timezone', 'Timezone')}</span>\n </Label>\n <Input\n value={scheduleEditor.timezone}\n onChange={(event) => updateScheduleEditor({ timezone: event.target.value })}\n disabled={isLoadingSchedule || isSavingSchedule || isDeletingSchedule || !selectedIntegration || !selectedEntityType}\n />\n </div>\n </div>\n\n <div className=\"mt-4 grid gap-3\">\n <div className=\"rounded-lg border bg-background p-3\">\n <div className=\"flex items-center justify-between gap-3\">\n <div className=\"space-y-1\">\n <Label className=\"text-sm font-medium\">{t('data_sync.dashboard.schedule.fullSync', 'Run scheduled jobs as full sync')}</Label>\n <p className=\"text-xs text-muted-foreground\">\n {t('data_sync.dashboard.schedule.fullSyncHelp', 'When enabled, every recurring run starts from the beginning instead of the saved cursor.')}\n </p>\n </div>\n <Switch\n checked={scheduleEditor.fullSync}\n onCheckedChange={(checked) => updateScheduleEditor({ fullSync: checked })}\n disabled={isLoadingSchedule || isSavingSchedule || isDeletingSchedule || !selectedIntegration || !selectedEntityType}\n />\n </div>\n </div>\n <div className=\"rounded-lg border bg-background p-3\">\n <div className=\"flex items-center justify-between gap-3\">\n <div className=\"space-y-1\">\n <Label className=\"text-sm font-medium\">{t('data_sync.dashboard.schedule.enabled', 'Schedule enabled')}</Label>\n <p className=\"text-xs text-muted-foreground\">\n {t('data_sync.dashboard.schedule.enabledHelp', 'Pause the recurring job without deleting the schedule definition.')}\n </p>\n </div>\n <Switch\n checked={scheduleEditor.isEnabled}\n onCheckedChange={(checked) => updateScheduleEditor({ isEnabled: checked })}\n disabled={isLoadingSchedule || isSavingSchedule || isDeletingSchedule || !selectedIntegration || !selectedEntityType}\n />\n </div>\n </div>\n </div>\n\n <div className=\"mt-4 flex flex-wrap items-center justify-between gap-3\">\n <div className=\"space-y-1 text-xs text-muted-foreground\">\n <div>\n {hasSavedSchedule\n ? (scheduleEditor.lastRunAt\n ? t('data_sync.dashboard.schedule.lastRun', 'Last scheduled run: {value}', {\n value: new Date(scheduleEditor.lastRunAt).toLocaleString(),\n })\n : t('data_sync.dashboard.schedule.neverRun', 'Saved, but no scheduled execution has completed yet.'))\n : t('data_sync.dashboard.schedule.none', 'No recurring schedule saved for this target yet.')}\n </div>\n </div>\n <div className=\"flex flex-wrap gap-2\">\n <Button\n type=\"button\"\n variant=\"outline\"\n onClick={() => void handleDeleteSchedule()}\n disabled={!hasSavedSchedule || isDeletingSchedule}\n >\n {isDeletingSchedule\n ? t('data_sync.dashboard.schedule.deleting', 'Removing...')\n : t('data_sync.dashboard.schedule.delete', 'Remove schedule')}\n </Button>\n <Button\n type=\"button\"\n variant=\"outline\"\n onClick={() => void handleSaveSchedule()}\n disabled={isSavingSchedule || !selectedIntegration || !selectedEntityType}\n >\n <CalendarClock className=\"mr-2 size-4\" />\n {isSavingSchedule\n ? t('data_sync.dashboard.schedule.saving', 'Saving...')\n : t('data_sync.dashboard.schedule.save', 'Save recurring schedule')}\n </Button>\n </div>\n </div>\n </div>\n </div>\n\n {selectedIntegration && !selectedIntegration.isEnabled ? (\n <Alert status=\"warning\">\n <AlertDescription>\n {t('integrations.detail.state.disabled', 'This integration is disabled. Enable it on the integration settings page before starting a sync.')}\n </AlertDescription>\n </Alert>\n ) : null}\n {selectedIntegration && !selectedIntegration.hasCredentials ? (\n <Alert status=\"warning\">\n <AlertDescription>\n {t('integrations.detail.credentials.notConfigured', 'Credentials are not configured yet. Save the integration credentials before starting a sync.')}\n </AlertDescription>\n </Alert>\n ) : null}\n {selectedIntegration && selectedIntegration.canStartRun === false ? (\n <Alert status=\"information\">\n <AlertDescription>\n {t('data_sync.dashboard.start.providerManaged', 'This integration starts sync runs from its own setup flow. Open the integration settings page to continue.')}\n </AlertDescription>\n </Alert>\n ) : null}\n </CardContent>\n </Card>\n\n <DataTable\n stickyActionsColumn\n title={t('data_sync.dashboard.title')}\n columns={columns}\n data={rows}\n filters={filters}\n filterValues={filterValues}\n onFiltersApply={handleFiltersApply}\n onFiltersClear={handleFiltersClear}\n searchValue={search}\n onSearchChange={(value) => { setSearch(value); setPage(1) }}\n searchPlaceholder={t('data_sync.dashboard.searchPlaceholder')}\n perspective={{ tableId: extensionPoints.hosts.runsTable.tableId }}\n onRowClick={(row) => {\n router.push(`/backend/data-sync/runs/${encodeURIComponent(row.id)}`)\n }}\n rowActions={(row) => (\n <RowActions items={[\n {\n id: 'view',\n label: t('data_sync.dashboard.actions.view'),\n onSelect: () => { router.push(`/backend/data-sync/runs/${encodeURIComponent(row.id)}`) },\n },\n ...(row.status === 'running' ? [{\n id: 'cancel',\n label: t('data_sync.runs.detail.cancel'),\n destructive: true,\n onSelect: () => { void handleCancel(row) },\n }] : []),\n ...(row.status === 'failed' ? [{\n id: 'retry',\n label: t('data_sync.runs.detail.retry'),\n onSelect: () => { void handleRetry(row) },\n }] : []),\n ]} />\n )}\n pagination={{ page, pageSize: 20, total, totalPages, totalIsCapped, onPageChange: setPage }}\n isLoading={isLoading}\n />\n </PageBody>\n </Page>\n )\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
async function failAbandonedRun(payload, info) {
|
|
2
|
+
const data = payload;
|
|
3
|
+
const runId = data?.payload?.runId;
|
|
4
|
+
const scope = data?.payload?.scope;
|
|
5
|
+
if (typeof runId !== "string" || typeof scope?.organizationId !== "string" || typeof scope?.tenantId !== "string") return;
|
|
6
|
+
const runScope = { organizationId: scope.organizationId, tenantId: scope.tenantId };
|
|
7
|
+
const errorMessage = `the queue abandoned this run's job without running it: ${info.reason}`;
|
|
8
|
+
const { createRequestContainer } = await import("@open-mercato/shared/lib/di/container");
|
|
9
|
+
const container = await createRequestContainer();
|
|
10
|
+
const runService = container.resolve("dataSyncRunService");
|
|
11
|
+
const run = await runService.markStatus(runId, "failed", runScope, errorMessage);
|
|
12
|
+
if (!run?.progressJobId) return;
|
|
13
|
+
const progressService = container.resolve("progressService");
|
|
14
|
+
await progressService.failJob(run.progressJobId, { errorMessage }, runScope);
|
|
15
|
+
}
|
|
16
|
+
export {
|
|
17
|
+
failAbandonedRun
|
|
18
|
+
};
|
|
19
|
+
//# sourceMappingURL=abandoned-run.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../src/modules/data_sync/lib/abandoned-run.ts"],
|
|
4
|
+
"sourcesContent": ["import type { AbandonedJobInfo } from '@open-mercato/queue'\n\n/**\n * Fail the run behind a job the queue gave up on.\n *\n * A resumable data_sync job is ONE long-lived job for a whole run \u2014 `runImport` is entered once and\n * stays there for the duration, which for a full backfill is days. Every worker death (a deploy, an\n * OOM) therefore stalls that job, and BullMQ's stalled counter is cumulative for the job's life:\n * past `DATA_SYNC_MAX_STALLED_COUNT` it writes a deferred failure and the next worker fails the job\n * BEFORE calling the processor.\n *\n * So `runImport` never runs, never throws, and never finalizes \u2014 and `sync_runs` is left saying\n * `running` for a run nothing is running, forever. That is the residual state `queue-policy.ts`\n * describes as what remains after a job exhausts its stall budget; this repairs it.\n *\n * It only ever moves a non-terminal run: `markStatus` refuses to overwrite `completed` / `failed` /\n * `cancelled`, so a job abandoned after its run has ended is a no-op. `onJobAbandoned` may deliver\n * the same job more than once, which for the same reason is also a no-op.\n *\n * The run's progress job is failed alongside it, the way the worker's own catch path does in\n * `workers/sync-import.ts`. Without that the two halves disagree \u2014 the run reads `failed` while the\n * operation still shows as in flight in the top bar \u2014 until the progress staleness sweep eventually\n * fails it with a generic no-heartbeat message instead of the reason the queue recorded.\n */\nexport async function failAbandonedRun(payload: unknown, info: AbandonedJobInfo): Promise<void> {\n const data = payload as { payload?: { runId?: unknown; scope?: { organizationId?: unknown; tenantId?: unknown } } } | undefined\n const runId = data?.payload?.runId\n const scope = data?.payload?.scope\n // Nothing to repair without both: the run row is keyed by id AND tenant scope.\n if (typeof runId !== 'string' || typeof scope?.organizationId !== 'string' || typeof scope?.tenantId !== 'string') return\n\n const runScope = { organizationId: scope.organizationId, tenantId: scope.tenantId }\n const errorMessage = `the queue abandoned this run's job without running it: ${info.reason}`\n\n const { createRequestContainer } = await import('@open-mercato/shared/lib/di/container')\n const container = await createRequestContainer()\n const runService = container.resolve('dataSyncRunService') as {\n markStatus(\n runId: string,\n status: string,\n scope: { organizationId: string; tenantId: string },\n error?: string,\n ): Promise<{ progressJobId?: string | null } | null>\n }\n const run = await runService.markStatus(runId, 'failed', runScope, errorMessage)\n if (!run?.progressJobId) return\n\n const progressService = container.resolve('progressService') as {\n failJob(\n jobId: string,\n input: { errorMessage: string },\n ctx: { organizationId: string; tenantId: string },\n ): Promise<unknown>\n }\n await progressService.failJob(run.progressJobId, { errorMessage }, runScope)\n}\n"],
|
|
5
|
+
"mappings": "AAwBA,eAAsB,iBAAiB,SAAkB,MAAuC;AAC9F,QAAM,OAAO;AACb,QAAM,QAAQ,MAAM,SAAS;AAC7B,QAAM,QAAQ,MAAM,SAAS;AAE7B,MAAI,OAAO,UAAU,YAAY,OAAO,OAAO,mBAAmB,YAAY,OAAO,OAAO,aAAa,SAAU;AAEnH,QAAM,WAAW,EAAE,gBAAgB,MAAM,gBAAgB,UAAU,MAAM,SAAS;AAClF,QAAM,eAAe,0DAA0D,KAAK,MAAM;AAE1F,QAAM,EAAE,uBAAuB,IAAI,MAAM,OAAO,uCAAuC;AACvF,QAAM,YAAY,MAAM,uBAAuB;AAC/C,QAAM,aAAa,UAAU,QAAQ,oBAAoB;AAQzD,QAAM,MAAM,MAAM,WAAW,WAAW,OAAO,UAAU,UAAU,YAAY;AAC/E,MAAI,CAAC,KAAK,cAAe;AAEzB,QAAM,kBAAkB,UAAU,QAAQ,iBAAiB;AAO3D,QAAM,gBAAgB,QAAQ,IAAI,eAAe,EAAE,aAAa,GAAG,QAAQ;AAC7E;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -50,6 +50,9 @@ function applyExportCounters(batch) {
|
|
|
50
50
|
};
|
|
51
51
|
}
|
|
52
52
|
const HEARTBEAT_TICK_MS = STALE_JOB_TIMEOUT_SECONDS * 1e3 / 4;
|
|
53
|
+
function isAbortError(error) {
|
|
54
|
+
return typeof error === "object" && error !== null && error.name === "AbortError";
|
|
55
|
+
}
|
|
53
56
|
async function* withHeartbeat(source, tick, intervalMs) {
|
|
54
57
|
const iterator = source[Symbol.asyncIterator]();
|
|
55
58
|
try {
|
|
@@ -122,6 +125,25 @@ function createSyncEngine(deps) {
|
|
|
122
125
|
});
|
|
123
126
|
};
|
|
124
127
|
}
|
|
128
|
+
function makeCancellationTick(progressJobId, scope, controller) {
|
|
129
|
+
if (!progressJobId) return () => {
|
|
130
|
+
};
|
|
131
|
+
let inFlight = false;
|
|
132
|
+
return () => {
|
|
133
|
+
if (inFlight || controller.signal.aborted) return;
|
|
134
|
+
inFlight = true;
|
|
135
|
+
progressService.isCancellationRequested(progressJobId, scope.tenantId, scope.organizationId).then((cancelled) => {
|
|
136
|
+
if (cancelled) controller.abort();
|
|
137
|
+
}).catch((error) => {
|
|
138
|
+
logger.warn("Cancellation poll failed", {
|
|
139
|
+
progressJobId,
|
|
140
|
+
error: error instanceof Error ? error.message : String(error)
|
|
141
|
+
});
|
|
142
|
+
}).finally(() => {
|
|
143
|
+
inFlight = false;
|
|
144
|
+
});
|
|
145
|
+
};
|
|
146
|
+
}
|
|
125
147
|
async function refreshCoverageSnapshots(entityTypes, scope) {
|
|
126
148
|
if (!entityTypes || entityTypes.length === 0) return;
|
|
127
149
|
await Promise.allSettled(
|
|
@@ -424,8 +446,12 @@ function createSyncEngine(deps) {
|
|
|
424
446
|
let processedCount = await seedProcessedCount(run.progressJobId, scope);
|
|
425
447
|
let totalCount = null;
|
|
426
448
|
let committedBatches = activeRun.batchesCompleted ?? 0;
|
|
449
|
+
let streamReportedDone = false;
|
|
427
450
|
const runTrace = captureTelemetryTrace();
|
|
428
451
|
const spanAttributes = runSpanAttributes(run, providerKey, scope);
|
|
452
|
+
const cancellation = new AbortController();
|
|
453
|
+
const heartbeat = makeHeartbeatTick(run.progressJobId, scope);
|
|
454
|
+
const pollCancellation = makeCancellationTick(run.progressJobId, scope, cancellation);
|
|
429
455
|
try {
|
|
430
456
|
const streamResult = await forEachBatch(
|
|
431
457
|
withHeartbeat(
|
|
@@ -437,9 +463,18 @@ function createSyncEngine(deps) {
|
|
|
437
463
|
mapping,
|
|
438
464
|
scope: { organizationId: scope.organizationId, tenantId: scope.tenantId },
|
|
439
465
|
runId: run.id,
|
|
440
|
-
parameters: run.parameters ?? {}
|
|
466
|
+
parameters: run.parameters ?? {},
|
|
467
|
+
signal: cancellation.signal
|
|
441
468
|
}),
|
|
442
|
-
|
|
469
|
+
// `finally` so a synchronous throw from the heartbeat cannot also stop cancellation
|
|
470
|
+
// from being observed for the rest of the run.
|
|
471
|
+
() => {
|
|
472
|
+
try {
|
|
473
|
+
heartbeat();
|
|
474
|
+
} finally {
|
|
475
|
+
pollCancellation();
|
|
476
|
+
}
|
|
477
|
+
},
|
|
443
478
|
HEARTBEAT_TICK_MS
|
|
444
479
|
),
|
|
445
480
|
{
|
|
@@ -453,7 +488,7 @@ function createSyncEngine(deps) {
|
|
|
453
488
|
"data_sync.batch_index": batch.batchIndex,
|
|
454
489
|
"data_sync.batch_size": batch.items.length
|
|
455
490
|
});
|
|
456
|
-
if (run.progressJobId && await progressService.isCancellationRequested(run.progressJobId, scope.tenantId, scope.organizationId)) {
|
|
491
|
+
if (cancellation.signal.aborted || run.progressJobId && await progressService.isCancellationRequested(run.progressJobId, scope.tenantId, scope.organizationId)) {
|
|
457
492
|
await finalizeRun(run.id, "cancelled", scope, void 0, operationalTelemetry);
|
|
458
493
|
return "stop";
|
|
459
494
|
}
|
|
@@ -479,6 +514,7 @@ function createSyncEngine(deps) {
|
|
|
479
514
|
{ expectedBatchesCompleted: committedBatches, persistSharedCursor }
|
|
480
515
|
);
|
|
481
516
|
committedBatches += 1;
|
|
517
|
+
streamReportedDone = batch.hasMore === false;
|
|
482
518
|
await updateProgress(run.progressJobId, processedCount, totalCount, scope);
|
|
483
519
|
await refreshCoverageSnapshots(batch.refreshCoverageEntityTypes, scope);
|
|
484
520
|
await logImportItemFailures(run.id, run.integrationId, batch.items, scope);
|
|
@@ -510,6 +546,10 @@ function createSyncEngine(deps) {
|
|
|
510
546
|
});
|
|
511
547
|
return;
|
|
512
548
|
}
|
|
549
|
+
if (cancellation.signal.aborted && isAbortError(error)) {
|
|
550
|
+
await finalizeRun(run.id, "cancelled", scope, void 0, operationalTelemetry);
|
|
551
|
+
return;
|
|
552
|
+
}
|
|
513
553
|
const message = error instanceof Error ? error.message : "Sync import failed";
|
|
514
554
|
await integrationLogService.write(
|
|
515
555
|
{
|
|
@@ -523,6 +563,10 @@ function createSyncEngine(deps) {
|
|
|
523
563
|
await finalizeRun(run.id, "failed", scope, message, operationalTelemetry);
|
|
524
564
|
return;
|
|
525
565
|
}
|
|
566
|
+
if (cancellation.signal.aborted && !streamReportedDone) {
|
|
567
|
+
await finalizeRun(run.id, "cancelled", scope, void 0, operationalTelemetry);
|
|
568
|
+
return;
|
|
569
|
+
}
|
|
526
570
|
await finalizeRun(run.id, "completed", scope, void 0, operationalTelemetry);
|
|
527
571
|
},
|
|
528
572
|
async runExport(runId, batchSize, scope) {
|
|
@@ -596,8 +640,12 @@ function createSyncEngine(deps) {
|
|
|
596
640
|
const mapping = await resolveMapping(adapter, run.entityType, scope);
|
|
597
641
|
let processedCount = await seedProcessedCount(run.progressJobId, scope);
|
|
598
642
|
let committedBatches = activeRun.batchesCompleted ?? 0;
|
|
643
|
+
let streamReportedDone = false;
|
|
599
644
|
const runTrace = captureTelemetryTrace();
|
|
600
645
|
const spanAttributes = runSpanAttributes(run, providerKey, scope);
|
|
646
|
+
const cancellation = new AbortController();
|
|
647
|
+
const heartbeat = makeHeartbeatTick(run.progressJobId, scope);
|
|
648
|
+
const pollCancellation = makeCancellationTick(run.progressJobId, scope, cancellation);
|
|
601
649
|
try {
|
|
602
650
|
const streamResult = await forEachBatch(
|
|
603
651
|
withHeartbeat(
|
|
@@ -609,9 +657,18 @@ function createSyncEngine(deps) {
|
|
|
609
657
|
mapping,
|
|
610
658
|
scope: { organizationId: scope.organizationId, tenantId: scope.tenantId },
|
|
611
659
|
runId: run.id,
|
|
612
|
-
parameters: run.parameters ?? {}
|
|
660
|
+
parameters: run.parameters ?? {},
|
|
661
|
+
signal: cancellation.signal
|
|
613
662
|
}),
|
|
614
|
-
|
|
663
|
+
// `finally` so a synchronous throw from the heartbeat cannot also stop cancellation
|
|
664
|
+
// from being observed for the rest of the run.
|
|
665
|
+
() => {
|
|
666
|
+
try {
|
|
667
|
+
heartbeat();
|
|
668
|
+
} finally {
|
|
669
|
+
pollCancellation();
|
|
670
|
+
}
|
|
671
|
+
},
|
|
615
672
|
HEARTBEAT_TICK_MS
|
|
616
673
|
),
|
|
617
674
|
{
|
|
@@ -625,7 +682,7 @@ function createSyncEngine(deps) {
|
|
|
625
682
|
"data_sync.batch_index": batch.batchIndex,
|
|
626
683
|
"data_sync.batch_size": batch.results.length
|
|
627
684
|
});
|
|
628
|
-
if (run.progressJobId && await progressService.isCancellationRequested(run.progressJobId, scope.tenantId, scope.organizationId)) {
|
|
685
|
+
if (cancellation.signal.aborted || run.progressJobId && await progressService.isCancellationRequested(run.progressJobId, scope.tenantId, scope.organizationId)) {
|
|
629
686
|
await finalizeRun(run.id, "cancelled", scope, void 0, operationalTelemetry);
|
|
630
687
|
return "stop";
|
|
631
688
|
}
|
|
@@ -651,6 +708,7 @@ function createSyncEngine(deps) {
|
|
|
651
708
|
{ expectedBatchesCompleted: committedBatches, persistSharedCursor }
|
|
652
709
|
);
|
|
653
710
|
committedBatches += 1;
|
|
711
|
+
streamReportedDone = batch.hasMore === false;
|
|
654
712
|
await updateProgress(run.progressJobId, processedCount, null, scope);
|
|
655
713
|
await logExportItemFailures(run.id, run.integrationId, batch.results, scope);
|
|
656
714
|
await writeOperationalLog({
|
|
@@ -680,6 +738,10 @@ function createSyncEngine(deps) {
|
|
|
680
738
|
});
|
|
681
739
|
return;
|
|
682
740
|
}
|
|
741
|
+
if (cancellation.signal.aborted && isAbortError(error)) {
|
|
742
|
+
await finalizeRun(run.id, "cancelled", scope, void 0, operationalTelemetry);
|
|
743
|
+
return;
|
|
744
|
+
}
|
|
683
745
|
const message = error instanceof Error ? error.message : "Sync export failed";
|
|
684
746
|
await integrationLogService.write(
|
|
685
747
|
{
|
|
@@ -693,6 +755,10 @@ function createSyncEngine(deps) {
|
|
|
693
755
|
await finalizeRun(run.id, "failed", scope, message, operationalTelemetry);
|
|
694
756
|
return;
|
|
695
757
|
}
|
|
758
|
+
if (cancellation.signal.aborted && !streamReportedDone) {
|
|
759
|
+
await finalizeRun(run.id, "cancelled", scope, void 0, operationalTelemetry);
|
|
760
|
+
return;
|
|
761
|
+
}
|
|
696
762
|
await finalizeRun(run.id, "completed", scope, void 0, operationalTelemetry);
|
|
697
763
|
}
|
|
698
764
|
};
|