@open-mercato/core 0.6.5-develop.4534.1.b459babe6d → 0.6.5-develop.4559.1.839e136509
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/AGENTS.md +5 -0
- package/dist/generated/entities/role/index.js +3 -1
- package/dist/generated/entities/role/index.js.map +2 -2
- package/dist/generated/entities/user/index.js +3 -1
- package/dist/generated/entities/user/index.js.map +2 -2
- package/dist/generated/entity-fields-registry.js +2 -0
- package/dist/generated/entity-fields-registry.js.map +2 -2
- package/dist/helpers/integration/communicationChannelsFixtures.js.map +2 -2
- package/dist/helpers/integration/dbFixtures.js +2 -1
- package/dist/helpers/integration/dbFixtures.js.map +2 -2
- package/dist/helpers/integration/optimisticLockUi.js +104 -0
- package/dist/helpers/integration/optimisticLockUi.js.map +7 -0
- package/dist/helpers/integration/salesFixtures.js +17 -0
- package/dist/helpers/integration/salesFixtures.js.map +2 -2
- package/dist/modules/api_keys/backend/api-keys/page.js +9 -5
- package/dist/modules/api_keys/backend/api-keys/page.js.map +2 -2
- package/dist/modules/attachments/components/AttachmentPartitionSettings.js +17 -9
- package/dist/modules/attachments/components/AttachmentPartitionSettings.js.map +2 -2
- package/dist/modules/auth/api/roles/acl/route.js +32 -13
- package/dist/modules/auth/api/roles/acl/route.js.map +2 -2
- package/dist/modules/auth/api/roles/route.js +3 -1
- package/dist/modules/auth/api/roles/route.js.map +2 -2
- package/dist/modules/auth/api/sidebar/preferences/route.js +71 -3
- package/dist/modules/auth/api/sidebar/preferences/route.js.map +2 -2
- package/dist/modules/auth/api/users/acl/route.js +42 -19
- package/dist/modules/auth/api/users/acl/route.js.map +2 -2
- package/dist/modules/auth/api/users/route.js +3 -1
- package/dist/modules/auth/api/users/route.js.map +2 -2
- package/dist/modules/auth/backend/roles/[id]/edit/page.js +24 -4
- package/dist/modules/auth/backend/roles/[id]/edit/page.js.map +2 -2
- package/dist/modules/auth/backend/roles/page.js +8 -4
- package/dist/modules/auth/backend/roles/page.js.map +2 -2
- package/dist/modules/auth/backend/users/[id]/edit/page.js +27 -5
- package/dist/modules/auth/backend/users/[id]/edit/page.js.map +2 -2
- package/dist/modules/auth/backend/users/page.js +6 -2
- package/dist/modules/auth/backend/users/page.js.map +2 -2
- package/dist/modules/auth/components/AclEditor.js +3 -1
- package/dist/modules/auth/components/AclEditor.js.map +2 -2
- package/dist/modules/auth/data/entities.js +6 -0
- package/dist/modules/auth/data/entities.js.map +2 -2
- package/dist/modules/auth/services/sidebarPreferencesService.js +32 -4
- package/dist/modules/auth/services/sidebarPreferencesService.js.map +2 -2
- package/dist/modules/business_rules/api/rules/route.js +28 -0
- package/dist/modules/business_rules/api/rules/route.js.map +2 -2
- package/dist/modules/business_rules/api/sets/route.js +28 -0
- package/dist/modules/business_rules/api/sets/route.js.map +2 -2
- package/dist/modules/business_rules/backend/rules/[id]/page.js +11 -4
- package/dist/modules/business_rules/backend/rules/[id]/page.js.map +3 -3
- package/dist/modules/business_rules/backend/rules/page.js +20 -11
- package/dist/modules/business_rules/backend/rules/page.js.map +2 -2
- package/dist/modules/business_rules/backend/sets/[id]/page.js +11 -4
- package/dist/modules/business_rules/backend/sets/[id]/page.js.map +2 -2
- package/dist/modules/business_rules/backend/sets/page.js +20 -11
- package/dist/modules/business_rules/backend/sets/page.js.map +2 -2
- package/dist/modules/catalog/api/categories/route.js +2 -0
- package/dist/modules/catalog/api/categories/route.js.map +2 -2
- package/dist/modules/catalog/api/products/route.js +2 -1
- package/dist/modules/catalog/api/products/route.js.map +2 -2
- package/dist/modules/catalog/backend/catalog/categories/[id]/edit/page.js +2 -0
- package/dist/modules/catalog/backend/catalog/categories/[id]/edit/page.js.map +2 -2
- package/dist/modules/catalog/backend/catalog/products/[id]/page.js +94 -40
- package/dist/modules/catalog/backend/catalog/products/[id]/page.js.map +2 -2
- package/dist/modules/catalog/backend/catalog/products/[productId]/variants/[variantId]/page.js +37 -8
- package/dist/modules/catalog/backend/catalog/products/[productId]/variants/[variantId]/page.js.map +2 -2
- package/dist/modules/catalog/backend/catalog/products/optionSchemaClient.js.map +2 -2
- package/dist/modules/catalog/commands/variants.js +32 -31
- package/dist/modules/catalog/commands/variants.js.map +2 -2
- package/dist/modules/catalog/components/PriceKindSettings.js +12 -5
- package/dist/modules/catalog/components/PriceKindSettings.js.map +2 -2
- package/dist/modules/catalog/components/categories/CategoriesDataTable.js.map +2 -2
- package/dist/modules/catalog/components/products/ProductMediaManager.js.map +2 -2
- package/dist/modules/catalog/components/products/ProductsDataTable.js +5 -3
- package/dist/modules/catalog/components/products/ProductsDataTable.js.map +2 -2
- package/dist/modules/catalog/components/products/productForm.js.map +2 -2
- package/dist/modules/catalog/components/products/variantForm.js +2 -1
- package/dist/modules/catalog/components/products/variantForm.js.map +2 -2
- package/dist/modules/communication_channels/api/post/test-seed/route.js +23 -2
- package/dist/modules/communication_channels/api/post/test-seed/route.js.map +2 -2
- package/dist/modules/communication_channels/backend/profile/communication-channels/page.js +5 -0
- package/dist/modules/communication_channels/backend/profile/communication-channels/page.js.map +2 -2
- package/dist/modules/communication_channels/commands/set-primary-channel.js +2 -1
- package/dist/modules/communication_channels/commands/set-primary-channel.js.map +2 -2
- package/dist/modules/currencies/backend/currencies/[id]/page.js +6 -3
- package/dist/modules/currencies/backend/currencies/[id]/page.js.map +2 -2
- package/dist/modules/currencies/backend/currencies/page.js +18 -11
- package/dist/modules/currencies/backend/currencies/page.js.map +2 -2
- package/dist/modules/currencies/backend/exchange-rates/[id]/page.js +1 -0
- package/dist/modules/currencies/backend/exchange-rates/[id]/page.js.map +2 -2
- package/dist/modules/currencies/backend/exchange-rates/page.js +10 -6
- package/dist/modules/currencies/backend/exchange-rates/page.js.map +2 -2
- package/dist/modules/currencies/commands/currencies.js +7 -5
- package/dist/modules/currencies/commands/currencies.js.map +2 -2
- package/dist/modules/currencies/components/CurrencyFetchingConfig.js +26 -19
- package/dist/modules/currencies/components/CurrencyFetchingConfig.js.map +2 -2
- package/dist/modules/customer_accounts/api/admin/roles/[id].js +28 -5
- package/dist/modules/customer_accounts/api/admin/roles/[id].js.map +2 -2
- package/dist/modules/customer_accounts/api/admin/roles.js +4 -2
- package/dist/modules/customer_accounts/api/admin/roles.js.map +2 -2
- package/dist/modules/customer_accounts/api/admin/users/[id].js +28 -5
- package/dist/modules/customer_accounts/api/admin/users/[id].js.map +2 -2
- package/dist/modules/customer_accounts/api/admin/users.js +2 -0
- package/dist/modules/customer_accounts/api/admin/users.js.map +2 -2
- package/dist/modules/customer_accounts/backend/customer_accounts/roles/[id]/page.js +16 -8
- package/dist/modules/customer_accounts/backend/customer_accounts/roles/[id]/page.js.map +2 -2
- package/dist/modules/customer_accounts/backend/customer_accounts/roles/page.js +8 -4
- package/dist/modules/customer_accounts/backend/customer_accounts/roles/page.js.map +2 -2
- package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/page.js +8 -4
- package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/page.js.map +2 -2
- package/dist/modules/customer_accounts/backend/customer_accounts/users/[id]/page.js +29 -18
- package/dist/modules/customer_accounts/backend/customer_accounts/users/[id]/page.js.map +2 -2
- package/dist/modules/customer_accounts/backend/customer_accounts/users/page.js +18 -11
- package/dist/modules/customer_accounts/backend/customer_accounts/users/page.js.map +2 -2
- package/dist/modules/customers/api/companies/route.js +13 -2
- package/dist/modules/customers/api/companies/route.js.map +2 -2
- package/dist/modules/customers/api/deals/route.js +2 -0
- package/dist/modules/customers/api/deals/route.js.map +2 -2
- package/dist/modules/customers/api/people/route.js +11 -2
- package/dist/modules/customers/api/people/route.js.map +2 -2
- package/dist/modules/customers/api/todos/route.js +1 -0
- package/dist/modules/customers/api/todos/route.js.map +2 -2
- package/dist/modules/customers/backend/config/customers/deals/page.js.map +2 -2
- package/dist/modules/customers/backend/config/customers/pipeline-stages/page.js +34 -21
- package/dist/modules/customers/backend/config/customers/pipeline-stages/page.js.map +2 -2
- package/dist/modules/customers/backend/customers/companies/[id]/page.js +45 -27
- package/dist/modules/customers/backend/customers/companies/[id]/page.js.map +2 -2
- package/dist/modules/customers/backend/customers/companies/page.js.map +2 -2
- package/dist/modules/customers/backend/customers/companies-v2/[id]/page.js +22 -5
- package/dist/modules/customers/backend/customers/companies-v2/[id]/page.js.map +2 -2
- package/dist/modules/customers/backend/customers/deals/[id]/hooks/useDealFormHandlers.js +30 -8
- package/dist/modules/customers/backend/customers/deals/[id]/hooks/useDealFormHandlers.js.map +2 -2
- package/dist/modules/customers/backend/customers/deals/[id]/page.js +1 -0
- package/dist/modules/customers/backend/customers/deals/[id]/page.js.map +2 -2
- package/dist/modules/customers/backend/customers/deals/page.js +16 -6
- package/dist/modules/customers/backend/customers/deals/page.js.map +2 -2
- package/dist/modules/customers/backend/customers/deals/pipeline/page.js +62 -39
- package/dist/modules/customers/backend/customers/deals/pipeline/page.js.map +2 -2
- package/dist/modules/customers/backend/customers/people/[id]/page.js +41 -26
- package/dist/modules/customers/backend/customers/people/[id]/page.js.map +2 -2
- package/dist/modules/customers/backend/customers/people/page.js.map +2 -2
- package/dist/modules/customers/backend/customers/people-v2/[id]/page.js +50 -23
- package/dist/modules/customers/backend/customers/people-v2/[id]/page.js.map +2 -2
- package/dist/modules/customers/commands/addresses.js +16 -14
- package/dist/modules/customers/commands/addresses.js.map +2 -2
- package/dist/modules/customers/commands/companies.js +1 -1
- package/dist/modules/customers/commands/companies.js.map +2 -2
- package/dist/modules/customers/commands/interactions.js +41 -4
- package/dist/modules/customers/commands/interactions.js.map +2 -2
- package/dist/modules/customers/commands/people.js +1 -1
- package/dist/modules/customers/commands/people.js.map +2 -2
- package/dist/modules/customers/commands/personCompanyLinks.js +8 -5
- package/dist/modules/customers/commands/personCompanyLinks.js.map +2 -2
- package/dist/modules/customers/commands/pipeline-stages.js +13 -11
- package/dist/modules/customers/commands/pipeline-stages.js.map +3 -3
- package/dist/modules/customers/components/AddressFormatSettings.js.map +2 -2
- package/dist/modules/customers/components/DictionarySettings.js +20 -13
- package/dist/modules/customers/components/DictionarySettings.js.map +2 -2
- package/dist/modules/customers/components/DictionarySortSettings.js +4 -0
- package/dist/modules/customers/components/DictionarySortSettings.js.map +2 -2
- package/dist/modules/customers/components/PipelineSettings.js +38 -23
- package/dist/modules/customers/components/PipelineSettings.js.map +2 -2
- package/dist/modules/customers/components/detail/ActivityTimeline.js +1 -1
- package/dist/modules/customers/components/detail/ActivityTimeline.js.map +2 -2
- package/dist/modules/customers/components/detail/AddressesSection.js +4 -0
- package/dist/modules/customers/components/detail/AddressesSection.js.map +2 -2
- package/dist/modules/customers/components/detail/CompanyPeopleSection.js +28 -22
- package/dist/modules/customers/components/detail/CompanyPeopleSection.js.map +2 -2
- package/dist/modules/customers/components/detail/DealsSection.js +36 -24
- package/dist/modules/customers/components/detail/DealsSection.js.map +2 -2
- package/dist/modules/customers/components/detail/EmailCardActions.js +5 -0
- package/dist/modules/customers/components/detail/EmailCardActions.js.map +2 -2
- package/dist/modules/customers/components/detail/EntityTagsDialog.js +7 -0
- package/dist/modules/customers/components/detail/EntityTagsDialog.js.map +2 -2
- package/dist/modules/customers/components/detail/ManageTagsDialog.js +34 -22
- package/dist/modules/customers/components/detail/ManageTagsDialog.js.map +2 -2
- package/dist/modules/customers/components/detail/PersonCompaniesSection.js +41 -29
- package/dist/modules/customers/components/detail/PersonCompaniesSection.js.map +2 -2
- package/dist/modules/customers/components/detail/RoleAssignmentRow.js +14 -8
- package/dist/modules/customers/components/detail/RoleAssignmentRow.js.map +2 -2
- package/dist/modules/customers/components/detail/ScheduleActivityDialog.js +14 -6
- package/dist/modules/customers/components/detail/ScheduleActivityDialog.js.map +2 -2
- package/dist/modules/customers/components/detail/hooks/useInteractionMutations.js +29 -13
- package/dist/modules/customers/components/detail/hooks/useInteractionMutations.js.map +2 -2
- package/dist/modules/customers/components/detail/hooks/useInteractions.js +77 -35
- package/dist/modules/customers/components/detail/hooks/useInteractions.js.map +2 -2
- package/dist/modules/customers/components/detail/hooks/usePersonTasks.js +25 -17
- package/dist/modules/customers/components/detail/hooks/usePersonTasks.js.map +2 -2
- package/dist/modules/customers/components/detail/schedule/useScheduleFormState.js.map +2 -2
- package/dist/modules/customers/components/formConfig.js.map +2 -2
- package/dist/modules/customers/data/guards.js +66 -0
- package/dist/modules/customers/data/guards.js.map +7 -0
- package/dist/modules/customers/di.js +37 -0
- package/dist/modules/customers/di.js.map +2 -2
- package/dist/modules/customers/lib/todoCompatibility.js +11 -0
- package/dist/modules/customers/lib/todoCompatibility.js.map +2 -2
- package/dist/modules/dashboards/components/WidgetVisibilityEditor.js.map +2 -2
- package/dist/modules/data_sync/api/options.js +4 -4
- package/dist/modules/data_sync/api/options.js.map +2 -2
- package/dist/modules/data_sync/api/schedules/route.js +9 -1
- package/dist/modules/data_sync/api/schedules/route.js.map +2 -2
- package/dist/modules/data_sync/backend/data-sync/page.js +17 -8
- package/dist/modules/data_sync/backend/data-sync/page.js.map +2 -2
- package/dist/modules/data_sync/components/IntegrationScheduleTab.js +43 -22
- package/dist/modules/data_sync/components/IntegrationScheduleTab.js.map +2 -2
- package/dist/modules/data_sync/lib/sync-schedule-service.js +9 -0
- package/dist/modules/data_sync/lib/sync-schedule-service.js.map +2 -2
- package/dist/modules/dictionaries/api/[dictionaryId]/entries/[entryId]/route.js +8 -1
- package/dist/modules/dictionaries/api/[dictionaryId]/entries/[entryId]/route.js.map +2 -2
- package/dist/modules/dictionaries/api/[dictionaryId]/route.js +17 -1
- package/dist/modules/dictionaries/api/[dictionaryId]/route.js.map +2 -2
- package/dist/modules/dictionaries/components/DictionariesManager.js +31 -10
- package/dist/modules/dictionaries/components/DictionariesManager.js.map +2 -2
- package/dist/modules/dictionaries/components/DictionaryEntriesEditor.js +28 -15
- package/dist/modules/dictionaries/components/DictionaryEntriesEditor.js.map +2 -2
- package/dist/modules/directory/api/organizations/route.js +3 -0
- package/dist/modules/directory/api/organizations/route.js.map +2 -2
- package/dist/modules/directory/backend/directory/organizations/[id]/edit/page.js +2 -0
- package/dist/modules/directory/backend/directory/organizations/[id]/edit/page.js.map +2 -2
- package/dist/modules/directory/backend/directory/organizations/page.js +9 -5
- package/dist/modules/directory/backend/directory/organizations/page.js.map +2 -2
- package/dist/modules/directory/backend/directory/tenants/[id]/edit/page.js +7 -3
- package/dist/modules/directory/backend/directory/tenants/[id]/edit/page.js.map +2 -2
- package/dist/modules/directory/backend/directory/tenants/page.js +8 -4
- package/dist/modules/directory/backend/directory/tenants/page.js.map +2 -2
- package/dist/modules/directory/commands/organizations.js +7 -2
- package/dist/modules/directory/commands/organizations.js.map +2 -2
- package/dist/modules/entities/api/records.js +66 -0
- package/dist/modules/entities/api/records.js.map +2 -2
- package/dist/modules/entities/backend/entities/user/[entityId]/records/[recordId]/page.js +1 -0
- package/dist/modules/entities/backend/entities/user/[entityId]/records/[recordId]/page.js.map +2 -2
- package/dist/modules/entities/backend/entities/user/[entityId]/records/page.js +8 -4
- package/dist/modules/entities/backend/entities/user/[entityId]/records/page.js.map +2 -2
- package/dist/modules/entities/lib/helpers.js +17 -0
- package/dist/modules/entities/lib/helpers.js.map +2 -2
- package/dist/modules/feature_toggles/api/global/[id]/override/route.js +2 -1
- package/dist/modules/feature_toggles/api/global/[id]/override/route.js.map +2 -2
- package/dist/modules/feature_toggles/api/overrides/route.js +15 -0
- package/dist/modules/feature_toggles/api/overrides/route.js.map +2 -2
- package/dist/modules/feature_toggles/backend/feature-toggles/global/[id]/edit/page.js +15 -14
- package/dist/modules/feature_toggles/backend/feature-toggles/global/[id]/edit/page.js.map +2 -2
- package/dist/modules/feature_toggles/components/FeatureToggleOverrideCard.js +20 -12
- package/dist/modules/feature_toggles/components/FeatureToggleOverrideCard.js.map +2 -2
- package/dist/modules/feature_toggles/components/FeatureTogglesTable.js +6 -2
- package/dist/modules/feature_toggles/components/FeatureTogglesTable.js.map +2 -2
- package/dist/modules/feature_toggles/components/formConfig.js +2 -1
- package/dist/modules/feature_toggles/components/formConfig.js.map +2 -2
- package/dist/modules/feature_toggles/components/overrideFormConfig.js +5 -1
- package/dist/modules/feature_toggles/components/overrideFormConfig.js.map +2 -2
- package/dist/modules/feature_toggles/data/validators.js +7 -4
- package/dist/modules/feature_toggles/data/validators.js.map +2 -2
- package/dist/modules/inbox_ops/api/settings/route.js +17 -2
- package/dist/modules/inbox_ops/api/settings/route.js.map +2 -2
- package/dist/modules/inbox_ops/backend/inbox-ops/settings/page.js +13 -8
- package/dist/modules/inbox_ops/backend/inbox-ops/settings/page.js.map +2 -2
- package/dist/modules/inbox_ops/components/proposals/EditActionDialog.js +9 -4
- package/dist/modules/inbox_ops/components/proposals/EditActionDialog.js.map +2 -2
- package/dist/modules/integrations/backend/integrations/bundle/[id]/page.js +18 -11
- package/dist/modules/integrations/backend/integrations/bundle/[id]/page.js.map +2 -2
- package/dist/modules/integrations/backend/integrations/page.js +12 -8
- package/dist/modules/integrations/backend/integrations/page.js.map +2 -2
- package/dist/modules/messages/commands/messages.js +13 -10
- package/dist/modules/messages/commands/messages.js.map +2 -2
- package/dist/modules/perspectives/api/[tableId]/route.js +39 -30
- package/dist/modules/perspectives/api/[tableId]/route.js.map +2 -2
- package/dist/modules/perspectives/services/perspectiveService.js +7 -0
- package/dist/modules/perspectives/services/perspectiveService.js.map +2 -2
- package/dist/modules/planner/backend/planner/availability-rulesets/[id]/page.js +6 -14
- package/dist/modules/planner/backend/planner/availability-rulesets/[id]/page.js.map +3 -3
- package/dist/modules/planner/backend/planner/availability-rulesets/page.js +4 -2
- package/dist/modules/planner/backend/planner/availability-rulesets/page.js.map +2 -2
- package/dist/modules/planner/components/AvailabilityRuleSetForm.js +2 -0
- package/dist/modules/planner/components/AvailabilityRuleSetForm.js.map +2 -2
- package/dist/modules/planner/components/AvailabilityRulesEditor.js +36 -11
- package/dist/modules/planner/components/AvailabilityRulesEditor.js.map +2 -2
- package/dist/modules/planner/components/AvailabilitySchedule.js +9 -5
- package/dist/modules/planner/components/AvailabilitySchedule.js.map +2 -2
- package/dist/modules/query_index/lib/engine.js +19 -0
- package/dist/modules/query_index/lib/engine.js.map +2 -2
- package/dist/modules/resources/backend/resources/resource-types/[id]/edit/page.js +1 -0
- package/dist/modules/resources/backend/resources/resource-types/[id]/edit/page.js.map +2 -2
- package/dist/modules/resources/backend/resources/resource-types/page.js +4 -2
- package/dist/modules/resources/backend/resources/resource-types/page.js.map +2 -2
- package/dist/modules/resources/backend/resources/resources/[id]/page.js +14 -3
- package/dist/modules/resources/backend/resources/resources/[id]/page.js.map +2 -2
- package/dist/modules/resources/backend/resources/resources/page.js +8 -4
- package/dist/modules/resources/backend/resources/resources/page.js.map +2 -2
- package/dist/modules/resources/components/ResourceCrudForm.js +2 -0
- package/dist/modules/resources/components/ResourceCrudForm.js.map +2 -2
- package/dist/modules/resources/components/ResourceTypeCrudForm.js +1 -0
- package/dist/modules/resources/components/ResourceTypeCrudForm.js.map +2 -2
- package/dist/modules/sales/api/documents/factory.js +7 -2
- package/dist/modules/sales/api/documents/factory.js.map +2 -2
- package/dist/modules/sales/backend/sales/channels/[channelId]/edit/page.js +3 -1
- package/dist/modules/sales/backend/sales/channels/[channelId]/edit/page.js.map +2 -2
- package/dist/modules/sales/backend/sales/channels/offers/page.js +13 -4
- package/dist/modules/sales/backend/sales/channels/offers/page.js.map +2 -2
- package/dist/modules/sales/backend/sales/channels/page.js +16 -4
- package/dist/modules/sales/backend/sales/channels/page.js.map +2 -2
- package/dist/modules/sales/backend/sales/documents/[id]/page.js +68 -22
- package/dist/modules/sales/backend/sales/documents/[id]/page.js.map +2 -2
- package/dist/modules/sales/backend/sales/documents/create/page.js.map +2 -2
- package/dist/modules/sales/commands/documentAddresses.js +181 -2
- package/dist/modules/sales/commands/documentAddresses.js.map +2 -2
- package/dist/modules/sales/commands/documents.js +29 -1
- package/dist/modules/sales/commands/documents.js.map +2 -2
- package/dist/modules/sales/commands/returns.js +12 -2
- package/dist/modules/sales/commands/returns.js.map +2 -2
- package/dist/modules/sales/commands/shared.js +15 -0
- package/dist/modules/sales/commands/shared.js.map +2 -2
- package/dist/modules/sales/commands/shipments.js +4 -1
- package/dist/modules/sales/commands/shipments.js.map +2 -2
- package/dist/modules/sales/components/AdjustmentKindSettings.js +19 -11
- package/dist/modules/sales/components/AdjustmentKindSettings.js.map +2 -2
- package/dist/modules/sales/components/DocumentNumberSettings.js.map +2 -2
- package/dist/modules/sales/components/OrderEditingSettings.js.map +2 -2
- package/dist/modules/sales/components/PaymentMethodsSettings.js +12 -4
- package/dist/modules/sales/components/PaymentMethodsSettings.js.map +2 -2
- package/dist/modules/sales/components/ShippingMethodsSettings.js +12 -4
- package/dist/modules/sales/components/ShippingMethodsSettings.js.map +2 -2
- package/dist/modules/sales/components/StatusSettings.js +18 -11
- package/dist/modules/sales/components/StatusSettings.js.map +2 -2
- package/dist/modules/sales/components/TaxRatesSettings.js +12 -4
- package/dist/modules/sales/components/TaxRatesSettings.js.map +2 -2
- package/dist/modules/sales/components/channels/ChannelOfferForm.js +47 -16
- package/dist/modules/sales/components/channels/ChannelOfferForm.js.map +2 -2
- package/dist/modules/sales/components/channels/SalesChannelOffersPanel.js +8 -4
- package/dist/modules/sales/components/channels/SalesChannelOffersPanel.js.map +2 -2
- package/dist/modules/sales/components/documents/AddressesSection.js +44 -25
- package/dist/modules/sales/components/documents/AddressesSection.js.map +2 -2
- package/dist/modules/sales/components/documents/AdjustmentsSection.js +43 -23
- package/dist/modules/sales/components/documents/AdjustmentsSection.js.map +2 -2
- package/dist/modules/sales/components/documents/ItemsSection.js +22 -13
- package/dist/modules/sales/components/documents/ItemsSection.js.map +2 -2
- package/dist/modules/sales/components/documents/LineItemDialog.js +23 -10
- package/dist/modules/sales/components/documents/LineItemDialog.js.map +2 -2
- package/dist/modules/sales/components/documents/PaymentDialog.js +29 -14
- package/dist/modules/sales/components/documents/PaymentDialog.js.map +2 -2
- package/dist/modules/sales/components/documents/PaymentsSection.js +20 -10
- package/dist/modules/sales/components/documents/PaymentsSection.js.map +2 -2
- package/dist/modules/sales/components/documents/ReturnDialog.js +26 -17
- package/dist/modules/sales/components/documents/ReturnDialog.js.map +2 -2
- package/dist/modules/sales/components/documents/ReturnsSection.js +3 -1
- package/dist/modules/sales/components/documents/ReturnsSection.js.map +2 -2
- package/dist/modules/sales/components/documents/SalesDocumentsTable.js +10 -5
- package/dist/modules/sales/components/documents/SalesDocumentsTable.js.map +2 -2
- package/dist/modules/sales/components/documents/ShipmentDialog.js +21 -7
- package/dist/modules/sales/components/documents/ShipmentDialog.js.map +2 -2
- package/dist/modules/sales/components/documents/ShipmentsSection.js +19 -10
- package/dist/modules/sales/components/documents/ShipmentsSection.js.map +2 -2
- package/dist/modules/sales/components/documents/optimisticLock.js +27 -0
- package/dist/modules/sales/components/documents/optimisticLock.js.map +7 -0
- package/dist/modules/sales/di.js +18 -0
- package/dist/modules/sales/di.js.map +2 -2
- package/dist/modules/staff/api/job-histories.js +11 -2
- package/dist/modules/staff/api/job-histories.js.map +2 -2
- package/dist/modules/staff/api/timesheets/time-entries/route.js +11 -4
- package/dist/modules/staff/api/timesheets/time-entries/route.js.map +2 -2
- package/dist/modules/staff/backend/staff/leave-requests/[id]/page.js +13 -8
- package/dist/modules/staff/backend/staff/leave-requests/[id]/page.js.map +2 -2
- package/dist/modules/staff/backend/staff/my-leave-requests/[id]/page.js +2 -1
- package/dist/modules/staff/backend/staff/my-leave-requests/[id]/page.js.map +2 -2
- package/dist/modules/staff/backend/staff/team-members/[id]/page.js +7 -4
- package/dist/modules/staff/backend/staff/team-members/[id]/page.js.map +2 -2
- package/dist/modules/staff/backend/staff/team-members/page.js +4 -2
- package/dist/modules/staff/backend/staff/team-members/page.js.map +2 -2
- package/dist/modules/staff/backend/staff/team-roles/[id]/edit/page.js +1 -0
- package/dist/modules/staff/backend/staff/team-roles/[id]/edit/page.js.map +2 -2
- package/dist/modules/staff/backend/staff/team-roles/page.js +4 -2
- package/dist/modules/staff/backend/staff/team-roles/page.js.map +2 -2
- package/dist/modules/staff/backend/staff/teams/[id]/edit/page.js +5 -2
- package/dist/modules/staff/backend/staff/teams/[id]/edit/page.js.map +2 -2
- package/dist/modules/staff/backend/staff/teams/page.js +12 -3
- package/dist/modules/staff/backend/staff/teams/page.js.map +2 -2
- package/dist/modules/staff/backend/staff/timesheets/page.js +4 -1
- package/dist/modules/staff/backend/staff/timesheets/page.js.map +2 -2
- package/dist/modules/staff/backend/staff/timesheets/projects/[id]/page.js.map +2 -2
- package/dist/modules/staff/backend/staff/timesheets/projects/page.js +12 -3
- package/dist/modules/staff/backend/staff/timesheets/projects/page.js.map +2 -2
- package/dist/modules/staff/commands/job-histories.js +40 -3
- package/dist/modules/staff/commands/job-histories.js.map +2 -2
- package/dist/modules/staff/components/LeaveRequestForm.js +1 -0
- package/dist/modules/staff/components/LeaveRequestForm.js.map +2 -2
- package/dist/modules/staff/components/TeamForm.js +1 -0
- package/dist/modules/staff/components/TeamForm.js.map +2 -2
- package/dist/modules/staff/components/TeamMemberForm.js +1 -0
- package/dist/modules/staff/components/TeamMemberForm.js.map +2 -2
- package/dist/modules/staff/components/TeamRoleForm.js +1 -0
- package/dist/modules/staff/components/TeamRoleForm.js.map +2 -2
- package/dist/modules/staff/components/detail/JobHistorySection.js +20 -7
- package/dist/modules/staff/components/detail/JobHistorySection.js.map +2 -2
- package/dist/modules/staff/data/validators.js +7 -1
- package/dist/modules/staff/data/validators.js.map +2 -2
- package/dist/modules/staff/lib/leaveRequestHelpers.js +2 -1
- package/dist/modules/staff/lib/leaveRequestHelpers.js.map +2 -2
- package/dist/modules/translations/components/TranslationManager.js +12 -8
- package/dist/modules/translations/components/TranslationManager.js.map +2 -2
- package/dist/modules/workflows/api/definitions/[id]/route.js +106 -0
- package/dist/modules/workflows/api/definitions/[id]/route.js.map +2 -2
- package/dist/modules/workflows/backend/definitions/[id]/page.js +11 -3
- package/dist/modules/workflows/backend/definitions/[id]/page.js.map +2 -2
- package/dist/modules/workflows/backend/definitions/page.js +19 -8
- package/dist/modules/workflows/backend/definitions/page.js.map +2 -2
- package/dist/modules/workflows/backend/definitions/visual-editor/page.js +29 -16
- package/dist/modules/workflows/backend/definitions/visual-editor/page.js.map +2 -2
- package/dist/modules/workflows/components/formConfig.js +4 -1
- package/dist/modules/workflows/components/formConfig.js.map +2 -2
- package/dist/modules/workflows/di.js +12 -0
- package/dist/modules/workflows/di.js.map +2 -2
- package/generated/entities/role/index.ts +1 -0
- package/generated/entities/user/index.ts +1 -0
- package/generated/entity-fields-registry.ts +2 -0
- package/jest.setup.ts +17 -0
- package/package.json +8 -7
- package/src/helpers/integration/communicationChannelsFixtures.ts +6 -0
- package/src/helpers/integration/dbFixtures.ts +1 -1
- package/src/helpers/integration/optimisticLockUi.ts +172 -0
- package/src/helpers/integration/salesFixtures.ts +29 -0
- package/src/modules/api_keys/backend/api-keys/page.tsx +10 -5
- package/src/modules/attachments/components/AttachmentPartitionSettings.tsx +19 -9
- package/src/modules/auth/api/roles/acl/route.ts +37 -11
- package/src/modules/auth/api/roles/route.ts +2 -0
- package/src/modules/auth/api/sidebar/preferences/route.ts +73 -0
- package/src/modules/auth/api/users/acl/route.ts +46 -18
- package/src/modules/auth/api/users/route.ts +2 -0
- package/src/modules/auth/backend/roles/[id]/edit/page.tsx +29 -4
- package/src/modules/auth/backend/roles/page.tsx +9 -4
- package/src/modules/auth/backend/users/[id]/edit/page.tsx +37 -4
- package/src/modules/auth/backend/users/page.tsx +7 -2
- package/src/modules/auth/components/AclEditor.tsx +10 -1
- package/src/modules/auth/data/entities.ts +7 -1
- package/src/modules/auth/services/sidebarPreferencesService.ts +38 -4
- package/src/modules/business_rules/api/rules/route.ts +30 -0
- package/src/modules/business_rules/api/sets/route.ts +30 -0
- package/src/modules/business_rules/backend/rules/[id]/page.tsx +16 -4
- package/src/modules/business_rules/backend/rules/page.tsx +20 -11
- package/src/modules/business_rules/backend/sets/[id]/page.tsx +16 -4
- package/src/modules/business_rules/backend/sets/page.tsx +20 -11
- package/src/modules/catalog/api/categories/route.ts +3 -0
- package/src/modules/catalog/api/products/route.ts +4 -0
- package/src/modules/catalog/backend/catalog/categories/[id]/edit/page.tsx +5 -0
- package/src/modules/catalog/backend/catalog/products/[id]/page.tsx +112 -35
- package/src/modules/catalog/backend/catalog/products/[productId]/variants/[variantId]/page.tsx +56 -7
- package/src/modules/catalog/backend/catalog/products/optionSchemaClient.ts +2 -0
- package/src/modules/catalog/commands/variants.ts +32 -32
- package/src/modules/catalog/components/PriceKindSettings.tsx +20 -7
- package/src/modules/catalog/components/categories/CategoriesDataTable.tsx +1 -0
- package/src/modules/catalog/components/products/ProductMediaManager.tsx +2 -0
- package/src/modules/catalog/components/products/ProductsDataTable.tsx +8 -4
- package/src/modules/catalog/components/products/productForm.ts +3 -0
- package/src/modules/catalog/components/products/variantForm.ts +9 -0
- package/src/modules/communication_channels/api/post/test-seed/route.ts +28 -1
- package/src/modules/communication_channels/backend/profile/communication-channels/page.tsx +5 -0
- package/src/modules/communication_channels/commands/set-primary-channel.ts +10 -7
- package/src/modules/currencies/backend/currencies/[id]/page.tsx +13 -6
- package/src/modules/currencies/backend/currencies/page.tsx +18 -11
- package/src/modules/currencies/backend/exchange-rates/[id]/page.tsx +3 -0
- package/src/modules/currencies/backend/exchange-rates/page.tsx +10 -6
- package/src/modules/currencies/commands/currencies.ts +10 -5
- package/src/modules/currencies/components/CurrencyFetchingConfig.tsx +31 -21
- package/src/modules/customer_accounts/api/admin/roles/[id].ts +35 -5
- package/src/modules/customer_accounts/api/admin/roles.ts +2 -0
- package/src/modules/customer_accounts/api/admin/users/[id].ts +38 -5
- package/src/modules/customer_accounts/api/admin/users.ts +2 -0
- package/src/modules/customer_accounts/backend/customer_accounts/roles/[id]/page.tsx +34 -20
- package/src/modules/customer_accounts/backend/customer_accounts/roles/page.tsx +9 -4
- package/src/modules/customer_accounts/backend/customer_accounts/settings/domain/page.tsx +11 -4
- package/src/modules/customer_accounts/backend/customer_accounts/users/[id]/page.tsx +28 -17
- package/src/modules/customer_accounts/backend/customer_accounts/users/page.tsx +19 -11
- package/src/modules/customers/AGENTS.md +2 -2
- package/src/modules/customers/api/companies/route.ts +14 -1
- package/src/modules/customers/api/deals/route.ts +3 -0
- package/src/modules/customers/api/people/route.ts +12 -1
- package/src/modules/customers/api/todos/route.ts +1 -0
- package/src/modules/customers/backend/config/customers/deals/page.tsx +1 -0
- package/src/modules/customers/backend/config/customers/pipeline-stages/page.tsx +36 -21
- package/src/modules/customers/backend/customers/companies/[id]/page.tsx +52 -27
- package/src/modules/customers/backend/customers/companies/page.tsx +2 -0
- package/src/modules/customers/backend/customers/companies-v2/[id]/page.tsx +27 -5
- package/src/modules/customers/backend/customers/deals/[id]/hooks/useDealFormHandlers.ts +39 -7
- package/src/modules/customers/backend/customers/deals/[id]/page.tsx +1 -0
- package/src/modules/customers/backend/customers/deals/page.tsx +18 -6
- package/src/modules/customers/backend/customers/deals/pipeline/page.tsx +64 -39
- package/src/modules/customers/backend/customers/people/[id]/page.tsx +46 -26
- package/src/modules/customers/backend/customers/people/page.tsx +2 -0
- package/src/modules/customers/backend/customers/people-v2/[id]/page.tsx +84 -24
- package/src/modules/customers/commands/addresses.ts +16 -14
- package/src/modules/customers/commands/companies.ts +3 -1
- package/src/modules/customers/commands/interactions.ts +50 -4
- package/src/modules/customers/commands/people.ts +2 -1
- package/src/modules/customers/commands/personCompanyLinks.ts +8 -5
- package/src/modules/customers/commands/pipeline-stages.ts +16 -16
- package/src/modules/customers/components/AddressFormatSettings.tsx +1 -0
- package/src/modules/customers/components/DictionarySettings.tsx +18 -13
- package/src/modules/customers/components/DictionarySortSettings.tsx +4 -0
- package/src/modules/customers/components/PipelineSettings.tsx +42 -21
- package/src/modules/customers/components/detail/ActivityTimeline.tsx +3 -3
- package/src/modules/customers/components/detail/AddressesSection.tsx +4 -0
- package/src/modules/customers/components/detail/CompanyPeopleSection.tsx +2 -0
- package/src/modules/customers/components/detail/DealsSection.tsx +4 -0
- package/src/modules/customers/components/detail/EmailCardActions.tsx +5 -0
- package/src/modules/customers/components/detail/EntityTagsDialog.tsx +7 -0
- package/src/modules/customers/components/detail/ManageTagsDialog.tsx +4 -0
- package/src/modules/customers/components/detail/PersonCompaniesSection.tsx +4 -0
- package/src/modules/customers/components/detail/RoleAssignmentRow.tsx +2 -0
- package/src/modules/customers/components/detail/ScheduleActivityDialog.tsx +23 -7
- package/src/modules/customers/components/detail/hooks/useInteractionMutations.ts +25 -15
- package/src/modules/customers/components/detail/hooks/useInteractions.ts +76 -35
- package/src/modules/customers/components/detail/hooks/usePersonTasks.ts +30 -17
- package/src/modules/customers/components/detail/schedule/useScheduleFormState.ts +2 -0
- package/src/modules/customers/components/detail/types.ts +1 -0
- package/src/modules/customers/components/formConfig.tsx +2 -0
- package/src/modules/customers/data/guards.ts +67 -0
- package/src/modules/customers/di.ts +66 -0
- package/src/modules/customers/i18n/de.json +2 -0
- package/src/modules/customers/i18n/en.json +2 -0
- package/src/modules/customers/i18n/es.json +2 -0
- package/src/modules/customers/i18n/pl.json +2 -0
- package/src/modules/customers/lib/todoCompatibility.ts +14 -0
- package/src/modules/dashboards/components/WidgetVisibilityEditor.tsx +2 -0
- package/src/modules/data_sync/api/options.ts +7 -4
- package/src/modules/data_sync/api/schedules/route.ts +9 -1
- package/src/modules/data_sync/backend/data-sync/page.tsx +18 -5
- package/src/modules/data_sync/components/IntegrationScheduleTab.tsx +46 -19
- package/src/modules/data_sync/lib/sync-schedule-service.ts +11 -0
- package/src/modules/dictionaries/api/[dictionaryId]/entries/[entryId]/route.ts +8 -1
- package/src/modules/dictionaries/api/[dictionaryId]/route.ts +23 -0
- package/src/modules/dictionaries/components/DictionariesManager.tsx +32 -9
- package/src/modules/dictionaries/components/DictionaryEntriesEditor.tsx +30 -14
- package/src/modules/dictionaries/i18n/de.json +1 -0
- package/src/modules/dictionaries/i18n/en.json +1 -0
- package/src/modules/dictionaries/i18n/es.json +1 -0
- package/src/modules/dictionaries/i18n/pl.json +1 -0
- package/src/modules/directory/api/organizations/route.ts +3 -0
- package/src/modules/directory/backend/directory/organizations/[id]/edit/page.tsx +8 -0
- package/src/modules/directory/backend/directory/organizations/page.tsx +10 -5
- package/src/modules/directory/backend/directory/tenants/[id]/edit/page.tsx +16 -5
- package/src/modules/directory/backend/directory/tenants/page.tsx +8 -4
- package/src/modules/directory/commands/organizations.ts +7 -4
- package/src/modules/entities/api/records.ts +99 -0
- package/src/modules/entities/backend/entities/user/[entityId]/records/[recordId]/page.tsx +7 -0
- package/src/modules/entities/backend/entities/user/[entityId]/records/page.tsx +8 -4
- package/src/modules/entities/lib/helpers.ts +17 -0
- package/src/modules/feature_toggles/api/global/[id]/override/route.ts +1 -0
- package/src/modules/feature_toggles/api/overrides/route.ts +19 -0
- package/src/modules/feature_toggles/backend/feature-toggles/global/[id]/edit/page.tsx +19 -13
- package/src/modules/feature_toggles/components/FeatureToggleOverrideCard.tsx +22 -12
- package/src/modules/feature_toggles/components/FeatureTogglesTable.tsx +7 -2
- package/src/modules/feature_toggles/components/formConfig.tsx +2 -1
- package/src/modules/feature_toggles/components/overrideFormConfig.tsx +10 -1
- package/src/modules/feature_toggles/data/validators.ts +11 -3
- package/src/modules/inbox_ops/api/settings/route.ts +18 -0
- package/src/modules/inbox_ops/backend/inbox-ops/settings/page.tsx +15 -10
- package/src/modules/inbox_ops/components/proposals/EditActionDialog.tsx +9 -4
- package/src/modules/integrations/backend/integrations/bundle/[id]/page.tsx +20 -11
- package/src/modules/integrations/backend/integrations/page.tsx +13 -8
- package/src/modules/messages/commands/messages.ts +27 -15
- package/src/modules/perspectives/api/[tableId]/route.ts +11 -2
- package/src/modules/perspectives/services/perspectiveService.ts +13 -1
- package/src/modules/planner/backend/planner/availability-rulesets/[id]/page.tsx +16 -14
- package/src/modules/planner/backend/planner/availability-rulesets/page.tsx +6 -3
- package/src/modules/planner/components/AvailabilityRuleSetForm.tsx +3 -0
- package/src/modules/planner/components/AvailabilityRulesEditor.tsx +58 -15
- package/src/modules/planner/components/AvailabilitySchedule.tsx +22 -7
- package/src/modules/query_index/lib/engine.ts +34 -0
- package/src/modules/resources/backend/resources/resource-types/[id]/edit/page.tsx +7 -1
- package/src/modules/resources/backend/resources/resource-types/page.tsx +6 -3
- package/src/modules/resources/backend/resources/resources/[id]/page.tsx +23 -3
- package/src/modules/resources/backend/resources/resources/page.tsx +15 -4
- package/src/modules/resources/components/ResourceCrudForm.tsx +3 -0
- package/src/modules/resources/components/ResourceTypeCrudForm.tsx +2 -0
- package/src/modules/sales/api/documents/factory.ts +13 -1
- package/src/modules/sales/backend/sales/channels/[channelId]/edit/page.tsx +6 -0
- package/src/modules/sales/backend/sales/channels/offers/page.tsx +10 -4
- package/src/modules/sales/backend/sales/channels/page.tsx +19 -4
- package/src/modules/sales/backend/sales/documents/[id]/page.tsx +73 -20
- package/src/modules/sales/backend/sales/documents/create/page.tsx +2 -0
- package/src/modules/sales/commands/documentAddresses.ts +226 -4
- package/src/modules/sales/commands/documents.ts +28 -0
- package/src/modules/sales/commands/returns.ts +12 -3
- package/src/modules/sales/commands/shared.ts +36 -0
- package/src/modules/sales/commands/shipments.ts +17 -1
- package/src/modules/sales/components/AdjustmentKindSettings.tsx +20 -11
- package/src/modules/sales/components/DocumentNumberSettings.tsx +1 -0
- package/src/modules/sales/components/OrderEditingSettings.tsx +1 -0
- package/src/modules/sales/components/PaymentMethodsSettings.tsx +12 -4
- package/src/modules/sales/components/ShippingMethodsSettings.tsx +12 -4
- package/src/modules/sales/components/StatusSettings.tsx +20 -11
- package/src/modules/sales/components/TaxRatesSettings.tsx +12 -5
- package/src/modules/sales/components/channels/ChannelOfferForm.tsx +67 -14
- package/src/modules/sales/components/channels/SalesChannelOffersPanel.tsx +7 -4
- package/src/modules/sales/components/documents/AddressesSection.tsx +35 -25
- package/src/modules/sales/components/documents/AdjustmentsSection.tsx +50 -25
- package/src/modules/sales/components/documents/ItemsSection.tsx +24 -13
- package/src/modules/sales/components/documents/LineItemDialog.tsx +26 -9
- package/src/modules/sales/components/documents/PaymentDialog.tsx +33 -14
- package/src/modules/sales/components/documents/PaymentsSection.tsx +22 -10
- package/src/modules/sales/components/documents/ReturnDialog.tsx +28 -17
- package/src/modules/sales/components/documents/ReturnsSection.tsx +4 -1
- package/src/modules/sales/components/documents/SalesDocumentsTable.tsx +11 -4
- package/src/modules/sales/components/documents/ShipmentDialog.tsx +23 -8
- package/src/modules/sales/components/documents/ShipmentsSection.tsx +20 -10
- package/src/modules/sales/components/documents/optimisticLock.ts +34 -0
- package/src/modules/sales/components/documents/shipmentTypes.ts +1 -0
- package/src/modules/sales/di.ts +35 -0
- package/src/modules/sales/i18n/de.json +3 -0
- package/src/modules/sales/i18n/en.json +3 -0
- package/src/modules/sales/i18n/es.json +3 -0
- package/src/modules/sales/i18n/pl.json +3 -0
- package/src/modules/staff/api/job-histories.ts +12 -2
- package/src/modules/staff/api/timesheets/time-entries/route.ts +16 -4
- package/src/modules/staff/backend/staff/leave-requests/[id]/page.tsx +12 -7
- package/src/modules/staff/backend/staff/my-leave-requests/[id]/page.tsx +2 -0
- package/src/modules/staff/backend/staff/team-members/[id]/page.tsx +16 -5
- package/src/modules/staff/backend/staff/team-members/page.tsx +6 -2
- package/src/modules/staff/backend/staff/team-roles/[id]/edit/page.tsx +8 -0
- package/src/modules/staff/backend/staff/team-roles/page.tsx +6 -2
- package/src/modules/staff/backend/staff/teams/[id]/edit/page.tsx +13 -3
- package/src/modules/staff/backend/staff/teams/page.tsx +9 -3
- package/src/modules/staff/backend/staff/timesheets/page.tsx +10 -1
- package/src/modules/staff/backend/staff/timesheets/projects/[id]/page.tsx +4 -0
- package/src/modules/staff/backend/staff/timesheets/projects/page.tsx +9 -3
- package/src/modules/staff/commands/job-histories.ts +42 -3
- package/src/modules/staff/components/LeaveRequestForm.tsx +2 -0
- package/src/modules/staff/components/TeamForm.tsx +2 -0
- package/src/modules/staff/components/TeamMemberForm.tsx +2 -0
- package/src/modules/staff/components/TeamRoleForm.tsx +2 -0
- package/src/modules/staff/components/detail/JobHistorySection.tsx +28 -6
- package/src/modules/staff/data/validators.ts +6 -0
- package/src/modules/staff/i18n/de.json +1 -0
- package/src/modules/staff/i18n/en.json +1 -0
- package/src/modules/staff/i18n/es.json +1 -0
- package/src/modules/staff/i18n/pl.json +1 -0
- package/src/modules/staff/lib/leaveRequestHelpers.ts +4 -0
- package/src/modules/translations/components/TranslationManager.tsx +13 -8
- package/src/modules/workflows/api/definitions/[id]/route.ts +112 -0
- package/src/modules/workflows/backend/definitions/[id]/page.tsx +20 -4
- package/src/modules/workflows/backend/definitions/page.tsx +20 -9
- package/src/modules/workflows/backend/definitions/visual-editor/page.tsx +29 -16
- package/src/modules/workflows/components/formConfig.tsx +5 -0
- package/src/modules/workflows/di.ts +20 -0
- package/src/modules/workflows/i18n/de.json +1 -0
- package/src/modules/workflows/i18n/en.json +1 -0
- package/src/modules/workflows/i18n/es.json +1 -0
- package/src/modules/workflows/i18n/pl.json +1 -0
|
@@ -7,7 +7,8 @@ import { Page, PageBody } from '@open-mercato/ui/backend/Page'
|
|
|
7
7
|
import { Button } from '@open-mercato/ui/primitives/button'
|
|
8
8
|
import { Spinner } from '@open-mercato/ui/primitives/spinner'
|
|
9
9
|
import { flash } from '@open-mercato/ui/backend/FlashMessages'
|
|
10
|
-
import { apiCallOrThrow, readApiResultOrThrow } from '@open-mercato/ui/backend/utils/apiCall'
|
|
10
|
+
import { apiCallOrThrow, readApiResultOrThrow, withScopedApiRequestHeaders } from '@open-mercato/ui/backend/utils/apiCall'
|
|
11
|
+
import { buildOptimisticLockHeader } from '@open-mercato/ui/backend/utils/optimisticLock'
|
|
11
12
|
import { collectCustomFieldValues } from '@open-mercato/ui/backend/utils/customFieldValues'
|
|
12
13
|
import { mapCrudServerErrorToFormErrors } from '@open-mercato/ui/backend/utils/serverErrors'
|
|
13
14
|
import { E } from '#generated/entities.ids.generated'
|
|
@@ -68,6 +69,7 @@ type PersonOverview = {
|
|
|
68
69
|
nextInteractionIcon?: string | null
|
|
69
70
|
nextInteractionColor?: string | null
|
|
70
71
|
organizationId?: string | null
|
|
72
|
+
updatedAt?: string | null
|
|
71
73
|
}
|
|
72
74
|
profile: {
|
|
73
75
|
id: string
|
|
@@ -317,19 +319,31 @@ export default function CustomerPersonDetailPage({ params }: { params?: { id?: s
|
|
|
317
319
|
async (patch: Record<string, unknown>, apply: (prev: PersonOverview) => PersonOverview) => {
|
|
318
320
|
if (!data) return
|
|
319
321
|
const payload = { id: data.person.id, ...patch }
|
|
320
|
-
await runMutationWithContext(
|
|
321
|
-
() =>
|
|
322
|
-
|
|
323
|
-
{
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
322
|
+
const result = await runMutationWithContext(
|
|
323
|
+
() => withScopedApiRequestHeaders(
|
|
324
|
+
buildOptimisticLockHeader((data?.person as { updatedAt?: string } | undefined)?.updatedAt),
|
|
325
|
+
() => apiCallOrThrow<{ ok?: boolean; updatedAt?: string | null }>(
|
|
326
|
+
'/api/customers/people',
|
|
327
|
+
{
|
|
328
|
+
method: 'PUT',
|
|
329
|
+
headers: { 'content-type': 'application/json' },
|
|
330
|
+
body: JSON.stringify(payload),
|
|
331
|
+
},
|
|
332
|
+
{ errorMessage: t('customers.people.detail.inline.error') },
|
|
333
|
+
),
|
|
329
334
|
),
|
|
330
335
|
payload,
|
|
331
336
|
)
|
|
332
|
-
|
|
337
|
+
// Refresh the optimistic-lock token so the next sequential inline edit does not
|
|
338
|
+
// send a stale updatedAt and falsely 409 (#2055, default-ON locking).
|
|
339
|
+
const nextUpdatedAt = result?.result?.updatedAt ?? null
|
|
340
|
+
setData((prev) => {
|
|
341
|
+
if (!prev) return prev
|
|
342
|
+
const applied = apply(prev)
|
|
343
|
+
return nextUpdatedAt
|
|
344
|
+
? { ...applied, person: { ...applied.person, updatedAt: nextUpdatedAt } }
|
|
345
|
+
: applied
|
|
346
|
+
})
|
|
333
347
|
},
|
|
334
348
|
[data, runMutationWithContext, t]
|
|
335
349
|
)
|
|
@@ -382,13 +396,16 @@ export default function CustomerPersonDetailPage({ params }: { params?: { id?: s
|
|
|
382
396
|
setIsDeleting(true)
|
|
383
397
|
try {
|
|
384
398
|
await runMutationWithContext(
|
|
385
|
-
() =>
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
399
|
+
() => withScopedApiRequestHeaders(
|
|
400
|
+
buildOptimisticLockHeader((data?.person as { updatedAt?: string } | undefined)?.updatedAt),
|
|
401
|
+
() => apiCallOrThrow(
|
|
402
|
+
`/api/customers/people?id=${encodeURIComponent(personId)}`,
|
|
403
|
+
{
|
|
404
|
+
method: 'DELETE',
|
|
405
|
+
headers: { 'content-type': 'application/json' },
|
|
406
|
+
},
|
|
407
|
+
{ errorMessage: t('customers.people.list.deleteError') },
|
|
408
|
+
),
|
|
392
409
|
),
|
|
393
410
|
{ id: personId },
|
|
394
411
|
)
|
|
@@ -428,14 +445,17 @@ export default function CustomerPersonDetailPage({ params }: { params?: { id?: s
|
|
|
428
445
|
customFields: customPayload,
|
|
429
446
|
}
|
|
430
447
|
await runMutationWithContext(
|
|
431
|
-
() =>
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
448
|
+
() => withScopedApiRequestHeaders(
|
|
449
|
+
buildOptimisticLockHeader((data?.person as { updatedAt?: string } | undefined)?.updatedAt),
|
|
450
|
+
() => apiCallOrThrow(
|
|
451
|
+
'/api/customers/people',
|
|
452
|
+
{
|
|
453
|
+
method: 'PUT',
|
|
454
|
+
headers: { 'content-type': 'application/json' },
|
|
455
|
+
body: JSON.stringify(payload),
|
|
456
|
+
},
|
|
457
|
+
{ errorMessage: t('customers.people.detail.inline.error') },
|
|
458
|
+
),
|
|
439
459
|
),
|
|
440
460
|
payload,
|
|
441
461
|
)
|
|
@@ -461,6 +461,7 @@ export default function CustomersPeoplePage() {
|
|
|
461
461
|
try {
|
|
462
462
|
await runSingleMutation({
|
|
463
463
|
operation: async () => {
|
|
464
|
+
// optimistic-lock-exempt: delete-only mutation — no field-level lost-update
|
|
464
465
|
await apiCallOrThrow(
|
|
465
466
|
`/api/customers/people?id=${encodeURIComponent(person.id)}`,
|
|
466
467
|
{
|
|
@@ -500,6 +501,7 @@ export default function CustomersPeoplePage() {
|
|
|
500
501
|
runBulkDelete(
|
|
501
502
|
selectedRows,
|
|
502
503
|
async (row) => {
|
|
504
|
+
// optimistic-lock-exempt: bulk delete-only mutation — no field-level lost-update
|
|
503
505
|
await apiCallOrThrow(`/api/customers/people?id=${encodeURIComponent(row.id)}`, {
|
|
504
506
|
method: 'DELETE',
|
|
505
507
|
headers: { 'content-type': 'application/json' },
|
|
@@ -7,12 +7,16 @@ import { User, Hash, Users, Building2 } from 'lucide-react'
|
|
|
7
7
|
import { Page, PageBody } from '@open-mercato/ui/backend/Page'
|
|
8
8
|
import { CrudForm } from '@open-mercato/ui/backend/CrudForm'
|
|
9
9
|
import { CollapsibleZoneLayout, type ZoneSectionDescriptor } from '@open-mercato/ui/backend/crud/CollapsibleZoneLayout'
|
|
10
|
+
import { useIsMobile } from '@open-mercato/ui/hooks/useIsMobile'
|
|
10
11
|
import { updateCrud, deleteCrud } from '@open-mercato/ui/backend/utils/crud'
|
|
11
12
|
import { apiCallOrThrow } from '@open-mercato/ui/backend/utils/apiCall'
|
|
12
13
|
import { readApiResultOrThrow } from '@open-mercato/ui/backend/utils/apiCall'
|
|
14
|
+
import { withScopedApiRequestHeaders } from '@open-mercato/ui/backend/utils/apiCall'
|
|
15
|
+
import { buildOptimisticLockHeader } from '@open-mercato/ui/backend/utils/optimisticLock'
|
|
13
16
|
import { createCrudFormError } from '@open-mercato/ui/backend/utils/serverErrors'
|
|
14
17
|
import { E } from '#generated/entities.ids.generated'
|
|
15
18
|
import { flash } from '@open-mercato/ui/backend/FlashMessages'
|
|
19
|
+
import { surfaceRecordConflict } from '@open-mercato/ui/backend/conflicts'
|
|
16
20
|
import { useT } from '@open-mercato/shared/lib/i18n/context'
|
|
17
21
|
import { useOrganizationScopeDetail } from '@open-mercato/shared/lib/frontend/useOrganizationScope'
|
|
18
22
|
import { Button } from '@open-mercato/ui/primitives/button'
|
|
@@ -53,6 +57,7 @@ export default function PersonDetailV2Page({ params }: { params?: { id?: string
|
|
|
53
57
|
const router = useRouter()
|
|
54
58
|
const searchParams = useSearchParams()
|
|
55
59
|
const { organizationId } = useOrganizationScopeDetail()
|
|
60
|
+
const isMobile = useIsMobile()
|
|
56
61
|
const { confirm, ConfirmDialogElement } = useConfirmDialog()
|
|
57
62
|
|
|
58
63
|
const detailTranslator = React.useMemo(() => createTranslatorWithFallback(t), [t])
|
|
@@ -62,6 +67,15 @@ export default function PersonDetailV2Page({ params }: { params?: { id?: string
|
|
|
62
67
|
const fields = React.useMemo(() => createPersonEditFields(t), [t])
|
|
63
68
|
|
|
64
69
|
const [data, setData] = React.useState<PersonOverview | null>(null)
|
|
70
|
+
// Mirror the latest `data` into a ref so save handlers always read the current
|
|
71
|
+
// optimistic-lock token (`person.updatedAt`) instead of the value captured in
|
|
72
|
+
// their `useCallback` closure. Without this, a header-field save issued after a
|
|
73
|
+
// prior in-page reload would send a stale token (or none), letting a concurrent
|
|
74
|
+
// two-tab overwrite slip through without the 409 + conflict bar (#2055, Alina A7).
|
|
75
|
+
const dataRef = React.useRef<PersonOverview | null>(null)
|
|
76
|
+
React.useEffect(() => {
|
|
77
|
+
dataRef.current = data
|
|
78
|
+
}, [data])
|
|
65
79
|
const [isLoading, setIsLoading] = React.useState(true)
|
|
66
80
|
const [error, setError] = React.useState<string | null>(null)
|
|
67
81
|
const [isNotFound, setIsNotFound] = React.useState(false)
|
|
@@ -124,7 +138,7 @@ export default function PersonDetailV2Page({ params }: { params?: { id?: string
|
|
|
124
138
|
|
|
125
139
|
// Data loading
|
|
126
140
|
const initialLoadDoneRef = React.useRef(false)
|
|
127
|
-
const loadData = React.useCallback(async () => {
|
|
141
|
+
const loadData = React.useCallback(async (lockTokenOverride?: string | null) => {
|
|
128
142
|
if (!id) {
|
|
129
143
|
setIsNotFound(true)
|
|
130
144
|
setIsLoading(false)
|
|
@@ -141,7 +155,15 @@ export default function PersonDetailV2Page({ params }: { params?: { id?: string
|
|
|
141
155
|
undefined,
|
|
142
156
|
{ errorMessage: t('customers.people.detail.error.load', 'Failed to load person.') },
|
|
143
157
|
)
|
|
144
|
-
|
|
158
|
+
// When the caller is the save handler, pin the optimistic-lock token to the
|
|
159
|
+
// value the write itself returned rather than the one this GET observed — a
|
|
160
|
+
// concurrent third-party bump between save and reload must stay stale so the
|
|
161
|
+
// next in-page save 409s (#2055, Alina A7). Applied in the same state update
|
|
162
|
+
// as the refresh to avoid a redundant second re-render.
|
|
163
|
+
const next = lockTokenOverride && payload?.person
|
|
164
|
+
? { ...payload, person: { ...payload.person, updatedAt: lockTokenOverride, updated_at: lockTokenOverride } }
|
|
165
|
+
: payload
|
|
166
|
+
setData(next as PersonOverview)
|
|
145
167
|
} catch (err) {
|
|
146
168
|
if ((err as { status?: number }).status === 404) {
|
|
147
169
|
setIsNotFound(true)
|
|
@@ -230,6 +252,7 @@ export default function PersonDetailV2Page({ params }: { params?: { id?: string
|
|
|
230
252
|
// moment instead of "today" (#1807 prefill).
|
|
231
253
|
const editPayload = {
|
|
232
254
|
id: activity.id,
|
|
255
|
+
updatedAt: typeof raw.updatedAt === 'string' ? raw.updatedAt as string : typeof raw.updated_at === 'string' ? raw.updated_at as string : null,
|
|
233
256
|
interactionType: typeof activity.interactionType === 'string' ? activity.interactionType : undefined,
|
|
234
257
|
title: typeof activity.title === 'string' ? activity.title : null,
|
|
235
258
|
body: typeof activity.body === 'string' ? activity.body : null,
|
|
@@ -329,9 +352,27 @@ export default function PersonDetailV2Page({ params }: { params?: { id?: string
|
|
|
329
352
|
throw err
|
|
330
353
|
}
|
|
331
354
|
|
|
332
|
-
|
|
355
|
+
// Attach the current optimistic-lock token directly on this write path so
|
|
356
|
+
// every header-field edit (displayName/status/…) carries `updatedAt`, not
|
|
357
|
+
// just the fields the embedded CrudForm intercepts. Read from `dataRef` so
|
|
358
|
+
// the token reflects the latest in-page reload rather than a stale closure
|
|
359
|
+
// capture, and let the 409 propagate to CrudForm's surfaceRecordConflict so
|
|
360
|
+
// the unified conflict bar renders (#2055, Alina A7).
|
|
361
|
+
const lockedUpdatedAt = dataRef.current?.person?.updatedAt
|
|
362
|
+
?? dataRef.current?.person?.updated_at
|
|
363
|
+
?? null
|
|
364
|
+
const updateResponse = await withScopedApiRequestHeaders(
|
|
365
|
+
buildOptimisticLockHeader(lockedUpdatedAt),
|
|
366
|
+
() => updateCrud<{ updatedAt?: string | null }>('customers/people', payload),
|
|
367
|
+
)
|
|
333
368
|
flash(t('customers.people.form.updateSuccess', 'Person updated.'), 'success')
|
|
334
|
-
|
|
369
|
+
// Refresh the view and pin the optimistic-lock token to the write's OWN
|
|
370
|
+
// authoritative `updatedAt` in a single reload (see loadData) so a
|
|
371
|
+
// concurrent third-party bump stays stale on the next save (#2055, Alina A7).
|
|
372
|
+
const savedUpdatedAt = typeof updateResponse.result?.updatedAt === 'string'
|
|
373
|
+
? updateResponse.result.updatedAt
|
|
374
|
+
: null
|
|
375
|
+
await loadData(savedUpdatedAt)
|
|
335
376
|
} finally {
|
|
336
377
|
setIsSaving(false)
|
|
337
378
|
}
|
|
@@ -351,10 +392,28 @@ export default function PersonDetailV2Page({ params }: { params?: { id?: string
|
|
|
351
392
|
variant: 'destructive',
|
|
352
393
|
})
|
|
353
394
|
if (!approved) return
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
395
|
+
try {
|
|
396
|
+
await runMutationWithContext(
|
|
397
|
+
() => withScopedApiRequestHeaders(
|
|
398
|
+
buildOptimisticLockHeader(data?.person?.updatedAt ?? data?.person?.updated_at ?? null),
|
|
399
|
+
() => deleteCrud('customers/people', { id: personId }),
|
|
400
|
+
),
|
|
401
|
+
{ id: personId, operation: 'deletePerson' },
|
|
402
|
+
)
|
|
403
|
+
} catch (err) {
|
|
404
|
+
// The guarded mutation routes a 409 to the unified conflict bar; surface
|
|
405
|
+
// any other server error (e.g. a linked-records delete guard) as a flash
|
|
406
|
+
// instead of letting it crash the page.
|
|
407
|
+
if (!surfaceRecordConflict(err, t)) {
|
|
408
|
+
flash(
|
|
409
|
+
err instanceof Error && err.message.trim().length > 0
|
|
410
|
+
? err.message
|
|
411
|
+
: t('customers.people.detail.deleteError', 'Failed to delete person.'),
|
|
412
|
+
'error',
|
|
413
|
+
)
|
|
414
|
+
}
|
|
415
|
+
return
|
|
416
|
+
}
|
|
358
417
|
flash(t('customers.people.list.deleteSuccess', 'Person deleted.'), 'success')
|
|
359
418
|
router.push('/backend/customers/people')
|
|
360
419
|
},
|
|
@@ -464,6 +523,7 @@ export default function PersonDetailV2Page({ params }: { params?: { id?: string
|
|
|
464
523
|
fields={fields}
|
|
465
524
|
groups={groups}
|
|
466
525
|
initialValues={initialValues}
|
|
526
|
+
optimisticLockUpdatedAt={data.person.updatedAt ?? data.person.updated_at ?? null}
|
|
467
527
|
onSubmit={handleFormSubmit}
|
|
468
528
|
onDelete={handleFormDelete}
|
|
469
529
|
hideFooterActions
|
|
@@ -602,22 +662,22 @@ export default function PersonDetailV2Page({ params }: { params?: { id?: string
|
|
|
602
662
|
</div>
|
|
603
663
|
</PersonDetailTabs>
|
|
604
664
|
)
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
665
|
+
// Render only the layout variant that matches the viewport. Mounting
|
|
666
|
+
// both the mobile and desktop layouts at once would create two
|
|
667
|
+
// CrudForm instances bound to one `formWrapperRef`, so header Save
|
|
668
|
+
// could submit the hidden instance's stale values and silently
|
|
669
|
+
// discard the edit (#2453). `useIsMobile` is SSR-safe.
|
|
670
|
+
return isMobile ? (
|
|
671
|
+
<MobilePersonDetail zone1={zone1Content} zone2={zone2Content} />
|
|
672
|
+
) : (
|
|
673
|
+
<CollapsibleZoneLayout
|
|
674
|
+
pageType="person-v2"
|
|
675
|
+
entityName={personName}
|
|
676
|
+
isDirty={isDirty}
|
|
677
|
+
sections={zoneSections}
|
|
678
|
+
zone1={zone1Content}
|
|
679
|
+
zone2={zone2Content}
|
|
680
|
+
/>
|
|
621
681
|
)
|
|
622
682
|
})()}
|
|
623
683
|
|
|
@@ -213,22 +213,24 @@ const updateAddressCommand: CommandHandler<AddressUpdateInput, { addressId: stri
|
|
|
213
213
|
ensureSameScope(entity, address.organizationId, address.tenantId)
|
|
214
214
|
address.entity = entity
|
|
215
215
|
}
|
|
216
|
-
if (parsed.name !== undefined) address.name = parsed.name ?? null
|
|
217
|
-
if (parsed.purpose !== undefined) address.purpose = parsed.purpose ?? null
|
|
218
|
-
if (parsed.companyName !== undefined) address.companyName = parsed.companyName ?? null
|
|
219
|
-
if (parsed.addressLine1 !== undefined) address.addressLine1 = parsed.addressLine1
|
|
220
|
-
if (parsed.addressLine2 !== undefined) address.addressLine2 = parsed.addressLine2 ?? null
|
|
221
|
-
if (parsed.buildingNumber !== undefined) address.buildingNumber = parsed.buildingNumber ?? null
|
|
222
|
-
if (parsed.flatNumber !== undefined) address.flatNumber = parsed.flatNumber ?? null
|
|
223
|
-
if (parsed.city !== undefined) address.city = parsed.city ?? null
|
|
224
|
-
if (parsed.region !== undefined) address.region = parsed.region ?? null
|
|
225
|
-
if (parsed.postalCode !== undefined) address.postalCode = parsed.postalCode ?? null
|
|
226
|
-
if (parsed.country !== undefined) address.country = parsed.country ?? null
|
|
227
|
-
if (parsed.latitude !== undefined) address.latitude = parsed.latitude ?? null
|
|
228
|
-
if (parsed.longitude !== undefined) address.longitude = parsed.longitude ?? null
|
|
229
|
-
if (parsed.isPrimary !== undefined) address.isPrimary = parsed.isPrimary
|
|
230
216
|
|
|
231
217
|
await withAtomicFlush(em, [
|
|
218
|
+
() => {
|
|
219
|
+
if (parsed.name !== undefined) address.name = parsed.name ?? null
|
|
220
|
+
if (parsed.purpose !== undefined) address.purpose = parsed.purpose ?? null
|
|
221
|
+
if (parsed.companyName !== undefined) address.companyName = parsed.companyName ?? null
|
|
222
|
+
if (parsed.addressLine1 !== undefined) address.addressLine1 = parsed.addressLine1
|
|
223
|
+
if (parsed.addressLine2 !== undefined) address.addressLine2 = parsed.addressLine2 ?? null
|
|
224
|
+
if (parsed.buildingNumber !== undefined) address.buildingNumber = parsed.buildingNumber ?? null
|
|
225
|
+
if (parsed.flatNumber !== undefined) address.flatNumber = parsed.flatNumber ?? null
|
|
226
|
+
if (parsed.city !== undefined) address.city = parsed.city ?? null
|
|
227
|
+
if (parsed.region !== undefined) address.region = parsed.region ?? null
|
|
228
|
+
if (parsed.postalCode !== undefined) address.postalCode = parsed.postalCode ?? null
|
|
229
|
+
if (parsed.country !== undefined) address.country = parsed.country ?? null
|
|
230
|
+
if (parsed.latitude !== undefined) address.latitude = parsed.latitude ?? null
|
|
231
|
+
if (parsed.longitude !== undefined) address.longitude = parsed.longitude ?? null
|
|
232
|
+
if (parsed.isPrimary !== undefined) address.isPrimary = parsed.isPrimary
|
|
233
|
+
},
|
|
232
234
|
async () => {
|
|
233
235
|
if (address.isPrimary) {
|
|
234
236
|
await enforcePrimaryAddress(em, typeof address.entity === 'string' ? address.entity : address.entity.id, address.id)
|
|
@@ -677,7 +677,9 @@ const updateCompanyCommand: CommandHandler<CompanyUpdateInput, { entityId: strin
|
|
|
677
677
|
})
|
|
678
678
|
await emitQueryIndexUpsertEvents(ctx, [companyEntityIndexEntry(record)])
|
|
679
679
|
|
|
680
|
-
|
|
680
|
+
// Expose the freshly-bumped updatedAt so the CRUD update response can hand it to
|
|
681
|
+
// inline-edit detail pages for sequential-save lock-token refresh (#2055).
|
|
682
|
+
return { entityId: record.id, updatedAt: record.updatedAt }
|
|
681
683
|
},
|
|
682
684
|
captureAfter: async (_input, result, ctx) => {
|
|
683
685
|
const em = (ctx.container.resolve('em') as EntityManager).fork()
|
|
@@ -38,6 +38,7 @@ import {
|
|
|
38
38
|
buildCustomFieldResetMap,
|
|
39
39
|
} from '@open-mercato/shared/lib/commands/customFieldSnapshots'
|
|
40
40
|
import { CrudHttpError } from '@open-mercato/shared/lib/crud/errors'
|
|
41
|
+
import { enforceRecordGoneIsConflict, enforceCommandOptimisticLock } from '@open-mercato/shared/lib/crud/optimistic-lock-command'
|
|
41
42
|
import { findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'
|
|
42
43
|
import type { CrudIndexerConfig, CrudEventsConfig } from '@open-mercato/shared/lib/crud/types'
|
|
43
44
|
import { recomputeNextInteraction } from '../lib/interactionProjection'
|
|
@@ -451,10 +452,25 @@ const updateInteractionCommand: CommandHandler<InteractionUpdateInput, { interac
|
|
|
451
452
|
const em = (ctx.container.resolve('em') as EntityManager).fork()
|
|
452
453
|
const { interaction, entityId, nextInteractionId } = await runInTransaction(em, async (trx) => {
|
|
453
454
|
const interaction = await findOneWithDecryption(trx, CustomerInteraction, { id: parsed.id, deletedAt: null })
|
|
454
|
-
if (!interaction)
|
|
455
|
+
if (!interaction) {
|
|
456
|
+
enforceRecordGoneIsConflict({ resourceKind: 'customers.interaction', resourceId: parsed.id, request: ctx.request ?? null })
|
|
457
|
+
throw new CrudHttpError(404, { error: 'Interaction not found' })
|
|
458
|
+
}
|
|
455
459
|
ensureTenantScope(ctx, interaction.tenantId)
|
|
456
460
|
ensureOrganizationScope(ctx, interaction.organizationId)
|
|
457
461
|
|
|
462
|
+
// Concurrent-edit guard for command-driven callers (e.g. the legacy
|
|
463
|
+
// /api/customers/todos route, which bypasses the makeCrudRoute lock guard):
|
|
464
|
+
// when the client opted into optimistic locking, a stale edit fails with the
|
|
465
|
+
// unified 409 instead of silently overwriting (#2055). Strictly additive —
|
|
466
|
+
// no-op when no expected-version header is present.
|
|
467
|
+
enforceCommandOptimisticLock({
|
|
468
|
+
resourceKind: 'customers.interaction',
|
|
469
|
+
resourceId: interaction.id,
|
|
470
|
+
current: interaction.updatedAt,
|
|
471
|
+
request: ctx.request ?? null,
|
|
472
|
+
})
|
|
473
|
+
|
|
458
474
|
// Email visibility is an access-controlled field: only the interaction's
|
|
459
475
|
// author may change a
|
|
460
476
|
// private email's visibility (mirrors the dedicated PATCH .../visibility
|
|
@@ -709,10 +725,20 @@ const completeInteractionCommand: CommandHandler<InteractionCompleteInput, { int
|
|
|
709
725
|
const em = (ctx.container.resolve('em') as EntityManager).fork()
|
|
710
726
|
const { interaction, entityId, nextInteractionId } = await runInTransaction(em, async (trx) => {
|
|
711
727
|
const interaction = await findOneWithDecryption(trx, CustomerInteraction, { id: parsed.id, deletedAt: null })
|
|
712
|
-
if (!interaction)
|
|
728
|
+
if (!interaction) {
|
|
729
|
+
enforceRecordGoneIsConflict({ resourceKind: 'customers.interaction', resourceId: parsed.id, request: ctx.request ?? null })
|
|
730
|
+
throw new CrudHttpError(404, { error: 'Interaction not found' })
|
|
731
|
+
}
|
|
713
732
|
ensureTenantScope(ctx, interaction.tenantId)
|
|
714
733
|
ensureOrganizationScope(ctx, interaction.organizationId)
|
|
715
734
|
|
|
735
|
+
enforceCommandOptimisticLock({
|
|
736
|
+
resourceKind: 'customers.interaction',
|
|
737
|
+
resourceId: interaction.id,
|
|
738
|
+
current: interaction.updatedAt,
|
|
739
|
+
request: ctx.request ?? null,
|
|
740
|
+
})
|
|
741
|
+
|
|
716
742
|
interaction.status = 'done'
|
|
717
743
|
interaction.occurredAt = parsed.occurredAt ?? new Date()
|
|
718
744
|
await trx.flush()
|
|
@@ -839,10 +865,20 @@ const cancelInteractionCommand: CommandHandler<InteractionCancelInput, { interac
|
|
|
839
865
|
const em = (ctx.container.resolve('em') as EntityManager).fork()
|
|
840
866
|
const { interaction, entityId, nextInteractionId } = await runInTransaction(em, async (trx) => {
|
|
841
867
|
const interaction = await findOneWithDecryption(trx, CustomerInteraction, { id: parsed.id, deletedAt: null })
|
|
842
|
-
if (!interaction)
|
|
868
|
+
if (!interaction) {
|
|
869
|
+
enforceRecordGoneIsConflict({ resourceKind: 'customers.interaction', resourceId: parsed.id, request: ctx.request ?? null })
|
|
870
|
+
throw new CrudHttpError(404, { error: 'Interaction not found' })
|
|
871
|
+
}
|
|
843
872
|
ensureTenantScope(ctx, interaction.tenantId)
|
|
844
873
|
ensureOrganizationScope(ctx, interaction.organizationId)
|
|
845
874
|
|
|
875
|
+
enforceCommandOptimisticLock({
|
|
876
|
+
resourceKind: 'customers.interaction',
|
|
877
|
+
resourceId: interaction.id,
|
|
878
|
+
current: interaction.updatedAt,
|
|
879
|
+
request: ctx.request ?? null,
|
|
880
|
+
})
|
|
881
|
+
|
|
846
882
|
interaction.status = 'canceled'
|
|
847
883
|
await trx.flush()
|
|
848
884
|
|
|
@@ -967,10 +1003,20 @@ const deleteInteractionCommand: CommandHandler<{ body?: Record<string, unknown>;
|
|
|
967
1003
|
const em = (ctx.container.resolve('em') as EntityManager).fork()
|
|
968
1004
|
const { interaction, entityId, nextInteractionId } = await runInTransaction(em, async (trx) => {
|
|
969
1005
|
const interaction = await findOneWithDecryption(trx, CustomerInteraction, { id, deletedAt: null })
|
|
970
|
-
if (!interaction)
|
|
1006
|
+
if (!interaction) {
|
|
1007
|
+
enforceRecordGoneIsConflict({ resourceKind: 'customers.interaction', resourceId: id, request: ctx.request ?? null })
|
|
1008
|
+
throw new CrudHttpError(404, { error: 'Interaction not found' })
|
|
1009
|
+
}
|
|
971
1010
|
ensureTenantScope(ctx, interaction.tenantId)
|
|
972
1011
|
ensureOrganizationScope(ctx, interaction.organizationId)
|
|
973
1012
|
|
|
1013
|
+
enforceCommandOptimisticLock({
|
|
1014
|
+
resourceKind: 'customers.interaction',
|
|
1015
|
+
resourceId: interaction.id,
|
|
1016
|
+
current: interaction.updatedAt,
|
|
1017
|
+
request: ctx.request ?? null,
|
|
1018
|
+
})
|
|
1019
|
+
|
|
974
1020
|
const entityId = typeof interaction.entity === 'string' ? interaction.entity : interaction.entity.id
|
|
975
1021
|
interaction.deletedAt = new Date()
|
|
976
1022
|
await trx.flush()
|
|
@@ -866,7 +866,8 @@ const updatePersonCommand: CommandHandler<PersonUpdateInput, { entityId: string
|
|
|
866
866
|
})
|
|
867
867
|
await emitQueryIndexUpsertEvents(ctx, [personEntityIndexEntry(record)])
|
|
868
868
|
|
|
869
|
-
|
|
869
|
+
// Expose the freshly-bumped updatedAt for inline-edit sequential-save lock refresh (#2055).
|
|
870
|
+
return { entityId: record.id, updatedAt: record.updatedAt }
|
|
870
871
|
},
|
|
871
872
|
captureAfter: async (_input, result, ctx) => {
|
|
872
873
|
const em = (ctx.container.resolve('em') as EntityManager).fork()
|
|
@@ -382,6 +382,7 @@ const updatePersonCompanyLinkCommand: CommandHandler<PersonCompanyLinkUpdateInpu
|
|
|
382
382
|
const profile = await requirePersonProfile(em, person)
|
|
383
383
|
const linkedCompany = await requireCompanyEntity(em, companyId, parsed.tenantId, parsed.organizationId)
|
|
384
384
|
|
|
385
|
+
const linkWasPrimary = link.isPrimary
|
|
385
386
|
await withAtomicFlush(em, [
|
|
386
387
|
async () => {
|
|
387
388
|
if (parsed.isPrimary) {
|
|
@@ -389,16 +390,18 @@ const updatePersonCompanyLinkCommand: CommandHandler<PersonCompanyLinkUpdateInpu
|
|
|
389
390
|
link.isPrimary = true
|
|
390
391
|
profile.company = linkedCompany
|
|
391
392
|
} else if (!parsed.isPrimary) {
|
|
392
|
-
const linkWasPrimary = link.isPrimary
|
|
393
393
|
link.isPrimary = false
|
|
394
|
-
if (linkWasPrimary) {
|
|
395
|
-
const remainingLinks = (await loadPersonCompanyLinks(em, person)).filter((entry) => entry.id !== link.id)
|
|
396
|
-
await promoteFallbackPrimaryLink(em, person, profile, remainingLinks, companyId)
|
|
397
|
-
} else if (profile.company && typeof profile.company !== 'string' && profile.company.id === companyId) {
|
|
394
|
+
if (!linkWasPrimary && profile.company && typeof profile.company !== 'string' && profile.company.id === companyId) {
|
|
398
395
|
profile.company = null
|
|
399
396
|
}
|
|
400
397
|
}
|
|
401
398
|
},
|
|
399
|
+
async () => {
|
|
400
|
+
if (!parsed.isPrimary && linkWasPrimary) {
|
|
401
|
+
const remainingLinks = (await loadPersonCompanyLinks(em, person)).filter((entry) => entry.id !== link.id)
|
|
402
|
+
await promoteFallbackPrimaryLink(em, person, profile, remainingLinks, companyId)
|
|
403
|
+
}
|
|
404
|
+
},
|
|
402
405
|
], { transaction: true })
|
|
403
406
|
|
|
404
407
|
const dataEngine = ctx.container.resolve('dataEngine') as DataEngine
|
|
@@ -49,18 +49,6 @@ const createPipelineStageCommand: CommandHandler<PipelineStageCreateInput, { sta
|
|
|
49
49
|
? existingStages.length
|
|
50
50
|
: Math.max(0, Math.min(requestedOrder, existingStages.length))
|
|
51
51
|
|
|
52
|
-
// Shift the order of every stage at or after the insert position. Skipping
|
|
53
|
-
// this step would either duplicate `order` values (silently corrupting kanban
|
|
54
|
-
// ordering) or push the new stage to the wrong spot when re-sorting.
|
|
55
|
-
if (requestedOrder !== undefined) {
|
|
56
|
-
for (const stage of existingStages) {
|
|
57
|
-
if (stage.order >= insertOrder) {
|
|
58
|
-
stage.order += 1
|
|
59
|
-
stage.updatedAt = new Date()
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
|
|
64
52
|
const stage = em.create(CustomerPipelineStage, {
|
|
65
53
|
organizationId: parsed.organizationId,
|
|
66
54
|
tenantId: parsed.tenantId,
|
|
@@ -73,6 +61,17 @@ const createPipelineStageCommand: CommandHandler<PipelineStageCreateInput, { sta
|
|
|
73
61
|
|
|
74
62
|
await withAtomicFlush(em, [
|
|
75
63
|
() => {
|
|
64
|
+
// Shift the order of every stage at or after the insert position. Skipping
|
|
65
|
+
// this step would either duplicate `order` values (silently corrupting kanban
|
|
66
|
+
// ordering) or push the new stage to the wrong spot when re-sorting.
|
|
67
|
+
if (requestedOrder !== undefined) {
|
|
68
|
+
for (const existing of existingStages) {
|
|
69
|
+
if (existing.order >= insertOrder) {
|
|
70
|
+
existing.order += 1
|
|
71
|
+
existing.updatedAt = new Date()
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
76
75
|
em.persist(stage)
|
|
77
76
|
},
|
|
78
77
|
async () => {
|
|
@@ -112,11 +111,12 @@ const updatePipelineStageCommand: CommandHandler<PipelineStageUpdateInput, void>
|
|
|
112
111
|
ensureTenantScope(ctx, stage.tenantId)
|
|
113
112
|
ensureOrganizationScope(ctx, stage.organizationId)
|
|
114
113
|
|
|
115
|
-
if (parsed.label !== undefined) stage.label = parsed.label
|
|
116
|
-
if (parsed.order !== undefined) stage.order = parsed.order
|
|
117
|
-
stage.updatedAt = new Date()
|
|
118
|
-
|
|
119
114
|
await withAtomicFlush(em, [
|
|
115
|
+
() => {
|
|
116
|
+
if (parsed.label !== undefined) stage.label = parsed.label
|
|
117
|
+
if (parsed.order !== undefined) stage.order = parsed.order
|
|
118
|
+
stage.updatedAt = new Date()
|
|
119
|
+
},
|
|
120
120
|
async () => {
|
|
121
121
|
if (parsed.label !== undefined || parsed.color !== undefined || parsed.icon !== undefined) {
|
|
122
122
|
await ensureDictionaryEntry(em, {
|
|
@@ -87,6 +87,7 @@ export function AddressFormatSettings() {
|
|
|
87
87
|
setPending(next)
|
|
88
88
|
setError(null)
|
|
89
89
|
try {
|
|
90
|
+
// optimistic-lock-exempt: single-row tenant address-format preference toggle — no per-record version / concurrent record edit
|
|
90
91
|
const call = await apiCall<Record<string, unknown>>(
|
|
91
92
|
'/api/customers/settings/address-format',
|
|
92
93
|
{
|
|
@@ -8,7 +8,8 @@ import {
|
|
|
8
8
|
DialogTitle,
|
|
9
9
|
} from '@open-mercato/ui/primitives/dialog'
|
|
10
10
|
import { flash } from '@open-mercato/ui/backend/FlashMessages'
|
|
11
|
-
import { apiCallOrThrow, readApiResultOrThrow } from '@open-mercato/ui/backend/utils/apiCall'
|
|
11
|
+
import { apiCallOrThrow, readApiResultOrThrow, withScopedApiRequestHeaders } from '@open-mercato/ui/backend/utils/apiCall'
|
|
12
|
+
import { buildOptimisticLockHeader } from '@open-mercato/ui/backend/utils/optimisticLock'
|
|
12
13
|
import { useOrganizationScopeVersion } from '@open-mercato/shared/lib/frontend/useOrganizationScope'
|
|
13
14
|
import { useT } from '@open-mercato/shared/lib/i18n/context'
|
|
14
15
|
import { useConfirmDialog } from '@open-mercato/ui/backend/confirm-dialog'
|
|
@@ -220,10 +221,12 @@ function CustomerDictionarySection({ kind, title, description }: CustomerDiction
|
|
|
220
221
|
})
|
|
221
222
|
if (!confirmed) return
|
|
222
223
|
try {
|
|
223
|
-
await
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
224
|
+
await withScopedApiRequestHeaders(buildOptimisticLockHeader(entry.updatedAt), () =>
|
|
225
|
+
apiCallOrThrow(
|
|
226
|
+
`/api/customers/dictionaries/${kind}/${encodeURIComponent(entry.id)}`,
|
|
227
|
+
{ method: 'DELETE' },
|
|
228
|
+
{ errorMessage: errorDelete },
|
|
229
|
+
),
|
|
227
230
|
)
|
|
228
231
|
flash(successDelete, 'success')
|
|
229
232
|
await loadEntries()
|
|
@@ -281,14 +284,16 @@ function CustomerDictionarySection({ kind, title, description }: CustomerDiction
|
|
|
281
284
|
closeDialog()
|
|
282
285
|
return
|
|
283
286
|
}
|
|
284
|
-
await
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
287
|
+
await withScopedApiRequestHeaders(buildOptimisticLockHeader(target.updatedAt), () =>
|
|
288
|
+
apiCallOrThrow(
|
|
289
|
+
`/api/customers/dictionaries/${kind}/${encodeURIComponent(target.id)}`,
|
|
290
|
+
{
|
|
291
|
+
method: 'PATCH',
|
|
292
|
+
headers: { 'content-type': 'application/json' },
|
|
293
|
+
body: JSON.stringify(body),
|
|
294
|
+
},
|
|
295
|
+
{ errorMessage: errorSave },
|
|
296
|
+
),
|
|
292
297
|
)
|
|
293
298
|
flash(successSave, 'success')
|
|
294
299
|
}
|
|
@@ -170,6 +170,10 @@ export function DictionarySortSettings() {
|
|
|
170
170
|
setError(null)
|
|
171
171
|
try {
|
|
172
172
|
await runMutation({
|
|
173
|
+
// optimistic-lock-exempt: tenant-scoped settings blob (dictionary sort
|
|
174
|
+
// modes), not a versioned per-record entity — there is no `updatedAt`
|
|
175
|
+
// round-trip to lock against, and concurrent writes converge on the
|
|
176
|
+
// last-selected preference. Mirrors other singleton settings PATCHes.
|
|
173
177
|
operation: async () => {
|
|
174
178
|
await apiCallOrThrow(
|
|
175
179
|
'/api/customers/settings/dictionary-sort-modes',
|