@open-mercato/core 0.6.5-develop.4516.1.88e6ab71a9 → 0.6.5-develop.4544.1.71c003c861
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +2 -2
- package/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/step_instance/index.js +2 -0
- package/dist/generated/entities/step_instance/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/entities/user_task/index.js +2 -0
- package/dist/generated/entities/user_task/index.js.map +2 -2
- package/dist/generated/entities/workflow_branch_instance/index.js +39 -0
- package/dist/generated/entities/workflow_branch_instance/index.js.map +7 -0
- package/dist/generated/entities/workflow_event/index.js +2 -0
- package/dist/generated/entities/workflow_event/index.js.map +2 -2
- package/dist/generated/entities/workflow_instance/index.js +2 -0
- package/dist/generated/entities/workflow_instance/index.js.map +2 -2
- package/dist/generated/entities.ids.generated.js +1 -0
- package/dist/generated/entities.ids.generated.js.map +2 -2
- package/dist/generated/entity-fields-registry.js +26 -0
- package/dist/generated/entity-fields-registry.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/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/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/progress/api/jobs/[id]/route.js +7 -1
- package/dist/modules/progress/api/jobs/[id]/route.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/shipping_carriers/api/cancel/route.js +2 -2
- package/dist/modules/shipping_carriers/api/cancel/route.js.map +2 -2
- package/dist/modules/shipping_carriers/lib/status-sync.js +8 -1
- package/dist/modules/shipping_carriers/lib/status-sync.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/NodeEditDialog.js +3 -1
- package/dist/modules/workflows/components/NodeEditDialog.js.map +2 -2
- package/dist/modules/workflows/components/WorkflowGraphImpl.js +4 -2
- package/dist/modules/workflows/components/WorkflowGraphImpl.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/components/nodes/ParallelForkNode.js +49 -0
- package/dist/modules/workflows/components/nodes/ParallelForkNode.js.map +7 -0
- package/dist/modules/workflows/components/nodes/ParallelJoinNode.js +49 -0
- package/dist/modules/workflows/components/nodes/ParallelJoinNode.js.map +7 -0
- package/dist/modules/workflows/components/nodes/index.js +4 -0
- package/dist/modules/workflows/components/nodes/index.js.map +2 -2
- package/dist/modules/workflows/data/entities.js +81 -0
- package/dist/modules/workflows/data/entities.js.map +2 -2
- package/dist/modules/workflows/data/validators.js +146 -1
- package/dist/modules/workflows/data/validators.js.map +2 -2
- package/dist/modules/workflows/di.js +12 -0
- package/dist/modules/workflows/di.js.map +2 -2
- package/dist/modules/workflows/events.js +7 -1
- package/dist/modules/workflows/events.js.map +2 -2
- package/dist/modules/workflows/lib/activity-executor.js +4 -2
- package/dist/modules/workflows/lib/activity-executor.js.map +2 -2
- package/dist/modules/workflows/lib/activity-queue-types.js.map +2 -2
- package/dist/modules/workflows/lib/event-logger.js +2 -0
- package/dist/modules/workflows/lib/event-logger.js.map +2 -2
- package/dist/modules/workflows/lib/execution-token.js +98 -0
- package/dist/modules/workflows/lib/execution-token.js.map +7 -0
- package/dist/modules/workflows/lib/node-type-icons.js +14 -5
- package/dist/modules/workflows/lib/node-type-icons.js.map +2 -2
- package/dist/modules/workflows/lib/parallel-handler.js +364 -0
- package/dist/modules/workflows/lib/parallel-handler.js.map +7 -0
- package/dist/modules/workflows/lib/signal-handler.js +63 -1
- package/dist/modules/workflows/lib/signal-handler.js.map +2 -2
- package/dist/modules/workflows/lib/step-handler.js +74 -30
- package/dist/modules/workflows/lib/step-handler.js.map +2 -2
- package/dist/modules/workflows/lib/task-handler.js +26 -0
- package/dist/modules/workflows/lib/task-handler.js.map +2 -2
- package/dist/modules/workflows/lib/timer-handler.js +26 -1
- package/dist/modules/workflows/lib/timer-handler.js.map +2 -2
- package/dist/modules/workflows/lib/transition-handler.js +33 -21
- package/dist/modules/workflows/lib/transition-handler.js.map +2 -2
- package/dist/modules/workflows/lib/workflow-executor.js +39 -1
- package/dist/modules/workflows/lib/workflow-executor.js.map +2 -2
- package/dist/modules/workflows/migrations/Migration20260602120000.js +24 -0
- package/dist/modules/workflows/migrations/Migration20260602120000.js.map +7 -0
- package/dist/modules/workflows/workers/workflow-activities.worker.js +8 -4
- package/dist/modules/workflows/workers/workflow-activities.worker.js.map +2 -2
- package/generated/entities/role/index.ts +1 -0
- package/generated/entities/step_instance/index.ts +1 -0
- package/generated/entities/user/index.ts +1 -0
- package/generated/entities/user_task/index.ts +1 -0
- package/generated/entities/workflow_branch_instance/index.ts +18 -0
- package/generated/entities/workflow_event/index.ts +1 -0
- package/generated/entities/workflow_instance/index.ts +1 -0
- package/generated/entities.ids.generated.ts +1 -0
- package/generated/entity-fields-registry.ts +26 -0
- package/jest.setup.ts +17 -0
- package/package.json +8 -7
- 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/backend/profile/communication-channels/page.tsx +5 -0
- 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/progress/api/jobs/[id]/route.ts +7 -0
- 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/shipping_carriers/api/cancel/route.ts +2 -2
- package/src/modules/shipping_carriers/lib/status-sync.ts +19 -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/NodeEditDialog.tsx +2 -0
- package/src/modules/workflows/components/WorkflowGraphImpl.tsx +3 -1
- package/src/modules/workflows/components/formConfig.tsx +5 -0
- package/src/modules/workflows/components/nodes/ParallelForkNode.tsx +66 -0
- package/src/modules/workflows/components/nodes/ParallelJoinNode.tsx +66 -0
- package/src/modules/workflows/components/nodes/index.ts +6 -0
- package/src/modules/workflows/data/entities.ts +109 -0
- package/src/modules/workflows/data/validators.ts +223 -0
- package/src/modules/workflows/di.ts +20 -0
- package/src/modules/workflows/events.ts +7 -0
- package/src/modules/workflows/i18n/de.json +13 -0
- package/src/modules/workflows/i18n/en.json +13 -0
- package/src/modules/workflows/i18n/es.json +13 -0
- package/src/modules/workflows/i18n/pl.json +13 -0
- package/src/modules/workflows/lib/activity-executor.ts +8 -2
- package/src/modules/workflows/lib/activity-queue-types.ts +3 -0
- package/src/modules/workflows/lib/event-logger.ts +3 -0
- package/src/modules/workflows/lib/execution-token.ts +166 -0
- package/src/modules/workflows/lib/node-type-icons.ts +11 -2
- package/src/modules/workflows/lib/parallel-handler.ts +575 -0
- package/src/modules/workflows/lib/signal-handler.ts +72 -1
- package/src/modules/workflows/lib/step-handler.ts +94 -34
- package/src/modules/workflows/lib/task-handler.ts +32 -0
- package/src/modules/workflows/lib/timer-handler.ts +30 -1
- package/src/modules/workflows/lib/transition-handler.ts +56 -24
- package/src/modules/workflows/lib/workflow-executor.ts +53 -1
- package/src/modules/workflows/migrations/.snapshot-open-mercato.json +263 -0
- package/src/modules/workflows/migrations/Migration20260602120000.ts +25 -0
- package/src/modules/workflows/workers/workflow-activities.worker.ts +9 -4
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../../src/modules/currencies/backend/exchange-rates/%5Bid%5D/page.tsx"],
|
|
4
|
-
"sourcesContent": ["'use client'\n\nimport * as React from 'react'\nimport { useRouter } from 'next/navigation'\nimport { Page, PageBody } from '@open-mercato/ui/backend/Page'\nimport { LoadingMessage, ErrorMessage, RecordNotFoundState } from '@open-mercato/ui/backend/detail'\nimport { CrudForm } from '@open-mercato/ui/backend/CrudForm'\nimport { updateCrud } from '@open-mercato/ui/backend/utils/crud'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { apiCall } from '@open-mercato/ui/backend/utils/apiCall'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport {\n loadCurrencyOptions,\n exchangeRateGroups,\n validateExchangeRateForm,\n buildExchangeRatePayload,\n} from '../../../lib/exchangeRateFormConfig'\n\n/**\n * Formats a Date object to YYYY-MM-DDTHH:MM format in local timezone\n * for use with datetime-local input\n */\nfunction formatDateTimeLocal(date: Date): string {\n const year = date.getFullYear()\n const month = String(date.getMonth() + 1).padStart(2, '0')\n const day = String(date.getDate()).padStart(2, '0')\n const hours = String(date.getHours()).padStart(2, '0')\n const minutes = String(date.getMinutes()).padStart(2, '0')\n return `${year}-${month}-${day}T${hours}:${minutes}`\n}\n\ntype ExchangeRateData = {\n id: string\n fromCurrencyCode: string\n toCurrencyCode: string\n rate: string\n date: string\n source: string | null\n type: string | null\n isActive: boolean\n organizationId: string\n tenantId: string\n}\n\nexport default function EditExchangeRatePage({ params }: { params?: { id?: string } }) {\n const t = useT()\n const router = useRouter()\n\n const [exchangeRate, setExchangeRate] = React.useState<ExchangeRateData | null>(null)\n const [loading, setLoading] = React.useState(true)\n const [error, setError] = React.useState<string | null>(null)\n const [isNotFound, setIsNotFound] = React.useState(false)\n\n const loadOptions = React.useCallback(\n (query?: string) => loadCurrencyOptions(apiCall, query),\n []\n )\n\n // Load exchange rate data\n React.useEffect(() => {\n async function loadExchangeRate() {\n try {\n const response = await apiCall<{ items: ExchangeRateData[] }>(`/api/currencies/exchange-rates?id=${params?.id}`)\n if (response.ok && response.result && response.result.items.length > 0) {\n setExchangeRate(response.result.items[0])\n } else if (response.ok) {\n setIsNotFound(true)\n } else {\n setError(t('exchangeRates.form.errors.load'))\n }\n } catch (err) {\n setError(t('exchangeRates.form.errors.load'))\n } finally {\n setLoading(false)\n }\n }\n loadExchangeRate()\n }, [params, t])\n\n const groups = React.useMemo(\n () => exchangeRateGroups(t, loadOptions),\n [t, loadOptions]\n )\n\n if (loading) {\n return (\n <Page>\n <PageBody>\n <LoadingMessage label={t('exchangeRates.form.loading')} />\n </PageBody>\n </Page>\n )\n }\n\n if (isNotFound) {\n return (\n <Page>\n <PageBody>\n <RecordNotFoundState\n label={t('exchangeRates.form.errors.notFound')}\n backHref=\"/backend/exchange-rates\"\n backLabel={t('exchangeRates.form.actions.backToList', 'Back to exchange rates')}\n />\n </PageBody>\n </Page>\n )\n }\n\n if (error || !exchangeRate) {\n return (\n <Page>\n <PageBody>\n <ErrorMessage label={error ?? t('exchangeRates.form.errors.load')} />\n </PageBody>\n </Page>\n )\n }\n\n return (\n <Page>\n <PageBody>\n <CrudForm\n title={t('exchangeRates.edit.title')}\n backHref=\"/backend/exchange-rates\"\n versionHistory={{ resourceKind: 'currencies.exchange_rate', resourceId: exchangeRate.id }}\n fields={[]}\n groups={groups}\n initialValues={{\n fromCurrencyCode: exchangeRate.fromCurrencyCode,\n toCurrencyCode: exchangeRate.toCurrencyCode,\n rate: parseFloat(exchangeRate.rate),\n date: formatDateTimeLocal(new Date(exchangeRate.date)),\n source: exchangeRate.source || '',\n type: exchangeRate.type || '',\n isActive: exchangeRate.isActive,\n }}\n submitLabel={t('exchangeRates.form.action.save')}\n cancelHref=\"/backend/exchange-rates\"\n onSubmit={async (values) => {\n const validated = validateExchangeRateForm(values, t)\n const payload = {\n id: exchangeRate.id,\n ...buildExchangeRatePayload(values, validated),\n }\n\n await updateCrud('currencies/exchange-rates', payload)\n\n flash(t('exchangeRates.flash.updated'), 'success')\n router.push('/backend/exchange-rates')\n }}\n />\n </PageBody>\n </Page>\n )\n}\n"],
|
|
5
|
-
"mappings": ";
|
|
4
|
+
"sourcesContent": ["'use client'\n\nimport * as React from 'react'\nimport { useRouter } from 'next/navigation'\nimport { Page, PageBody } from '@open-mercato/ui/backend/Page'\nimport { LoadingMessage, ErrorMessage, RecordNotFoundState } from '@open-mercato/ui/backend/detail'\nimport { CrudForm } from '@open-mercato/ui/backend/CrudForm'\nimport { updateCrud } from '@open-mercato/ui/backend/utils/crud'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { apiCall } from '@open-mercato/ui/backend/utils/apiCall'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport {\n loadCurrencyOptions,\n exchangeRateGroups,\n validateExchangeRateForm,\n buildExchangeRatePayload,\n} from '../../../lib/exchangeRateFormConfig'\n\n/**\n * Formats a Date object to YYYY-MM-DDTHH:MM format in local timezone\n * for use with datetime-local input\n */\nfunction formatDateTimeLocal(date: Date): string {\n const year = date.getFullYear()\n const month = String(date.getMonth() + 1).padStart(2, '0')\n const day = String(date.getDate()).padStart(2, '0')\n const hours = String(date.getHours()).padStart(2, '0')\n const minutes = String(date.getMinutes()).padStart(2, '0')\n return `${year}-${month}-${day}T${hours}:${minutes}`\n}\n\ntype ExchangeRateData = {\n id: string\n fromCurrencyCode: string\n toCurrencyCode: string\n rate: string\n date: string\n source: string | null\n type: string | null\n isActive: boolean\n organizationId: string\n tenantId: string\n updatedAt?: string | null\n updated_at?: string | null\n}\n\nexport default function EditExchangeRatePage({ params }: { params?: { id?: string } }) {\n const t = useT()\n const router = useRouter()\n\n const [exchangeRate, setExchangeRate] = React.useState<ExchangeRateData | null>(null)\n const [loading, setLoading] = React.useState(true)\n const [error, setError] = React.useState<string | null>(null)\n const [isNotFound, setIsNotFound] = React.useState(false)\n\n const loadOptions = React.useCallback(\n (query?: string) => loadCurrencyOptions(apiCall, query),\n []\n )\n\n // Load exchange rate data\n React.useEffect(() => {\n async function loadExchangeRate() {\n try {\n const response = await apiCall<{ items: ExchangeRateData[] }>(`/api/currencies/exchange-rates?id=${params?.id}`)\n if (response.ok && response.result && response.result.items.length > 0) {\n setExchangeRate(response.result.items[0])\n } else if (response.ok) {\n setIsNotFound(true)\n } else {\n setError(t('exchangeRates.form.errors.load'))\n }\n } catch (err) {\n setError(t('exchangeRates.form.errors.load'))\n } finally {\n setLoading(false)\n }\n }\n loadExchangeRate()\n }, [params, t])\n\n const groups = React.useMemo(\n () => exchangeRateGroups(t, loadOptions),\n [t, loadOptions]\n )\n\n if (loading) {\n return (\n <Page>\n <PageBody>\n <LoadingMessage label={t('exchangeRates.form.loading')} />\n </PageBody>\n </Page>\n )\n }\n\n if (isNotFound) {\n return (\n <Page>\n <PageBody>\n <RecordNotFoundState\n label={t('exchangeRates.form.errors.notFound')}\n backHref=\"/backend/exchange-rates\"\n backLabel={t('exchangeRates.form.actions.backToList', 'Back to exchange rates')}\n />\n </PageBody>\n </Page>\n )\n }\n\n if (error || !exchangeRate) {\n return (\n <Page>\n <PageBody>\n <ErrorMessage label={error ?? t('exchangeRates.form.errors.load')} />\n </PageBody>\n </Page>\n )\n }\n\n return (\n <Page>\n <PageBody>\n <CrudForm\n title={t('exchangeRates.edit.title')}\n backHref=\"/backend/exchange-rates\"\n versionHistory={{ resourceKind: 'currencies.exchange_rate', resourceId: exchangeRate.id }}\n fields={[]}\n groups={groups}\n optimisticLockUpdatedAt={exchangeRate.updatedAt ?? exchangeRate.updated_at ?? null}\n initialValues={{\n fromCurrencyCode: exchangeRate.fromCurrencyCode,\n toCurrencyCode: exchangeRate.toCurrencyCode,\n rate: parseFloat(exchangeRate.rate),\n date: formatDateTimeLocal(new Date(exchangeRate.date)),\n source: exchangeRate.source || '',\n type: exchangeRate.type || '',\n isActive: exchangeRate.isActive,\n }}\n submitLabel={t('exchangeRates.form.action.save')}\n cancelHref=\"/backend/exchange-rates\"\n onSubmit={async (values) => {\n const validated = validateExchangeRateForm(values, t)\n const payload = {\n id: exchangeRate.id,\n ...buildExchangeRatePayload(values, validated),\n }\n\n await updateCrud('currencies/exchange-rates', payload)\n\n flash(t('exchangeRates.flash.updated'), 'success')\n router.push('/backend/exchange-rates')\n }}\n />\n </PageBody>\n </Page>\n )\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -9,7 +9,8 @@ import { Button } from "@open-mercato/ui/primitives/button";
|
|
|
9
9
|
import { BooleanIcon } from "@open-mercato/ui/backend/ValueIcons";
|
|
10
10
|
import { Plus } from "lucide-react";
|
|
11
11
|
import { useT } from "@open-mercato/shared/lib/i18n/context";
|
|
12
|
-
import { apiCall } from "@open-mercato/ui/backend/utils/apiCall";
|
|
12
|
+
import { apiCall, withScopedApiRequestHeaders } from "@open-mercato/ui/backend/utils/apiCall";
|
|
13
|
+
import { buildOptimisticLockHeader } from "@open-mercato/ui/backend/utils/optimisticLock";
|
|
13
14
|
import { flash } from "@open-mercato/ui/backend/FlashMessages";
|
|
14
15
|
import { useOrganizationScopeVersion } from "@open-mercato/shared/lib/frontend/useOrganizationScope";
|
|
15
16
|
import { useConfirmDialog } from "@open-mercato/ui/backend/confirm-dialog";
|
|
@@ -77,11 +78,14 @@ function ExchangeRatesPage() {
|
|
|
77
78
|
});
|
|
78
79
|
if (!confirmed) return;
|
|
79
80
|
try {
|
|
80
|
-
const call = await
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
81
|
+
const call = await withScopedApiRequestHeaders(
|
|
82
|
+
buildOptimisticLockHeader(row.updatedAt),
|
|
83
|
+
() => apiCall(`/api/currencies/exchange-rates`, {
|
|
84
|
+
method: "DELETE",
|
|
85
|
+
headers: { "Content-Type": "application/json" },
|
|
86
|
+
body: JSON.stringify({ id: row.id, organizationId: row.organizationId, tenantId: row.tenantId })
|
|
87
|
+
})
|
|
88
|
+
);
|
|
85
89
|
if (!call.ok) {
|
|
86
90
|
flash(t("exchangeRates.flash.deleteError"), "error");
|
|
87
91
|
return;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../src/modules/currencies/backend/exchange-rates/page.tsx"],
|
|
4
|
-
"sourcesContent": ["'use client'\n\nimport * as React from 'react'\nimport Link from 'next/link'\nimport { Page, PageBody } from '@open-mercato/ui/backend/Page'\nimport { DataTable } from '@open-mercato/ui/backend/DataTable'\nimport type { ColumnDef } from '@tanstack/react-table'\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 { Plus } from 'lucide-react'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { apiCall } 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 { useConfirmDialog } from '@open-mercato/ui/backend/confirm-dialog'\nimport type { FilterDef, FilterValues } from '@open-mercato/ui/backend/FilterBar'\n\ntype ExchangeRateRow = {\n id: string\n fromCurrencyCode: string\n toCurrencyCode: string\n rate: string\n date: string\n source: string | null\n type: string | null\n isActive: boolean\n organizationId: string\n tenantId: string\n createdAt: string\n updatedAt: string\n}\n\ntype ResponsePayload = {\n items: ExchangeRateRow[]\n total: number\n page: number\n totalPages: number\n}\n\nexport default function ExchangeRatesPage() {\n const t = useT()\n const { confirm: confirmDialog, ConfirmDialogElement } = useConfirmDialog()\n const [rows, setRows] = React.useState<ExchangeRateRow[]>([])\n const [page, setPage] = React.useState(1)\n const [total, setTotal] = React.useState(0)\n const [totalPages, setTotalPages] = React.useState(1)\n const [search, setSearch] = React.useState('')\n const [filters, setFilters] = React.useState<FilterValues>({})\n const [isLoading, setIsLoading] = React.useState(true)\n const [reloadToken, setReloadToken] = React.useState(0)\n const scopeVersion = useOrganizationScopeVersion()\n\n React.useEffect(() => {\n let cancelled = false\n async function load() {\n setIsLoading(true)\n try {\n const params = new URLSearchParams()\n params.set('page', String(page))\n params.set('pageSize', '50')\n if (search) params.set('search', search)\n if (filters.fromCurrencyCode) params.set('fromCurrencyCode', String(filters.fromCurrencyCode))\n if (filters.toCurrencyCode) params.set('toCurrencyCode', String(filters.toCurrencyCode))\n if (filters.source) params.set('source', String(filters.source))\n if (filters.type) params.set('type', String(filters.type))\n if (filters.isActive === 'true') params.set('isActive', 'true')\n if (filters.isActive === 'false') params.set('isActive', 'false')\n\n const fallback: ResponsePayload = { items: [], total: 0, page, totalPages: 1 }\n const call = await apiCall<ResponsePayload>(\n `/api/currencies/exchange-rates?${params.toString()}`,\n undefined,\n { fallback }\n )\n\n if (!call.ok) {\n flash(t('exchangeRates.list.error.load'), 'error')\n return\n }\n\n const payload = call.result ?? fallback\n if (!cancelled) {\n setRows(Array.isArray(payload.items) ? payload.items : [])\n setTotal(payload.total || 0)\n setTotalPages(payload.totalPages || 1)\n }\n } catch (error) {\n if (!cancelled) {\n flash(t('exchangeRates.list.error.load'), 'error')\n }\n } finally {\n if (!cancelled) setIsLoading(false)\n }\n }\n load()\n return () => {\n cancelled = true\n }\n }, [page, search, filters, reloadToken, scopeVersion, t])\n\n const handleDelete = React.useCallback(\n async (row: ExchangeRateRow) => {\n const confirmed = await confirmDialog({\n title: t('exchangeRates.list.confirmDelete', { pair: `${row.fromCurrencyCode}/${row.toCurrencyCode}` }),\n variant: 'destructive',\n })\n if (!confirmed) return\n\n try {\n const call = await apiCall(`/api/currencies/exchange-rates`, {\n method: 'DELETE',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ id: row.id, organizationId: row.organizationId, tenantId: row.tenantId }),\n })\n\n if (!call.ok) {\n flash(t('exchangeRates.flash.deleteError'), 'error')\n return\n }\n\n flash(t('exchangeRates.flash.deleted'), 'success')\n setReloadToken((token) => token + 1)\n } catch (error) {\n flash(t('exchangeRates.flash.deleteError'), 'error')\n }\n },\n [t, confirmDialog]\n )\n\n const columns = React.useMemo<ColumnDef<ExchangeRateRow>[]>(\n () => [\n {\n accessorKey: 'currencyPair',\n header: t('exchangeRates.list.columns.currencyPair'),\n cell: ({ row }) => (\n <span className=\"font-mono font-medium\">\n {row.original.fromCurrencyCode} \u2192 {row.original.toCurrencyCode}\n </span>\n ),\n },\n {\n accessorKey: 'rate',\n header: t('exchangeRates.list.columns.rate'),\n cell: ({ row }) => (\n <span className=\"font-mono\">\n {parseFloat(row.original.rate).toFixed(8)}\n </span>\n ),\n },\n {\n accessorKey: 'date',\n header: t('exchangeRates.list.columns.date'),\n cell: ({ row }) => new Date(row.original.date).toLocaleString(undefined, {\n year: 'numeric',\n month: '2-digit',\n day: '2-digit',\n hour: '2-digit',\n minute: '2-digit'\n }),\n },\n {\n accessorKey: 'source',\n header: t('exchangeRates.list.columns.source'),\n cell: ({ row }) => row.original.source || '\u2014',\n },\n {\n accessorKey: 'type',\n header: t('exchangeRates.list.columns.type'),\n cell: ({ row }) => {\n const type = row.original.type\n if (!type) return '\u2014'\n return (\n <span className={`inline-flex items-center px-2 py-1 rounded-md text-xs font-medium ${\n type === 'buy' \n ? 'bg-green-100 text-green-800' \n : 'bg-blue-100 text-blue-800'\n }`}>\n {type === 'buy' ? t('exchangeRates.list.type.buy') : t('exchangeRates.list.type.sell')}\n </span>\n )\n },\n },\n {\n accessorKey: 'isActive',\n header: t('exchangeRates.list.columns.active'),\n enableSorting: false,\n cell: ({ getValue }) => <BooleanIcon value={Boolean(getValue())} />,\n },\n {\n accessorKey: 'createdAt',\n header: t('exchangeRates.list.columns.createdAt'),\n cell: ({ row }) => {\n const date = row.original.createdAt\n return date ? new Date(date).toLocaleString() : '\u2014'\n },\n },\n {\n accessorKey: 'updatedAt',\n header: t('exchangeRates.list.columns.updatedAt'),\n cell: ({ row }) => {\n const date = row.original.updatedAt\n return date ? new Date(date).toLocaleString() : '\u2014'\n },\n },\n ],\n [t]\n )\n\n const filterDefs = React.useMemo<FilterDef[]>(\n () => [\n {\n id: 'fromCurrencyCode',\n label: t('exchangeRates.list.filters.fromCurrency'),\n type: 'text',\n },\n {\n id: 'toCurrencyCode',\n label: t('exchangeRates.list.filters.toCurrency'),\n type: 'text',\n },\n {\n id: 'source',\n label: t('exchangeRates.list.filters.source'),\n type: 'text',\n },\n {\n id: 'type',\n label: t('exchangeRates.list.filters.type'),\n type: 'select',\n options: [\n { label: t('exchangeRates.list.filters.all'), value: '' },\n { label: t('exchangeRates.list.type.buy'), value: 'buy' },\n { label: t('exchangeRates.list.type.sell'), value: 'sell' },\n ],\n },\n {\n id: 'isActive',\n label: t('exchangeRates.list.filters.status'),\n type: 'select',\n options: [\n { label: t('exchangeRates.list.filters.all'), value: '' },\n { label: t('exchangeRates.list.filters.active'), value: 'true' },\n { label: t('exchangeRates.list.filters.inactive'), value: 'false' },\n ],\n },\n ],\n [t]\n )\n\n return (\n <Page>\n <PageBody>\n <DataTable\n title={t('exchangeRates.list.title')}\n columns={columns}\n data={rows}\n searchValue={search}\n onSearchChange={(value) => {\n setSearch(value)\n setPage(1)\n }}\n searchPlaceholder={t('exchangeRates.list.searchPlaceholder')}\n filters={filterDefs}\n filterValues={filters}\n onFiltersApply={(values) => {\n setFilters(values)\n setPage(1)\n }}\n onFiltersClear={() => {\n setFilters({})\n setPage(1)\n }}\n actions={\n <Button asChild>\n <Link href=\"/backend/exchange-rates/create\">\n <Plus className=\"mr-2 h-4 w-4\" />\n {t('exchangeRates.list.actions.create')}\n </Link>\n </Button>\n }\n rowActions={(row) => (\n <RowActions\n items={[\n {\n id: 'edit',\n label: t('common.edit'),\n href: `/backend/exchange-rates/${row.id}`,\n },\n {\n id: 'delete',\n label: t('common.delete'),\n destructive: true,\n onSelect: () => handleDelete(row),\n },\n ]}\n />\n )}\n pagination={{ page, pageSize: 50, total, totalPages, onPageChange: setPage }}\n isLoading={isLoading}\n perspective={{ tableId: 'exchange-rates.list' }}\n />\n </PageBody>\n {ConfirmDialogElement}\n </Page>\n )\n}\n"],
|
|
5
|
-
"mappings": ";
|
|
4
|
+
"sourcesContent": ["'use client'\n\nimport * as React from 'react'\nimport Link from 'next/link'\nimport { Page, PageBody } from '@open-mercato/ui/backend/Page'\nimport { DataTable } from '@open-mercato/ui/backend/DataTable'\nimport type { ColumnDef } from '@tanstack/react-table'\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 { Plus } from 'lucide-react'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { apiCall, withScopedApiRequestHeaders } from '@open-mercato/ui/backend/utils/apiCall'\nimport { buildOptimisticLockHeader } from '@open-mercato/ui/backend/utils/optimisticLock'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { useOrganizationScopeVersion } from '@open-mercato/shared/lib/frontend/useOrganizationScope'\nimport { useConfirmDialog } from '@open-mercato/ui/backend/confirm-dialog'\nimport type { FilterDef, FilterValues } from '@open-mercato/ui/backend/FilterBar'\n\ntype ExchangeRateRow = {\n id: string\n fromCurrencyCode: string\n toCurrencyCode: string\n rate: string\n date: string\n source: string | null\n type: string | null\n isActive: boolean\n organizationId: string\n tenantId: string\n createdAt: string\n updatedAt: string\n}\n\ntype ResponsePayload = {\n items: ExchangeRateRow[]\n total: number\n page: number\n totalPages: number\n}\n\nexport default function ExchangeRatesPage() {\n const t = useT()\n const { confirm: confirmDialog, ConfirmDialogElement } = useConfirmDialog()\n const [rows, setRows] = React.useState<ExchangeRateRow[]>([])\n const [page, setPage] = React.useState(1)\n const [total, setTotal] = React.useState(0)\n const [totalPages, setTotalPages] = React.useState(1)\n const [search, setSearch] = React.useState('')\n const [filters, setFilters] = React.useState<FilterValues>({})\n const [isLoading, setIsLoading] = React.useState(true)\n const [reloadToken, setReloadToken] = React.useState(0)\n const scopeVersion = useOrganizationScopeVersion()\n\n React.useEffect(() => {\n let cancelled = false\n async function load() {\n setIsLoading(true)\n try {\n const params = new URLSearchParams()\n params.set('page', String(page))\n params.set('pageSize', '50')\n if (search) params.set('search', search)\n if (filters.fromCurrencyCode) params.set('fromCurrencyCode', String(filters.fromCurrencyCode))\n if (filters.toCurrencyCode) params.set('toCurrencyCode', String(filters.toCurrencyCode))\n if (filters.source) params.set('source', String(filters.source))\n if (filters.type) params.set('type', String(filters.type))\n if (filters.isActive === 'true') params.set('isActive', 'true')\n if (filters.isActive === 'false') params.set('isActive', 'false')\n\n const fallback: ResponsePayload = { items: [], total: 0, page, totalPages: 1 }\n const call = await apiCall<ResponsePayload>(\n `/api/currencies/exchange-rates?${params.toString()}`,\n undefined,\n { fallback }\n )\n\n if (!call.ok) {\n flash(t('exchangeRates.list.error.load'), 'error')\n return\n }\n\n const payload = call.result ?? fallback\n if (!cancelled) {\n setRows(Array.isArray(payload.items) ? payload.items : [])\n setTotal(payload.total || 0)\n setTotalPages(payload.totalPages || 1)\n }\n } catch (error) {\n if (!cancelled) {\n flash(t('exchangeRates.list.error.load'), 'error')\n }\n } finally {\n if (!cancelled) setIsLoading(false)\n }\n }\n load()\n return () => {\n cancelled = true\n }\n }, [page, search, filters, reloadToken, scopeVersion, t])\n\n const handleDelete = React.useCallback(\n async (row: ExchangeRateRow) => {\n const confirmed = await confirmDialog({\n title: t('exchangeRates.list.confirmDelete', { pair: `${row.fromCurrencyCode}/${row.toCurrencyCode}` }),\n variant: 'destructive',\n })\n if (!confirmed) return\n\n try {\n const call = await withScopedApiRequestHeaders(\n buildOptimisticLockHeader(row.updatedAt),\n () => apiCall(`/api/currencies/exchange-rates`, {\n method: 'DELETE',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ id: row.id, organizationId: row.organizationId, tenantId: row.tenantId }),\n }),\n )\n\n if (!call.ok) {\n flash(t('exchangeRates.flash.deleteError'), 'error')\n return\n }\n\n flash(t('exchangeRates.flash.deleted'), 'success')\n setReloadToken((token) => token + 1)\n } catch (error) {\n flash(t('exchangeRates.flash.deleteError'), 'error')\n }\n },\n [t, confirmDialog]\n )\n\n const columns = React.useMemo<ColumnDef<ExchangeRateRow>[]>(\n () => [\n {\n accessorKey: 'currencyPair',\n header: t('exchangeRates.list.columns.currencyPair'),\n cell: ({ row }) => (\n <span className=\"font-mono font-medium\">\n {row.original.fromCurrencyCode} \u2192 {row.original.toCurrencyCode}\n </span>\n ),\n },\n {\n accessorKey: 'rate',\n header: t('exchangeRates.list.columns.rate'),\n cell: ({ row }) => (\n <span className=\"font-mono\">\n {parseFloat(row.original.rate).toFixed(8)}\n </span>\n ),\n },\n {\n accessorKey: 'date',\n header: t('exchangeRates.list.columns.date'),\n cell: ({ row }) => new Date(row.original.date).toLocaleString(undefined, {\n year: 'numeric',\n month: '2-digit',\n day: '2-digit',\n hour: '2-digit',\n minute: '2-digit'\n }),\n },\n {\n accessorKey: 'source',\n header: t('exchangeRates.list.columns.source'),\n cell: ({ row }) => row.original.source || '\u2014',\n },\n {\n accessorKey: 'type',\n header: t('exchangeRates.list.columns.type'),\n cell: ({ row }) => {\n const type = row.original.type\n if (!type) return '\u2014'\n return (\n <span className={`inline-flex items-center px-2 py-1 rounded-md text-xs font-medium ${\n type === 'buy' \n ? 'bg-green-100 text-green-800' \n : 'bg-blue-100 text-blue-800'\n }`}>\n {type === 'buy' ? t('exchangeRates.list.type.buy') : t('exchangeRates.list.type.sell')}\n </span>\n )\n },\n },\n {\n accessorKey: 'isActive',\n header: t('exchangeRates.list.columns.active'),\n enableSorting: false,\n cell: ({ getValue }) => <BooleanIcon value={Boolean(getValue())} />,\n },\n {\n accessorKey: 'createdAt',\n header: t('exchangeRates.list.columns.createdAt'),\n cell: ({ row }) => {\n const date = row.original.createdAt\n return date ? new Date(date).toLocaleString() : '\u2014'\n },\n },\n {\n accessorKey: 'updatedAt',\n header: t('exchangeRates.list.columns.updatedAt'),\n cell: ({ row }) => {\n const date = row.original.updatedAt\n return date ? new Date(date).toLocaleString() : '\u2014'\n },\n },\n ],\n [t]\n )\n\n const filterDefs = React.useMemo<FilterDef[]>(\n () => [\n {\n id: 'fromCurrencyCode',\n label: t('exchangeRates.list.filters.fromCurrency'),\n type: 'text',\n },\n {\n id: 'toCurrencyCode',\n label: t('exchangeRates.list.filters.toCurrency'),\n type: 'text',\n },\n {\n id: 'source',\n label: t('exchangeRates.list.filters.source'),\n type: 'text',\n },\n {\n id: 'type',\n label: t('exchangeRates.list.filters.type'),\n type: 'select',\n options: [\n { label: t('exchangeRates.list.filters.all'), value: '' },\n { label: t('exchangeRates.list.type.buy'), value: 'buy' },\n { label: t('exchangeRates.list.type.sell'), value: 'sell' },\n ],\n },\n {\n id: 'isActive',\n label: t('exchangeRates.list.filters.status'),\n type: 'select',\n options: [\n { label: t('exchangeRates.list.filters.all'), value: '' },\n { label: t('exchangeRates.list.filters.active'), value: 'true' },\n { label: t('exchangeRates.list.filters.inactive'), value: 'false' },\n ],\n },\n ],\n [t]\n )\n\n return (\n <Page>\n <PageBody>\n <DataTable\n title={t('exchangeRates.list.title')}\n columns={columns}\n data={rows}\n searchValue={search}\n onSearchChange={(value) => {\n setSearch(value)\n setPage(1)\n }}\n searchPlaceholder={t('exchangeRates.list.searchPlaceholder')}\n filters={filterDefs}\n filterValues={filters}\n onFiltersApply={(values) => {\n setFilters(values)\n setPage(1)\n }}\n onFiltersClear={() => {\n setFilters({})\n setPage(1)\n }}\n actions={\n <Button asChild>\n <Link href=\"/backend/exchange-rates/create\">\n <Plus className=\"mr-2 h-4 w-4\" />\n {t('exchangeRates.list.actions.create')}\n </Link>\n </Button>\n }\n rowActions={(row) => (\n <RowActions\n items={[\n {\n id: 'edit',\n label: t('common.edit'),\n href: `/backend/exchange-rates/${row.id}`,\n },\n {\n id: 'delete',\n label: t('common.delete'),\n destructive: true,\n onSelect: () => handleDelete(row),\n },\n ]}\n />\n )}\n pagination={{ page, pageSize: 50, total, totalPages, onPageChange: setPage }}\n isLoading={isLoading}\n perspective={{ tableId: 'exchange-rates.list' }}\n />\n </PageBody>\n {ConfirmDialogElement}\n </Page>\n )\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -177,14 +177,16 @@ const updateCurrencyCommand = {
|
|
|
177
177
|
if (Object.keys(changes).length === 0) {
|
|
178
178
|
return { currencyId: record.id };
|
|
179
179
|
}
|
|
180
|
-
for (const [key, change] of Object.entries(changes)) {
|
|
181
|
-
;
|
|
182
|
-
record[key] = change.to;
|
|
183
|
-
}
|
|
184
|
-
record.updatedAt = /* @__PURE__ */ new Date();
|
|
185
180
|
await withAtomicFlush(
|
|
186
181
|
em,
|
|
187
182
|
[
|
|
183
|
+
() => {
|
|
184
|
+
for (const [key, change] of Object.entries(changes)) {
|
|
185
|
+
;
|
|
186
|
+
record[key] = change.to;
|
|
187
|
+
}
|
|
188
|
+
record.updatedAt = /* @__PURE__ */ new Date();
|
|
189
|
+
},
|
|
188
190
|
() => parsed.isBase === true && record.isBase ? enforceBaseCurrency(em, record.id, record.organizationId, record.tenantId) : void 0
|
|
189
191
|
],
|
|
190
192
|
{ transaction: true }
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/modules/currencies/commands/currencies.ts"],
|
|
4
|
-
"sourcesContent": ["import { registerCommand } from '@open-mercato/shared/lib/commands'\nimport type { CommandHandler } from '@open-mercato/shared/lib/commands'\nimport { buildChanges, requireId, emitCrudSideEffects } from '@open-mercato/shared/lib/commands/helpers'\nimport { extractUndoPayload, type UndoPayload } from '@open-mercato/shared/lib/commands/undo'\nimport { withAtomicFlush } from '@open-mercato/shared/lib/commands/flush'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { CrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport { Currency, ExchangeRate } from '../data/entities'\nimport {\n currencyCreateSchema,\n currencyUpdateSchema,\n currencyDeleteSchema,\n type CurrencyCreateInput,\n type CurrencyUpdateInput,\n type CurrencyDeleteInput,\n} from '../data/validators'\nimport type { CrudEventsConfig } from '@open-mercato/shared/lib/crud/types'\nimport type { DataEngine } from '@open-mercato/shared/lib/data/engine'\n\nconst currencyCrudEvents: CrudEventsConfig = {\n module: 'currencies',\n entity: 'currency',\n persistent: true,\n buildPayload: (ctx) => ({\n id: ctx.identifiers.id,\n organizationId: ctx.identifiers.organizationId,\n tenantId: ctx.identifiers.tenantId,\n }),\n}\n\ntype CurrencySnapshot = {\n id: string\n organizationId: string\n tenantId: string\n code: string\n name: string\n symbol: string | null\n decimalPlaces: number\n thousandsSeparator: string | null\n decimalSeparator: string | null\n isBase: boolean\n isActive: boolean\n createdAt: string\n updatedAt: string\n}\n\ntype CurrencyUndoPayload = UndoPayload<CurrencySnapshot>\n\nasync function loadCurrencySnapshot(em: EntityManager, id: string): Promise<CurrencySnapshot | null> {\n const record = await em.findOne(Currency, { id })\n if (!record) return null\n return {\n id: record.id,\n organizationId: record.organizationId,\n tenantId: record.tenantId,\n code: record.code,\n name: record.name,\n symbol: record.symbol ?? null,\n decimalPlaces: record.decimalPlaces,\n thousandsSeparator: record.thousandsSeparator ?? null,\n decimalSeparator: record.decimalSeparator ?? null,\n isBase: !!record.isBase,\n isActive: !!record.isActive,\n createdAt: record.createdAt.toISOString(),\n updatedAt: record.updatedAt.toISOString(),\n }\n}\n\nasync function enforceBaseCurrency(\n em: EntityManager,\n currencyId: string,\n organizationId: string,\n tenantId: string\n): Promise<void> {\n await em.nativeUpdate(\n Currency,\n {\n organizationId,\n tenantId,\n id: { $ne: currencyId },\n isBase: true,\n deletedAt: null,\n },\n { isBase: false, updatedAt: new Date() }\n )\n}\n\nconst createCurrencyCommand: CommandHandler<CurrencyCreateInput, { currencyId: string }> = {\n id: 'currencies.currencies.create',\n async execute(input, ctx) {\n const parsed = currencyCreateSchema.parse(input)\n\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n\n // Check for duplicate code\n const existing = await em.findOne(Currency, {\n code: parsed.code,\n organizationId: parsed.organizationId,\n tenantId: parsed.tenantId,\n deletedAt: null,\n })\n if (existing) {\n throw new CrudHttpError(400, { error: 'Currency code already exists for this organization.' })\n }\n\n const now = new Date()\n const record = em.create(Currency, {\n organizationId: parsed.organizationId,\n tenantId: parsed.tenantId,\n code: parsed.code,\n name: parsed.name,\n symbol: parsed.symbol ?? null,\n decimalPlaces: parsed.decimalPlaces ?? 2,\n thousandsSeparator: parsed.thousandsSeparator ?? null,\n decimalSeparator: parsed.decimalSeparator ?? null,\n isBase: parsed.isBase ?? false,\n isActive: parsed.isActive !== false,\n createdAt: now,\n updatedAt: now,\n })\n em.persist(record)\n\n // Demote any existing base currency and insert the new record in one\n // transaction; a partial commit would leave zero or two base currencies.\n await withAtomicFlush(\n em,\n [\n () =>\n record.isBase\n ? enforceBaseCurrency(em, record.id, record.organizationId, record.tenantId)\n : undefined,\n ],\n { transaction: true },\n )\n\n const de = ctx.container.resolve('dataEngine') as DataEngine\n await emitCrudSideEffects({\n dataEngine: de,\n action: 'created',\n entity: record,\n identifiers: {\n id: record.id,\n organizationId: record.organizationId,\n tenantId: record.tenantId,\n },\n events: currencyCrudEvents,\n })\n\n return { currencyId: record.id }\n },\n captureAfter: async (_input, result, ctx) => {\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n return loadCurrencySnapshot(em, result.currencyId)\n },\n buildLog: async ({ snapshots }) => {\n const after = snapshots.after as CurrencySnapshot | undefined\n if (!after) return null\n const { translate } = await resolveTranslations()\n return {\n actionLabel: translate('currencies.audit.create', 'Create currency'),\n resourceKind: 'currencies.currency',\n resourceId: after.id,\n tenantId: after.tenantId,\n organizationId: after.organizationId,\n snapshotAfter: after,\n payload: { undo: { after } },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const payload = extractUndoPayload<CurrencyUndoPayload>(logEntry)\n const after = payload?.after ?? null\n if (!after) return\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const record = await em.findOne(Currency, { id: after.id })\n if (!record) return\n record.deletedAt = new Date()\n record.isActive = false\n await em.flush()\n },\n}\n\nconst updateCurrencyCommand: CommandHandler<CurrencyUpdateInput, { currencyId: string }> = {\n id: 'currencies.currencies.update',\n async prepare(input, ctx) {\n requireId(input.id, 'Currency ID is required')\n const em = ctx.container.resolve('em') as EntityManager\n const before = await loadCurrencySnapshot(em, input.id)\n return { before }\n },\n async execute(input, ctx) {\n const parsed = currencyUpdateSchema.parse(input)\n requireId(parsed.id, 'Currency ID is required')\n\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const record = await em.findOne(Currency, { id: parsed.id, deletedAt: null })\n if (!record) {\n throw new CrudHttpError(404, { error: 'Currency not found' })\n }\n\n // Check code uniqueness if changing code\n if (parsed.code && parsed.code !== record.code) {\n const existing = await em.findOne(Currency, {\n code: parsed.code,\n organizationId: record.organizationId,\n tenantId: record.tenantId,\n id: { $ne: record.id },\n deletedAt: null,\n })\n if (existing) {\n throw new CrudHttpError(400, { error: 'Currency code already exists for this organization.' })\n }\n }\n\n const allChanges = buildChanges(record as unknown as Record<string, unknown>, parsed, [\n 'code',\n 'name',\n 'symbol',\n 'decimalPlaces',\n 'thousandsSeparator',\n 'decimalSeparator',\n 'isBase',\n 'isActive',\n ])\n const changes = Object.fromEntries(\n Object.entries(allChanges).filter(([, c]) => c.to !== undefined),\n ) as Record<string, { from: unknown; to: unknown }>\n\n if (Object.keys(changes).length === 0) {\n return { currencyId: record.id }\n }\n\n for (const [key, change] of Object.entries(changes)) {\n ;(record as any)[key] = change.to\n }\n record.updatedAt = new Date()\n\n // Demote any existing base currency and persist the scalar changes in one\n // transaction; a partial commit would leave zero or two base currencies.\n await withAtomicFlush(\n em,\n [\n () =>\n parsed.isBase === true && record.isBase\n ? enforceBaseCurrency(em, record.id, record.organizationId, record.tenantId)\n : undefined,\n ],\n { transaction: true },\n )\n\n const de = ctx.container.resolve('dataEngine') as DataEngine\n await emitCrudSideEffects({\n dataEngine: de,\n action: 'updated',\n entity: record,\n identifiers: {\n id: record.id,\n organizationId: record.organizationId,\n tenantId: record.tenantId,\n },\n events: currencyCrudEvents,\n })\n\n return { currencyId: record.id }\n },\n captureAfter: async (_input, result, ctx) => {\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n return loadCurrencySnapshot(em, result.currencyId)\n },\n buildLog: async ({ snapshots, result }) => {\n const before = snapshots.before as CurrencySnapshot | undefined\n const after = snapshots.after as CurrencySnapshot | undefined\n if (!after) return null\n const { translate } = await resolveTranslations()\n return {\n actionLabel: translate('currencies.audit.update', 'Update currency'),\n resourceKind: 'currencies.currency',\n resourceId: after.id,\n tenantId: after.tenantId,\n organizationId: after.organizationId,\n snapshotBefore: before ?? undefined,\n snapshotAfter: after,\n payload: { undo: { before, after } },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const payload = extractUndoPayload<CurrencyUndoPayload>(logEntry)\n const before = payload?.before ?? null\n if (!before) return\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const record = await em.findOne(Currency, { id: before.id })\n if (!record) return\n Object.assign(record, {\n code: before.code,\n name: before.name,\n symbol: before.symbol,\n decimalPlaces: before.decimalPlaces,\n thousandsSeparator: before.thousandsSeparator,\n decimalSeparator: before.decimalSeparator,\n isBase: before.isBase,\n isActive: before.isActive,\n updatedAt: new Date(),\n })\n await em.flush()\n },\n}\n\nconst deleteCurrencyCommand: CommandHandler<CurrencyDeleteInput, { currencyId: string }> = {\n id: 'currencies.currencies.delete',\n async prepare(input, ctx) {\n requireId(input.id, 'Currency ID is required')\n const em = ctx.container.resolve('em') as EntityManager\n const before = await loadCurrencySnapshot(em, input.id)\n return { before }\n },\n async execute(input, ctx) {\n const parsed = currencyDeleteSchema.parse(input)\n requireId(parsed.id, 'Currency ID is required')\n\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const record = await em.findOne(Currency, { id: parsed.id, deletedAt: null })\n if (!record) {\n throw new CrudHttpError(404, { error: 'Currency not found' })\n }\n\n // Prevent deleting base currency\n if (record.isBase) {\n throw new CrudHttpError(400, { error: 'Cannot delete the base currency' })\n }\n\n // Prevent deleting currency with active exchange rates\n const activeRatesCount = await em.count(ExchangeRate, {\n $or: [\n { fromCurrencyCode: record.code },\n { toCurrencyCode: record.code },\n ],\n organizationId: record.organizationId,\n tenantId: record.tenantId,\n deletedAt: null,\n isActive: true,\n })\n \n if (activeRatesCount > 0) {\n throw new CrudHttpError(400, { \n error: `Cannot delete currency ${record.code} because it has ${activeRatesCount} active exchange rate(s). Please delete or deactivate the exchange rates first.` \n })\n }\n\n record.deletedAt = new Date()\n record.isActive = false\n await em.flush()\n\n const de = ctx.container.resolve('dataEngine') as DataEngine\n await emitCrudSideEffects({\n dataEngine: de,\n action: 'deleted',\n entity: record,\n identifiers: {\n id: record.id,\n organizationId: record.organizationId,\n tenantId: record.tenantId,\n },\n events: currencyCrudEvents,\n })\n\n return { currencyId: record.id }\n },\n buildLog: async ({ snapshots, result }) => {\n const before = snapshots.before as CurrencySnapshot | undefined\n if (!before) return null\n const { translate } = await resolveTranslations()\n return {\n actionLabel: translate('currencies.audit.delete', 'Delete currency'),\n resourceKind: 'currencies.currency',\n resourceId: before.id,\n tenantId: before.tenantId,\n organizationId: before.organizationId,\n snapshotBefore: before,\n payload: { undo: { before } },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const payload = extractUndoPayload<CurrencyUndoPayload>(logEntry)\n const before = payload?.before ?? null\n if (!before) return\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const record = await em.findOne(Currency, { id: before.id })\n if (!record) return\n record.deletedAt = null\n record.isActive = before.isActive\n record.updatedAt = new Date()\n await em.flush()\n },\n}\n\nregisterCommand(createCurrencyCommand)\nregisterCommand(updateCurrencyCommand)\nregisterCommand(deleteCurrencyCommand)\n\nexport { createCurrencyCommand, updateCurrencyCommand, deleteCurrencyCommand }\n"],
|
|
5
|
-
"mappings": "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
|
|
4
|
+
"sourcesContent": ["import { registerCommand } from '@open-mercato/shared/lib/commands'\nimport type { CommandHandler } from '@open-mercato/shared/lib/commands'\nimport { buildChanges, requireId, emitCrudSideEffects } from '@open-mercato/shared/lib/commands/helpers'\nimport { extractUndoPayload, type UndoPayload } from '@open-mercato/shared/lib/commands/undo'\nimport { withAtomicFlush } from '@open-mercato/shared/lib/commands/flush'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { CrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport { Currency, ExchangeRate } from '../data/entities'\nimport {\n currencyCreateSchema,\n currencyUpdateSchema,\n currencyDeleteSchema,\n type CurrencyCreateInput,\n type CurrencyUpdateInput,\n type CurrencyDeleteInput,\n} from '../data/validators'\nimport type { CrudEventsConfig } from '@open-mercato/shared/lib/crud/types'\nimport type { DataEngine } from '@open-mercato/shared/lib/data/engine'\n\nconst currencyCrudEvents: CrudEventsConfig = {\n module: 'currencies',\n entity: 'currency',\n persistent: true,\n buildPayload: (ctx) => ({\n id: ctx.identifiers.id,\n organizationId: ctx.identifiers.organizationId,\n tenantId: ctx.identifiers.tenantId,\n }),\n}\n\ntype CurrencySnapshot = {\n id: string\n organizationId: string\n tenantId: string\n code: string\n name: string\n symbol: string | null\n decimalPlaces: number\n thousandsSeparator: string | null\n decimalSeparator: string | null\n isBase: boolean\n isActive: boolean\n createdAt: string\n updatedAt: string\n}\n\ntype CurrencyUndoPayload = UndoPayload<CurrencySnapshot>\n\nasync function loadCurrencySnapshot(em: EntityManager, id: string): Promise<CurrencySnapshot | null> {\n const record = await em.findOne(Currency, { id })\n if (!record) return null\n return {\n id: record.id,\n organizationId: record.organizationId,\n tenantId: record.tenantId,\n code: record.code,\n name: record.name,\n symbol: record.symbol ?? null,\n decimalPlaces: record.decimalPlaces,\n thousandsSeparator: record.thousandsSeparator ?? null,\n decimalSeparator: record.decimalSeparator ?? null,\n isBase: !!record.isBase,\n isActive: !!record.isActive,\n createdAt: record.createdAt.toISOString(),\n updatedAt: record.updatedAt.toISOString(),\n }\n}\n\nasync function enforceBaseCurrency(\n em: EntityManager,\n currencyId: string,\n organizationId: string,\n tenantId: string\n): Promise<void> {\n await em.nativeUpdate(\n Currency,\n {\n organizationId,\n tenantId,\n id: { $ne: currencyId },\n isBase: true,\n deletedAt: null,\n },\n { isBase: false, updatedAt: new Date() }\n )\n}\n\nconst createCurrencyCommand: CommandHandler<CurrencyCreateInput, { currencyId: string }> = {\n id: 'currencies.currencies.create',\n async execute(input, ctx) {\n const parsed = currencyCreateSchema.parse(input)\n\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n\n // Check for duplicate code\n const existing = await em.findOne(Currency, {\n code: parsed.code,\n organizationId: parsed.organizationId,\n tenantId: parsed.tenantId,\n deletedAt: null,\n })\n if (existing) {\n throw new CrudHttpError(400, { error: 'Currency code already exists for this organization.' })\n }\n\n const now = new Date()\n const record = em.create(Currency, {\n organizationId: parsed.organizationId,\n tenantId: parsed.tenantId,\n code: parsed.code,\n name: parsed.name,\n symbol: parsed.symbol ?? null,\n decimalPlaces: parsed.decimalPlaces ?? 2,\n thousandsSeparator: parsed.thousandsSeparator ?? null,\n decimalSeparator: parsed.decimalSeparator ?? null,\n isBase: parsed.isBase ?? false,\n isActive: parsed.isActive !== false,\n createdAt: now,\n updatedAt: now,\n })\n em.persist(record)\n\n // Demote any existing base currency and insert the new record in one\n // transaction; a partial commit would leave zero or two base currencies.\n await withAtomicFlush(\n em,\n [\n () =>\n record.isBase\n ? enforceBaseCurrency(em, record.id, record.organizationId, record.tenantId)\n : undefined,\n ],\n { transaction: true },\n )\n\n const de = ctx.container.resolve('dataEngine') as DataEngine\n await emitCrudSideEffects({\n dataEngine: de,\n action: 'created',\n entity: record,\n identifiers: {\n id: record.id,\n organizationId: record.organizationId,\n tenantId: record.tenantId,\n },\n events: currencyCrudEvents,\n })\n\n return { currencyId: record.id }\n },\n captureAfter: async (_input, result, ctx) => {\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n return loadCurrencySnapshot(em, result.currencyId)\n },\n buildLog: async ({ snapshots }) => {\n const after = snapshots.after as CurrencySnapshot | undefined\n if (!after) return null\n const { translate } = await resolveTranslations()\n return {\n actionLabel: translate('currencies.audit.create', 'Create currency'),\n resourceKind: 'currencies.currency',\n resourceId: after.id,\n tenantId: after.tenantId,\n organizationId: after.organizationId,\n snapshotAfter: after,\n payload: { undo: { after } },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const payload = extractUndoPayload<CurrencyUndoPayload>(logEntry)\n const after = payload?.after ?? null\n if (!after) return\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const record = await em.findOne(Currency, { id: after.id })\n if (!record) return\n record.deletedAt = new Date()\n record.isActive = false\n await em.flush()\n },\n}\n\nconst updateCurrencyCommand: CommandHandler<CurrencyUpdateInput, { currencyId: string }> = {\n id: 'currencies.currencies.update',\n async prepare(input, ctx) {\n requireId(input.id, 'Currency ID is required')\n const em = ctx.container.resolve('em') as EntityManager\n const before = await loadCurrencySnapshot(em, input.id)\n return { before }\n },\n async execute(input, ctx) {\n const parsed = currencyUpdateSchema.parse(input)\n requireId(parsed.id, 'Currency ID is required')\n\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const record = await em.findOne(Currency, { id: parsed.id, deletedAt: null })\n if (!record) {\n throw new CrudHttpError(404, { error: 'Currency not found' })\n }\n\n // Check code uniqueness if changing code\n if (parsed.code && parsed.code !== record.code) {\n const existing = await em.findOne(Currency, {\n code: parsed.code,\n organizationId: record.organizationId,\n tenantId: record.tenantId,\n id: { $ne: record.id },\n deletedAt: null,\n })\n if (existing) {\n throw new CrudHttpError(400, { error: 'Currency code already exists for this organization.' })\n }\n }\n\n const allChanges = buildChanges(record as unknown as Record<string, unknown>, parsed, [\n 'code',\n 'name',\n 'symbol',\n 'decimalPlaces',\n 'thousandsSeparator',\n 'decimalSeparator',\n 'isBase',\n 'isActive',\n ])\n const changes = Object.fromEntries(\n Object.entries(allChanges).filter(([, c]) => c.to !== undefined),\n ) as Record<string, { from: unknown; to: unknown }>\n\n if (Object.keys(changes).length === 0) {\n return { currencyId: record.id }\n }\n\n // Demote any existing base currency and persist the scalar changes in one\n // transaction; a partial commit would leave zero or two base currencies.\n // The scalar mutations live in the first phase so the per-phase flush issues\n // the pending changeset on the managed `record` before `enforceBaseCurrency`\n // runs its interleaved `nativeUpdate` (which would otherwise drop the pending\n // UPDATE under v7).\n await withAtomicFlush(\n em,\n [\n () => {\n for (const [key, change] of Object.entries(changes)) {\n ;(record as any)[key] = change.to\n }\n record.updatedAt = new Date()\n },\n () =>\n parsed.isBase === true && record.isBase\n ? enforceBaseCurrency(em, record.id, record.organizationId, record.tenantId)\n : undefined,\n ],\n { transaction: true },\n )\n\n const de = ctx.container.resolve('dataEngine') as DataEngine\n await emitCrudSideEffects({\n dataEngine: de,\n action: 'updated',\n entity: record,\n identifiers: {\n id: record.id,\n organizationId: record.organizationId,\n tenantId: record.tenantId,\n },\n events: currencyCrudEvents,\n })\n\n return { currencyId: record.id }\n },\n captureAfter: async (_input, result, ctx) => {\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n return loadCurrencySnapshot(em, result.currencyId)\n },\n buildLog: async ({ snapshots, result }) => {\n const before = snapshots.before as CurrencySnapshot | undefined\n const after = snapshots.after as CurrencySnapshot | undefined\n if (!after) return null\n const { translate } = await resolveTranslations()\n return {\n actionLabel: translate('currencies.audit.update', 'Update currency'),\n resourceKind: 'currencies.currency',\n resourceId: after.id,\n tenantId: after.tenantId,\n organizationId: after.organizationId,\n snapshotBefore: before ?? undefined,\n snapshotAfter: after,\n payload: { undo: { before, after } },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const payload = extractUndoPayload<CurrencyUndoPayload>(logEntry)\n const before = payload?.before ?? null\n if (!before) return\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const record = await em.findOne(Currency, { id: before.id })\n if (!record) return\n Object.assign(record, {\n code: before.code,\n name: before.name,\n symbol: before.symbol,\n decimalPlaces: before.decimalPlaces,\n thousandsSeparator: before.thousandsSeparator,\n decimalSeparator: before.decimalSeparator,\n isBase: before.isBase,\n isActive: before.isActive,\n updatedAt: new Date(),\n })\n await em.flush()\n },\n}\n\nconst deleteCurrencyCommand: CommandHandler<CurrencyDeleteInput, { currencyId: string }> = {\n id: 'currencies.currencies.delete',\n async prepare(input, ctx) {\n requireId(input.id, 'Currency ID is required')\n const em = ctx.container.resolve('em') as EntityManager\n const before = await loadCurrencySnapshot(em, input.id)\n return { before }\n },\n async execute(input, ctx) {\n const parsed = currencyDeleteSchema.parse(input)\n requireId(parsed.id, 'Currency ID is required')\n\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const record = await em.findOne(Currency, { id: parsed.id, deletedAt: null })\n if (!record) {\n throw new CrudHttpError(404, { error: 'Currency not found' })\n }\n\n // Prevent deleting base currency\n if (record.isBase) {\n throw new CrudHttpError(400, { error: 'Cannot delete the base currency' })\n }\n\n // Prevent deleting currency with active exchange rates\n const activeRatesCount = await em.count(ExchangeRate, {\n $or: [\n { fromCurrencyCode: record.code },\n { toCurrencyCode: record.code },\n ],\n organizationId: record.organizationId,\n tenantId: record.tenantId,\n deletedAt: null,\n isActive: true,\n })\n \n if (activeRatesCount > 0) {\n throw new CrudHttpError(400, { \n error: `Cannot delete currency ${record.code} because it has ${activeRatesCount} active exchange rate(s). Please delete or deactivate the exchange rates first.` \n })\n }\n\n record.deletedAt = new Date()\n record.isActive = false\n await em.flush()\n\n const de = ctx.container.resolve('dataEngine') as DataEngine\n await emitCrudSideEffects({\n dataEngine: de,\n action: 'deleted',\n entity: record,\n identifiers: {\n id: record.id,\n organizationId: record.organizationId,\n tenantId: record.tenantId,\n },\n events: currencyCrudEvents,\n })\n\n return { currencyId: record.id }\n },\n buildLog: async ({ snapshots, result }) => {\n const before = snapshots.before as CurrencySnapshot | undefined\n if (!before) return null\n const { translate } = await resolveTranslations()\n return {\n actionLabel: translate('currencies.audit.delete', 'Delete currency'),\n resourceKind: 'currencies.currency',\n resourceId: before.id,\n tenantId: before.tenantId,\n organizationId: before.organizationId,\n snapshotBefore: before,\n payload: { undo: { before } },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const payload = extractUndoPayload<CurrencyUndoPayload>(logEntry)\n const before = payload?.before ?? null\n if (!before) return\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const record = await em.findOne(Currency, { id: before.id })\n if (!record) return\n record.deletedAt = null\n record.isActive = before.isActive\n record.updatedAt = new Date()\n await em.flush()\n },\n}\n\nregisterCommand(createCurrencyCommand)\nregisterCommand(updateCurrencyCommand)\nregisterCommand(deleteCurrencyCommand)\n\nexport { createCurrencyCommand, updateCurrencyCommand, deleteCurrencyCommand }\n"],
|
|
5
|
+
"mappings": "AAAA,SAAS,uBAAuB;AAEhC,SAAS,cAAc,WAAW,2BAA2B;AAC7D,SAAS,0BAA4C;AACrD,SAAS,uBAAuB;AAEhC,SAAS,qBAAqB;AAC9B,SAAS,2BAA2B;AACpC,SAAS,UAAU,oBAAoB;AACvC;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,OAIK;AAIP,MAAM,qBAAuC;AAAA,EAC3C,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,YAAY;AAAA,EACZ,cAAc,CAAC,SAAS;AAAA,IACtB,IAAI,IAAI,YAAY;AAAA,IACpB,gBAAgB,IAAI,YAAY;AAAA,IAChC,UAAU,IAAI,YAAY;AAAA,EAC5B;AACF;AAoBA,eAAe,qBAAqB,IAAmB,IAA8C;AACnG,QAAM,SAAS,MAAM,GAAG,QAAQ,UAAU,EAAE,GAAG,CAAC;AAChD,MAAI,CAAC,OAAQ,QAAO;AACpB,SAAO;AAAA,IACL,IAAI,OAAO;AAAA,IACX,gBAAgB,OAAO;AAAA,IACvB,UAAU,OAAO;AAAA,IACjB,MAAM,OAAO;AAAA,IACb,MAAM,OAAO;AAAA,IACb,QAAQ,OAAO,UAAU;AAAA,IACzB,eAAe,OAAO;AAAA,IACtB,oBAAoB,OAAO,sBAAsB;AAAA,IACjD,kBAAkB,OAAO,oBAAoB;AAAA,IAC7C,QAAQ,CAAC,CAAC,OAAO;AAAA,IACjB,UAAU,CAAC,CAAC,OAAO;AAAA,IACnB,WAAW,OAAO,UAAU,YAAY;AAAA,IACxC,WAAW,OAAO,UAAU,YAAY;AAAA,EAC1C;AACF;AAEA,eAAe,oBACb,IACA,YACA,gBACA,UACe;AACf,QAAM,GAAG;AAAA,IACP;AAAA,IACA;AAAA,MACE;AAAA,MACA;AAAA,MACA,IAAI,EAAE,KAAK,WAAW;AAAA,MACtB,QAAQ;AAAA,MACR,WAAW;AAAA,IACb;AAAA,IACA,EAAE,QAAQ,OAAO,WAAW,oBAAI,KAAK,EAAE;AAAA,EACzC;AACF;AAEA,MAAM,wBAAqF;AAAA,EACzF,IAAI;AAAA,EACJ,MAAM,QAAQ,OAAO,KAAK;AACxB,UAAM,SAAS,qBAAqB,MAAM,KAAK;AAE/C,UAAM,KAAM,IAAI,UAAU,QAAQ,IAAI,EAAoB,KAAK;AAG/D,UAAM,WAAW,MAAM,GAAG,QAAQ,UAAU;AAAA,MAC1C,MAAM,OAAO;AAAA,MACb,gBAAgB,OAAO;AAAA,MACvB,UAAU,OAAO;AAAA,MACjB,WAAW;AAAA,IACb,CAAC;AACD,QAAI,UAAU;AACZ,YAAM,IAAI,cAAc,KAAK,EAAE,OAAO,sDAAsD,CAAC;AAAA,IAC/F;AAEA,UAAM,MAAM,oBAAI,KAAK;AACrB,UAAM,SAAS,GAAG,OAAO,UAAU;AAAA,MACjC,gBAAgB,OAAO;AAAA,MACvB,UAAU,OAAO;AAAA,MACjB,MAAM,OAAO;AAAA,MACb,MAAM,OAAO;AAAA,MACb,QAAQ,OAAO,UAAU;AAAA,MACzB,eAAe,OAAO,iBAAiB;AAAA,MACvC,oBAAoB,OAAO,sBAAsB;AAAA,MACjD,kBAAkB,OAAO,oBAAoB;AAAA,MAC7C,QAAQ,OAAO,UAAU;AAAA,MACzB,UAAU,OAAO,aAAa;AAAA,MAC9B,WAAW;AAAA,MACX,WAAW;AAAA,IACb,CAAC;AACD,OAAG,QAAQ,MAAM;AAIjB,UAAM;AAAA,MACJ;AAAA,MACA;AAAA,QACE,MACE,OAAO,SACH,oBAAoB,IAAI,OAAO,IAAI,OAAO,gBAAgB,OAAO,QAAQ,IACzE;AAAA,MACR;AAAA,MACA,EAAE,aAAa,KAAK;AAAA,IACtB;AAEA,UAAM,KAAK,IAAI,UAAU,QAAQ,YAAY;AAC7C,UAAM,oBAAoB;AAAA,MACxB,YAAY;AAAA,MACZ,QAAQ;AAAA,MACR,QAAQ;AAAA,MACR,aAAa;AAAA,QACX,IAAI,OAAO;AAAA,QACX,gBAAgB,OAAO;AAAA,QACvB,UAAU,OAAO;AAAA,MACnB;AAAA,MACA,QAAQ;AAAA,IACV,CAAC;AAED,WAAO,EAAE,YAAY,OAAO,GAAG;AAAA,EACjC;AAAA,EACA,cAAc,OAAO,QAAQ,QAAQ,QAAQ;AAC3C,UAAM,KAAM,IAAI,UAAU,QAAQ,IAAI,EAAoB,KAAK;AAC/D,WAAO,qBAAqB,IAAI,OAAO,UAAU;AAAA,EACnD;AAAA,EACA,UAAU,OAAO,EAAE,UAAU,MAAM;AACjC,UAAM,QAAQ,UAAU;AACxB,QAAI,CAAC,MAAO,QAAO;AACnB,UAAM,EAAE,UAAU,IAAI,MAAM,oBAAoB;AAChD,WAAO;AAAA,MACL,aAAa,UAAU,2BAA2B,iBAAiB;AAAA,MACnE,cAAc;AAAA,MACd,YAAY,MAAM;AAAA,MAClB,UAAU,MAAM;AAAA,MAChB,gBAAgB,MAAM;AAAA,MACtB,eAAe;AAAA,MACf,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE;AAAA,IAC7B;AAAA,EACF;AAAA,EACA,MAAM,OAAO,EAAE,UAAU,IAAI,MAAM;AACjC,UAAM,UAAU,mBAAwC,QAAQ;AAChE,UAAM,QAAQ,SAAS,SAAS;AAChC,QAAI,CAAC,MAAO;AACZ,UAAM,KAAM,IAAI,UAAU,QAAQ,IAAI,EAAoB,KAAK;AAC/D,UAAM,SAAS,MAAM,GAAG,QAAQ,UAAU,EAAE,IAAI,MAAM,GAAG,CAAC;AAC1D,QAAI,CAAC,OAAQ;AACb,WAAO,YAAY,oBAAI,KAAK;AAC5B,WAAO,WAAW;AAClB,UAAM,GAAG,MAAM;AAAA,EACjB;AACF;AAEA,MAAM,wBAAqF;AAAA,EACzF,IAAI;AAAA,EACJ,MAAM,QAAQ,OAAO,KAAK;AACxB,cAAU,MAAM,IAAI,yBAAyB;AAC7C,UAAM,KAAK,IAAI,UAAU,QAAQ,IAAI;AACrC,UAAM,SAAS,MAAM,qBAAqB,IAAI,MAAM,EAAE;AACtD,WAAO,EAAE,OAAO;AAAA,EAClB;AAAA,EACA,MAAM,QAAQ,OAAO,KAAK;AACxB,UAAM,SAAS,qBAAqB,MAAM,KAAK;AAC/C,cAAU,OAAO,IAAI,yBAAyB;AAE9C,UAAM,KAAM,IAAI,UAAU,QAAQ,IAAI,EAAoB,KAAK;AAC/D,UAAM,SAAS,MAAM,GAAG,QAAQ,UAAU,EAAE,IAAI,OAAO,IAAI,WAAW,KAAK,CAAC;AAC5E,QAAI,CAAC,QAAQ;AACX,YAAM,IAAI,cAAc,KAAK,EAAE,OAAO,qBAAqB,CAAC;AAAA,IAC9D;AAGA,QAAI,OAAO,QAAQ,OAAO,SAAS,OAAO,MAAM;AAC9C,YAAM,WAAW,MAAM,GAAG,QAAQ,UAAU;AAAA,QAC1C,MAAM,OAAO;AAAA,QACb,gBAAgB,OAAO;AAAA,QACvB,UAAU,OAAO;AAAA,QACjB,IAAI,EAAE,KAAK,OAAO,GAAG;AAAA,QACrB,WAAW;AAAA,MACb,CAAC;AACD,UAAI,UAAU;AACZ,cAAM,IAAI,cAAc,KAAK,EAAE,OAAO,sDAAsD,CAAC;AAAA,MAC/F;AAAA,IACF;AAEA,UAAM,aAAa,aAAa,QAA8C,QAAQ;AAAA,MACpF;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF,CAAC;AACD,UAAM,UAAU,OAAO;AAAA,MACrB,OAAO,QAAQ,UAAU,EAAE,OAAO,CAAC,CAAC,EAAE,CAAC,MAAM,EAAE,OAAO,MAAS;AAAA,IACjE;AAEA,QAAI,OAAO,KAAK,OAAO,EAAE,WAAW,GAAG;AACrC,aAAO,EAAE,YAAY,OAAO,GAAG;AAAA,IACjC;AAQA,UAAM;AAAA,MACJ;AAAA,MACA;AAAA,QACE,MAAM;AACJ,qBAAW,CAAC,KAAK,MAAM,KAAK,OAAO,QAAQ,OAAO,GAAG;AACnD;AAAC,YAAC,OAAe,GAAG,IAAI,OAAO;AAAA,UACjC;AACA,iBAAO,YAAY,oBAAI,KAAK;AAAA,QAC9B;AAAA,QACA,MACE,OAAO,WAAW,QAAQ,OAAO,SAC7B,oBAAoB,IAAI,OAAO,IAAI,OAAO,gBAAgB,OAAO,QAAQ,IACzE;AAAA,MACR;AAAA,MACA,EAAE,aAAa,KAAK;AAAA,IACtB;AAEA,UAAM,KAAK,IAAI,UAAU,QAAQ,YAAY;AAC7C,UAAM,oBAAoB;AAAA,MACxB,YAAY;AAAA,MACZ,QAAQ;AAAA,MACR,QAAQ;AAAA,MACR,aAAa;AAAA,QACX,IAAI,OAAO;AAAA,QACX,gBAAgB,OAAO;AAAA,QACvB,UAAU,OAAO;AAAA,MACnB;AAAA,MACA,QAAQ;AAAA,IACV,CAAC;AAED,WAAO,EAAE,YAAY,OAAO,GAAG;AAAA,EACjC;AAAA,EACA,cAAc,OAAO,QAAQ,QAAQ,QAAQ;AAC3C,UAAM,KAAM,IAAI,UAAU,QAAQ,IAAI,EAAoB,KAAK;AAC/D,WAAO,qBAAqB,IAAI,OAAO,UAAU;AAAA,EACnD;AAAA,EACA,UAAU,OAAO,EAAE,WAAW,OAAO,MAAM;AACzC,UAAM,SAAS,UAAU;AACzB,UAAM,QAAQ,UAAU;AACxB,QAAI,CAAC,MAAO,QAAO;AACnB,UAAM,EAAE,UAAU,IAAI,MAAM,oBAAoB;AAChD,WAAO;AAAA,MACL,aAAa,UAAU,2BAA2B,iBAAiB;AAAA,MACnE,cAAc;AAAA,MACd,YAAY,MAAM;AAAA,MAClB,UAAU,MAAM;AAAA,MAChB,gBAAgB,MAAM;AAAA,MACtB,gBAAgB,UAAU;AAAA,MAC1B,eAAe;AAAA,MACf,SAAS,EAAE,MAAM,EAAE,QAAQ,MAAM,EAAE;AAAA,IACrC;AAAA,EACF;AAAA,EACA,MAAM,OAAO,EAAE,UAAU,IAAI,MAAM;AACjC,UAAM,UAAU,mBAAwC,QAAQ;AAChE,UAAM,SAAS,SAAS,UAAU;AAClC,QAAI,CAAC,OAAQ;AACb,UAAM,KAAM,IAAI,UAAU,QAAQ,IAAI,EAAoB,KAAK;AAC/D,UAAM,SAAS,MAAM,GAAG,QAAQ,UAAU,EAAE,IAAI,OAAO,GAAG,CAAC;AAC3D,QAAI,CAAC,OAAQ;AACb,WAAO,OAAO,QAAQ;AAAA,MACpB,MAAM,OAAO;AAAA,MACb,MAAM,OAAO;AAAA,MACb,QAAQ,OAAO;AAAA,MACf,eAAe,OAAO;AAAA,MACtB,oBAAoB,OAAO;AAAA,MAC3B,kBAAkB,OAAO;AAAA,MACzB,QAAQ,OAAO;AAAA,MACf,UAAU,OAAO;AAAA,MACjB,WAAW,oBAAI,KAAK;AAAA,IACtB,CAAC;AACD,UAAM,GAAG,MAAM;AAAA,EACjB;AACF;AAEA,MAAM,wBAAqF;AAAA,EACzF,IAAI;AAAA,EACJ,MAAM,QAAQ,OAAO,KAAK;AACxB,cAAU,MAAM,IAAI,yBAAyB;AAC7C,UAAM,KAAK,IAAI,UAAU,QAAQ,IAAI;AACrC,UAAM,SAAS,MAAM,qBAAqB,IAAI,MAAM,EAAE;AACtD,WAAO,EAAE,OAAO;AAAA,EAClB;AAAA,EACA,MAAM,QAAQ,OAAO,KAAK;AACxB,UAAM,SAAS,qBAAqB,MAAM,KAAK;AAC/C,cAAU,OAAO,IAAI,yBAAyB;AAE9C,UAAM,KAAM,IAAI,UAAU,QAAQ,IAAI,EAAoB,KAAK;AAC/D,UAAM,SAAS,MAAM,GAAG,QAAQ,UAAU,EAAE,IAAI,OAAO,IAAI,WAAW,KAAK,CAAC;AAC5E,QAAI,CAAC,QAAQ;AACX,YAAM,IAAI,cAAc,KAAK,EAAE,OAAO,qBAAqB,CAAC;AAAA,IAC9D;AAGA,QAAI,OAAO,QAAQ;AACjB,YAAM,IAAI,cAAc,KAAK,EAAE,OAAO,kCAAkC,CAAC;AAAA,IAC3E;AAGA,UAAM,mBAAmB,MAAM,GAAG,MAAM,cAAc;AAAA,MACpD,KAAK;AAAA,QACH,EAAE,kBAAkB,OAAO,KAAK;AAAA,QAChC,EAAE,gBAAgB,OAAO,KAAK;AAAA,MAChC;AAAA,MACA,gBAAgB,OAAO;AAAA,MACvB,UAAU,OAAO;AAAA,MACjB,WAAW;AAAA,MACX,UAAU;AAAA,IACZ,CAAC;AAED,QAAI,mBAAmB,GAAG;AACxB,YAAM,IAAI,cAAc,KAAK;AAAA,QAC3B,OAAO,0BAA0B,OAAO,IAAI,mBAAmB,gBAAgB;AAAA,MACjF,CAAC;AAAA,IACH;AAEA,WAAO,YAAY,oBAAI,KAAK;AAC5B,WAAO,WAAW;AAClB,UAAM,GAAG,MAAM;AAEf,UAAM,KAAK,IAAI,UAAU,QAAQ,YAAY;AAC7C,UAAM,oBAAoB;AAAA,MACxB,YAAY;AAAA,MACZ,QAAQ;AAAA,MACR,QAAQ;AAAA,MACR,aAAa;AAAA,QACX,IAAI,OAAO;AAAA,QACX,gBAAgB,OAAO;AAAA,QACvB,UAAU,OAAO;AAAA,MACnB;AAAA,MACA,QAAQ;AAAA,IACV,CAAC;AAED,WAAO,EAAE,YAAY,OAAO,GAAG;AAAA,EACjC;AAAA,EACA,UAAU,OAAO,EAAE,WAAW,OAAO,MAAM;AACzC,UAAM,SAAS,UAAU;AACzB,QAAI,CAAC,OAAQ,QAAO;AACpB,UAAM,EAAE,UAAU,IAAI,MAAM,oBAAoB;AAChD,WAAO;AAAA,MACL,aAAa,UAAU,2BAA2B,iBAAiB;AAAA,MACnE,cAAc;AAAA,MACd,YAAY,OAAO;AAAA,MACnB,UAAU,OAAO;AAAA,MACjB,gBAAgB,OAAO;AAAA,MACvB,gBAAgB;AAAA,MAChB,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE;AAAA,IAC9B;AAAA,EACF;AAAA,EACA,MAAM,OAAO,EAAE,UAAU,IAAI,MAAM;AACjC,UAAM,UAAU,mBAAwC,QAAQ;AAChE,UAAM,SAAS,SAAS,UAAU;AAClC,QAAI,CAAC,OAAQ;AACb,UAAM,KAAM,IAAI,UAAU,QAAQ,IAAI,EAAoB,KAAK;AAC/D,UAAM,SAAS,MAAM,GAAG,QAAQ,UAAU,EAAE,IAAI,OAAO,GAAG,CAAC;AAC3D,QAAI,CAAC,OAAQ;AACb,WAAO,YAAY;AACnB,WAAO,WAAW,OAAO;AACzB,WAAO,YAAY,oBAAI,KAAK;AAC5B,UAAM,GAAG,MAAM;AAAA,EACjB;AACF;AAEA,gBAAgB,qBAAqB;AACrC,gBAAgB,qBAAqB;AACrC,gBAAgB,qBAAqB;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
3
3
|
import { useState, useEffect, useCallback, useMemo } from "react";
|
|
4
4
|
import { flash } from "@open-mercato/ui/backend/FlashMessages";
|
|
5
|
-
import { apiCall } from "@open-mercato/ui/backend/utils/apiCall";
|
|
5
|
+
import { apiCall, withScopedApiRequestHeaders } from "@open-mercato/ui/backend/utils/apiCall";
|
|
6
|
+
import { buildOptimisticLockHeader } from "@open-mercato/ui/backend/utils/optimisticLock";
|
|
6
7
|
import { Button } from "@open-mercato/ui/primitives/button";
|
|
7
8
|
import { Spinner } from "@open-mercato/ui/primitives/spinner";
|
|
8
9
|
import { Switch } from "@open-mercato/ui/primitives/switch";
|
|
@@ -67,16 +68,19 @@ function CurrencyFetchingConfig() {
|
|
|
67
68
|
setLoading(false);
|
|
68
69
|
}
|
|
69
70
|
}, [availableProviders, initializeMissingProviders, t]);
|
|
70
|
-
const toggleEnabled = useCallback(async (configId, currentValue) => {
|
|
71
|
+
const toggleEnabled = useCallback(async (configId, currentValue, updatedAt) => {
|
|
71
72
|
try {
|
|
72
|
-
const { result } = await
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
73
|
+
const { result } = await withScopedApiRequestHeaders(
|
|
74
|
+
buildOptimisticLockHeader(updatedAt),
|
|
75
|
+
() => apiCall("/api/currencies/fetch-configs", {
|
|
76
|
+
method: "PUT",
|
|
77
|
+
headers: { "Content-Type": "application/json" },
|
|
78
|
+
body: JSON.stringify({
|
|
79
|
+
id: configId,
|
|
80
|
+
isEnabled: !currentValue
|
|
81
|
+
})
|
|
78
82
|
})
|
|
79
|
-
|
|
83
|
+
);
|
|
80
84
|
if (result?.config) {
|
|
81
85
|
setConfigs(
|
|
82
86
|
(prev) => prev.map((c) => c.id === configId ? result.config : c)
|
|
@@ -90,16 +94,19 @@ function CurrencyFetchingConfig() {
|
|
|
90
94
|
flash(err.message || t("currencies.fetch.error_update_config"), "error");
|
|
91
95
|
}
|
|
92
96
|
}, [t]);
|
|
93
|
-
const updateSyncTime = useCallback(async (configId, syncTime) => {
|
|
97
|
+
const updateSyncTime = useCallback(async (configId, syncTime, updatedAt) => {
|
|
94
98
|
try {
|
|
95
|
-
const { result } = await
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
99
|
+
const { result } = await withScopedApiRequestHeaders(
|
|
100
|
+
buildOptimisticLockHeader(updatedAt),
|
|
101
|
+
() => apiCall("/api/currencies/fetch-configs", {
|
|
102
|
+
method: "PUT",
|
|
103
|
+
headers: { "Content-Type": "application/json" },
|
|
104
|
+
body: JSON.stringify({
|
|
105
|
+
id: configId,
|
|
106
|
+
syncTime: syncTime || null
|
|
107
|
+
})
|
|
101
108
|
})
|
|
102
|
-
|
|
109
|
+
);
|
|
103
110
|
if (result?.config) {
|
|
104
111
|
setConfigs(
|
|
105
112
|
(prev) => prev.map((c) => c.id === configId ? result.config : c)
|
|
@@ -197,7 +204,7 @@ function CurrencyFetchingConfig() {
|
|
|
197
204
|
Switch,
|
|
198
205
|
{
|
|
199
206
|
checked: config.isEnabled,
|
|
200
|
-
onCheckedChange: () => toggleEnabled(config.id, config.isEnabled)
|
|
207
|
+
onCheckedChange: () => toggleEnabled(config.id, config.isEnabled, config.updatedAt)
|
|
201
208
|
}
|
|
202
209
|
)
|
|
203
210
|
] }),
|
|
@@ -213,7 +220,7 @@ function CurrencyFetchingConfig() {
|
|
|
213
220
|
{
|
|
214
221
|
type: "time",
|
|
215
222
|
value: config.syncTime || "09:00",
|
|
216
|
-
onChange: (e) => updateSyncTime(config.id, e.target.value),
|
|
223
|
+
onChange: (e) => updateSyncTime(config.id, e.target.value, config.updatedAt),
|
|
217
224
|
className: "rounded border bg-background px-2 py-1.5 text-sm"
|
|
218
225
|
}
|
|
219
226
|
)
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/modules/currencies/components/CurrencyFetchingConfig.tsx"],
|
|
4
|
-
"sourcesContent": ["'use client'\n\nimport { useState, useEffect, useCallback, useMemo } from 'react'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { apiCall } from '@open-mercato/ui/backend/utils/apiCall'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { Spinner } from '@open-mercato/ui/primitives/spinner'\nimport { Switch } from '@open-mercato/ui/primitives/switch'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\n\ninterface FetchConfig {\n id: string\n provider: string\n isEnabled: boolean\n syncTime: string | null\n lastSyncAt: string | null\n lastSyncStatus: string | null\n lastSyncMessage: string | null\n lastSyncCount: number | null\n}\n\nexport default function CurrencyFetchingConfig() {\n const t = useT()\n const [configs, setConfigs] = useState<FetchConfig[]>([])\n const [loading, setLoading] = useState(true)\n const [fetching, setFetching] = useState<string | null>(null)\n const [initializing, setInitializing] = useState(false)\n\n // Available providers that should be configured\n const availableProviders = useMemo(() => ['NBP', 'Raiffeisen Bank Polska'], [])\n\n const createProviderConfig = useCallback(async (provider: string) => {\n try {\n await apiCall('/api/currencies/fetch-configs', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n provider,\n isEnabled: false,\n syncTime: '09:00',\n }),\n })\n } catch (err: any) {\n // Ignore errors for duplicate providers\n if (!err.message?.includes('already configured')) {\n throw err\n }\n }\n }, [])\n\n const initializeMissingProviders = useCallback(async (providers: string[]) => {\n setInitializing(true)\n try {\n for (const provider of providers) {\n await createProviderConfig(provider)\n }\n // Reload configs after initialization\n const { result } = await apiCall('/api/currencies/fetch-configs')\n if (result?.configs) {\n setConfigs(result.configs as FetchConfig[])\n }\n } catch (err: any) {\n console.error('Failed to initialize providers:', err)\n } finally {\n setInitializing(false)\n }\n }, [createProviderConfig])\n\n const loadConfigs = useCallback(async () => {\n try {\n const { result } = await apiCall('/api/currencies/fetch-configs')\n\n if (result?.configs) {\n const existingConfigs = result.configs as FetchConfig[]\n setConfigs(existingConfigs)\n\n // Check if we need to initialize any missing providers\n const existingProviders = existingConfigs.map((c) => c.provider)\n const missingProviders = availableProviders.filter(\n (p) => !existingProviders.includes(p)\n )\n\n // Auto-initialize missing providers\n if (missingProviders.length > 0) {\n await initializeMissingProviders(missingProviders)\n }\n }\n } catch (err: any) {\n flash(err.message || t('currencies.fetch.error_load_configs'), 'error')\n } finally {\n setLoading(false)\n }\n }, [availableProviders, initializeMissingProviders, t])\n\n const toggleEnabled = useCallback(async (configId: string, currentValue: boolean) => {\n try {\n const { result } = await apiCall('/api/currencies/fetch-configs', {\n method: 'PUT',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n id: configId,\n isEnabled: !currentValue,\n }),\n })\n\n if (result?.config) {\n setConfigs((prev) =>\n prev.map((c) => (c.id === configId ? (result.config as FetchConfig) : c))\n )\n flash(\n currentValue\n ? t('currencies.fetch.provider_disabled')\n : t('currencies.fetch.provider_enabled'),\n 'success'\n )\n }\n } catch (err: any) {\n flash(err.message || t('currencies.fetch.error_update_config'), 'error')\n }\n }, [t])\n\n const updateSyncTime = useCallback(async (configId: string, syncTime: string) => {\n try {\n const { result } = await apiCall('/api/currencies/fetch-configs', {\n method: 'PUT',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n id: configId,\n syncTime: syncTime || null,\n }),\n })\n\n if (result?.config) {\n setConfigs((prev) =>\n prev.map((c) => (c.id === configId ? (result.config as FetchConfig) : c))\n )\n }\n } catch (err: any) {\n flash(err.message || t('currencies.fetch.error_update_sync_time'), 'error')\n }\n }, [t])\n\n const fetchNow = useCallback(async (provider: string) => {\n setFetching(provider)\n\n try {\n const { result } = await apiCall('/api/currencies/fetch-rates', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n providers: [provider],\n }),\n })\n\n if (result) {\n const byProvider = result.byProvider as Record<string, { count: number; errors?: string[] }>\n const count = byProvider?.[provider]?.count || 0\n\n if (count === 0) {\n flash(t('currencies.fetch.sync_no_rates'), 'warning')\n } else {\n flash(`${t('currencies.fetch.sync_success')}: ${count} rates fetched`, 'success')\n }\n await loadConfigs()\n }\n } catch (err: any) {\n flash(err.message || t('currencies.fetch.sync_error'), 'error')\n } finally {\n setFetching(null)\n }\n }, [t, loadConfigs])\n\n useEffect(() => {\n loadConfigs()\n }, [loadConfigs])\n\n function formatLastSync(date: string | null): string {\n if (!date) return 'Never'\n return new Date(date).toLocaleString()\n }\n\n function getStatusBadge(status: string | null) {\n if (!status) return null\n\n const styles: Record<string, string> = {\n success: 'bg-green-100 text-green-800 px-2 py-1 rounded text-xs',\n error: 'bg-red-100 text-red-800 px-2 py-1 rounded text-xs',\n partial: 'bg-yellow-100 text-yellow-800 px-2 py-1 rounded text-xs',\n }\n\n return (\n <span className={styles[status] || styles.error}>\n {status}\n </span>\n )\n }\n\n function getProviderName(provider: string): string {\n if (provider === 'NBP') return t('currencies.fetch.provider_nbp')\n if (provider === 'Raiffeisen Bank Polska') return t('currencies.fetch.provider_raiffeisen')\n return provider\n }\n\n function getProviderDescription(provider: string): string {\n if (provider === 'NBP') {\n return t('currencies.fetch.provider_nbp_description')\n }\n if (provider === 'Raiffeisen Bank Polska') {\n return t('currencies.fetch.provider_raiffeisen_description')\n }\n return ''\n }\n\n if (loading || initializing) {\n return (\n <section className=\"space-y-3 rounded-lg border bg-background p-4\">\n <header className=\"space-y-2\">\n <h2 className=\"text-xl font-semibold\">{t('currencies.fetch.title')}</h2>\n <p className=\"text-sm text-muted-foreground\">\n {t('currencies.fetch.description')}\n </p>\n </header>\n <div className=\"flex items-center gap-2 text-sm text-muted-foreground\">\n <Spinner className=\"h-4 w-4\" />\n {initializing ? t('currencies.fetch.initializing_providers') : t('currencies.fetch.loading')}\n </div>\n </section>\n )\n }\n\n return (\n <section className=\"space-y-6 rounded-lg border bg-background p-4\">\n <header className=\"space-y-2\">\n <h2 className=\"text-xl font-semibold\">{t('currencies.fetch.title')}</h2>\n <p className=\"text-sm text-muted-foreground\">\n {t('currencies.fetch.description')}\n </p>\n </header>\n\n <div className=\"space-y-3\">\n {configs.map((config) => (\n <div\n key={config.id}\n className=\"rounded-lg border bg-card p-4\"\n >\n <div className=\"flex items-start justify-between gap-4\">\n <div className=\"flex-1\">\n <h3 className=\"text-sm font-medium\">\n {getProviderName(config.provider)}\n </h3>\n <p className=\"text-xs text-muted-foreground mt-0.5\">\n {getProviderDescription(config.provider)}\n </p>\n </div>\n\n <Switch\n checked={config.isEnabled}\n onCheckedChange={() => toggleEnabled(config.id, config.isEnabled)}\n />\n </div>\n\n {config.isEnabled && (\n <>\n <div className=\"flex items-baseline gap-3 flex-wrap mt-3\">\n <div className=\"flex items-baseline gap-2\">\n <label className=\"text-xs text-muted-foreground whitespace-nowrap\">\n {t('currencies.fetch.sync_time')}:\n </label>\n <input\n type=\"time\"\n value={config.syncTime || '09:00'}\n onChange={(e) => updateSyncTime(config.id, e.target.value)}\n className=\"rounded border bg-background px-2 py-1.5 text-sm\"\n />\n </div>\n\n <div className=\"flex items-baseline gap-2\">\n <label className=\"text-xs text-muted-foreground whitespace-nowrap\">\n {t('currencies.fetch.last_sync')}:\n </label>\n <span className=\"text-xs text-muted-foreground\">\n {formatLastSync(config.lastSyncAt)}\n </span>\n {getStatusBadge(config.lastSyncStatus)}\n </div>\n\n <Button\n onClick={() => fetchNow(config.provider)}\n disabled={fetching === config.provider}\n size=\"default\"\n className=\"ml-auto min-w-32\"\n >\n {fetching === config.provider ? (\n <div>\n <Spinner className=\"mr-1.5\" size='sm' />\n {t('currencies.fetch.fetching')}\n </div>\n ) : (\n t('currencies.fetch.fetch_now')\n )}\n </Button>\n </div>\n\n {config.lastSyncCount !== null && (\n <div className=\"text-xs text-muted-foreground mt-2\">\n {t('currencies.fetch.last_sync_count')}: <span className=\"font-medium\">{config.lastSyncCount}</span> rates\n </div>\n )}\n\n {config.lastSyncMessage && config.lastSyncStatus === 'error' && (\n <div className=\"rounded border border-red-200 bg-red-50 p-2 text-xs text-red-700 mt-2\">\n {config.lastSyncMessage}\n </div>\n )}\n </>\n )}\n </div>\n ))}\n\n {configs.length === 0 && (\n <div className=\"rounded border bg-background/80 p-6 text-center\">\n <p className=\"text-sm text-muted-foreground\">\n {t('currencies.fetch.no_providers')}\n </p>\n <Button\n onClick={() => initializeMissingProviders(availableProviders)}\n >\n {t('currencies.fetch.initialize_providers')}\n </Button>\n </div>\n )}\n </div>\n </section>\n )\n}\n"],
|
|
5
|
-
"mappings": ";
|
|
4
|
+
"sourcesContent": ["'use client'\n\nimport { useState, useEffect, useCallback, useMemo } from 'react'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { apiCall, withScopedApiRequestHeaders } from '@open-mercato/ui/backend/utils/apiCall'\nimport { buildOptimisticLockHeader } from '@open-mercato/ui/backend/utils/optimisticLock'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { Spinner } from '@open-mercato/ui/primitives/spinner'\nimport { Switch } from '@open-mercato/ui/primitives/switch'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\n\ninterface FetchConfig {\n id: string\n provider: string\n isEnabled: boolean\n syncTime: string | null\n lastSyncAt: string | null\n lastSyncStatus: string | null\n lastSyncMessage: string | null\n lastSyncCount: number | null\n updatedAt?: string | null\n}\n\nexport default function CurrencyFetchingConfig() {\n const t = useT()\n const [configs, setConfigs] = useState<FetchConfig[]>([])\n const [loading, setLoading] = useState(true)\n const [fetching, setFetching] = useState<string | null>(null)\n const [initializing, setInitializing] = useState(false)\n\n // Available providers that should be configured\n const availableProviders = useMemo(() => ['NBP', 'Raiffeisen Bank Polska'], [])\n\n const createProviderConfig = useCallback(async (provider: string) => {\n try {\n await apiCall('/api/currencies/fetch-configs', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n provider,\n isEnabled: false,\n syncTime: '09:00',\n }),\n })\n } catch (err: any) {\n // Ignore errors for duplicate providers\n if (!err.message?.includes('already configured')) {\n throw err\n }\n }\n }, [])\n\n const initializeMissingProviders = useCallback(async (providers: string[]) => {\n setInitializing(true)\n try {\n for (const provider of providers) {\n await createProviderConfig(provider)\n }\n // Reload configs after initialization\n const { result } = await apiCall('/api/currencies/fetch-configs')\n if (result?.configs) {\n setConfigs(result.configs as FetchConfig[])\n }\n } catch (err: any) {\n console.error('Failed to initialize providers:', err)\n } finally {\n setInitializing(false)\n }\n }, [createProviderConfig])\n\n const loadConfigs = useCallback(async () => {\n try {\n const { result } = await apiCall('/api/currencies/fetch-configs')\n\n if (result?.configs) {\n const existingConfigs = result.configs as FetchConfig[]\n setConfigs(existingConfigs)\n\n // Check if we need to initialize any missing providers\n const existingProviders = existingConfigs.map((c) => c.provider)\n const missingProviders = availableProviders.filter(\n (p) => !existingProviders.includes(p)\n )\n\n // Auto-initialize missing providers\n if (missingProviders.length > 0) {\n await initializeMissingProviders(missingProviders)\n }\n }\n } catch (err: any) {\n flash(err.message || t('currencies.fetch.error_load_configs'), 'error')\n } finally {\n setLoading(false)\n }\n }, [availableProviders, initializeMissingProviders, t])\n\n const toggleEnabled = useCallback(async (configId: string, currentValue: boolean, updatedAt?: string | null) => {\n try {\n const { result } = await withScopedApiRequestHeaders(\n buildOptimisticLockHeader(updatedAt),\n () =>\n apiCall('/api/currencies/fetch-configs', {\n method: 'PUT',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n id: configId,\n isEnabled: !currentValue,\n }),\n }),\n )\n\n if (result?.config) {\n setConfigs((prev) =>\n prev.map((c) => (c.id === configId ? (result.config as FetchConfig) : c))\n )\n flash(\n currentValue\n ? t('currencies.fetch.provider_disabled')\n : t('currencies.fetch.provider_enabled'),\n 'success'\n )\n }\n } catch (err: any) {\n flash(err.message || t('currencies.fetch.error_update_config'), 'error')\n }\n }, [t])\n\n const updateSyncTime = useCallback(async (configId: string, syncTime: string, updatedAt?: string | null) => {\n try {\n const { result } = await withScopedApiRequestHeaders(\n buildOptimisticLockHeader(updatedAt),\n () =>\n apiCall('/api/currencies/fetch-configs', {\n method: 'PUT',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n id: configId,\n syncTime: syncTime || null,\n }),\n }),\n )\n\n if (result?.config) {\n setConfigs((prev) =>\n prev.map((c) => (c.id === configId ? (result.config as FetchConfig) : c))\n )\n }\n } catch (err: any) {\n flash(err.message || t('currencies.fetch.error_update_sync_time'), 'error')\n }\n }, [t])\n\n const fetchNow = useCallback(async (provider: string) => {\n setFetching(provider)\n\n try {\n const { result } = await apiCall('/api/currencies/fetch-rates', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n providers: [provider],\n }),\n })\n\n if (result) {\n const byProvider = result.byProvider as Record<string, { count: number; errors?: string[] }>\n const count = byProvider?.[provider]?.count || 0\n\n if (count === 0) {\n flash(t('currencies.fetch.sync_no_rates'), 'warning')\n } else {\n flash(`${t('currencies.fetch.sync_success')}: ${count} rates fetched`, 'success')\n }\n await loadConfigs()\n }\n } catch (err: any) {\n flash(err.message || t('currencies.fetch.sync_error'), 'error')\n } finally {\n setFetching(null)\n }\n }, [t, loadConfigs])\n\n useEffect(() => {\n loadConfigs()\n }, [loadConfigs])\n\n function formatLastSync(date: string | null): string {\n if (!date) return 'Never'\n return new Date(date).toLocaleString()\n }\n\n function getStatusBadge(status: string | null) {\n if (!status) return null\n\n const styles: Record<string, string> = {\n success: 'bg-green-100 text-green-800 px-2 py-1 rounded text-xs',\n error: 'bg-red-100 text-red-800 px-2 py-1 rounded text-xs',\n partial: 'bg-yellow-100 text-yellow-800 px-2 py-1 rounded text-xs',\n }\n\n return (\n <span className={styles[status] || styles.error}>\n {status}\n </span>\n )\n }\n\n function getProviderName(provider: string): string {\n if (provider === 'NBP') return t('currencies.fetch.provider_nbp')\n if (provider === 'Raiffeisen Bank Polska') return t('currencies.fetch.provider_raiffeisen')\n return provider\n }\n\n function getProviderDescription(provider: string): string {\n if (provider === 'NBP') {\n return t('currencies.fetch.provider_nbp_description')\n }\n if (provider === 'Raiffeisen Bank Polska') {\n return t('currencies.fetch.provider_raiffeisen_description')\n }\n return ''\n }\n\n if (loading || initializing) {\n return (\n <section className=\"space-y-3 rounded-lg border bg-background p-4\">\n <header className=\"space-y-2\">\n <h2 className=\"text-xl font-semibold\">{t('currencies.fetch.title')}</h2>\n <p className=\"text-sm text-muted-foreground\">\n {t('currencies.fetch.description')}\n </p>\n </header>\n <div className=\"flex items-center gap-2 text-sm text-muted-foreground\">\n <Spinner className=\"h-4 w-4\" />\n {initializing ? t('currencies.fetch.initializing_providers') : t('currencies.fetch.loading')}\n </div>\n </section>\n )\n }\n\n return (\n <section className=\"space-y-6 rounded-lg border bg-background p-4\">\n <header className=\"space-y-2\">\n <h2 className=\"text-xl font-semibold\">{t('currencies.fetch.title')}</h2>\n <p className=\"text-sm text-muted-foreground\">\n {t('currencies.fetch.description')}\n </p>\n </header>\n\n <div className=\"space-y-3\">\n {configs.map((config) => (\n <div\n key={config.id}\n className=\"rounded-lg border bg-card p-4\"\n >\n <div className=\"flex items-start justify-between gap-4\">\n <div className=\"flex-1\">\n <h3 className=\"text-sm font-medium\">\n {getProviderName(config.provider)}\n </h3>\n <p className=\"text-xs text-muted-foreground mt-0.5\">\n {getProviderDescription(config.provider)}\n </p>\n </div>\n\n <Switch\n checked={config.isEnabled}\n onCheckedChange={() => toggleEnabled(config.id, config.isEnabled, config.updatedAt)}\n />\n </div>\n\n {config.isEnabled && (\n <>\n <div className=\"flex items-baseline gap-3 flex-wrap mt-3\">\n <div className=\"flex items-baseline gap-2\">\n <label className=\"text-xs text-muted-foreground whitespace-nowrap\">\n {t('currencies.fetch.sync_time')}:\n </label>\n <input\n type=\"time\"\n value={config.syncTime || '09:00'}\n onChange={(e) => updateSyncTime(config.id, e.target.value, config.updatedAt)}\n className=\"rounded border bg-background px-2 py-1.5 text-sm\"\n />\n </div>\n\n <div className=\"flex items-baseline gap-2\">\n <label className=\"text-xs text-muted-foreground whitespace-nowrap\">\n {t('currencies.fetch.last_sync')}:\n </label>\n <span className=\"text-xs text-muted-foreground\">\n {formatLastSync(config.lastSyncAt)}\n </span>\n {getStatusBadge(config.lastSyncStatus)}\n </div>\n\n <Button\n onClick={() => fetchNow(config.provider)}\n disabled={fetching === config.provider}\n size=\"default\"\n className=\"ml-auto min-w-32\"\n >\n {fetching === config.provider ? (\n <div>\n <Spinner className=\"mr-1.5\" size='sm' />\n {t('currencies.fetch.fetching')}\n </div>\n ) : (\n t('currencies.fetch.fetch_now')\n )}\n </Button>\n </div>\n\n {config.lastSyncCount !== null && (\n <div className=\"text-xs text-muted-foreground mt-2\">\n {t('currencies.fetch.last_sync_count')}: <span className=\"font-medium\">{config.lastSyncCount}</span> rates\n </div>\n )}\n\n {config.lastSyncMessage && config.lastSyncStatus === 'error' && (\n <div className=\"rounded border border-red-200 bg-red-50 p-2 text-xs text-red-700 mt-2\">\n {config.lastSyncMessage}\n </div>\n )}\n </>\n )}\n </div>\n ))}\n\n {configs.length === 0 && (\n <div className=\"rounded border bg-background/80 p-6 text-center\">\n <p className=\"text-sm text-muted-foreground\">\n {t('currencies.fetch.no_providers')}\n </p>\n <Button\n onClick={() => initializeMissingProviders(availableProviders)}\n >\n {t('currencies.fetch.initialize_providers')}\n </Button>\n </div>\n )}\n </div>\n </section>\n )\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -5,6 +5,8 @@ import { createRequestContainer } from "@open-mercato/shared/lib/di/container";
|
|
|
5
5
|
import { CustomerRole, CustomerRoleAcl, CustomerUserRole } from "@open-mercato/core/modules/customer_accounts/data/entities";
|
|
6
6
|
import { updateRoleSchema } from "@open-mercato/core/modules/customer_accounts/data/validators";
|
|
7
7
|
import { emitCustomerAccountsEvent } from "@open-mercato/core/modules/customer_accounts/events";
|
|
8
|
+
import { enforceCommandOptimisticLock } from "@open-mercato/shared/lib/crud/optimistic-lock-command";
|
|
9
|
+
import { isCrudHttpError } from "@open-mercato/shared/lib/crud/errors";
|
|
8
10
|
const metadata = {};
|
|
9
11
|
const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
10
12
|
async function GET(req, { params }) {
|
|
@@ -82,21 +84,31 @@ async function PUT(req, { params }) {
|
|
|
82
84
|
if (role.isSystem && parsed.data.name !== void 0 && parsed.data.name !== role.name) {
|
|
83
85
|
return NextResponse.json({ ok: false, error: "Cannot change name of a system role" }, { status: 400 });
|
|
84
86
|
}
|
|
85
|
-
|
|
87
|
+
try {
|
|
88
|
+
enforceCommandOptimisticLock({
|
|
89
|
+
resourceKind: "customer_accounts.role",
|
|
90
|
+
resourceId: role.id,
|
|
91
|
+
current: role.updatedAt ?? null,
|
|
92
|
+
request: req
|
|
93
|
+
});
|
|
94
|
+
} catch (err) {
|
|
95
|
+
if (isCrudHttpError(err)) return NextResponse.json(err.body, { status: err.status });
|
|
96
|
+
throw err;
|
|
97
|
+
}
|
|
98
|
+
const nextUpdatedAt = /* @__PURE__ */ new Date();
|
|
99
|
+
const updates = { updatedAt: nextUpdatedAt };
|
|
86
100
|
if (parsed.data.name !== void 0) updates.name = parsed.data.name;
|
|
87
101
|
if (parsed.data.description !== void 0) updates.description = parsed.data.description;
|
|
88
102
|
if (parsed.data.isDefault !== void 0) updates.isDefault = parsed.data.isDefault;
|
|
89
103
|
if (parsed.data.customerAssignable !== void 0) updates.customerAssignable = parsed.data.customerAssignable;
|
|
90
|
-
|
|
91
|
-
await em.nativeUpdate(CustomerRole, { id: role.id }, updates);
|
|
92
|
-
}
|
|
104
|
+
await em.nativeUpdate(CustomerRole, { id: role.id }, updates);
|
|
93
105
|
void emitCustomerAccountsEvent("customer_accounts.role.updated", {
|
|
94
106
|
id: role.id,
|
|
95
107
|
name: updates.name || role.name,
|
|
96
108
|
tenantId: auth.tenantId,
|
|
97
109
|
organizationId: auth.orgId
|
|
98
110
|
}).catch(() => void 0);
|
|
99
|
-
return NextResponse.json({ ok: true });
|
|
111
|
+
return NextResponse.json({ ok: true, updatedAt: nextUpdatedAt.toISOString() });
|
|
100
112
|
}
|
|
101
113
|
async function DELETE(req, { params }) {
|
|
102
114
|
const auth = await getAuthFromRequest(req);
|
|
@@ -121,6 +133,17 @@ async function DELETE(req, { params }) {
|
|
|
121
133
|
if (role.isSystem) {
|
|
122
134
|
return NextResponse.json({ ok: false, error: "Cannot delete a system role" }, { status: 400 });
|
|
123
135
|
}
|
|
136
|
+
try {
|
|
137
|
+
enforceCommandOptimisticLock({
|
|
138
|
+
resourceKind: "customer_accounts.role",
|
|
139
|
+
resourceId: role.id,
|
|
140
|
+
current: role.updatedAt ?? null,
|
|
141
|
+
request: req
|
|
142
|
+
});
|
|
143
|
+
} catch (err) {
|
|
144
|
+
if (isCrudHttpError(err)) return NextResponse.json(err.body, { status: err.status });
|
|
145
|
+
throw err;
|
|
146
|
+
}
|
|
124
147
|
const assignedUsersCount = await em.count(CustomerUserRole, {
|
|
125
148
|
role: role.id,
|
|
126
149
|
deletedAt: null
|