@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
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../src/modules/catalog/components/categories/CategoriesDataTable.tsx"],
|
|
4
|
-
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport Link from 'next/link'\nimport { useQuery, useQueryClient } from '@tanstack/react-query'\nimport type { ColumnDef } from '@tanstack/react-table'\nimport { DataTable } from '@open-mercato/ui/backend/DataTable'\nimport type { FilterValues } from '@open-mercato/ui/backend/FilterBar'\nimport { RowActions } from '@open-mercato/ui/backend/RowActions'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { BooleanIcon } from '@open-mercato/ui/backend/ValueIcons'\nimport { apiCall, apiCallOrThrow, readApiResultOrThrow } from '@open-mercato/ui/backend/utils/apiCall'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { useOrganizationScopeVersion } from '@open-mercato/shared/lib/frontend/useOrganizationScope'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { useConfirmDialog } from '@open-mercato/ui/backend/confirm-dialog'\nimport { formatCategoryTreeLabel } from '../../lib/categoryTree'\n\ntype CategoryRow = {\n id: string\n name: string\n slug: string | null\n description: string | null\n parentId: string | null\n parentName: string | null\n depth: number\n treePath: string\n pathLabel: string\n childCount: number\n descendantCount: number\n isActive: boolean\n}\n\ntype CategoriesResponse = {\n items: CategoryRow[]\n total: number\n page: number\n pageSize: number\n totalPages: number\n}\n\nconst PAGE_SIZE = 50\nconst TREE_BASE_INDENT = 18\nconst TREE_STEP_INDENT = 14\n\nfunction computeIndent(depth: number): number {\n if (depth <= 0) return 0\n return TREE_BASE_INDENT + (depth - 1) * TREE_STEP_INDENT\n}\n\nexport default function CategoriesDataTable() {\n const t = useT()\n const { confirm, ConfirmDialogElement } = useConfirmDialog()\n const queryClient = useQueryClient()\n const scopeVersion = useOrganizationScopeVersion()\n const [page, setPage] = React.useState(1)\n const [status, setStatus] = React.useState<'all' | 'active' | 'inactive'>('all')\n const [search, setSearch] = React.useState('')\n const [canManage, setCanManage] = React.useState(false)\n\n React.useEffect(() => {\n let cancelled = false\n async function load() {\n try {\n const call = await apiCall<{ granted?: string[]; ok?: boolean }>('/api/auth/feature-check', {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ features: ['catalog.categories.manage'] }),\n })\n if (!cancelled) {\n const granted = Array.isArray(call.result?.granted) ? call.result.granted : []\n setCanManage(call.result?.ok === true || granted.includes('catalog.categories.manage'))\n }\n } catch {\n if (!cancelled) setCanManage(false)\n }\n }\n load()\n return () => {\n cancelled = true\n }\n }, [])\n\n const queryParams = React.useMemo(() => {\n const params = new URLSearchParams()\n params.set('view', 'manage')\n params.set('page', String(page))\n params.set('pageSize', String(PAGE_SIZE))\n params.set('status', status)\n if (search) params.set('search', search)\n return params.toString()\n }, [page, status, search])\n\n const { data, isLoading } = useQuery<CategoriesResponse>({\n queryKey: ['catalog-categories', queryParams, scopeVersion],\n queryFn: async () => {\n const payload = await readApiResultOrThrow<CategoriesResponse>(\n `/api/catalog/categories?${queryParams}`,\n undefined,\n { errorMessage: t('catalog.categories.list.error.load', 'Failed to load categories') },\n )\n return {\n items: Array.isArray(payload.items) ? payload.items : [],\n total: typeof payload.total === 'number' ? payload.total : 0,\n page: typeof payload.page === 'number' ? payload.page : 1,\n pageSize: typeof payload.pageSize === 'number' ? payload.pageSize : PAGE_SIZE,\n totalPages: typeof payload.totalPages === 'number' ? payload.totalPages : 1,\n }\n },\n })\n\n const rows = data?.items ?? []\n const total = data?.total ?? 0\n const totalPages = data?.totalPages ?? 0\n\n const columns = React.useMemo<ColumnDef<CategoryRow>[]>(() => [\n {\n accessorKey: 'name',\n header: t('catalog.categories.list.columns.category', 'Category'),\n meta: { priority: 1 },\n cell: ({ row }) => {\n const depth = row.original.depth ?? 0\n return (\n <div className=\"flex items-center text-sm font-medium leading-none text-foreground\">\n <span style={{ marginLeft: computeIndent(depth), whiteSpace: 'pre' }}>\n {formatCategoryTreeLabel(row.original.name, depth)}\n </span>\n </div>\n )\n },\n },\n {\n accessorKey: 'pathLabel',\n header: t('catalog.categories.list.columns.path', 'Path'),\n meta: { priority: 3 },\n cell: ({ getValue }) => {\n const value = getValue<string>()\n return <span className=\"text-xs text-muted-foreground\">{value || '\u2014'}</span>\n },\n },\n {\n accessorKey: 'parentName',\n header: t('catalog.categories.list.columns.parent', 'Parent'),\n meta: { priority: 4 },\n cell: ({ getValue }) => getValue<string>() || t('catalog.categories.list.none', '\u2014'),\n },\n {\n accessorKey: 'childCount',\n header: t('catalog.categories.list.columns.children', 'Children'),\n meta: { priority: 5 },\n },\n {\n accessorKey: 'isActive',\n header: t('catalog.categories.list.columns.active', 'Active'),\n enableSorting: false,\n meta: { priority: 2 },\n cell: ({ getValue }) => <BooleanIcon value={Boolean(getValue())} />,\n },\n ], [t])\n\n const handleDelete = React.useCallback(async (category: CategoryRow) => {\n const confirmLabel = t(\n 'catalog.categories.list.confirmDelete',\n 'Archive category \"{{name}}\"?',\n { name: category.name },\n )\n const confirmed = await confirm({\n title: confirmLabel,\n variant: 'destructive',\n })\n if (!confirmed) return\n try {\n await apiCallOrThrow(\n `/api/catalog/categories?id=${encodeURIComponent(category.id)}`,\n { method: 'DELETE' },\n { errorMessage: t('catalog.categories.list.error.delete', 'Failed to delete category') },\n )\n await queryClient.invalidateQueries({ queryKey: ['catalog-categories'] })\n flash(t('catalog.categories.flash.deleted', 'Category archived'), 'success')\n } catch (err: unknown) {\n const fallback = t('catalog.categories.list.error.delete', 'Failed to delete category')\n const message = err instanceof Error ? err.message : fallback\n flash(message, 'error')\n }\n }, [confirm, queryClient, t])\n\n return (\n <>\n <DataTable\n title={t('catalog.categories.list.title', 'Categories')}\n actions={canManage ? (\n <Button asChild>\n <Link href=\"/backend/catalog/categories/create\">\n {t('catalog.categories.list.actions.create', 'Create')}\n </Link>\n </Button>\n ) : undefined}\n columns={columns}\n data={rows}\n searchValue={search}\n searchPlaceholder={t('catalog.categories.list.searchPlaceholder', 'Search categories')}\n onSearchChange={(value) => { setSearch(value); setPage(1) }}\n filters={[\n {\n id: 'status',\n label: t('catalog.categories.list.filters.status', 'Status'),\n type: 'select',\n options: [\n { value: 'all', label: t('catalog.categories.list.filters.all', 'All') },\n { value: 'active', label: t('catalog.categories.list.filters.active', 'Active') },\n { value: 'inactive', label: t('catalog.categories.list.filters.inactive', 'Inactive') },\n ],\n },\n ]}\n filterValues={status === 'all' ? {} : { status }}\n onFiltersApply={(values: FilterValues) => {\n const nextStatus = (values.status as 'all' | 'active' | 'inactive' | undefined) ?? 'all'\n setStatus(nextStatus)\n setPage(1)\n }}\n onFiltersClear={() => {\n setStatus('all')\n setPage(1)\n }}\n sortable={false}\n perspective={{ tableId: 'catalog.categories.list' }}\n rowActions={(row) => (\n canManage ? (\n <RowActions\n items={[\n { id: 'edit', label: t('catalog.categories.list.actions.edit', 'Edit'), href: `/backend/catalog/categories/${row.id}/edit` },\n { id: 'delete', label: t('catalog.categories.list.actions.delete', 'Delete'), destructive: true, onSelect: () => handleDelete(row) },\n ]}\n />\n ) : null\n )}\n pagination={{\n page,\n pageSize: PAGE_SIZE,\n total,\n totalPages,\n onPageChange: setPage,\n }}\n isLoading={isLoading}\n />\n {ConfirmDialogElement}\n </>\n )\n}\n"],
|
|
5
|
-
"mappings": ";AA4HY,
|
|
4
|
+
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport Link from 'next/link'\nimport { useQuery, useQueryClient } from '@tanstack/react-query'\nimport type { ColumnDef } from '@tanstack/react-table'\nimport { DataTable } from '@open-mercato/ui/backend/DataTable'\nimport type { FilterValues } from '@open-mercato/ui/backend/FilterBar'\nimport { RowActions } from '@open-mercato/ui/backend/RowActions'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { BooleanIcon } from '@open-mercato/ui/backend/ValueIcons'\nimport { apiCall, apiCallOrThrow, readApiResultOrThrow } from '@open-mercato/ui/backend/utils/apiCall'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { useOrganizationScopeVersion } from '@open-mercato/shared/lib/frontend/useOrganizationScope'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { useConfirmDialog } from '@open-mercato/ui/backend/confirm-dialog'\nimport { formatCategoryTreeLabel } from '../../lib/categoryTree'\n\ntype CategoryRow = {\n id: string\n name: string\n slug: string | null\n description: string | null\n parentId: string | null\n parentName: string | null\n depth: number\n treePath: string\n pathLabel: string\n childCount: number\n descendantCount: number\n isActive: boolean\n}\n\ntype CategoriesResponse = {\n items: CategoryRow[]\n total: number\n page: number\n pageSize: number\n totalPages: number\n}\n\nconst PAGE_SIZE = 50\nconst TREE_BASE_INDENT = 18\nconst TREE_STEP_INDENT = 14\n\nfunction computeIndent(depth: number): number {\n if (depth <= 0) return 0\n return TREE_BASE_INDENT + (depth - 1) * TREE_STEP_INDENT\n}\n\nexport default function CategoriesDataTable() {\n const t = useT()\n const { confirm, ConfirmDialogElement } = useConfirmDialog()\n const queryClient = useQueryClient()\n const scopeVersion = useOrganizationScopeVersion()\n const [page, setPage] = React.useState(1)\n const [status, setStatus] = React.useState<'all' | 'active' | 'inactive'>('all')\n const [search, setSearch] = React.useState('')\n const [canManage, setCanManage] = React.useState(false)\n\n React.useEffect(() => {\n let cancelled = false\n async function load() {\n try {\n const call = await apiCall<{ granted?: string[]; ok?: boolean }>('/api/auth/feature-check', {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ features: ['catalog.categories.manage'] }),\n })\n if (!cancelled) {\n const granted = Array.isArray(call.result?.granted) ? call.result.granted : []\n setCanManage(call.result?.ok === true || granted.includes('catalog.categories.manage'))\n }\n } catch {\n if (!cancelled) setCanManage(false)\n }\n }\n load()\n return () => {\n cancelled = true\n }\n }, [])\n\n const queryParams = React.useMemo(() => {\n const params = new URLSearchParams()\n params.set('view', 'manage')\n params.set('page', String(page))\n params.set('pageSize', String(PAGE_SIZE))\n params.set('status', status)\n if (search) params.set('search', search)\n return params.toString()\n }, [page, status, search])\n\n const { data, isLoading } = useQuery<CategoriesResponse>({\n queryKey: ['catalog-categories', queryParams, scopeVersion],\n queryFn: async () => {\n const payload = await readApiResultOrThrow<CategoriesResponse>(\n `/api/catalog/categories?${queryParams}`,\n undefined,\n { errorMessage: t('catalog.categories.list.error.load', 'Failed to load categories') },\n )\n return {\n items: Array.isArray(payload.items) ? payload.items : [],\n total: typeof payload.total === 'number' ? payload.total : 0,\n page: typeof payload.page === 'number' ? payload.page : 1,\n pageSize: typeof payload.pageSize === 'number' ? payload.pageSize : PAGE_SIZE,\n totalPages: typeof payload.totalPages === 'number' ? payload.totalPages : 1,\n }\n },\n })\n\n const rows = data?.items ?? []\n const total = data?.total ?? 0\n const totalPages = data?.totalPages ?? 0\n\n const columns = React.useMemo<ColumnDef<CategoryRow>[]>(() => [\n {\n accessorKey: 'name',\n header: t('catalog.categories.list.columns.category', 'Category'),\n meta: { priority: 1 },\n cell: ({ row }) => {\n const depth = row.original.depth ?? 0\n return (\n <div className=\"flex items-center text-sm font-medium leading-none text-foreground\">\n <span style={{ marginLeft: computeIndent(depth), whiteSpace: 'pre' }}>\n {formatCategoryTreeLabel(row.original.name, depth)}\n </span>\n </div>\n )\n },\n },\n {\n accessorKey: 'pathLabel',\n header: t('catalog.categories.list.columns.path', 'Path'),\n meta: { priority: 3 },\n cell: ({ getValue }) => {\n const value = getValue<string>()\n return <span className=\"text-xs text-muted-foreground\">{value || '\u2014'}</span>\n },\n },\n {\n accessorKey: 'parentName',\n header: t('catalog.categories.list.columns.parent', 'Parent'),\n meta: { priority: 4 },\n cell: ({ getValue }) => getValue<string>() || t('catalog.categories.list.none', '\u2014'),\n },\n {\n accessorKey: 'childCount',\n header: t('catalog.categories.list.columns.children', 'Children'),\n meta: { priority: 5 },\n },\n {\n accessorKey: 'isActive',\n header: t('catalog.categories.list.columns.active', 'Active'),\n enableSorting: false,\n meta: { priority: 2 },\n cell: ({ getValue }) => <BooleanIcon value={Boolean(getValue())} />,\n },\n ], [t])\n\n const handleDelete = React.useCallback(async (category: CategoryRow) => {\n const confirmLabel = t(\n 'catalog.categories.list.confirmDelete',\n 'Archive category \"{{name}}\"?',\n { name: category.name },\n )\n const confirmed = await confirm({\n title: confirmLabel,\n variant: 'destructive',\n })\n if (!confirmed) return\n try {\n // optimistic-lock-exempt: delete-only mutation \u2014 no field-level lost-update\n await apiCallOrThrow(\n `/api/catalog/categories?id=${encodeURIComponent(category.id)}`,\n { method: 'DELETE' },\n { errorMessage: t('catalog.categories.list.error.delete', 'Failed to delete category') },\n )\n await queryClient.invalidateQueries({ queryKey: ['catalog-categories'] })\n flash(t('catalog.categories.flash.deleted', 'Category archived'), 'success')\n } catch (err: unknown) {\n const fallback = t('catalog.categories.list.error.delete', 'Failed to delete category')\n const message = err instanceof Error ? err.message : fallback\n flash(message, 'error')\n }\n }, [confirm, queryClient, t])\n\n return (\n <>\n <DataTable\n title={t('catalog.categories.list.title', 'Categories')}\n actions={canManage ? (\n <Button asChild>\n <Link href=\"/backend/catalog/categories/create\">\n {t('catalog.categories.list.actions.create', 'Create')}\n </Link>\n </Button>\n ) : undefined}\n columns={columns}\n data={rows}\n searchValue={search}\n searchPlaceholder={t('catalog.categories.list.searchPlaceholder', 'Search categories')}\n onSearchChange={(value) => { setSearch(value); setPage(1) }}\n filters={[\n {\n id: 'status',\n label: t('catalog.categories.list.filters.status', 'Status'),\n type: 'select',\n options: [\n { value: 'all', label: t('catalog.categories.list.filters.all', 'All') },\n { value: 'active', label: t('catalog.categories.list.filters.active', 'Active') },\n { value: 'inactive', label: t('catalog.categories.list.filters.inactive', 'Inactive') },\n ],\n },\n ]}\n filterValues={status === 'all' ? {} : { status }}\n onFiltersApply={(values: FilterValues) => {\n const nextStatus = (values.status as 'all' | 'active' | 'inactive' | undefined) ?? 'all'\n setStatus(nextStatus)\n setPage(1)\n }}\n onFiltersClear={() => {\n setStatus('all')\n setPage(1)\n }}\n sortable={false}\n perspective={{ tableId: 'catalog.categories.list' }}\n rowActions={(row) => (\n canManage ? (\n <RowActions\n items={[\n { id: 'edit', label: t('catalog.categories.list.actions.edit', 'Edit'), href: `/backend/catalog/categories/${row.id}/edit` },\n { id: 'delete', label: t('catalog.categories.list.actions.delete', 'Delete'), destructive: true, onSelect: () => handleDelete(row) },\n ]}\n />\n ) : null\n )}\n pagination={{\n page,\n pageSize: PAGE_SIZE,\n total,\n totalPages,\n onPageChange: setPage,\n }}\n isLoading={isLoading}\n />\n {ConfirmDialogElement}\n </>\n )\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../src/modules/catalog/components/products/ProductMediaManager.tsx"],
|
|
4
|
-
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { Upload, Image as ImageIcon, Trash2, Star } from 'lucide-react'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { apiCall } from '@open-mercato/ui/backend/utils/apiCall'\nimport { cn } from '@open-mercato/shared/lib/utils'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { buildAttachmentImageUrl, slugifyAttachmentFileName } from '@open-mercato/core/modules/attachments/lib/imageUrls'\n\nexport type ProductMediaItem = {\n id: string\n url: string\n fileName: string\n fileSize: number\n thumbnailUrl?: string | null\n}\n\ntype Props = {\n entityId: string\n draftRecordId: string\n items: ProductMediaItem[]\n defaultMediaId: string | null\n onItemsChange: (items: ProductMediaItem[]) => void\n onDefaultChange: (attachmentId: string | null) => void\n}\n\nfunction humanFileSize(size: number): string {\n if (!Number.isFinite(size)) return `${size}`\n const units = ['B', 'KB', 'MB', 'GB']\n let idx = 0\n let value = size\n while (value >= 1024 && idx < units.length - 1) {\n value /= 1024\n idx++\n }\n return `${value.toFixed(idx === 0 ? 0 : 1)} ${units[idx]}`\n}\n\nexport function ProductMediaManager({\n entityId,\n draftRecordId,\n items,\n defaultMediaId,\n onItemsChange,\n onDefaultChange,\n}: Props) {\n const t = useT()\n const fileInputRef = React.useRef<HTMLInputElement | null>(null)\n const [error, setError] = React.useState<string | null>(null)\n const [isUploading, setUploading] = React.useState(false)\n const [isDragOver, setDragOver] = React.useState(false)\n\n const acceptFiles = React.useCallback(\n async (files: FileList | null) => {\n if (!files || !files.length || !draftRecordId) return\n setError(null)\n setUploading(true)\n let nextItems = items\n let defaultId = defaultMediaId\n try {\n for (const file of Array.from(files)) {\n if (!file.type.startsWith('image/')) {\n setError(t('catalog.products.media.errors.imagesOnly', 'Only image files are supported.'))\n continue\n }\n const fd = new FormData()\n fd.set('entityId', entityId)\n fd.set('recordId', draftRecordId)\n fd.set('file', file)\n const call = await apiCall<{ ok?: boolean; item?: ProductMediaItem; error?: string }>(\n '/api/attachments',\n { method: 'POST', body: fd },\n { fallback: null },\n )\n if (!call.ok || !call.result?.item) {\n const message = call.result?.error ?? t('catalog.products.media.errors.uploadFailed', 'Upload failed.')\n setError(message)\n break\n }\n const existingIds = new Set(nextItems.map((entry) => entry.id))\n if (existingIds.has(call.result.item.id)) continue\n const slug = slugifyAttachmentFileName(call.result.item.fileName)\n const uploaded = {\n ...call.result.item,\n thumbnailUrl:\n call.result.item.thumbnailUrl ??\n buildAttachmentImageUrl(call.result.item.id, { width: 360, height: 360, slug }),\n }\n nextItems = [...nextItems, uploaded]\n if (!defaultId) {\n defaultId = uploaded.id\n }\n }\n if (nextItems !== items) {\n onItemsChange(nextItems)\n }\n if (!defaultMediaId && defaultId) {\n onDefaultChange(defaultId)\n }\n } finally {\n setUploading(false)\n if (fileInputRef.current) {\n fileInputRef.current.value = ''\n }\n }\n },\n [defaultMediaId, draftRecordId, entityId, items, onDefaultChange, onItemsChange, t],\n )\n\n const handleRemove = React.useCallback(\n async (attachmentId: string) => {\n setError(null)\n const call = await apiCall<{ ok?: boolean; error?: string }>(\n `/api/attachments?id=${encodeURIComponent(attachmentId)}`,\n { method: 'DELETE' },\n { fallback: null },\n )\n if (!call.ok) {\n setError(call.result?.error ?? t('catalog.products.media.errors.deleteFailed', 'Failed to delete media.'))\n return\n }\n const next = items.filter((item) => item.id !== attachmentId)\n onItemsChange(next)\n if (defaultMediaId === attachmentId) {\n onDefaultChange(next[0]?.id ?? null)\n }\n },\n [defaultMediaId, items, onDefaultChange, onItemsChange, t],\n )\n\n const onDrop = React.useCallback(\n (event: React.DragEvent<HTMLDivElement>) => {\n event.preventDefault()\n event.stopPropagation()\n setDragOver(false)\n void acceptFiles(event.dataTransfer?.files ?? null)\n },\n [acceptFiles],\n )\n\n const onDragOver = React.useCallback((event: React.DragEvent<HTMLDivElement>) => {\n event.preventDefault()\n event.stopPropagation()\n setDragOver(true)\n }, [])\n\n const onDragLeave = React.useCallback((event: React.DragEvent<HTMLDivElement>) => {\n event.preventDefault()\n event.stopPropagation()\n setDragOver(false)\n }, [])\n\n const pickFiles = React.useCallback(() => {\n fileInputRef.current?.click()\n }, [])\n\n return (\n <div className=\"space-y-3\">\n <label className=\"text-sm font-medium\">{t('catalog.products.media.label', 'Media')}</label>\n <div\n className={cn(\n 'flex flex-col items-center justify-center rounded-lg border border-dashed p-6 text-center transition-colors',\n isDragOver ? 'border-primary bg-primary/5' : 'border-muted-foreground/30',\n )}\n onDrop={onDrop}\n onDragOver={onDragOver}\n onDragLeave={onDragLeave}\n role=\"presentation\"\n >\n <Upload className=\"mx-auto h-6 w-6 text-muted-foreground\" />\n <p className=\"mt-2 text-sm text-muted-foreground\">\n {t('catalog.products.media.dropHint', 'Drag and drop images here or click to upload.')}\n </p>\n <Button type=\"button\" variant=\"outline\" size=\"sm\" className=\"mt-4\" onClick={pickFiles} disabled={isUploading}>\n {isUploading ? t('catalog.products.media.uploading', 'Uploading\u2026') : t('catalog.products.media.choose', 'Choose files')}\n </Button>\n <input\n ref={fileInputRef}\n type=\"file\"\n accept=\"image/*\"\n multiple\n className=\"hidden\"\n onChange={(event) => void acceptFiles(event.target.files)}\n />\n </div>\n {error ? <p className=\"text-xs font-medium text-red-600\">{error}</p> : null}\n {items.length ? (\n <div className=\"grid grid-cols-2 gap-3 sm:grid-cols-3\">\n {items.map((item) => {\n const isDefault = defaultMediaId === item.id\n const slug = slugifyAttachmentFileName(item.fileName)\n const thumbnail =\n item.thumbnailUrl ||\n buildAttachmentImageUrl(item.id, { width: 360, height: 360, slug })\n return (\n <div key={item.id} className=\"flex flex-col rounded-md border bg-card\">\n <div className=\"relative aspect-square overflow-hidden rounded-t-md bg-muted\">\n {thumbnail ? (\n <img src={thumbnail} alt={item.fileName} className=\"h-full w-full object-cover\" />\n ) : (\n <div className=\"flex h-full items-center justify-center text-muted-foreground\">\n <ImageIcon className=\"h-8 w-8\" />\n </div>\n )}\n <div className=\"absolute right-1 top-1 flex gap-1\">\n <Button\n type=\"button\"\n variant={isDefault ? 'default' : 'secondary'}\n size=\"icon\"\n className={cn('h-7 w-7', isDefault ? '' : 'opacity-80')}\n onClick={() => onDefaultChange(item.id)}\n >\n <Star className=\"h-3.5 w-3.5\" />\n </Button>\n <Button type=\"button\" variant=\"secondary\" size=\"icon\" className=\"h-7 w-7\" onClick={() => void handleRemove(item.id)}>\n <Trash2 className=\"h-3.5 w-3.5\" />\n </Button>\n </div>\n </div>\n <div className=\"p-2\">\n <p className=\"line-clamp-1 text-sm font-medium\">{item.fileName}</p>\n <p className=\"text-xs text-muted-foreground\">{humanFileSize(item.fileSize)}</p>\n {isDefault ? (\n <p className=\"text-xs font-semibold text-primary\">\n {t('catalog.products.media.default', 'Default preview')}\n </p>\n ) : null}\n </div>\n </div>\n )\n })}\n </div>\n ) : (\n <p className=\"text-xs text-muted-foreground\">\n {t('catalog.products.media.empty', 'No media uploaded yet.')}\n </p>\n )}\n </div>\n )\n}\n"],
|
|
5
|
-
"mappings": ";
|
|
4
|
+
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { Upload, Image as ImageIcon, Trash2, Star } from 'lucide-react'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { apiCall } from '@open-mercato/ui/backend/utils/apiCall'\nimport { cn } from '@open-mercato/shared/lib/utils'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { buildAttachmentImageUrl, slugifyAttachmentFileName } from '@open-mercato/core/modules/attachments/lib/imageUrls'\n\nexport type ProductMediaItem = {\n id: string\n url: string\n fileName: string\n fileSize: number\n thumbnailUrl?: string | null\n}\n\ntype Props = {\n entityId: string\n draftRecordId: string\n items: ProductMediaItem[]\n defaultMediaId: string | null\n onItemsChange: (items: ProductMediaItem[]) => void\n onDefaultChange: (attachmentId: string | null) => void\n}\n\nfunction humanFileSize(size: number): string {\n if (!Number.isFinite(size)) return `${size}`\n const units = ['B', 'KB', 'MB', 'GB']\n let idx = 0\n let value = size\n while (value >= 1024 && idx < units.length - 1) {\n value /= 1024\n idx++\n }\n return `${value.toFixed(idx === 0 ? 0 : 1)} ${units[idx]}`\n}\n\nexport function ProductMediaManager({\n entityId,\n draftRecordId,\n items,\n defaultMediaId,\n onItemsChange,\n onDefaultChange,\n}: Props) {\n const t = useT()\n const fileInputRef = React.useRef<HTMLInputElement | null>(null)\n const [error, setError] = React.useState<string | null>(null)\n const [isUploading, setUploading] = React.useState(false)\n const [isDragOver, setDragOver] = React.useState(false)\n\n const acceptFiles = React.useCallback(\n async (files: FileList | null) => {\n if (!files || !files.length || !draftRecordId) return\n setError(null)\n setUploading(true)\n let nextItems = items\n let defaultId = defaultMediaId\n try {\n for (const file of Array.from(files)) {\n if (!file.type.startsWith('image/')) {\n setError(t('catalog.products.media.errors.imagesOnly', 'Only image files are supported.'))\n continue\n }\n const fd = new FormData()\n fd.set('entityId', entityId)\n fd.set('recordId', draftRecordId)\n fd.set('file', file)\n // optimistic-lock-exempt: media attach/detach/reorder\n const call = await apiCall<{ ok?: boolean; item?: ProductMediaItem; error?: string }>(\n '/api/attachments',\n { method: 'POST', body: fd },\n { fallback: null },\n )\n if (!call.ok || !call.result?.item) {\n const message = call.result?.error ?? t('catalog.products.media.errors.uploadFailed', 'Upload failed.')\n setError(message)\n break\n }\n const existingIds = new Set(nextItems.map((entry) => entry.id))\n if (existingIds.has(call.result.item.id)) continue\n const slug = slugifyAttachmentFileName(call.result.item.fileName)\n const uploaded = {\n ...call.result.item,\n thumbnailUrl:\n call.result.item.thumbnailUrl ??\n buildAttachmentImageUrl(call.result.item.id, { width: 360, height: 360, slug }),\n }\n nextItems = [...nextItems, uploaded]\n if (!defaultId) {\n defaultId = uploaded.id\n }\n }\n if (nextItems !== items) {\n onItemsChange(nextItems)\n }\n if (!defaultMediaId && defaultId) {\n onDefaultChange(defaultId)\n }\n } finally {\n setUploading(false)\n if (fileInputRef.current) {\n fileInputRef.current.value = ''\n }\n }\n },\n [defaultMediaId, draftRecordId, entityId, items, onDefaultChange, onItemsChange, t],\n )\n\n const handleRemove = React.useCallback(\n async (attachmentId: string) => {\n setError(null)\n // optimistic-lock-exempt: media attach/detach/reorder\n const call = await apiCall<{ ok?: boolean; error?: string }>(\n `/api/attachments?id=${encodeURIComponent(attachmentId)}`,\n { method: 'DELETE' },\n { fallback: null },\n )\n if (!call.ok) {\n setError(call.result?.error ?? t('catalog.products.media.errors.deleteFailed', 'Failed to delete media.'))\n return\n }\n const next = items.filter((item) => item.id !== attachmentId)\n onItemsChange(next)\n if (defaultMediaId === attachmentId) {\n onDefaultChange(next[0]?.id ?? null)\n }\n },\n [defaultMediaId, items, onDefaultChange, onItemsChange, t],\n )\n\n const onDrop = React.useCallback(\n (event: React.DragEvent<HTMLDivElement>) => {\n event.preventDefault()\n event.stopPropagation()\n setDragOver(false)\n void acceptFiles(event.dataTransfer?.files ?? null)\n },\n [acceptFiles],\n )\n\n const onDragOver = React.useCallback((event: React.DragEvent<HTMLDivElement>) => {\n event.preventDefault()\n event.stopPropagation()\n setDragOver(true)\n }, [])\n\n const onDragLeave = React.useCallback((event: React.DragEvent<HTMLDivElement>) => {\n event.preventDefault()\n event.stopPropagation()\n setDragOver(false)\n }, [])\n\n const pickFiles = React.useCallback(() => {\n fileInputRef.current?.click()\n }, [])\n\n return (\n <div className=\"space-y-3\">\n <label className=\"text-sm font-medium\">{t('catalog.products.media.label', 'Media')}</label>\n <div\n className={cn(\n 'flex flex-col items-center justify-center rounded-lg border border-dashed p-6 text-center transition-colors',\n isDragOver ? 'border-primary bg-primary/5' : 'border-muted-foreground/30',\n )}\n onDrop={onDrop}\n onDragOver={onDragOver}\n onDragLeave={onDragLeave}\n role=\"presentation\"\n >\n <Upload className=\"mx-auto h-6 w-6 text-muted-foreground\" />\n <p className=\"mt-2 text-sm text-muted-foreground\">\n {t('catalog.products.media.dropHint', 'Drag and drop images here or click to upload.')}\n </p>\n <Button type=\"button\" variant=\"outline\" size=\"sm\" className=\"mt-4\" onClick={pickFiles} disabled={isUploading}>\n {isUploading ? t('catalog.products.media.uploading', 'Uploading\u2026') : t('catalog.products.media.choose', 'Choose files')}\n </Button>\n <input\n ref={fileInputRef}\n type=\"file\"\n accept=\"image/*\"\n multiple\n className=\"hidden\"\n onChange={(event) => void acceptFiles(event.target.files)}\n />\n </div>\n {error ? <p className=\"text-xs font-medium text-red-600\">{error}</p> : null}\n {items.length ? (\n <div className=\"grid grid-cols-2 gap-3 sm:grid-cols-3\">\n {items.map((item) => {\n const isDefault = defaultMediaId === item.id\n const slug = slugifyAttachmentFileName(item.fileName)\n const thumbnail =\n item.thumbnailUrl ||\n buildAttachmentImageUrl(item.id, { width: 360, height: 360, slug })\n return (\n <div key={item.id} className=\"flex flex-col rounded-md border bg-card\">\n <div className=\"relative aspect-square overflow-hidden rounded-t-md bg-muted\">\n {thumbnail ? (\n <img src={thumbnail} alt={item.fileName} className=\"h-full w-full object-cover\" />\n ) : (\n <div className=\"flex h-full items-center justify-center text-muted-foreground\">\n <ImageIcon className=\"h-8 w-8\" />\n </div>\n )}\n <div className=\"absolute right-1 top-1 flex gap-1\">\n <Button\n type=\"button\"\n variant={isDefault ? 'default' : 'secondary'}\n size=\"icon\"\n className={cn('h-7 w-7', isDefault ? '' : 'opacity-80')}\n onClick={() => onDefaultChange(item.id)}\n >\n <Star className=\"h-3.5 w-3.5\" />\n </Button>\n <Button type=\"button\" variant=\"secondary\" size=\"icon\" className=\"h-7 w-7\" onClick={() => void handleRemove(item.id)}>\n <Trash2 className=\"h-3.5 w-3.5\" />\n </Button>\n </div>\n </div>\n <div className=\"p-2\">\n <p className=\"line-clamp-1 text-sm font-medium\">{item.fileName}</p>\n <p className=\"text-xs text-muted-foreground\">{humanFileSize(item.fileSize)}</p>\n {isDefault ? (\n <p className=\"text-xs font-semibold text-primary\">\n {t('catalog.products.media.default', 'Default preview')}\n </p>\n ) : null}\n </div>\n </div>\n )\n })}\n </div>\n ) : (\n <p className=\"text-xs text-muted-foreground\">\n {t('catalog.products.media.empty', 'No media uploaded yet.')}\n </p>\n )}\n </div>\n )\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -5,9 +5,10 @@ import Link from "next/link";
|
|
|
5
5
|
import { DataTable } from "@open-mercato/ui/backend/DataTable";
|
|
6
6
|
import { Button } from "@open-mercato/ui/primitives/button";
|
|
7
7
|
import { RowActions } from "@open-mercato/ui/backend/RowActions";
|
|
8
|
-
import { apiCall, readApiResultOrThrow } from "@open-mercato/ui/backend/utils/apiCall";
|
|
8
|
+
import { apiCall, readApiResultOrThrow, withScopedApiRequestHeaders } from "@open-mercato/ui/backend/utils/apiCall";
|
|
9
9
|
import { flash } from "@open-mercato/ui/backend/FlashMessages";
|
|
10
10
|
import { deleteCrud, buildCrudExportUrl } from "@open-mercato/ui/backend/utils/crud";
|
|
11
|
+
import { buildOptimisticLockHeader } from "@open-mercato/ui/backend/utils/optimisticLock";
|
|
11
12
|
import { useCustomFieldDefs } from "@open-mercato/ui/backend/utils/customFieldDefs";
|
|
12
13
|
import { applyCustomFieldVisibility } from "@open-mercato/ui/backend/utils/customFieldColumns";
|
|
13
14
|
import { BooleanIcon } from "@open-mercato/ui/backend/ValueIcons";
|
|
@@ -463,9 +464,10 @@ function ProductsDataTable({
|
|
|
463
464
|
});
|
|
464
465
|
if (!confirmed) return;
|
|
465
466
|
try {
|
|
466
|
-
|
|
467
|
+
const headers = buildOptimisticLockHeader(typeof row.updated_at === "string" ? row.updated_at : null);
|
|
468
|
+
await withScopedApiRequestHeaders(headers, () => deleteCrud("catalog/products", row.id, {
|
|
467
469
|
errorMessage: t("catalog.products.list.error.delete", "Failed to delete product")
|
|
468
|
-
});
|
|
470
|
+
}));
|
|
469
471
|
flash(t("catalog.products.flash.deleted", "Product deleted"), "success");
|
|
470
472
|
setReloadToken((token) => token + 1);
|
|
471
473
|
} catch (error) {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../src/modules/catalog/components/products/ProductsDataTable.tsx"],
|
|
4
|
-
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport Link from 'next/link'\nimport type { ColumnDef, SortingState } from '@tanstack/react-table'\nimport { DataTable, type DataTableExportFormat } from '@open-mercato/ui/backend/DataTable'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { RowActions } from '@open-mercato/ui/backend/RowActions'\nimport { apiCall, readApiResultOrThrow } from '@open-mercato/ui/backend/utils/apiCall'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { deleteCrud, buildCrudExportUrl } from '@open-mercato/ui/backend/utils/crud'\nimport { useCustomFieldDefs } from '@open-mercato/ui/backend/utils/customFieldDefs'\nimport { applyCustomFieldVisibility } from '@open-mercato/ui/backend/utils/customFieldColumns'\nimport type { FilterDef, FilterValues } from '@open-mercato/ui/backend/FilterBar'\nimport type { FilterOption } from '@open-mercato/ui/backend/FilterOverlay'\nimport { BooleanIcon } from '@open-mercato/ui/backend/ValueIcons'\nimport { useOrganizationScopeVersion } from '@open-mercato/shared/lib/frontend/useOrganizationScope'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { useConfirmDialog } from '@open-mercato/ui/backend/confirm-dialog'\nimport { useAppEvent } from '@open-mercato/ui/backend/injection/useAppEvent'\nimport { E } from '#generated/entities.ids.generated'\nimport { ProductImageCell } from './ProductImageCell'\n\ntype PricingScope = {\n variant_id?: string | null\n offer_id?: string | null\n channel_id?: string | null\n user_id?: string | null\n user_group_id?: string | null\n customer_id?: string | null\n customer_group_id?: string | null\n}\n\ntype PricingInfo = {\n kind?: string | null\n price_kind_id?: string | null\n price_kind_code?: string | null\n currency_code?: string | null\n unit_price_net?: string | null\n unit_price_gross?: string | null\n min_quantity?: number | null\n max_quantity?: number | null\n tax_rate?: string | null\n scope?: PricingScope | null\n} | null\n\ntype OfferInfo = {\n id: string\n channelId: string\n channelName?: string | null\n channelCode?: string | null\n title: string\n description?: string | null\n isActive: boolean\n}\n\nexport type ProductRow = {\n id: string\n title: string\n subtitle?: string | null\n description?: string | null\n sku?: string | null\n handle?: string | null\n product_type?: string | null\n status_entry_id?: string | null\n primary_currency_code?: string | null\n default_unit?: string | null\n default_media_id?: string | null\n default_media_url?: string | null\n is_configurable?: boolean\n is_active?: boolean\n metadata?: Record<string, unknown> | null\n custom_fieldset_code?: string | null\n created_at?: string\n updated_at?: string\n offers?: OfferInfo[]\n pricing?: PricingInfo\n} & Record<string, unknown>\n\ntype ProductsResponse = {\n items?: ProductRow[]\n total?: number\n totalPages?: number\n}\n\nconst PAGE_SIZE = 25\nconst ENTITY_ID = E.catalog.catalog_product\n\nfunction formatDate(value?: string): string {\n if (!value) return '\u2014'\n const date = new Date(value)\n if (Number.isNaN(date.getTime())) return '\u2014'\n return date.toLocaleDateString()\n}\n\nfunction renderOffers(offers: OfferInfo[] | undefined): React.ReactNode {\n if (!offers || offers.length === 0) return <span className=\"text-xs text-muted-foreground\">\u2014</span>\n const visible = offers.slice(0, 3)\n return (\n <div className=\"flex flex-wrap gap-1\">\n {visible.map((offer) => {\n const label =\n typeof offer.channelName === 'string' && offer.channelName.trim().length\n ? offer.channelName.trim()\n : typeof offer.title === 'string' && offer.title.trim().length\n ? offer.title.trim()\n : offer.channelId\n const badgeTitle =\n typeof offer.channelCode === 'string' && offer.channelCode.trim().length ? offer.channelCode : undefined\n return (\n <span\n key={offer.id}\n className={`inline-flex items-center rounded-full border px-2 py-0.5 text-xs ${\n offer.isActive ? 'bg-secondary/80 text-secondary-foreground' : 'bg-muted text-muted-foreground'\n }`}\n title={badgeTitle}\n >\n {label}\n </span>\n )\n })}\n {offers.length > visible.length ? (\n <span className=\"text-xs text-muted-foreground\">+{offers.length - visible.length}</span>\n ) : null}\n </div>\n )\n}\n\nfunction renderPrice(pricing: PricingInfo | undefined, currency?: string | null, fallback = '\u2014'): React.ReactNode {\n if (!pricing) return <span className=\"text-xs text-muted-foreground\">{fallback}</span>\n const unit = pricing.unit_price_net ?? pricing.unit_price_gross\n if (unit == null) return <span className=\"text-xs text-muted-foreground\">{fallback}</span>\n const formatted = `${currency ?? pricing.currency_code ?? ''} ${unit}`\n const kind = pricing.kind ?? 'list'\n return (\n <div className=\"flex flex-col\">\n <span className=\"font-medium\">{formatted.trim()}</span>\n <span className=\"text-xs text-muted-foreground\">{kind}</span>\n </div>\n )\n}\n\nexport type ProductsDataTableSnapshot = {\n search: string\n filterValues: FilterValues\n total: number\n}\n\nexport type ProductsDataTableProps = {\n /**\n * Extra actions rendered alongside the built-in Create button in the\n * DataTable header. Used by the Step 4.9 AI merchandising sheet\n * trigger without coupling DataTable to the AI module.\n */\n extraActions?: React.ReactNode\n /**\n * Optional callback invoked whenever the table's search / filter /\n * total-matching snapshot changes. Used by the Step 4.9 AI merchandising\n * sheet to form a selection-aware pageContext per spec \u00A710.1.\n */\n onSnapshotChange?: (snapshot: ProductsDataTableSnapshot) => void\n}\n\nexport default function ProductsDataTable({\n extraActions,\n onSnapshotChange,\n}: ProductsDataTableProps = {}) {\n const t = useT()\n const { confirm, ConfirmDialogElement } = useConfirmDialog()\n const scopeVersion = useOrganizationScopeVersion()\n const [rows, setRows] = React.useState<ProductRow[]>([])\n const [page, setPage] = React.useState(1)\n const [total, setTotal] = React.useState(0)\n const [totalPages, setTotalPages] = React.useState(1)\n const [cacheStatus, setCacheStatus] = React.useState<'hit' | 'miss' | null>(null)\n const [sorting, setSorting] = React.useState<SortingState>([{ id: 'title', desc: false }])\n const [search, setSearch] = React.useState('')\n const [filterValues, setFilterValues] = React.useState<FilterValues>({})\n const [isLoading, setIsLoading] = React.useState(false)\n const [reloadToken, setReloadToken] = React.useState(0)\n // Step 5.18 (spec \u00A710 line 836, D18 demo): refresh the list when a\n // catalog.product.* event arrives via the DOM event bridge. Confirmed\n // AI bulk mutations (one `ai.action.confirmed` + one\n // `catalog.product.updated` per record) and direct API writes both\n // surface here so the table reflects the new state without a manual\n // reload.\n useAppEvent('catalog.product.*', () => {\n setReloadToken((token) => token + 1)\n })\n const [customFieldsetFilter, setCustomFieldsetFilter] = React.useState<string | null>(null)\n const { data: customFieldDefs = [] } = useCustomFieldDefs(ENTITY_ID, {\n keyExtras: [scopeVersion, reloadToken],\n })\n const [channelOptionsCache, setChannelOptionsCache] = React.useState<Record<string, FilterOption>>({})\n const [categoryOptionsCache, setCategoryOptionsCache] = React.useState<Record<string, FilterOption>>({})\n const [tagOptionsCache, setTagOptionsCache] = React.useState<Record<string, FilterOption>>({})\n\n const registerOptions = React.useCallback(\n (\n setter: React.Dispatch<React.SetStateAction<Record<string, FilterOption>>>,\n options: FilterOption[]\n ) => {\n setter((prev) => {\n const next = { ...prev }\n options.forEach((opt) => {\n if (opt.value) next[opt.value] = opt\n })\n return next\n })\n },\n []\n )\n\n const registerChannelOptions = React.useCallback(\n (options: FilterOption[]) => registerOptions(setChannelOptionsCache, options),\n [registerOptions]\n )\n const registerCategoryOptions = React.useCallback(\n (options: FilterOption[]) => registerOptions(setCategoryOptionsCache, options),\n [registerOptions]\n )\n const registerTagOptions = React.useCallback(\n (options: FilterOption[]) => registerOptions(setTagOptionsCache, options),\n [registerOptions]\n )\n\n const channelOptions = React.useMemo(() => Object.values(channelOptionsCache), [channelOptionsCache])\n const categoryOptions = React.useMemo(() => Object.values(categoryOptionsCache), [categoryOptionsCache])\n const tagOptions = React.useMemo(() => Object.values(tagOptionsCache), [tagOptionsCache])\n\n const loadChannelOptions = React.useCallback(\n async (term?: string): Promise<FilterOption[]> => {\n try {\n const params = new URLSearchParams({ pageSize: '100', isActive: 'true' })\n if (term && term.trim().length) params.set('search', term.trim())\n const payload = await readApiResultOrThrow<{ items?: Array<{ id?: string; name?: string; code?: string }> }>(\n `/api/sales/channels?${params.toString()}`,\n undefined,\n { errorMessage: t('catalog.products.filters.channelsLoadError', 'Failed to load channels') },\n )\n const items = Array.isArray(payload?.items) ? payload.items : []\n const options = items\n .map((entry) => {\n const value = typeof entry.id === 'string' ? entry.id : null\n if (!value) return null\n const label =\n typeof entry.name === 'string'\n ? entry.name\n : typeof entry.code === 'string'\n ? entry.code\n : value\n return { value, label, description: typeof entry.code === 'string' ? entry.code : undefined }\n })\n .filter((option) => !!option) as FilterOption[]\n registerChannelOptions(options)\n return options\n } catch {\n return []\n }\n },\n [registerChannelOptions, t],\n )\n\n const loadCategoryOptions = React.useCallback(\n async (term?: string): Promise<FilterOption[]> => {\n try {\n const params = new URLSearchParams({ pageSize: '200', view: 'manage' })\n if (term && term.trim().length) params.set('search', term.trim())\n const payload = await readApiResultOrThrow<{ items?: Array<{ id?: string; name?: string; parentName?: string | null }> }>(\n `/api/catalog/categories?${params.toString()}`,\n undefined,\n { errorMessage: t('catalog.products.filters.categoriesLoadError', 'Failed to load categories') },\n )\n const items = Array.isArray(payload?.items) ? payload.items : []\n const options = items\n .map((entry) => {\n const value = typeof entry.id === 'string' ? entry.id : null\n if (!value) return null\n const label = typeof entry.name === 'string' && entry.name.trim().length ? entry.name : value\n const description =\n typeof entry.parentName === 'string' && entry.parentName.trim().length ? entry.parentName : null\n return { value, label, description }\n })\n .filter((option) => !!option) as FilterOption[]\n registerCategoryOptions(options)\n return options\n } catch {\n return []\n }\n },\n [registerCategoryOptions, t],\n )\n\n const loadTagOptions = React.useCallback(\n async (term?: string): Promise<FilterOption[]> => {\n try {\n const params = new URLSearchParams({ pageSize: '100' })\n if (term && term.trim().length) params.set('search', term.trim())\n const payload = await readApiResultOrThrow<{ items?: Array<{ id?: string; label?: string }> }>(\n `/api/catalog/tags?${params.toString()}`,\n undefined,\n { errorMessage: t('catalog.products.filters.tagsLoadError', 'Failed to load tags') },\n )\n const items = Array.isArray(payload?.items) ? payload.items : []\n const options = items\n .map((entry) => {\n const value = typeof entry.id === 'string' ? entry.id : null\n if (!value) return null\n const label = typeof entry.label === 'string' && entry.label.trim().length ? entry.label : value\n return { value, label }\n })\n .filter((option) => !!option) as FilterOption[]\n registerTagOptions(options)\n return options\n } catch {\n return []\n }\n },\n [registerTagOptions, t],\n )\n\n const productTypeOptions = React.useMemo<FilterOption[]>(() => [\n { value: 'simple', label: t('catalog.products.types.simple', 'Simple') },\n { value: 'configurable', label: t('catalog.products.types.configurable', 'Configurable') },\n { value: 'virtual', label: t('catalog.products.types.virtual', 'Virtual') },\n { value: 'downloadable', label: t('catalog.products.types.downloadable', 'Downloadable') },\n {\n value: 'bundle',\n label: `${t('catalog.products.types.bundle', 'Bundle')} (${t('common.comingSoon', 'Coming soon')})`,\n },\n {\n value: 'grouped',\n label: `${t('catalog.products.types.grouped', 'Grouped')} (${t('common.comingSoon', 'Coming soon')})`,\n },\n ], [t])\n\n const productTypeLabelMap = React.useMemo(() => {\n const map = new Map<string, string>()\n productTypeOptions.forEach((opt) => map.set(opt.value, opt.label))\n return map\n }, [productTypeOptions])\n\n const filters = React.useMemo<FilterDef[]>(() => [\n { id: 'status', label: t('catalog.products.filters.status'), type: 'text' },\n { id: 'isActive', label: t('catalog.products.filters.active'), type: 'checkbox' },\n { id: 'configurable', label: t('catalog.products.filters.configurable'), type: 'checkbox' },\n { id: 'productType', label: t('catalog.products.filters.productType', 'Type'), type: 'select', options: productTypeOptions },\n {\n id: 'channelIds',\n label: t('catalog.products.filters.channels'),\n type: 'tags',\n loadOptions: loadChannelOptions,\n options: channelOptions,\n formatValue: (val) => channelOptionsCache[val]?.label ?? val,\n formatDescription: (val) => channelOptionsCache[val]?.description ?? null,\n },\n {\n id: 'categoryIds',\n label: t('catalog.products.filters.categories', 'Categories'),\n type: 'tags',\n loadOptions: loadCategoryOptions,\n options: categoryOptions,\n formatValue: (val) => categoryOptionsCache[val]?.label ?? val,\n formatDescription: (val) => categoryOptionsCache[val]?.description ?? null,\n },\n {\n id: 'tagIds',\n label: t('catalog.products.filters.tags', 'Tags'),\n type: 'tags',\n loadOptions: loadTagOptions,\n options: tagOptions,\n formatValue: (val) => tagOptionsCache[val]?.label ?? val,\n },\n ], [\n categoryOptions,\n categoryOptionsCache,\n channelOptions,\n channelOptionsCache,\n loadCategoryOptions,\n loadChannelOptions,\n loadTagOptions,\n productTypeOptions,\n tagOptions,\n tagOptionsCache,\n t,\n ])\n\n const columns = React.useMemo<ColumnDef<ProductRow>[]>(() => {\n const base: ColumnDef<ProductRow>[] = [\n {\n id: 'media',\n header: '',\n size: 80,\n cell: ({ row }) => (\n <ProductImageCell\n mediaId={row.original.default_media_id}\n mediaUrl={row.original.default_media_url}\n title={row.original.title}\n cropType=\"contain\"\n />\n ),\n meta: { sticky: true },\n },\n {\n accessorKey: 'title',\n header: t('catalog.products.table.title', 'Title'),\n cell: ({ row }) => (\n <div className=\"flex flex-col\">\n <span className=\"font-medium\">{row.original.title || '\u2014'}</span>\n {row.original.subtitle ? (\n <span className=\"text-xs text-muted-foreground\">{row.original.subtitle}</span>\n ) : null}\n {row.original.handle ? (\n <span className=\"text-xs text-muted-foreground\">/{row.original.handle}</span>\n ) : null}\n {row.original.description ? (\n <span className=\"text-xs text-muted-foreground\">{row.original.description}</span>\n ) : null}\n </div>\n ),\n meta: { sticky: true },\n },\n {\n accessorKey: 'sku',\n header: t('catalog.products.table.sku', 'SKU'),\n cell: ({ getValue }) => {\n const value = getValue()\n return value ? <span className=\"font-mono text-xs\">{String(value)}</span> : <span className=\"text-xs text-muted-foreground\">\u2014</span>\n },\n },\n {\n accessorKey: 'product_type',\n header: t('catalog.products.table.type'),\n cell: ({ row }) => {\n const type = typeof row.original.product_type === 'string' ? row.original.product_type : 'simple'\n const label = productTypeLabelMap.get(type) ?? type\n return <span className=\"text-xs text-muted-foreground\">{label}</span>\n },\n },\n {\n accessorKey: 'is_configurable',\n header: t('catalog.products.table.configurable'),\n cell: ({ row }) => <BooleanIcon value={!!row.original.is_configurable} />,\n },\n {\n accessorKey: 'is_active',\n header: t('catalog.products.table.active'),\n cell: ({ row }) => <BooleanIcon value={!!row.original.is_active} />,\n },\n {\n accessorKey: 'pricing',\n header: t('catalog.products.table.price'),\n cell: ({ row }) => renderPrice(row.original.pricing, row.original.primary_currency_code),\n },\n {\n accessorKey: 'offers',\n header: t('catalog.products.table.channels'),\n cell: ({ row }) => renderOffers(row.original.offers),\n },\n {\n accessorKey: 'updated_at',\n header: t('catalog.products.table.updatedAt'),\n cell: ({ row }) => <span className=\"text-xs text-muted-foreground\">{formatDate(row.original.updated_at)}</span>,\n },\n ]\n return applyCustomFieldVisibility(base, customFieldDefs)\n }, [customFieldDefs, productTypeLabelMap, t])\n\n const handleSearchChange = React.useCallback((value: string) => {\n setSearch(value)\n setPage(1)\n }, [])\n\n const handleFiltersApply = React.useCallback((values: FilterValues) => {\n setFilterValues(values)\n setPage(1)\n }, [])\n\n const handleFiltersClear = React.useCallback(() => {\n setFilterValues({})\n setPage(1)\n }, [])\n\n const handleCustomFieldsetFilterChange = React.useCallback(\n (value: string | null) => {\n if (value === customFieldsetFilter) return\n setCustomFieldsetFilter(value)\n setFilterValues((prev) => {\n const entries = Object.entries(prev)\n if (!entries.some(([key]) => key.startsWith('cf_'))) return prev\n const next: FilterValues = {}\n entries.forEach(([key, val]) => {\n if (!key.startsWith('cf_')) next[key] = val\n })\n return next\n })\n setPage(1)\n },\n [customFieldsetFilter],\n )\n\n const handleRefresh = React.useCallback(() => {\n setReloadToken((token) => token + 1)\n }, [])\n\n const queryParams = React.useMemo(() => {\n const params = new URLSearchParams()\n params.set('page', String(page))\n params.set('pageSize', String(PAGE_SIZE))\n if (search.trim()) params.set('search', search.trim())\n const sort = sorting[0]\n if (sort?.id) {\n params.set('sortField', sort.id)\n params.set('sortDir', sort.desc ? 'desc' : 'asc')\n }\n const status = filterValues.status\n if (typeof status === 'string' && status.trim()) {\n params.set('status', status.trim())\n }\n if (filterValues.isActive === true) params.set('isActive', 'true')\n if (filterValues.isActive === false) params.set('isActive', 'false')\n if (filterValues.configurable === true) params.set('configurable', 'true')\n if (filterValues.configurable === false) params.set('configurable', 'false')\n if (typeof filterValues.productType === 'string' && filterValues.productType.trim()) {\n params.set('productType', filterValues.productType.trim())\n }\n if (Array.isArray(filterValues.channelIds) && filterValues.channelIds.length) {\n const values = filterValues.channelIds\n .map((value) => (typeof value === 'string' ? value : null))\n .filter((value): value is string => !!value)\n if (values.length) params.set('channelIds', values.join(','))\n }\n if (Array.isArray(filterValues.categoryIds) && filterValues.categoryIds.length) {\n const values = filterValues.categoryIds\n .map((value) => (typeof value === 'string' ? value : null))\n .filter((value): value is string => !!value)\n if (values.length) params.set('categoryIds', values.join(','))\n }\n if (Array.isArray(filterValues.tagIds) && filterValues.tagIds.length) {\n const values = filterValues.tagIds\n .map((value) => (typeof value === 'string' ? value : null))\n .filter((value): value is string => !!value)\n if (values.length) params.set('tagIds', values.join(','))\n }\n Object.entries(filterValues).forEach(([key, value]) => {\n if (!key.startsWith('cf_') || value == null) return\n if (Array.isArray(value)) {\n const entries = value\n .map((entry) => (typeof entry === 'string' ? entry.trim() : String(entry || '').trim()))\n .filter((entry) => entry.length > 0)\n if (entries.length) params.set(key, entries.join(','))\n } else if (typeof value === 'object' && value !== null && ('from' in (value as Record<string, unknown>) || 'to' in (value as Record<string, unknown>))) {\n const range = value as { from?: string; to?: string }\n if (typeof range.from === 'string' && range.from.trim().length) {\n params.set(`${key}:from`, range.from.trim())\n }\n if (typeof range.to === 'string' && range.to.trim().length) {\n params.set(`${key}:to`, range.to.trim())\n }\n } else if (typeof value === 'string' && value.trim()) {\n params.set(key, value.trim())\n }\n })\n if (typeof customFieldsetFilter === 'string' && customFieldsetFilter.trim().length > 0) {\n params.set('customFieldset', customFieldsetFilter.trim())\n }\n return params.toString()\n }, [customFieldsetFilter, filterValues, page, search, sorting])\n\n React.useEffect(() => {\n let cancelled = false\n async function load() {\n setIsLoading(true)\n setCacheStatus(null)\n try {\n const fallback: ProductsResponse = { items: [], total: 0, totalPages: 1 }\n const call = await apiCall<ProductsResponse>(\n `/api/catalog/products?${queryParams}`,\n undefined,\n { fallback },\n )\n if (!call.ok) {\n const message = t('catalog.products.list.error.load', 'Failed to load products')\n flash(message, 'error')\n if (!cancelled) setCacheStatus(null)\n return\n }\n const payload = call.result ?? fallback\n if (cancelled) return\n setCacheStatus(call.cacheStatus ?? null)\n const items = Array.isArray(payload.items) ? payload.items : []\n const normalized = items.filter((item): item is ProductRow => typeof item?.id === 'string')\n setRows(normalized)\n setTotal(typeof payload.total === 'number' ? payload.total : normalized.length)\n setTotalPages(typeof payload.totalPages === 'number' ? payload.totalPages : 1)\n } catch (error) {\n if (!cancelled) {\n setCacheStatus(null)\n const message =\n error instanceof Error\n ? error.message\n : t('catalog.products.list.error.load', 'Failed to load products')\n flash(message, 'error')\n }\n } finally {\n if (!cancelled) setIsLoading(false)\n }\n }\n load()\n return () => {\n cancelled = true\n }\n }, [queryParams, reloadToken, scopeVersion, t])\n\n const handleDelete = React.useCallback(async (row: ProductRow) => {\n const confirmed = await confirm({\n title: t('catalog.products.list.deleteConfirm', 'Delete this product?'),\n variant: 'destructive',\n })\n if (!confirmed) return\n try {\n await deleteCrud('catalog/products', row.id, {\n errorMessage: t('catalog.products.list.error.delete', 'Failed to delete product'),\n })\n flash(t('catalog.products.flash.deleted', 'Product deleted'), 'success')\n setReloadToken((token) => token + 1)\n } catch (error) {\n const message =\n error instanceof Error\n ? error.message\n : t('catalog.products.list.error.delete', 'Failed to delete product')\n flash(message, 'error')\n }\n }, [confirm, t])\n\n React.useEffect(() => {\n if (!onSnapshotChange) return\n onSnapshotChange({ search, filterValues, total })\n }, [onSnapshotChange, search, filterValues, total])\n\n const currentParams = React.useMemo(() => Object.fromEntries(new URLSearchParams(queryParams)), [queryParams])\n\n const exportConfig = React.useMemo(() => ({\n view: {\n getUrl: (format: DataTableExportFormat) =>\n buildCrudExportUrl('catalog/products', { ...currentParams, exportScope: 'view' }, format),\n },\n full: {\n getUrl: (format: DataTableExportFormat) =>\n buildCrudExportUrl('catalog/products', { ...currentParams, exportScope: 'full', all: 'true' }, format),\n },\n }), [currentParams])\n\n return (\n <>\n <DataTable<ProductRow>\n title={t('catalog.products.page.title', 'Products & services')}\n entityId={ENTITY_ID}\n customFieldFilterKeyExtras={[scopeVersion, reloadToken]}\n refreshButton={{\n label: t('catalog.products.actions.refresh', 'Refresh'),\n onRefresh: handleRefresh,\n isRefreshing: isLoading,\n }}\n actions={(\n <div className=\"flex items-center gap-2\">\n {extraActions}\n <Button asChild>\n <Link href=\"/backend/catalog/products/create\">\n {t('catalog.products.actions.create', 'Create')}\n </Link>\n </Button>\n </div>\n )}\n columns={columns}\n data={rows}\n searchValue={search}\n onSearchChange={handleSearchChange}\n filters={filters}\n filterValues={filterValues}\n onFiltersApply={handleFiltersApply}\n onFiltersClear={handleFiltersClear}\n onCustomFieldFilterFieldsetChange={handleCustomFieldsetFilterChange}\n sorting={sorting}\n onSortingChange={setSorting}\n injectionSpotId=\"data-table:catalog.products\"\n injectionContext={{\n search,\n filters: filterValues,\n customFieldset: customFieldsetFilter,\n page,\n sorting,\n scopeVersion,\n // Step 5.15: surface `total` so the merchandising AI widget\n // (rendered in `data-table:catalog.products:header`) can build\n // a selection-aware pageContext per spec \u00A710.1 without taking a\n // dependency on the host page.\n total,\n totalMatching: total,\n }}\n pagination={{\n page,\n pageSize: PAGE_SIZE,\n total,\n totalPages,\n onPageChange: setPage,\n cacheStatus,\n }}\n exporter={exportConfig}\n isLoading={isLoading}\n perspective={{ tableId: 'catalog.products.list' }}\n stickyActionsColumn\n rowActions={(row) => (\n <RowActions\n items={[\n {\n id: 'edit',\n label: t('catalog.products.table.actions.edit', 'Edit'),\n href: `/backend/catalog/products/${row.id}`,\n },\n {\n id: 'delete',\n label: t('catalog.products.table.actions.delete', 'Delete'),\n destructive: true,\n onSelect: () => {\n void handleDelete(row)\n },\n },\n ]}\n />\n )}\n />\n {ConfirmDialogElement}\n </>\n )\n}\n"],
|
|
5
|
-
"mappings": "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",
|
|
4
|
+
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport Link from 'next/link'\nimport type { ColumnDef, SortingState } from '@tanstack/react-table'\nimport { DataTable, type DataTableExportFormat } from '@open-mercato/ui/backend/DataTable'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { RowActions } from '@open-mercato/ui/backend/RowActions'\nimport { apiCall, readApiResultOrThrow, withScopedApiRequestHeaders } from '@open-mercato/ui/backend/utils/apiCall'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { deleteCrud, buildCrudExportUrl } from '@open-mercato/ui/backend/utils/crud'\nimport { buildOptimisticLockHeader } from '@open-mercato/ui/backend/utils/optimisticLock'\nimport { useCustomFieldDefs } from '@open-mercato/ui/backend/utils/customFieldDefs'\nimport { applyCustomFieldVisibility } from '@open-mercato/ui/backend/utils/customFieldColumns'\nimport type { FilterDef, FilterValues } from '@open-mercato/ui/backend/FilterBar'\nimport type { FilterOption } from '@open-mercato/ui/backend/FilterOverlay'\nimport { BooleanIcon } from '@open-mercato/ui/backend/ValueIcons'\nimport { useOrganizationScopeVersion } from '@open-mercato/shared/lib/frontend/useOrganizationScope'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { useConfirmDialog } from '@open-mercato/ui/backend/confirm-dialog'\nimport { useAppEvent } from '@open-mercato/ui/backend/injection/useAppEvent'\nimport { E } from '#generated/entities.ids.generated'\nimport { ProductImageCell } from './ProductImageCell'\n\ntype PricingScope = {\n variant_id?: string | null\n offer_id?: string | null\n channel_id?: string | null\n user_id?: string | null\n user_group_id?: string | null\n customer_id?: string | null\n customer_group_id?: string | null\n}\n\ntype PricingInfo = {\n kind?: string | null\n price_kind_id?: string | null\n price_kind_code?: string | null\n currency_code?: string | null\n unit_price_net?: string | null\n unit_price_gross?: string | null\n min_quantity?: number | null\n max_quantity?: number | null\n tax_rate?: string | null\n scope?: PricingScope | null\n} | null\n\ntype OfferInfo = {\n id: string\n channelId: string\n channelName?: string | null\n channelCode?: string | null\n title: string\n description?: string | null\n isActive: boolean\n}\n\nexport type ProductRow = {\n id: string\n title: string\n subtitle?: string | null\n description?: string | null\n sku?: string | null\n handle?: string | null\n product_type?: string | null\n status_entry_id?: string | null\n primary_currency_code?: string | null\n default_unit?: string | null\n default_media_id?: string | null\n default_media_url?: string | null\n is_configurable?: boolean\n is_active?: boolean\n metadata?: Record<string, unknown> | null\n custom_fieldset_code?: string | null\n created_at?: string\n updated_at?: string\n offers?: OfferInfo[]\n pricing?: PricingInfo\n} & Record<string, unknown>\n\ntype ProductsResponse = {\n items?: ProductRow[]\n total?: number\n totalPages?: number\n}\n\nconst PAGE_SIZE = 25\nconst ENTITY_ID = E.catalog.catalog_product\n\nfunction formatDate(value?: string): string {\n if (!value) return '\u2014'\n const date = new Date(value)\n if (Number.isNaN(date.getTime())) return '\u2014'\n return date.toLocaleDateString()\n}\n\nfunction renderOffers(offers: OfferInfo[] | undefined): React.ReactNode {\n if (!offers || offers.length === 0) return <span className=\"text-xs text-muted-foreground\">\u2014</span>\n const visible = offers.slice(0, 3)\n return (\n <div className=\"flex flex-wrap gap-1\">\n {visible.map((offer) => {\n const label =\n typeof offer.channelName === 'string' && offer.channelName.trim().length\n ? offer.channelName.trim()\n : typeof offer.title === 'string' && offer.title.trim().length\n ? offer.title.trim()\n : offer.channelId\n const badgeTitle =\n typeof offer.channelCode === 'string' && offer.channelCode.trim().length ? offer.channelCode : undefined\n return (\n <span\n key={offer.id}\n className={`inline-flex items-center rounded-full border px-2 py-0.5 text-xs ${\n offer.isActive ? 'bg-secondary/80 text-secondary-foreground' : 'bg-muted text-muted-foreground'\n }`}\n title={badgeTitle}\n >\n {label}\n </span>\n )\n })}\n {offers.length > visible.length ? (\n <span className=\"text-xs text-muted-foreground\">+{offers.length - visible.length}</span>\n ) : null}\n </div>\n )\n}\n\nfunction renderPrice(pricing: PricingInfo | undefined, currency?: string | null, fallback = '\u2014'): React.ReactNode {\n if (!pricing) return <span className=\"text-xs text-muted-foreground\">{fallback}</span>\n const unit = pricing.unit_price_net ?? pricing.unit_price_gross\n if (unit == null) return <span className=\"text-xs text-muted-foreground\">{fallback}</span>\n const formatted = `${currency ?? pricing.currency_code ?? ''} ${unit}`\n const kind = pricing.kind ?? 'list'\n return (\n <div className=\"flex flex-col\">\n <span className=\"font-medium\">{formatted.trim()}</span>\n <span className=\"text-xs text-muted-foreground\">{kind}</span>\n </div>\n )\n}\n\nexport type ProductsDataTableSnapshot = {\n search: string\n filterValues: FilterValues\n total: number\n}\n\nexport type ProductsDataTableProps = {\n /**\n * Extra actions rendered alongside the built-in Create button in the\n * DataTable header. Used by the Step 4.9 AI merchandising sheet\n * trigger without coupling DataTable to the AI module.\n */\n extraActions?: React.ReactNode\n /**\n * Optional callback invoked whenever the table's search / filter /\n * total-matching snapshot changes. Used by the Step 4.9 AI merchandising\n * sheet to form a selection-aware pageContext per spec \u00A710.1.\n */\n onSnapshotChange?: (snapshot: ProductsDataTableSnapshot) => void\n}\n\nexport default function ProductsDataTable({\n extraActions,\n onSnapshotChange,\n}: ProductsDataTableProps = {}) {\n const t = useT()\n const { confirm, ConfirmDialogElement } = useConfirmDialog()\n const scopeVersion = useOrganizationScopeVersion()\n const [rows, setRows] = React.useState<ProductRow[]>([])\n const [page, setPage] = React.useState(1)\n const [total, setTotal] = React.useState(0)\n const [totalPages, setTotalPages] = React.useState(1)\n const [cacheStatus, setCacheStatus] = React.useState<'hit' | 'miss' | null>(null)\n const [sorting, setSorting] = React.useState<SortingState>([{ id: 'title', desc: false }])\n const [search, setSearch] = React.useState('')\n const [filterValues, setFilterValues] = React.useState<FilterValues>({})\n const [isLoading, setIsLoading] = React.useState(false)\n const [reloadToken, setReloadToken] = React.useState(0)\n // Step 5.18 (spec \u00A710 line 836, D18 demo): refresh the list when a\n // catalog.product.* event arrives via the DOM event bridge. Confirmed\n // AI bulk mutations (one `ai.action.confirmed` + one\n // `catalog.product.updated` per record) and direct API writes both\n // surface here so the table reflects the new state without a manual\n // reload.\n useAppEvent('catalog.product.*', () => {\n setReloadToken((token) => token + 1)\n })\n const [customFieldsetFilter, setCustomFieldsetFilter] = React.useState<string | null>(null)\n const { data: customFieldDefs = [] } = useCustomFieldDefs(ENTITY_ID, {\n keyExtras: [scopeVersion, reloadToken],\n })\n const [channelOptionsCache, setChannelOptionsCache] = React.useState<Record<string, FilterOption>>({})\n const [categoryOptionsCache, setCategoryOptionsCache] = React.useState<Record<string, FilterOption>>({})\n const [tagOptionsCache, setTagOptionsCache] = React.useState<Record<string, FilterOption>>({})\n\n const registerOptions = React.useCallback(\n (\n setter: React.Dispatch<React.SetStateAction<Record<string, FilterOption>>>,\n options: FilterOption[]\n ) => {\n setter((prev) => {\n const next = { ...prev }\n options.forEach((opt) => {\n if (opt.value) next[opt.value] = opt\n })\n return next\n })\n },\n []\n )\n\n const registerChannelOptions = React.useCallback(\n (options: FilterOption[]) => registerOptions(setChannelOptionsCache, options),\n [registerOptions]\n )\n const registerCategoryOptions = React.useCallback(\n (options: FilterOption[]) => registerOptions(setCategoryOptionsCache, options),\n [registerOptions]\n )\n const registerTagOptions = React.useCallback(\n (options: FilterOption[]) => registerOptions(setTagOptionsCache, options),\n [registerOptions]\n )\n\n const channelOptions = React.useMemo(() => Object.values(channelOptionsCache), [channelOptionsCache])\n const categoryOptions = React.useMemo(() => Object.values(categoryOptionsCache), [categoryOptionsCache])\n const tagOptions = React.useMemo(() => Object.values(tagOptionsCache), [tagOptionsCache])\n\n const loadChannelOptions = React.useCallback(\n async (term?: string): Promise<FilterOption[]> => {\n try {\n const params = new URLSearchParams({ pageSize: '100', isActive: 'true' })\n if (term && term.trim().length) params.set('search', term.trim())\n const payload = await readApiResultOrThrow<{ items?: Array<{ id?: string; name?: string; code?: string }> }>(\n `/api/sales/channels?${params.toString()}`,\n undefined,\n { errorMessage: t('catalog.products.filters.channelsLoadError', 'Failed to load channels') },\n )\n const items = Array.isArray(payload?.items) ? payload.items : []\n const options = items\n .map((entry) => {\n const value = typeof entry.id === 'string' ? entry.id : null\n if (!value) return null\n const label =\n typeof entry.name === 'string'\n ? entry.name\n : typeof entry.code === 'string'\n ? entry.code\n : value\n return { value, label, description: typeof entry.code === 'string' ? entry.code : undefined }\n })\n .filter((option) => !!option) as FilterOption[]\n registerChannelOptions(options)\n return options\n } catch {\n return []\n }\n },\n [registerChannelOptions, t],\n )\n\n const loadCategoryOptions = React.useCallback(\n async (term?: string): Promise<FilterOption[]> => {\n try {\n const params = new URLSearchParams({ pageSize: '200', view: 'manage' })\n if (term && term.trim().length) params.set('search', term.trim())\n const payload = await readApiResultOrThrow<{ items?: Array<{ id?: string; name?: string; parentName?: string | null }> }>(\n `/api/catalog/categories?${params.toString()}`,\n undefined,\n { errorMessage: t('catalog.products.filters.categoriesLoadError', 'Failed to load categories') },\n )\n const items = Array.isArray(payload?.items) ? payload.items : []\n const options = items\n .map((entry) => {\n const value = typeof entry.id === 'string' ? entry.id : null\n if (!value) return null\n const label = typeof entry.name === 'string' && entry.name.trim().length ? entry.name : value\n const description =\n typeof entry.parentName === 'string' && entry.parentName.trim().length ? entry.parentName : null\n return { value, label, description }\n })\n .filter((option) => !!option) as FilterOption[]\n registerCategoryOptions(options)\n return options\n } catch {\n return []\n }\n },\n [registerCategoryOptions, t],\n )\n\n const loadTagOptions = React.useCallback(\n async (term?: string): Promise<FilterOption[]> => {\n try {\n const params = new URLSearchParams({ pageSize: '100' })\n if (term && term.trim().length) params.set('search', term.trim())\n const payload = await readApiResultOrThrow<{ items?: Array<{ id?: string; label?: string }> }>(\n `/api/catalog/tags?${params.toString()}`,\n undefined,\n { errorMessage: t('catalog.products.filters.tagsLoadError', 'Failed to load tags') },\n )\n const items = Array.isArray(payload?.items) ? payload.items : []\n const options = items\n .map((entry) => {\n const value = typeof entry.id === 'string' ? entry.id : null\n if (!value) return null\n const label = typeof entry.label === 'string' && entry.label.trim().length ? entry.label : value\n return { value, label }\n })\n .filter((option) => !!option) as FilterOption[]\n registerTagOptions(options)\n return options\n } catch {\n return []\n }\n },\n [registerTagOptions, t],\n )\n\n const productTypeOptions = React.useMemo<FilterOption[]>(() => [\n { value: 'simple', label: t('catalog.products.types.simple', 'Simple') },\n { value: 'configurable', label: t('catalog.products.types.configurable', 'Configurable') },\n { value: 'virtual', label: t('catalog.products.types.virtual', 'Virtual') },\n { value: 'downloadable', label: t('catalog.products.types.downloadable', 'Downloadable') },\n {\n value: 'bundle',\n label: `${t('catalog.products.types.bundle', 'Bundle')} (${t('common.comingSoon', 'Coming soon')})`,\n },\n {\n value: 'grouped',\n label: `${t('catalog.products.types.grouped', 'Grouped')} (${t('common.comingSoon', 'Coming soon')})`,\n },\n ], [t])\n\n const productTypeLabelMap = React.useMemo(() => {\n const map = new Map<string, string>()\n productTypeOptions.forEach((opt) => map.set(opt.value, opt.label))\n return map\n }, [productTypeOptions])\n\n const filters = React.useMemo<FilterDef[]>(() => [\n { id: 'status', label: t('catalog.products.filters.status'), type: 'text' },\n { id: 'isActive', label: t('catalog.products.filters.active'), type: 'checkbox' },\n { id: 'configurable', label: t('catalog.products.filters.configurable'), type: 'checkbox' },\n { id: 'productType', label: t('catalog.products.filters.productType', 'Type'), type: 'select', options: productTypeOptions },\n {\n id: 'channelIds',\n label: t('catalog.products.filters.channels'),\n type: 'tags',\n loadOptions: loadChannelOptions,\n options: channelOptions,\n formatValue: (val) => channelOptionsCache[val]?.label ?? val,\n formatDescription: (val) => channelOptionsCache[val]?.description ?? null,\n },\n {\n id: 'categoryIds',\n label: t('catalog.products.filters.categories', 'Categories'),\n type: 'tags',\n loadOptions: loadCategoryOptions,\n options: categoryOptions,\n formatValue: (val) => categoryOptionsCache[val]?.label ?? val,\n formatDescription: (val) => categoryOptionsCache[val]?.description ?? null,\n },\n {\n id: 'tagIds',\n label: t('catalog.products.filters.tags', 'Tags'),\n type: 'tags',\n loadOptions: loadTagOptions,\n options: tagOptions,\n formatValue: (val) => tagOptionsCache[val]?.label ?? val,\n },\n ], [\n categoryOptions,\n categoryOptionsCache,\n channelOptions,\n channelOptionsCache,\n loadCategoryOptions,\n loadChannelOptions,\n loadTagOptions,\n productTypeOptions,\n tagOptions,\n tagOptionsCache,\n t,\n ])\n\n const columns = React.useMemo<ColumnDef<ProductRow>[]>(() => {\n const base: ColumnDef<ProductRow>[] = [\n {\n id: 'media',\n header: '',\n size: 80,\n cell: ({ row }) => (\n <ProductImageCell\n mediaId={row.original.default_media_id}\n mediaUrl={row.original.default_media_url}\n title={row.original.title}\n cropType=\"contain\"\n />\n ),\n meta: { sticky: true },\n },\n {\n accessorKey: 'title',\n header: t('catalog.products.table.title', 'Title'),\n cell: ({ row }) => (\n <div className=\"flex flex-col\">\n <span className=\"font-medium\">{row.original.title || '\u2014'}</span>\n {row.original.subtitle ? (\n <span className=\"text-xs text-muted-foreground\">{row.original.subtitle}</span>\n ) : null}\n {row.original.handle ? (\n <span className=\"text-xs text-muted-foreground\">/{row.original.handle}</span>\n ) : null}\n {row.original.description ? (\n <span className=\"text-xs text-muted-foreground\">{row.original.description}</span>\n ) : null}\n </div>\n ),\n meta: { sticky: true },\n },\n {\n accessorKey: 'sku',\n header: t('catalog.products.table.sku', 'SKU'),\n cell: ({ getValue }) => {\n const value = getValue()\n return value ? <span className=\"font-mono text-xs\">{String(value)}</span> : <span className=\"text-xs text-muted-foreground\">\u2014</span>\n },\n },\n {\n accessorKey: 'product_type',\n header: t('catalog.products.table.type'),\n cell: ({ row }) => {\n const type = typeof row.original.product_type === 'string' ? row.original.product_type : 'simple'\n const label = productTypeLabelMap.get(type) ?? type\n return <span className=\"text-xs text-muted-foreground\">{label}</span>\n },\n },\n {\n accessorKey: 'is_configurable',\n header: t('catalog.products.table.configurable'),\n cell: ({ row }) => <BooleanIcon value={!!row.original.is_configurable} />,\n },\n {\n accessorKey: 'is_active',\n header: t('catalog.products.table.active'),\n cell: ({ row }) => <BooleanIcon value={!!row.original.is_active} />,\n },\n {\n accessorKey: 'pricing',\n header: t('catalog.products.table.price'),\n cell: ({ row }) => renderPrice(row.original.pricing, row.original.primary_currency_code),\n },\n {\n accessorKey: 'offers',\n header: t('catalog.products.table.channels'),\n cell: ({ row }) => renderOffers(row.original.offers),\n },\n {\n accessorKey: 'updated_at',\n header: t('catalog.products.table.updatedAt'),\n cell: ({ row }) => <span className=\"text-xs text-muted-foreground\">{formatDate(row.original.updated_at)}</span>,\n },\n ]\n return applyCustomFieldVisibility(base, customFieldDefs)\n }, [customFieldDefs, productTypeLabelMap, t])\n\n const handleSearchChange = React.useCallback((value: string) => {\n setSearch(value)\n setPage(1)\n }, [])\n\n const handleFiltersApply = React.useCallback((values: FilterValues) => {\n setFilterValues(values)\n setPage(1)\n }, [])\n\n const handleFiltersClear = React.useCallback(() => {\n setFilterValues({})\n setPage(1)\n }, [])\n\n const handleCustomFieldsetFilterChange = React.useCallback(\n (value: string | null) => {\n if (value === customFieldsetFilter) return\n setCustomFieldsetFilter(value)\n setFilterValues((prev) => {\n const entries = Object.entries(prev)\n if (!entries.some(([key]) => key.startsWith('cf_'))) return prev\n const next: FilterValues = {}\n entries.forEach(([key, val]) => {\n if (!key.startsWith('cf_')) next[key] = val\n })\n return next\n })\n setPage(1)\n },\n [customFieldsetFilter],\n )\n\n const handleRefresh = React.useCallback(() => {\n setReloadToken((token) => token + 1)\n }, [])\n\n const queryParams = React.useMemo(() => {\n const params = new URLSearchParams()\n params.set('page', String(page))\n params.set('pageSize', String(PAGE_SIZE))\n if (search.trim()) params.set('search', search.trim())\n const sort = sorting[0]\n if (sort?.id) {\n params.set('sortField', sort.id)\n params.set('sortDir', sort.desc ? 'desc' : 'asc')\n }\n const status = filterValues.status\n if (typeof status === 'string' && status.trim()) {\n params.set('status', status.trim())\n }\n if (filterValues.isActive === true) params.set('isActive', 'true')\n if (filterValues.isActive === false) params.set('isActive', 'false')\n if (filterValues.configurable === true) params.set('configurable', 'true')\n if (filterValues.configurable === false) params.set('configurable', 'false')\n if (typeof filterValues.productType === 'string' && filterValues.productType.trim()) {\n params.set('productType', filterValues.productType.trim())\n }\n if (Array.isArray(filterValues.channelIds) && filterValues.channelIds.length) {\n const values = filterValues.channelIds\n .map((value) => (typeof value === 'string' ? value : null))\n .filter((value): value is string => !!value)\n if (values.length) params.set('channelIds', values.join(','))\n }\n if (Array.isArray(filterValues.categoryIds) && filterValues.categoryIds.length) {\n const values = filterValues.categoryIds\n .map((value) => (typeof value === 'string' ? value : null))\n .filter((value): value is string => !!value)\n if (values.length) params.set('categoryIds', values.join(','))\n }\n if (Array.isArray(filterValues.tagIds) && filterValues.tagIds.length) {\n const values = filterValues.tagIds\n .map((value) => (typeof value === 'string' ? value : null))\n .filter((value): value is string => !!value)\n if (values.length) params.set('tagIds', values.join(','))\n }\n Object.entries(filterValues).forEach(([key, value]) => {\n if (!key.startsWith('cf_') || value == null) return\n if (Array.isArray(value)) {\n const entries = value\n .map((entry) => (typeof entry === 'string' ? entry.trim() : String(entry || '').trim()))\n .filter((entry) => entry.length > 0)\n if (entries.length) params.set(key, entries.join(','))\n } else if (typeof value === 'object' && value !== null && ('from' in (value as Record<string, unknown>) || 'to' in (value as Record<string, unknown>))) {\n const range = value as { from?: string; to?: string }\n if (typeof range.from === 'string' && range.from.trim().length) {\n params.set(`${key}:from`, range.from.trim())\n }\n if (typeof range.to === 'string' && range.to.trim().length) {\n params.set(`${key}:to`, range.to.trim())\n }\n } else if (typeof value === 'string' && value.trim()) {\n params.set(key, value.trim())\n }\n })\n if (typeof customFieldsetFilter === 'string' && customFieldsetFilter.trim().length > 0) {\n params.set('customFieldset', customFieldsetFilter.trim())\n }\n return params.toString()\n }, [customFieldsetFilter, filterValues, page, search, sorting])\n\n React.useEffect(() => {\n let cancelled = false\n async function load() {\n setIsLoading(true)\n setCacheStatus(null)\n try {\n const fallback: ProductsResponse = { items: [], total: 0, totalPages: 1 }\n const call = await apiCall<ProductsResponse>(\n `/api/catalog/products?${queryParams}`,\n undefined,\n { fallback },\n )\n if (!call.ok) {\n const message = t('catalog.products.list.error.load', 'Failed to load products')\n flash(message, 'error')\n if (!cancelled) setCacheStatus(null)\n return\n }\n const payload = call.result ?? fallback\n if (cancelled) return\n setCacheStatus(call.cacheStatus ?? null)\n const items = Array.isArray(payload.items) ? payload.items : []\n const normalized = items.filter((item): item is ProductRow => typeof item?.id === 'string')\n setRows(normalized)\n setTotal(typeof payload.total === 'number' ? payload.total : normalized.length)\n setTotalPages(typeof payload.totalPages === 'number' ? payload.totalPages : 1)\n } catch (error) {\n if (!cancelled) {\n setCacheStatus(null)\n const message =\n error instanceof Error\n ? error.message\n : t('catalog.products.list.error.load', 'Failed to load products')\n flash(message, 'error')\n }\n } finally {\n if (!cancelled) setIsLoading(false)\n }\n }\n load()\n return () => {\n cancelled = true\n }\n }, [queryParams, reloadToken, scopeVersion, t])\n\n const handleDelete = React.useCallback(async (row: ProductRow) => {\n const confirmed = await confirm({\n title: t('catalog.products.list.deleteConfirm', 'Delete this product?'),\n variant: 'destructive',\n })\n if (!confirmed) return\n try {\n const headers = buildOptimisticLockHeader(typeof row.updated_at === 'string' ? row.updated_at : null)\n await withScopedApiRequestHeaders(headers, () => (\n deleteCrud('catalog/products', row.id, {\n errorMessage: t('catalog.products.list.error.delete', 'Failed to delete product'),\n })\n ))\n flash(t('catalog.products.flash.deleted', 'Product deleted'), 'success')\n setReloadToken((token) => token + 1)\n } catch (error) {\n const message =\n error instanceof Error\n ? error.message\n : t('catalog.products.list.error.delete', 'Failed to delete product')\n flash(message, 'error')\n }\n }, [confirm, t])\n\n React.useEffect(() => {\n if (!onSnapshotChange) return\n onSnapshotChange({ search, filterValues, total })\n }, [onSnapshotChange, search, filterValues, total])\n\n const currentParams = React.useMemo(() => Object.fromEntries(new URLSearchParams(queryParams)), [queryParams])\n\n const exportConfig = React.useMemo(() => ({\n view: {\n getUrl: (format: DataTableExportFormat) =>\n buildCrudExportUrl('catalog/products', { ...currentParams, exportScope: 'view' }, format),\n },\n full: {\n getUrl: (format: DataTableExportFormat) =>\n buildCrudExportUrl('catalog/products', { ...currentParams, exportScope: 'full', all: 'true' }, format),\n },\n }), [currentParams])\n\n return (\n <>\n <DataTable<ProductRow>\n title={t('catalog.products.page.title', 'Products & services')}\n entityId={ENTITY_ID}\n customFieldFilterKeyExtras={[scopeVersion, reloadToken]}\n refreshButton={{\n label: t('catalog.products.actions.refresh', 'Refresh'),\n onRefresh: handleRefresh,\n isRefreshing: isLoading,\n }}\n actions={(\n <div className=\"flex items-center gap-2\">\n {extraActions}\n <Button asChild>\n <Link href=\"/backend/catalog/products/create\">\n {t('catalog.products.actions.create', 'Create')}\n </Link>\n </Button>\n </div>\n )}\n columns={columns}\n data={rows}\n searchValue={search}\n onSearchChange={handleSearchChange}\n filters={filters}\n filterValues={filterValues}\n onFiltersApply={handleFiltersApply}\n onFiltersClear={handleFiltersClear}\n onCustomFieldFilterFieldsetChange={handleCustomFieldsetFilterChange}\n sorting={sorting}\n onSortingChange={setSorting}\n injectionSpotId=\"data-table:catalog.products\"\n injectionContext={{\n search,\n filters: filterValues,\n customFieldset: customFieldsetFilter,\n page,\n sorting,\n scopeVersion,\n // Step 5.15: surface `total` so the merchandising AI widget\n // (rendered in `data-table:catalog.products:header`) can build\n // a selection-aware pageContext per spec \u00A710.1 without taking a\n // dependency on the host page.\n total,\n totalMatching: total,\n }}\n pagination={{\n page,\n pageSize: PAGE_SIZE,\n total,\n totalPages,\n onPageChange: setPage,\n cacheStatus,\n }}\n exporter={exportConfig}\n isLoading={isLoading}\n perspective={{ tableId: 'catalog.products.list' }}\n stickyActionsColumn\n rowActions={(row) => (\n <RowActions\n items={[\n {\n id: 'edit',\n label: t('catalog.products.table.actions.edit', 'Edit'),\n href: `/backend/catalog/products/${row.id}`,\n },\n {\n id: 'delete',\n label: t('catalog.products.table.actions.delete', 'Delete'),\n destructive: true,\n onSelect: () => {\n void handleDelete(row)\n },\n },\n ]}\n />\n )}\n />\n {ConfirmDialogElement}\n </>\n )\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../src/modules/catalog/components/products/productForm.ts"],
|
|
4
|
-
"sourcesContent": ["import { z } from \"zod\";\nimport { slugify } from \"@open-mercato/shared/lib/slugify\";\nimport { parseObjectLike } from \"@open-mercato/shared/lib/json/parseObjectLike\";\nimport type { ReferenceUnitCode } from \"@open-mercato/shared/lib/units/unitCodes\";\nimport {\n CATALOG_CONFIGURABLE_PRODUCT_TYPES,\n type CatalogProductOptionSchema,\n type CatalogProductType,\n} from \"../../data/types\";\nimport type { ProductMediaItem } from \"./ProductMediaManager\";\n\nexport { slugify };\n\nexport type PriceKindSummary = {\n id: string;\n code: string;\n title: string;\n currencyCode: string | null;\n displayMode: \"including-tax\" | \"excluding-tax\";\n};\n\nexport type PriceKindApiPayload = {\n id?: string | number;\n code?: string;\n title?: string;\n currencyCode?: string | null;\n currency_code?: string | null;\n displayMode?: string | null;\n display_mode?: string | null;\n};\n\nexport type TaxRateSummary = {\n id: string;\n name: string;\n code: string | null;\n rate: number | null;\n isDefault: boolean;\n};\n\nexport type ProductOptionInput = {\n id: string;\n title: string;\n values: Array<{ id: string; label: string }>;\n};\n\nexport type ProductDimensions = {\n width?: number;\n height?: number;\n depth?: number;\n unit?: string | null;\n} | null;\n\nexport type ProductWeight = {\n value?: number;\n unit?: string | null;\n} | null;\n\nexport type VariantPriceValue = {\n amount: string;\n};\n\nexport type ProductUnitRoundingMode = \"half_up\" | \"down\" | \"up\";\nexport type ProductUnitPriceReferenceUnit = ReferenceUnitCode;\n\nexport type ProductUnitConversionDraft = {\n id: string | null;\n unitCode: string;\n toBaseFactor: string;\n sortOrder: string;\n isActive: boolean;\n};\n\nexport type VariantDraft = {\n id: string;\n title: string;\n sku: string;\n isDefault: boolean;\n taxRateId: string | null;\n manageInventory: boolean;\n allowBackorder: boolean;\n hasInventoryKit: boolean;\n optionValues: Record<string, string>;\n prices: Record<string, VariantPriceValue>;\n};\n\nexport type ProductFormValues = {\n title: string;\n subtitle: string;\n handle: string;\n sku: string;\n productType: CatalogProductType;\n description: string;\n useMarkdown: boolean;\n taxRateId: string | null;\n mediaDraftId: string;\n mediaItems: ProductMediaItem[];\n defaultMediaId: string | null;\n defaultMediaUrl: string;\n hasVariants: boolean;\n options: ProductOptionInput[];\n variants: VariantDraft[];\n metadata?: Record<string, unknown> | null;\n dimensions?: ProductDimensions;\n weight?: ProductWeight;\n defaultUnit: string | null;\n defaultSalesUnit: string | null;\n defaultSalesUnitQuantity: string;\n uomRoundingScale: string;\n uomRoundingMode: ProductUnitRoundingMode;\n unitPriceEnabled: boolean;\n unitPriceReferenceUnit: string | null;\n unitPriceBaseQuantity: string;\n unitConversions: ProductUnitConversionDraft[];\n customFieldsetCode?: string | null;\n categoryIds: string[];\n channelIds: string[];\n tags: string[];\n optionSchemaId?: string | null;\n};\n\nconst optionalPositiveNumberInput = z.preprocess((value) => {\n if (value === null || value === undefined) return undefined;\n if (typeof value === \"string\" && value.trim().length === 0) return undefined;\n return value;\n}, z.coerce.number().positive().optional());\n\nconst optionalBoundedIntegerInput = (min: number, max: number) =>\n z.preprocess((value) => {\n if (value === null || value === undefined) return undefined;\n if (typeof value === \"string\" && value.trim().length === 0)\n return undefined;\n return value;\n }, z.coerce.number().int().min(min).max(max).optional());\n\nexport const productFormSchema = z\n .object({\n title: z.string().trim().min(1, \"catalog.products.validation.titleRequired\"),\n subtitle: z.string().optional(),\n handle: z\n .string()\n .trim()\n .regex(\n /^[a-z0-9\\-_]*$/,\n \"catalog.products.validation.handleFormat\",\n )\n .max(150)\n .optional(),\n sku: z\n .string()\n .trim()\n .regex(\n /^[A-Za-z0-9\\-_\\.]*$/,\n \"catalog.products.validation.skuFormat\",\n )\n .max(191)\n .optional(),\n productType: z.string().optional(),\n description: z.string().optional(),\n useMarkdown: z.boolean().optional(),\n taxRateId: z.string().uuid().nullable().optional(),\n hasVariants: z.boolean().optional(),\n mediaDraftId: z.string().optional(),\n mediaItems: z.any().optional(),\n defaultMediaId: z.string().uuid().nullable().optional(),\n defaultMediaUrl: z.string().trim().max(500).nullable().optional(),\n options: z.any().optional(),\n variants: z.any().optional(),\n // Use a permissive schema to avoid zod classic `_zod` runtime crashes on records in edge builds.\n metadata: z\n .custom<Record<string, unknown>>(() => true)\n .nullable()\n .optional(),\n dimensions: z\n .object({\n width: z.coerce.number().min(0).optional(),\n height: z.coerce.number().min(0).optional(),\n depth: z.coerce.number().min(0).optional(),\n unit: z.string().trim().max(25).optional(),\n })\n .nullable()\n .optional(),\n weight: z\n .object({\n value: z.coerce.number().min(0).optional(),\n unit: z.string().trim().max(25).optional(),\n })\n .nullable()\n .optional(),\n defaultUnit: z.string().trim().max(50).nullable().optional(),\n defaultSalesUnit: z.string().trim().max(50).nullable().optional(),\n defaultSalesUnitQuantity: optionalPositiveNumberInput,\n uomRoundingScale: optionalBoundedIntegerInput(0, 6),\n uomRoundingMode: z.enum([\"half_up\", \"down\", \"up\"]).optional(),\n unitPriceEnabled: z.boolean().optional(),\n unitPriceReferenceUnit: z.string().trim().max(50).nullable().optional(),\n unitPriceBaseQuantity: optionalPositiveNumberInput,\n unitConversions: z\n .array(\n z.object({\n id: z.string().nullable().optional(),\n unitCode: z.string().trim().max(50),\n toBaseFactor: z.coerce.number().positive(),\n sortOrder: z.coerce.number().int().min(0).max(100000).optional(),\n isActive: z.boolean().optional(),\n }),\n )\n .optional(),\n customFieldsetCode: z.string().optional().nullable(),\n categoryIds: z.array(z.string().uuid()).optional(),\n channelIds: z.array(z.string().uuid()).optional(),\n tags: z.array(z.string().trim().min(1).max(100)).optional(),\n optionSchemaId: z.string().uuid().nullable().optional(),\n })\n .passthrough()\n .refine(\n (data) => !data.unitPriceEnabled || (data.unitPriceReferenceUnit != null && data.unitPriceReferenceUnit.length > 0),\n { message: 'catalog.products.validation.referenceUnitRequired', path: ['unitPriceReferenceUnit'] }\n )\n .refine(\n (data) => !data.defaultSalesUnit || (data.defaultUnit != null && data.defaultUnit.length > 0),\n { message: 'catalog.products.validation.baseUnitRequired', path: ['defaultUnit'] }\n );\n\nexport const PRODUCT_FORM_STEPS = [\n \"general\",\n \"organize\",\n \"uom\",\n \"variants\",\n] as const;\n\nexport const BASE_INITIAL_VALUES: ProductFormValues = {\n title: \"\",\n subtitle: \"\",\n handle: \"\",\n sku: \"\",\n productType: \"simple\",\n description: \"\",\n useMarkdown: false,\n mediaDraftId: \"\",\n mediaItems: [],\n defaultMediaId: null,\n defaultMediaUrl: \"\",\n taxRateId: null,\n hasVariants: false,\n options: [],\n variants: [],\n metadata: {},\n dimensions: null,\n weight: null,\n defaultUnit: null,\n defaultSalesUnit: null,\n defaultSalesUnitQuantity: \"1\",\n uomRoundingScale: \"4\",\n uomRoundingMode: \"half_up\",\n unitPriceEnabled: false,\n unitPriceReferenceUnit: null,\n unitPriceBaseQuantity: \"\",\n unitConversions: [],\n customFieldsetCode: null,\n categoryIds: [],\n channelIds: [],\n tags: [],\n optionSchemaId: null,\n};\n\nexport const isConfigurableProductType = (type: string): boolean =>\n (CATALOG_CONFIGURABLE_PRODUCT_TYPES as readonly string[]).includes(type);\n\nexport const createInitialProductFormValues = (): ProductFormValues => ({\n ...BASE_INITIAL_VALUES,\n mediaDraftId: createLocalId(),\n variants: [createVariantDraft(null, { isDefault: true })],\n});\n\nexport const createVariantDraft = (\n productTaxRateId: string | null,\n overrides: Partial<VariantDraft> = {},\n): VariantDraft => ({\n id: createLocalId(),\n title: \"Default variant\",\n sku: \"\",\n isDefault: false,\n taxRateId: productTaxRateId ?? null,\n manageInventory: false,\n allowBackorder: false,\n hasInventoryKit: false,\n optionValues: {},\n prices: {},\n ...overrides,\n});\n\nexport const createProductUnitConversionDraft = (\n overrides: Partial<ProductUnitConversionDraft> = {},\n): ProductUnitConversionDraft => ({\n id: null,\n unitCode: \"\",\n toBaseFactor: \"\",\n sortOrder: \"\",\n isActive: true,\n ...overrides,\n});\n\nexport const buildOptionValuesKey = (\n optionValues?: Record<string, string>,\n): string => {\n if (!optionValues) return \"\";\n return Object.keys(optionValues)\n .sort((a, b) => a.localeCompare(b))\n .map((key) => `${key}:${optionValues[key] ?? \"\"}`)\n .join(\"|\");\n};\n\nexport const haveSameOptionValues = (\n current: Record<string, string> | undefined,\n next: Record<string, string>,\n): boolean => {\n const a = current ?? {};\n const keys = new Set([...Object.keys(a), ...Object.keys(next)]);\n for (const key of keys) {\n if ((a[key] ?? \"\") !== (next[key] ?? \"\")) return false;\n }\n return true;\n};\n\nconst parseNumeric = (input: unknown): number | null => {\n const numeric = typeof input === \"number\" ? input : Number(input);\n if (!Number.isFinite(numeric) || numeric < 0) return null;\n return numeric;\n};\n\nexport const normalizeProductDimensions = (raw: unknown): ProductDimensions => {\n const source = parseObjectLike(raw);\n if (!source) return null;\n const width = parseNumeric(source.width);\n const height = parseNumeric(source.height);\n const depth = parseNumeric(source.depth);\n const unit =\n typeof source.unit === \"string\" && source.unit.trim().length\n ? source.unit.trim()\n : null;\n const clean: Record<string, unknown> = {};\n if (width !== null) clean.width = width;\n if (height !== null) clean.height = height;\n if (depth !== null) clean.depth = depth;\n if (unit) clean.unit = unit;\n return Object.keys(clean).length ? (clean as ProductDimensions) : null;\n};\n\nexport const normalizeProductWeight = (raw: unknown): ProductWeight => {\n const source = parseObjectLike(raw);\n if (!source) return null;\n const value = parseNumeric(source.value);\n const unit =\n typeof source.unit === \"string\" && source.unit.trim().length\n ? source.unit.trim()\n : null;\n if (value === null && !unit) return null;\n const clean: Record<string, unknown> = {};\n if (value !== null) clean.value = value;\n if (unit) clean.unit = unit;\n return clean as ProductWeight;\n};\n\nexport const sanitizeProductDimensions = (\n raw: ProductDimensions,\n): ProductDimensions => {\n return normalizeProductDimensions(raw ?? null);\n};\n\nexport const sanitizeProductWeight = (raw: ProductWeight): ProductWeight => {\n return normalizeProductWeight(raw ?? null);\n};\n\nexport const updateDimensionValue = (\n current: ProductDimensions,\n field: \"width\" | \"height\" | \"depth\" | \"unit\",\n raw: string,\n): ProductDimensions => {\n const base = normalizeProductDimensions(current) ?? {};\n if (field === \"unit\") {\n base.unit = raw;\n } else {\n const numeric = parseNumeric(raw);\n if (numeric === null) {\n delete base[field];\n } else {\n base[field] = numeric;\n }\n }\n return sanitizeProductDimensions(base);\n};\n\nexport const updateWeightValue = (\n current: ProductWeight,\n field: \"value\" | \"unit\",\n raw: string,\n): ProductWeight => {\n const base = normalizeProductWeight(current) ?? {};\n if (field === \"unit\") {\n base.unit = raw;\n } else {\n const numeric = parseNumeric(raw);\n if (numeric === null) {\n delete (base as Record<string, unknown>).value;\n } else {\n base.value = numeric;\n }\n }\n return sanitizeProductWeight(base);\n};\n\nexport const normalizePriceKindSummary = (\n input: PriceKindApiPayload | undefined | null,\n): PriceKindSummary | null => {\n if (!input) return null;\n const getString = (value: unknown): string | null => {\n if (typeof value === \"string\" && value.trim().length) return value.trim();\n if (typeof value === \"number\" || typeof value === \"bigint\")\n return String(value);\n return null;\n };\n const id = getString(input.id);\n const code = getString(input.code);\n const title = getString(input.title);\n if (!id || !code || !title) return null;\n const currency =\n getString(input.currencyCode) ?? getString(input.currency_code);\n const displayRaw =\n getString(input.displayMode) ?? getString(input.display_mode);\n const displayMode: PriceKindSummary[\"displayMode\"] =\n displayRaw === \"including-tax\" ? \"including-tax\" : \"excluding-tax\";\n return {\n id,\n code,\n title,\n currencyCode: currency,\n displayMode,\n };\n};\n\nexport const formatTaxRateLabel = (rate: TaxRateSummary): string => {\n const extras: string[] = [];\n if (typeof rate.rate === \"number\" && Number.isFinite(rate.rate)) {\n extras.push(`${rate.rate}%`);\n }\n if (rate.code) {\n extras.push(rate.code.toUpperCase());\n }\n if (!extras.length) return rate.name;\n return `${rate.name} \u2022 ${extras.join(\" \u00B7 \")}`;\n};\n\nexport function createLocalId(): string {\n return Math.random().toString(36).slice(2, 10);\n}\n\nexport function buildOptionSchemaDefinition(\n options: ProductOptionInput[] | undefined,\n name: string,\n): CatalogProductOptionSchema | null {\n const list = Array.isArray(options) ? options : [];\n if (!list.length) return null;\n const normalizedName =\n name && name.trim().length ? name.trim() : \"Product options\";\n const schemaOptions = list\n .map((option) => {\n const title = option.title?.trim() || \"\";\n const code = resolveOptionCode(option);\n const values = Array.isArray(option.values) ? option.values : [];\n return {\n code: code || slugify(createLocalId()),\n label: title || code || \"Option\",\n inputType: \"select\" as const,\n choices: values\n .map((value) => {\n const label = value.label?.trim() || \"\";\n const valueCode = slugify(label || value.id || createLocalId());\n if (!label && !valueCode) return null;\n return {\n code: valueCode || slugify(createLocalId()),\n label: label || valueCode || \"Choice\",\n };\n })\n .filter((entry): entry is { code: string; label: string } => !!entry),\n };\n })\n .filter((entry) => entry.label.trim().length);\n if (!schemaOptions.length) return null;\n return {\n version: 1,\n name: normalizedName,\n options: schemaOptions,\n };\n}\n\nexport function convertSchemaToProductOptions(\n schema: CatalogProductOptionSchema | null | undefined,\n): ProductOptionInput[] {\n if (!schema || !Array.isArray(schema.options)) return [];\n return schema.options.map((option) => ({\n id: createLocalId(),\n title: option.label ?? option.code ?? \"Option\",\n values: Array.isArray(option.choices)\n ? option.choices.map((choice) => ({\n id: createLocalId(),\n label: choice.label ?? choice.code ?? \"\",\n }))\n : [],\n }));\n}\n\nfunction resolveOptionCode(option: ProductOptionInput): string {\n const base = option.title?.trim() || option.id?.trim() || \"\";\n const slugged = slugify(base);\n if (slugged.length) return slugged;\n if (base.length) return base;\n return createLocalId();\n}\n\nexport function buildVariantCombinations(\n options: ProductOptionInput[],\n): Record<string, string>[] {\n if (!options.length) return [];\n const [first, ...rest] = options;\n if (!first || !Array.isArray(first.values) || !first.values.length) return [];\n const firstKey = resolveOptionCode(first);\n const initial = first.values.map((value) => ({ [firstKey]: value.label }));\n return rest.reduce<Record<string, string>[]>((acc, option) => {\n if (!Array.isArray(option.values) || !option.values.length) return [];\n const optionKey = resolveOptionCode(option);\n const combos: Record<string, string>[] = [];\n acc.forEach((partial) => {\n option.values.forEach((value) => {\n combos.push({ ...partial, [optionKey]: value.label });\n });\n });\n return combos;\n }, initial);\n}\n\nexport type VariantMediaFallbackInput = {\n isDefault: boolean;\n defaultMediaId: string | null;\n name: string;\n};\n\nexport type VariantMediaFallbackResult = {\n defaultMediaId: string;\n variantName: string;\n} | null;\n\nexport function resolveVariantMediaFallback(\n variants: VariantMediaFallbackInput[],\n): VariantMediaFallbackResult {\n const defaultVariant = variants.find(\n (v) => v.isDefault && v.defaultMediaId,\n );\n const fallback = defaultVariant ?? variants.find((v) => v.defaultMediaId);\n if (fallback?.defaultMediaId) {\n return { defaultMediaId: fallback.defaultMediaId, variantName: fallback.name };\n }\n return null;\n}\n"],
|
|
5
|
-
"mappings": "AAAA,SAAS,SAAS;AAClB,SAAS,eAAe;AACxB,SAAS,uBAAuB;AAEhC;AAAA,EACE;AAAA,OAGK;
|
|
4
|
+
"sourcesContent": ["import { z } from \"zod\";\nimport { slugify } from \"@open-mercato/shared/lib/slugify\";\nimport { parseObjectLike } from \"@open-mercato/shared/lib/json/parseObjectLike\";\nimport type { ReferenceUnitCode } from \"@open-mercato/shared/lib/units/unitCodes\";\nimport {\n CATALOG_CONFIGURABLE_PRODUCT_TYPES,\n type CatalogProductOptionSchema,\n type CatalogProductType,\n} from \"../../data/types\";\nimport type { ProductMediaItem } from \"./ProductMediaManager\";\n\nexport { slugify };\n\nexport type PriceKindSummary = {\n id: string;\n code: string;\n title: string;\n currencyCode: string | null;\n displayMode: \"including-tax\" | \"excluding-tax\";\n};\n\nexport type PriceKindApiPayload = {\n id?: string | number;\n code?: string;\n title?: string;\n currencyCode?: string | null;\n currency_code?: string | null;\n displayMode?: string | null;\n display_mode?: string | null;\n};\n\nexport type TaxRateSummary = {\n id: string;\n name: string;\n code: string | null;\n rate: number | null;\n isDefault: boolean;\n};\n\nexport type ProductOptionInput = {\n id: string;\n title: string;\n values: Array<{ id: string; label: string }>;\n};\n\nexport type ProductDimensions = {\n width?: number;\n height?: number;\n depth?: number;\n unit?: string | null;\n} | null;\n\nexport type ProductWeight = {\n value?: number;\n unit?: string | null;\n} | null;\n\nexport type VariantPriceValue = {\n amount: string;\n};\n\nexport type ProductUnitRoundingMode = \"half_up\" | \"down\" | \"up\";\nexport type ProductUnitPriceReferenceUnit = ReferenceUnitCode;\n\nexport type ProductUnitConversionDraft = {\n id: string | null;\n unitCode: string;\n toBaseFactor: string;\n sortOrder: string;\n isActive: boolean;\n /** The conversion row's version, for the per-row optimistic-lock header (#2055). Optional \u2014 only loaded rows carry it. */\n updatedAt?: string | null;\n};\n\nexport type VariantDraft = {\n id: string;\n title: string;\n sku: string;\n isDefault: boolean;\n taxRateId: string | null;\n manageInventory: boolean;\n allowBackorder: boolean;\n hasInventoryKit: boolean;\n optionValues: Record<string, string>;\n prices: Record<string, VariantPriceValue>;\n};\n\nexport type ProductFormValues = {\n title: string;\n subtitle: string;\n handle: string;\n sku: string;\n productType: CatalogProductType;\n description: string;\n useMarkdown: boolean;\n taxRateId: string | null;\n mediaDraftId: string;\n mediaItems: ProductMediaItem[];\n defaultMediaId: string | null;\n defaultMediaUrl: string;\n hasVariants: boolean;\n options: ProductOptionInput[];\n variants: VariantDraft[];\n metadata?: Record<string, unknown> | null;\n dimensions?: ProductDimensions;\n weight?: ProductWeight;\n defaultUnit: string | null;\n defaultSalesUnit: string | null;\n defaultSalesUnitQuantity: string;\n uomRoundingScale: string;\n uomRoundingMode: ProductUnitRoundingMode;\n unitPriceEnabled: boolean;\n unitPriceReferenceUnit: string | null;\n unitPriceBaseQuantity: string;\n unitConversions: ProductUnitConversionDraft[];\n customFieldsetCode?: string | null;\n categoryIds: string[];\n channelIds: string[];\n tags: string[];\n optionSchemaId?: string | null;\n updatedAt?: string | null;\n};\n\nconst optionalPositiveNumberInput = z.preprocess((value) => {\n if (value === null || value === undefined) return undefined;\n if (typeof value === \"string\" && value.trim().length === 0) return undefined;\n return value;\n}, z.coerce.number().positive().optional());\n\nconst optionalBoundedIntegerInput = (min: number, max: number) =>\n z.preprocess((value) => {\n if (value === null || value === undefined) return undefined;\n if (typeof value === \"string\" && value.trim().length === 0)\n return undefined;\n return value;\n }, z.coerce.number().int().min(min).max(max).optional());\n\nexport const productFormSchema = z\n .object({\n title: z.string().trim().min(1, \"catalog.products.validation.titleRequired\"),\n subtitle: z.string().optional(),\n handle: z\n .string()\n .trim()\n .regex(\n /^[a-z0-9\\-_]*$/,\n \"catalog.products.validation.handleFormat\",\n )\n .max(150)\n .optional(),\n sku: z\n .string()\n .trim()\n .regex(\n /^[A-Za-z0-9\\-_\\.]*$/,\n \"catalog.products.validation.skuFormat\",\n )\n .max(191)\n .optional(),\n productType: z.string().optional(),\n description: z.string().optional(),\n useMarkdown: z.boolean().optional(),\n taxRateId: z.string().uuid().nullable().optional(),\n hasVariants: z.boolean().optional(),\n mediaDraftId: z.string().optional(),\n mediaItems: z.any().optional(),\n defaultMediaId: z.string().uuid().nullable().optional(),\n defaultMediaUrl: z.string().trim().max(500).nullable().optional(),\n options: z.any().optional(),\n variants: z.any().optional(),\n // Use a permissive schema to avoid zod classic `_zod` runtime crashes on records in edge builds.\n metadata: z\n .custom<Record<string, unknown>>(() => true)\n .nullable()\n .optional(),\n dimensions: z\n .object({\n width: z.coerce.number().min(0).optional(),\n height: z.coerce.number().min(0).optional(),\n depth: z.coerce.number().min(0).optional(),\n unit: z.string().trim().max(25).optional(),\n })\n .nullable()\n .optional(),\n weight: z\n .object({\n value: z.coerce.number().min(0).optional(),\n unit: z.string().trim().max(25).optional(),\n })\n .nullable()\n .optional(),\n defaultUnit: z.string().trim().max(50).nullable().optional(),\n defaultSalesUnit: z.string().trim().max(50).nullable().optional(),\n defaultSalesUnitQuantity: optionalPositiveNumberInput,\n uomRoundingScale: optionalBoundedIntegerInput(0, 6),\n uomRoundingMode: z.enum([\"half_up\", \"down\", \"up\"]).optional(),\n unitPriceEnabled: z.boolean().optional(),\n unitPriceReferenceUnit: z.string().trim().max(50).nullable().optional(),\n unitPriceBaseQuantity: optionalPositiveNumberInput,\n unitConversions: z\n .array(\n z.object({\n id: z.string().nullable().optional(),\n unitCode: z.string().trim().max(50),\n toBaseFactor: z.coerce.number().positive(),\n sortOrder: z.coerce.number().int().min(0).max(100000).optional(),\n isActive: z.boolean().optional(),\n }),\n )\n .optional(),\n customFieldsetCode: z.string().optional().nullable(),\n categoryIds: z.array(z.string().uuid()).optional(),\n channelIds: z.array(z.string().uuid()).optional(),\n tags: z.array(z.string().trim().min(1).max(100)).optional(),\n optionSchemaId: z.string().uuid().nullable().optional(),\n })\n .passthrough()\n .refine(\n (data) => !data.unitPriceEnabled || (data.unitPriceReferenceUnit != null && data.unitPriceReferenceUnit.length > 0),\n { message: 'catalog.products.validation.referenceUnitRequired', path: ['unitPriceReferenceUnit'] }\n )\n .refine(\n (data) => !data.defaultSalesUnit || (data.defaultUnit != null && data.defaultUnit.length > 0),\n { message: 'catalog.products.validation.baseUnitRequired', path: ['defaultUnit'] }\n );\n\nexport const PRODUCT_FORM_STEPS = [\n \"general\",\n \"organize\",\n \"uom\",\n \"variants\",\n] as const;\n\nexport const BASE_INITIAL_VALUES: ProductFormValues = {\n title: \"\",\n subtitle: \"\",\n handle: \"\",\n sku: \"\",\n productType: \"simple\",\n description: \"\",\n useMarkdown: false,\n mediaDraftId: \"\",\n mediaItems: [],\n defaultMediaId: null,\n defaultMediaUrl: \"\",\n taxRateId: null,\n hasVariants: false,\n options: [],\n variants: [],\n metadata: {},\n dimensions: null,\n weight: null,\n defaultUnit: null,\n defaultSalesUnit: null,\n defaultSalesUnitQuantity: \"1\",\n uomRoundingScale: \"4\",\n uomRoundingMode: \"half_up\",\n unitPriceEnabled: false,\n unitPriceReferenceUnit: null,\n unitPriceBaseQuantity: \"\",\n unitConversions: [],\n customFieldsetCode: null,\n categoryIds: [],\n channelIds: [],\n tags: [],\n optionSchemaId: null,\n};\n\nexport const isConfigurableProductType = (type: string): boolean =>\n (CATALOG_CONFIGURABLE_PRODUCT_TYPES as readonly string[]).includes(type);\n\nexport const createInitialProductFormValues = (): ProductFormValues => ({\n ...BASE_INITIAL_VALUES,\n mediaDraftId: createLocalId(),\n variants: [createVariantDraft(null, { isDefault: true })],\n});\n\nexport const createVariantDraft = (\n productTaxRateId: string | null,\n overrides: Partial<VariantDraft> = {},\n): VariantDraft => ({\n id: createLocalId(),\n title: \"Default variant\",\n sku: \"\",\n isDefault: false,\n taxRateId: productTaxRateId ?? null,\n manageInventory: false,\n allowBackorder: false,\n hasInventoryKit: false,\n optionValues: {},\n prices: {},\n ...overrides,\n});\n\nexport const createProductUnitConversionDraft = (\n overrides: Partial<ProductUnitConversionDraft> = {},\n): ProductUnitConversionDraft => ({\n id: null,\n unitCode: \"\",\n toBaseFactor: \"\",\n sortOrder: \"\",\n isActive: true,\n ...overrides,\n});\n\nexport const buildOptionValuesKey = (\n optionValues?: Record<string, string>,\n): string => {\n if (!optionValues) return \"\";\n return Object.keys(optionValues)\n .sort((a, b) => a.localeCompare(b))\n .map((key) => `${key}:${optionValues[key] ?? \"\"}`)\n .join(\"|\");\n};\n\nexport const haveSameOptionValues = (\n current: Record<string, string> | undefined,\n next: Record<string, string>,\n): boolean => {\n const a = current ?? {};\n const keys = new Set([...Object.keys(a), ...Object.keys(next)]);\n for (const key of keys) {\n if ((a[key] ?? \"\") !== (next[key] ?? \"\")) return false;\n }\n return true;\n};\n\nconst parseNumeric = (input: unknown): number | null => {\n const numeric = typeof input === \"number\" ? input : Number(input);\n if (!Number.isFinite(numeric) || numeric < 0) return null;\n return numeric;\n};\n\nexport const normalizeProductDimensions = (raw: unknown): ProductDimensions => {\n const source = parseObjectLike(raw);\n if (!source) return null;\n const width = parseNumeric(source.width);\n const height = parseNumeric(source.height);\n const depth = parseNumeric(source.depth);\n const unit =\n typeof source.unit === \"string\" && source.unit.trim().length\n ? source.unit.trim()\n : null;\n const clean: Record<string, unknown> = {};\n if (width !== null) clean.width = width;\n if (height !== null) clean.height = height;\n if (depth !== null) clean.depth = depth;\n if (unit) clean.unit = unit;\n return Object.keys(clean).length ? (clean as ProductDimensions) : null;\n};\n\nexport const normalizeProductWeight = (raw: unknown): ProductWeight => {\n const source = parseObjectLike(raw);\n if (!source) return null;\n const value = parseNumeric(source.value);\n const unit =\n typeof source.unit === \"string\" && source.unit.trim().length\n ? source.unit.trim()\n : null;\n if (value === null && !unit) return null;\n const clean: Record<string, unknown> = {};\n if (value !== null) clean.value = value;\n if (unit) clean.unit = unit;\n return clean as ProductWeight;\n};\n\nexport const sanitizeProductDimensions = (\n raw: ProductDimensions,\n): ProductDimensions => {\n return normalizeProductDimensions(raw ?? null);\n};\n\nexport const sanitizeProductWeight = (raw: ProductWeight): ProductWeight => {\n return normalizeProductWeight(raw ?? null);\n};\n\nexport const updateDimensionValue = (\n current: ProductDimensions,\n field: \"width\" | \"height\" | \"depth\" | \"unit\",\n raw: string,\n): ProductDimensions => {\n const base = normalizeProductDimensions(current) ?? {};\n if (field === \"unit\") {\n base.unit = raw;\n } else {\n const numeric = parseNumeric(raw);\n if (numeric === null) {\n delete base[field];\n } else {\n base[field] = numeric;\n }\n }\n return sanitizeProductDimensions(base);\n};\n\nexport const updateWeightValue = (\n current: ProductWeight,\n field: \"value\" | \"unit\",\n raw: string,\n): ProductWeight => {\n const base = normalizeProductWeight(current) ?? {};\n if (field === \"unit\") {\n base.unit = raw;\n } else {\n const numeric = parseNumeric(raw);\n if (numeric === null) {\n delete (base as Record<string, unknown>).value;\n } else {\n base.value = numeric;\n }\n }\n return sanitizeProductWeight(base);\n};\n\nexport const normalizePriceKindSummary = (\n input: PriceKindApiPayload | undefined | null,\n): PriceKindSummary | null => {\n if (!input) return null;\n const getString = (value: unknown): string | null => {\n if (typeof value === \"string\" && value.trim().length) return value.trim();\n if (typeof value === \"number\" || typeof value === \"bigint\")\n return String(value);\n return null;\n };\n const id = getString(input.id);\n const code = getString(input.code);\n const title = getString(input.title);\n if (!id || !code || !title) return null;\n const currency =\n getString(input.currencyCode) ?? getString(input.currency_code);\n const displayRaw =\n getString(input.displayMode) ?? getString(input.display_mode);\n const displayMode: PriceKindSummary[\"displayMode\"] =\n displayRaw === \"including-tax\" ? \"including-tax\" : \"excluding-tax\";\n return {\n id,\n code,\n title,\n currencyCode: currency,\n displayMode,\n };\n};\n\nexport const formatTaxRateLabel = (rate: TaxRateSummary): string => {\n const extras: string[] = [];\n if (typeof rate.rate === \"number\" && Number.isFinite(rate.rate)) {\n extras.push(`${rate.rate}%`);\n }\n if (rate.code) {\n extras.push(rate.code.toUpperCase());\n }\n if (!extras.length) return rate.name;\n return `${rate.name} \u2022 ${extras.join(\" \u00B7 \")}`;\n};\n\nexport function createLocalId(): string {\n return Math.random().toString(36).slice(2, 10);\n}\n\nexport function buildOptionSchemaDefinition(\n options: ProductOptionInput[] | undefined,\n name: string,\n): CatalogProductOptionSchema | null {\n const list = Array.isArray(options) ? options : [];\n if (!list.length) return null;\n const normalizedName =\n name && name.trim().length ? name.trim() : \"Product options\";\n const schemaOptions = list\n .map((option) => {\n const title = option.title?.trim() || \"\";\n const code = resolveOptionCode(option);\n const values = Array.isArray(option.values) ? option.values : [];\n return {\n code: code || slugify(createLocalId()),\n label: title || code || \"Option\",\n inputType: \"select\" as const,\n choices: values\n .map((value) => {\n const label = value.label?.trim() || \"\";\n const valueCode = slugify(label || value.id || createLocalId());\n if (!label && !valueCode) return null;\n return {\n code: valueCode || slugify(createLocalId()),\n label: label || valueCode || \"Choice\",\n };\n })\n .filter((entry): entry is { code: string; label: string } => !!entry),\n };\n })\n .filter((entry) => entry.label.trim().length);\n if (!schemaOptions.length) return null;\n return {\n version: 1,\n name: normalizedName,\n options: schemaOptions,\n };\n}\n\nexport function convertSchemaToProductOptions(\n schema: CatalogProductOptionSchema | null | undefined,\n): ProductOptionInput[] {\n if (!schema || !Array.isArray(schema.options)) return [];\n return schema.options.map((option) => ({\n id: createLocalId(),\n title: option.label ?? option.code ?? \"Option\",\n values: Array.isArray(option.choices)\n ? option.choices.map((choice) => ({\n id: createLocalId(),\n label: choice.label ?? choice.code ?? \"\",\n }))\n : [],\n }));\n}\n\nfunction resolveOptionCode(option: ProductOptionInput): string {\n const base = option.title?.trim() || option.id?.trim() || \"\";\n const slugged = slugify(base);\n if (slugged.length) return slugged;\n if (base.length) return base;\n return createLocalId();\n}\n\nexport function buildVariantCombinations(\n options: ProductOptionInput[],\n): Record<string, string>[] {\n if (!options.length) return [];\n const [first, ...rest] = options;\n if (!first || !Array.isArray(first.values) || !first.values.length) return [];\n const firstKey = resolveOptionCode(first);\n const initial = first.values.map((value) => ({ [firstKey]: value.label }));\n return rest.reduce<Record<string, string>[]>((acc, option) => {\n if (!Array.isArray(option.values) || !option.values.length) return [];\n const optionKey = resolveOptionCode(option);\n const combos: Record<string, string>[] = [];\n acc.forEach((partial) => {\n option.values.forEach((value) => {\n combos.push({ ...partial, [optionKey]: value.label });\n });\n });\n return combos;\n }, initial);\n}\n\nexport type VariantMediaFallbackInput = {\n isDefault: boolean;\n defaultMediaId: string | null;\n name: string;\n};\n\nexport type VariantMediaFallbackResult = {\n defaultMediaId: string;\n variantName: string;\n} | null;\n\nexport function resolveVariantMediaFallback(\n variants: VariantMediaFallbackInput[],\n): VariantMediaFallbackResult {\n const defaultVariant = variants.find(\n (v) => v.isDefault && v.defaultMediaId,\n );\n const fallback = defaultVariant ?? variants.find((v) => v.defaultMediaId);\n if (fallback?.defaultMediaId) {\n return { defaultMediaId: fallback.defaultMediaId, variantName: fallback.name };\n }\n return null;\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -71,7 +71,8 @@ function mapPriceItemToDraft(item, kindDisplayModes) {
|
|
|
71
71
|
priceId: typeof item.id === "string" ? item.id : void 0,
|
|
72
72
|
amount: kindMode === "including-tax" ? unitGross ?? unitNet ?? "" : unitNet ?? unitGross ?? "",
|
|
73
73
|
currencyCode: typeof item.currency_code === "string" ? item.currency_code : typeof item.currencyCode === "string" ? item.currencyCode : null,
|
|
74
|
-
displayMode: kindMode
|
|
74
|
+
displayMode: kindMode,
|
|
75
|
+
updatedAt: typeof item.updatedAt === "string" ? item.updatedAt : typeof item.updated_at === "string" ? item.updated_at : null
|
|
75
76
|
};
|
|
76
77
|
}
|
|
77
78
|
export {
|