@open-mercato/core 0.6.8-develop.7100.1.fbf66fca35 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +1 -1
- package/dist/helpers/integration/api.js +1 -13
- package/dist/helpers/integration/api.js.map +2 -2
- package/dist/helpers/integration/communicationChannelsFixtures.js +0 -19
- package/dist/helpers/integration/communicationChannelsFixtures.js.map +2 -2
- package/dist/modules/api_keys/api/keys/route.js +1 -1
- package/dist/modules/api_keys/api/keys/route.js.map +2 -2
- package/dist/modules/api_keys/backend/api-keys/page.js +1 -3
- package/dist/modules/api_keys/backend/api-keys/page.js.map +2 -2
- package/dist/modules/attachments/api/route.js +2 -2
- package/dist/modules/attachments/api/route.js.map +2 -2
- package/dist/modules/attachments/components/AttachmentLibrary.js +0 -2
- package/dist/modules/attachments/components/AttachmentLibrary.js.map +2 -2
- package/dist/modules/attachments/di.js +1 -11
- package/dist/modules/attachments/di.js.map +2 -2
- package/dist/modules/attachments/index.js.map +1 -1
- package/dist/modules/attachments/lib/scoped-upload-service.js +2 -15
- package/dist/modules/attachments/lib/scoped-upload-service.js.map +2 -2
- package/dist/modules/attachments/lib/upload-limits.js +2 -6
- package/dist/modules/attachments/lib/upload-limits.js.map +2 -2
- package/dist/modules/audit_logs/backend/audit-logs/page.js +0 -6
- package/dist/modules/audit_logs/backend/audit-logs/page.js.map +2 -2
- package/dist/modules/audit_logs/lib/display-helpers.js +11 -19
- package/dist/modules/audit_logs/lib/display-helpers.js.map +2 -2
- package/dist/modules/audit_logs/services/actionLogService.js +12 -57
- package/dist/modules/audit_logs/services/actionLogService.js.map +3 -3
- package/dist/modules/auth/acl.js +5 -30
- package/dist/modules/auth/acl.js.map +2 -2
- package/dist/modules/auth/api/feature-check.js +2 -8
- package/dist/modules/auth/api/feature-check.js.map +2 -2
- package/dist/modules/auth/api/login.js +20 -4
- package/dist/modules/auth/api/login.js.map +2 -2
- package/dist/modules/auth/api/roles/acl/route.js +33 -28
- package/dist/modules/auth/api/roles/acl/route.js.map +2 -2
- package/dist/modules/auth/api/routeError.js +11 -0
- package/dist/modules/auth/api/routeError.js.map +7 -0
- package/dist/modules/auth/api/session/refresh.js +1 -25
- package/dist/modules/auth/api/session/refresh.js.map +2 -2
- package/dist/modules/auth/api/users/acl/route.js +73 -102
- package/dist/modules/auth/api/users/acl/route.js.map +2 -2
- package/dist/modules/auth/api/users/route.js +5 -13
- package/dist/modules/auth/api/users/route.js.map +2 -2
- package/dist/modules/auth/backend/roles/[id]/edit/page.meta.js +1 -5
- package/dist/modules/auth/backend/roles/[id]/edit/page.meta.js.map +2 -2
- package/dist/modules/auth/backend/roles/page.js +1 -3
- package/dist/modules/auth/backend/roles/page.js.map +2 -2
- package/dist/modules/auth/backend/users/[id]/edit/page.meta.js +1 -5
- package/dist/modules/auth/backend/users/[id]/edit/page.meta.js.map +2 -2
- package/dist/modules/auth/backend/users/page.js +1 -2
- package/dist/modules/auth/backend/users/page.js.map +2 -2
- package/dist/modules/auth/components/AclEditor.js +30 -39
- package/dist/modules/auth/components/AclEditor.js.map +2 -2
- package/dist/modules/auth/data/entities.js.map +2 -2
- package/dist/modules/auth/data/validators.js +1 -5
- package/dist/modules/auth/data/validators.js.map +2 -2
- package/dist/modules/auth/di.js +9 -35
- package/dist/modules/auth/di.js.map +2 -2
- package/dist/modules/auth/frontend/reset/[token]/page.js +0 -46
- package/dist/modules/auth/frontend/reset/[token]/page.js.map +2 -2
- package/dist/modules/auth/lib/grantChecks.js.map +2 -2
- package/dist/modules/auth/services/authService.js +2 -16
- package/dist/modules/auth/services/authService.js.map +2 -2
- package/dist/modules/auth/services/rbacService.js +24 -67
- package/dist/modules/auth/services/rbacService.js.map +3 -3
- package/dist/modules/business_rules/backend/logs/page.js +1 -3
- package/dist/modules/business_rules/backend/logs/page.js.map +2 -2
- package/dist/modules/business_rules/backend/rules/[id]/page.meta.js +6 -1
- package/dist/modules/business_rules/backend/rules/[id]/page.meta.js.map +2 -2
- package/dist/modules/business_rules/backend/rules/create/page.meta.js +5 -1
- package/dist/modules/business_rules/backend/rules/create/page.meta.js.map +2 -2
- package/dist/modules/business_rules/backend/rules/page.js +1 -3
- package/dist/modules/business_rules/backend/rules/page.js.map +2 -2
- package/dist/modules/business_rules/backend/sets/[id]/page.meta.js +6 -1
- package/dist/modules/business_rules/backend/sets/[id]/page.meta.js.map +2 -2
- package/dist/modules/business_rules/backend/sets/create/page.meta.js +5 -1
- package/dist/modules/business_rules/backend/sets/create/page.meta.js.map +2 -2
- package/dist/modules/business_rules/backend/sets/page.js +1 -3
- package/dist/modules/business_rules/backend/sets/page.js.map +2 -2
- package/dist/modules/business_rules/lib/expression-evaluator.js +9 -9
- package/dist/modules/business_rules/lib/expression-evaluator.js.map +2 -2
- package/dist/modules/business_rules/lib/rule-evaluator.js +9 -10
- package/dist/modules/business_rules/lib/rule-evaluator.js.map +2 -2
- package/dist/modules/catalog/ai-agents.js +1 -1
- package/dist/modules/catalog/ai-agents.js.map +1 -1
- package/dist/modules/catalog/ai-tools/merchandising-pack.js +2 -2
- package/dist/modules/catalog/ai-tools/merchandising-pack.js.map +2 -2
- package/dist/modules/catalog/backend/catalog/products/MerchandisingAssistantSheet.js +2 -5
- package/dist/modules/catalog/backend/catalog/products/MerchandisingAssistantSheet.js.map +2 -2
- package/dist/modules/catalog/components/categories/CategoriesDataTable.js +0 -2
- package/dist/modules/catalog/components/categories/CategoriesDataTable.js.map +2 -2
- package/dist/modules/catalog/components/products/ProductsDataTable.js +1 -7
- package/dist/modules/catalog/components/products/ProductsDataTable.js.map +2 -2
- package/dist/modules/catalog/widgets/injection/merchandising-assistant-trigger/widget.client.js +0 -2
- package/dist/modules/catalog/widgets/injection/merchandising-assistant-trigger/widget.client.js.map +2 -2
- package/dist/modules/communication_channels/api/post/channels/[id]/test-send/route.js +1 -9
- package/dist/modules/communication_channels/api/post/channels/[id]/test-send/route.js.map +2 -2
- package/dist/modules/communication_channels/api/post/test-seed/route.js +10 -95
- package/dist/modules/communication_channels/api/post/test-seed/route.js.map +2 -2
- package/dist/modules/communication_channels/backend/communication_channels/channels/page.js +0 -3
- package/dist/modules/communication_channels/backend/communication_channels/channels/page.js.map +2 -2
- package/dist/modules/communication_channels/commands/ingest-inbound-message.js +1 -14
- package/dist/modules/communication_channels/commands/ingest-inbound-message.js.map +2 -2
- package/dist/modules/communication_channels/di.js +1 -7
- package/dist/modules/communication_channels/di.js.map +2 -2
- package/dist/modules/communication_channels/lib/email-capabilities.js +1 -4
- package/dist/modules/communication_channels/lib/email-capabilities.js.map +2 -2
- package/dist/modules/communication_channels/lib/test-seed.js +2 -41
- package/dist/modules/communication_channels/lib/test-seed.js.map +2 -2
- package/dist/modules/configs/lib/system-status.js +0 -9
- package/dist/modules/configs/lib/system-status.js.map +2 -2
- package/dist/modules/currencies/backend/currencies/page.js +1 -3
- package/dist/modules/currencies/backend/currencies/page.js.map +2 -2
- package/dist/modules/currencies/backend/exchange-rates/page.js +1 -3
- package/dist/modules/currencies/backend/exchange-rates/page.js.map +2 -2
- package/dist/modules/customer_accounts/api/admin/domain-mappings.js +0 -9
- package/dist/modules/customer_accounts/api/admin/domain-mappings.js.map +2 -2
- package/dist/modules/customer_accounts/backend/customer_accounts/roles/page.js +1 -3
- package/dist/modules/customer_accounts/backend/customer_accounts/roles/page.js.map +2 -2
- package/dist/modules/customer_accounts/backend/customer_accounts/settings/CustomerAccountsSettingsPageClient.js +84 -0
- package/dist/modules/customer_accounts/backend/customer_accounts/settings/CustomerAccountsSettingsPageClient.js.map +7 -0
- package/dist/modules/customer_accounts/backend/customer_accounts/settings/page.js +7 -81
- package/dist/modules/customer_accounts/backend/customer_accounts/settings/page.js.map +2 -2
- package/dist/modules/customer_accounts/backend/customer_accounts/users/PortalUsersPageClient.js +523 -0
- package/dist/modules/customer_accounts/backend/customer_accounts/users/PortalUsersPageClient.js.map +7 -0
- package/dist/modules/customer_accounts/backend/customer_accounts/users/[id]/PortalUserDetailPageClient.js +731 -0
- package/dist/modules/customer_accounts/backend/customer_accounts/users/[id]/PortalUserDetailPageClient.js.map +7 -0
- package/dist/modules/customer_accounts/backend/customer_accounts/users/[id]/page.js +7 -727
- 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 +7 -521
- package/dist/modules/customer_accounts/backend/customer_accounts/users/page.js.map +2 -2
- package/dist/modules/customer_accounts/extension-points.js +1 -1
- package/dist/modules/customer_accounts/extension-points.js.map +2 -2
- package/dist/modules/customer_accounts/lib/portalUrl.js +69 -0
- package/dist/modules/customer_accounts/lib/portalUrl.js.map +7 -0
- package/dist/modules/customer_accounts/services/domainMappingService.js +0 -10
- package/dist/modules/customer_accounts/services/domainMappingService.js.map +2 -2
- package/dist/modules/customers/ai-tools/companies-pack.js +1 -4
- package/dist/modules/customers/ai-tools/companies-pack.js.map +2 -2
- package/dist/modules/customers/ai-tools/deal-analyzer-pack.js +2 -13
- package/dist/modules/customers/ai-tools/deal-analyzer-pack.js.map +2 -2
- package/dist/modules/customers/ai-tools/deals-pack.js +6 -52
- package/dist/modules/customers/ai-tools/deals-pack.js.map +2 -2
- package/dist/modules/customers/ai-tools/people-pack.js +1 -4
- package/dist/modules/customers/ai-tools/people-pack.js.map +2 -2
- package/dist/modules/customers/api/activities/route.js +6 -48
- package/dist/modules/customers/api/activities/route.js.map +2 -2
- package/dist/modules/customers/api/companies/[id]/people/route.js +64 -20
- package/dist/modules/customers/api/companies/[id]/people/route.js.map +2 -2
- package/dist/modules/customers/api/companies/[id]/route.js +94 -17
- package/dist/modules/customers/api/companies/[id]/route.js.map +2 -2
- package/dist/modules/customers/api/dashboard/widgets/customer-todos/route.js +5 -8
- package/dist/modules/customers/api/dashboard/widgets/customer-todos/route.js.map +2 -2
- package/dist/modules/customers/api/deals/[id]/companies/route.js +1 -1
- package/dist/modules/customers/api/deals/[id]/companies/route.js.map +2 -2
- package/dist/modules/customers/api/deals/[id]/people/route.js +1 -1
- package/dist/modules/customers/api/deals/[id]/people/route.js.map +2 -2
- package/dist/modules/customers/api/deals/[id]/route.js +25 -8
- package/dist/modules/customers/api/deals/[id]/route.js.map +2 -2
- package/dist/modules/customers/api/deals/[id]/stats/route.js +22 -3
- package/dist/modules/customers/api/deals/[id]/stats/route.js.map +2 -2
- package/dist/modules/customers/api/deals/aggregate/route.js +4 -11
- package/dist/modules/customers/api/deals/aggregate/route.js.map +2 -2
- package/dist/modules/customers/api/deals/map/route.js +1 -2
- package/dist/modules/customers/api/deals/map/route.js.map +2 -2
- package/dist/modules/customers/api/deals/route.js +1 -38
- package/dist/modules/customers/api/deals/route.js.map +2 -2
- package/dist/modules/customers/api/entity-roles-factory.js +4 -4
- package/dist/modules/customers/api/entity-roles-factory.js.map +2 -2
- package/dist/modules/customers/api/interactions/route.js +1 -3
- package/dist/modules/customers/api/interactions/route.js.map +2 -2
- package/dist/modules/customers/api/interactions/tasks/route.js +4 -27
- package/dist/modules/customers/api/interactions/tasks/route.js.map +2 -2
- package/dist/modules/customers/api/people/[id]/companies/[linkId]/route.js +3 -4
- package/dist/modules/customers/api/people/[id]/companies/[linkId]/route.js.map +2 -2
- package/dist/modules/customers/api/people/[id]/companies/context.js +1 -1
- package/dist/modules/customers/api/people/[id]/companies/context.js.map +2 -2
- package/dist/modules/customers/api/people/[id]/companies/enriched/route.js +1 -1
- package/dist/modules/customers/api/people/[id]/companies/enriched/route.js.map +2 -2
- package/dist/modules/customers/api/people/[id]/route.js +10 -11
- package/dist/modules/customers/api/people/[id]/route.js.map +2 -2
- package/dist/modules/customers/api/people/check-phone/route.js +9 -51
- package/dist/modules/customers/api/people/check-phone/route.js.map +2 -2
- package/dist/modules/customers/api/todos/route.js +5 -30
- package/dist/modules/customers/api/todos/route.js.map +2 -2
- package/dist/modules/customers/backend/customers/companies/page.js +4 -6
- package/dist/modules/customers/backend/customers/companies/page.js.map +2 -2
- package/dist/modules/customers/backend/customers/deals/[id]/page.js +1 -6
- package/dist/modules/customers/backend/customers/deals/[id]/page.js.map +2 -2
- package/dist/modules/customers/backend/customers/deals/page.js +4 -7
- package/dist/modules/customers/backend/customers/deals/page.js.map +2 -2
- package/dist/modules/customers/backend/customers/deals/pipeline/components/Lane.js +13 -4
- package/dist/modules/customers/backend/customers/deals/pipeline/components/Lane.js.map +2 -2
- package/dist/modules/customers/backend/customers/deals/pipeline/components/StatusFilterPopover.js +14 -85
- package/dist/modules/customers/backend/customers/deals/pipeline/components/StatusFilterPopover.js.map +2 -2
- package/dist/modules/customers/backend/customers/people/page.js +4 -6
- package/dist/modules/customers/backend/customers/people/page.js.map +2 -2
- package/dist/modules/customers/backend/hooks/useEmailDuplicateCheck.js +3 -9
- package/dist/modules/customers/backend/hooks/useEmailDuplicateCheck.js.map +2 -2
- package/dist/modules/customers/commands/deals.js +34 -21
- package/dist/modules/customers/commands/deals.js.map +2 -2
- package/dist/modules/customers/commands/interactions.js.map +1 -1
- package/dist/modules/customers/commands/personCompanyLinks.js +2 -131
- package/dist/modules/customers/commands/personCompanyLinks.js.map +2 -2
- package/dist/modules/customers/components/CustomerTodosTable.js +0 -1
- package/dist/modules/customers/components/CustomerTodosTable.js.map +2 -2
- package/dist/modules/customers/components/DealsKpiStrip.js +6 -7
- package/dist/modules/customers/components/DealsKpiStrip.js.map +2 -2
- package/dist/modules/customers/components/calendar/AgendaList.js +2 -2
- package/dist/modules/customers/components/calendar/AgendaList.js.map +2 -2
- package/dist/modules/customers/components/calendar/CalendarEventEditor.js +1 -1
- package/dist/modules/customers/components/calendar/CalendarEventEditor.js.map +2 -2
- package/dist/modules/customers/components/calendar/CalendarScreen.js +1 -1
- package/dist/modules/customers/components/calendar/CalendarScreen.js.map +2 -2
- package/dist/modules/customers/components/calendar/CalendarSettingsModal.js +1 -1
- package/dist/modules/customers/components/calendar/CalendarSettingsModal.js.map +2 -2
- package/dist/modules/customers/components/calendar/EventBlock.js +1 -1
- package/dist/modules/customers/components/calendar/EventBlock.js.map +2 -2
- package/dist/modules/customers/components/calendar/EventPeekPopover.js +2 -3
- package/dist/modules/customers/components/calendar/EventPeekPopover.js.map +2 -2
- package/dist/modules/customers/components/calendar/editor/PeopleField.js +5 -40
- package/dist/modules/customers/components/calendar/editor/PeopleField.js.map +2 -2
- package/dist/modules/customers/components/calendar/editor/hooks.js +1 -2
- package/dist/modules/customers/components/calendar/editor/hooks.js.map +2 -2
- package/dist/modules/customers/components/calendar/types.js +1 -1
- package/dist/modules/customers/components/calendar/types.js.map +2 -2
- package/dist/modules/customers/components/detail/AssignRoleDialog.js +33 -28
- package/dist/modules/customers/components/detail/AssignRoleDialog.js.map +2 -2
- package/dist/modules/customers/components/detail/CompanyKpiBar.js +4 -5
- package/dist/modules/customers/components/detail/CompanyKpiBar.js.map +2 -2
- package/dist/modules/customers/components/detail/CompanyPeopleSection.js +1 -3
- package/dist/modules/customers/components/detail/CompanyPeopleSection.js.map +2 -2
- package/dist/modules/customers/components/detail/DealForm.js +7 -19
- package/dist/modules/customers/components/detail/DealForm.js.map +2 -2
- package/dist/modules/customers/components/detail/DealsSection.js +5 -6
- package/dist/modules/customers/components/detail/DealsSection.js.map +2 -2
- package/dist/modules/customers/components/detail/EntityTagsDialog.js +11 -35
- package/dist/modules/customers/components/detail/EntityTagsDialog.js.map +2 -2
- package/dist/modules/customers/components/detail/PersonCompaniesSection.js +1 -3
- package/dist/modules/customers/components/detail/PersonCompaniesSection.js.map +2 -2
- package/dist/modules/customers/components/detail/hooks/usePersonTasks.js +13 -19
- package/dist/modules/customers/components/detail/hooks/usePersonTasks.js.map +2 -2
- package/dist/modules/customers/components/detail/schedule/ParticipantsField.js +4 -4
- package/dist/modules/customers/components/detail/schedule/ParticipantsField.js.map +2 -2
- package/dist/modules/customers/components/detail/schedule/useScheduleFormState.js +3 -3
- package/dist/modules/customers/components/detail/schedule/useScheduleFormState.js.map +2 -2
- package/dist/modules/customers/components/detail/utils.js +5 -5
- package/dist/modules/customers/components/detail/utils.js.map +2 -2
- package/dist/modules/customers/data/entities.js.map +1 -1
- package/dist/modules/customers/data/validators.js +3 -32
- package/dist/modules/customers/data/validators.js.map +2 -2
- package/dist/modules/customers/events.js +0 -5
- package/dist/modules/customers/events.js.map +2 -2
- package/dist/modules/customers/lib/calendar/conflicts.js +2 -8
- package/dist/modules/customers/lib/calendar/conflicts.js.map +2 -2
- package/dist/modules/customers/lib/calendar/editorPayload.js +2 -3
- package/dist/modules/customers/lib/calendar/editorPayload.js.map +2 -2
- package/dist/modules/customers/lib/calendar/mapItem.js +3 -8
- package/dist/modules/customers/lib/calendar/mapItem.js.map +2 -2
- package/dist/modules/customers/lib/dealStatus.js +0 -30
- package/dist/modules/customers/lib/dealStatus.js.map +2 -2
- package/dist/modules/customers/lib/findPeopleByAddresses.js +0 -1
- package/dist/modules/customers/lib/findPeopleByAddresses.js.map +2 -2
- package/dist/modules/customers/lib/personCompanies.js +2 -100
- package/dist/modules/customers/lib/personCompanies.js.map +2 -2
- package/dist/modules/customers/lib/todoCompatibility.js +0 -12
- package/dist/modules/customers/lib/todoCompatibility.js.map +2 -2
- package/dist/modules/dashboards/lib/widgetScope.js +1 -6
- package/dist/modules/dashboards/lib/widgetScope.js.map +2 -2
- package/dist/modules/data_sync/backend/data-sync/page.js +1 -3
- package/dist/modules/data_sync/backend/data-sync/page.js.map +2 -2
- package/dist/modules/data_sync/lib/sync-engine.js +6 -72
- package/dist/modules/data_sync/lib/sync-engine.js.map +2 -2
- package/dist/modules/data_sync/workers/sync-export.js +1 -5
- package/dist/modules/data_sync/workers/sync-export.js.map +2 -2
- package/dist/modules/data_sync/workers/sync-import.js +1 -5
- package/dist/modules/data_sync/workers/sync-import.js.map +2 -2
- package/dist/modules/devices/acl.js +1 -9
- package/dist/modules/devices/acl.js.map +2 -2
- package/dist/modules/devices/api/admin/devices/[id]/route.js +32 -5
- package/dist/modules/devices/api/admin/devices/[id]/route.js.map +2 -2
- package/dist/modules/devices/api/admin/devices/route.js +1 -14
- package/dist/modules/devices/api/admin/devices/route.js.map +2 -2
- package/dist/modules/devices/api/deviceList.js +12 -99
- package/dist/modules/devices/api/deviceList.js.map +2 -2
- package/dist/modules/devices/api/openapi.js +1 -3
- package/dist/modules/devices/api/openapi.js.map +2 -2
- package/dist/modules/devices/api/route.js +1 -9
- package/dist/modules/devices/api/route.js.map +2 -2
- package/dist/modules/devices/backend/devices/[id]/page.js +26 -10
- package/dist/modules/devices/backend/devices/[id]/page.js.map +2 -2
- package/dist/modules/devices/backend/devices/create/page.js +1 -17
- package/dist/modules/devices/backend/devices/create/page.js.map +2 -2
- package/dist/modules/devices/backend/devices/page.js +34 -28
- package/dist/modules/devices/backend/devices/page.js.map +2 -2
- package/dist/modules/devices/setup.js +2 -6
- package/dist/modules/devices/setup.js.map +2 -2
- package/dist/modules/directory/backend/directory/organizations/page.js +1 -2
- package/dist/modules/directory/backend/directory/organizations/page.js.map +2 -2
- package/dist/modules/directory/backend/directory/tenants/page.js +1 -2
- package/dist/modules/directory/backend/directory/tenants/page.js.map +2 -2
- package/dist/modules/directory/di.js +0 -9
- package/dist/modules/directory/di.js.map +2 -2
- package/dist/modules/directory/utils/organizationScope.js.map +2 -2
- package/dist/modules/entities/api/records.js +2 -4
- package/dist/modules/entities/api/records.js.map +2 -2
- package/dist/modules/entities/backend/entities/user/[entityId]/records/page.js +1 -3
- package/dist/modules/entities/backend/entities/user/[entityId]/records/page.js.map +2 -2
- package/dist/modules/eudr/api/plots/route.js +1 -5
- package/dist/modules/eudr/api/plots/route.js.map +2 -2
- package/dist/modules/eudr/backend/eudr/evidence-submissions/[id]/page.meta.js +3 -1
- package/dist/modules/eudr/backend/eudr/evidence-submissions/[id]/page.meta.js.map +2 -2
- package/dist/modules/eudr/backend/eudr/evidence-submissions/create/page.meta.js +1 -0
- package/dist/modules/eudr/backend/eudr/evidence-submissions/create/page.meta.js.map +2 -2
- package/dist/modules/eudr/backend/eudr/evidence-submissions/page.js +0 -3
- package/dist/modules/eudr/backend/eudr/evidence-submissions/page.js.map +2 -2
- package/dist/modules/eudr/backend/eudr/evidence-submissions/page.meta.js +4 -1
- package/dist/modules/eudr/backend/eudr/evidence-submissions/page.meta.js.map +2 -2
- package/dist/modules/eudr/backend/eudr/page.meta.js +2 -2
- package/dist/modules/eudr/backend/eudr/page.meta.js.map +2 -2
- package/dist/modules/eudr/backend/eudr/plots/[id]/page.js +8 -2
- package/dist/modules/eudr/backend/eudr/plots/[id]/page.js.map +2 -2
- package/dist/modules/eudr/backend/eudr/plots/[id]/page.meta.js +3 -1
- package/dist/modules/eudr/backend/eudr/plots/[id]/page.meta.js.map +2 -2
- package/dist/modules/eudr/backend/eudr/plots/create/page.js +8 -2
- package/dist/modules/eudr/backend/eudr/plots/create/page.js.map +2 -2
- package/dist/modules/eudr/backend/eudr/plots/create/page.meta.js +1 -0
- package/dist/modules/eudr/backend/eudr/plots/create/page.meta.js.map +2 -2
- package/dist/modules/eudr/backend/eudr/plots/page.js +0 -3
- package/dist/modules/eudr/backend/eudr/plots/page.js.map +2 -2
- package/dist/modules/eudr/backend/eudr/plots/page.meta.js +4 -1
- package/dist/modules/eudr/backend/eudr/plots/page.meta.js.map +2 -2
- package/dist/modules/eudr/backend/eudr/product-mappings/[id]/page.meta.js +3 -1
- package/dist/modules/eudr/backend/eudr/product-mappings/[id]/page.meta.js.map +2 -2
- package/dist/modules/eudr/backend/eudr/product-mappings/create/page.meta.js +1 -0
- package/dist/modules/eudr/backend/eudr/product-mappings/create/page.meta.js.map +2 -2
- package/dist/modules/eudr/backend/eudr/product-mappings/page.js +0 -3
- package/dist/modules/eudr/backend/eudr/product-mappings/page.js.map +2 -2
- package/dist/modules/eudr/backend/eudr/product-mappings/page.meta.js +4 -1
- package/dist/modules/eudr/backend/eudr/product-mappings/page.meta.js.map +2 -2
- package/dist/modules/eudr/backend/eudr/risk-assessments/[id]/page.meta.js +3 -1
- package/dist/modules/eudr/backend/eudr/risk-assessments/[id]/page.meta.js.map +2 -2
- package/dist/modules/eudr/backend/eudr/risk-assessments/create/page.meta.js +1 -0
- package/dist/modules/eudr/backend/eudr/risk-assessments/create/page.meta.js.map +2 -2
- package/dist/modules/eudr/backend/eudr/risk-assessments/page.js +0 -3
- package/dist/modules/eudr/backend/eudr/risk-assessments/page.js.map +2 -2
- package/dist/modules/eudr/backend/eudr/risk-assessments/page.meta.js +4 -1
- package/dist/modules/eudr/backend/eudr/risk-assessments/page.meta.js.map +2 -2
- package/dist/modules/eudr/backend/eudr/statements/[id]/page.js +2 -0
- package/dist/modules/eudr/backend/eudr/statements/[id]/page.js.map +2 -2
- package/dist/modules/eudr/backend/eudr/statements/[id]/page.meta.js +3 -1
- package/dist/modules/eudr/backend/eudr/statements/[id]/page.meta.js.map +2 -2
- package/dist/modules/eudr/backend/eudr/statements/create/page.meta.js +1 -0
- package/dist/modules/eudr/backend/eudr/statements/create/page.meta.js.map +2 -2
- package/dist/modules/eudr/backend/eudr/statements/page.js +30 -28
- package/dist/modules/eudr/backend/eudr/statements/page.js.map +2 -2
- package/dist/modules/eudr/backend/eudr/statements/page.meta.js +4 -1
- package/dist/modules/eudr/backend/eudr/statements/page.meta.js.map +2 -2
- package/dist/modules/eudr/commands/statements.js +8 -3
- package/dist/modules/eudr/commands/statements.js.map +2 -2
- package/dist/modules/eudr/components/crudErrorI18n.js +21 -1
- package/dist/modules/eudr/components/crudErrorI18n.js.map +2 -2
- package/dist/modules/eudr/components/formConfig.js +6 -2
- package/dist/modules/eudr/components/formConfig.js.map +2 -2
- package/dist/modules/eudr/data/validators.js +1 -4
- package/dist/modules/eudr/data/validators.js.map +2 -2
- package/dist/modules/eudr/lib/statement-lifecycle.js +9 -1
- package/dist/modules/eudr/lib/statement-lifecycle.js.map +2 -2
- package/dist/modules/feature_toggles/components/FeatureTogglesTable.js +0 -1
- package/dist/modules/feature_toggles/components/FeatureTogglesTable.js.map +2 -2
- package/dist/modules/feature_toggles/components/OverridesTable.js +0 -1
- package/dist/modules/feature_toggles/components/OverridesTable.js.map +2 -2
- package/dist/modules/feature_toggles/defaults.json +8 -0
- package/dist/modules/inbox_ops/backend/inbox-ops/log/page.js +0 -3
- package/dist/modules/inbox_ops/backend/inbox-ops/log/page.js.map +2 -2
- package/dist/modules/inbox_ops/backend/inbox-ops/page.js +0 -3
- package/dist/modules/inbox_ops/backend/inbox-ops/page.js.map +2 -2
- package/dist/modules/inbox_ops/lib/htmlToPlainText.js +12 -1
- package/dist/modules/inbox_ops/lib/htmlToPlainText.js.map +2 -2
- package/dist/modules/integrations/api/[id]/credentials/route.js +1 -6
- package/dist/modules/integrations/api/[id]/credentials/route.js.map +2 -2
- package/dist/modules/integrations/backend/integrations/[id]/page.js +29 -38
- package/dist/modules/integrations/backend/integrations/[id]/page.js.map +2 -2
- package/dist/modules/integrations/backend/integrations/bundle/[id]/page.js +9 -36
- package/dist/modules/integrations/backend/integrations/bundle/[id]/page.js.map +2 -2
- package/dist/modules/integrations/data/validators.js +3 -11
- package/dist/modules/integrations/data/validators.js.map +2 -2
- package/dist/modules/integrations/lib/credentials-masking.js +2 -6
- package/dist/modules/integrations/lib/credentials-masking.js.map +2 -2
- package/dist/modules/messages/api/[id]/route.js +1 -29
- package/dist/modules/messages/api/[id]/route.js.map +2 -2
- package/dist/modules/messages/api/openapi.js +2 -17
- package/dist/modules/messages/api/openapi.js.map +2 -2
- package/dist/modules/messages/api/route.js +2 -17
- package/dist/modules/messages/api/route.js.map +2 -2
- package/dist/modules/messages/components/MessagesInboxPageClient.js +14 -7
- package/dist/modules/messages/components/MessagesInboxPageClient.js.map +2 -2
- package/dist/modules/messages/components/inboxFilters.js +0 -15
- package/dist/modules/messages/components/inboxFilters.js.map +2 -2
- package/dist/modules/messages/components/message-detail/hooks/useMessageDetailsConversation.js +1 -11
- package/dist/modules/messages/components/message-detail/hooks/useMessageDetailsConversation.js.map +2 -2
- package/dist/modules/messages/components/message-detail/panels/MessageHeader.js +1 -3
- package/dist/modules/messages/components/message-detail/panels/MessageHeader.js.map +2 -2
- package/dist/modules/messages/components/message-detail/panels/thread-panel.js +1 -2
- package/dist/modules/messages/components/message-detail/panels/thread-panel.js.map +2 -2
- package/dist/modules/messages/components/messageListLabels.js +2 -14
- package/dist/modules/messages/components/messageListLabels.js.map +2 -2
- package/dist/modules/messages/data/validators.js +4 -18
- package/dist/modules/messages/data/validators.js.map +2 -2
- package/dist/modules/payment_gateways/api/webhook/[provider]/route.js +6 -4
- package/dist/modules/payment_gateways/api/webhook/[provider]/route.js.map +2 -2
- package/dist/modules/payment_gateways/backend/payment-gateways/page.js +1 -3
- package/dist/modules/payment_gateways/backend/payment-gateways/page.js.map +2 -2
- package/dist/modules/payment_gateways/lib/webhook-processor.js +0 -7
- package/dist/modules/payment_gateways/lib/webhook-processor.js.map +2 -2
- package/dist/modules/planner/backend/planner/availability-rulesets/page.js +0 -3
- package/dist/modules/planner/backend/planner/availability-rulesets/page.js.map +2 -2
- package/dist/modules/push_notifications/backend/push_notifications/page.js +1 -3
- package/dist/modules/push_notifications/backend/push_notifications/page.js.map +2 -2
- package/dist/modules/push_notifications/backend/push_notifications/send/page.js +1 -1
- package/dist/modules/push_notifications/backend/push_notifications/send/page.js.map +2 -2
- package/dist/modules/query_index/lib/engine.js +51 -150
- package/dist/modules/query_index/lib/engine.js.map +2 -2
- package/dist/modules/query_index/lib/search-tokens.js +3 -79
- package/dist/modules/query_index/lib/search-tokens.js.map +2 -2
- package/dist/modules/resources/backend/resources/resource-types/page.js +1 -3
- package/dist/modules/resources/backend/resources/resource-types/page.js.map +2 -2
- package/dist/modules/resources/backend/resources/resources/page.js +1 -3
- package/dist/modules/resources/backend/resources/resources/page.js.map +2 -2
- package/dist/modules/sales/backend/sales/channels/offers/page.js +0 -3
- package/dist/modules/sales/backend/sales/channels/offers/page.js.map +2 -2
- package/dist/modules/sales/backend/sales/channels/page.js +0 -3
- package/dist/modules/sales/backend/sales/channels/page.js.map +2 -2
- package/dist/modules/sales/backend/sales/documents/[id]/page.js +8 -10
- package/dist/modules/sales/backend/sales/documents/[id]/page.js.map +2 -2
- package/dist/modules/sales/commands/documents.js +1 -79
- package/dist/modules/sales/commands/documents.js.map +2 -2
- package/dist/modules/sales/components/PriceWithCurrency.js +5 -7
- package/dist/modules/sales/components/PriceWithCurrency.js.map +2 -2
- package/dist/modules/sales/components/channels/SalesChannelOffersPanel.js +0 -3
- package/dist/modules/sales/components/channels/SalesChannelOffersPanel.js.map +2 -2
- package/dist/modules/sales/components/documents/AdjustmentsSection.js +6 -8
- package/dist/modules/sales/components/documents/AdjustmentsSection.js.map +2 -2
- package/dist/modules/sales/components/documents/ItemsSection.js +22 -51
- package/dist/modules/sales/components/documents/ItemsSection.js.map +2 -2
- package/dist/modules/sales/components/documents/LineItemDialog.js +19 -84
- package/dist/modules/sales/components/documents/LineItemDialog.js.map +2 -2
- package/dist/modules/sales/components/documents/PaymentsSection.js +9 -23
- package/dist/modules/sales/components/documents/PaymentsSection.js.map +2 -2
- package/dist/modules/sales/components/documents/ReturnsSection.js +6 -8
- package/dist/modules/sales/components/documents/ReturnsSection.js.map +2 -2
- package/dist/modules/sales/components/documents/SalesDocumentsTable.js +14 -68
- package/dist/modules/sales/components/documents/SalesDocumentsTable.js.map +2 -2
- package/dist/modules/sales/components/documents/SalesOrderDraftLines.js +5 -6
- package/dist/modules/sales/components/documents/SalesOrderDraftLines.js.map +2 -2
- package/dist/modules/sales/components/documents/ShipmentDialog.js +4 -5
- package/dist/modules/sales/components/documents/ShipmentDialog.js.map +2 -2
- package/dist/modules/sales/components/documents/ShipmentsSection.js +6 -8
- package/dist/modules/sales/components/documents/ShipmentsSection.js.map +2 -2
- package/dist/modules/sales/components/documents/lineItemUtils.js +2 -2
- package/dist/modules/sales/components/documents/lineItemUtils.js.map +2 -2
- package/dist/modules/sales/data/entities.js +2 -8
- package/dist/modules/sales/data/entities.js.map +2 -2
- package/dist/modules/sales/extension-points.js +0 -1
- package/dist/modules/sales/extension-points.js.map +2 -2
- package/dist/modules/sales/setup.js +0 -2
- package/dist/modules/sales/setup.js.map +2 -2
- package/dist/modules/sales/widgets/injection/document-history/widget.client.js +44 -2
- package/dist/modules/sales/widgets/injection/document-history/widget.client.js.map +2 -2
- package/dist/modules/sales/widgets/injection/payment-gateway-status-column/widget.js +2 -10
- package/dist/modules/sales/widgets/injection/payment-gateway-status-column/widget.js.map +2 -2
- package/dist/modules/sales/widgets/injection-table.js +7 -4
- package/dist/modules/sales/widgets/injection-table.js.map +2 -2
- package/dist/modules/staff/backend/staff/leave-requests/page.js +0 -3
- package/dist/modules/staff/backend/staff/leave-requests/page.js.map +2 -2
- package/dist/modules/staff/backend/staff/my-leave-requests/page.js +0 -3
- package/dist/modules/staff/backend/staff/my-leave-requests/page.js.map +2 -2
- package/dist/modules/staff/backend/staff/team-members/page.js +0 -3
- package/dist/modules/staff/backend/staff/team-members/page.js.map +2 -2
- package/dist/modules/staff/backend/staff/team-roles/page.js +0 -3
- package/dist/modules/staff/backend/staff/team-roles/page.js.map +2 -2
- package/dist/modules/staff/backend/staff/teams/[id]/edit/page.js +0 -3
- package/dist/modules/staff/backend/staff/teams/[id]/edit/page.js.map +2 -2
- package/dist/modules/staff/backend/staff/teams/page.js +0 -3
- package/dist/modules/staff/backend/staff/teams/page.js.map +2 -2
- package/dist/modules/staff/backend/staff/timesheets/page.js +18 -29
- package/dist/modules/staff/backend/staff/timesheets/page.js.map +2 -2
- package/dist/modules/staff/backend/staff/timesheets/projects/page.js +0 -3
- package/dist/modules/staff/backend/staff/timesheets/projects/page.js.map +2 -2
- package/dist/modules/sync_excel/lib/adapters/customers.js +0 -1
- package/dist/modules/sync_excel/lib/adapters/customers.js.map +2 -2
- package/dist/modules/warranty_claims/api/assignees/route.js +3 -3
- package/dist/modules/warranty_claims/api/assignees/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/portal/attachments/route.js +2 -2
- package/dist/modules/warranty_claims/api/portal/attachments/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/portal/claims/route.js +9 -12
- package/dist/modules/warranty_claims/api/portal/claims/route.js.map +2 -2
- package/dist/modules/warranty_claims/backend/page.js +0 -3
- package/dist/modules/warranty_claims/backend/page.js.map +2 -2
- package/dist/modules/warranty_claims/backend/warranty_claims/registrations/page.js +0 -3
- package/dist/modules/warranty_claims/backend/warranty_claims/registrations/page.js.map +2 -2
- package/dist/modules/warranty_claims/backend/warranty_claims/troubleshooting-guides/page.js +0 -3
- package/dist/modules/warranty_claims/backend/warranty_claims/troubleshooting-guides/page.js.map +2 -2
- package/dist/modules/warranty_claims/backend/warranty_claims/vendor-policies/page.js +0 -3
- package/dist/modules/warranty_claims/backend/warranty_claims/vendor-policies/page.js.map +2 -2
- package/dist/modules/warranty_claims/frontend/[orgSlug]/portal/claims/page.js +0 -3
- package/dist/modules/warranty_claims/frontend/[orgSlug]/portal/claims/page.js.map +2 -2
- package/dist/modules/wms/api/warehouses/route.js +9 -13
- package/dist/modules/wms/api/warehouses/route.js.map +2 -2
- package/dist/modules/wms/api/zones/route.js +2 -15
- package/dist/modules/wms/api/zones/route.js.map +2 -2
- package/dist/modules/wms/commands/configuration.js +9 -121
- package/dist/modules/wms/commands/configuration.js.map +2 -2
- package/dist/modules/wms/commands/inventory-actions.js +0 -3
- package/dist/modules/wms/commands/inventory-actions.js.map +2 -2
- package/dist/modules/wms/commands/shared.js +1 -9
- package/dist/modules/wms/commands/shared.js.map +2 -2
- package/dist/modules/wms/components/backend/WmsConfigurationPage.js +111 -76
- package/dist/modules/wms/components/backend/WmsConfigurationPage.js.map +3 -3
- package/dist/modules/wms/components/backend/WmsInventoryConsolePage.js +0 -1
- package/dist/modules/wms/components/backend/WmsInventoryConsolePage.js.map +2 -2
- package/dist/modules/wms/components/backend/WmsLocationDetailPage.js +20 -16
- package/dist/modules/wms/components/backend/WmsLocationDetailPage.js.map +2 -2
- package/dist/modules/wms/components/backend/WmsLotDetailPage.js +10 -6
- package/dist/modules/wms/components/backend/WmsLotDetailPage.js.map +2 -2
- package/dist/modules/wms/components/backend/WmsLotsListPage.js +0 -1
- package/dist/modules/wms/components/backend/WmsLotsListPage.js.map +2 -2
- package/dist/modules/wms/components/backend/WmsOperationalDashboardPage.js +11 -7
- package/dist/modules/wms/components/backend/WmsOperationalDashboardPage.js.map +2 -2
- package/dist/modules/wms/components/backend/WmsSkuDetailPage.js +10 -6
- package/dist/modules/wms/components/backend/WmsSkuDetailPage.js.map +2 -2
- package/dist/modules/wms/components/backend/inventoryMutationLoaders.js +20 -27
- package/dist/modules/wms/components/backend/inventoryMutationLoaders.js.map +2 -2
- package/dist/modules/workflows/backend/definitions/page.js +1 -3
- package/dist/modules/workflows/backend/definitions/page.js.map +2 -2
- package/dist/modules/workflows/backend/events/page.js +1 -3
- package/dist/modules/workflows/backend/events/page.js.map +2 -2
- package/dist/modules/workflows/backend/instances/page.js +1 -3
- package/dist/modules/workflows/backend/instances/page.js.map +2 -2
- package/dist/modules/workflows/backend/tasks/page.js +1 -3
- package/dist/modules/workflows/backend/tasks/page.js.map +2 -2
- package/dist/modules/workflows/lib/activity-executor.js +0 -4
- package/dist/modules/workflows/lib/activity-executor.js.map +2 -2
- package/package.json +10 -9
- package/src/helpers/integration/api.ts +1 -33
- package/src/helpers/integration/communicationChannelsFixtures.ts +1 -69
- package/src/modules/api_keys/api/keys/route.ts +1 -3
- package/src/modules/api_keys/backend/api-keys/page.tsx +1 -4
- package/src/modules/attachments/AGENTS.md +0 -15
- package/src/modules/attachments/api/route.ts +2 -2
- package/src/modules/attachments/components/AttachmentLibrary.tsx +0 -3
- package/src/modules/attachments/di.ts +0 -16
- package/src/modules/attachments/i18n/de.json +0 -5
- package/src/modules/attachments/i18n/en.json +0 -5
- package/src/modules/attachments/i18n/es.json +0 -5
- package/src/modules/attachments/i18n/ko.json +0 -5
- package/src/modules/attachments/i18n/pl.json +0 -5
- package/src/modules/attachments/index.ts +0 -10
- package/src/modules/attachments/lib/scoped-upload-service.ts +0 -46
- package/src/modules/attachments/lib/upload-limits.ts +2 -6
- package/src/modules/audit_logs/backend/audit-logs/page.tsx +0 -8
- package/src/modules/audit_logs/lib/display-helpers.tsx +32 -42
- package/src/modules/audit_logs/services/actionLogService.ts +11 -74
- package/src/modules/auth/acl.ts +6 -30
- package/src/modules/auth/api/feature-check.ts +3 -10
- package/src/modules/auth/api/login.ts +24 -9
- package/src/modules/auth/api/roles/acl/route.ts +39 -42
- package/src/modules/auth/api/routeError.ts +30 -0
- package/src/modules/auth/api/session/refresh.ts +1 -32
- package/src/modules/auth/api/users/acl/route.ts +101 -185
- package/src/modules/auth/api/users/route.ts +5 -18
- package/src/modules/auth/backend/roles/[id]/edit/page.meta.ts +2 -5
- package/src/modules/auth/backend/roles/page.tsx +2 -4
- package/src/modules/auth/backend/users/[id]/edit/page.meta.ts +2 -5
- package/src/modules/auth/backend/users/page.tsx +2 -3
- package/src/modules/auth/components/AclEditor.tsx +33 -63
- package/src/modules/auth/data/entities.ts +1 -4
- package/src/modules/auth/data/validators.ts +0 -5
- package/src/modules/auth/di.ts +16 -46
- package/src/modules/auth/frontend/reset/[token]/page.tsx +0 -74
- package/src/modules/auth/i18n/de.json +0 -389
- package/src/modules/auth/i18n/en.json +0 -389
- package/src/modules/auth/i18n/es.json +0 -389
- package/src/modules/auth/i18n/ko.json +0 -389
- package/src/modules/auth/i18n/pl.json +2 -391
- package/src/modules/auth/lib/grantChecks.ts +0 -6
- package/src/modules/auth/services/authService.ts +2 -18
- package/src/modules/auth/services/rbacService.ts +24 -110
- package/src/modules/business_rules/backend/logs/page.tsx +1 -4
- package/src/modules/business_rules/backend/rules/[id]/page.meta.ts +5 -0
- package/src/modules/business_rules/backend/rules/create/page.meta.ts +5 -1
- package/src/modules/business_rules/backend/rules/page.tsx +1 -4
- package/src/modules/business_rules/backend/sets/[id]/page.meta.ts +5 -0
- package/src/modules/business_rules/backend/sets/create/page.meta.ts +5 -1
- package/src/modules/business_rules/backend/sets/page.tsx +1 -4
- package/src/modules/business_rules/lib/expression-evaluator.ts +9 -9
- package/src/modules/business_rules/lib/rule-evaluator.ts +9 -10
- package/src/modules/catalog/ai-agents.ts +1 -1
- package/src/modules/catalog/ai-tools/merchandising-pack.ts +2 -2
- package/src/modules/catalog/backend/catalog/products/MerchandisingAssistantSheet.tsx +5 -12
- package/src/modules/catalog/components/categories/CategoriesDataTable.tsx +0 -3
- package/src/modules/catalog/components/products/ProductsDataTable.tsx +0 -7
- package/src/modules/catalog/i18n/de.json +0 -1
- package/src/modules/catalog/i18n/en.json +0 -1
- package/src/modules/catalog/i18n/es.json +0 -1
- package/src/modules/catalog/i18n/ko.json +0 -1
- package/src/modules/catalog/i18n/pl.json +0 -1
- package/src/modules/catalog/widgets/injection/merchandising-assistant-trigger/widget.client.tsx +0 -4
- package/src/modules/communication_channels/api/post/channels/[id]/test-send/route.ts +1 -17
- package/src/modules/communication_channels/api/post/test-seed/route.ts +16 -155
- package/src/modules/communication_channels/backend/communication_channels/channels/page.tsx +0 -4
- package/src/modules/communication_channels/commands/ingest-inbound-message.ts +1 -38
- package/src/modules/communication_channels/di.ts +0 -7
- package/src/modules/communication_channels/lib/adapter.ts +0 -14
- package/src/modules/communication_channels/lib/email-capabilities.ts +0 -4
- package/src/modules/communication_channels/lib/test-seed.ts +4 -64
- package/src/modules/configs/i18n/de.json +0 -2
- package/src/modules/configs/i18n/en.json +0 -2
- package/src/modules/configs/i18n/es.json +0 -2
- package/src/modules/configs/i18n/ko.json +0 -2
- package/src/modules/configs/i18n/pl.json +0 -2
- package/src/modules/configs/lib/system-status.ts +0 -9
- package/src/modules/currencies/backend/currencies/page.tsx +1 -4
- package/src/modules/currencies/backend/exchange-rates/page.tsx +1 -4
- package/src/modules/customer_accounts/api/admin/domain-mappings.ts +0 -7
- package/src/modules/customer_accounts/backend/customer_accounts/roles/page.tsx +1 -4
- package/src/modules/customer_accounts/backend/customer_accounts/settings/CustomerAccountsSettingsPageClient.tsx +138 -0
- package/src/modules/customer_accounts/backend/customer_accounts/settings/page.tsx +8 -132
- package/src/modules/customer_accounts/backend/customer_accounts/users/PortalUsersPageClient.tsx +598 -0
- package/src/modules/customer_accounts/backend/customer_accounts/users/[id]/PortalUserDetailPageClient.tsx +838 -0
- package/src/modules/customer_accounts/backend/customer_accounts/users/[id]/page.tsx +6 -838
- package/src/modules/customer_accounts/backend/customer_accounts/users/page.tsx +6 -590
- package/src/modules/customer_accounts/extension-points.ts +1 -1
- package/src/modules/customer_accounts/lib/portalUrl.ts +112 -0
- package/src/modules/customer_accounts/services/domainMappingService.ts +0 -10
- package/src/modules/customers/ai-tools/companies-pack.ts +1 -4
- package/src/modules/customers/ai-tools/deal-analyzer-pack.ts +2 -17
- package/src/modules/customers/ai-tools/deals-pack.ts +1 -66
- package/src/modules/customers/ai-tools/people-pack.ts +1 -4
- package/src/modules/customers/api/activities/route.ts +0 -45
- package/src/modules/customers/api/companies/[id]/people/route.ts +73 -24
- package/src/modules/customers/api/companies/[id]/route.ts +107 -24
- package/src/modules/customers/api/dashboard/widgets/customer-todos/route.ts +8 -16
- package/src/modules/customers/api/deals/[id]/companies/route.ts +1 -4
- package/src/modules/customers/api/deals/[id]/people/route.ts +1 -4
- package/src/modules/customers/api/deals/[id]/route.ts +28 -17
- package/src/modules/customers/api/deals/[id]/stats/route.ts +25 -10
- package/src/modules/customers/api/deals/aggregate/route.ts +4 -14
- package/src/modules/customers/api/deals/map/route.ts +0 -1
- package/src/modules/customers/api/deals/route.ts +1 -50
- package/src/modules/customers/api/entity-roles-factory.ts +4 -8
- package/src/modules/customers/api/interactions/route.ts +2 -8
- package/src/modules/customers/api/interactions/tasks/route.ts +1 -24
- package/src/modules/customers/api/people/[id]/companies/[linkId]/route.ts +4 -12
- package/src/modules/customers/api/people/[id]/companies/context.ts +1 -4
- package/src/modules/customers/api/people/[id]/companies/enriched/route.ts +1 -4
- package/src/modules/customers/api/people/[id]/route.ts +11 -16
- package/src/modules/customers/api/people/check-phone/route.ts +10 -75
- package/src/modules/customers/api/todos/route.ts +2 -27
- package/src/modules/customers/backend/customers/companies/page.tsx +4 -7
- package/src/modules/customers/backend/customers/deals/[id]/page.tsx +2 -7
- package/src/modules/customers/backend/customers/deals/page.tsx +4 -10
- package/src/modules/customers/backend/customers/deals/pipeline/components/Lane.tsx +14 -5
- package/src/modules/customers/backend/customers/deals/pipeline/components/StatusFilterPopover.tsx +27 -109
- package/src/modules/customers/backend/customers/people/page.tsx +4 -7
- package/src/modules/customers/backend/hooks/useEmailDuplicateCheck.ts +4 -12
- package/src/modules/customers/commands/deals.ts +59 -34
- package/src/modules/customers/commands/interactions.ts +1 -1
- package/src/modules/customers/commands/personCompanyLinks.ts +8 -224
- package/src/modules/customers/components/CustomerTodosTable.tsx +0 -2
- package/src/modules/customers/components/DealsKpiStrip.tsx +7 -7
- package/src/modules/customers/components/calendar/AgendaList.tsx +3 -3
- package/src/modules/customers/components/calendar/CalendarEventEditor.tsx +1 -1
- package/src/modules/customers/components/calendar/CalendarScreen.tsx +1 -1
- package/src/modules/customers/components/calendar/CalendarSettingsModal.tsx +1 -1
- package/src/modules/customers/components/calendar/EventBlock.tsx +2 -2
- package/src/modules/customers/components/calendar/EventPeekPopover.tsx +2 -4
- package/src/modules/customers/components/calendar/editor/PeopleField.tsx +5 -61
- package/src/modules/customers/components/calendar/editor/hooks.ts +1 -7
- package/src/modules/customers/components/calendar/types.ts +2 -2
- package/src/modules/customers/components/detail/AssignRoleDialog.tsx +12 -7
- package/src/modules/customers/components/detail/CompanyKpiBar.tsx +4 -5
- package/src/modules/customers/components/detail/CompanyPeopleSection.tsx +1 -8
- package/src/modules/customers/components/detail/DealForm.tsx +2 -22
- package/src/modules/customers/components/detail/DealsSection.tsx +5 -11
- package/src/modules/customers/components/detail/EntityTagsDialog.tsx +12 -58
- package/src/modules/customers/components/detail/PersonCompaniesSection.tsx +1 -8
- package/src/modules/customers/components/detail/hooks/usePersonTasks.ts +14 -23
- package/src/modules/customers/components/detail/schedule/ParticipantsField.tsx +5 -5
- package/src/modules/customers/components/detail/schedule/useScheduleFormState.ts +5 -5
- package/src/modules/customers/components/detail/types.ts +1 -1
- package/src/modules/customers/components/detail/utils.ts +5 -15
- package/src/modules/customers/data/entities.ts +1 -1
- package/src/modules/customers/data/validators.ts +9 -50
- package/src/modules/customers/events.ts +0 -5
- package/src/modules/customers/i18n/de.json +1 -9
- package/src/modules/customers/i18n/en.json +1 -9
- package/src/modules/customers/i18n/es.json +1 -9
- package/src/modules/customers/i18n/ko.json +1 -9
- package/src/modules/customers/i18n/pl.json +1 -9
- package/src/modules/customers/lib/calendar/conflicts.ts +2 -10
- package/src/modules/customers/lib/calendar/editorPayload.ts +3 -4
- package/src/modules/customers/lib/calendar/mapItem.ts +3 -8
- package/src/modules/customers/lib/dealStatus.ts +0 -42
- package/src/modules/customers/lib/findPeopleByAddresses.ts +6 -8
- package/src/modules/customers/lib/personCompanies.ts +1 -136
- package/src/modules/customers/lib/todoCompatibility.ts +1 -19
- package/src/modules/dashboards/i18n/es.json +4 -4
- package/src/modules/dashboards/i18n/pl.json +7 -7
- package/src/modules/dashboards/lib/widgetScope.ts +0 -6
- package/src/modules/data_sync/AGENTS.md +1 -1
- package/src/modules/data_sync/backend/data-sync/page.tsx +1 -4
- package/src/modules/data_sync/lib/adapter.ts +0 -36
- package/src/modules/data_sync/lib/sync-engine.ts +5 -113
- package/src/modules/data_sync/workers/sync-export.ts +0 -4
- package/src/modules/data_sync/workers/sync-import.ts +0 -4
- package/src/modules/devices/AGENTS.md +1 -20
- package/src/modules/devices/acl.ts +1 -9
- package/src/modules/devices/api/admin/devices/[id]/route.ts +34 -5
- package/src/modules/devices/api/admin/devices/route.ts +1 -14
- package/src/modules/devices/api/deviceList.ts +11 -135
- package/src/modules/devices/api/openapi.ts +1 -9
- package/src/modules/devices/api/route.ts +1 -9
- package/src/modules/devices/backend/devices/[id]/page.tsx +32 -18
- package/src/modules/devices/backend/devices/create/page.tsx +1 -17
- package/src/modules/devices/backend/devices/page.tsx +45 -44
- package/src/modules/devices/i18n/de.json +2 -3
- package/src/modules/devices/i18n/en.json +2 -3
- package/src/modules/devices/i18n/es.json +2 -3
- package/src/modules/devices/i18n/ko.json +2 -3
- package/src/modules/devices/i18n/pl.json +2 -3
- package/src/modules/devices/setup.ts +2 -6
- package/src/modules/directory/backend/directory/organizations/page.tsx +1 -3
- package/src/modules/directory/backend/directory/tenants/page.tsx +1 -3
- package/src/modules/directory/di.ts +2 -14
- package/src/modules/directory/utils/organizationScope.ts +15 -4
- package/src/modules/entities/api/records.ts +0 -2
- package/src/modules/entities/backend/entities/user/[entityId]/records/page.tsx +1 -4
- package/src/modules/eudr/api/plots/route.ts +0 -4
- package/src/modules/eudr/backend/eudr/evidence-submissions/[id]/page.meta.ts +2 -0
- package/src/modules/eudr/backend/eudr/evidence-submissions/create/page.meta.ts +1 -0
- package/src/modules/eudr/backend/eudr/evidence-submissions/page.meta.ts +4 -1
- package/src/modules/eudr/backend/eudr/evidence-submissions/page.tsx +0 -4
- package/src/modules/eudr/backend/eudr/page.meta.ts +2 -2
- package/src/modules/eudr/backend/eudr/plots/[id]/page.meta.ts +2 -0
- package/src/modules/eudr/backend/eudr/plots/[id]/page.tsx +8 -2
- package/src/modules/eudr/backend/eudr/plots/create/page.meta.ts +1 -0
- package/src/modules/eudr/backend/eudr/plots/create/page.tsx +8 -2
- package/src/modules/eudr/backend/eudr/plots/page.meta.ts +4 -1
- package/src/modules/eudr/backend/eudr/plots/page.tsx +0 -4
- package/src/modules/eudr/backend/eudr/product-mappings/[id]/page.meta.ts +2 -0
- package/src/modules/eudr/backend/eudr/product-mappings/create/page.meta.ts +1 -0
- package/src/modules/eudr/backend/eudr/product-mappings/page.meta.ts +4 -1
- package/src/modules/eudr/backend/eudr/product-mappings/page.tsx +0 -4
- package/src/modules/eudr/backend/eudr/risk-assessments/[id]/page.meta.ts +2 -0
- package/src/modules/eudr/backend/eudr/risk-assessments/create/page.meta.ts +1 -0
- package/src/modules/eudr/backend/eudr/risk-assessments/page.meta.ts +4 -1
- package/src/modules/eudr/backend/eudr/risk-assessments/page.tsx +0 -4
- package/src/modules/eudr/backend/eudr/statements/[id]/page.meta.ts +2 -0
- package/src/modules/eudr/backend/eudr/statements/[id]/page.tsx +2 -0
- package/src/modules/eudr/backend/eudr/statements/create/page.meta.ts +1 -0
- package/src/modules/eudr/backend/eudr/statements/page.meta.ts +4 -1
- package/src/modules/eudr/backend/eudr/statements/page.tsx +32 -30
- package/src/modules/eudr/commands/statements.ts +7 -2
- package/src/modules/eudr/components/crudErrorI18n.ts +31 -0
- package/src/modules/eudr/components/formConfig.tsx +9 -1
- package/src/modules/eudr/data/validators.ts +1 -16
- package/src/modules/eudr/i18n/de.json +3 -2
- package/src/modules/eudr/i18n/en.json +3 -2
- package/src/modules/eudr/i18n/es.json +3 -2
- package/src/modules/eudr/i18n/ko.json +3 -2
- package/src/modules/eudr/i18n/pl.json +3 -2
- package/src/modules/eudr/lib/statement-lifecycle.ts +8 -0
- package/src/modules/feature_toggles/components/FeatureTogglesTable.tsx +1 -2
- package/src/modules/feature_toggles/components/OverridesTable.tsx +1 -2
- package/src/modules/feature_toggles/defaults.json +8 -0
- package/src/modules/inbox_ops/backend/inbox-ops/log/page.tsx +0 -4
- package/src/modules/inbox_ops/backend/inbox-ops/page.tsx +0 -4
- package/src/modules/inbox_ops/lib/htmlToPlainText.ts +17 -1
- package/src/modules/integrations/api/[id]/credentials/route.ts +4 -6
- package/src/modules/integrations/backend/integrations/[id]/page.tsx +31 -61
- package/src/modules/integrations/backend/integrations/bundle/[id]/page.tsx +9 -44
- package/src/modules/integrations/data/validators.ts +2 -12
- package/src/modules/integrations/i18n/de.json +0 -1
- package/src/modules/integrations/i18n/en.json +0 -1
- package/src/modules/integrations/i18n/es.json +0 -1
- package/src/modules/integrations/i18n/ko.json +0 -1
- package/src/modules/integrations/i18n/pl.json +0 -1
- package/src/modules/integrations/lib/credentials-masking.ts +6 -10
- package/src/modules/messages/api/[id]/route.ts +1 -55
- package/src/modules/messages/api/openapi.ts +0 -20
- package/src/modules/messages/api/route.ts +2 -31
- package/src/modules/messages/components/MessagesInboxPageClient.tsx +16 -9
- package/src/modules/messages/components/inboxFilters.ts +0 -36
- package/src/modules/messages/components/message-detail/hooks/useMessageDetailsConversation.ts +1 -14
- package/src/modules/messages/components/message-detail/panels/MessageHeader.tsx +1 -3
- package/src/modules/messages/components/message-detail/panels/thread-panel.tsx +1 -2
- package/src/modules/messages/components/message-detail/types.ts +0 -3
- package/src/modules/messages/components/messageListLabels.ts +3 -52
- package/src/modules/messages/data/validators.ts +4 -43
- package/src/modules/payment_gateways/api/webhook/[provider]/route.ts +6 -4
- package/src/modules/payment_gateways/backend/payment-gateways/page.tsx +1 -4
- package/src/modules/payment_gateways/lib/webhook-processor.ts +0 -11
- package/src/modules/planner/backend/planner/availability-rulesets/page.tsx +0 -4
- package/src/modules/push_notifications/backend/push_notifications/page.tsx +1 -4
- package/src/modules/push_notifications/backend/push_notifications/send/page.tsx +4 -4
- package/src/modules/query_index/lib/engine.ts +61 -249
- package/src/modules/query_index/lib/search-tokens.ts +3 -135
- package/src/modules/resources/backend/resources/resource-types/page.tsx +1 -4
- package/src/modules/resources/backend/resources/resources/page.tsx +1 -4
- package/src/modules/sales/backend/sales/channels/offers/page.tsx +0 -4
- package/src/modules/sales/backend/sales/channels/page.tsx +0 -4
- package/src/modules/sales/backend/sales/documents/[id]/page.tsx +8 -14
- package/src/modules/sales/commands/documents.ts +0 -93
- package/src/modules/sales/components/PriceWithCurrency.tsx +5 -17
- package/src/modules/sales/components/channels/SalesChannelOffersPanel.tsx +0 -4
- package/src/modules/sales/components/documents/AdjustmentsSection.tsx +5 -6
- package/src/modules/sales/components/documents/ItemsSection.tsx +18 -56
- package/src/modules/sales/components/documents/LineItemDialog.tsx +21 -138
- package/src/modules/sales/components/documents/PaymentsSection.tsx +9 -20
- package/src/modules/sales/components/documents/ReturnsSection.tsx +5 -6
- package/src/modules/sales/components/documents/SalesDocumentsTable.tsx +14 -106
- package/src/modules/sales/components/documents/SalesOrderDraftLines.tsx +5 -6
- package/src/modules/sales/components/documents/ShipmentDialog.tsx +4 -5
- package/src/modules/sales/components/documents/ShipmentsSection.tsx +5 -6
- package/src/modules/sales/components/documents/lineItemUtils.ts +2 -7
- package/src/modules/sales/data/entities.ts +0 -6
- package/src/modules/sales/extension-points.ts +0 -1
- package/src/modules/sales/i18n/de.json +37 -8
- package/src/modules/sales/i18n/en.json +37 -8
- package/src/modules/sales/i18n/es.json +37 -8
- package/src/modules/sales/i18n/ko.json +37 -8
- package/src/modules/sales/i18n/pl.json +37 -8
- package/src/modules/sales/migrations/.snapshot-open-mercato.json +0 -60
- package/src/modules/sales/setup.ts +0 -2
- package/src/modules/sales/widgets/injection/document-history/widget.client.tsx +42 -2
- package/src/modules/sales/widgets/injection/payment-gateway-status-column/widget.ts +1 -9
- package/src/modules/sales/widgets/injection-table.ts +7 -4
- package/src/modules/staff/backend/staff/leave-requests/page.tsx +0 -4
- package/src/modules/staff/backend/staff/my-leave-requests/page.tsx +0 -4
- package/src/modules/staff/backend/staff/team-members/page.tsx +0 -6
- package/src/modules/staff/backend/staff/team-roles/page.tsx +0 -4
- package/src/modules/staff/backend/staff/teams/[id]/edit/page.tsx +0 -4
- package/src/modules/staff/backend/staff/teams/page.tsx +0 -4
- package/src/modules/staff/backend/staff/timesheets/page.tsx +1 -12
- package/src/modules/staff/backend/staff/timesheets/projects/page.tsx +0 -4
- package/src/modules/sync_excel/lib/adapters/customers.ts +0 -5
- package/src/modules/warranty_claims/api/assignees/route.ts +3 -3
- package/src/modules/warranty_claims/api/portal/attachments/route.ts +2 -2
- package/src/modules/warranty_claims/api/portal/claims/route.ts +9 -12
- package/src/modules/warranty_claims/backend/page.tsx +0 -4
- package/src/modules/warranty_claims/backend/warranty_claims/registrations/page.tsx +0 -4
- package/src/modules/warranty_claims/backend/warranty_claims/troubleshooting-guides/page.tsx +0 -4
- package/src/modules/warranty_claims/backend/warranty_claims/vendor-policies/page.tsx +0 -4
- package/src/modules/warranty_claims/frontend/[orgSlug]/portal/claims/page.tsx +0 -4
- package/src/modules/wms/api/warehouses/route.ts +9 -13
- package/src/modules/wms/api/zones/route.ts +0 -16
- package/src/modules/wms/commands/configuration.ts +10 -159
- package/src/modules/wms/commands/inventory-actions.ts +0 -3
- package/src/modules/wms/commands/shared.ts +0 -17
- package/src/modules/wms/components/backend/WmsConfigurationPage.tsx +141 -95
- package/src/modules/wms/components/backend/WmsInventoryConsolePage.tsx +0 -2
- package/src/modules/wms/components/backend/WmsLocationDetailPage.tsx +30 -26
- package/src/modules/wms/components/backend/WmsLotDetailPage.tsx +21 -17
- package/src/modules/wms/components/backend/WmsLotsListPage.tsx +0 -2
- package/src/modules/wms/components/backend/WmsOperationalDashboardPage.tsx +17 -13
- package/src/modules/wms/components/backend/WmsSkuDetailPage.tsx +16 -12
- package/src/modules/wms/components/backend/inventoryMutationLoaders.ts +20 -36
- package/src/modules/wms/i18n/de.json +0 -2
- package/src/modules/wms/i18n/en.json +0 -2
- package/src/modules/wms/i18n/es.json +0 -2
- package/src/modules/wms/i18n/ko.json +0 -2
- package/src/modules/wms/i18n/pl.json +0 -2
- package/src/modules/workflows/backend/definitions/page.tsx +1 -4
- package/src/modules/workflows/backend/events/page.tsx +1 -4
- package/src/modules/workflows/backend/instances/page.tsx +1 -4
- package/src/modules/workflows/backend/tasks/page.tsx +1 -4
- package/src/modules/workflows/lib/activity-executor.ts +0 -9
- package/dist/modules/api_keys/di.js +0 -9
- package/dist/modules/api_keys/di.js.map +0 -7
- package/dist/modules/api_keys/services/principalService.js +0 -31
- package/dist/modules/api_keys/services/principalService.js.map +0 -7
- package/dist/modules/attachments/lib/attachment-service.js +0 -313
- package/dist/modules/attachments/lib/attachment-service.js.map +0 -7
- package/dist/modules/auth/api/reset/validate.js +0 -55
- package/dist/modules/auth/api/reset/validate.js.map +0 -7
- package/dist/modules/auth/commands/acl.js +0 -254
- package/dist/modules/auth/commands/acl.js.map +0 -7
- package/dist/modules/auth/lib/userIdFilter.js +0 -16
- package/dist/modules/auth/lib/userIdFilter.js.map +0 -7
- package/dist/modules/auth/services/principalService.js +0 -200
- package/dist/modules/auth/services/principalService.js.map +0 -7
- package/dist/modules/auth/services/roleOrganizationScope.js +0 -41
- package/dist/modules/auth/services/roleOrganizationScope.js.map +0 -7
- package/dist/modules/communication_channels/lib/outbound-recipient.js +0 -33
- package/dist/modules/communication_channels/lib/outbound-recipient.js.map +0 -7
- package/dist/modules/communication_channels/lib/resolve-channel-type.js +0 -51
- package/dist/modules/communication_channels/lib/resolve-channel-type.js.map +0 -7
- package/dist/modules/customers/backend/customers/deals/pipeline/components/toneClasses.js +0 -18
- package/dist/modules/customers/backend/customers/deals/pipeline/components/toneClasses.js.map +0 -7
- package/dist/modules/customers/lib/calendar/participantIdentity.js +0 -9
- package/dist/modules/customers/lib/calendar/participantIdentity.js.map +0 -7
- package/dist/modules/customers/lib/closureStage.js +0 -45
- package/dist/modules/customers/lib/closureStage.js.map +0 -7
- package/dist/modules/customers/lib/detailReadAccess.js +0 -12
- package/dist/modules/customers/lib/detailReadAccess.js.map +0 -7
- package/dist/modules/data_sync/lib/abandoned-run.js +0 -19
- package/dist/modules/data_sync/lib/abandoned-run.js.map +0 -7
- package/dist/modules/devices/api/deviceSerialization.js +0 -46
- package/dist/modules/devices/api/deviceSerialization.js.map +0 -7
- package/dist/modules/devices/backend/devices/useDeviceUserLabels.js +0 -34
- package/dist/modules/devices/backend/devices/useDeviceUserLabels.js.map +0 -7
- package/dist/modules/devices/backend/devices/userOptions.js +0 -52
- package/dist/modules/devices/backend/devices/userOptions.js.map +0 -7
- package/dist/modules/directory/services/organizationHierarchyService.js +0 -29
- package/dist/modules/directory/services/organizationHierarchyService.js.map +0 -7
- package/dist/modules/directory/services/organizationScopeService.js +0 -56
- package/dist/modules/directory/services/organizationScopeService.js.map +0 -7
- package/dist/modules/integrations/backend/integrations/credential-secret-fields.js +0 -55
- package/dist/modules/integrations/backend/integrations/credential-secret-fields.js.map +0 -7
- package/dist/modules/messages/lib/channel-sender-identity.js +0 -26
- package/dist/modules/messages/lib/channel-sender-identity.js.map +0 -7
- package/dist/modules/messages/lib/composeSourceChannelType.js +0 -41
- package/dist/modules/messages/lib/composeSourceChannelType.js.map +0 -7
- package/dist/modules/query_index/lib/search-entity-policy.js +0 -14
- package/dist/modules/query_index/lib/search-entity-policy.js.map +0 -7
- package/dist/modules/sales/components/documents/lineItemShipmentLock.js +0 -54
- package/dist/modules/sales/components/documents/lineItemShipmentLock.js.map +0 -7
- package/dist/modules/sales/lib/salesChannelsToggleSeed.js +0 -33
- package/dist/modules/sales/lib/salesChannelsToggleSeed.js.map +0 -7
- package/dist/modules/sales/migrations/Migration20260821120000_sales_notes_addresses_context_idx.js +0 -32
- package/dist/modules/sales/migrations/Migration20260821120000_sales_notes_addresses_context_idx.js.map +0 -7
- package/dist/modules/wms/api/warehouseSearch.js +0 -21
- package/dist/modules/wms/api/warehouseSearch.js.map +0 -7
- package/dist/modules/wms/components/backend/WarehouseEditDialog.js +0 -158
- package/dist/modules/wms/components/backend/WarehouseEditDialog.js.map +0 -7
- package/dist/modules/wms/components/backend/warehouseFormOptions.js +0 -66
- package/dist/modules/wms/components/backend/warehouseFormOptions.js.map +0 -7
- package/src/modules/api_keys/di.ts +0 -7
- package/src/modules/api_keys/services/principalService.ts +0 -33
- package/src/modules/attachments/lib/attachment-service.ts +0 -468
- package/src/modules/auth/api/reset/validate.ts +0 -57
- package/src/modules/auth/commands/acl.ts +0 -495
- package/src/modules/auth/lib/userIdFilter.ts +0 -31
- package/src/modules/auth/services/principalService.ts +0 -266
- package/src/modules/auth/services/roleOrganizationScope.ts +0 -70
- package/src/modules/communication_channels/lib/outbound-recipient.ts +0 -71
- package/src/modules/communication_channels/lib/resolve-channel-type.ts +0 -99
- package/src/modules/customers/backend/customers/deals/pipeline/components/toneClasses.ts +0 -21
- package/src/modules/customers/lib/calendar/participantIdentity.ts +0 -17
- package/src/modules/customers/lib/closureStage.ts +0 -76
- package/src/modules/customers/lib/detailReadAccess.ts +0 -35
- package/src/modules/data_sync/lib/abandoned-run.ts +0 -56
- package/src/modules/devices/api/deviceSerialization.ts +0 -50
- package/src/modules/devices/backend/devices/useDeviceUserLabels.ts +0 -45
- package/src/modules/devices/backend/devices/userOptions.ts +0 -99
- package/src/modules/directory/services/organizationHierarchyService.ts +0 -34
- package/src/modules/directory/services/organizationScopeService.ts +0 -85
- package/src/modules/integrations/backend/integrations/credential-secret-fields.ts +0 -84
- package/src/modules/messages/lib/channel-sender-identity.ts +0 -55
- package/src/modules/messages/lib/composeSourceChannelType.ts +0 -95
- package/src/modules/query_index/lib/search-entity-policy.ts +0 -46
- package/src/modules/sales/components/documents/lineItemShipmentLock.ts +0 -74
- package/src/modules/sales/lib/salesChannelsToggleSeed.ts +0 -42
- package/src/modules/sales/migrations/Migration20260821120000_sales_notes_addresses_context_idx.ts +0 -87
- package/src/modules/wms/api/warehouseSearch.ts +0 -23
- package/src/modules/wms/components/backend/WarehouseEditDialog.tsx +0 -216
- package/src/modules/wms/components/backend/warehouseFormOptions.ts +0 -72
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/modules/catalog/ai-tools/merchandising-pack.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * D18 catalog merchandising read tools (Phase 1 WS-C, Step 3.11).\n *\n * Ships the seven canonical tool names the `catalog.merchandising_assistant`\n * agent (Step 4.9) whitelists verbatim:\n *\n * - `catalog.search_products` \u2014 fulltext + filter search (hybrid path).\n * - `catalog.get_product_bundle` \u2014 aggregate bundle for a single product.\n * - `catalog.list_selected_products` \u2014 bundle aggregate for an ID array.\n * - `catalog.get_product_media` \u2014 media metadata (attachment IDs only \u2014\n * the Step 3.7 attachment bridge converts these to model file parts at\n * runtime invocation; this tool does NOT call the bridge directly).\n * - `catalog.get_attribute_schema` \u2014 merged module + category + product\n * custom-field schema.\n * - `catalog.get_category_brief` \u2014 category snapshot with inherited schema.\n * - `catalog.list_price_kinds` \u2014 D18 spec-named price-kind enumerator.\n *\n * Every tool is read-only (no `isMutation: true`). Mutation tooling for D18\n * lands in Step 5.14 under the pending-action contract.\n *\n * Tenant scoping: all DB access uses `findWithDecryption` /\n * `findOneWithDecryption`, plus a defensive post-filter against\n * `row.tenantId === ctx.tenantId`. Cross-tenant IDs surface through the\n * `missingIds` output (not an error), so a chat agent receives a uniform\n * not-found signal whether the product is missing, deleted, or out of scope.\n *\n * Shared helpers:\n * `list_price_kinds` + Step 3.10's `list_price_kinds_base` both route\n * through `listPriceKindsCore` in `./_shared.ts`; there is no duplicate\n * query path.\n *\n * Step 3.12 (authoring pack) promoted the product-bundle builder plus\n * `toProductSummary` / `resolveAttributeSchema` into `./_shared.ts` so\n * both packs consume the same loader. This file now just wires the\n * shared helpers into tool handlers.\n */\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { z } from 'zod'\nimport { findOneWithDecryption, findWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport { type QueryEngine, type QueryResult, SortDir } from '@open-mercato/shared/lib/query/types'\nimport { E } from '#generated/entities.ids.generated'\nimport { Attachment } from '@open-mercato/core/modules/attachments/data/entities'\nimport {\n CatalogProduct,\n CatalogProductCategory,\n CatalogProductCategoryAssignment,\n CatalogProductPrice,\n CatalogProductTag,\n CatalogProductTagAssignment,\n} from '../data/entities'\nimport { assertTenantScope, type CatalogAiToolDefinition, type CatalogToolContext } from './types'\nimport {\n buildProductBundle,\n buildScope,\n listPriceKindsCore,\n resolveAttributeSchema,\n resolveEm,\n toPriceNumeric,\n toProductSummary,\n type ProductBundle,\n type ProductBundleResult,\n} from './_shared'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\n\nconst logger = createLogger('catalog')\n\ntype SearchServiceLike = {\n search: (query: string, options: {\n tenantId: string\n organizationId?: string | null\n limit?: number\n entityTypes?: string[]\n }) => Promise<Array<{\n entityId: string\n recordId: string\n score: number\n source: string\n presenter?: unknown\n url?: string\n }>>\n}\n\nconst CATALOG_PRODUCT_ENTITY = 'catalog:catalog_product'\n\nfunction resolveSearchService(ctx: CatalogToolContext): SearchServiceLike | null {\n try {\n return ctx.container.resolve<SearchServiceLike>('searchService')\n } catch {\n return null\n }\n}\n\n/* -------------------------------------------------------------------------- */\n/* catalog.search_products */\n/* -------------------------------------------------------------------------- */\n\nconst searchProductsInput = z\n .object({\n q: z.string().trim().optional().describe('Optional fulltext query (title / subtitle / sku / handle). Omit or leave empty (or do NOT pass it at all) to list all products. NEVER use \"*\", \"**\", or \"%\" \u2014 they are not wildcards and will be discarded.'),\n limit: z.number().int().min(1).max(100).optional().describe('Page size. Default 50, hard maximum 100. Results are paginated: keep calling with the next `offset` until a page returns fewer than `limit` items, instead of asking for more than 100 rows. Do not treat `offset >= total` as the end \u2014 `total` is a floor when `totalIsCapped` is true.'),\n offset: z.number().int().min(0).optional().describe('Rows to skip for pagination (default 0). Combine with `limit` to fetch subsequent pages \u2014 for example offset=100, limit=100 fetches rows 101..200.'),\n categoryId: z.string().optional().describe('Restrict to products assigned to this catalog category UUID. Only use a category ID returned by a previous tool call \u2014 do NOT guess. Empty string is treated the same as omitting the field.'),\n priceMin: z.number().optional().describe('Lower-bound (inclusive) on the gross unit price. OMIT this field when you do not want a lower bound \u2014 do NOT pass 0 as \"no minimum\", because 0 is a real bound that combined with priceMax=0 will return only free products.'),\n priceMax: z.number().optional().describe('Upper-bound (inclusive) on the gross unit price. OMIT this field when you do not want an upper bound \u2014 do NOT pass 0 as \"no maximum\", because 0 is a real bound that will exclude every priced product.'),\n tags: z.array(z.string()).optional().describe('Tag labels or slugs (any-match) the product carries. Omit or pass an empty array to skip the tag filter.'),\n active: z.boolean().optional().describe('When true, only active products are returned. Omit to include inactive products as well.'),\n })\n .passthrough()\n\ntype SearchProductsInput = z.infer<typeof searchProductsInput>\n\nasync function queryProductsWithFilters(\n em: EntityManager,\n ctx: CatalogToolContext,\n tenantId: string,\n input: SearchProductsInput,\n restrictToIds: string[] | null,\n): Promise<{ items: ReturnType<typeof toProductSummary>[]; total: number }> {\n const limit = input.limit ?? 50\n const offset = input.offset ?? 0\n\n let idRestriction: string[] | null = restrictToIds\n ? Array.from(new Set(restrictToIds))\n : null\n\n if (input.categoryId) {\n const assignments = await findWithDecryption<CatalogProductCategoryAssignment>(\n em,\n CatalogProductCategoryAssignment,\n { tenantId, category: input.categoryId } as any,\n undefined,\n buildScope(ctx, tenantId),\n )\n const ids = assignments\n .map((assignment) => {\n const product = (assignment as any).product\n if (!product) return null\n return typeof product === 'string' ? product : product.id ?? null\n })\n .filter((value: string | null): value is string => typeof value === 'string' && value.length > 0)\n if (!ids.length) return { items: [], total: 0 }\n idRestriction = idRestriction\n ? idRestriction.filter((id) => ids.includes(id))\n : ids\n if (!idRestriction.length) return { items: [], total: 0 }\n }\n\n if (input.tags && input.tags.length > 0) {\n const tagWhere: Record<string, unknown> = {\n tenantId,\n $or: [\n { slug: { $in: input.tags } },\n { label: { $in: input.tags } },\n ],\n }\n if (ctx.organizationId) tagWhere.organizationId = ctx.organizationId\n const tags = await findWithDecryption<CatalogProductTag>(\n em,\n CatalogProductTag,\n tagWhere as any,\n undefined,\n buildScope(ctx, tenantId),\n )\n const tagIds = tags.map((tag) => tag.id)\n if (!tagIds.length) return { items: [], total: 0 }\n const assignments = await findWithDecryption<CatalogProductTagAssignment>(\n em,\n CatalogProductTagAssignment,\n { tenantId, tag: { $in: tagIds } } as any,\n undefined,\n buildScope(ctx, tenantId),\n )\n const scopedIds = assignments\n .map((assignment) => {\n const product = (assignment as any).product\n if (!product) return null\n return typeof product === 'string' ? product : product.id ?? null\n })\n .filter((value: string | null): value is string => typeof value === 'string' && value.length > 0)\n if (!scopedIds.length) return { items: [], total: 0 }\n idRestriction = idRestriction\n ? idRestriction.filter((id) => scopedIds.includes(id))\n : scopedIds\n if (!idRestriction.length) return { items: [], total: 0 }\n }\n\n if (input.priceMin !== undefined || input.priceMax !== undefined) {\n const priceWhere: Record<string, unknown> = { tenantId }\n if (ctx.organizationId) priceWhere.organizationId = ctx.organizationId\n const priceRows = await findWithDecryption<CatalogProductPrice>(\n em,\n CatalogProductPrice,\n priceWhere as any,\n undefined,\n buildScope(ctx, tenantId),\n )\n const bounded = priceRows.filter((row) => {\n const net = toPriceNumeric(row.unitPriceNet ?? null)\n const gross = toPriceNumeric(row.unitPriceGross ?? null)\n const comparable = gross ?? net\n if (comparable === null) return false\n if (input.priceMin !== undefined && comparable < input.priceMin) return false\n if (input.priceMax !== undefined && comparable > input.priceMax) return false\n return true\n })\n const scopedIds = bounded\n .map((row) => {\n const product = (row as any).product\n if (!product) return null\n return typeof product === 'string' ? product : product.id ?? null\n })\n .filter((value: string | null): value is string => typeof value === 'string' && value.length > 0)\n if (!scopedIds.length) return { items: [], total: 0 }\n idRestriction = idRestriction\n ? idRestriction.filter((id) => scopedIds.includes(id))\n : Array.from(new Set(scopedIds))\n if (!idRestriction.length) return { items: [], total: 0 }\n }\n\n const filters: Record<string, unknown> = {}\n if (input.active === true) filters.is_active = true\n if (input.q) {\n const pattern = `%${input.q}%`\n filters.$or = [\n { title: { $ilike: pattern } },\n { subtitle: { $ilike: pattern } },\n { sku: { $ilike: pattern } },\n { handle: { $ilike: pattern } },\n ]\n }\n if (idRestriction) {\n filters.id = { $in: idRestriction }\n }\n\n const qe = ctx.container.resolve<QueryEngine>('queryEngine')\n const result: QueryResult = await qe.query('catalog:catalog_product', {\n filters,\n sort: [{ field: 'created_at', dir: SortDir.Desc }],\n page: { page: Math.floor(offset / limit) + 1, pageSize: limit },\n tenantId,\n organizationId: ctx.organizationId ?? undefined,\n })\n\n const productIds = result.items\n .map((row: Record<string, unknown>) => row.id as string)\n .filter((id): id is string => typeof id === 'string')\n if (!productIds.length) return { items: [], total: result.total }\n\n const products = await findWithDecryption<CatalogProduct>(\n em,\n CatalogProduct,\n { tenantId, id: { $in: productIds }, deletedAt: null } as any,\n undefined,\n buildScope(ctx, tenantId),\n )\n const productById = new Map(products.map((p) => [p.id, p]))\n const ordered = productIds\n .map((id) => productById.get(id))\n .filter((p): p is CatalogProduct => !!p)\n\n return { items: ordered.map(toProductSummary), total: result.total }\n}\n\nconst searchProductsTool: CatalogAiToolDefinition = {\n name: 'catalog.search_products',\n displayName: 'Search products',\n description:\n 'Hybrid search + filter across tenant products. When `q` is non-empty, routes through the search service (tenant + organization scoped) then hydrates tenant-scoped product summaries; when `q` is empty or omitted, runs the catalog query engine with the supplied filters and returns ALL products. ' +\n 'Pagination: response shape is `{ items, total, totalIsCapped, limit, offset, source }`. Hard cap is 100 rows per call (`limit` max=100, default=50); keep calling with the next `offset` until a page returns fewer than `limit` items, rather than asking for >100. When `totalIsCapped` is true, `total` is a floor \u2014 render it as \"at least N\" and never treat `offset >= total` as exhaustion. ' +\n 'Empty / sentinel inputs: pass an empty `q` (or omit it) to list all products. NEVER pass `0` for `priceMin`/`priceMax` to mean \"no bound\" \u2014 `0` is a real numeric bound (priceMin=0 + priceMax=0 returns only free products). To remove a bound, OMIT the field. Empty string `categoryId` and empty `tags` arrays are ignored. ' +\n '`source` in the output indicates which path executed (`search_service` vs `query_engine`).',\n inputSchema: searchProductsInput,\n requiredFeatures: ['catalog.products.view'],\n tags: ['read', 'catalog', 'merchandising'],\n isMutation: false,\n handler: async (rawInput, ctx) => {\n const { tenantId } = assertTenantScope(ctx)\n const input = searchProductsInput.parse(rawInput)\n const em = resolveEm(ctx)\n const limit = input.limit ?? 50\n const offset = input.offset ?? 0\n\n // Treat wildcard-style and empty queries as \"list all\".\n if (!input.q || input.q === '*' || input.q === '**' || input.q === '%') {\n input.q = undefined\n }\n\n // Empty-string sentinel for `categoryId` is the agent's way of saying\n // \"no category filter\" \u2014 normalize before the UUID guard runs.\n if (typeof input.categoryId === 'string' && input.categoryId.trim() === '') {\n input.categoryId = undefined\n }\n\n // Empty `tags` array is meaningless as a filter; drop it so the price /\n // category narrowing path doesn't execute a no-op tag join.\n if (Array.isArray(input.tags) && input.tags.length === 0) {\n input.tags = undefined\n }\n\n // Agents commonly pass `priceMin: 0` / `priceMax: 0` thinking 0 means\n // \"no bound\". It does not \u2014 0 is a real inclusive bound and the two\n // together ask for free products only. Treat the (0, 0) pair as\n // \"unset\" so the empty-bound default carries through. A standalone 0\n // (e.g. priceMin=0 with priceMax=50) is left intact because it can be\n // a meaningful \"everything from free up to 50\" filter.\n if (\n input.priceMin === 0 &&\n input.priceMax === 0\n ) {\n input.priceMin = undefined\n input.priceMax = undefined\n }\n\n // Guard against hallucinated or invalid category IDs.\n if (input.categoryId) {\n const uuidPattern = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i\n if (!uuidPattern.test(input.categoryId)) {\n input.categoryId = undefined\n } else {\n try {\n const catExists = await em.count(CatalogProductCategory, { id: input.categoryId, tenantId } as any)\n if (catExists === 0) input.categoryId = undefined\n } catch {\n input.categoryId = undefined\n }\n }\n }\n\n if (input.q && input.q.trim().length > 0) {\n const service = resolveSearchService(ctx)\n if (service) {\n const hits = await service.search(input.q.trim(), {\n tenantId,\n organizationId: ctx.organizationId,\n limit,\n entityTypes: [CATALOG_PRODUCT_ENTITY],\n })\n const hitIds = hits\n .filter((hit) => hit.entityId === CATALOG_PRODUCT_ENTITY)\n .map((hit) => hit.recordId)\n .filter((id): id is string => typeof id === 'string' && id.length > 0)\n if (!hitIds.length) {\n return { items: [], total: 0, limit, offset, source: 'search_service' as const }\n }\n // Hydrate tenant-scoped products from the hit IDs, then apply any\n // structured filters the search service can't express (category /\n // price / tags / active). Search services in this repo do not\n // currently accept structured filters, so we narrow in-process and\n // document that in the return report + description.\n const { items, total } = await queryProductsWithFilters(em, ctx, tenantId, { ...input, q: undefined }, hitIds)\n return { items, total, limit, offset, source: 'search_service' as const }\n }\n // Fall through to the query-engine path if the search service is not\n // registered in the DI container \u2014 keeps the tool usable in test\n // harnesses and during early bring-up.\n }\n\n const { items, total } = await queryProductsWithFilters(em, ctx, tenantId, input, null)\n return { items, total, limit, offset, source: 'query_engine' as const }\n },\n}\n\n/* -------------------------------------------------------------------------- */\n/* catalog.get_product_bundle / catalog.list_selected_products */\n/* -------------------------------------------------------------------------- */\n\nconst getProductBundleInput = z.object({\n productId: z.string().uuid().describe('Catalog product id (UUID).'),\n})\n\nconst getProductBundleTool: CatalogAiToolDefinition = {\n name: 'catalog.get_product_bundle',\n displayName: 'Get product bundle',\n description:\n 'Aggregate product snapshot for D18 merchandising: core fields + categories + tags + variants + prices (base + best via pricing service) + media metadata + custom-field values + merged attribute schema. Media bytes flow through the attachment bridge (use `catalog.get_product_media` then the bridge). Returns `{ found: false }` on miss or cross-tenant access.',\n inputSchema: getProductBundleInput,\n requiredFeatures: ['catalog.products.view'],\n tags: ['read', 'catalog', 'merchandising'],\n isMutation: false,\n handler: async (rawInput, ctx) => {\n const { tenantId } = assertTenantScope(ctx)\n const input = getProductBundleInput.parse(rawInput)\n const em = resolveEm(ctx)\n return buildProductBundle(em, ctx, tenantId, input.productId)\n },\n}\n\nconst listSelectedProductsInput = z.object({\n productIds: z\n .array(z.string().uuid())\n .min(1)\n .max(50)\n .describe('1..50 catalog product ids (UUIDs). Duplicates are collapsed; cross-tenant ids drop into `missingIds`.'),\n})\n\nconst listSelectedProductsTool: CatalogAiToolDefinition = {\n name: 'catalog.list_selected_products',\n displayName: 'List selected products (bundles)',\n description:\n 'Bulk variant of `catalog.get_product_bundle`: resolves 1..50 product ids into tenant-scoped bundle aggregates. Missing / cross-tenant ids are returned in `missingIds` (not as an error) so selection-aware agents can render partial results.',\n inputSchema: listSelectedProductsInput,\n requiredFeatures: ['catalog.products.view'],\n tags: ['read', 'catalog', 'merchandising'],\n isMutation: false,\n handler: async (rawInput, ctx) => {\n const { tenantId } = assertTenantScope(ctx)\n const input = listSelectedProductsInput.parse(rawInput)\n const em = resolveEm(ctx)\n const unique = Array.from(new Set(input.productIds))\n const resolved = await Promise.all(\n unique.map(async (productId) => ({ productId, result: await buildProductBundle(em, ctx, tenantId, productId) })),\n )\n const items: ProductBundle[] = []\n const missingIds: string[] = []\n for (const entry of resolved) {\n if (entry.result.found) {\n items.push(entry.result)\n } else {\n missingIds.push(entry.productId)\n logger.warn('catalog.list_selected_products product not in scope', { productId: entry.productId })\n }\n }\n return { items, missingIds }\n },\n}\n\n/* -------------------------------------------------------------------------- */\n/* catalog.get_product_media */\n/* -------------------------------------------------------------------------- */\n\nconst getProductMediaInput = z.object({\n productId: z.string().uuid().describe('Catalog product id (UUID).'),\n limit: z.number().int().min(1).max(100).optional().describe('Max rows (default 50, max 100).'),\n offset: z.number().int().min(0).optional().describe('Rows to skip (default 0).'),\n})\n\nconst getProductMediaTool: CatalogAiToolDefinition = {\n name: 'catalog.get_product_media',\n displayName: 'Get product media (with attachment IDs)',\n description:\n 'List media records attached to a product with metadata (filename, mime, size, sort order) and the `attachmentId` string for each row. Does NOT invoke the attachment bridge \u2014 the Step 3.7 runtime bridge converts attachment ids into model file parts when the chat/object helper invokes this tool in-context. Returns `{ items, total, limit, offset }`.',\n inputSchema: getProductMediaInput,\n requiredFeatures: ['catalog.products.view'],\n tags: ['read', 'catalog', 'merchandising'],\n isMutation: false,\n handler: async (rawInput, ctx) => {\n const { tenantId } = assertTenantScope(ctx)\n const input = getProductMediaInput.parse(rawInput)\n const em = resolveEm(ctx)\n const limit = input.limit ?? 50\n const offset = input.offset ?? 0\n const where: Record<string, unknown> = {\n tenantId,\n entityId: E.catalog.catalog_product,\n recordId: input.productId,\n }\n if (ctx.organizationId) where.organizationId = ctx.organizationId\n const [rows, total] = await Promise.all([\n findWithDecryption<Attachment>(\n em,\n Attachment,\n where as any,\n { limit, offset, orderBy: { createdAt: 'asc' } as any } as any,\n buildScope(ctx, tenantId),\n ),\n em.count(Attachment, where as any),\n ])\n const filtered = rows.filter((row) => (row.tenantId ?? null) === tenantId)\n return {\n items: filtered.map((row) => ({\n mediaId: row.id,\n productId: input.productId,\n attachmentId: row.id,\n fileName: row.fileName,\n mediaType: row.mimeType,\n size: row.fileSize,\n altText: null,\n sortOrder: 0,\n })),\n total,\n limit,\n offset,\n }\n },\n}\n\n/* -------------------------------------------------------------------------- */\n/* catalog.get_attribute_schema / catalog.get_category_brief */\n/* -------------------------------------------------------------------------- */\n\nconst getAttributeSchemaInput = z.object({\n productId: z.string().uuid().optional().describe('Narrow schema resolution to this product (scope: `product`).'),\n categoryId: z.string().uuid().optional().describe('Narrow schema resolution to this category (scope: `category`).'),\n})\n\nconst getAttributeSchemaTool: CatalogAiToolDefinition = {\n name: 'catalog.get_attribute_schema',\n displayName: 'Get attribute schema',\n description:\n 'Resolve the merged custom-field attribute schema for catalog products. When both `productId` and `categoryId` are absent, returns module-level fields only. Reuses the shared `loadCustomFieldDefinitionIndex` resolver so tenant + organization scoping rules stay consistent with CrudForm / admin routes.',\n inputSchema: getAttributeSchemaInput,\n requiredFeatures: ['catalog.products.view'],\n tags: ['read', 'catalog', 'merchandising'],\n isMutation: false,\n handler: async (rawInput, ctx) => {\n const { tenantId } = assertTenantScope(ctx)\n const input = getAttributeSchemaInput.parse(rawInput)\n return resolveAttributeSchema(ctx, tenantId, input.productId, input.categoryId)\n },\n}\n\nconst getCategoryBriefInput = z.object({\n categoryId: z.string().uuid().describe('Category id (UUID).'),\n})\n\nconst getCategoryBriefTool: CatalogAiToolDefinition = {\n name: 'catalog.get_category_brief',\n displayName: 'Get category brief',\n description:\n 'Category name, full tree path, description, and merged attribute schema (same resolver as `catalog.get_attribute_schema` with `categoryId`). Returns `{ found: false }` on miss / cross-tenant.',\n inputSchema: getCategoryBriefInput,\n requiredFeatures: ['catalog.categories.view'],\n tags: ['read', 'catalog', 'merchandising'],\n isMutation: false,\n handler: async (rawInput, ctx) => {\n const { tenantId } = assertTenantScope(ctx)\n const input = getCategoryBriefInput.parse(rawInput)\n const em = resolveEm(ctx)\n const where: Record<string, unknown> = {\n id: input.categoryId,\n tenantId,\n deletedAt: null,\n }\n if (ctx.organizationId) where.organizationId = ctx.organizationId\n const category = await findOneWithDecryption<CatalogProductCategory>(\n em,\n CatalogProductCategory,\n where as any,\n undefined,\n buildScope(ctx, tenantId),\n )\n if (!category || category.tenantId !== tenantId) {\n return { found: false as const, categoryId: input.categoryId }\n }\n const attributeSchema = await resolveAttributeSchema(ctx, tenantId, undefined, category.id)\n return {\n found: true as const,\n id: category.id,\n name: category.name,\n path: category.treePath ?? null,\n description: category.description ?? null,\n attributeSchema,\n }\n },\n}\n\n/* -------------------------------------------------------------------------- */\n/* catalog.list_price_kinds */\n/* -------------------------------------------------------------------------- */\n\nconst listPriceKindsInput = z\n .object({\n limit: z.number().int().min(1).max(100).optional().describe('Max rows (default 50, max 100).'),\n offset: z.number().int().min(0).optional().describe('Rows to skip (default 0).'),\n })\n .passthrough()\n\nconst listPriceKindsTool: CatalogAiToolDefinition = {\n name: 'catalog.list_price_kinds',\n displayName: 'List price kinds',\n description:\n 'Enumerate tenant price kinds for the D18 merchandising assistant. Shares the tenant-scoped query path with `catalog.list_price_kinds_base`; the two tools differ only in description/framing (the base tool is the low-level settings enumerator, this one is the spec-named D18 surface).',\n inputSchema: listPriceKindsInput,\n requiredFeatures: ['catalog.settings.manage'],\n tags: ['read', 'catalog', 'merchandising'],\n isMutation: false,\n handler: async (rawInput, ctx) => {\n const { tenantId } = assertTenantScope(ctx)\n const input = listPriceKindsInput.parse(rawInput)\n const base = await listPriceKindsCore(ctx, input, tenantId)\n return {\n items: base.items.map((row) => ({\n id: row.id,\n code: row.code,\n name: row.title,\n scope: row.organizationId ? ('organization' as const) : ('tenant' as const),\n currency: row.currencyCode,\n appliesTo: row.isPromotion ? ('promotion' as const) : ('regular' as const),\n })),\n total: base.total,\n limit: base.limit,\n offset: base.offset,\n }\n },\n}\n\n/* -------------------------------------------------------------------------- */\n/* Export */\n/* -------------------------------------------------------------------------- */\n\nexport const merchandisingAiTools: CatalogAiToolDefinition[] = [\n searchProductsTool,\n getProductBundleTool,\n listSelectedProductsTool,\n getProductMediaTool,\n getAttributeSchemaTool,\n getCategoryBriefTool,\n listPriceKindsTool,\n]\n\nexport default merchandisingAiTools\n"],
|
|
5
|
-
"mappings": "AAqCA,SAAS,SAAS;AAClB,SAAS,uBAAuB,0BAA0B;AAC1D,SAA6C,eAAe;AAC5D,SAAS,SAAS;AAClB,SAAS,kBAAkB;AAC3B;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,yBAAgF;AACzF;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAGK;AACP,SAAS,oBAAoB;AAE7B,MAAM,SAAS,aAAa,SAAS;AAkBrC,MAAM,yBAAyB;AAE/B,SAAS,qBAAqB,KAAmD;AAC/E,MAAI;AACF,WAAO,IAAI,UAAU,QAA2B,eAAe;AAAA,EACjE,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAMA,MAAM,sBAAsB,EACzB,OAAO;AAAA,EACN,GAAG,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,SAAS,kNAA6M;AAAA,EACtP,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,EAAE,IAAI,GAAG,EAAE,SAAS,EAAE,SAAS,gSAA2R;AAAA,EACvV,QAAQ,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,EAAE,SAAS,EAAE,SAAS,yJAAoJ;AAAA,EACxM,YAAY,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,mMAA8L;AAAA,EACzO,UAAU,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,mOAA8N;AAAA,EACvQ,UAAU,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,8MAAyM;AAAA,EAClP,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,SAAS,EAAE,SAAS,0GAA0G;AAAA,EACxJ,QAAQ,EAAE,QAAQ,EAAE,SAAS,EAAE,SAAS,0FAA0F;AACpI,CAAC,EACA,YAAY;AAIf,eAAe,yBACb,IACA,KACA,UACA,OACA,eAC0E;AAC1E,QAAM,QAAQ,MAAM,SAAS;AAC7B,QAAM,SAAS,MAAM,UAAU;AAE/B,MAAI,gBAAiC,gBACjC,MAAM,KAAK,IAAI,IAAI,aAAa,CAAC,IACjC;AAEJ,MAAI,MAAM,YAAY;AACpB,UAAM,cAAc,MAAM;AAAA,MACxB;AAAA,MACA;AAAA,MACA,EAAE,UAAU,UAAU,MAAM,WAAW;AAAA,MACvC;AAAA,MACA,WAAW,KAAK,QAAQ;AAAA,IAC1B;AACA,UAAM,MAAM,YACT,IAAI,CAAC,eAAe;AACnB,YAAM,UAAW,WAAmB;AACpC,UAAI,CAAC,QAAS,QAAO;AACrB,aAAO,OAAO,YAAY,WAAW,UAAU,QAAQ,MAAM;AAAA,IAC/D,CAAC,EACA,OAAO,CAAC,UAA0C,OAAO,UAAU,YAAY,MAAM,SAAS,CAAC;AAClG,QAAI,CAAC,IAAI,OAAQ,QAAO,EAAE,OAAO,CAAC,GAAG,OAAO,EAAE;AAC9C,oBAAgB,gBACZ,cAAc,OAAO,CAAC,OAAO,IAAI,SAAS,EAAE,CAAC,IAC7C;AACJ,QAAI,CAAC,cAAc,OAAQ,QAAO,EAAE,OAAO,CAAC,GAAG,OAAO,EAAE;AAAA,EAC1D;AAEA,MAAI,MAAM,QAAQ,MAAM,KAAK,SAAS,GAAG;AACvC,UAAM,WAAoC;AAAA,MACxC;AAAA,MACA,KAAK;AAAA,QACH,EAAE,MAAM,EAAE,KAAK,MAAM,KAAK,EAAE;AAAA,QAC5B,EAAE,OAAO,EAAE,KAAK,MAAM,KAAK,EAAE;AAAA,MAC/B;AAAA,IACF;AACA,QAAI,IAAI,eAAgB,UAAS,iBAAiB,IAAI;AACtD,UAAM,OAAO,MAAM;AAAA,MACjB;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,WAAW,KAAK,QAAQ;AAAA,IAC1B;AACA,UAAM,SAAS,KAAK,IAAI,CAAC,QAAQ,IAAI,EAAE;AACvC,QAAI,CAAC,OAAO,OAAQ,QAAO,EAAE,OAAO,CAAC,GAAG,OAAO,EAAE;AACjD,UAAM,cAAc,MAAM;AAAA,MACxB;AAAA,MACA;AAAA,MACA,EAAE,UAAU,KAAK,EAAE,KAAK,OAAO,EAAE;AAAA,MACjC;AAAA,MACA,WAAW,KAAK,QAAQ;AAAA,IAC1B;AACA,UAAM,YAAY,YACf,IAAI,CAAC,eAAe;AACnB,YAAM,UAAW,WAAmB;AACpC,UAAI,CAAC,QAAS,QAAO;AACrB,aAAO,OAAO,YAAY,WAAW,UAAU,QAAQ,MAAM;AAAA,IAC/D,CAAC,EACA,OAAO,CAAC,UAA0C,OAAO,UAAU,YAAY,MAAM,SAAS,CAAC;AAClG,QAAI,CAAC,UAAU,OAAQ,QAAO,EAAE,OAAO,CAAC,GAAG,OAAO,EAAE;AACpD,oBAAgB,gBACZ,cAAc,OAAO,CAAC,OAAO,UAAU,SAAS,EAAE,CAAC,IACnD;AACJ,QAAI,CAAC,cAAc,OAAQ,QAAO,EAAE,OAAO,CAAC,GAAG,OAAO,EAAE;AAAA,EAC1D;AAEA,MAAI,MAAM,aAAa,UAAa,MAAM,aAAa,QAAW;AAChE,UAAM,aAAsC,EAAE,SAAS;AACvD,QAAI,IAAI,eAAgB,YAAW,iBAAiB,IAAI;AACxD,UAAM,YAAY,MAAM;AAAA,MACtB;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,WAAW,KAAK,QAAQ;AAAA,IAC1B;AACA,UAAM,UAAU,UAAU,OAAO,CAAC,QAAQ;AACxC,YAAM,MAAM,eAAe,IAAI,gBAAgB,IAAI;AACnD,YAAM,QAAQ,eAAe,IAAI,kBAAkB,IAAI;AACvD,YAAM,aAAa,SAAS;AAC5B,UAAI,eAAe,KAAM,QAAO;AAChC,UAAI,MAAM,aAAa,UAAa,aAAa,MAAM,SAAU,QAAO;AACxE,UAAI,MAAM,aAAa,UAAa,aAAa,MAAM,SAAU,QAAO;AACxE,aAAO;AAAA,IACT,CAAC;AACD,UAAM,YAAY,QACf,IAAI,CAAC,QAAQ;AACZ,YAAM,UAAW,IAAY;AAC7B,UAAI,CAAC,QAAS,QAAO;AACrB,aAAO,OAAO,YAAY,WAAW,UAAU,QAAQ,MAAM;AAAA,IAC/D,CAAC,EACA,OAAO,CAAC,UAA0C,OAAO,UAAU,YAAY,MAAM,SAAS,CAAC;AAClG,QAAI,CAAC,UAAU,OAAQ,QAAO,EAAE,OAAO,CAAC,GAAG,OAAO,EAAE;AACpD,oBAAgB,gBACZ,cAAc,OAAO,CAAC,OAAO,UAAU,SAAS,EAAE,CAAC,IACnD,MAAM,KAAK,IAAI,IAAI,SAAS,CAAC;AACjC,QAAI,CAAC,cAAc,OAAQ,QAAO,EAAE,OAAO,CAAC,GAAG,OAAO,EAAE;AAAA,EAC1D;AAEA,QAAM,UAAmC,CAAC;AAC1C,MAAI,MAAM,WAAW,KAAM,SAAQ,YAAY;AAC/C,MAAI,MAAM,GAAG;AACX,UAAM,UAAU,IAAI,MAAM,CAAC;AAC3B,YAAQ,MAAM;AAAA,MACZ,EAAE,OAAO,EAAE,QAAQ,QAAQ,EAAE;AAAA,MAC7B,EAAE,UAAU,EAAE,QAAQ,QAAQ,EAAE;AAAA,MAChC,EAAE,KAAK,EAAE,QAAQ,QAAQ,EAAE;AAAA,MAC3B,EAAE,QAAQ,EAAE,QAAQ,QAAQ,EAAE;AAAA,IAChC;AAAA,EACF;AACA,MAAI,eAAe;AACjB,YAAQ,KAAK,EAAE,KAAK,cAAc;AAAA,EACpC;AAEA,QAAM,KAAK,IAAI,UAAU,QAAqB,aAAa;AAC3D,QAAM,SAAsB,MAAM,GAAG,MAAM,2BAA2B;AAAA,IACpE;AAAA,IACA,MAAM,CAAC,EAAE,OAAO,cAAc,KAAK,QAAQ,KAAK,CAAC;AAAA,IACjD,MAAM,EAAE,MAAM,KAAK,MAAM,SAAS,KAAK,IAAI,GAAG,UAAU,MAAM;AAAA,IAC9D;AAAA,IACA,gBAAgB,IAAI,kBAAkB;AAAA,EACxC,CAAC;AAED,QAAM,aAAa,OAAO,MACvB,IAAI,CAAC,QAAiC,IAAI,EAAY,EACtD,OAAO,CAAC,OAAqB,OAAO,OAAO,QAAQ;AACtD,MAAI,CAAC,WAAW,OAAQ,QAAO,EAAE,OAAO,CAAC,GAAG,OAAO,OAAO,MAAM;AAEhE,QAAM,WAAW,MAAM;AAAA,IACrB;AAAA,IACA;AAAA,IACA,EAAE,UAAU,IAAI,EAAE,KAAK,WAAW,GAAG,WAAW,KAAK;AAAA,IACrD;AAAA,IACA,WAAW,KAAK,QAAQ;AAAA,EAC1B;AACA,QAAM,cAAc,IAAI,IAAI,SAAS,IAAI,CAAC,MAAM,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC;AAC1D,QAAM,UAAU,WACb,IAAI,CAAC,OAAO,YAAY,IAAI,EAAE,CAAC,EAC/B,OAAO,CAAC,MAA2B,CAAC,CAAC,CAAC;AAEzC,SAAO,EAAE,OAAO,QAAQ,IAAI,gBAAgB,GAAG,OAAO,OAAO,MAAM;AACrE;AAEA,MAAM,qBAA8C;AAAA,EAClD,MAAM;AAAA,EACN,aAAa;AAAA,EACb,aACE;AAAA,EAIF,aAAa;AAAA,EACb,kBAAkB,CAAC,uBAAuB;AAAA,EAC1C,MAAM,CAAC,QAAQ,WAAW,eAAe;AAAA,EACzC,YAAY;AAAA,EACZ,SAAS,OAAO,UAAU,QAAQ;AAChC,UAAM,EAAE,SAAS,IAAI,kBAAkB,GAAG;AAC1C,UAAM,QAAQ,oBAAoB,MAAM,QAAQ;AAChD,UAAM,KAAK,UAAU,GAAG;AACxB,UAAM,QAAQ,MAAM,SAAS;AAC7B,UAAM,SAAS,MAAM,UAAU;AAG/B,QAAI,CAAC,MAAM,KAAK,MAAM,MAAM,OAAO,MAAM,MAAM,QAAQ,MAAM,MAAM,KAAK;AACtE,YAAM,IAAI;AAAA,IACZ;AAIA,QAAI,OAAO,MAAM,eAAe,YAAY,MAAM,WAAW,KAAK,MAAM,IAAI;AAC1E,YAAM,aAAa;AAAA,IACrB;AAIA,QAAI,MAAM,QAAQ,MAAM,IAAI,KAAK,MAAM,KAAK,WAAW,GAAG;AACxD,YAAM,OAAO;AAAA,IACf;AAQA,QACE,MAAM,aAAa,KACnB,MAAM,aAAa,GACnB;AACA,YAAM,WAAW;AACjB,YAAM,WAAW;AAAA,IACnB;AAGA,QAAI,MAAM,YAAY;AACpB,YAAM,cAAc;AACpB,UAAI,CAAC,YAAY,KAAK,MAAM,UAAU,GAAG;AACvC,cAAM,aAAa;AAAA,MACrB,OAAO;AACL,YAAI;AACF,gBAAM,YAAY,MAAM,GAAG,MAAM,wBAAwB,EAAE,IAAI,MAAM,YAAY,SAAS,CAAQ;AAClG,cAAI,cAAc,EAAG,OAAM,aAAa;AAAA,QAC1C,QAAQ;AACN,gBAAM,aAAa;AAAA,QACrB;AAAA,MACF;AAAA,IACF;AAEA,QAAI,MAAM,KAAK,MAAM,EAAE,KAAK,EAAE,SAAS,GAAG;AACxC,YAAM,UAAU,qBAAqB,GAAG;AACxC,UAAI,SAAS;AACX,cAAM,OAAO,MAAM,QAAQ,OAAO,MAAM,EAAE,KAAK,GAAG;AAAA,UAChD;AAAA,UACA,gBAAgB,IAAI;AAAA,UACpB;AAAA,UACA,aAAa,CAAC,sBAAsB;AAAA,QACtC,CAAC;AACD,cAAM,SAAS,KACZ,OAAO,CAAC,QAAQ,IAAI,aAAa,sBAAsB,EACvD,IAAI,CAAC,QAAQ,IAAI,QAAQ,EACzB,OAAO,CAAC,OAAqB,OAAO,OAAO,YAAY,GAAG,SAAS,CAAC;AACvE,YAAI,CAAC,OAAO,QAAQ;AAClB,iBAAO,EAAE,OAAO,CAAC,GAAG,OAAO,GAAG,OAAO,QAAQ,QAAQ,iBAA0B;AAAA,QACjF;AAMA,cAAM,EAAE,OAAAA,QAAO,OAAAC,OAAM,IAAI,MAAM,yBAAyB,IAAI,KAAK,UAAU,EAAE,GAAG,OAAO,GAAG,OAAU,GAAG,MAAM;AAC7G,eAAO,EAAE,OAAAD,QAAO,OAAAC,QAAO,OAAO,QAAQ,QAAQ,iBAA0B;AAAA,MAC1E;AAAA,IAIF;AAEA,UAAM,EAAE,OAAO,MAAM,IAAI,MAAM,yBAAyB,IAAI,KAAK,UAAU,OAAO,IAAI;AACtF,WAAO,EAAE,OAAO,OAAO,OAAO,QAAQ,QAAQ,eAAwB;AAAA,EACxE;AACF;AAMA,MAAM,wBAAwB,EAAE,OAAO;AAAA,EACrC,WAAW,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS,4BAA4B;AACpE,CAAC;AAED,MAAM,uBAAgD;AAAA,EACpD,MAAM;AAAA,EACN,aAAa;AAAA,EACb,aACE;AAAA,EACF,aAAa;AAAA,EACb,kBAAkB,CAAC,uBAAuB;AAAA,EAC1C,MAAM,CAAC,QAAQ,WAAW,eAAe;AAAA,EACzC,YAAY;AAAA,EACZ,SAAS,OAAO,UAAU,QAAQ;AAChC,UAAM,EAAE,SAAS,IAAI,kBAAkB,GAAG;AAC1C,UAAM,QAAQ,sBAAsB,MAAM,QAAQ;AAClD,UAAM,KAAK,UAAU,GAAG;AACxB,WAAO,mBAAmB,IAAI,KAAK,UAAU,MAAM,SAAS;AAAA,EAC9D;AACF;AAEA,MAAM,4BAA4B,EAAE,OAAO;AAAA,EACzC,YAAY,EACT,MAAM,EAAE,OAAO,EAAE,KAAK,CAAC,EACvB,IAAI,CAAC,EACL,IAAI,EAAE,EACN,SAAS,uGAAuG;AACrH,CAAC;AAED,MAAM,2BAAoD;AAAA,EACxD,MAAM;AAAA,EACN,aAAa;AAAA,EACb,aACE;AAAA,EACF,aAAa;AAAA,EACb,kBAAkB,CAAC,uBAAuB;AAAA,EAC1C,MAAM,CAAC,QAAQ,WAAW,eAAe;AAAA,EACzC,YAAY;AAAA,EACZ,SAAS,OAAO,UAAU,QAAQ;AAChC,UAAM,EAAE,SAAS,IAAI,kBAAkB,GAAG;AAC1C,UAAM,QAAQ,0BAA0B,MAAM,QAAQ;AACtD,UAAM,KAAK,UAAU,GAAG;AACxB,UAAM,SAAS,MAAM,KAAK,IAAI,IAAI,MAAM,UAAU,CAAC;AACnD,UAAM,WAAW,MAAM,QAAQ;AAAA,MAC7B,OAAO,IAAI,OAAO,eAAe,EAAE,WAAW,QAAQ,MAAM,mBAAmB,IAAI,KAAK,UAAU,SAAS,EAAE,EAAE;AAAA,IACjH;AACA,UAAM,QAAyB,CAAC;AAChC,UAAM,aAAuB,CAAC;AAC9B,eAAW,SAAS,UAAU;AAC5B,UAAI,MAAM,OAAO,OAAO;AACtB,cAAM,KAAK,MAAM,MAAM;AAAA,MACzB,OAAO;AACL,mBAAW,KAAK,MAAM,SAAS;AAC/B,eAAO,KAAK,uDAAuD,EAAE,WAAW,MAAM,UAAU,CAAC;AAAA,MACnG;AAAA,IACF;AACA,WAAO,EAAE,OAAO,WAAW;AAAA,EAC7B;AACF;AAMA,MAAM,uBAAuB,EAAE,OAAO;AAAA,EACpC,WAAW,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS,4BAA4B;AAAA,EAClE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,EAAE,IAAI,GAAG,EAAE,SAAS,EAAE,SAAS,iCAAiC;AAAA,EAC7F,QAAQ,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,EAAE,SAAS,EAAE,SAAS,2BAA2B;AACjF,CAAC;AAED,MAAM,sBAA+C;AAAA,EACnD,MAAM;AAAA,EACN,aAAa;AAAA,EACb,aACE;AAAA,EACF,aAAa;AAAA,EACb,kBAAkB,CAAC,uBAAuB;AAAA,EAC1C,MAAM,CAAC,QAAQ,WAAW,eAAe;AAAA,EACzC,YAAY;AAAA,EACZ,SAAS,OAAO,UAAU,QAAQ;AAChC,UAAM,EAAE,SAAS,IAAI,kBAAkB,GAAG;AAC1C,UAAM,QAAQ,qBAAqB,MAAM,QAAQ;AACjD,UAAM,KAAK,UAAU,GAAG;AACxB,UAAM,QAAQ,MAAM,SAAS;AAC7B,UAAM,SAAS,MAAM,UAAU;AAC/B,UAAM,QAAiC;AAAA,MACrC;AAAA,MACA,UAAU,EAAE,QAAQ;AAAA,MACpB,UAAU,MAAM;AAAA,IAClB;AACA,QAAI,IAAI,eAAgB,OAAM,iBAAiB,IAAI;AACnD,UAAM,CAAC,MAAM,KAAK,IAAI,MAAM,QAAQ,IAAI;AAAA,MACtC;AAAA,QACE;AAAA,QACA;AAAA,QACA;AAAA,QACA,EAAE,OAAO,QAAQ,SAAS,EAAE,WAAW,MAAM,EAAS;AAAA,QACtD,WAAW,KAAK,QAAQ;AAAA,MAC1B;AAAA,MACA,GAAG,MAAM,YAAY,KAAY;AAAA,IACnC,CAAC;AACD,UAAM,WAAW,KAAK,OAAO,CAAC,SAAS,IAAI,YAAY,UAAU,QAAQ;AACzE,WAAO;AAAA,MACL,OAAO,SAAS,IAAI,CAAC,SAAS;AAAA,QAC5B,SAAS,IAAI;AAAA,QACb,WAAW,MAAM;AAAA,QACjB,cAAc,IAAI;AAAA,QAClB,UAAU,IAAI;AAAA,QACd,WAAW,IAAI;AAAA,QACf,MAAM,IAAI;AAAA,QACV,SAAS;AAAA,QACT,WAAW;AAAA,MACb,EAAE;AAAA,MACF;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AACF;AAMA,MAAM,0BAA0B,EAAE,OAAO;AAAA,EACvC,WAAW,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,SAAS,8DAA8D;AAAA,EAC/G,YAAY,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,SAAS,gEAAgE;AACpH,CAAC;AAED,MAAM,yBAAkD;AAAA,EACtD,MAAM;AAAA,EACN,aAAa;AAAA,EACb,aACE;AAAA,EACF,aAAa;AAAA,EACb,kBAAkB,CAAC,uBAAuB;AAAA,EAC1C,MAAM,CAAC,QAAQ,WAAW,eAAe;AAAA,EACzC,YAAY;AAAA,EACZ,SAAS,OAAO,UAAU,QAAQ;AAChC,UAAM,EAAE,SAAS,IAAI,kBAAkB,GAAG;AAC1C,UAAM,QAAQ,wBAAwB,MAAM,QAAQ;AACpD,WAAO,uBAAuB,KAAK,UAAU,MAAM,WAAW,MAAM,UAAU;AAAA,EAChF;AACF;AAEA,MAAM,wBAAwB,EAAE,OAAO;AAAA,EACrC,YAAY,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS,qBAAqB;AAC9D,CAAC;AAED,MAAM,uBAAgD;AAAA,EACpD,MAAM;AAAA,EACN,aAAa;AAAA,EACb,aACE;AAAA,EACF,aAAa;AAAA,EACb,kBAAkB,CAAC,yBAAyB;AAAA,EAC5C,MAAM,CAAC,QAAQ,WAAW,eAAe;AAAA,EACzC,YAAY;AAAA,EACZ,SAAS,OAAO,UAAU,QAAQ;AAChC,UAAM,EAAE,SAAS,IAAI,kBAAkB,GAAG;AAC1C,UAAM,QAAQ,sBAAsB,MAAM,QAAQ;AAClD,UAAM,KAAK,UAAU,GAAG;AACxB,UAAM,QAAiC;AAAA,MACrC,IAAI,MAAM;AAAA,MACV;AAAA,MACA,WAAW;AAAA,IACb;AACA,QAAI,IAAI,eAAgB,OAAM,iBAAiB,IAAI;AACnD,UAAM,WAAW,MAAM;AAAA,MACrB;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,WAAW,KAAK,QAAQ;AAAA,IAC1B;AACA,QAAI,CAAC,YAAY,SAAS,aAAa,UAAU;AAC/C,aAAO,EAAE,OAAO,OAAgB,YAAY,MAAM,WAAW;AAAA,IAC/D;AACA,UAAM,kBAAkB,MAAM,uBAAuB,KAAK,UAAU,QAAW,SAAS,EAAE;AAC1F,WAAO;AAAA,MACL,OAAO;AAAA,MACP,IAAI,SAAS;AAAA,MACb,MAAM,SAAS;AAAA,MACf,MAAM,SAAS,YAAY;AAAA,MAC3B,aAAa,SAAS,eAAe;AAAA,MACrC;AAAA,IACF;AAAA,EACF;AACF;AAMA,MAAM,sBAAsB,EACzB,OAAO;AAAA,EACN,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,EAAE,IAAI,GAAG,EAAE,SAAS,EAAE,SAAS,iCAAiC;AAAA,EAC7F,QAAQ,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,EAAE,SAAS,EAAE,SAAS,2BAA2B;AACjF,CAAC,EACA,YAAY;AAEf,MAAM,qBAA8C;AAAA,EAClD,MAAM;AAAA,EACN,aAAa;AAAA,EACb,aACE;AAAA,EACF,aAAa;AAAA,EACb,kBAAkB,CAAC,yBAAyB;AAAA,EAC5C,MAAM,CAAC,QAAQ,WAAW,eAAe;AAAA,EACzC,YAAY;AAAA,EACZ,SAAS,OAAO,UAAU,QAAQ;AAChC,UAAM,EAAE,SAAS,IAAI,kBAAkB,GAAG;AAC1C,UAAM,QAAQ,oBAAoB,MAAM,QAAQ;AAChD,UAAM,OAAO,MAAM,mBAAmB,KAAK,OAAO,QAAQ;AAC1D,WAAO;AAAA,MACL,OAAO,KAAK,MAAM,IAAI,CAAC,SAAS;AAAA,QAC9B,IAAI,IAAI;AAAA,QACR,MAAM,IAAI;AAAA,QACV,MAAM,IAAI;AAAA,QACV,OAAO,IAAI,iBAAkB,iBAA4B;AAAA,QACzD,UAAU,IAAI;AAAA,QACd,WAAW,IAAI,cAAe,cAAyB;AAAA,MACzD,EAAE;AAAA,MACF,OAAO,KAAK;AAAA,MACZ,OAAO,KAAK;AAAA,MACZ,QAAQ,KAAK;AAAA,IACf;AAAA,EACF;AACF;AAMO,MAAM,uBAAkD;AAAA,EAC7D;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAEA,IAAO,6BAAQ;",
|
|
4
|
+
"sourcesContent": ["/**\n * D18 catalog merchandising read tools (Phase 1 WS-C, Step 3.11).\n *\n * Ships the seven canonical tool names the `catalog.merchandising_assistant`\n * agent (Step 4.9) whitelists verbatim:\n *\n * - `catalog.search_products` \u2014 fulltext + filter search (hybrid path).\n * - `catalog.get_product_bundle` \u2014 aggregate bundle for a single product.\n * - `catalog.list_selected_products` \u2014 bundle aggregate for an ID array.\n * - `catalog.get_product_media` \u2014 media metadata (attachment IDs only \u2014\n * the Step 3.7 attachment bridge converts these to model file parts at\n * runtime invocation; this tool does NOT call the bridge directly).\n * - `catalog.get_attribute_schema` \u2014 merged module + category + product\n * custom-field schema.\n * - `catalog.get_category_brief` \u2014 category snapshot with inherited schema.\n * - `catalog.list_price_kinds` \u2014 D18 spec-named price-kind enumerator.\n *\n * Every tool is read-only (no `isMutation: true`). Mutation tooling for D18\n * lands in Step 5.14 under the pending-action contract.\n *\n * Tenant scoping: all DB access uses `findWithDecryption` /\n * `findOneWithDecryption`, plus a defensive post-filter against\n * `row.tenantId === ctx.tenantId`. Cross-tenant IDs surface through the\n * `missingIds` output (not an error), so a chat agent receives a uniform\n * not-found signal whether the product is missing, deleted, or out of scope.\n *\n * Shared helpers:\n * `list_price_kinds` + Step 3.10's `list_price_kinds_base` both route\n * through `listPriceKindsCore` in `./_shared.ts`; there is no duplicate\n * query path.\n *\n * Step 3.12 (authoring pack) promoted the product-bundle builder plus\n * `toProductSummary` / `resolveAttributeSchema` into `./_shared.ts` so\n * both packs consume the same loader. This file now just wires the\n * shared helpers into tool handlers.\n */\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { z } from 'zod'\nimport { findOneWithDecryption, findWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport { type QueryEngine, type QueryResult, SortDir } from '@open-mercato/shared/lib/query/types'\nimport { E } from '#generated/entities.ids.generated'\nimport { Attachment } from '@open-mercato/core/modules/attachments/data/entities'\nimport {\n CatalogProduct,\n CatalogProductCategory,\n CatalogProductCategoryAssignment,\n CatalogProductPrice,\n CatalogProductTag,\n CatalogProductTagAssignment,\n} from '../data/entities'\nimport { assertTenantScope, type CatalogAiToolDefinition, type CatalogToolContext } from './types'\nimport {\n buildProductBundle,\n buildScope,\n listPriceKindsCore,\n resolveAttributeSchema,\n resolveEm,\n toPriceNumeric,\n toProductSummary,\n type ProductBundle,\n type ProductBundleResult,\n} from './_shared'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\n\nconst logger = createLogger('catalog')\n\ntype SearchServiceLike = {\n search: (query: string, options: {\n tenantId: string\n organizationId?: string | null\n limit?: number\n entityTypes?: string[]\n }) => Promise<Array<{\n entityId: string\n recordId: string\n score: number\n source: string\n presenter?: unknown\n url?: string\n }>>\n}\n\nconst CATALOG_PRODUCT_ENTITY = 'catalog:catalog_product'\n\nfunction resolveSearchService(ctx: CatalogToolContext): SearchServiceLike | null {\n try {\n return ctx.container.resolve<SearchServiceLike>('searchService')\n } catch {\n return null\n }\n}\n\n/* -------------------------------------------------------------------------- */\n/* catalog.search_products */\n/* -------------------------------------------------------------------------- */\n\nconst searchProductsInput = z\n .object({\n q: z.string().trim().optional().describe('Optional fulltext query (title / subtitle / sku / handle). Omit or leave empty (or do NOT pass it at all) to list all products. NEVER use \"*\", \"**\", or \"%\" \u2014 they are not wildcards and will be discarded.'),\n limit: z.number().int().min(1).max(100).optional().describe('Page size. Default 50, hard maximum 100. Results are paginated: when `total` exceeds `limit + offset`, call again with the next `offset` instead of asking for more than 100 rows.'),\n offset: z.number().int().min(0).optional().describe('Rows to skip for pagination (default 0). Combine with `limit` to fetch subsequent pages \u2014 for example offset=100, limit=100 fetches rows 101..200.'),\n categoryId: z.string().optional().describe('Restrict to products assigned to this catalog category UUID. Only use a category ID returned by a previous tool call \u2014 do NOT guess. Empty string is treated the same as omitting the field.'),\n priceMin: z.number().optional().describe('Lower-bound (inclusive) on the gross unit price. OMIT this field when you do not want a lower bound \u2014 do NOT pass 0 as \"no minimum\", because 0 is a real bound that combined with priceMax=0 will return only free products.'),\n priceMax: z.number().optional().describe('Upper-bound (inclusive) on the gross unit price. OMIT this field when you do not want an upper bound \u2014 do NOT pass 0 as \"no maximum\", because 0 is a real bound that will exclude every priced product.'),\n tags: z.array(z.string()).optional().describe('Tag labels or slugs (any-match) the product carries. Omit or pass an empty array to skip the tag filter.'),\n active: z.boolean().optional().describe('When true, only active products are returned. Omit to include inactive products as well.'),\n })\n .passthrough()\n\ntype SearchProductsInput = z.infer<typeof searchProductsInput>\n\nasync function queryProductsWithFilters(\n em: EntityManager,\n ctx: CatalogToolContext,\n tenantId: string,\n input: SearchProductsInput,\n restrictToIds: string[] | null,\n): Promise<{ items: ReturnType<typeof toProductSummary>[]; total: number }> {\n const limit = input.limit ?? 50\n const offset = input.offset ?? 0\n\n let idRestriction: string[] | null = restrictToIds\n ? Array.from(new Set(restrictToIds))\n : null\n\n if (input.categoryId) {\n const assignments = await findWithDecryption<CatalogProductCategoryAssignment>(\n em,\n CatalogProductCategoryAssignment,\n { tenantId, category: input.categoryId } as any,\n undefined,\n buildScope(ctx, tenantId),\n )\n const ids = assignments\n .map((assignment) => {\n const product = (assignment as any).product\n if (!product) return null\n return typeof product === 'string' ? product : product.id ?? null\n })\n .filter((value: string | null): value is string => typeof value === 'string' && value.length > 0)\n if (!ids.length) return { items: [], total: 0 }\n idRestriction = idRestriction\n ? idRestriction.filter((id) => ids.includes(id))\n : ids\n if (!idRestriction.length) return { items: [], total: 0 }\n }\n\n if (input.tags && input.tags.length > 0) {\n const tagWhere: Record<string, unknown> = {\n tenantId,\n $or: [\n { slug: { $in: input.tags } },\n { label: { $in: input.tags } },\n ],\n }\n if (ctx.organizationId) tagWhere.organizationId = ctx.organizationId\n const tags = await findWithDecryption<CatalogProductTag>(\n em,\n CatalogProductTag,\n tagWhere as any,\n undefined,\n buildScope(ctx, tenantId),\n )\n const tagIds = tags.map((tag) => tag.id)\n if (!tagIds.length) return { items: [], total: 0 }\n const assignments = await findWithDecryption<CatalogProductTagAssignment>(\n em,\n CatalogProductTagAssignment,\n { tenantId, tag: { $in: tagIds } } as any,\n undefined,\n buildScope(ctx, tenantId),\n )\n const scopedIds = assignments\n .map((assignment) => {\n const product = (assignment as any).product\n if (!product) return null\n return typeof product === 'string' ? product : product.id ?? null\n })\n .filter((value: string | null): value is string => typeof value === 'string' && value.length > 0)\n if (!scopedIds.length) return { items: [], total: 0 }\n idRestriction = idRestriction\n ? idRestriction.filter((id) => scopedIds.includes(id))\n : scopedIds\n if (!idRestriction.length) return { items: [], total: 0 }\n }\n\n if (input.priceMin !== undefined || input.priceMax !== undefined) {\n const priceWhere: Record<string, unknown> = { tenantId }\n if (ctx.organizationId) priceWhere.organizationId = ctx.organizationId\n const priceRows = await findWithDecryption<CatalogProductPrice>(\n em,\n CatalogProductPrice,\n priceWhere as any,\n undefined,\n buildScope(ctx, tenantId),\n )\n const bounded = priceRows.filter((row) => {\n const net = toPriceNumeric(row.unitPriceNet ?? null)\n const gross = toPriceNumeric(row.unitPriceGross ?? null)\n const comparable = gross ?? net\n if (comparable === null) return false\n if (input.priceMin !== undefined && comparable < input.priceMin) return false\n if (input.priceMax !== undefined && comparable > input.priceMax) return false\n return true\n })\n const scopedIds = bounded\n .map((row) => {\n const product = (row as any).product\n if (!product) return null\n return typeof product === 'string' ? product : product.id ?? null\n })\n .filter((value: string | null): value is string => typeof value === 'string' && value.length > 0)\n if (!scopedIds.length) return { items: [], total: 0 }\n idRestriction = idRestriction\n ? idRestriction.filter((id) => scopedIds.includes(id))\n : Array.from(new Set(scopedIds))\n if (!idRestriction.length) return { items: [], total: 0 }\n }\n\n const filters: Record<string, unknown> = {}\n if (input.active === true) filters.is_active = true\n if (input.q) {\n const pattern = `%${input.q}%`\n filters.$or = [\n { title: { $ilike: pattern } },\n { subtitle: { $ilike: pattern } },\n { sku: { $ilike: pattern } },\n { handle: { $ilike: pattern } },\n ]\n }\n if (idRestriction) {\n filters.id = { $in: idRestriction }\n }\n\n const qe = ctx.container.resolve<QueryEngine>('queryEngine')\n const result: QueryResult = await qe.query('catalog:catalog_product', {\n filters,\n sort: [{ field: 'created_at', dir: SortDir.Desc }],\n page: { page: Math.floor(offset / limit) + 1, pageSize: limit },\n tenantId,\n organizationId: ctx.organizationId ?? undefined,\n })\n\n const productIds = result.items\n .map((row: Record<string, unknown>) => row.id as string)\n .filter((id): id is string => typeof id === 'string')\n if (!productIds.length) return { items: [], total: result.total }\n\n const products = await findWithDecryption<CatalogProduct>(\n em,\n CatalogProduct,\n { tenantId, id: { $in: productIds }, deletedAt: null } as any,\n undefined,\n buildScope(ctx, tenantId),\n )\n const productById = new Map(products.map((p) => [p.id, p]))\n const ordered = productIds\n .map((id) => productById.get(id))\n .filter((p): p is CatalogProduct => !!p)\n\n return { items: ordered.map(toProductSummary), total: result.total }\n}\n\nconst searchProductsTool: CatalogAiToolDefinition = {\n name: 'catalog.search_products',\n displayName: 'Search products',\n description:\n 'Hybrid search + filter across tenant products. When `q` is non-empty, routes through the search service (tenant + organization scoped) then hydrates tenant-scoped product summaries; when `q` is empty or omitted, runs the catalog query engine with the supplied filters and returns ALL products. ' +\n 'Pagination: response shape is `{ items, total, limit, offset, source }`. Hard cap is 100 rows per call (`limit` max=100, default=50); if `total` exceeds `limit + offset`, call again with the next `offset` rather than asking for >100. ' +\n 'Empty / sentinel inputs: pass an empty `q` (or omit it) to list all products. NEVER pass `0` for `priceMin`/`priceMax` to mean \"no bound\" \u2014 `0` is a real numeric bound (priceMin=0 + priceMax=0 returns only free products). To remove a bound, OMIT the field. Empty string `categoryId` and empty `tags` arrays are ignored. ' +\n '`source` in the output indicates which path executed (`search_service` vs `query_engine`).',\n inputSchema: searchProductsInput,\n requiredFeatures: ['catalog.products.view'],\n tags: ['read', 'catalog', 'merchandising'],\n isMutation: false,\n handler: async (rawInput, ctx) => {\n const { tenantId } = assertTenantScope(ctx)\n const input = searchProductsInput.parse(rawInput)\n const em = resolveEm(ctx)\n const limit = input.limit ?? 50\n const offset = input.offset ?? 0\n\n // Treat wildcard-style and empty queries as \"list all\".\n if (!input.q || input.q === '*' || input.q === '**' || input.q === '%') {\n input.q = undefined\n }\n\n // Empty-string sentinel for `categoryId` is the agent's way of saying\n // \"no category filter\" \u2014 normalize before the UUID guard runs.\n if (typeof input.categoryId === 'string' && input.categoryId.trim() === '') {\n input.categoryId = undefined\n }\n\n // Empty `tags` array is meaningless as a filter; drop it so the price /\n // category narrowing path doesn't execute a no-op tag join.\n if (Array.isArray(input.tags) && input.tags.length === 0) {\n input.tags = undefined\n }\n\n // Agents commonly pass `priceMin: 0` / `priceMax: 0` thinking 0 means\n // \"no bound\". It does not \u2014 0 is a real inclusive bound and the two\n // together ask for free products only. Treat the (0, 0) pair as\n // \"unset\" so the empty-bound default carries through. A standalone 0\n // (e.g. priceMin=0 with priceMax=50) is left intact because it can be\n // a meaningful \"everything from free up to 50\" filter.\n if (\n input.priceMin === 0 &&\n input.priceMax === 0\n ) {\n input.priceMin = undefined\n input.priceMax = undefined\n }\n\n // Guard against hallucinated or invalid category IDs.\n if (input.categoryId) {\n const uuidPattern = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i\n if (!uuidPattern.test(input.categoryId)) {\n input.categoryId = undefined\n } else {\n try {\n const catExists = await em.count(CatalogProductCategory, { id: input.categoryId, tenantId } as any)\n if (catExists === 0) input.categoryId = undefined\n } catch {\n input.categoryId = undefined\n }\n }\n }\n\n if (input.q && input.q.trim().length > 0) {\n const service = resolveSearchService(ctx)\n if (service) {\n const hits = await service.search(input.q.trim(), {\n tenantId,\n organizationId: ctx.organizationId,\n limit,\n entityTypes: [CATALOG_PRODUCT_ENTITY],\n })\n const hitIds = hits\n .filter((hit) => hit.entityId === CATALOG_PRODUCT_ENTITY)\n .map((hit) => hit.recordId)\n .filter((id): id is string => typeof id === 'string' && id.length > 0)\n if (!hitIds.length) {\n return { items: [], total: 0, limit, offset, source: 'search_service' as const }\n }\n // Hydrate tenant-scoped products from the hit IDs, then apply any\n // structured filters the search service can't express (category /\n // price / tags / active). Search services in this repo do not\n // currently accept structured filters, so we narrow in-process and\n // document that in the return report + description.\n const { items, total } = await queryProductsWithFilters(em, ctx, tenantId, { ...input, q: undefined }, hitIds)\n return { items, total, limit, offset, source: 'search_service' as const }\n }\n // Fall through to the query-engine path if the search service is not\n // registered in the DI container \u2014 keeps the tool usable in test\n // harnesses and during early bring-up.\n }\n\n const { items, total } = await queryProductsWithFilters(em, ctx, tenantId, input, null)\n return { items, total, limit, offset, source: 'query_engine' as const }\n },\n}\n\n/* -------------------------------------------------------------------------- */\n/* catalog.get_product_bundle / catalog.list_selected_products */\n/* -------------------------------------------------------------------------- */\n\nconst getProductBundleInput = z.object({\n productId: z.string().uuid().describe('Catalog product id (UUID).'),\n})\n\nconst getProductBundleTool: CatalogAiToolDefinition = {\n name: 'catalog.get_product_bundle',\n displayName: 'Get product bundle',\n description:\n 'Aggregate product snapshot for D18 merchandising: core fields + categories + tags + variants + prices (base + best via pricing service) + media metadata + custom-field values + merged attribute schema. Media bytes flow through the attachment bridge (use `catalog.get_product_media` then the bridge). Returns `{ found: false }` on miss or cross-tenant access.',\n inputSchema: getProductBundleInput,\n requiredFeatures: ['catalog.products.view'],\n tags: ['read', 'catalog', 'merchandising'],\n isMutation: false,\n handler: async (rawInput, ctx) => {\n const { tenantId } = assertTenantScope(ctx)\n const input = getProductBundleInput.parse(rawInput)\n const em = resolveEm(ctx)\n return buildProductBundle(em, ctx, tenantId, input.productId)\n },\n}\n\nconst listSelectedProductsInput = z.object({\n productIds: z\n .array(z.string().uuid())\n .min(1)\n .max(50)\n .describe('1..50 catalog product ids (UUIDs). Duplicates are collapsed; cross-tenant ids drop into `missingIds`.'),\n})\n\nconst listSelectedProductsTool: CatalogAiToolDefinition = {\n name: 'catalog.list_selected_products',\n displayName: 'List selected products (bundles)',\n description:\n 'Bulk variant of `catalog.get_product_bundle`: resolves 1..50 product ids into tenant-scoped bundle aggregates. Missing / cross-tenant ids are returned in `missingIds` (not as an error) so selection-aware agents can render partial results.',\n inputSchema: listSelectedProductsInput,\n requiredFeatures: ['catalog.products.view'],\n tags: ['read', 'catalog', 'merchandising'],\n isMutation: false,\n handler: async (rawInput, ctx) => {\n const { tenantId } = assertTenantScope(ctx)\n const input = listSelectedProductsInput.parse(rawInput)\n const em = resolveEm(ctx)\n const unique = Array.from(new Set(input.productIds))\n const resolved = await Promise.all(\n unique.map(async (productId) => ({ productId, result: await buildProductBundle(em, ctx, tenantId, productId) })),\n )\n const items: ProductBundle[] = []\n const missingIds: string[] = []\n for (const entry of resolved) {\n if (entry.result.found) {\n items.push(entry.result)\n } else {\n missingIds.push(entry.productId)\n logger.warn('catalog.list_selected_products product not in scope', { productId: entry.productId })\n }\n }\n return { items, missingIds }\n },\n}\n\n/* -------------------------------------------------------------------------- */\n/* catalog.get_product_media */\n/* -------------------------------------------------------------------------- */\n\nconst getProductMediaInput = z.object({\n productId: z.string().uuid().describe('Catalog product id (UUID).'),\n limit: z.number().int().min(1).max(100).optional().describe('Max rows (default 50, max 100).'),\n offset: z.number().int().min(0).optional().describe('Rows to skip (default 0).'),\n})\n\nconst getProductMediaTool: CatalogAiToolDefinition = {\n name: 'catalog.get_product_media',\n displayName: 'Get product media (with attachment IDs)',\n description:\n 'List media records attached to a product with metadata (filename, mime, size, sort order) and the `attachmentId` string for each row. Does NOT invoke the attachment bridge \u2014 the Step 3.7 runtime bridge converts attachment ids into model file parts when the chat/object helper invokes this tool in-context. Returns `{ items, total, limit, offset }`.',\n inputSchema: getProductMediaInput,\n requiredFeatures: ['catalog.products.view'],\n tags: ['read', 'catalog', 'merchandising'],\n isMutation: false,\n handler: async (rawInput, ctx) => {\n const { tenantId } = assertTenantScope(ctx)\n const input = getProductMediaInput.parse(rawInput)\n const em = resolveEm(ctx)\n const limit = input.limit ?? 50\n const offset = input.offset ?? 0\n const where: Record<string, unknown> = {\n tenantId,\n entityId: E.catalog.catalog_product,\n recordId: input.productId,\n }\n if (ctx.organizationId) where.organizationId = ctx.organizationId\n const [rows, total] = await Promise.all([\n findWithDecryption<Attachment>(\n em,\n Attachment,\n where as any,\n { limit, offset, orderBy: { createdAt: 'asc' } as any } as any,\n buildScope(ctx, tenantId),\n ),\n em.count(Attachment, where as any),\n ])\n const filtered = rows.filter((row) => (row.tenantId ?? null) === tenantId)\n return {\n items: filtered.map((row) => ({\n mediaId: row.id,\n productId: input.productId,\n attachmentId: row.id,\n fileName: row.fileName,\n mediaType: row.mimeType,\n size: row.fileSize,\n altText: null,\n sortOrder: 0,\n })),\n total,\n limit,\n offset,\n }\n },\n}\n\n/* -------------------------------------------------------------------------- */\n/* catalog.get_attribute_schema / catalog.get_category_brief */\n/* -------------------------------------------------------------------------- */\n\nconst getAttributeSchemaInput = z.object({\n productId: z.string().uuid().optional().describe('Narrow schema resolution to this product (scope: `product`).'),\n categoryId: z.string().uuid().optional().describe('Narrow schema resolution to this category (scope: `category`).'),\n})\n\nconst getAttributeSchemaTool: CatalogAiToolDefinition = {\n name: 'catalog.get_attribute_schema',\n displayName: 'Get attribute schema',\n description:\n 'Resolve the merged custom-field attribute schema for catalog products. When both `productId` and `categoryId` are absent, returns module-level fields only. Reuses the shared `loadCustomFieldDefinitionIndex` resolver so tenant + organization scoping rules stay consistent with CrudForm / admin routes.',\n inputSchema: getAttributeSchemaInput,\n requiredFeatures: ['catalog.products.view'],\n tags: ['read', 'catalog', 'merchandising'],\n isMutation: false,\n handler: async (rawInput, ctx) => {\n const { tenantId } = assertTenantScope(ctx)\n const input = getAttributeSchemaInput.parse(rawInput)\n return resolveAttributeSchema(ctx, tenantId, input.productId, input.categoryId)\n },\n}\n\nconst getCategoryBriefInput = z.object({\n categoryId: z.string().uuid().describe('Category id (UUID).'),\n})\n\nconst getCategoryBriefTool: CatalogAiToolDefinition = {\n name: 'catalog.get_category_brief',\n displayName: 'Get category brief',\n description:\n 'Category name, full tree path, description, and merged attribute schema (same resolver as `catalog.get_attribute_schema` with `categoryId`). Returns `{ found: false }` on miss / cross-tenant.',\n inputSchema: getCategoryBriefInput,\n requiredFeatures: ['catalog.categories.view'],\n tags: ['read', 'catalog', 'merchandising'],\n isMutation: false,\n handler: async (rawInput, ctx) => {\n const { tenantId } = assertTenantScope(ctx)\n const input = getCategoryBriefInput.parse(rawInput)\n const em = resolveEm(ctx)\n const where: Record<string, unknown> = {\n id: input.categoryId,\n tenantId,\n deletedAt: null,\n }\n if (ctx.organizationId) where.organizationId = ctx.organizationId\n const category = await findOneWithDecryption<CatalogProductCategory>(\n em,\n CatalogProductCategory,\n where as any,\n undefined,\n buildScope(ctx, tenantId),\n )\n if (!category || category.tenantId !== tenantId) {\n return { found: false as const, categoryId: input.categoryId }\n }\n const attributeSchema = await resolveAttributeSchema(ctx, tenantId, undefined, category.id)\n return {\n found: true as const,\n id: category.id,\n name: category.name,\n path: category.treePath ?? null,\n description: category.description ?? null,\n attributeSchema,\n }\n },\n}\n\n/* -------------------------------------------------------------------------- */\n/* catalog.list_price_kinds */\n/* -------------------------------------------------------------------------- */\n\nconst listPriceKindsInput = z\n .object({\n limit: z.number().int().min(1).max(100).optional().describe('Max rows (default 50, max 100).'),\n offset: z.number().int().min(0).optional().describe('Rows to skip (default 0).'),\n })\n .passthrough()\n\nconst listPriceKindsTool: CatalogAiToolDefinition = {\n name: 'catalog.list_price_kinds',\n displayName: 'List price kinds',\n description:\n 'Enumerate tenant price kinds for the D18 merchandising assistant. Shares the tenant-scoped query path with `catalog.list_price_kinds_base`; the two tools differ only in description/framing (the base tool is the low-level settings enumerator, this one is the spec-named D18 surface).',\n inputSchema: listPriceKindsInput,\n requiredFeatures: ['catalog.settings.manage'],\n tags: ['read', 'catalog', 'merchandising'],\n isMutation: false,\n handler: async (rawInput, ctx) => {\n const { tenantId } = assertTenantScope(ctx)\n const input = listPriceKindsInput.parse(rawInput)\n const base = await listPriceKindsCore(ctx, input, tenantId)\n return {\n items: base.items.map((row) => ({\n id: row.id,\n code: row.code,\n name: row.title,\n scope: row.organizationId ? ('organization' as const) : ('tenant' as const),\n currency: row.currencyCode,\n appliesTo: row.isPromotion ? ('promotion' as const) : ('regular' as const),\n })),\n total: base.total,\n limit: base.limit,\n offset: base.offset,\n }\n },\n}\n\n/* -------------------------------------------------------------------------- */\n/* Export */\n/* -------------------------------------------------------------------------- */\n\nexport const merchandisingAiTools: CatalogAiToolDefinition[] = [\n searchProductsTool,\n getProductBundleTool,\n listSelectedProductsTool,\n getProductMediaTool,\n getAttributeSchemaTool,\n getCategoryBriefTool,\n listPriceKindsTool,\n]\n\nexport default merchandisingAiTools\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["items", "total"]
|
|
7
7
|
}
|
|
@@ -113,7 +113,7 @@ function useContextItems(pageContext) {
|
|
|
113
113
|
const t = useT();
|
|
114
114
|
return React.useMemo(() => {
|
|
115
115
|
const items = [];
|
|
116
|
-
const { selectedCount, totalMatching,
|
|
116
|
+
const { selectedCount, totalMatching, filter } = pageContext.extra;
|
|
117
117
|
if (selectedCount > 0) {
|
|
118
118
|
items.push({
|
|
119
119
|
label: t(
|
|
@@ -123,10 +123,7 @@ function useContextItems(pageContext) {
|
|
|
123
123
|
});
|
|
124
124
|
} else if (totalMatching > 0) {
|
|
125
125
|
items.push({
|
|
126
|
-
label:
|
|
127
|
-
"catalog.merchandising_assistant.context.matchingProductsCapped",
|
|
128
|
-
"{count}+ products in view"
|
|
129
|
-
).replace("{count}", String(totalMatching)) : t(
|
|
126
|
+
label: t(
|
|
130
127
|
"catalog.merchandising_assistant.context.matchingProducts",
|
|
131
128
|
"{count} products in view"
|
|
132
129
|
).replace("{count}", String(totalMatching))
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../../src/modules/catalog/backend/catalog/products/MerchandisingAssistantSheet.tsx"],
|
|
4
|
-
"sourcesContent": ["\"use client\"\n\n/**\n * MerchandisingAssistantSheet \u2014 Step 4.9 (Spec \u00A710 D18).\n *\n * Embeds `<AiChat agent=\"catalog.merchandising_assistant\" pageContext={...} />`\n * in a right-side sheet (built on the shared Dialog primitive because\n * `packages/ui` does not ship a dedicated Sheet/Drawer primitive in\n * Phase 2). The trigger is a button rendered in the products-list page\n * header.\n *\n * Phase 2 is strictly read-only: the sheet shows proposals (structured\n * output), but the mutation tools (`catalog.update_product`,\n * `catalog.bulk_update_products`, `catalog.apply_attribute_extraction`,\n * `catalog.update_product_media_descriptions`) are intentionally NOT in\n * the agent whitelist. Phase 5.14 introduces those via the pending-action\n * contract.\n *\n * pageContext follows spec \u00A710.1 exactly:\n *\n * {\n * view: 'catalog.products.list',\n * recordType: null,\n * recordId: string, // \"\" or comma-separated UUIDs\n * extra: {\n * filter: { categoryId, priceRange, tags, status },\n * totalMatching: number,\n * selectedCount: number,\n * }\n * }\n */\n\nimport * as React from 'react'\nimport { Boxes, ChevronDown, FileText, Package, PanelRightOpen, PenLine, Tags, TrendingUp } from 'lucide-react'\nimport { AiChat, type AiChatSuggestion, type AiChatContextItem } from '@open-mercato/ui/ai/AiChat'\nimport { AiIcon } from '@open-mercato/ui/ai/AiIcon'\nimport { useAiDock } from '@open-mercato/ui/ai/AiDock'\nimport { useAiChatSessions } from '@open-mercato/ui/ai/AiChatSessions'\nimport { ChatPaneTabs } from '@open-mercato/ui/ai/ChatPaneTabs'\n// Side-effect import: registers the `catalog.stats-card` UI part on the\n// global registry the first time this client bundle loads. Tools that\n// emit `{ uiPart: { componentId: 'catalog.stats-card' } }` envelopes\n// (catalog.show_stats today; user-defined tools tomorrow) automatically\n// resolve to the card without dispatcher changes.\nimport '../../../components/CatalogStatsCard'\nimport { apiCall } from '@open-mercato/ui/backend/utils/apiCall'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { ButtonGroup } from '@open-mercato/ui/primitives/button-group'\nimport { IconButton } from '@open-mercato/ui/primitives/icon-button'\nimport {\n Dialog,\n DialogContent,\n DialogDescription,\n DialogHeader,\n DialogTitle,\n} from '@open-mercato/ui/primitives/dialog'\nimport { Popover, PopoverContent, PopoverTrigger } from '@open-mercato/ui/primitives/popover'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { cn } from '@open-mercato/shared/lib/utils'\n\nexport interface MerchandisingPageContextFilter {\n categoryId: string | null\n priceRange: { min?: number; max?: number } | null\n tags: string[]\n status: string | null\n}\n\nexport interface MerchandisingPageContext {\n view: 'catalog.products.list'\n entityType?: 'catalog.products.list'\n recordType: null\n recordId: string\n extra: {\n filter: MerchandisingPageContextFilter\n totalMatching: number\n /** `totalMatching` is a floor, not an exact count (server-capped list count). */\n totalMatchingIsCapped?: boolean\n selectedCount: number\n }\n}\n\nexport interface MerchandisingAssistantSheetProps {\n /** Selection-aware page context, built by the products list host. */\n pageContext: MerchandisingPageContext\n /** When false (feature-gated by the host), the sheet renders nothing. */\n enabled?: boolean\n className?: string\n}\n\nexport const MERCHANDISING_AGENT_ID = 'catalog.merchandising_assistant'\n\nfunction useMerchandisingSuggestions(\n hasSelection: boolean,\n selectedCount: number,\n): AiChatSuggestion[] {\n const t = useT()\n return React.useMemo(() => {\n if (hasSelection) {\n return [\n {\n label: t(\n 'catalog.merchandising_assistant.suggestions.draftDescriptions',\n 'Draft product descriptions for selected items',\n ),\n prompt: `Draft compelling product descriptions for my ${selectedCount} selected products`,\n icon: <PenLine className=\"size-4\" />,\n },\n {\n label: t(\n 'catalog.merchandising_assistant.suggestions.extractAttributes',\n 'Extract attributes from descriptions',\n ),\n prompt: `Extract structured attributes from the descriptions of my ${selectedCount} selected products`,\n icon: <Tags className=\"size-4\" />,\n },\n {\n label: t(\n 'catalog.merchandising_assistant.suggestions.titleVariants',\n 'Generate title variants for SEO',\n ),\n prompt: `Generate SEO-optimized title variants for my ${selectedCount} selected products`,\n icon: <FileText className=\"size-4\" />,\n },\n {\n label: t(\n 'catalog.merchandising_assistant.suggestions.priceAdjustments',\n 'Suggest price adjustments',\n ),\n prompt: `Analyze and suggest price adjustments for my ${selectedCount} selected products`,\n icon: <TrendingUp className=\"size-4\" />,\n },\n ]\n }\n return [\n {\n label: t(\n 'catalog.merchandising_assistant.suggestions.showStats',\n 'Show catalog overview',\n ),\n // Triggers the `catalog.show_stats` tool, which returns the inline\n // catalog-stats UI part (live counts of products, active products,\n // categories, tags). Demo entry-point for the dynamic UI-part path.\n prompt: 'Show me a quick catalog overview using the stats card.',\n icon: <Boxes className=\"size-4\" />,\n },\n {\n label: t(\n 'catalog.merchandising_assistant.suggestions.browseProducts',\n 'Show me an overview of my product catalog',\n ),\n prompt: 'Give me an overview of my product catalog \u2014 categories, total products, and pricing ranges',\n icon: <Package className=\"size-4\" />,\n },\n {\n label: t(\n 'catalog.merchandising_assistant.suggestions.findMissingDescriptions',\n 'Find products with missing descriptions',\n ),\n prompt: 'Find products that are missing descriptions or have very short descriptions',\n icon: <PenLine className=\"size-4\" />,\n },\n {\n label: t(\n 'catalog.merchandising_assistant.suggestions.analyzeAttributes',\n 'Analyze attribute coverage',\n ),\n prompt: 'Analyze which products have incomplete attribute data',\n icon: <Tags className=\"size-4\" />,\n },\n {\n label: t(\n 'catalog.merchandising_assistant.suggestions.pricingOverview',\n 'Show pricing distribution',\n ),\n prompt: 'Show me the pricing distribution across categories',\n icon: <TrendingUp className=\"size-4\" />,\n },\n ]\n }, [hasSelection, selectedCount, t])\n}\n\nfunction useContextItems(pageContext: MerchandisingPageContext): AiChatContextItem[] {\n const t = useT()\n return React.useMemo(() => {\n const items: AiChatContextItem[] = []\n const { selectedCount, totalMatching, totalMatchingIsCapped, filter } = pageContext.extra\n if (selectedCount > 0) {\n items.push({\n label: t(\n 'catalog.merchandising_assistant.context.selectedProducts',\n '{count} products selected',\n ).replace('{count}', String(selectedCount)),\n })\n } else if (totalMatching > 0) {\n items.push({\n label: totalMatchingIsCapped\n ? t(\n 'catalog.merchandising_assistant.context.matchingProductsCapped',\n '{count}+ products in view',\n ).replace('{count}', String(totalMatching))\n : t(\n 'catalog.merchandising_assistant.context.matchingProducts',\n '{count} products in view',\n ).replace('{count}', String(totalMatching)),\n })\n }\n if (filter.categoryId) {\n items.push({\n label: t('catalog.merchandising_assistant.context.filteredByCategory', 'Filtered by category'),\n detail: filter.categoryId,\n })\n }\n if (filter.status) {\n items.push({ label: filter.status })\n }\n if (filter.tags.length > 0) {\n items.push({\n label: t('catalog.merchandising_assistant.context.tags', '{count} tags').replace(\n '{count}',\n String(filter.tags.length),\n ),\n })\n }\n return items\n }, [pageContext, t])\n}\n\ninterface MerchandisingAgentDescriptor {\n id: string\n label: string\n description: string\n icon: React.ReactNode\n}\n\ninterface AgentsResponse {\n agents?: Array<{\n id?: string | null\n }>\n}\n\ninterface MerchandisingAgentsState {\n agents: MerchandisingAgentDescriptor[]\n loaded: boolean\n}\n\nfunction useMerchandisingAgents(): MerchandisingAgentsState {\n const t = useT()\n const [accessibleAgentIds, setAccessibleAgentIds] = React.useState<Set<string> | null>(null)\n const declaredAgents = React.useMemo(\n () => [\n {\n id: MERCHANDISING_AGENT_ID,\n label: t(\n 'catalog.merchandising_assistant.agents.merchandising.label',\n 'Merchandising Assistant',\n ),\n description: t(\n 'catalog.merchandising_assistant.agents.merchandising.description',\n 'Draft copy, normalize attributes, and propose price changes for the current selection.',\n ),\n icon: <AiIcon className=\"size-4\" />,\n },\n ],\n [t],\n )\n\n React.useEffect(() => {\n let cancelled = false\n apiCall<AgentsResponse>('/api/ai_assistant/ai/agents', {\n credentials: 'same-origin',\n headers: { 'x-om-forbidden-redirect': '0', 'x-om-unauthorized-redirect': '0' },\n })\n .then((call) => {\n if (cancelled) return\n if (!call.ok || !call.result || !Array.isArray(call.result.agents)) {\n setAccessibleAgentIds(new Set())\n return\n }\n setAccessibleAgentIds(\n new Set(\n call.result.agents\n .map((agent) => agent?.id)\n .filter((id): id is string => typeof id === 'string' && id.length > 0),\n ),\n )\n })\n .catch(() => {\n if (!cancelled) setAccessibleAgentIds(new Set())\n })\n return () => {\n cancelled = true\n }\n }, [])\n\n return React.useMemo(\n () => ({\n agents:\n accessibleAgentIds === null\n ? []\n : declaredAgents.filter((agent) => accessibleAgentIds.has(agent.id)),\n loaded: accessibleAgentIds !== null,\n }),\n [accessibleAgentIds, declaredAgents],\n )\n}\n\nexport function MerchandisingAssistantSheet({\n pageContext,\n enabled = true,\n className,\n}: MerchandisingAssistantSheetProps): React.ReactElement | null {\n const t = useT()\n const dock = useAiDock()\n const [open, setOpen] = React.useState(false)\n const [popoverOpen, setPopoverOpen] = React.useState(false)\n const [activeAgent, setActiveAgent] = React.useState<string>(MERCHANDISING_AGENT_ID)\n const [lastAgent, setLastAgent] = React.useState<string | null>(null)\n\n const selectedCount = pageContext.extra.selectedCount\n const hasSelection = selectedCount > 0\n const suggestions = useMerchandisingSuggestions(hasSelection, selectedCount)\n const contextItems = useContextItems(pageContext)\n const { agents, loaded: agentsLoaded } = useMerchandisingAgents()\n\n if (!enabled || !agentsLoaded || agents.length === 0) return null\n\n const openAgent = (agentId: string) => {\n setActiveAgent(agentId)\n setLastAgent(agentId)\n setPopoverOpen(false)\n if (dock.state.assistant?.agent === agentId) {\n dock.dock(dock.state.assistant)\n setOpen(false)\n return\n }\n setOpen(true)\n }\n\n const handleSelectAgent = (agentId: string) => {\n openAgent(agentId)\n }\n\n const handleMainTriggerClick = () => {\n if (agents.length === 1) {\n openAgent(agents[0].id)\n return\n }\n if (lastAgent && agents.some((a) => a.id === lastAgent)) {\n openAgent(lastAgent)\n return\n }\n setPopoverOpen(true)\n }\n\n const handleDock = () => {\n const agent = agents.find((a) => a.id === activeAgent) ?? agents[0]\n if (!agent) return\n dock.dock({\n agent: agent.id,\n label: agent.label,\n description: t('catalog.merchandising_assistant.dock.subtitle', 'Catalog'),\n pageContext: pageContext as unknown as Record<string, unknown>,\n placeholder: t(\n 'catalog.merchandising_assistant.sheet.composerPlaceholder',\n 'Ask for descriptions, attributes, titles, or price ideas...',\n ),\n suggestions,\n contextItems,\n welcomeTitle: t(\n 'catalog.merchandising_assistant.sheet.welcomeTitle',\n 'Merchandising Assistant',\n ),\n welcomeDescription: hasSelection\n ? t(\n 'catalog.merchandising_assistant.sheet.welcomeDescriptionSelection',\n 'Ready to work with your {count} selected products. Try one of these:',\n ).replace('{count}', String(selectedCount))\n : t(\n 'catalog.merchandising_assistant.sheet.welcomeDescriptionAll',\n 'Select products for targeted actions, or explore your catalog:',\n ),\n })\n setOpen(false)\n }\n\n const triggerLabel = t(\n 'catalog.merchandising_assistant.trigger.ariaLabel',\n 'Open AI merchandising assistant',\n )\n const labelText = t('catalog.merchandising_assistant.trigger.label', 'AI')\n const moreAgentsLabel = t(\n 'catalog.merchandising_assistant.trigger.moreAgentsAriaLabel',\n 'Choose an AI assistant',\n )\n\n return (\n <>\n <ButtonGroup className={className}>\n <Button\n type=\"button\"\n variant=\"outline\"\n onClick={handleMainTriggerClick}\n data-ai-merchandising-trigger=\"\"\n aria-label={triggerLabel}\n title={triggerLabel}\n className=\"relative\"\n >\n <AiIcon className=\"size-4\" />\n <span>{labelText}</span>\n {hasSelection ? (\n <span\n className=\"absolute -top-1 -right-1 inline-flex h-4 min-w-4 items-center justify-center rounded-full bg-primary px-1 text-[10px] font-medium leading-none text-primary-foreground\"\n data-ai-merchandising-selected-count={selectedCount}\n >\n {selectedCount}\n </span>\n ) : null}\n </Button>\n {agents.length > 1 ? (\n <Popover open={popoverOpen} onOpenChange={setPopoverOpen}>\n <PopoverTrigger asChild>\n <IconButton\n type=\"button\"\n variant=\"outline\"\n aria-label={moreAgentsLabel}\n title={moreAgentsLabel}\n data-ai-merchandising-picker=\"\"\n >\n <ChevronDown className=\"size-4\" aria-hidden />\n </IconButton>\n </PopoverTrigger>\n <PopoverContent align=\"end\" className=\"w-72 p-1\">\n <div className=\"px-3 pt-2 pb-1 text-xs font-medium uppercase tracking-wide text-muted-foreground\">\n {t('catalog.merchandising_assistant.popover.heading', 'AI assistants')}\n </div>\n <div className=\"flex flex-col gap-0.5\">\n {agents.map((agent) => (\n <Button\n key={agent.id}\n type=\"button\"\n variant=\"ghost\"\n onClick={() => handleSelectAgent(agent.id)}\n data-ai-merchandising-agent-option={agent.id}\n className=\"h-auto w-full items-start justify-start gap-2 whitespace-normal rounded-sm px-2 py-2 text-left text-sm font-normal\"\n >\n <span className=\"mt-0.5 inline-flex size-6 items-center justify-center rounded-full bg-secondary text-secondary-foreground\">\n {agent.icon}\n </span>\n <span className=\"flex-1 min-w-0\">\n <span className=\"block font-medium leading-tight\">{agent.label}</span>\n <span className=\"block text-xs text-muted-foreground leading-snug\">\n {agent.description}\n </span>\n </span>\n </Button>\n ))}\n </div>\n </PopoverContent>\n </Popover>\n ) : null}\n </ButtonGroup>\n <Dialog open={open} onOpenChange={setOpen}>\n <DialogContent\n className={cn(\n // Mobile: full-screen sheet. Desktop (\u2265sm): right-anchored side sheet.\n // The Dialog primitive applies a centering transform at the\n // sm breakpoint; each piece (`top`, `left`, transform, inset)\n // must be overridden at the same breakpoint or the panel\n // renders half off the viewport.\n 'top-0 left-0 right-0 bottom-0 translate-x-0 translate-y-0 max-w-none w-screen h-svh max-h-svh rounded-none',\n 'sm:top-0 sm:bottom-0 sm:right-0 sm:left-auto sm:translate-x-0 sm:translate-y-0',\n 'sm:max-w-xl sm:w-[36rem] sm:rounded-l-2xl sm:h-screen sm:max-h-screen',\n 'flex flex-col gap-3 p-4 z-[70]',\n )}\n data-ai-merchandising-sheet=\"\"\n >\n <DialogHeader>\n <div className=\"flex items-center gap-3 pr-8\">\n {/* Dock button lives on the LEFT \u2014 the Dialog primitive\n auto-renders an X close button absolutely positioned in\n the top-right corner, so anything we drop in the header's\n right side visually collides with it. Mobile hides the\n dock entirely (the side panel is desktop-only). */}\n <IconButton\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n aria-label={t('catalog.merchandising_assistant.sheet.dock', 'Dock to side')}\n title={t('catalog.merchandising_assistant.sheet.dock', 'Dock to side')}\n onClick={handleDock}\n data-ai-merchandising-dock=\"\"\n className=\"hidden lg:inline-flex shrink-0\"\n >\n <PanelRightOpen className=\"size-4\" aria-hidden />\n </IconButton>\n <DialogTitle className=\"flex-1 min-w-0 truncate\">\n {t('catalog.merchandising_assistant.sheet.title', 'Catalog merchandising assistant')}\n </DialogTitle>\n {hasSelection ? (\n <span\n className=\"shrink-0 inline-flex items-center rounded-full border border-border bg-secondary px-2 py-0.5 text-xs text-secondary-foreground\"\n data-ai-merchandising-selection-pill=\"\"\n data-ai-merchandising-selected-count={selectedCount}\n >\n {t(\n 'catalog.merchandising_assistant.sheet.selectionPill',\n 'Acting on {count} products',\n ).replace('{count}', String(selectedCount))}\n </span>\n ) : null}\n </div>\n <DialogDescription>\n {hasSelection\n ? t(\n 'catalog.merchandising_assistant.sheet.descriptionWithSelection',\n 'Working with {count} selected products. Ask for descriptions, attribute extraction, title suggestions, or pricing analysis.',\n ).replace('{count}', String(selectedCount))\n : t(\n 'catalog.merchandising_assistant.sheet.description',\n 'Your AI merchandising copilot. Select products from the list for targeted actions, or explore your full catalog.',\n )}\n </DialogDescription>\n </DialogHeader>\n <MerchandisingChatBody\n activeAgent={activeAgent}\n pageContext={pageContext}\n suggestions={suggestions}\n contextItems={contextItems}\n hasSelection={hasSelection}\n selectedCount={selectedCount}\n />\n </DialogContent>\n </Dialog>\n </>\n )\n}\n\ninterface MerchandisingChatBodyProps {\n activeAgent: string\n pageContext: MerchandisingPageContext\n suggestions: AiChatSuggestion[]\n contextItems: AiChatContextItem[]\n hasSelection: boolean\n selectedCount: number\n}\n\nfunction MerchandisingChatBody({\n activeAgent,\n pageContext,\n suggestions,\n contextItems,\n hasSelection,\n selectedCount,\n}: MerchandisingChatBodyProps) {\n const t = useT()\n const sessions = useAiChatSessions()\n const session = sessions.getActiveSession(activeAgent)\n\n React.useEffect(() => {\n if (!session) sessions.ensureSession(activeAgent)\n }, [activeAgent, session, sessions])\n\n return (\n <>\n <ChatPaneTabs agentId={activeAgent} className=\"border-b\" />\n <div className=\"min-h-0 flex-1\" data-ai-merchandising-chat-container=\"\">\n {session ? (\n <AiChat\n key={session.id}\n agent={activeAgent}\n conversationId={session.conversationId}\n pageContext={pageContext as unknown as Record<string, unknown>}\n className=\"h-full\"\n placeholder={t(\n 'catalog.merchandising_assistant.sheet.composerPlaceholder',\n 'Ask for descriptions, attributes, titles, or price ideas...',\n )}\n suggestions={suggestions}\n contextItems={contextItems}\n welcomeTitle={t(\n 'catalog.merchandising_assistant.sheet.welcomeTitle',\n 'Merchandising Assistant',\n )}\n welcomeDescription={\n hasSelection\n ? t(\n 'catalog.merchandising_assistant.sheet.welcomeDescriptionSelection',\n 'Ready to work with your {count} selected products. Try one of these:',\n ).replace('{count}', String(selectedCount))\n : t(\n 'catalog.merchandising_assistant.sheet.welcomeDescriptionAll',\n 'Select products for targeted actions, or explore your catalog:',\n )\n }\n />\n ) : null}\n </div>\n </>\n )\n}\n\nexport default MerchandisingAssistantSheet\n"],
|
|
5
|
-
"mappings": ";
|
|
4
|
+
"sourcesContent": ["\"use client\"\n\n/**\n * MerchandisingAssistantSheet \u2014 Step 4.9 (Spec \u00A710 D18).\n *\n * Embeds `<AiChat agent=\"catalog.merchandising_assistant\" pageContext={...} />`\n * in a right-side sheet (built on the shared Dialog primitive because\n * `packages/ui` does not ship a dedicated Sheet/Drawer primitive in\n * Phase 2). The trigger is a button rendered in the products-list page\n * header.\n *\n * Phase 2 is strictly read-only: the sheet shows proposals (structured\n * output), but the mutation tools (`catalog.update_product`,\n * `catalog.bulk_update_products`, `catalog.apply_attribute_extraction`,\n * `catalog.update_product_media_descriptions`) are intentionally NOT in\n * the agent whitelist. Phase 5.14 introduces those via the pending-action\n * contract.\n *\n * pageContext follows spec \u00A710.1 exactly:\n *\n * {\n * view: 'catalog.products.list',\n * recordType: null,\n * recordId: string, // \"\" or comma-separated UUIDs\n * extra: {\n * filter: { categoryId, priceRange, tags, status },\n * totalMatching: number,\n * selectedCount: number,\n * }\n * }\n */\n\nimport * as React from 'react'\nimport { Boxes, ChevronDown, FileText, Package, PanelRightOpen, PenLine, Tags, TrendingUp } from 'lucide-react'\nimport { AiChat, type AiChatSuggestion, type AiChatContextItem } from '@open-mercato/ui/ai/AiChat'\nimport { AiIcon } from '@open-mercato/ui/ai/AiIcon'\nimport { useAiDock } from '@open-mercato/ui/ai/AiDock'\nimport { useAiChatSessions } from '@open-mercato/ui/ai/AiChatSessions'\nimport { ChatPaneTabs } from '@open-mercato/ui/ai/ChatPaneTabs'\n// Side-effect import: registers the `catalog.stats-card` UI part on the\n// global registry the first time this client bundle loads. Tools that\n// emit `{ uiPart: { componentId: 'catalog.stats-card' } }` envelopes\n// (catalog.show_stats today; user-defined tools tomorrow) automatically\n// resolve to the card without dispatcher changes.\nimport '../../../components/CatalogStatsCard'\nimport { apiCall } from '@open-mercato/ui/backend/utils/apiCall'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { ButtonGroup } from '@open-mercato/ui/primitives/button-group'\nimport { IconButton } from '@open-mercato/ui/primitives/icon-button'\nimport {\n Dialog,\n DialogContent,\n DialogDescription,\n DialogHeader,\n DialogTitle,\n} from '@open-mercato/ui/primitives/dialog'\nimport { Popover, PopoverContent, PopoverTrigger } from '@open-mercato/ui/primitives/popover'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { cn } from '@open-mercato/shared/lib/utils'\n\nexport interface MerchandisingPageContextFilter {\n categoryId: string | null\n priceRange: { min?: number; max?: number } | null\n tags: string[]\n status: string | null\n}\n\nexport interface MerchandisingPageContext {\n view: 'catalog.products.list'\n entityType?: 'catalog.products.list'\n recordType: null\n recordId: string\n extra: {\n filter: MerchandisingPageContextFilter\n totalMatching: number\n selectedCount: number\n }\n}\n\nexport interface MerchandisingAssistantSheetProps {\n /** Selection-aware page context, built by the products list host. */\n pageContext: MerchandisingPageContext\n /** When false (feature-gated by the host), the sheet renders nothing. */\n enabled?: boolean\n className?: string\n}\n\nexport const MERCHANDISING_AGENT_ID = 'catalog.merchandising_assistant'\n\nfunction useMerchandisingSuggestions(\n hasSelection: boolean,\n selectedCount: number,\n): AiChatSuggestion[] {\n const t = useT()\n return React.useMemo(() => {\n if (hasSelection) {\n return [\n {\n label: t(\n 'catalog.merchandising_assistant.suggestions.draftDescriptions',\n 'Draft product descriptions for selected items',\n ),\n prompt: `Draft compelling product descriptions for my ${selectedCount} selected products`,\n icon: <PenLine className=\"size-4\" />,\n },\n {\n label: t(\n 'catalog.merchandising_assistant.suggestions.extractAttributes',\n 'Extract attributes from descriptions',\n ),\n prompt: `Extract structured attributes from the descriptions of my ${selectedCount} selected products`,\n icon: <Tags className=\"size-4\" />,\n },\n {\n label: t(\n 'catalog.merchandising_assistant.suggestions.titleVariants',\n 'Generate title variants for SEO',\n ),\n prompt: `Generate SEO-optimized title variants for my ${selectedCount} selected products`,\n icon: <FileText className=\"size-4\" />,\n },\n {\n label: t(\n 'catalog.merchandising_assistant.suggestions.priceAdjustments',\n 'Suggest price adjustments',\n ),\n prompt: `Analyze and suggest price adjustments for my ${selectedCount} selected products`,\n icon: <TrendingUp className=\"size-4\" />,\n },\n ]\n }\n return [\n {\n label: t(\n 'catalog.merchandising_assistant.suggestions.showStats',\n 'Show catalog overview',\n ),\n // Triggers the `catalog.show_stats` tool, which returns the inline\n // catalog-stats UI part (live counts of products, active products,\n // categories, tags). Demo entry-point for the dynamic UI-part path.\n prompt: 'Show me a quick catalog overview using the stats card.',\n icon: <Boxes className=\"size-4\" />,\n },\n {\n label: t(\n 'catalog.merchandising_assistant.suggestions.browseProducts',\n 'Show me an overview of my product catalog',\n ),\n prompt: 'Give me an overview of my product catalog \u2014 categories, total products, and pricing ranges',\n icon: <Package className=\"size-4\" />,\n },\n {\n label: t(\n 'catalog.merchandising_assistant.suggestions.findMissingDescriptions',\n 'Find products with missing descriptions',\n ),\n prompt: 'Find products that are missing descriptions or have very short descriptions',\n icon: <PenLine className=\"size-4\" />,\n },\n {\n label: t(\n 'catalog.merchandising_assistant.suggestions.analyzeAttributes',\n 'Analyze attribute coverage',\n ),\n prompt: 'Analyze which products have incomplete attribute data',\n icon: <Tags className=\"size-4\" />,\n },\n {\n label: t(\n 'catalog.merchandising_assistant.suggestions.pricingOverview',\n 'Show pricing distribution',\n ),\n prompt: 'Show me the pricing distribution across categories',\n icon: <TrendingUp className=\"size-4\" />,\n },\n ]\n }, [hasSelection, selectedCount, t])\n}\n\nfunction useContextItems(pageContext: MerchandisingPageContext): AiChatContextItem[] {\n const t = useT()\n return React.useMemo(() => {\n const items: AiChatContextItem[] = []\n const { selectedCount, totalMatching, filter } = pageContext.extra\n if (selectedCount > 0) {\n items.push({\n label: t(\n 'catalog.merchandising_assistant.context.selectedProducts',\n '{count} products selected',\n ).replace('{count}', String(selectedCount)),\n })\n } else if (totalMatching > 0) {\n items.push({\n label: t(\n 'catalog.merchandising_assistant.context.matchingProducts',\n '{count} products in view',\n ).replace('{count}', String(totalMatching)),\n })\n }\n if (filter.categoryId) {\n items.push({\n label: t('catalog.merchandising_assistant.context.filteredByCategory', 'Filtered by category'),\n detail: filter.categoryId,\n })\n }\n if (filter.status) {\n items.push({ label: filter.status })\n }\n if (filter.tags.length > 0) {\n items.push({\n label: t('catalog.merchandising_assistant.context.tags', '{count} tags').replace(\n '{count}',\n String(filter.tags.length),\n ),\n })\n }\n return items\n }, [pageContext, t])\n}\n\ninterface MerchandisingAgentDescriptor {\n id: string\n label: string\n description: string\n icon: React.ReactNode\n}\n\ninterface AgentsResponse {\n agents?: Array<{\n id?: string | null\n }>\n}\n\ninterface MerchandisingAgentsState {\n agents: MerchandisingAgentDescriptor[]\n loaded: boolean\n}\n\nfunction useMerchandisingAgents(): MerchandisingAgentsState {\n const t = useT()\n const [accessibleAgentIds, setAccessibleAgentIds] = React.useState<Set<string> | null>(null)\n const declaredAgents = React.useMemo(\n () => [\n {\n id: MERCHANDISING_AGENT_ID,\n label: t(\n 'catalog.merchandising_assistant.agents.merchandising.label',\n 'Merchandising Assistant',\n ),\n description: t(\n 'catalog.merchandising_assistant.agents.merchandising.description',\n 'Draft copy, normalize attributes, and propose price changes for the current selection.',\n ),\n icon: <AiIcon className=\"size-4\" />,\n },\n ],\n [t],\n )\n\n React.useEffect(() => {\n let cancelled = false\n apiCall<AgentsResponse>('/api/ai_assistant/ai/agents', {\n credentials: 'same-origin',\n headers: { 'x-om-forbidden-redirect': '0', 'x-om-unauthorized-redirect': '0' },\n })\n .then((call) => {\n if (cancelled) return\n if (!call.ok || !call.result || !Array.isArray(call.result.agents)) {\n setAccessibleAgentIds(new Set())\n return\n }\n setAccessibleAgentIds(\n new Set(\n call.result.agents\n .map((agent) => agent?.id)\n .filter((id): id is string => typeof id === 'string' && id.length > 0),\n ),\n )\n })\n .catch(() => {\n if (!cancelled) setAccessibleAgentIds(new Set())\n })\n return () => {\n cancelled = true\n }\n }, [])\n\n return React.useMemo(\n () => ({\n agents:\n accessibleAgentIds === null\n ? []\n : declaredAgents.filter((agent) => accessibleAgentIds.has(agent.id)),\n loaded: accessibleAgentIds !== null,\n }),\n [accessibleAgentIds, declaredAgents],\n )\n}\n\nexport function MerchandisingAssistantSheet({\n pageContext,\n enabled = true,\n className,\n}: MerchandisingAssistantSheetProps): React.ReactElement | null {\n const t = useT()\n const dock = useAiDock()\n const [open, setOpen] = React.useState(false)\n const [popoverOpen, setPopoverOpen] = React.useState(false)\n const [activeAgent, setActiveAgent] = React.useState<string>(MERCHANDISING_AGENT_ID)\n const [lastAgent, setLastAgent] = React.useState<string | null>(null)\n\n const selectedCount = pageContext.extra.selectedCount\n const hasSelection = selectedCount > 0\n const suggestions = useMerchandisingSuggestions(hasSelection, selectedCount)\n const contextItems = useContextItems(pageContext)\n const { agents, loaded: agentsLoaded } = useMerchandisingAgents()\n\n if (!enabled || !agentsLoaded || agents.length === 0) return null\n\n const openAgent = (agentId: string) => {\n setActiveAgent(agentId)\n setLastAgent(agentId)\n setPopoverOpen(false)\n if (dock.state.assistant?.agent === agentId) {\n dock.dock(dock.state.assistant)\n setOpen(false)\n return\n }\n setOpen(true)\n }\n\n const handleSelectAgent = (agentId: string) => {\n openAgent(agentId)\n }\n\n const handleMainTriggerClick = () => {\n if (agents.length === 1) {\n openAgent(agents[0].id)\n return\n }\n if (lastAgent && agents.some((a) => a.id === lastAgent)) {\n openAgent(lastAgent)\n return\n }\n setPopoverOpen(true)\n }\n\n const handleDock = () => {\n const agent = agents.find((a) => a.id === activeAgent) ?? agents[0]\n if (!agent) return\n dock.dock({\n agent: agent.id,\n label: agent.label,\n description: t('catalog.merchandising_assistant.dock.subtitle', 'Catalog'),\n pageContext: pageContext as unknown as Record<string, unknown>,\n placeholder: t(\n 'catalog.merchandising_assistant.sheet.composerPlaceholder',\n 'Ask for descriptions, attributes, titles, or price ideas...',\n ),\n suggestions,\n contextItems,\n welcomeTitle: t(\n 'catalog.merchandising_assistant.sheet.welcomeTitle',\n 'Merchandising Assistant',\n ),\n welcomeDescription: hasSelection\n ? t(\n 'catalog.merchandising_assistant.sheet.welcomeDescriptionSelection',\n 'Ready to work with your {count} selected products. Try one of these:',\n ).replace('{count}', String(selectedCount))\n : t(\n 'catalog.merchandising_assistant.sheet.welcomeDescriptionAll',\n 'Select products for targeted actions, or explore your catalog:',\n ),\n })\n setOpen(false)\n }\n\n const triggerLabel = t(\n 'catalog.merchandising_assistant.trigger.ariaLabel',\n 'Open AI merchandising assistant',\n )\n const labelText = t('catalog.merchandising_assistant.trigger.label', 'AI')\n const moreAgentsLabel = t(\n 'catalog.merchandising_assistant.trigger.moreAgentsAriaLabel',\n 'Choose an AI assistant',\n )\n\n return (\n <>\n <ButtonGroup className={className}>\n <Button\n type=\"button\"\n variant=\"outline\"\n onClick={handleMainTriggerClick}\n data-ai-merchandising-trigger=\"\"\n aria-label={triggerLabel}\n title={triggerLabel}\n className=\"relative\"\n >\n <AiIcon className=\"size-4\" />\n <span>{labelText}</span>\n {hasSelection ? (\n <span\n className=\"absolute -top-1 -right-1 inline-flex h-4 min-w-4 items-center justify-center rounded-full bg-primary px-1 text-[10px] font-medium leading-none text-primary-foreground\"\n data-ai-merchandising-selected-count={selectedCount}\n >\n {selectedCount}\n </span>\n ) : null}\n </Button>\n {agents.length > 1 ? (\n <Popover open={popoverOpen} onOpenChange={setPopoverOpen}>\n <PopoverTrigger asChild>\n <IconButton\n type=\"button\"\n variant=\"outline\"\n aria-label={moreAgentsLabel}\n title={moreAgentsLabel}\n data-ai-merchandising-picker=\"\"\n >\n <ChevronDown className=\"size-4\" aria-hidden />\n </IconButton>\n </PopoverTrigger>\n <PopoverContent align=\"end\" className=\"w-72 p-1\">\n <div className=\"px-3 pt-2 pb-1 text-xs font-medium uppercase tracking-wide text-muted-foreground\">\n {t('catalog.merchandising_assistant.popover.heading', 'AI assistants')}\n </div>\n <div className=\"flex flex-col gap-0.5\">\n {agents.map((agent) => (\n <Button\n key={agent.id}\n type=\"button\"\n variant=\"ghost\"\n onClick={() => handleSelectAgent(agent.id)}\n data-ai-merchandising-agent-option={agent.id}\n className=\"h-auto w-full items-start justify-start gap-2 whitespace-normal rounded-sm px-2 py-2 text-left text-sm font-normal\"\n >\n <span className=\"mt-0.5 inline-flex size-6 items-center justify-center rounded-full bg-secondary text-secondary-foreground\">\n {agent.icon}\n </span>\n <span className=\"flex-1 min-w-0\">\n <span className=\"block font-medium leading-tight\">{agent.label}</span>\n <span className=\"block text-xs text-muted-foreground leading-snug\">\n {agent.description}\n </span>\n </span>\n </Button>\n ))}\n </div>\n </PopoverContent>\n </Popover>\n ) : null}\n </ButtonGroup>\n <Dialog open={open} onOpenChange={setOpen}>\n <DialogContent\n className={cn(\n // Mobile: full-screen sheet. Desktop (\u2265sm): right-anchored side sheet.\n // The Dialog primitive applies a centering transform at the\n // sm breakpoint; each piece (`top`, `left`, transform, inset)\n // must be overridden at the same breakpoint or the panel\n // renders half off the viewport.\n 'top-0 left-0 right-0 bottom-0 translate-x-0 translate-y-0 max-w-none w-screen h-svh max-h-svh rounded-none',\n 'sm:top-0 sm:bottom-0 sm:right-0 sm:left-auto sm:translate-x-0 sm:translate-y-0',\n 'sm:max-w-xl sm:w-[36rem] sm:rounded-l-2xl sm:h-screen sm:max-h-screen',\n 'flex flex-col gap-3 p-4 z-[70]',\n )}\n data-ai-merchandising-sheet=\"\"\n >\n <DialogHeader>\n <div className=\"flex items-center gap-3 pr-8\">\n {/* Dock button lives on the LEFT \u2014 the Dialog primitive\n auto-renders an X close button absolutely positioned in\n the top-right corner, so anything we drop in the header's\n right side visually collides with it. Mobile hides the\n dock entirely (the side panel is desktop-only). */}\n <IconButton\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n aria-label={t('catalog.merchandising_assistant.sheet.dock', 'Dock to side')}\n title={t('catalog.merchandising_assistant.sheet.dock', 'Dock to side')}\n onClick={handleDock}\n data-ai-merchandising-dock=\"\"\n className=\"hidden lg:inline-flex shrink-0\"\n >\n <PanelRightOpen className=\"size-4\" aria-hidden />\n </IconButton>\n <DialogTitle className=\"flex-1 min-w-0 truncate\">\n {t('catalog.merchandising_assistant.sheet.title', 'Catalog merchandising assistant')}\n </DialogTitle>\n {hasSelection ? (\n <span\n className=\"shrink-0 inline-flex items-center rounded-full border border-border bg-secondary px-2 py-0.5 text-xs text-secondary-foreground\"\n data-ai-merchandising-selection-pill=\"\"\n data-ai-merchandising-selected-count={selectedCount}\n >\n {t(\n 'catalog.merchandising_assistant.sheet.selectionPill',\n 'Acting on {count} products',\n ).replace('{count}', String(selectedCount))}\n </span>\n ) : null}\n </div>\n <DialogDescription>\n {hasSelection\n ? t(\n 'catalog.merchandising_assistant.sheet.descriptionWithSelection',\n 'Working with {count} selected products. Ask for descriptions, attribute extraction, title suggestions, or pricing analysis.',\n ).replace('{count}', String(selectedCount))\n : t(\n 'catalog.merchandising_assistant.sheet.description',\n 'Your AI merchandising copilot. Select products from the list for targeted actions, or explore your full catalog.',\n )}\n </DialogDescription>\n </DialogHeader>\n <MerchandisingChatBody\n activeAgent={activeAgent}\n pageContext={pageContext}\n suggestions={suggestions}\n contextItems={contextItems}\n hasSelection={hasSelection}\n selectedCount={selectedCount}\n />\n </DialogContent>\n </Dialog>\n </>\n )\n}\n\ninterface MerchandisingChatBodyProps {\n activeAgent: string\n pageContext: MerchandisingPageContext\n suggestions: AiChatSuggestion[]\n contextItems: AiChatContextItem[]\n hasSelection: boolean\n selectedCount: number\n}\n\nfunction MerchandisingChatBody({\n activeAgent,\n pageContext,\n suggestions,\n contextItems,\n hasSelection,\n selectedCount,\n}: MerchandisingChatBodyProps) {\n const t = useT()\n const sessions = useAiChatSessions()\n const session = sessions.getActiveSession(activeAgent)\n\n React.useEffect(() => {\n if (!session) sessions.ensureSession(activeAgent)\n }, [activeAgent, session, sessions])\n\n return (\n <>\n <ChatPaneTabs agentId={activeAgent} className=\"border-b\" />\n <div className=\"min-h-0 flex-1\" data-ai-merchandising-chat-container=\"\">\n {session ? (\n <AiChat\n key={session.id}\n agent={activeAgent}\n conversationId={session.conversationId}\n pageContext={pageContext as unknown as Record<string, unknown>}\n className=\"h-full\"\n placeholder={t(\n 'catalog.merchandising_assistant.sheet.composerPlaceholder',\n 'Ask for descriptions, attributes, titles, or price ideas...',\n )}\n suggestions={suggestions}\n contextItems={contextItems}\n welcomeTitle={t(\n 'catalog.merchandising_assistant.sheet.welcomeTitle',\n 'Merchandising Assistant',\n )}\n welcomeDescription={\n hasSelection\n ? t(\n 'catalog.merchandising_assistant.sheet.welcomeDescriptionSelection',\n 'Ready to work with your {count} selected products. Try one of these:',\n ).replace('{count}', String(selectedCount))\n : t(\n 'catalog.merchandising_assistant.sheet.welcomeDescriptionAll',\n 'Select products for targeted actions, or explore your catalog:',\n )\n }\n />\n ) : null}\n </div>\n </>\n )\n}\n\nexport default MerchandisingAssistantSheet\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -82,7 +82,6 @@ function CategoriesDataTable() {
|
|
|
82
82
|
const rows = data?.items ?? [];
|
|
83
83
|
const total = data?.total ?? 0;
|
|
84
84
|
const totalPages = data?.totalPages ?? 0;
|
|
85
|
-
const totalIsCapped = data?.totalIsCapped === true;
|
|
86
85
|
const columns = React.useMemo(() => [
|
|
87
86
|
{
|
|
88
87
|
accessorKey: "name",
|
|
@@ -206,7 +205,6 @@ function CategoriesDataTable() {
|
|
|
206
205
|
pageSize: PAGE_SIZE,
|
|
207
206
|
total,
|
|
208
207
|
totalPages,
|
|
209
|
-
totalIsCapped,
|
|
210
208
|
onPageChange: setPage
|
|
211
209
|
},
|
|
212
210
|
isLoading
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../src/modules/catalog/components/categories/CategoriesDataTable.tsx"],
|
|
4
|
-
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { extensionPoints } from '@open-mercato/core/modules/catalog/extension-points'\nimport Link from 'next/link'\nimport { useQuery, useQueryClient } from '@tanstack/react-query'\nimport type { LegacyColumnDef as ColumnDef } from '@tanstack/react-table/legacy'\nimport { DataTable } from '@open-mercato/ui/backend/DataTable'\nimport { ListEmptyState } from '@open-mercato/ui/backend/filters/ListEmptyState'\nimport type { FilterValues } from '@open-mercato/ui/backend/FilterBar'\nimport { RowActions } from '@open-mercato/ui/backend/RowActions'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { BooleanIcon } from '@open-mercato/ui/backend/ValueIcons'\nimport { apiCall, apiCallOrThrow, readApiResultOrThrow } from '@open-mercato/ui/backend/utils/apiCall'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { useOrganizationScopeVersion } from '@open-mercato/shared/lib/frontend/useOrganizationScope'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { useConfirmDialog } from '@open-mercato/ui/backend/confirm-dialog'\nimport { formatCategoryTreeLabel } from '../../lib/categoryTree'\n\ntype CategoryRow = {\n id: string\n name: string\n slug: string | null\n description: string | null\n parentId: string | null\n parentName: string | null\n depth: number\n treePath: string\n pathLabel: string\n childCount: number\n descendantCount: number\n isActive: boolean\n}\n\ntype CategoriesResponse = {\n items: CategoryRow[]\n total: number\n page: number\n pageSize: number\n totalPages: number\n
|
|
5
|
-
"mappings": ";
|
|
4
|
+
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { extensionPoints } from '@open-mercato/core/modules/catalog/extension-points'\nimport Link from 'next/link'\nimport { useQuery, useQueryClient } from '@tanstack/react-query'\nimport type { LegacyColumnDef as ColumnDef } from '@tanstack/react-table/legacy'\nimport { DataTable } from '@open-mercato/ui/backend/DataTable'\nimport { ListEmptyState } from '@open-mercato/ui/backend/filters/ListEmptyState'\nimport type { FilterValues } from '@open-mercato/ui/backend/FilterBar'\nimport { RowActions } from '@open-mercato/ui/backend/RowActions'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { BooleanIcon } from '@open-mercato/ui/backend/ValueIcons'\nimport { apiCall, apiCallOrThrow, readApiResultOrThrow } from '@open-mercato/ui/backend/utils/apiCall'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { useOrganizationScopeVersion } from '@open-mercato/shared/lib/frontend/useOrganizationScope'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { useConfirmDialog } from '@open-mercato/ui/backend/confirm-dialog'\nimport { formatCategoryTreeLabel } from '../../lib/categoryTree'\n\ntype CategoryRow = {\n id: string\n name: string\n slug: string | null\n description: string | null\n parentId: string | null\n parentName: string | null\n depth: number\n treePath: string\n pathLabel: string\n childCount: number\n descendantCount: number\n isActive: boolean\n}\n\ntype CategoriesResponse = {\n items: CategoryRow[]\n total: number\n page: number\n pageSize: number\n totalPages: number\n}\n\nconst PAGE_SIZE = 50\nconst TREE_BASE_INDENT = 18\nconst TREE_STEP_INDENT = 14\n\nfunction computeIndent(depth: number): number {\n if (depth <= 0) return 0\n return TREE_BASE_INDENT + (depth - 1) * TREE_STEP_INDENT\n}\n\nexport default function CategoriesDataTable() {\n const t = useT()\n const { confirm, ConfirmDialogElement } = useConfirmDialog()\n const queryClient = useQueryClient()\n const scopeVersion = useOrganizationScopeVersion()\n const [page, setPage] = React.useState(1)\n const [status, setStatus] = React.useState<'all' | 'active' | 'inactive'>('all')\n const [search, setSearch] = React.useState('')\n const [canManage, setCanManage] = React.useState(false)\n\n React.useEffect(() => {\n let cancelled = false\n async function load() {\n try {\n const call = await apiCall<{ granted?: string[]; ok?: boolean }>('/api/auth/feature-check', {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ features: ['catalog.categories.manage'] }),\n })\n if (!cancelled) {\n const granted = Array.isArray(call.result?.granted) ? call.result.granted : []\n setCanManage(call.result?.ok === true || granted.includes('catalog.categories.manage'))\n }\n } catch {\n if (!cancelled) setCanManage(false)\n }\n }\n load()\n return () => {\n cancelled = true\n }\n }, [])\n\n const queryParams = React.useMemo(() => {\n const params = new URLSearchParams()\n params.set('view', 'manage')\n params.set('page', String(page))\n params.set('pageSize', String(PAGE_SIZE))\n params.set('status', status)\n if (search) params.set('search', search)\n return params.toString()\n }, [page, status, search])\n\n const { data, isLoading } = useQuery<CategoriesResponse>({\n queryKey: ['catalog-categories', queryParams, scopeVersion],\n queryFn: async () => {\n const payload = await readApiResultOrThrow<CategoriesResponse>(\n `/api/catalog/categories?${queryParams}`,\n undefined,\n { errorMessage: t('catalog.categories.list.error.load', 'Failed to load categories') },\n )\n return {\n items: Array.isArray(payload.items) ? payload.items : [],\n total: typeof payload.total === 'number' ? payload.total : 0,\n page: typeof payload.page === 'number' ? payload.page : 1,\n pageSize: typeof payload.pageSize === 'number' ? payload.pageSize : PAGE_SIZE,\n totalPages: typeof payload.totalPages === 'number' ? payload.totalPages : 1,\n }\n },\n })\n\n const rows = data?.items ?? []\n const total = data?.total ?? 0\n const totalPages = data?.totalPages ?? 0\n\n const columns = React.useMemo<ColumnDef<CategoryRow>[]>(() => [\n {\n accessorKey: 'name',\n header: t('catalog.categories.list.columns.category', 'Category'),\n meta: { priority: 1 },\n cell: ({ row }) => {\n const depth = row.original.depth ?? 0\n return (\n <div className=\"flex items-center text-sm font-medium leading-none text-foreground\">\n <span style={{ marginLeft: computeIndent(depth), whiteSpace: 'pre' }}>\n {formatCategoryTreeLabel(row.original.name, depth)}\n </span>\n </div>\n )\n },\n },\n {\n accessorKey: 'pathLabel',\n header: t('catalog.categories.list.columns.path', 'Path'),\n meta: { priority: 3 },\n cell: ({ getValue }) => {\n const value = getValue<string>()\n return <span className=\"text-xs text-muted-foreground\">{value || '\u2014'}</span>\n },\n },\n {\n accessorKey: 'parentName',\n header: t('catalog.categories.list.columns.parent', 'Parent'),\n meta: { priority: 4 },\n cell: ({ getValue }) => getValue<string>() || t('catalog.categories.list.none', '\u2014'),\n },\n {\n accessorKey: 'childCount',\n header: t('catalog.categories.list.columns.children', 'Children'),\n meta: { priority: 5 },\n },\n {\n accessorKey: 'isActive',\n header: t('catalog.categories.list.columns.active', 'Active'),\n enableSorting: false,\n meta: { priority: 2 },\n cell: ({ getValue }) => <BooleanIcon value={Boolean(getValue())} />,\n },\n ], [t])\n\n const handleDelete = React.useCallback(async (category: CategoryRow) => {\n const confirmLabel = t(\n 'catalog.categories.list.confirmDelete',\n 'Archive category \"{{name}}\"?',\n { name: category.name },\n )\n const confirmed = await confirm({\n title: confirmLabel,\n variant: 'destructive',\n })\n if (!confirmed) return\n try {\n // optimistic-lock-exempt: delete-only mutation \u2014 no field-level lost-update\n await apiCallOrThrow(\n `/api/catalog/categories?id=${encodeURIComponent(category.id)}`,\n { method: 'DELETE' },\n { errorMessage: t('catalog.categories.list.error.delete', 'Failed to delete category') },\n )\n await queryClient.invalidateQueries({ queryKey: ['catalog-categories'] })\n flash(t('catalog.categories.flash.deleted', 'Category archived'), 'success')\n } catch (err: unknown) {\n const fallback = t('catalog.categories.list.error.delete', 'Failed to delete category')\n const message = err instanceof Error ? err.message : fallback\n flash(message, 'error')\n }\n }, [confirm, queryClient, t])\n\n return (\n <>\n <DataTable\n title={t('catalog.categories.list.title', 'Categories')}\n actions={canManage ? (\n <Button asChild>\n <Link href=\"/backend/catalog/categories/create\">\n {t('catalog.categories.list.actions.create', 'Create')}\n </Link>\n </Button>\n ) : undefined}\n columns={columns}\n data={rows}\n emptyState={(\n <ListEmptyState\n entityName={t('catalog.categories.list.title', 'Categories')}\n createHref={canManage ? '/backend/catalog/categories/create' : undefined}\n createLabel={canManage ? t('catalog.categories.list.actions.create', 'Create') : undefined}\n />\n )}\n searchValue={search}\n searchPlaceholder={t('catalog.categories.list.searchPlaceholder', 'Search categories')}\n onSearchChange={(value) => { setSearch(value); setPage(1) }}\n filters={[\n {\n id: 'status',\n label: t('catalog.categories.list.filters.status', 'Status'),\n type: 'select',\n options: [\n { value: 'all', label: t('catalog.categories.list.filters.all', 'All') },\n { value: 'active', label: t('catalog.categories.list.filters.active', 'Active') },\n { value: 'inactive', label: t('catalog.categories.list.filters.inactive', 'Inactive') },\n ],\n },\n ]}\n filterValues={status === 'all' ? {} : { status }}\n onFiltersApply={(values: FilterValues) => {\n const nextStatus = (values.status as 'all' | 'active' | 'inactive' | undefined) ?? 'all'\n setStatus(nextStatus)\n setPage(1)\n }}\n onFiltersClear={() => {\n setStatus('all')\n setPage(1)\n }}\n sortable={false}\n perspective={{ tableId: extensionPoints.hosts.categoriesTable.tableId }}\n rowActions={(row) => (\n canManage ? (\n <RowActions\n items={[\n { id: 'edit', label: t('catalog.categories.list.actions.edit', 'Edit'), href: `/backend/catalog/categories/${row.id}/edit` },\n { id: 'delete', label: t('catalog.categories.list.actions.delete', 'Delete'), destructive: true, onSelect: () => handleDelete(row) },\n ]}\n />\n ) : null\n )}\n pagination={{\n page,\n pageSize: PAGE_SIZE,\n total,\n totalPages,\n onPageChange: setPage,\n }}\n isLoading={isLoading}\n />\n {ConfirmDialogElement}\n </>\n )\n}\n"],
|
|
5
|
+
"mappings": ";AA8HY,SAgER,UAhEQ,KAgER,YAhEQ;AA5HZ,YAAY,WAAW;AACvB,SAAS,uBAAuB;AAChC,OAAO,UAAU;AACjB,SAAS,UAAU,sBAAsB;AAEzC,SAAS,iBAAiB;AAC1B,SAAS,sBAAsB;AAE/B,SAAS,kBAAkB;AAC3B,SAAS,cAAc;AACvB,SAAS,mBAAmB;AAC5B,SAAS,SAAS,gBAAgB,4BAA4B;AAC9D,SAAS,aAAa;AACtB,SAAS,mCAAmC;AAC5C,SAAS,YAAY;AACrB,SAAS,wBAAwB;AACjC,SAAS,+BAA+B;AAyBxC,MAAM,YAAY;AAClB,MAAM,mBAAmB;AACzB,MAAM,mBAAmB;AAEzB,SAAS,cAAc,OAAuB;AAC5C,MAAI,SAAS,EAAG,QAAO;AACvB,SAAO,oBAAoB,QAAQ,KAAK;AAC1C;AAEe,SAAR,sBAAuC;AAC5C,QAAM,IAAI,KAAK;AACf,QAAM,EAAE,SAAS,qBAAqB,IAAI,iBAAiB;AAC3D,QAAM,cAAc,eAAe;AACnC,QAAM,eAAe,4BAA4B;AACjD,QAAM,CAAC,MAAM,OAAO,IAAI,MAAM,SAAS,CAAC;AACxC,QAAM,CAAC,QAAQ,SAAS,IAAI,MAAM,SAAwC,KAAK;AAC/E,QAAM,CAAC,QAAQ,SAAS,IAAI,MAAM,SAAS,EAAE;AAC7C,QAAM,CAAC,WAAW,YAAY,IAAI,MAAM,SAAS,KAAK;AAEtD,QAAM,UAAU,MAAM;AACpB,QAAI,YAAY;AAChB,mBAAe,OAAO;AACpB,UAAI;AACF,cAAM,OAAO,MAAM,QAA8C,2BAA2B;AAAA,UAC1F,QAAQ;AAAA,UACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,UAC9C,MAAM,KAAK,UAAU,EAAE,UAAU,CAAC,2BAA2B,EAAE,CAAC;AAAA,QAClE,CAAC;AACD,YAAI,CAAC,WAAW;AACd,gBAAM,UAAU,MAAM,QAAQ,KAAK,QAAQ,OAAO,IAAI,KAAK,OAAO,UAAU,CAAC;AAC7E,uBAAa,KAAK,QAAQ,OAAO,QAAQ,QAAQ,SAAS,2BAA2B,CAAC;AAAA,QACxF;AAAA,MACF,QAAQ;AACN,YAAI,CAAC,UAAW,cAAa,KAAK;AAAA,MACpC;AAAA,IACF;AACA,SAAK;AACL,WAAO,MAAM;AACX,kBAAY;AAAA,IACd;AAAA,EACF,GAAG,CAAC,CAAC;AAEL,QAAM,cAAc,MAAM,QAAQ,MAAM;AACtC,UAAM,SAAS,IAAI,gBAAgB;AACnC,WAAO,IAAI,QAAQ,QAAQ;AAC3B,WAAO,IAAI,QAAQ,OAAO,IAAI,CAAC;AAC/B,WAAO,IAAI,YAAY,OAAO,SAAS,CAAC;AACxC,WAAO,IAAI,UAAU,MAAM;AAC3B,QAAI,OAAQ,QAAO,IAAI,UAAU,MAAM;AACvC,WAAO,OAAO,SAAS;AAAA,EACzB,GAAG,CAAC,MAAM,QAAQ,MAAM,CAAC;AAEzB,QAAM,EAAE,MAAM,UAAU,IAAI,SAA6B;AAAA,IACvD,UAAU,CAAC,sBAAsB,aAAa,YAAY;AAAA,IAC1D,SAAS,YAAY;AACnB,YAAM,UAAU,MAAM;AAAA,QACpB,2BAA2B,WAAW;AAAA,QACtC;AAAA,QACA,EAAE,cAAc,EAAE,sCAAsC,2BAA2B,EAAE;AAAA,MACvF;AACA,aAAO;AAAA,QACL,OAAO,MAAM,QAAQ,QAAQ,KAAK,IAAI,QAAQ,QAAQ,CAAC;AAAA,QACvD,OAAO,OAAO,QAAQ,UAAU,WAAW,QAAQ,QAAQ;AAAA,QAC3D,MAAM,OAAO,QAAQ,SAAS,WAAW,QAAQ,OAAO;AAAA,QACxD,UAAU,OAAO,QAAQ,aAAa,WAAW,QAAQ,WAAW;AAAA,QACpE,YAAY,OAAO,QAAQ,eAAe,WAAW,QAAQ,aAAa;AAAA,MAC5E;AAAA,IACF;AAAA,EACF,CAAC;AAED,QAAM,OAAO,MAAM,SAAS,CAAC;AAC7B,QAAM,QAAQ,MAAM,SAAS;AAC7B,QAAM,aAAa,MAAM,cAAc;AAEvC,QAAM,UAAU,MAAM,QAAkC,MAAM;AAAA,IAC5D;AAAA,MACE,aAAa;AAAA,MACb,QAAQ,EAAE,4CAA4C,UAAU;AAAA,MAChE,MAAM,EAAE,UAAU,EAAE;AAAA,MACpB,MAAM,CAAC,EAAE,IAAI,MAAM;AACjB,cAAM,QAAQ,IAAI,SAAS,SAAS;AACpC,eACE,oBAAC,SAAI,WAAU,sEACb,8BAAC,UAAK,OAAO,EAAE,YAAY,cAAc,KAAK,GAAG,YAAY,MAAM,GAChE,kCAAwB,IAAI,SAAS,MAAM,KAAK,GACnD,GACF;AAAA,MAEJ;AAAA,IACF;AAAA,IACA;AAAA,MACE,aAAa;AAAA,MACb,QAAQ,EAAE,wCAAwC,MAAM;AAAA,MACxD,MAAM,EAAE,UAAU,EAAE;AAAA,MACpB,MAAM,CAAC,EAAE,SAAS,MAAM;AACtB,cAAM,QAAQ,SAAiB;AAC/B,eAAO,oBAAC,UAAK,WAAU,iCAAiC,mBAAS,UAAI;AAAA,MACvE;AAAA,IACF;AAAA,IACA;AAAA,MACE,aAAa;AAAA,MACb,QAAQ,EAAE,0CAA0C,QAAQ;AAAA,MAC5D,MAAM,EAAE,UAAU,EAAE;AAAA,MACpB,MAAM,CAAC,EAAE,SAAS,MAAM,SAAiB,KAAK,EAAE,gCAAgC,QAAG;AAAA,IACrF;AAAA,IACA;AAAA,MACE,aAAa;AAAA,MACb,QAAQ,EAAE,4CAA4C,UAAU;AAAA,MAChE,MAAM,EAAE,UAAU,EAAE;AAAA,IACtB;AAAA,IACA;AAAA,MACE,aAAa;AAAA,MACb,QAAQ,EAAE,0CAA0C,QAAQ;AAAA,MAC5D,eAAe;AAAA,MACf,MAAM,EAAE,UAAU,EAAE;AAAA,MACpB,MAAM,CAAC,EAAE,SAAS,MAAM,oBAAC,eAAY,OAAO,QAAQ,SAAS,CAAC,GAAG;AAAA,IACnE;AAAA,EACF,GAAG,CAAC,CAAC,CAAC;AAEN,QAAM,eAAe,MAAM,YAAY,OAAO,aAA0B;AACtE,UAAM,eAAe;AAAA,MACnB;AAAA,MACA;AAAA,MACA,EAAE,MAAM,SAAS,KAAK;AAAA,IACxB;AACA,UAAM,YAAY,MAAM,QAAQ;AAAA,MAC9B,OAAO;AAAA,MACP,SAAS;AAAA,IACX,CAAC;AACD,QAAI,CAAC,UAAW;AAChB,QAAI;AAEF,YAAM;AAAA,QACJ,8BAA8B,mBAAmB,SAAS,EAAE,CAAC;AAAA,QAC7D,EAAE,QAAQ,SAAS;AAAA,QACnB,EAAE,cAAc,EAAE,wCAAwC,2BAA2B,EAAE;AAAA,MACzF;AACA,YAAM,YAAY,kBAAkB,EAAE,UAAU,CAAC,oBAAoB,EAAE,CAAC;AACxE,YAAM,EAAE,oCAAoC,mBAAmB,GAAG,SAAS;AAAA,IAC7E,SAAS,KAAc;AACrB,YAAM,WAAW,EAAE,wCAAwC,2BAA2B;AACtF,YAAM,UAAU,eAAe,QAAQ,IAAI,UAAU;AACrD,YAAM,SAAS,OAAO;AAAA,IACxB;AAAA,EACF,GAAG,CAAC,SAAS,aAAa,CAAC,CAAC;AAE5B,SACE,iCACE;AAAA;AAAA,MAAC;AAAA;AAAA,QACC,OAAO,EAAE,iCAAiC,YAAY;AAAA,QACtD,SAAS,YACP,oBAAC,UAAO,SAAO,MACb,8BAAC,QAAK,MAAK,sCACR,YAAE,0CAA0C,QAAQ,GACvD,GACF,IACE;AAAA,QACJ;AAAA,QACA,MAAM;AAAA,QACN,YACE;AAAA,UAAC;AAAA;AAAA,YACC,YAAY,EAAE,iCAAiC,YAAY;AAAA,YAC3D,YAAY,YAAY,uCAAuC;AAAA,YAC/D,aAAa,YAAY,EAAE,0CAA0C,QAAQ,IAAI;AAAA;AAAA,QACnF;AAAA,QAEF,aAAa;AAAA,QACb,mBAAmB,EAAE,6CAA6C,mBAAmB;AAAA,QACrF,gBAAgB,CAAC,UAAU;AAAE,oBAAU,KAAK;AAAG,kBAAQ,CAAC;AAAA,QAAE;AAAA,QAC1D,SAAS;AAAA,UACP;AAAA,YACE,IAAI;AAAA,YACJ,OAAO,EAAE,0CAA0C,QAAQ;AAAA,YAC3D,MAAM;AAAA,YACN,SAAS;AAAA,cACP,EAAE,OAAO,OAAO,OAAO,EAAE,uCAAuC,KAAK,EAAE;AAAA,cACvE,EAAE,OAAO,UAAU,OAAO,EAAE,0CAA0C,QAAQ,EAAE;AAAA,cAChF,EAAE,OAAO,YAAY,OAAO,EAAE,4CAA4C,UAAU,EAAE;AAAA,YACxF;AAAA,UACF;AAAA,QACF;AAAA,QACA,cAAc,WAAW,QAAQ,CAAC,IAAI,EAAE,OAAO;AAAA,QAC/C,gBAAgB,CAAC,WAAyB;AACxC,gBAAM,aAAc,OAAO,UAAwD;AACnF,oBAAU,UAAU;AACpB,kBAAQ,CAAC;AAAA,QACX;AAAA,QACA,gBAAgB,MAAM;AACpB,oBAAU,KAAK;AACf,kBAAQ,CAAC;AAAA,QACX;AAAA,QACA,UAAU;AAAA,QACV,aAAa,EAAE,SAAS,gBAAgB,MAAM,gBAAgB,QAAQ;AAAA,QACtE,YAAY,CAAC,QACX,YACE;AAAA,UAAC;AAAA;AAAA,YACC,OAAO;AAAA,cACL,EAAE,IAAI,QAAQ,OAAO,EAAE,wCAAwC,MAAM,GAAG,MAAM,+BAA+B,IAAI,EAAE,QAAQ;AAAA,cAC3H,EAAE,IAAI,UAAU,OAAO,EAAE,0CAA0C,QAAQ,GAAG,aAAa,MAAM,UAAU,MAAM,aAAa,GAAG,EAAE;AAAA,YACrI;AAAA;AAAA,QACF,IACE;AAAA,QAEN,YAAY;AAAA,UACV;AAAA,UACA,UAAU;AAAA,UACV;AAAA,UACA;AAAA,UACA,cAAc;AAAA,QAChB;AAAA,QACA;AAAA;AAAA,IACF;AAAA,IACC;AAAA,KACH;AAEJ;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -74,7 +74,6 @@ function ProductsDataTable({
|
|
|
74
74
|
const [page, setPage] = React.useState(1);
|
|
75
75
|
const [total, setTotal] = React.useState(0);
|
|
76
76
|
const [totalPages, setTotalPages] = React.useState(1);
|
|
77
|
-
const [totalIsCapped, setTotalIsCapped] = React.useState(false);
|
|
78
77
|
const [cacheStatus, setCacheStatus] = React.useState(null);
|
|
79
78
|
const [sorting, setSorting] = React.useState([{ id: "title", desc: false }]);
|
|
80
79
|
const [search, setSearch] = React.useState("");
|
|
@@ -446,7 +445,6 @@ function ProductsDataTable({
|
|
|
446
445
|
setRows(normalized);
|
|
447
446
|
setTotal(typeof payload.total === "number" ? payload.total : normalized.length);
|
|
448
447
|
setTotalPages(typeof payload.totalPages === "number" ? payload.totalPages : 1);
|
|
449
|
-
setTotalIsCapped(payload.totalIsCapped === true);
|
|
450
448
|
} catch (error) {
|
|
451
449
|
if (!cancelled) {
|
|
452
450
|
setCacheStatus(null);
|
|
@@ -541,17 +539,13 @@ function ProductsDataTable({
|
|
|
541
539
|
// a selection-aware pageContext per spec §10.1 without taking a
|
|
542
540
|
// dependency on the host page.
|
|
543
541
|
total,
|
|
544
|
-
totalMatching: total
|
|
545
|
-
// `total` is a floor when the server capped the count, so the
|
|
546
|
-
// merchandising widget must not present it as an exact match count.
|
|
547
|
-
totalIsCapped
|
|
542
|
+
totalMatching: total
|
|
548
543
|
},
|
|
549
544
|
pagination: {
|
|
550
545
|
page,
|
|
551
546
|
pageSize: PAGE_SIZE,
|
|
552
547
|
total,
|
|
553
548
|
totalPages,
|
|
554
|
-
totalIsCapped,
|
|
555
549
|
onPageChange: setPage,
|
|
556
550
|
cacheStatus
|
|
557
551
|
},
|