@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
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@open-mercato/core",
|
|
3
|
-
"version": "0.6.5-develop.
|
|
3
|
+
"version": "0.6.5-develop.4559.1.839e136509",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -237,6 +237,7 @@
|
|
|
237
237
|
"html-to-text": "^10.0.0",
|
|
238
238
|
"mammoth": "^1.9.0",
|
|
239
239
|
"pdfjs-dist": "^6.0.227",
|
|
240
|
+
"resend": "^6.12.3",
|
|
240
241
|
"sanitize-html": "^2.13.0",
|
|
241
242
|
"semver": "^7.8.1",
|
|
242
243
|
"svix": "^1.95.1",
|
|
@@ -244,16 +245,16 @@
|
|
|
244
245
|
"zod": "^4.4.3"
|
|
245
246
|
},
|
|
246
247
|
"peerDependencies": {
|
|
247
|
-
"@open-mercato/ai-assistant": "0.6.5-develop.
|
|
248
|
-
"@open-mercato/shared": "0.6.5-develop.
|
|
249
|
-
"@open-mercato/ui": "0.6.5-develop.
|
|
248
|
+
"@open-mercato/ai-assistant": "0.6.5-develop.4559.1.839e136509",
|
|
249
|
+
"@open-mercato/shared": "0.6.5-develop.4559.1.839e136509",
|
|
250
|
+
"@open-mercato/ui": "0.6.5-develop.4559.1.839e136509",
|
|
250
251
|
"react": "^19.0.0",
|
|
251
252
|
"react-dom": "^19.0.0"
|
|
252
253
|
},
|
|
253
254
|
"devDependencies": {
|
|
254
|
-
"@open-mercato/ai-assistant": "0.6.5-develop.
|
|
255
|
-
"@open-mercato/shared": "0.6.5-develop.
|
|
256
|
-
"@open-mercato/ui": "0.6.5-develop.
|
|
255
|
+
"@open-mercato/ai-assistant": "0.6.5-develop.4559.1.839e136509",
|
|
256
|
+
"@open-mercato/shared": "0.6.5-develop.4559.1.839e136509",
|
|
257
|
+
"@open-mercato/ui": "0.6.5-develop.4559.1.839e136509",
|
|
257
258
|
"@testing-library/dom": "^10.4.1",
|
|
258
259
|
"@testing-library/jest-dom": "^6.9.1",
|
|
259
260
|
"@testing-library/react": "^16.3.1",
|
|
@@ -64,6 +64,11 @@ export async function seedConnectedChannel(
|
|
|
64
64
|
*
|
|
65
65
|
* Returns the created link + message ids (the message id is the platform
|
|
66
66
|
* `messages.message` id, usable as `messageThreadId` to thread a follow-up).
|
|
67
|
+
*
|
|
68
|
+
* Pass `createThreadMapping: true` to also seed a `ChannelThreadMapping` for the
|
|
69
|
+
* thread — required before exercising the reaction (`/messages/[id]/reactions`)
|
|
70
|
+
* and thread-assign (`/threads/[id]/assign`) routes, which resolve the owning
|
|
71
|
+
* channel through that mapping.
|
|
67
72
|
*/
|
|
68
73
|
export async function seedInboundMessage(
|
|
69
74
|
request: APIRequestContext,
|
|
@@ -80,6 +85,7 @@ export async function seedInboundMessage(
|
|
|
80
85
|
references?: string[];
|
|
81
86
|
messageThreadId?: string;
|
|
82
87
|
providerKey?: string;
|
|
88
|
+
createThreadMapping?: boolean;
|
|
83
89
|
},
|
|
84
90
|
): Promise<{ channelLinkId: string; messageId: string; conversationId: string }> {
|
|
85
91
|
const response = await apiRequest(request, 'POST', TEST_SEED_PATH, {
|
|
@@ -44,7 +44,7 @@ function resolveDatabaseUrl(): string {
|
|
|
44
44
|
return url;
|
|
45
45
|
}
|
|
46
46
|
|
|
47
|
-
async function withClient<T>(run: (client: Client) => Promise<T>): Promise<T> {
|
|
47
|
+
export async function withClient<T>(run: (client: Client) => Promise<T>): Promise<T> {
|
|
48
48
|
const client = new Client({ connectionString: resolveDatabaseUrl() });
|
|
49
49
|
await client.connect();
|
|
50
50
|
try {
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
import { expect, type APIRequestContext, type Page } from '@playwright/test'
|
|
2
|
+
import {
|
|
3
|
+
OPTIMISTIC_LOCK_HEADER_NAME,
|
|
4
|
+
OPTIMISTIC_LOCK_CONFLICT_CODE,
|
|
5
|
+
} from '@open-mercato/shared/lib/crud/optimistic-lock-headers'
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Shared helpers for the browser-driven optimistic-lock specs
|
|
9
|
+
* (`TC-LOCK-OSS-014..046`). They make the conflict deterministic without two
|
|
10
|
+
* real tabs or sleeps: the spec loads an edit page in the browser (the form
|
|
11
|
+
* captures the record's `updated_at`), then advances `updated_at` out-of-band
|
|
12
|
+
* via a header-less API PUT (additive path, always succeeds), and finally edits
|
|
13
|
+
* + saves in the browser so the now-stale header triggers the 409 → conflict bar.
|
|
14
|
+
*
|
|
15
|
+
* See `packages/core/src/modules/sales/__integration__/__concurrent_edit_pattern.md`
|
|
16
|
+
* and the conflict bar component
|
|
17
|
+
* `packages/ui/src/backend/conflicts/RecordConflictBanner.tsx`
|
|
18
|
+
* (`data-testid="record-conflict-banner"`).
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
const BASE_URL = process.env.BASE_URL?.trim() || ''
|
|
22
|
+
|
|
23
|
+
export function resolveApiUrl(path: string): string {
|
|
24
|
+
return BASE_URL ? `${BASE_URL}${path}` : path
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export const CONFLICT_BANNER_TESTID = 'record-conflict-banner'
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* The enterprise `record_locks` module (enabled in CI via
|
|
31
|
+
* `OM_ENABLE_ENTERPRISE_MODULES=true`) supersedes the OSS banner with a richer
|
|
32
|
+
* "Conflict detected" resolution dialog. Both are valid surfaces for "the stale
|
|
33
|
+
* write was refused": on a CrudForm edit page either one can win depending on
|
|
34
|
+
* whether the record_locks incoming-changes SSE (fired by the out-of-band bump)
|
|
35
|
+
* is processed before the browser's stale save reaches the server. Asserting one
|
|
36
|
+
* fixed surface is therefore racy; we wait for whichever conflict surface appears.
|
|
37
|
+
* (List-delete / non-form flows have no record_locks lock and only ever surface
|
|
38
|
+
* the OSS banner, so matching either surface is safe there too.)
|
|
39
|
+
*/
|
|
40
|
+
export const CONFLICT_DIALOG_TESTID = 'record-lock-conflict-dialog'
|
|
41
|
+
|
|
42
|
+
function authHeaders(token: string, lockValue?: string): Record<string, string> {
|
|
43
|
+
const headers: Record<string, string> = {
|
|
44
|
+
Authorization: `Bearer ${token}`,
|
|
45
|
+
'Content-Type': 'application/json',
|
|
46
|
+
}
|
|
47
|
+
if (lockValue !== undefined) headers[OPTIMISTIC_LOCK_HEADER_NAME] = lockValue
|
|
48
|
+
return headers
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Read a record's current `updated_at` from a list-shaped CRUD GET
|
|
53
|
+
* (`GET <basePath>?id=<id>` → `items[0].updated_at`), normalized to ISO.
|
|
54
|
+
* Works for the `makeCrudRoute` list responses (snake or camel case).
|
|
55
|
+
*/
|
|
56
|
+
export async function readUpdatedAt(
|
|
57
|
+
request: APIRequestContext,
|
|
58
|
+
token: string,
|
|
59
|
+
basePath: string,
|
|
60
|
+
id: string,
|
|
61
|
+
idParam = 'id',
|
|
62
|
+
): Promise<string> {
|
|
63
|
+
const response = await request.fetch(
|
|
64
|
+
resolveApiUrl(`${basePath}?${idParam}=${encodeURIComponent(id)}`),
|
|
65
|
+
{ method: 'GET', headers: authHeaders(token) },
|
|
66
|
+
)
|
|
67
|
+
expect(response.status(), `GET ${basePath}?${idParam}=... should be 200`).toBe(200)
|
|
68
|
+
const body = (await response.json()) as
|
|
69
|
+
| { items?: Array<Record<string, unknown>> }
|
|
70
|
+
| Record<string, unknown>
|
|
71
|
+
const item = Array.isArray((body as { items?: unknown[] }).items)
|
|
72
|
+
? (body as { items: Array<Record<string, unknown>> }).items[0]
|
|
73
|
+
: (body as Record<string, unknown>)
|
|
74
|
+
expect(item, `response should include the record for id=${id}`).toBeTruthy()
|
|
75
|
+
const raw = (item?.updated_at ?? item?.updatedAt) as string | undefined
|
|
76
|
+
expect(typeof raw, `record should expose updated_at, got ${String(raw)}`).toBe('string')
|
|
77
|
+
const ms = Date.parse(raw as string)
|
|
78
|
+
expect(Number.isFinite(ms), `updated_at should parse, got ${String(raw)}`).toBe(true)
|
|
79
|
+
return new Date(ms).toISOString()
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Advance a record's `updated_at` out-of-band so the browser's loaded form now
|
|
84
|
+
* holds a stale token. Uses a **header-less** PUT (the strictly-additive path
|
|
85
|
+
* always succeeds and bumps `updated_at`). Returns the new ISO `updated_at`.
|
|
86
|
+
*/
|
|
87
|
+
export async function bumpRecordViaApi(
|
|
88
|
+
request: APIRequestContext,
|
|
89
|
+
token: string,
|
|
90
|
+
basePath: string,
|
|
91
|
+
putBody: Record<string, unknown>,
|
|
92
|
+
opts: { idParam?: string; method?: 'PUT' | 'PATCH' } = {},
|
|
93
|
+
): Promise<string | null> {
|
|
94
|
+
const response = await request.fetch(resolveApiUrl(basePath), {
|
|
95
|
+
method: opts.method ?? 'PUT',
|
|
96
|
+
headers: authHeaders(token),
|
|
97
|
+
data: putBody,
|
|
98
|
+
})
|
|
99
|
+
expect(
|
|
100
|
+
response.status(),
|
|
101
|
+
`out-of-band ${opts.method ?? 'PUT'} ${basePath} should succeed (additive path), got ${response.status()}`,
|
|
102
|
+
).toBeLessThan(300)
|
|
103
|
+
const id = putBody[opts.idParam ?? 'id']
|
|
104
|
+
if (typeof id === 'string') {
|
|
105
|
+
try {
|
|
106
|
+
return await readUpdatedAt(request, token, basePath, id, opts.idParam)
|
|
107
|
+
} catch {
|
|
108
|
+
return null
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
return null
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/** Direct API helpers to assert the 409 contract body (used by the negative/UX specs). */
|
|
115
|
+
export async function putWithLock(
|
|
116
|
+
request: APIRequestContext,
|
|
117
|
+
token: string,
|
|
118
|
+
basePath: string,
|
|
119
|
+
body: Record<string, unknown>,
|
|
120
|
+
lockValue: string,
|
|
121
|
+
) {
|
|
122
|
+
return request.fetch(resolveApiUrl(basePath), {
|
|
123
|
+
method: 'PUT',
|
|
124
|
+
headers: authHeaders(token, lockValue),
|
|
125
|
+
data: body,
|
|
126
|
+
})
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
export async function expectConflictBody(response: { status(): number; json(): Promise<unknown> }) {
|
|
130
|
+
expect(response.status(), 'stale write should be 409').toBe(409)
|
|
131
|
+
const body = (await response.json()) as { code?: string; currentUpdatedAt?: string; expectedUpdatedAt?: string }
|
|
132
|
+
expect(body.code, 'body.code should be the optimistic-lock conflict code').toBe(OPTIMISTIC_LOCK_CONFLICT_CODE)
|
|
133
|
+
return body
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Assert a stale save was refused and surfaced as a conflict. Matches EITHER the
|
|
138
|
+
* OSS `record-conflict-banner` OR the enterprise record_locks "Conflict detected"
|
|
139
|
+
* dialog — see `CONFLICT_DIALOG_TESTID` for why both are valid and why pinning one
|
|
140
|
+
* is racy when enterprise modules are enabled.
|
|
141
|
+
*/
|
|
142
|
+
export async function expectConflictBanner(page: Page): Promise<void> {
|
|
143
|
+
const conflictSurface = page
|
|
144
|
+
.getByTestId(CONFLICT_BANNER_TESTID)
|
|
145
|
+
.or(page.getByTestId(CONFLICT_DIALOG_TESTID))
|
|
146
|
+
await expect(
|
|
147
|
+
conflictSurface.first(),
|
|
148
|
+
'a conflict surface (OSS bar or record_locks dialog) should appear after a stale save',
|
|
149
|
+
).toBeVisible({ timeout: 10_000 })
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/** Assert no conflict surface is present (a clean single-tab save must not 409). */
|
|
153
|
+
export async function expectNoConflictBanner(page: Page): Promise<void> {
|
|
154
|
+
await expect(
|
|
155
|
+
page.getByTestId(CONFLICT_BANNER_TESTID),
|
|
156
|
+
'a clean save must not surface a false-positive conflict bar',
|
|
157
|
+
).toHaveCount(0)
|
|
158
|
+
await expect(
|
|
159
|
+
page.getByTestId(CONFLICT_DIALOG_TESTID),
|
|
160
|
+
'a clean save must not surface a false-positive conflict dialog',
|
|
161
|
+
).toHaveCount(0)
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/** Click the conflict bar's Refresh button. */
|
|
165
|
+
export async function clickConflictRefresh(page: Page): Promise<void> {
|
|
166
|
+
await page.getByTestId(CONFLICT_BANNER_TESTID).getByRole('button', { name: /refresh/i }).click()
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/** Dismiss the conflict bar via its close (X) button. */
|
|
170
|
+
export async function dismissConflictBanner(page: Page): Promise<void> {
|
|
171
|
+
await page.getByTestId(CONFLICT_BANNER_TESTID).getByRole('button', { name: /dismiss/i }).click()
|
|
172
|
+
}
|
|
@@ -73,6 +73,35 @@ export async function createOrderLineFixture(
|
|
|
73
73
|
);
|
|
74
74
|
}
|
|
75
75
|
|
|
76
|
+
/**
|
|
77
|
+
* Probe whether the authenticated principal can create a sales order on the
|
|
78
|
+
* current tenant (i.e. holds `sales.orders.manage`). Sales-write integration
|
|
79
|
+
* specs use this to self-skip on dev databases whose role ACLs were never
|
|
80
|
+
* synced (`yarn mercato auth sync-role-acls`) rather than fail spuriously —
|
|
81
|
+
* CI bootstraps a fully-synced tenant so the probe passes there. The probed
|
|
82
|
+
* order is deleted immediately so the check leaves no residue.
|
|
83
|
+
*/
|
|
84
|
+
export async function canManageSalesOrders(
|
|
85
|
+
request: APIRequestContext,
|
|
86
|
+
token: string,
|
|
87
|
+
): Promise<boolean> {
|
|
88
|
+
const response = await apiRequest(request, 'POST', '/api/sales/orders', {
|
|
89
|
+
token,
|
|
90
|
+
data: { currencyCode: 'USD' },
|
|
91
|
+
});
|
|
92
|
+
if (response.status() === 403) return false;
|
|
93
|
+
if (!response.ok()) return false;
|
|
94
|
+
const id = readId((await response.json()) as unknown, ['id', 'orderId']);
|
|
95
|
+
if (id) {
|
|
96
|
+
try {
|
|
97
|
+
await apiRequest(request, 'DELETE', '/api/sales/orders', { token, data: { id } });
|
|
98
|
+
} catch {
|
|
99
|
+
// best-effort cleanup
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
return true;
|
|
103
|
+
}
|
|
104
|
+
|
|
76
105
|
export async function deleteSalesEntityIfExists(
|
|
77
106
|
request: APIRequestContext,
|
|
78
107
|
token: string | null,
|
|
@@ -6,7 +6,8 @@ import { DataTable } from '@open-mercato/ui/backend/DataTable'
|
|
|
6
6
|
import type { ColumnDef } from '@tanstack/react-table'
|
|
7
7
|
import { Button } from '@open-mercato/ui/primitives/button'
|
|
8
8
|
import { RowActions } from '@open-mercato/ui/backend/RowActions'
|
|
9
|
-
import { apiCall } from '@open-mercato/ui/backend/utils/apiCall'
|
|
9
|
+
import { apiCall, withScopedApiRequestHeaders } from '@open-mercato/ui/backend/utils/apiCall'
|
|
10
|
+
import { buildOptimisticLockHeader } from '@open-mercato/ui/backend/utils/optimisticLock'
|
|
10
11
|
import { flash } from '@open-mercato/ui/backend/FlashMessages'
|
|
11
12
|
import { useOrganizationScopeVersion } from '@open-mercato/shared/lib/frontend/useOrganizationScope'
|
|
12
13
|
import { useT } from '@open-mercato/shared/lib/i18n/context'
|
|
@@ -25,6 +26,7 @@ type Row = {
|
|
|
25
26
|
lastUsedAt: string | null
|
|
26
27
|
expiresAt: string | null
|
|
27
28
|
roles: RoleSummary[]
|
|
29
|
+
updatedAt?: string | null
|
|
28
30
|
}
|
|
29
31
|
|
|
30
32
|
type ResponsePayload = {
|
|
@@ -104,10 +106,13 @@ export default function ApiKeysListPage() {
|
|
|
104
106
|
})
|
|
105
107
|
if (!confirmed) return
|
|
106
108
|
try {
|
|
107
|
-
const call = await
|
|
108
|
-
|
|
109
|
-
{
|
|
110
|
-
|
|
109
|
+
const call = await withScopedApiRequestHeaders(
|
|
110
|
+
buildOptimisticLockHeader(row.updatedAt),
|
|
111
|
+
() => apiCall<{ error?: string }>(
|
|
112
|
+
`/api/api_keys/keys?id=${encodeURIComponent(row.id)}`,
|
|
113
|
+
{ method: 'DELETE' },
|
|
114
|
+
{ fallback: null },
|
|
115
|
+
),
|
|
111
116
|
)
|
|
112
117
|
if (!call.ok) {
|
|
113
118
|
const errorPayload = call.result as { error?: string } | undefined
|
|
@@ -22,7 +22,8 @@ import {
|
|
|
22
22
|
DialogHeader,
|
|
23
23
|
DialogTitle,
|
|
24
24
|
} from '@open-mercato/ui/primitives/dialog'
|
|
25
|
-
import { apiCall, readApiResultOrThrow } from '@open-mercato/ui/backend/utils/apiCall'
|
|
25
|
+
import { apiCall, readApiResultOrThrow, withScopedApiRequestHeaders } from '@open-mercato/ui/backend/utils/apiCall'
|
|
26
|
+
import { buildOptimisticLockHeader } from '@open-mercato/ui/backend/utils/optimisticLock'
|
|
26
27
|
import { flash } from '@open-mercato/ui/backend/FlashMessages'
|
|
27
28
|
import { raiseCrudError } from '@open-mercato/ui/backend/utils/serverErrors'
|
|
28
29
|
import { useT } from '@open-mercato/shared/lib/i18n/context'
|
|
@@ -41,6 +42,7 @@ type Partition = {
|
|
|
41
42
|
configJson: Record<string, unknown> | null
|
|
42
43
|
envKey: string
|
|
43
44
|
createdAt: string | null
|
|
45
|
+
updatedAt?: string | null
|
|
44
46
|
}
|
|
45
47
|
|
|
46
48
|
type DialogState =
|
|
@@ -230,11 +232,15 @@ export function AttachmentPartitionSettings({ s3Enabled }: AttachmentPartitionSe
|
|
|
230
232
|
dialog.mode === 'edit'
|
|
231
233
|
? JSON.stringify({ id: dialog.entry.id, ...payload })
|
|
232
234
|
: JSON.stringify(payload)
|
|
233
|
-
const
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
235
|
+
const lockHeader =
|
|
236
|
+
dialog.mode === 'edit' ? buildOptimisticLockHeader(dialog.entry.updatedAt) : {}
|
|
237
|
+
const call = await withScopedApiRequestHeaders(lockHeader, () =>
|
|
238
|
+
apiCall('/api/attachments/partitions', {
|
|
239
|
+
method,
|
|
240
|
+
headers: { 'content-type': 'application/json' },
|
|
241
|
+
body,
|
|
242
|
+
}),
|
|
243
|
+
)
|
|
238
244
|
if (!call.ok) {
|
|
239
245
|
await raiseCrudError(
|
|
240
246
|
call.response,
|
|
@@ -271,9 +277,13 @@ export function AttachmentPartitionSettings({ s3Enabled }: AttachmentPartitionSe
|
|
|
271
277
|
})
|
|
272
278
|
if (!confirmed) return
|
|
273
279
|
try {
|
|
274
|
-
const call = await
|
|
275
|
-
|
|
276
|
-
|
|
280
|
+
const call = await withScopedApiRequestHeaders(
|
|
281
|
+
buildOptimisticLockHeader(entry.updatedAt),
|
|
282
|
+
() =>
|
|
283
|
+
apiCall(`/api/attachments/partitions?id=${encodeURIComponent(entry.id)}`, {
|
|
284
|
+
method: 'DELETE',
|
|
285
|
+
}),
|
|
286
|
+
)
|
|
277
287
|
if (!call.ok) {
|
|
278
288
|
await raiseCrudError(
|
|
279
289
|
call.response,
|
|
@@ -5,11 +5,12 @@ import { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'
|
|
|
5
5
|
import { createRequestContainer } from '@open-mercato/shared/lib/di/container'
|
|
6
6
|
import { logCrudAccess } from '@open-mercato/shared/lib/crud/factory'
|
|
7
7
|
import { isCrudHttpError } from '@open-mercato/shared/lib/crud/errors'
|
|
8
|
+
import { enforceCommandOptimisticLock } from '@open-mercato/shared/lib/crud/optimistic-lock-command'
|
|
9
|
+
import { withAtomicFlush } from '@open-mercato/shared/lib/commands/flush'
|
|
8
10
|
import { RoleAcl, Role } from '@open-mercato/core/modules/auth/data/entities'
|
|
9
11
|
import type { EntityManager } from '@mikro-orm/postgresql'
|
|
10
12
|
import { resolveIsSuperAdmin } from '@open-mercato/core/modules/auth/lib/tenantAccess'
|
|
11
13
|
import { RbacService } from '@open-mercato/core/modules/auth/services/rbacService'
|
|
12
|
-
import { withAtomicFlush } from '@open-mercato/shared/lib/commands/flush'
|
|
13
14
|
import {
|
|
14
15
|
assertActorCanGrantAcl,
|
|
15
16
|
assertActorCanModifySuperAdminRoleTarget,
|
|
@@ -39,6 +40,7 @@ const roleAclResponseSchema = z.object({
|
|
|
39
40
|
isSuperAdmin: z.boolean(),
|
|
40
41
|
features: z.array(z.string()),
|
|
41
42
|
organizations: z.array(z.string()).nullable(),
|
|
43
|
+
updatedAt: z.string().nullable(),
|
|
42
44
|
})
|
|
43
45
|
|
|
44
46
|
const roleAclUpdateResponseSchema = z.object({
|
|
@@ -101,8 +103,9 @@ export async function GET(req: Request) {
|
|
|
101
103
|
isSuperAdmin: !!acl.isSuperAdmin,
|
|
102
104
|
features: Array.isArray(acl.featuresJson) ? acl.featuresJson : [],
|
|
103
105
|
organizations: Array.isArray(acl.organizationsJson) ? acl.organizationsJson : null,
|
|
106
|
+
updatedAt: acl.updatedAt instanceof Date ? acl.updatedAt.toISOString() : null,
|
|
104
107
|
}
|
|
105
|
-
: { isSuperAdmin: false, features: [], organizations: null }
|
|
108
|
+
: { isSuperAdmin: false, features: [], organizations: null, updatedAt: null }
|
|
106
109
|
|
|
107
110
|
await logCrudAccess({
|
|
108
111
|
container,
|
|
@@ -173,7 +176,24 @@ export async function PUT(req: Request) {
|
|
|
173
176
|
}
|
|
174
177
|
|
|
175
178
|
let acl = await em.findOne(RoleAcl, { role, tenantId: targetTenantId })
|
|
176
|
-
|
|
179
|
+
// Optimistic lock: refuse a stale ACL overwrite so two admins editing the same
|
|
180
|
+
// role's features in parallel cannot silently clobber each other (#2055). The
|
|
181
|
+
// check is strictly additive — when the client sends no expected-version header
|
|
182
|
+
// it is a no-op. Skipped when the ACL row does not exist yet (first grant has
|
|
183
|
+
// no prior version to conflict with).
|
|
184
|
+
if (acl) {
|
|
185
|
+
try {
|
|
186
|
+
enforceCommandOptimisticLock({
|
|
187
|
+
resourceKind: 'auth.role_acl',
|
|
188
|
+
resourceId: acl.id,
|
|
189
|
+
current: acl.updatedAt ?? null,
|
|
190
|
+
request: req,
|
|
191
|
+
})
|
|
192
|
+
} catch (err) {
|
|
193
|
+
if (isCrudHttpError(err)) return NextResponse.json(err.body, { status: err.status })
|
|
194
|
+
throw err
|
|
195
|
+
}
|
|
196
|
+
} else {
|
|
177
197
|
acl = em.create(RoleAcl, {
|
|
178
198
|
role,
|
|
179
199
|
tenantId: targetTenantId,
|
|
@@ -209,15 +229,21 @@ export async function PUT(req: Request) {
|
|
|
209
229
|
throw err
|
|
210
230
|
}
|
|
211
231
|
|
|
232
|
+
// Persist the ACL mutation inside a transaction so the role-permission write
|
|
233
|
+
// commits atomically (proper ACL-edit transaction handling).
|
|
212
234
|
const aclToPersist = acl
|
|
213
|
-
await withAtomicFlush(
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
235
|
+
await withAtomicFlush(
|
|
236
|
+
em,
|
|
237
|
+
[
|
|
238
|
+
() => {
|
|
239
|
+
aclToPersist.organizationsJson = requestedOrganizations
|
|
240
|
+
aclToPersist.isSuperAdmin = requestedIsSuperAdmin
|
|
241
|
+
aclToPersist.featuresJson = requestedFeatures
|
|
242
|
+
em.persist(aclToPersist)
|
|
243
|
+
},
|
|
244
|
+
],
|
|
245
|
+
{ transaction: true },
|
|
246
|
+
)
|
|
221
247
|
|
|
222
248
|
// Invalidate cache for all users in this tenant since role ACL changed
|
|
223
249
|
if (targetTenantId) {
|
|
@@ -43,6 +43,7 @@ const roleListItemSchema = z.object({
|
|
|
43
43
|
tenantId: z.string().uuid().nullable(),
|
|
44
44
|
tenantIds: z.array(z.string().uuid()).optional(),
|
|
45
45
|
tenantName: z.string().nullable(),
|
|
46
|
+
updatedAt: z.string().nullable().optional(),
|
|
46
47
|
})
|
|
47
48
|
|
|
48
49
|
const roleListResponseSchema = z.object({
|
|
@@ -223,6 +224,7 @@ export async function GET(req: Request) {
|
|
|
223
224
|
tenantId: tenantId ?? null,
|
|
224
225
|
tenantIds: exposeTenant && tenantId ? [tenantId] : [],
|
|
225
226
|
tenantName: exposeTenant ? tenantName : null,
|
|
227
|
+
updatedAt: r.updatedAt instanceof Date ? r.updatedAt.toISOString() : null,
|
|
226
228
|
...(cfByRole[idStr] || {}),
|
|
227
229
|
}
|
|
228
230
|
})
|
|
@@ -9,13 +9,17 @@ import {
|
|
|
9
9
|
sidebarPreferencesScopeSchema,
|
|
10
10
|
} from '../../../data/validators'
|
|
11
11
|
import {
|
|
12
|
+
loadRoleSidebarPreferenceUpdatedAt,
|
|
12
13
|
loadRoleSidebarPreferences,
|
|
13
14
|
loadSidebarPreference,
|
|
15
|
+
loadSidebarPreferenceUpdatedAt,
|
|
14
16
|
saveRoleSidebarPreference,
|
|
15
17
|
saveSidebarPreference,
|
|
16
18
|
} from '../../../services/sidebarPreferencesService'
|
|
17
19
|
import { SIDEBAR_PREFERENCES_VERSION } from '@open-mercato/shared/modules/navigation/sidebarPreferences'
|
|
18
20
|
import { withAtomicFlush } from '@open-mercato/shared/lib/commands/flush'
|
|
21
|
+
import { enforceCommandOptimisticLock } from '@open-mercato/shared/lib/crud/optimistic-lock-command'
|
|
22
|
+
import { isCrudHttpError } from '@open-mercato/shared/lib/crud/errors'
|
|
19
23
|
import { Role, RoleSidebarPreference } from '../../../data/entities'
|
|
20
24
|
import type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'
|
|
21
25
|
import { z } from 'zod'
|
|
@@ -47,6 +51,7 @@ const sidebarPreferencesResponseSchema = z.object({
|
|
|
47
51
|
canApplyToRoles: z.boolean(),
|
|
48
52
|
roles: z.array(sidebarRoleEntrySchema),
|
|
49
53
|
scope: sidebarPreferencesScopeSchema,
|
|
54
|
+
updatedAt: z.string().datetime().nullable(),
|
|
50
55
|
})
|
|
51
56
|
|
|
52
57
|
const sidebarPreferencesUpdateResponseSchema = sidebarPreferencesResponseSchema.extend({
|
|
@@ -165,6 +170,11 @@ export async function GET(req: Request) {
|
|
|
165
170
|
})
|
|
166
171
|
const pref = rolePrefs.get(role.id) ?? null
|
|
167
172
|
const rolesPayload = await loadRolesPayload(em, { tenantId: auth.tenantId ?? null, locale })
|
|
173
|
+
const roleVersion = await loadRoleSidebarPreferenceUpdatedAt(em, {
|
|
174
|
+
roleId: role.id,
|
|
175
|
+
tenantId: auth.tenantId ?? null,
|
|
176
|
+
locale,
|
|
177
|
+
})
|
|
168
178
|
return NextResponse.json({
|
|
169
179
|
locale,
|
|
170
180
|
settings: pref
|
|
@@ -180,6 +190,7 @@ export async function GET(req: Request) {
|
|
|
180
190
|
canApplyToRoles,
|
|
181
191
|
roles: rolesPayload,
|
|
182
192
|
scope: { type: 'role', roleId: role.id },
|
|
193
|
+
updatedAt: roleVersion?.updatedAt ? roleVersion.updatedAt.toISOString() : null,
|
|
183
194
|
})
|
|
184
195
|
}
|
|
185
196
|
|
|
@@ -198,6 +209,15 @@ export async function GET(req: Request) {
|
|
|
198
209
|
? await loadRolesPayload(em, { tenantId: auth.tenantId ?? null, locale })
|
|
199
210
|
: []
|
|
200
211
|
|
|
212
|
+
const userVersion = effectiveUserId
|
|
213
|
+
? await loadSidebarPreferenceUpdatedAt(em, {
|
|
214
|
+
userId: effectiveUserId,
|
|
215
|
+
tenantId: auth.tenantId ?? null,
|
|
216
|
+
organizationId: auth.orgId ?? null,
|
|
217
|
+
locale,
|
|
218
|
+
})
|
|
219
|
+
: null
|
|
220
|
+
|
|
201
221
|
return NextResponse.json({
|
|
202
222
|
locale,
|
|
203
223
|
settings: {
|
|
@@ -211,6 +231,7 @@ export async function GET(req: Request) {
|
|
|
211
231
|
canApplyToRoles,
|
|
212
232
|
roles: rolesPayload,
|
|
213
233
|
scope: { type: 'user' },
|
|
234
|
+
updatedAt: userVersion?.updatedAt ? userVersion.updatedAt.toISOString() : null,
|
|
214
235
|
})
|
|
215
236
|
}
|
|
216
237
|
|
|
@@ -318,11 +339,34 @@ export async function PUT(req: Request) {
|
|
|
318
339
|
if (!role) {
|
|
319
340
|
return NextResponse.json({ error: 'Role not found' }, { status: 404 })
|
|
320
341
|
}
|
|
342
|
+
const existingRolePref = await loadRoleSidebarPreferenceUpdatedAt(em, {
|
|
343
|
+
roleId: role.id,
|
|
344
|
+
tenantId: auth.tenantId ?? null,
|
|
345
|
+
locale,
|
|
346
|
+
})
|
|
347
|
+
if (existingRolePref) {
|
|
348
|
+
try {
|
|
349
|
+
enforceCommandOptimisticLock({
|
|
350
|
+
resourceKind: 'auth.role_sidebar_preference',
|
|
351
|
+
resourceId: existingRolePref.id,
|
|
352
|
+
current: existingRolePref.updatedAt ?? null,
|
|
353
|
+
request: req,
|
|
354
|
+
})
|
|
355
|
+
} catch (err) {
|
|
356
|
+
if (isCrudHttpError(err)) return NextResponse.json(err.body, { status: err.status })
|
|
357
|
+
throw err
|
|
358
|
+
}
|
|
359
|
+
}
|
|
321
360
|
const saved = await saveRoleSidebarPreference(em, {
|
|
322
361
|
roleId: role.id,
|
|
323
362
|
tenantId: auth.tenantId ?? null,
|
|
324
363
|
locale,
|
|
325
364
|
}, payload)
|
|
365
|
+
const savedRoleVersion = await loadRoleSidebarPreferenceUpdatedAt(em, {
|
|
366
|
+
roleId: role.id,
|
|
367
|
+
tenantId: auth.tenantId ?? null,
|
|
368
|
+
locale,
|
|
369
|
+
})
|
|
326
370
|
if (cache?.deleteByTags) {
|
|
327
371
|
try {
|
|
328
372
|
await cache.deleteByTags([`nav:sidebar:role:${role.id}`])
|
|
@@ -342,6 +386,7 @@ export async function PUT(req: Request) {
|
|
|
342
386
|
canApplyToRoles,
|
|
343
387
|
roles: rolesPayload,
|
|
344
388
|
scope: { type: 'role', roleId: role.id },
|
|
389
|
+
updatedAt: savedRoleVersion?.updatedAt ? savedRoleVersion.updatedAt.toISOString() : null,
|
|
345
390
|
appliedRoles: [],
|
|
346
391
|
clearedRoles: [],
|
|
347
392
|
})
|
|
@@ -356,6 +401,26 @@ export async function PUT(req: Request) {
|
|
|
356
401
|
return NextResponse.json({ error: 'Forbidden', requiredFeatures: [FEATURE_MANAGE] }, { status: 403 })
|
|
357
402
|
}
|
|
358
403
|
|
|
404
|
+
const existingUserPref = await loadSidebarPreferenceUpdatedAt(em, {
|
|
405
|
+
userId: effectiveUserId,
|
|
406
|
+
tenantId: auth.tenantId ?? null,
|
|
407
|
+
organizationId: auth.orgId ?? null,
|
|
408
|
+
locale,
|
|
409
|
+
})
|
|
410
|
+
if (existingUserPref) {
|
|
411
|
+
try {
|
|
412
|
+
enforceCommandOptimisticLock({
|
|
413
|
+
resourceKind: 'auth.sidebar_preference',
|
|
414
|
+
resourceId: existingUserPref.id,
|
|
415
|
+
current: existingUserPref.updatedAt ?? null,
|
|
416
|
+
request: req,
|
|
417
|
+
})
|
|
418
|
+
} catch (err) {
|
|
419
|
+
if (isCrudHttpError(err)) return NextResponse.json(err.body, { status: err.status })
|
|
420
|
+
throw err
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
|
|
359
424
|
const settings = await saveSidebarPreference(em, {
|
|
360
425
|
userId: effectiveUserId,
|
|
361
426
|
tenantId: auth.tenantId ?? null,
|
|
@@ -444,12 +509,20 @@ export async function PUT(req: Request) {
|
|
|
444
509
|
}))
|
|
445
510
|
}
|
|
446
511
|
|
|
512
|
+
const savedUserVersion = await loadSidebarPreferenceUpdatedAt(em, {
|
|
513
|
+
userId: effectiveUserId,
|
|
514
|
+
tenantId: auth.tenantId ?? null,
|
|
515
|
+
organizationId: auth.orgId ?? null,
|
|
516
|
+
locale,
|
|
517
|
+
})
|
|
518
|
+
|
|
447
519
|
return NextResponse.json({
|
|
448
520
|
locale,
|
|
449
521
|
settings,
|
|
450
522
|
canApplyToRoles,
|
|
451
523
|
roles: rolesPayload,
|
|
452
524
|
scope: { type: 'user' },
|
|
525
|
+
updatedAt: savedUserVersion?.updatedAt ? savedUserVersion.updatedAt.toISOString() : null,
|
|
453
526
|
appliedRoles: updatedRoleIds,
|
|
454
527
|
clearedRoles: filteredClearRoleIds,
|
|
455
528
|
})
|