@open-mercato/core 0.5.1-develop.2860.07af3a6a9d → 0.5.1-develop.2874.77704bccbd
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/dist/modules/api_docs/frontend/docs/api/Explorer.js +18 -18
- package/dist/modules/api_docs/frontend/docs/api/Explorer.js.map +2 -2
- package/dist/modules/api_keys/backend/api-keys/create/page.js +1 -1
- package/dist/modules/api_keys/backend/api-keys/create/page.js.map +1 -1
- package/dist/modules/attachments/components/AttachmentLibrary.js +2 -2
- package/dist/modules/attachments/components/AttachmentLibrary.js.map +2 -2
- package/dist/modules/attachments/components/AttachmentPartitionSettings.js +1 -1
- package/dist/modules/attachments/components/AttachmentPartitionSettings.js.map +1 -1
- package/dist/modules/attachments/fields/attachment.js +1 -1
- package/dist/modules/attachments/fields/attachment.js.map +1 -1
- package/dist/modules/audit_logs/components/ActionLogDetailsDialog.js +1 -1
- package/dist/modules/audit_logs/components/ActionLogDetailsDialog.js.map +2 -2
- package/dist/modules/audit_logs/lib/display-helpers.js +1 -1
- package/dist/modules/audit_logs/lib/display-helpers.js.map +1 -1
- package/dist/modules/auth/backend/users/create/page.js +1 -1
- package/dist/modules/auth/backend/users/create/page.js.map +1 -1
- package/dist/modules/business_rules/backend/rules/page.js +6 -6
- package/dist/modules/business_rules/backend/rules/page.js.map +2 -2
- package/dist/modules/business_rules/backend/sets/page.js +2 -2
- package/dist/modules/business_rules/backend/sets/page.js.map +2 -2
- package/dist/modules/business_rules/components/ActionBuilder.js +5 -5
- package/dist/modules/business_rules/components/ActionBuilder.js.map +2 -2
- package/dist/modules/business_rules/components/ActionRow.js +8 -8
- package/dist/modules/business_rules/components/ActionRow.js.map +1 -1
- package/dist/modules/business_rules/components/ConditionBuilder.js +5 -5
- package/dist/modules/business_rules/components/ConditionBuilder.js.map +2 -2
- package/dist/modules/business_rules/components/ConditionGroup.js +2 -2
- package/dist/modules/business_rules/components/ConditionGroup.js.map +1 -1
- package/dist/modules/business_rules/components/ConditionRow.js +3 -3
- package/dist/modules/business_rules/components/ConditionRow.js.map +2 -2
- package/dist/modules/business_rules/components/RuleSetMembers.js +8 -8
- package/dist/modules/business_rules/components/RuleSetMembers.js.map +2 -2
- package/dist/modules/catalog/backend/catalog/products/[id]/page.js +2 -2
- package/dist/modules/catalog/backend/catalog/products/[id]/page.js.map +1 -1
- package/dist/modules/catalog/backend/catalog/products/create/page.js +5 -5
- package/dist/modules/catalog/backend/catalog/products/create/page.js.map +1 -1
- package/dist/modules/catalog/components/products/MetadataEditor.js +1 -1
- package/dist/modules/catalog/components/products/MetadataEditor.js.map +1 -1
- package/dist/modules/catalog/components/products/ProductImageCell.js +1 -1
- package/dist/modules/catalog/components/products/ProductImageCell.js.map +1 -1
- package/dist/modules/catalog/components/products/VariantBuilder.js +1 -1
- package/dist/modules/catalog/components/products/VariantBuilder.js.map +1 -1
- package/dist/modules/catalog/widgets/injection/product-seo/widget.client.js +1 -1
- package/dist/modules/catalog/widgets/injection/product-seo/widget.client.js.map +2 -2
- package/dist/modules/currencies/components/CurrencyFetchingConfig.js +1 -1
- package/dist/modules/currencies/components/CurrencyFetchingConfig.js.map +1 -1
- package/dist/modules/customer_accounts/backend/customer_accounts/roles/page.js +2 -2
- package/dist/modules/customer_accounts/backend/customer_accounts/roles/page.js.map +2 -2
- package/dist/modules/customer_accounts/backend/customer_accounts/users/[id]/page.js +9 -9
- 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 -7
- package/dist/modules/customer_accounts/backend/customer_accounts/users/page.js.map +2 -2
- package/dist/modules/customer_accounts/widgets/injection/account-status/widget.client.js +2 -2
- package/dist/modules/customer_accounts/widgets/injection/account-status/widget.client.js.map +1 -1
- package/dist/modules/customers/backend/config/customers/pipeline-stages/page.js +3 -3
- package/dist/modules/customers/backend/config/customers/pipeline-stages/page.js.map +1 -1
- package/dist/modules/customers/backend/customers/deals/pipeline/page.js +2 -2
- package/dist/modules/customers/backend/customers/deals/pipeline/page.js.map +1 -1
- package/dist/modules/customers/components/AddressTiles.js +1 -1
- package/dist/modules/customers/components/AddressTiles.js.map +1 -1
- package/dist/modules/customers/components/detail/ActivityForm.js +3 -3
- package/dist/modules/customers/components/detail/ActivityForm.js.map +1 -1
- package/dist/modules/customers/components/detail/AnnualRevenueField.js +2 -2
- package/dist/modules/customers/components/detail/AnnualRevenueField.js.map +1 -1
- package/dist/modules/customers/components/detail/CustomFieldValuesList.js +1 -1
- package/dist/modules/customers/components/detail/CustomFieldValuesList.js.map +1 -1
- package/dist/modules/customers/components/detail/DealForm.js +1 -1
- package/dist/modules/customers/components/detail/DealForm.js.map +2 -2
- package/dist/modules/customers/components/detail/DealsSection.js +1 -1
- package/dist/modules/customers/components/detail/DealsSection.js.map +1 -1
- package/dist/modules/customers/components/detail/DetailFieldsSection.js +1 -1
- package/dist/modules/customers/components/detail/DetailFieldsSection.js.map +1 -1
- package/dist/modules/customers/components/detail/InlineEditors.js +5 -5
- package/dist/modules/customers/components/detail/InlineEditors.js.map +2 -2
- package/dist/modules/customers/components/detail/TasksSection.js +1 -1
- package/dist/modules/customers/components/detail/TasksSection.js.map +1 -1
- package/dist/modules/customers/components/detail/TimelineItemHeader.js +1 -1
- package/dist/modules/customers/components/detail/TimelineItemHeader.js.map +1 -1
- package/dist/modules/customers/components/formConfig.js +2 -2
- package/dist/modules/customers/components/formConfig.js.map +1 -1
- package/dist/modules/customers/widgets/dashboard/customer-todos/widget.client.js +1 -1
- package/dist/modules/customers/widgets/dashboard/customer-todos/widget.client.js.map +1 -1
- package/dist/modules/customers/widgets/dashboard/new-customers/widget.client.js +2 -2
- package/dist/modules/customers/widgets/dashboard/new-customers/widget.client.js.map +1 -1
- package/dist/modules/customers/widgets/dashboard/new-deals/widget.client.js +1 -1
- package/dist/modules/customers/widgets/dashboard/new-deals/widget.client.js.map +1 -1
- package/dist/modules/customers/widgets/dashboard/next-interactions/widget.client.js +1 -1
- package/dist/modules/customers/widgets/dashboard/next-interactions/widget.client.js.map +1 -1
- package/dist/modules/dashboards/components/WidgetVisibilityEditor.js +1 -1
- package/dist/modules/dashboards/components/WidgetVisibilityEditor.js.map +1 -1
- package/dist/modules/dashboards/widgets/dashboard/aov-kpi/widget.client.js +1 -1
- package/dist/modules/dashboards/widgets/dashboard/aov-kpi/widget.client.js.map +1 -1
- package/dist/modules/dashboards/widgets/dashboard/new-customers-kpi/widget.client.js +1 -1
- package/dist/modules/dashboards/widgets/dashboard/new-customers-kpi/widget.client.js.map +1 -1
- package/dist/modules/dashboards/widgets/dashboard/orders-by-status/widget.client.js +1 -1
- package/dist/modules/dashboards/widgets/dashboard/orders-by-status/widget.client.js.map +1 -1
- package/dist/modules/dashboards/widgets/dashboard/orders-kpi/widget.client.js +1 -1
- package/dist/modules/dashboards/widgets/dashboard/orders-kpi/widget.client.js.map +1 -1
- package/dist/modules/dashboards/widgets/dashboard/revenue-kpi/widget.client.js +1 -1
- package/dist/modules/dashboards/widgets/dashboard/revenue-kpi/widget.client.js.map +1 -1
- package/dist/modules/dashboards/widgets/dashboard/revenue-trend/widget.client.js +2 -2
- package/dist/modules/dashboards/widgets/dashboard/revenue-trend/widget.client.js.map +1 -1
- package/dist/modules/dashboards/widgets/dashboard/sales-by-region/widget.client.js +1 -1
- package/dist/modules/dashboards/widgets/dashboard/sales-by-region/widget.client.js.map +1 -1
- package/dist/modules/dashboards/widgets/dashboard/top-customers/widget.client.js +1 -1
- package/dist/modules/dashboards/widgets/dashboard/top-customers/widget.client.js.map +1 -1
- package/dist/modules/dashboards/widgets/dashboard/top-products/widget.client.js +2 -2
- package/dist/modules/dashboards/widgets/dashboard/top-products/widget.client.js.map +1 -1
- package/dist/modules/data_sync/backend/data-sync/page.js +4 -4
- package/dist/modules/data_sync/backend/data-sync/page.js.map +2 -2
- package/dist/modules/data_sync/backend/data-sync/runs/[id]/page.js +2 -2
- package/dist/modules/data_sync/backend/data-sync/runs/[id]/page.js.map +1 -1
- package/dist/modules/dictionaries/components/AppearanceSelector.js +3 -3
- package/dist/modules/dictionaries/components/AppearanceSelector.js.map +2 -2
- package/dist/modules/dictionaries/components/DictionariesManager.js +4 -4
- package/dist/modules/dictionaries/components/DictionariesManager.js.map +2 -2
- package/dist/modules/dictionaries/components/DictionaryEntriesEditor.js +2 -2
- package/dist/modules/dictionaries/components/DictionaryEntriesEditor.js.map +2 -2
- package/dist/modules/dictionaries/components/DictionaryEntrySelect.js +3 -3
- package/dist/modules/dictionaries/components/DictionaryEntrySelect.js.map +1 -1
- package/dist/modules/dictionaries/fields/dictionary.js +4 -4
- package/dist/modules/dictionaries/fields/dictionary.js.map +1 -1
- package/dist/modules/entities/components/EncryptionManager.js +3 -3
- package/dist/modules/entities/components/EncryptionManager.js.map +2 -2
- package/dist/modules/entities/components/UserEntitiesTable.js +1 -1
- package/dist/modules/entities/components/UserEntitiesTable.js.map +2 -2
- package/dist/modules/feature_toggles/components/formConfig.js +1 -1
- package/dist/modules/feature_toggles/components/formConfig.js.map +1 -1
- package/dist/modules/feature_toggles/components/overrideFormConfig.js +2 -2
- package/dist/modules/feature_toggles/components/overrideFormConfig.js.map +1 -1
- package/dist/modules/inbox_ops/backend/inbox-ops/proposals/[id]/page.js +12 -12
- package/dist/modules/inbox_ops/backend/inbox-ops/proposals/[id]/page.js.map +2 -2
- package/dist/modules/inbox_ops/components/messages/InboxEmailPreview.js +1 -1
- package/dist/modules/inbox_ops/components/messages/InboxEmailPreview.js.map +1 -1
- package/dist/modules/inbox_ops/components/proposals/ActionCard.js +12 -12
- package/dist/modules/inbox_ops/components/proposals/ActionCard.js.map +2 -2
- package/dist/modules/inbox_ops/widgets/notifications/ProposalCreatedRenderer.js +3 -3
- package/dist/modules/inbox_ops/widgets/notifications/ProposalCreatedRenderer.js.map +2 -2
- package/dist/modules/integrations/backend/integrations/[id]/page.js +6 -6
- package/dist/modules/integrations/backend/integrations/[id]/page.js.map +2 -2
- package/dist/modules/messages/components/MessagesInboxPageClient.js +1 -1
- package/dist/modules/messages/components/MessagesInboxPageClient.js.map +1 -1
- package/dist/modules/messages/components/defaults/DefaultMessageListItem.js +1 -1
- package/dist/modules/messages/components/defaults/DefaultMessageListItem.js.map +1 -1
- package/dist/modules/messages/components/defaults/MessageRecordObjectDetail.js +1 -1
- package/dist/modules/messages/components/defaults/MessageRecordObjectDetail.js.map +1 -1
- package/dist/modules/messages/components/defaults/MessageRecordObjectPreview.js +1 -1
- package/dist/modules/messages/components/defaults/MessageRecordObjectPreview.js.map +1 -1
- package/dist/modules/messages/components/message-detail/panels/MessageListComponent.js +1 -1
- package/dist/modules/messages/components/message-detail/panels/MessageListComponent.js.map +1 -1
- package/dist/modules/messages/components/message-detail/panels/attachments-panel.js +1 -1
- package/dist/modules/messages/components/message-detail/panels/attachments-panel.js.map +1 -1
- package/dist/modules/payment_gateways/backend/payment-gateways/page.js +11 -11
- package/dist/modules/payment_gateways/backend/payment-gateways/page.js.map +2 -2
- package/dist/modules/planner/components/AvailabilityRulesEditor.js +2 -2
- package/dist/modules/planner/components/AvailabilityRulesEditor.js.map +2 -2
- package/dist/modules/portal/frontend/[orgSlug]/portal/dashboard/page.js +2 -2
- package/dist/modules/portal/frontend/[orgSlug]/portal/dashboard/page.js.map +2 -2
- package/dist/modules/portal/frontend/[orgSlug]/portal/login/page.js +3 -3
- package/dist/modules/portal/frontend/[orgSlug]/portal/login/page.js.map +2 -2
- package/dist/modules/portal/frontend/[orgSlug]/portal/page.js +3 -3
- package/dist/modules/portal/frontend/[orgSlug]/portal/page.js.map +2 -2
- package/dist/modules/portal/frontend/[orgSlug]/portal/profile/page.js +4 -4
- package/dist/modules/portal/frontend/[orgSlug]/portal/profile/page.js.map +2 -2
- package/dist/modules/portal/frontend/[orgSlug]/portal/signup/page.js +4 -4
- package/dist/modules/portal/frontend/[orgSlug]/portal/signup/page.js.map +2 -2
- package/dist/modules/resources/backend/resources/resources/[id]/page.js +1 -1
- package/dist/modules/resources/backend/resources/resources/[id]/page.js.map +1 -1
- package/dist/modules/sales/backend/sales/documents/[id]/page.js +4 -4
- package/dist/modules/sales/backend/sales/documents/[id]/page.js.map +1 -1
- package/dist/modules/sales/components/DocumentNumberSettings.js +1 -1
- package/dist/modules/sales/components/DocumentNumberSettings.js.map +1 -1
- package/dist/modules/sales/components/channels/ChannelOfferForm.js +1 -1
- package/dist/modules/sales/components/channels/ChannelOfferForm.js.map +1 -1
- package/dist/modules/sales/components/documents/AdjustmentDialog.js +1 -1
- package/dist/modules/sales/components/documents/AdjustmentDialog.js.map +1 -1
- package/dist/modules/sales/components/documents/DocumentTotals.js +3 -3
- package/dist/modules/sales/components/documents/DocumentTotals.js.map +1 -1
- package/dist/modules/sales/components/documents/PaymentDialog.js +1 -1
- package/dist/modules/sales/components/documents/PaymentDialog.js.map +1 -1
- package/dist/modules/sales/components/documents/SalesDocumentForm.js +2 -2
- package/dist/modules/sales/components/documents/SalesDocumentForm.js.map +2 -2
- package/dist/modules/sales/widgets/dashboard/new-orders/widget.client.js +4 -4
- package/dist/modules/sales/widgets/dashboard/new-orders/widget.client.js.map +1 -1
- package/dist/modules/sales/widgets/dashboard/new-quotes/widget.client.js +4 -4
- package/dist/modules/sales/widgets/dashboard/new-quotes/widget.client.js.map +1 -1
- package/dist/modules/sales/widgets/injection/document-history/widget.client.js +2 -2
- package/dist/modules/sales/widgets/injection/document-history/widget.client.js.map +1 -1
- package/dist/modules/sales/widgets/messages/SalesDocumentMessageDetail.js +1 -1
- package/dist/modules/sales/widgets/messages/SalesDocumentMessageDetail.js.map +1 -1
- package/dist/modules/sales/widgets/messages/SalesDocumentMessagePreview.js +1 -1
- package/dist/modules/sales/widgets/messages/SalesDocumentMessagePreview.js.map +1 -1
- package/dist/modules/shipping_carriers/lib/shipment-wizard/components/PackageEditor.js +1 -1
- package/dist/modules/shipping_carriers/lib/shipment-wizard/components/PackageEditor.js.map +1 -1
- package/dist/modules/staff/backend/staff/team-members/[id]/page.js +1 -1
- package/dist/modules/staff/backend/staff/team-members/[id]/page.js.map +1 -1
- package/dist/modules/translations/components/TranslationDrawerAction.js +2 -2
- package/dist/modules/translations/components/TranslationDrawerAction.js.map +1 -1
- package/dist/modules/translations/components/TranslationManager.js +3 -3
- package/dist/modules/translations/components/TranslationManager.js.map +1 -1
- package/dist/modules/translations/widgets/injection/translation-manager/widget.client.js +2 -2
- package/dist/modules/translations/widgets/injection/translation-manager/widget.client.js.map +1 -1
- package/dist/modules/workflows/backend/definitions/[id]/page.js +5 -5
- package/dist/modules/workflows/backend/definitions/[id]/page.js.map +2 -2
- package/dist/modules/workflows/backend/definitions/visual-editor/page.js +2 -2
- package/dist/modules/workflows/backend/definitions/visual-editor/page.js.map +2 -2
- package/dist/modules/workflows/backend/events/[id]/page.js +4 -4
- package/dist/modules/workflows/backend/events/[id]/page.js.map +1 -1
- package/dist/modules/workflows/backend/instances/[id]/page.js +2 -2
- package/dist/modules/workflows/backend/instances/[id]/page.js.map +1 -1
- package/dist/modules/workflows/backend/tasks/[id]/page.js +20 -20
- package/dist/modules/workflows/backend/tasks/[id]/page.js.map +2 -2
- package/dist/modules/workflows/components/DefinitionTriggersEditor.js +1 -1
- package/dist/modules/workflows/components/DefinitionTriggersEditor.js.map +1 -1
- package/dist/modules/workflows/components/EdgeEditDialog.js +12 -12
- package/dist/modules/workflows/components/EdgeEditDialog.js.map +1 -1
- package/dist/modules/workflows/components/NodeEditDialog.js +26 -26
- package/dist/modules/workflows/components/NodeEditDialog.js.map +1 -1
- package/dist/modules/workflows/components/fields/FormFieldArrayEditor.js +1 -1
- package/dist/modules/workflows/components/fields/FormFieldArrayEditor.js.map +1 -1
- package/dist/modules/workflows/components/mobile/MobileDefinitionDetail.js +2 -2
- package/dist/modules/workflows/components/mobile/MobileDefinitionDetail.js.map +2 -2
- package/dist/modules/workflows/components/mobile/MobileInstanceOverview.js +7 -7
- package/dist/modules/workflows/components/mobile/MobileInstanceOverview.js.map +2 -2
- package/dist/modules/workflows/components/mobile/MobileTaskForm.js +11 -11
- package/dist/modules/workflows/components/mobile/MobileTaskForm.js.map +2 -2
- package/dist/modules/workflows/components/mobile/MobileVisualEditor.js +1 -1
- package/dist/modules/workflows/components/mobile/MobileVisualEditor.js.map +1 -1
- package/dist/modules/workflows/components/mobile/MobileWorkflowTimeline.js +23 -23
- package/dist/modules/workflows/components/mobile/MobileWorkflowTimeline.js.map +2 -2
- package/dist/modules/workflows/frontend/checkout-demo/page.js +6 -6
- package/dist/modules/workflows/frontend/checkout-demo/page.js.map +1 -1
- package/package.json +3 -3
- package/src/modules/api_docs/frontend/docs/api/Explorer.tsx +18 -18
- package/src/modules/api_keys/backend/api-keys/create/page.tsx +1 -1
- package/src/modules/attachments/components/AttachmentLibrary.tsx +3 -3
- package/src/modules/attachments/components/AttachmentPartitionSettings.tsx +1 -1
- package/src/modules/attachments/fields/attachment.tsx +1 -1
- package/src/modules/audit_logs/components/ActionLogDetailsDialog.tsx +1 -1
- package/src/modules/audit_logs/lib/display-helpers.tsx +1 -1
- package/src/modules/auth/backend/users/create/page.tsx +1 -1
- package/src/modules/business_rules/backend/rules/page.tsx +7 -7
- package/src/modules/business_rules/backend/sets/page.tsx +3 -3
- package/src/modules/business_rules/components/ActionBuilder.tsx +6 -6
- package/src/modules/business_rules/components/ActionRow.tsx +8 -8
- package/src/modules/business_rules/components/ConditionBuilder.tsx +6 -6
- package/src/modules/business_rules/components/ConditionGroup.tsx +2 -2
- package/src/modules/business_rules/components/ConditionRow.tsx +3 -3
- package/src/modules/business_rules/components/RuleSetMembers.tsx +9 -9
- package/src/modules/catalog/backend/catalog/products/[id]/page.tsx +2 -2
- package/src/modules/catalog/backend/catalog/products/create/page.tsx +5 -5
- package/src/modules/catalog/components/products/MetadataEditor.tsx +1 -1
- package/src/modules/catalog/components/products/ProductImageCell.tsx +1 -1
- package/src/modules/catalog/components/products/VariantBuilder.tsx +1 -1
- package/src/modules/catalog/widgets/injection/product-seo/widget.client.tsx +1 -1
- package/src/modules/currencies/components/CurrencyFetchingConfig.tsx +1 -1
- package/src/modules/customer_accounts/backend/customer_accounts/roles/page.tsx +2 -2
- package/src/modules/customer_accounts/backend/customer_accounts/users/[id]/page.tsx +10 -10
- package/src/modules/customer_accounts/backend/customer_accounts/users/page.tsx +9 -9
- package/src/modules/customer_accounts/widgets/injection/account-status/widget.client.tsx +2 -2
- package/src/modules/customers/backend/config/customers/pipeline-stages/page.tsx +3 -3
- package/src/modules/customers/backend/customers/deals/pipeline/page.tsx +2 -2
- package/src/modules/customers/components/AddressTiles.tsx +1 -1
- package/src/modules/customers/components/detail/ActivityForm.tsx +3 -3
- package/src/modules/customers/components/detail/AnnualRevenueField.tsx +2 -2
- package/src/modules/customers/components/detail/CustomFieldValuesList.tsx +1 -1
- package/src/modules/customers/components/detail/DealForm.tsx +1 -1
- package/src/modules/customers/components/detail/DealsSection.tsx +1 -1
- package/src/modules/customers/components/detail/DetailFieldsSection.tsx +1 -1
- package/src/modules/customers/components/detail/InlineEditors.tsx +5 -5
- package/src/modules/customers/components/detail/TasksSection.tsx +1 -1
- package/src/modules/customers/components/detail/TimelineItemHeader.tsx +1 -1
- package/src/modules/customers/components/formConfig.tsx +2 -2
- package/src/modules/customers/widgets/dashboard/customer-todos/widget.client.tsx +1 -1
- package/src/modules/customers/widgets/dashboard/new-customers/widget.client.tsx +2 -2
- package/src/modules/customers/widgets/dashboard/new-deals/widget.client.tsx +1 -1
- package/src/modules/customers/widgets/dashboard/next-interactions/widget.client.tsx +1 -1
- package/src/modules/dashboards/components/WidgetVisibilityEditor.tsx +1 -1
- package/src/modules/dashboards/widgets/dashboard/aov-kpi/widget.client.tsx +1 -1
- package/src/modules/dashboards/widgets/dashboard/new-customers-kpi/widget.client.tsx +1 -1
- package/src/modules/dashboards/widgets/dashboard/orders-by-status/widget.client.tsx +1 -1
- package/src/modules/dashboards/widgets/dashboard/orders-kpi/widget.client.tsx +1 -1
- package/src/modules/dashboards/widgets/dashboard/revenue-kpi/widget.client.tsx +1 -1
- package/src/modules/dashboards/widgets/dashboard/revenue-trend/widget.client.tsx +2 -2
- package/src/modules/dashboards/widgets/dashboard/sales-by-region/widget.client.tsx +1 -1
- package/src/modules/dashboards/widgets/dashboard/top-customers/widget.client.tsx +1 -1
- package/src/modules/dashboards/widgets/dashboard/top-products/widget.client.tsx +2 -2
- package/src/modules/data_sync/backend/data-sync/page.tsx +4 -4
- package/src/modules/data_sync/backend/data-sync/runs/[id]/page.tsx +2 -2
- package/src/modules/dictionaries/components/AppearanceSelector.tsx +3 -3
- package/src/modules/dictionaries/components/DictionariesManager.tsx +4 -4
- package/src/modules/dictionaries/components/DictionaryEntriesEditor.tsx +2 -2
- package/src/modules/dictionaries/components/DictionaryEntrySelect.tsx +3 -3
- package/src/modules/dictionaries/fields/dictionary.tsx +4 -4
- package/src/modules/entities/components/EncryptionManager.tsx +3 -3
- package/src/modules/entities/components/UserEntitiesTable.tsx +1 -1
- package/src/modules/feature_toggles/components/formConfig.tsx +1 -1
- package/src/modules/feature_toggles/components/overrideFormConfig.tsx +2 -2
- package/src/modules/inbox_ops/backend/inbox-ops/proposals/[id]/page.tsx +12 -12
- package/src/modules/inbox_ops/components/messages/InboxEmailPreview.tsx +1 -1
- package/src/modules/inbox_ops/components/proposals/ActionCard.tsx +12 -12
- package/src/modules/inbox_ops/widgets/notifications/ProposalCreatedRenderer.tsx +4 -4
- package/src/modules/integrations/backend/integrations/[id]/page.tsx +6 -6
- package/src/modules/messages/components/MessagesInboxPageClient.tsx +1 -1
- package/src/modules/messages/components/defaults/DefaultMessageListItem.tsx +1 -1
- package/src/modules/messages/components/defaults/MessageRecordObjectDetail.tsx +1 -1
- package/src/modules/messages/components/defaults/MessageRecordObjectPreview.tsx +1 -1
- package/src/modules/messages/components/message-detail/panels/MessageListComponent.tsx +1 -1
- package/src/modules/messages/components/message-detail/panels/attachments-panel.tsx +1 -1
- package/src/modules/payment_gateways/backend/payment-gateways/page.tsx +11 -11
- package/src/modules/planner/components/AvailabilityRulesEditor.tsx +2 -2
- package/src/modules/portal/frontend/[orgSlug]/portal/dashboard/page.tsx +2 -2
- package/src/modules/portal/frontend/[orgSlug]/portal/login/page.tsx +3 -3
- package/src/modules/portal/frontend/[orgSlug]/portal/page.tsx +3 -3
- package/src/modules/portal/frontend/[orgSlug]/portal/profile/page.tsx +4 -4
- package/src/modules/portal/frontend/[orgSlug]/portal/signup/page.tsx +4 -4
- package/src/modules/resources/backend/resources/resources/[id]/page.tsx +1 -1
- package/src/modules/sales/backend/sales/documents/[id]/page.tsx +4 -4
- package/src/modules/sales/components/DocumentNumberSettings.tsx +1 -1
- package/src/modules/sales/components/channels/ChannelOfferForm.tsx +1 -1
- package/src/modules/sales/components/documents/AdjustmentDialog.tsx +1 -1
- package/src/modules/sales/components/documents/DocumentTotals.tsx +3 -3
- package/src/modules/sales/components/documents/PaymentDialog.tsx +1 -1
- package/src/modules/sales/components/documents/SalesDocumentForm.tsx +2 -2
- package/src/modules/sales/widgets/dashboard/new-orders/widget.client.tsx +4 -4
- package/src/modules/sales/widgets/dashboard/new-quotes/widget.client.tsx +4 -4
- package/src/modules/sales/widgets/injection/document-history/widget.client.tsx +2 -2
- package/src/modules/sales/widgets/messages/SalesDocumentMessageDetail.tsx +1 -1
- package/src/modules/sales/widgets/messages/SalesDocumentMessagePreview.tsx +1 -1
- package/src/modules/shipping_carriers/lib/shipment-wizard/components/PackageEditor.tsx +1 -1
- package/src/modules/staff/backend/staff/team-members/[id]/page.tsx +1 -1
- package/src/modules/translations/components/TranslationDrawerAction.tsx +2 -2
- package/src/modules/translations/components/TranslationManager.tsx +3 -3
- package/src/modules/translations/widgets/injection/translation-manager/widget.client.tsx +2 -2
- package/src/modules/workflows/backend/definitions/[id]/page.tsx +5 -5
- package/src/modules/workflows/backend/definitions/visual-editor/page.tsx +2 -2
- package/src/modules/workflows/backend/events/[id]/page.tsx +4 -4
- package/src/modules/workflows/backend/instances/[id]/page.tsx +2 -2
- package/src/modules/workflows/backend/tasks/[id]/page.tsx +23 -23
- package/src/modules/workflows/components/DefinitionTriggersEditor.tsx +1 -1
- package/src/modules/workflows/components/EdgeEditDialog.tsx +12 -12
- package/src/modules/workflows/components/NodeEditDialog.tsx +26 -26
- package/src/modules/workflows/components/fields/FormFieldArrayEditor.tsx +1 -1
- package/src/modules/workflows/components/mobile/MobileDefinitionDetail.tsx +2 -2
- package/src/modules/workflows/components/mobile/MobileInstanceOverview.tsx +7 -7
- package/src/modules/workflows/components/mobile/MobileTaskForm.tsx +14 -14
- package/src/modules/workflows/components/mobile/MobileVisualEditor.tsx +1 -1
- package/src/modules/workflows/components/mobile/MobileWorkflowTimeline.tsx +23 -23
- package/src/modules/workflows/frontend/checkout-demo/page.tsx +6 -6
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../src/modules/catalog/components/products/MetadataEditor.tsx"],
|
|
4
|
-
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { ChevronDown, ChevronRight, Plus, Trash2 } from 'lucide-react'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { Input } from '@open-mercato/ui/primitives/input'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { createLocalId } from './productForm'\n\ntype MetadataEntry = {\n id: string\n key: string\n value: string\n}\n\ntype MetadataEditorProps = {\n value?: Record<string, unknown> | null\n onChange: (next: Record<string, unknown>) => void\n defaultCollapsed?: boolean\n title?: string\n description?: string\n embedded?: boolean\n}\n\nconst toEntries = (value?: Record<string, unknown> | null): MetadataEntry[] => {\n if (!value || typeof value !== 'object') return []\n return Object.entries(value).map(([key, entry]) => ({\n id: `${key}-${createLocalId()}`,\n key,\n value: serializeMetadataValue(entry),\n }))\n}\n\nconst serializeMetadataValue = (value: unknown): string => {\n if (value == null) return ''\n if (typeof value === 'string') return value\n if (typeof value === 'number' || typeof value === 'boolean') return String(value)\n try {\n return JSON.stringify(value)\n } catch {\n return String(value)\n }\n}\n\nconst parseMetadataValue = (raw: string): unknown => {\n const trimmed = raw.trim()\n if (!trimmed.length) return ''\n if (trimmed === 'true') return true\n if (trimmed === 'false') return false\n const numeric = Number(trimmed)\n if (!Number.isNaN(numeric) && trimmed === `${numeric}`) return numeric\n if ((trimmed.startsWith('{') && trimmed.endsWith('}')) || (trimmed.startsWith('[') && trimmed.endsWith(']'))) {\n try {\n return JSON.parse(trimmed)\n } catch {\n return trimmed\n }\n }\n return trimmed\n}\n\nexport function MetadataEditor({\n value,\n onChange,\n defaultCollapsed = true,\n title,\n description,\n embedded = false,\n}: MetadataEditorProps) {\n const t = useT()\n const [collapsed, setCollapsed] = React.useState(defaultCollapsed)\n const [entries, setEntries] = React.useState<MetadataEntry[]>(() => toEntries(value))\n const skipSyncRef = React.useRef(false)\n\n React.useEffect(() => {\n if (skipSyncRef.current) {\n skipSyncRef.current = false\n return\n }\n setEntries(toEntries(value))\n }, [value])\n\n const emitChange = React.useCallback(\n (nextEntries: MetadataEntry[]) => {\n skipSyncRef.current = true\n setEntries(nextEntries)\n const next: Record<string, unknown> = {}\n nextEntries.forEach(({ key, value }) => {\n const normalizedKey = key.trim()\n if (!normalizedKey.length) return\n next[normalizedKey] = parseMetadataValue(value)\n })\n onChange(next)\n },\n [onChange],\n )\n\n const updateEntry = React.useCallback(\n (id: string, field: 'key' | 'value', nextValue: string) => {\n const next = entries.map((entry) => (entry.id === id ? { ...entry, [field]: nextValue } : entry))\n emitChange(next)\n },\n [emitChange, entries],\n )\n\n const addEntry = React.useCallback(() => {\n const next: MetadataEntry[] = [...entries, { id: createLocalId(), key: '', value: '' }]\n emitChange(next)\n setCollapsed(false)\n }, [emitChange, entries])\n\n const removeEntry = React.useCallback(\n (id: string) => {\n emitChange(entries.filter((entry) => entry.id !== id))\n },\n [emitChange, entries],\n )\n\n const resolvedTitle = title ?? t('catalog.products.edit.metadata.title', 'Metadata')\n const resolvedDescription =\n description ?? t('catalog.products.edit.metadata.hint', 'Attach structured key/value pairs for integrations.')\n const hasTitle = typeof resolvedTitle === 'string' && resolvedTitle.trim().length > 0\n const hasDescription = typeof resolvedDescription === 'string' && resolvedDescription.trim().length > 0\n\n const content = (\n <>\n <div className=\"flex items-center justify-between gap-3\">\n <div className=\"flex-1\">\n {hasTitle ? <p className=\"text-sm font-medium\">{resolvedTitle}</p> : null}\n {hasDescription ? <p className=\"text-xs text-muted-foreground\">{resolvedDescription}</p> : null}\n </div>\n <div className=\"flex items-center gap-2\">\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n onClick={() => setCollapsed((prev) => !prev)}\n className=\"gap-2 text-xs\"\n >\n {collapsed ? <ChevronRight className=\"h-4 w-4\" /> : <ChevronDown className=\"h-4 w-4\" />}\n {collapsed\n ? t('catalog.products.edit.metadata.expand', 'Show metadata')\n : t('catalog.products.edit.metadata.collapse', 'Hide metadata')}\n </Button>\n <Button type=\"button\" size=\"sm\" variant=\"outline\" onClick={addEntry}>\n <Plus className=\"mr-1 h-4 w-4\" />\n {t('catalog.products.edit.metadata.add', 'Add entry')}\n </Button>\n </div>\n </div>\n {!collapsed ? (\n <div className=\"mt-3 space-y-3\">\n {entries.length === 0 ? (\n <p className=\"text-xs text-muted-foreground\">\n {t('catalog.products.edit.metadata.empty', 'No metadata. Add your first entry.')}\n </p>\n ) : null}\n {entries.map((entry) => (\n <div key={entry.id} className=\"flex flex-col gap-2 rounded-md bg-muted/
|
|
4
|
+
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { ChevronDown, ChevronRight, Plus, Trash2 } from 'lucide-react'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { Input } from '@open-mercato/ui/primitives/input'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { createLocalId } from './productForm'\n\ntype MetadataEntry = {\n id: string\n key: string\n value: string\n}\n\ntype MetadataEditorProps = {\n value?: Record<string, unknown> | null\n onChange: (next: Record<string, unknown>) => void\n defaultCollapsed?: boolean\n title?: string\n description?: string\n embedded?: boolean\n}\n\nconst toEntries = (value?: Record<string, unknown> | null): MetadataEntry[] => {\n if (!value || typeof value !== 'object') return []\n return Object.entries(value).map(([key, entry]) => ({\n id: `${key}-${createLocalId()}`,\n key,\n value: serializeMetadataValue(entry),\n }))\n}\n\nconst serializeMetadataValue = (value: unknown): string => {\n if (value == null) return ''\n if (typeof value === 'string') return value\n if (typeof value === 'number' || typeof value === 'boolean') return String(value)\n try {\n return JSON.stringify(value)\n } catch {\n return String(value)\n }\n}\n\nconst parseMetadataValue = (raw: string): unknown => {\n const trimmed = raw.trim()\n if (!trimmed.length) return ''\n if (trimmed === 'true') return true\n if (trimmed === 'false') return false\n const numeric = Number(trimmed)\n if (!Number.isNaN(numeric) && trimmed === `${numeric}`) return numeric\n if ((trimmed.startsWith('{') && trimmed.endsWith('}')) || (trimmed.startsWith('[') && trimmed.endsWith(']'))) {\n try {\n return JSON.parse(trimmed)\n } catch {\n return trimmed\n }\n }\n return trimmed\n}\n\nexport function MetadataEditor({\n value,\n onChange,\n defaultCollapsed = true,\n title,\n description,\n embedded = false,\n}: MetadataEditorProps) {\n const t = useT()\n const [collapsed, setCollapsed] = React.useState(defaultCollapsed)\n const [entries, setEntries] = React.useState<MetadataEntry[]>(() => toEntries(value))\n const skipSyncRef = React.useRef(false)\n\n React.useEffect(() => {\n if (skipSyncRef.current) {\n skipSyncRef.current = false\n return\n }\n setEntries(toEntries(value))\n }, [value])\n\n const emitChange = React.useCallback(\n (nextEntries: MetadataEntry[]) => {\n skipSyncRef.current = true\n setEntries(nextEntries)\n const next: Record<string, unknown> = {}\n nextEntries.forEach(({ key, value }) => {\n const normalizedKey = key.trim()\n if (!normalizedKey.length) return\n next[normalizedKey] = parseMetadataValue(value)\n })\n onChange(next)\n },\n [onChange],\n )\n\n const updateEntry = React.useCallback(\n (id: string, field: 'key' | 'value', nextValue: string) => {\n const next = entries.map((entry) => (entry.id === id ? { ...entry, [field]: nextValue } : entry))\n emitChange(next)\n },\n [emitChange, entries],\n )\n\n const addEntry = React.useCallback(() => {\n const next: MetadataEntry[] = [...entries, { id: createLocalId(), key: '', value: '' }]\n emitChange(next)\n setCollapsed(false)\n }, [emitChange, entries])\n\n const removeEntry = React.useCallback(\n (id: string) => {\n emitChange(entries.filter((entry) => entry.id !== id))\n },\n [emitChange, entries],\n )\n\n const resolvedTitle = title ?? t('catalog.products.edit.metadata.title', 'Metadata')\n const resolvedDescription =\n description ?? t('catalog.products.edit.metadata.hint', 'Attach structured key/value pairs for integrations.')\n const hasTitle = typeof resolvedTitle === 'string' && resolvedTitle.trim().length > 0\n const hasDescription = typeof resolvedDescription === 'string' && resolvedDescription.trim().length > 0\n\n const content = (\n <>\n <div className=\"flex items-center justify-between gap-3\">\n <div className=\"flex-1\">\n {hasTitle ? <p className=\"text-sm font-medium\">{resolvedTitle}</p> : null}\n {hasDescription ? <p className=\"text-xs text-muted-foreground\">{resolvedDescription}</p> : null}\n </div>\n <div className=\"flex items-center gap-2\">\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n onClick={() => setCollapsed((prev) => !prev)}\n className=\"gap-2 text-xs\"\n >\n {collapsed ? <ChevronRight className=\"h-4 w-4\" /> : <ChevronDown className=\"h-4 w-4\" />}\n {collapsed\n ? t('catalog.products.edit.metadata.expand', 'Show metadata')\n : t('catalog.products.edit.metadata.collapse', 'Hide metadata')}\n </Button>\n <Button type=\"button\" size=\"sm\" variant=\"outline\" onClick={addEntry}>\n <Plus className=\"mr-1 h-4 w-4\" />\n {t('catalog.products.edit.metadata.add', 'Add entry')}\n </Button>\n </div>\n </div>\n {!collapsed ? (\n <div className=\"mt-3 space-y-3\">\n {entries.length === 0 ? (\n <p className=\"text-xs text-muted-foreground\">\n {t('catalog.products.edit.metadata.empty', 'No metadata. Add your first entry.')}\n </p>\n ) : null}\n {entries.map((entry) => (\n <div key={entry.id} className=\"flex flex-col gap-2 rounded-md bg-muted/50 p-3 sm:flex-row sm:items-center\">\n <div className=\"flex flex-1 flex-col gap-2 sm:flex-row\">\n <Input\n value={entry.key}\n placeholder={t('catalog.products.edit.metadata.keyPlaceholder', 'Key')}\n onChange={(event) => updateEntry(entry.id, 'key', event.target.value)}\n className=\"sm:flex-1\"\n />\n <Input\n value={entry.value}\n placeholder={t('catalog.products.edit.metadata.valuePlaceholder', 'Value')}\n onChange={(event) => updateEntry(entry.id, 'value', event.target.value)}\n className=\"sm:flex-1\"\n />\n </div>\n <Button type=\"button\" variant=\"ghost\" size=\"icon\" onClick={() => removeEntry(entry.id)}>\n <Trash2 className=\"h-4 w-4 text-muted-foreground hover:text-destructive\" />\n <span className=\"sr-only\">{t('catalog.products.edit.metadata.remove', 'Remove entry')}</span>\n </Button>\n </div>\n ))}\n </div>\n ) : null}\n </>\n )\n\n if (embedded) {\n return <div className=\"space-y-3\">{content}</div>\n }\n\n return <div className=\"rounded-lg border p-4\">{content}</div>\n}\n"],
|
|
5
5
|
"mappings": "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",
|
|
6
6
|
"names": ["value"]
|
|
7
7
|
}
|
|
@@ -15,7 +15,7 @@ function ProductImageCell({ mediaId, mediaUrl, title, cropType = "cover" }) {
|
|
|
15
15
|
if (!previewUrl) {
|
|
16
16
|
return /* @__PURE__ */ jsx("div", { className: "flex h-16 w-16 items-center justify-center rounded-md border border-dashed text-muted-foreground", children: /* @__PURE__ */ jsx(ImageIcon, { className: "h-4 w-4", "aria-hidden": "true" }) });
|
|
17
17
|
}
|
|
18
|
-
return /* @__PURE__ */ jsx("div", { className: "flex h-16 w-16 items-center justify-center overflow-hidden rounded-md border bg-muted/
|
|
18
|
+
return /* @__PURE__ */ jsx("div", { className: "flex h-16 w-16 items-center justify-center overflow-hidden rounded-md border bg-muted/30", children: /* @__PURE__ */ jsx(
|
|
19
19
|
"img",
|
|
20
20
|
{
|
|
21
21
|
src: previewUrl,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../src/modules/catalog/components/products/ProductImageCell.tsx"],
|
|
4
|
-
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { Image as ImageIcon } from 'lucide-react'\nimport { buildAttachmentImageUrl, type ImageCropType } from '@open-mercato/core/modules/attachments/lib/imageUrls'\n\ntype ProductImageCellProps = {\n mediaId?: string | null\n mediaUrl?: string | null\n title?: string | null\n cropType?: ImageCropType\n}\n\nconst IMAGE_SIZE = 96\n\nexport function ProductImageCell({ mediaId, mediaUrl, title, cropType = 'cover' }: ProductImageCellProps) {\n const previewUrl = React.useMemo(() => {\n if (typeof mediaUrl === 'string' && mediaUrl.trim().length > 0) return mediaUrl\n if (typeof mediaId === 'string' && mediaId.trim().length > 0) {\n return buildAttachmentImageUrl(mediaId, { width: IMAGE_SIZE, height: IMAGE_SIZE, cropType })\n }\n return null\n }, [cropType, mediaId, mediaUrl])\n\n if (!previewUrl) {\n return (\n <div className=\"flex h-16 w-16 items-center justify-center rounded-md border border-dashed text-muted-foreground\">\n <ImageIcon className=\"h-4 w-4\" aria-hidden=\"true\" />\n </div>\n )\n }\n\n return (\n <div className=\"flex h-16 w-16 items-center justify-center overflow-hidden rounded-md border bg-muted/
|
|
4
|
+
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { Image as ImageIcon } from 'lucide-react'\nimport { buildAttachmentImageUrl, type ImageCropType } from '@open-mercato/core/modules/attachments/lib/imageUrls'\n\ntype ProductImageCellProps = {\n mediaId?: string | null\n mediaUrl?: string | null\n title?: string | null\n cropType?: ImageCropType\n}\n\nconst IMAGE_SIZE = 96\n\nexport function ProductImageCell({ mediaId, mediaUrl, title, cropType = 'cover' }: ProductImageCellProps) {\n const previewUrl = React.useMemo(() => {\n if (typeof mediaUrl === 'string' && mediaUrl.trim().length > 0) return mediaUrl\n if (typeof mediaId === 'string' && mediaId.trim().length > 0) {\n return buildAttachmentImageUrl(mediaId, { width: IMAGE_SIZE, height: IMAGE_SIZE, cropType })\n }\n return null\n }, [cropType, mediaId, mediaUrl])\n\n if (!previewUrl) {\n return (\n <div className=\"flex h-16 w-16 items-center justify-center rounded-md border border-dashed text-muted-foreground\">\n <ImageIcon className=\"h-4 w-4\" aria-hidden=\"true\" />\n </div>\n )\n }\n\n return (\n <div className=\"flex h-16 w-16 items-center justify-center overflow-hidden rounded-md border bg-muted/30\">\n <img\n src={previewUrl}\n alt={title ?? ''}\n className={`h-full w-full ${cropType === 'contain' ? 'object-contain' : 'object-cover'}`}\n />\n </div>\n )\n}\n"],
|
|
5
5
|
"mappings": ";AA2BQ;AAzBR,YAAY,WAAW;AACvB,SAAS,SAAS,iBAAiB;AACnC,SAAS,+BAAmD;AAS5D,MAAM,aAAa;AAEZ,SAAS,iBAAiB,EAAE,SAAS,UAAU,OAAO,WAAW,QAAQ,GAA0B;AACxG,QAAM,aAAa,MAAM,QAAQ,MAAM;AACrC,QAAI,OAAO,aAAa,YAAY,SAAS,KAAK,EAAE,SAAS,EAAG,QAAO;AACvE,QAAI,OAAO,YAAY,YAAY,QAAQ,KAAK,EAAE,SAAS,GAAG;AAC5D,aAAO,wBAAwB,SAAS,EAAE,OAAO,YAAY,QAAQ,YAAY,SAAS,CAAC;AAAA,IAC7F;AACA,WAAO;AAAA,EACT,GAAG,CAAC,UAAU,SAAS,QAAQ,CAAC;AAEhC,MAAI,CAAC,YAAY;AACf,WACE,oBAAC,SAAI,WAAU,oGACb,8BAAC,aAAU,WAAU,WAAU,eAAY,QAAO,GACpD;AAAA,EAEJ;AAEA,SACE,oBAAC,SAAI,WAAU,4FACb;AAAA,IAAC;AAAA;AAAA,MACC,KAAK;AAAA,MACL,KAAK,SAAS;AAAA,MACd,WAAW,iBAAiB,aAAa,YAAY,mBAAmB,cAAc;AAAA;AAAA,EACxF,GACF;AAEJ;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -264,7 +264,7 @@ function VariantPricesSection({
|
|
|
264
264
|
) }),
|
|
265
265
|
/* @__PURE__ */ jsx("div", { className: "space-y-3", children: priceKinds.length ? priceKinds.map((kind) => {
|
|
266
266
|
const draft = values.prices?.[kind.id];
|
|
267
|
-
return /* @__PURE__ */ jsxs("div", { className: "rounded bg-muted/
|
|
267
|
+
return /* @__PURE__ */ jsxs("div", { className: "rounded bg-muted/50 p-3", children: [
|
|
268
268
|
/* @__PURE__ */ jsx("div", { className: "flex items-center justify-between", children: /* @__PURE__ */ jsxs("div", { children: [
|
|
269
269
|
/* @__PURE__ */ jsx("p", { className: "text-sm font-semibold", children: kind.title }),
|
|
270
270
|
/* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: kind.currencyCode ? `${kind.currencyCode.toUpperCase()} \u2022 ${kind.displayMode === "including-tax" ? t("catalog.priceKinds.form.displayMode.include", "Including tax") : t("catalog.priceKinds.form.displayMode.exclude", "Excluding tax")}` : t("catalog.variants.form.priceMissingCurrency", "No currency configured") })
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../src/modules/catalog/components/products/VariantBuilder.tsx"],
|
|
4
|
-
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { Label } from '@open-mercato/ui/primitives/label'\nimport { Input } from '@open-mercato/ui/primitives/input'\nimport { Switch } from '@open-mercato/ui/primitives/switch'\nimport { ProductMediaManager } from './ProductMediaManager'\nimport { MetadataEditor } from './MetadataEditor'\nimport type { PriceKindSummary, TaxRateSummary } from './productForm'\nimport { formatTaxRateLabel } from './productForm'\nimport type { OptionDefinition, VariantFormValues, VariantPriceDraft } from './variantForm'\nimport { E } from '#generated/entities.ids.generated'\n\ntype VariantBuilderProps = {\n values: VariantFormValues\n setValue: (id: string, value: unknown) => void\n errors: Record<string, string>\n optionDefinitions: OptionDefinition[]\n priceKinds: PriceKindSummary[]\n taxRates: TaxRateSummary[]\n}\n\ntype VariantSectionBaseProps = {\n values: VariantFormValues\n setValue: (id: string, value: unknown) => void\n errors: Record<string, string>\n}\n\ntype VariantOptionValuesSectionProps = {\n values: VariantFormValues\n setValue: (id: string, value: unknown) => void\n optionDefinitions: OptionDefinition[]\n showHeading?: boolean\n}\n\ntype VariantDimensionsSectionProps = {\n values: VariantFormValues\n setValue: (id: string, value: unknown) => void\n showHeading?: boolean\n}\n\ntype VariantMetadataSectionProps = {\n values: VariantFormValues\n setValue: (id: string, value: unknown) => void\n showIntro?: boolean\n embedded?: boolean\n}\n\ntype VariantPricesSectionProps = {\n values: VariantFormValues\n setValue: (id: string, value: unknown) => void\n priceKinds: PriceKindSummary[]\n taxRates: TaxRateSummary[]\n showHeader?: boolean\n embedded?: boolean\n}\n\ntype VariantMediaSectionProps = {\n values: VariantFormValues\n setValue: (id: string, value: unknown) => void\n showLabel?: boolean\n}\n\nexport function VariantBuilder({\n values,\n setValue,\n errors,\n optionDefinitions,\n priceKinds,\n taxRates,\n}: VariantBuilderProps) {\n return (\n <div className=\"space-y-6\">\n <VariantBasicsSection values={values} setValue={setValue} errors={errors} />\n <VariantOptionValuesSection values={values} setValue={setValue} optionDefinitions={optionDefinitions} />\n <VariantDimensionsSection values={values} setValue={setValue} />\n <VariantMetadataSection values={values} setValue={setValue} />\n <VariantPricesSection values={values} setValue={setValue} priceKinds={priceKinds} taxRates={taxRates} />\n <VariantMediaSection values={values} setValue={setValue} />\n </div>\n )\n}\n\nexport function VariantBasicsSection({ values, setValue, errors }: VariantSectionBaseProps) {\n const t = useT()\n return (\n <div className=\"space-y-6\">\n <div className=\"space-y-2\">\n <Label className=\"flex items-center gap-1\">\n {t('catalog.variants.form.nameLabel', 'Name')}\n <span className=\"text-red-600\">*</span>\n </Label>\n <Input\n value={values.name}\n onChange={(event) => setValue('name', event.target.value)}\n placeholder={t('catalog.variants.form.namePlaceholder', 'e.g., Blue / Small')}\n />\n {errors.name ? <p className=\"text-xs text-red-600\">{errors.name}</p> : null}\n </div>\n <div className=\"grid gap-4 md:grid-cols-2\">\n <div className=\"space-y-2\">\n <Label>{t('catalog.variants.form.skuLabel', 'SKU')}</Label>\n <Input\n value={values.sku}\n onChange={(event) => setValue('sku', event.target.value)}\n placeholder={t('catalog.variants.form.skuPlaceholder', 'Unique identifier')}\n />\n </div>\n <div className=\"space-y-2\">\n <Label>{t('catalog.variants.form.barcodeLabel', 'Barcode')}</Label>\n <Input\n value={values.barcode}\n onChange={(event) => setValue('barcode', event.target.value)}\n placeholder={t('catalog.variants.form.barcodePlaceholder', 'EAN, UPC, etc.')}\n />\n </div>\n </div>\n <div className=\"grid gap-4 md:grid-cols-2\">\n <label className=\"flex items-center justify-between gap-2 rounded border px-3 py-2\">\n <div>\n <p className=\"text-sm font-medium\">{t('catalog.variants.form.isDefaultLabel', 'Default variant')}</p>\n <p className=\"text-xs text-muted-foreground\">{t('catalog.variants.form.isDefaultHint', 'Used in storefronts')}</p>\n </div>\n <Switch checked={values.isDefault} onCheckedChange={(next) => setValue('isDefault', next)} />\n </label>\n <label className=\"flex items-center justify-between gap-2 rounded border px-3 py-2\">\n <div>\n <p className=\"text-sm font-medium\">{t('catalog.variants.form.isActiveLabel', 'Active')}</p>\n <p className=\"text-xs text-muted-foreground\">{t('catalog.variants.form.isActiveHint', 'Inactive variants stay hidden')}</p>\n </div>\n <Switch checked={values.isActive !== false} onCheckedChange={(next) => setValue('isActive', next)} />\n </label>\n </div>\n </div>\n )\n}\n\nexport function VariantOptionValuesSection({\n values,\n setValue,\n optionDefinitions,\n showHeading = true,\n}: VariantOptionValuesSectionProps) {\n const t = useT()\n\n const handleOptionChange = React.useCallback(\n (code: string, next: string) => {\n setValue('optionValues', { ...(values.optionValues ?? {}), [code]: next })\n },\n [setValue, values.optionValues],\n )\n\n if (!optionDefinitions.length) return null\n\n return (\n <div className=\"space-y-3\">\n {showHeading ? <h3 className=\"text-sm font-semibold\">{t('catalog.variants.form.options', 'Option values')}</h3> : null}\n <div className=\"grid gap-4 md:grid-cols-2\">\n {optionDefinitions.map((option) => (\n <div key={option.code} className=\"space-y-2\">\n <Label className=\"text-xs uppercase text-muted-foreground\">{option.label}</Label>\n <select\n className=\"w-full rounded border px-3 py-2 text-sm\"\n value={values.optionValues?.[option.code] ?? ''}\n onChange={(event) => handleOptionChange(option.code, event.target.value)}\n >\n <option value=\"\">{t('catalog.variants.form.optionPlaceholder', 'Select value')}</option>\n {option.values.map((value) => (\n <option key={value.id} value={value.label}>\n {value.label}\n </option>\n ))}\n </select>\n </div>\n ))}\n </div>\n </div>\n )\n}\n\nexport function VariantDimensionsSection({ values, setValue, showHeading = true }: VariantDimensionsSectionProps) {\n const t = useT()\n const metadata = normalizeMetadata(values.metadata)\n const dimensionValues = normalizeDimensions(metadata)\n const weightValues = normalizeWeight(metadata)\n const dimensionUnitPlaceholder = t('catalog.variants.form.dimensionUnitPlaceholder', 'cm')\n const weightUnitPlaceholder = t('catalog.variants.form.weightUnitPlaceholder', 'kg')\n\n return (\n <div className=\"space-y-4\">\n {showHeading ? <h3 className=\"text-sm font-semibold\">{t('catalog.variants.form.dimensions', 'Dimensions & weight')}</h3> : null}\n <div className=\"grid gap-4 md:grid-cols-2\">\n <DimensionInput\n label={t('catalog.variants.form.width', 'Width')}\n value={dimensionValues.width ?? ''}\n onChange={(value) => setValue('metadata', applyDimension(metadata, 'width', value))}\n />\n <DimensionInput\n label={t('catalog.variants.form.height', 'Height')}\n value={dimensionValues.height ?? ''}\n onChange={(value) => setValue('metadata', applyDimension(metadata, 'height', value))}\n />\n <DimensionInput\n label={t('catalog.variants.form.depth', 'Depth')}\n value={dimensionValues.depth ?? ''}\n onChange={(value) => setValue('metadata', applyDimension(metadata, 'depth', value))}\n />\n <DimensionInput\n label={t('catalog.variants.form.dimensionUnit', 'Size unit')}\n value={dimensionValues.unit ?? ''}\n placeholder={dimensionUnitPlaceholder}\n onChange={(value) => setValue('metadata', applyDimension(metadata, 'unit', value))}\n />\n <DimensionInput\n label={t('catalog.variants.form.weight', 'Weight')}\n value={weightValues.value ?? ''}\n onChange={(value) => setValue('metadata', applyWeight(metadata, 'value', value))}\n />\n <DimensionInput\n label={t('catalog.variants.form.weightUnit', 'Weight unit')}\n value={weightValues.unit ?? ''}\n placeholder={weightUnitPlaceholder}\n onChange={(value) => setValue('metadata', applyWeight(metadata, 'unit', value))}\n />\n </div>\n </div>\n )\n}\n\nexport function VariantMetadataSection({\n values,\n setValue,\n showIntro = true,\n embedded = false,\n}: VariantMetadataSectionProps) {\n const metadata = normalizeMetadata(values.metadata)\n const systemMetadata = React.useMemo(() => extractSystemMetadata(metadata), [metadata])\n const customMetadata = React.useMemo(() => stripSystemMetadata(metadata), [metadata])\n\n const handleMetadataChange = React.useCallback(\n (next: Record<string, unknown>) => {\n const merged: Record<string, unknown> = {}\n Object.entries(systemMetadata).forEach(([key, value]) => {\n merged[key] = value\n })\n Object.entries(next).forEach(([key, value]) => {\n merged[key] = value\n })\n setValue('metadata', merged)\n },\n [setValue, systemMetadata],\n )\n\n return (\n <MetadataEditor\n value={customMetadata}\n onChange={handleMetadataChange}\n title={showIntro ? undefined : ''}\n description={showIntro ? undefined : ''}\n embedded={embedded}\n />\n )\n}\n\nexport function VariantPricesSection({\n values,\n setValue,\n priceKinds,\n taxRates,\n showHeader = true,\n embedded = false,\n}: VariantPricesSectionProps) {\n const t = useT()\n\n const updatePrice = React.useCallback(\n (priceKindId: string, patch: Partial<VariantPriceDraft>) => {\n const prev = values.prices?.[priceKindId] ?? { priceKindId, amount: '', displayMode: 'excluding-tax' }\n setValue('prices', { ...values.prices, [priceKindId]: { ...prev, ...patch, priceKindId } })\n },\n [setValue, values.prices],\n )\n\n const containerClass = embedded ? 'space-y-4' : 'space-y-4 rounded-lg border p-4'\n\n return (\n <div className={containerClass}>\n {showHeader ? (\n <div className=\"flex items-center justify-between gap-2\">\n <div>\n <h3 className=\"text-sm font-semibold\">{t('catalog.variants.form.pricesLabel', 'Prices')}</h3>\n <p className=\"text-xs text-muted-foreground\">\n {t('catalog.variants.form.pricesHint', 'Populate list prices per price kind.')}\n </p>\n </div>\n <div className=\"flex items-center gap-2\">\n <select\n className=\"rounded border px-3 py-2 text-sm\"\n value={values.taxRateId ?? ''}\n onChange={(event) => setValue('taxRateId', event.target.value || null)}\n >\n <option value=\"\">{t('catalog.variants.form.pricesTaxNone', 'No tax override')}</option>\n {taxRates.map((rate) => (\n <option key={rate.id} value={rate.id}>\n {formatTaxRateLabel(rate)}\n </option>\n ))}\n </select>\n </div>\n </div>\n ) : (\n <div className=\"flex justify-end\">\n <select\n className=\"rounded border px-3 py-2 text-sm\"\n value={values.taxRateId ?? ''}\n onChange={(event) => setValue('taxRateId', event.target.value || null)}\n >\n <option value=\"\">{t('catalog.variants.form.pricesTaxNone', 'No tax override')}</option>\n {taxRates.map((rate) => (\n <option key={rate.id} value={rate.id}>\n {formatTaxRateLabel(rate)}\n </option>\n ))}\n </select>\n </div>\n )}\n <div className=\"space-y-3\">\n {priceKinds.length ? (\n priceKinds.map((kind) => {\n const draft = values.prices?.[kind.id]\n return (\n <div key={kind.id} className=\"rounded bg-muted/40 p-3\">\n <div className=\"flex items-center justify-between\">\n <div>\n <p className=\"text-sm font-semibold\">{kind.title}</p>\n <p className=\"text-xs text-muted-foreground\">\n {kind.currencyCode\n ? `${kind.currencyCode.toUpperCase()} \u2022 ${kind.displayMode === 'including-tax' ? t('catalog.priceKinds.form.displayMode.include', 'Including tax') : t('catalog.priceKinds.form.displayMode.exclude', 'Excluding tax')}`\n : t('catalog.variants.form.priceMissingCurrency', 'No currency configured')}\n </p>\n </div>\n </div>\n <Input\n className=\"mt-3\"\n value={draft?.amount ?? ''}\n onChange={(event) => updatePrice(kind.id, { amount: event.target.value })}\n placeholder=\"0.00\"\n />\n </div>\n )\n })\n ) : (\n <p className=\"text-xs text-muted-foreground\">{t('catalog.variants.form.pricesEmpty', 'No price kinds configured yet.')}</p>\n )}\n </div>\n </div>\n )\n}\n\nexport function VariantMediaSection({ values, setValue, showLabel = true }: VariantMediaSectionProps) {\n const t = useT()\n return (\n <div className=\"space-y-2\">\n {showLabel ? <Label>{t('catalog.variants.form.media', 'Media')}</Label> : null}\n <ProductMediaManager\n entityId={E.catalog.catalog_product_variant}\n draftRecordId={values.mediaDraftId}\n items={Array.isArray(values.mediaItems) ? values.mediaItems : []}\n defaultMediaId={values.defaultMediaId}\n onItemsChange={(next) => setValue('mediaItems', next)}\n onDefaultChange={(next) => setValue('defaultMediaId', next)}\n />\n </div>\n )\n}\n\nfunction DimensionInput({\n label,\n value,\n onChange,\n placeholder = '0',\n}: {\n label: string\n value: string | number | undefined\n onChange: (next: string) => void\n placeholder?: string\n}) {\n return (\n <div className=\"space-y-2\">\n <Label className=\"text-xs uppercase text-muted-foreground\">{label}</Label>\n <Input value={value ?? ''} onChange={(event) => onChange(event.target.value)} placeholder={placeholder} />\n </div>\n )\n}\n\nfunction normalizeMetadata(input: unknown): Record<string, any> {\n return typeof input === 'object' && input ? { ...(input as Record<string, unknown>) } : {}\n}\n\nfunction normalizeDimensions(metadata: Record<string, any>) {\n const raw = metadata.dimensions\n if (!raw || typeof raw !== 'object') return {}\n return {\n width: typeof raw.width === 'number' ? raw.width : undefined,\n height: typeof raw.height === 'number' ? raw.height : undefined,\n depth: typeof raw.depth === 'number' ? raw.depth : undefined,\n unit: typeof raw.unit === 'string' ? raw.unit : undefined,\n }\n}\n\nfunction normalizeWeight(metadata: Record<string, any>) {\n const raw = metadata.weight\n if (!raw || typeof raw !== 'object') return {}\n return {\n value: typeof raw.value === 'number' ? raw.value : undefined,\n unit: typeof raw.unit === 'string' ? raw.unit : undefined,\n }\n}\n\nfunction applyDimension(metadata: Record<string, any>, field: 'width' | 'height' | 'depth' | 'unit', raw: string) {\n const dims = normalizeDimensions(metadata)\n if (field === 'unit') {\n dims.unit = raw\n } else {\n const numeric = Number(raw)\n dims[field] = Number.isNaN(numeric) ? undefined : numeric\n }\n const clean = cleanupDimensions(dims)\n if (clean) return { ...metadata, dimensions: clean }\n const copy = { ...metadata }\n delete copy.dimensions\n return copy\n}\n\nfunction applyWeight(metadata: Record<string, any>, field: 'value' | 'unit', raw: string) {\n const weight = normalizeWeight(metadata)\n if (field === 'unit') weight.unit = raw\n else {\n const numeric = Number(raw)\n weight.value = Number.isNaN(numeric) ? undefined : numeric\n }\n const clean = cleanupWeight(weight)\n if (clean) return { ...metadata, weight: clean }\n const copy = { ...metadata }\n delete copy.weight\n return copy\n}\n\nfunction cleanupDimensions(dims: { width?: number; height?: number; depth?: number; unit?: string }) {\n const clean: Record<string, unknown> = {}\n if (typeof dims.width === 'number' && Number.isFinite(dims.width)) clean.width = dims.width\n if (typeof dims.height === 'number' && Number.isFinite(dims.height)) clean.height = dims.height\n if (typeof dims.depth === 'number' && Number.isFinite(dims.depth)) clean.depth = dims.depth\n if (typeof dims.unit === 'string' && dims.unit.trim().length) clean.unit = dims.unit\n return Object.keys(clean).length ? clean : null\n}\n\nfunction cleanupWeight(weight: { value?: number; unit?: string }) {\n const clean: Record<string, unknown> = {}\n if (typeof weight.value === 'number' && Number.isFinite(weight.value)) clean.value = weight.value\n if (typeof weight.unit === 'string' && weight.unit.trim().length) clean.unit = weight.unit\n return Object.keys(clean).length ? clean : null\n}\n\nfunction stripSystemMetadata(metadata: Record<string, any>) {\n const copy: Record<string, unknown> = {}\n for (const [key, value] of Object.entries(metadata)) {\n if (key === 'dimensions' || key === 'weight') continue\n copy[key] = value\n }\n return copy\n}\n\nfunction extractSystemMetadata(metadata: Record<string, any>) {\n const system: Record<string, unknown> = {}\n if (metadata.dimensions) system.dimensions = metadata.dimensions\n if (metadata.weight) system.weight = metadata.weight\n return system\n}\n"],
|
|
4
|
+
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { Label } from '@open-mercato/ui/primitives/label'\nimport { Input } from '@open-mercato/ui/primitives/input'\nimport { Switch } from '@open-mercato/ui/primitives/switch'\nimport { ProductMediaManager } from './ProductMediaManager'\nimport { MetadataEditor } from './MetadataEditor'\nimport type { PriceKindSummary, TaxRateSummary } from './productForm'\nimport { formatTaxRateLabel } from './productForm'\nimport type { OptionDefinition, VariantFormValues, VariantPriceDraft } from './variantForm'\nimport { E } from '#generated/entities.ids.generated'\n\ntype VariantBuilderProps = {\n values: VariantFormValues\n setValue: (id: string, value: unknown) => void\n errors: Record<string, string>\n optionDefinitions: OptionDefinition[]\n priceKinds: PriceKindSummary[]\n taxRates: TaxRateSummary[]\n}\n\ntype VariantSectionBaseProps = {\n values: VariantFormValues\n setValue: (id: string, value: unknown) => void\n errors: Record<string, string>\n}\n\ntype VariantOptionValuesSectionProps = {\n values: VariantFormValues\n setValue: (id: string, value: unknown) => void\n optionDefinitions: OptionDefinition[]\n showHeading?: boolean\n}\n\ntype VariantDimensionsSectionProps = {\n values: VariantFormValues\n setValue: (id: string, value: unknown) => void\n showHeading?: boolean\n}\n\ntype VariantMetadataSectionProps = {\n values: VariantFormValues\n setValue: (id: string, value: unknown) => void\n showIntro?: boolean\n embedded?: boolean\n}\n\ntype VariantPricesSectionProps = {\n values: VariantFormValues\n setValue: (id: string, value: unknown) => void\n priceKinds: PriceKindSummary[]\n taxRates: TaxRateSummary[]\n showHeader?: boolean\n embedded?: boolean\n}\n\ntype VariantMediaSectionProps = {\n values: VariantFormValues\n setValue: (id: string, value: unknown) => void\n showLabel?: boolean\n}\n\nexport function VariantBuilder({\n values,\n setValue,\n errors,\n optionDefinitions,\n priceKinds,\n taxRates,\n}: VariantBuilderProps) {\n return (\n <div className=\"space-y-6\">\n <VariantBasicsSection values={values} setValue={setValue} errors={errors} />\n <VariantOptionValuesSection values={values} setValue={setValue} optionDefinitions={optionDefinitions} />\n <VariantDimensionsSection values={values} setValue={setValue} />\n <VariantMetadataSection values={values} setValue={setValue} />\n <VariantPricesSection values={values} setValue={setValue} priceKinds={priceKinds} taxRates={taxRates} />\n <VariantMediaSection values={values} setValue={setValue} />\n </div>\n )\n}\n\nexport function VariantBasicsSection({ values, setValue, errors }: VariantSectionBaseProps) {\n const t = useT()\n return (\n <div className=\"space-y-6\">\n <div className=\"space-y-2\">\n <Label className=\"flex items-center gap-1\">\n {t('catalog.variants.form.nameLabel', 'Name')}\n <span className=\"text-red-600\">*</span>\n </Label>\n <Input\n value={values.name}\n onChange={(event) => setValue('name', event.target.value)}\n placeholder={t('catalog.variants.form.namePlaceholder', 'e.g., Blue / Small')}\n />\n {errors.name ? <p className=\"text-xs text-red-600\">{errors.name}</p> : null}\n </div>\n <div className=\"grid gap-4 md:grid-cols-2\">\n <div className=\"space-y-2\">\n <Label>{t('catalog.variants.form.skuLabel', 'SKU')}</Label>\n <Input\n value={values.sku}\n onChange={(event) => setValue('sku', event.target.value)}\n placeholder={t('catalog.variants.form.skuPlaceholder', 'Unique identifier')}\n />\n </div>\n <div className=\"space-y-2\">\n <Label>{t('catalog.variants.form.barcodeLabel', 'Barcode')}</Label>\n <Input\n value={values.barcode}\n onChange={(event) => setValue('barcode', event.target.value)}\n placeholder={t('catalog.variants.form.barcodePlaceholder', 'EAN, UPC, etc.')}\n />\n </div>\n </div>\n <div className=\"grid gap-4 md:grid-cols-2\">\n <label className=\"flex items-center justify-between gap-2 rounded border px-3 py-2\">\n <div>\n <p className=\"text-sm font-medium\">{t('catalog.variants.form.isDefaultLabel', 'Default variant')}</p>\n <p className=\"text-xs text-muted-foreground\">{t('catalog.variants.form.isDefaultHint', 'Used in storefronts')}</p>\n </div>\n <Switch checked={values.isDefault} onCheckedChange={(next) => setValue('isDefault', next)} />\n </label>\n <label className=\"flex items-center justify-between gap-2 rounded border px-3 py-2\">\n <div>\n <p className=\"text-sm font-medium\">{t('catalog.variants.form.isActiveLabel', 'Active')}</p>\n <p className=\"text-xs text-muted-foreground\">{t('catalog.variants.form.isActiveHint', 'Inactive variants stay hidden')}</p>\n </div>\n <Switch checked={values.isActive !== false} onCheckedChange={(next) => setValue('isActive', next)} />\n </label>\n </div>\n </div>\n )\n}\n\nexport function VariantOptionValuesSection({\n values,\n setValue,\n optionDefinitions,\n showHeading = true,\n}: VariantOptionValuesSectionProps) {\n const t = useT()\n\n const handleOptionChange = React.useCallback(\n (code: string, next: string) => {\n setValue('optionValues', { ...(values.optionValues ?? {}), [code]: next })\n },\n [setValue, values.optionValues],\n )\n\n if (!optionDefinitions.length) return null\n\n return (\n <div className=\"space-y-3\">\n {showHeading ? <h3 className=\"text-sm font-semibold\">{t('catalog.variants.form.options', 'Option values')}</h3> : null}\n <div className=\"grid gap-4 md:grid-cols-2\">\n {optionDefinitions.map((option) => (\n <div key={option.code} className=\"space-y-2\">\n <Label className=\"text-xs uppercase text-muted-foreground\">{option.label}</Label>\n <select\n className=\"w-full rounded border px-3 py-2 text-sm\"\n value={values.optionValues?.[option.code] ?? ''}\n onChange={(event) => handleOptionChange(option.code, event.target.value)}\n >\n <option value=\"\">{t('catalog.variants.form.optionPlaceholder', 'Select value')}</option>\n {option.values.map((value) => (\n <option key={value.id} value={value.label}>\n {value.label}\n </option>\n ))}\n </select>\n </div>\n ))}\n </div>\n </div>\n )\n}\n\nexport function VariantDimensionsSection({ values, setValue, showHeading = true }: VariantDimensionsSectionProps) {\n const t = useT()\n const metadata = normalizeMetadata(values.metadata)\n const dimensionValues = normalizeDimensions(metadata)\n const weightValues = normalizeWeight(metadata)\n const dimensionUnitPlaceholder = t('catalog.variants.form.dimensionUnitPlaceholder', 'cm')\n const weightUnitPlaceholder = t('catalog.variants.form.weightUnitPlaceholder', 'kg')\n\n return (\n <div className=\"space-y-4\">\n {showHeading ? <h3 className=\"text-sm font-semibold\">{t('catalog.variants.form.dimensions', 'Dimensions & weight')}</h3> : null}\n <div className=\"grid gap-4 md:grid-cols-2\">\n <DimensionInput\n label={t('catalog.variants.form.width', 'Width')}\n value={dimensionValues.width ?? ''}\n onChange={(value) => setValue('metadata', applyDimension(metadata, 'width', value))}\n />\n <DimensionInput\n label={t('catalog.variants.form.height', 'Height')}\n value={dimensionValues.height ?? ''}\n onChange={(value) => setValue('metadata', applyDimension(metadata, 'height', value))}\n />\n <DimensionInput\n label={t('catalog.variants.form.depth', 'Depth')}\n value={dimensionValues.depth ?? ''}\n onChange={(value) => setValue('metadata', applyDimension(metadata, 'depth', value))}\n />\n <DimensionInput\n label={t('catalog.variants.form.dimensionUnit', 'Size unit')}\n value={dimensionValues.unit ?? ''}\n placeholder={dimensionUnitPlaceholder}\n onChange={(value) => setValue('metadata', applyDimension(metadata, 'unit', value))}\n />\n <DimensionInput\n label={t('catalog.variants.form.weight', 'Weight')}\n value={weightValues.value ?? ''}\n onChange={(value) => setValue('metadata', applyWeight(metadata, 'value', value))}\n />\n <DimensionInput\n label={t('catalog.variants.form.weightUnit', 'Weight unit')}\n value={weightValues.unit ?? ''}\n placeholder={weightUnitPlaceholder}\n onChange={(value) => setValue('metadata', applyWeight(metadata, 'unit', value))}\n />\n </div>\n </div>\n )\n}\n\nexport function VariantMetadataSection({\n values,\n setValue,\n showIntro = true,\n embedded = false,\n}: VariantMetadataSectionProps) {\n const metadata = normalizeMetadata(values.metadata)\n const systemMetadata = React.useMemo(() => extractSystemMetadata(metadata), [metadata])\n const customMetadata = React.useMemo(() => stripSystemMetadata(metadata), [metadata])\n\n const handleMetadataChange = React.useCallback(\n (next: Record<string, unknown>) => {\n const merged: Record<string, unknown> = {}\n Object.entries(systemMetadata).forEach(([key, value]) => {\n merged[key] = value\n })\n Object.entries(next).forEach(([key, value]) => {\n merged[key] = value\n })\n setValue('metadata', merged)\n },\n [setValue, systemMetadata],\n )\n\n return (\n <MetadataEditor\n value={customMetadata}\n onChange={handleMetadataChange}\n title={showIntro ? undefined : ''}\n description={showIntro ? undefined : ''}\n embedded={embedded}\n />\n )\n}\n\nexport function VariantPricesSection({\n values,\n setValue,\n priceKinds,\n taxRates,\n showHeader = true,\n embedded = false,\n}: VariantPricesSectionProps) {\n const t = useT()\n\n const updatePrice = React.useCallback(\n (priceKindId: string, patch: Partial<VariantPriceDraft>) => {\n const prev = values.prices?.[priceKindId] ?? { priceKindId, amount: '', displayMode: 'excluding-tax' }\n setValue('prices', { ...values.prices, [priceKindId]: { ...prev, ...patch, priceKindId } })\n },\n [setValue, values.prices],\n )\n\n const containerClass = embedded ? 'space-y-4' : 'space-y-4 rounded-lg border p-4'\n\n return (\n <div className={containerClass}>\n {showHeader ? (\n <div className=\"flex items-center justify-between gap-2\">\n <div>\n <h3 className=\"text-sm font-semibold\">{t('catalog.variants.form.pricesLabel', 'Prices')}</h3>\n <p className=\"text-xs text-muted-foreground\">\n {t('catalog.variants.form.pricesHint', 'Populate list prices per price kind.')}\n </p>\n </div>\n <div className=\"flex items-center gap-2\">\n <select\n className=\"rounded border px-3 py-2 text-sm\"\n value={values.taxRateId ?? ''}\n onChange={(event) => setValue('taxRateId', event.target.value || null)}\n >\n <option value=\"\">{t('catalog.variants.form.pricesTaxNone', 'No tax override')}</option>\n {taxRates.map((rate) => (\n <option key={rate.id} value={rate.id}>\n {formatTaxRateLabel(rate)}\n </option>\n ))}\n </select>\n </div>\n </div>\n ) : (\n <div className=\"flex justify-end\">\n <select\n className=\"rounded border px-3 py-2 text-sm\"\n value={values.taxRateId ?? ''}\n onChange={(event) => setValue('taxRateId', event.target.value || null)}\n >\n <option value=\"\">{t('catalog.variants.form.pricesTaxNone', 'No tax override')}</option>\n {taxRates.map((rate) => (\n <option key={rate.id} value={rate.id}>\n {formatTaxRateLabel(rate)}\n </option>\n ))}\n </select>\n </div>\n )}\n <div className=\"space-y-3\">\n {priceKinds.length ? (\n priceKinds.map((kind) => {\n const draft = values.prices?.[kind.id]\n return (\n <div key={kind.id} className=\"rounded bg-muted/50 p-3\">\n <div className=\"flex items-center justify-between\">\n <div>\n <p className=\"text-sm font-semibold\">{kind.title}</p>\n <p className=\"text-xs text-muted-foreground\">\n {kind.currencyCode\n ? `${kind.currencyCode.toUpperCase()} \u2022 ${kind.displayMode === 'including-tax' ? t('catalog.priceKinds.form.displayMode.include', 'Including tax') : t('catalog.priceKinds.form.displayMode.exclude', 'Excluding tax')}`\n : t('catalog.variants.form.priceMissingCurrency', 'No currency configured')}\n </p>\n </div>\n </div>\n <Input\n className=\"mt-3\"\n value={draft?.amount ?? ''}\n onChange={(event) => updatePrice(kind.id, { amount: event.target.value })}\n placeholder=\"0.00\"\n />\n </div>\n )\n })\n ) : (\n <p className=\"text-xs text-muted-foreground\">{t('catalog.variants.form.pricesEmpty', 'No price kinds configured yet.')}</p>\n )}\n </div>\n </div>\n )\n}\n\nexport function VariantMediaSection({ values, setValue, showLabel = true }: VariantMediaSectionProps) {\n const t = useT()\n return (\n <div className=\"space-y-2\">\n {showLabel ? <Label>{t('catalog.variants.form.media', 'Media')}</Label> : null}\n <ProductMediaManager\n entityId={E.catalog.catalog_product_variant}\n draftRecordId={values.mediaDraftId}\n items={Array.isArray(values.mediaItems) ? values.mediaItems : []}\n defaultMediaId={values.defaultMediaId}\n onItemsChange={(next) => setValue('mediaItems', next)}\n onDefaultChange={(next) => setValue('defaultMediaId', next)}\n />\n </div>\n )\n}\n\nfunction DimensionInput({\n label,\n value,\n onChange,\n placeholder = '0',\n}: {\n label: string\n value: string | number | undefined\n onChange: (next: string) => void\n placeholder?: string\n}) {\n return (\n <div className=\"space-y-2\">\n <Label className=\"text-xs uppercase text-muted-foreground\">{label}</Label>\n <Input value={value ?? ''} onChange={(event) => onChange(event.target.value)} placeholder={placeholder} />\n </div>\n )\n}\n\nfunction normalizeMetadata(input: unknown): Record<string, any> {\n return typeof input === 'object' && input ? { ...(input as Record<string, unknown>) } : {}\n}\n\nfunction normalizeDimensions(metadata: Record<string, any>) {\n const raw = metadata.dimensions\n if (!raw || typeof raw !== 'object') return {}\n return {\n width: typeof raw.width === 'number' ? raw.width : undefined,\n height: typeof raw.height === 'number' ? raw.height : undefined,\n depth: typeof raw.depth === 'number' ? raw.depth : undefined,\n unit: typeof raw.unit === 'string' ? raw.unit : undefined,\n }\n}\n\nfunction normalizeWeight(metadata: Record<string, any>) {\n const raw = metadata.weight\n if (!raw || typeof raw !== 'object') return {}\n return {\n value: typeof raw.value === 'number' ? raw.value : undefined,\n unit: typeof raw.unit === 'string' ? raw.unit : undefined,\n }\n}\n\nfunction applyDimension(metadata: Record<string, any>, field: 'width' | 'height' | 'depth' | 'unit', raw: string) {\n const dims = normalizeDimensions(metadata)\n if (field === 'unit') {\n dims.unit = raw\n } else {\n const numeric = Number(raw)\n dims[field] = Number.isNaN(numeric) ? undefined : numeric\n }\n const clean = cleanupDimensions(dims)\n if (clean) return { ...metadata, dimensions: clean }\n const copy = { ...metadata }\n delete copy.dimensions\n return copy\n}\n\nfunction applyWeight(metadata: Record<string, any>, field: 'value' | 'unit', raw: string) {\n const weight = normalizeWeight(metadata)\n if (field === 'unit') weight.unit = raw\n else {\n const numeric = Number(raw)\n weight.value = Number.isNaN(numeric) ? undefined : numeric\n }\n const clean = cleanupWeight(weight)\n if (clean) return { ...metadata, weight: clean }\n const copy = { ...metadata }\n delete copy.weight\n return copy\n}\n\nfunction cleanupDimensions(dims: { width?: number; height?: number; depth?: number; unit?: string }) {\n const clean: Record<string, unknown> = {}\n if (typeof dims.width === 'number' && Number.isFinite(dims.width)) clean.width = dims.width\n if (typeof dims.height === 'number' && Number.isFinite(dims.height)) clean.height = dims.height\n if (typeof dims.depth === 'number' && Number.isFinite(dims.depth)) clean.depth = dims.depth\n if (typeof dims.unit === 'string' && dims.unit.trim().length) clean.unit = dims.unit\n return Object.keys(clean).length ? clean : null\n}\n\nfunction cleanupWeight(weight: { value?: number; unit?: string }) {\n const clean: Record<string, unknown> = {}\n if (typeof weight.value === 'number' && Number.isFinite(weight.value)) clean.value = weight.value\n if (typeof weight.unit === 'string' && weight.unit.trim().length) clean.unit = weight.unit\n return Object.keys(clean).length ? clean : null\n}\n\nfunction stripSystemMetadata(metadata: Record<string, any>) {\n const copy: Record<string, unknown> = {}\n for (const [key, value] of Object.entries(metadata)) {\n if (key === 'dimensions' || key === 'weight') continue\n copy[key] = value\n }\n return copy\n}\n\nfunction extractSystemMetadata(metadata: Record<string, any>) {\n const system: Record<string, unknown> = {}\n if (metadata.dimensions) system.dimensions = metadata.dimensions\n if (metadata.weight) system.weight = metadata.weight\n return system\n}\n"],
|
|
5
5
|
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -73,7 +73,7 @@ function ProductSeoWidget({ data }) {
|
|
|
73
73
|
/* @__PURE__ */ jsx("span", { className: `font-medium ${descScore.color}`, children: descScore.text })
|
|
74
74
|
] })
|
|
75
75
|
] }),
|
|
76
|
-
/* @__PURE__ */ jsxs("p", { className: "text-
|
|
76
|
+
/* @__PURE__ */ jsxs("p", { className: "text-overline text-muted-foreground", children: [
|
|
77
77
|
t("catalog.products.create.seoWidget.footer", "Example widget powered by the injection system."),
|
|
78
78
|
" ",
|
|
79
79
|
/* @__PURE__ */ jsx("a", { className: "text-primary underline", href: "/docs/framework/admin-ui/widget-injection", target: "_blank", rel: "noreferrer", children: t("catalog.products.create.seoWidget.learnMore", "Learn how to build your own") }),
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../../src/modules/catalog/widgets/injection/product-seo/widget.client.tsx"],
|
|
4
|
-
"sourcesContent": ["\"use client\"\nimport * as React from 'react'\nimport type { InjectionWidgetComponentProps } from '@open-mercato/shared/modules/widgets/injection'\nimport { subscribeProductSeoValidation } from './state'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\n\ntype SeoData = {\n title?: string | null\n name?: string | null\n description?: string | null\n}\n\ntype ValidationState = { ok: boolean; issues: string[]; message?: string }\n\ntype IssueKey = 'addTitle' | 'titleTooShort' | 'titleTooLong' | 'addDescription' | 'descriptionTooShort'\n\nfunction computeIssueKeys(title: string, description: string): IssueKey[] {\n const issues: IssueKey[] = []\n if (!title) {\n issues.push('addTitle')\n } else {\n if (title.length < 10) issues.push('titleTooShort')\n if (title.length > 60) issues.push('titleTooLong')\n }\n if (!description) {\n issues.push('addDescription')\n } else if (description.length < 50) {\n issues.push('descriptionTooShort')\n }\n return issues\n}\n\nexport default function ProductSeoWidget({ data }: InjectionWidgetComponentProps<unknown, SeoData>) {\n const t = useT()\n const title = (data?.title || data?.name || '') ?? ''\n const description = data?.description ?? ''\n const baselineIssueKeys = React.useMemo(() => computeIssueKeys(title, description), [title, description])\n const [validation, setValidation] = React.useState<ValidationState>({ ok: baselineIssueKeys.length === 0, issues: baselineIssueKeys })\n\n React.useEffect(() => {\n setValidation({ ok: baselineIssueKeys.length === 0, issues: baselineIssueKeys })\n }, [baselineIssueKeys])\n\n React.useEffect(() => {\n return subscribeProductSeoValidation((payload) => {\n setValidation({\n ok: payload.ok,\n issues: payload.issues,\n message: payload.message,\n })\n })\n }, [])\n\n const titleScore = React.useMemo(() => {\n if (!title) return { text: t('catalog.products.create.seoWidget.missing', 'Missing'), color: 'text-red-600', bg: 'bg-red-50', border: 'border-red-200' }\n if (title.length < 10) return { text: t('catalog.products.create.seoWidget.tooShort', 'Too short'), color: 'text-amber-600', bg: 'bg-amber-50', border: 'border-amber-200' }\n if (title.length > 60) return { text: t('catalog.products.create.seoWidget.tooLong', 'Too long'), color: 'text-amber-600', bg: 'bg-amber-50', border: 'border-amber-200' }\n return { text: t('catalog.products.create.seoWidget.good', 'Good'), color: 'text-green-600', bg: 'bg-green-50', border: 'border-green-200' }\n }, [title, t])\n\n const descScore = React.useMemo(() => {\n if (!description) return { text: t('catalog.products.create.seoWidget.missing', 'Missing'), color: 'text-red-600', bg: 'bg-red-50', border: 'border-red-200' }\n if (description.length < 50) return { text: t('catalog.products.create.seoWidget.tooShort', 'Too short'), color: 'text-amber-600', bg: 'bg-amber-50', border: 'border-amber-200' }\n return { text: t('catalog.products.create.seoWidget.good', 'Good'), color: 'text-green-600', bg: 'bg-green-50', border: 'border-green-200' }\n }, [description, t])\n\n const statusBadge = validation.ok ? (\n <span className=\"rounded-full bg-emerald-50 px-2 py-0.5 text-xs font-medium text-emerald-700 border border-emerald-200\">\n {t('catalog.products.create.seoWidget.ready', 'Ready')}\n </span>\n ) : (\n <span className=\"rounded-full bg-amber-50 px-2 py-0.5 text-xs font-medium text-amber-700 border border-amber-200\">\n {t('catalog.products.create.seoWidget.needsAttention', 'Needs attention')}\n </span>\n )\n\n const translateIssue = (issueKey: string): string => {\n return t(`catalog.products.create.seoWidget.issues.${issueKey}`, issueKey)\n }\n\n return (\n <div className=\"space-y-3 rounded-lg border bg-card p-4 shadow-sm\">\n <div className=\"flex items-start justify-between gap-3\">\n <div>\n <div className=\"text-sm font-semibold text-foreground\">{t('catalog.products.create.seoWidget.title', 'SEO Optimization')}</div>\n <p className=\"text-xs text-muted-foreground\">{t('catalog.products.create.seoWidget.hint', 'Keep titles 10\u201360 chars and descriptions 50+ chars.')}</p>\n </div>\n {statusBadge}\n </div>\n\n {validation.message || validation.issues.length ? (\n <div className={`rounded-md border p-3 text-xs ${validation.ok ? 'border-emerald-200 bg-emerald-50 text-emerald-800' : 'border-amber-200 bg-amber-50 text-amber-900'}`}>\n {validation.message ? <div className=\"font-medium\">{validation.message}</div> : null}\n {validation.issues.length ? (\n <ul className=\"ml-4 list-disc space-y-1 pt-1\">\n {validation.issues.map((issue) => (\n <li key={issue}>{translateIssue(issue)}</li>\n ))}\n </ul>\n ) : null}\n </div>\n ) : null}\n\n <div className=\"rounded border bg-muted/30 p-3 text-sm\">\n <div className=\"flex items-center justify-between\">\n <span className=\"text-muted-foreground\">{t('catalog.products.create.seoWidget.titleLabel', 'Title ({{count}} chars)', { count: title.length })}</span>\n <span className={`font-medium ${titleScore.color}`}>{titleScore.text}</span>\n </div>\n <div className=\"mt-2 flex items-center justify-between\">\n <span className=\"text-muted-foreground\">{t('catalog.products.create.seoWidget.descriptionLabel', 'Description ({{count}} chars)', { count: description.length })}</span>\n <span className={`font-medium ${descScore.color}`}>{descScore.text}</span>\n </div>\n </div>\n\n <p className=\"text-
|
|
5
|
-
"mappings": ";AAmEI,cAgBI,YAhBJ;AAlEJ,YAAY,WAAW;AAEvB,SAAS,qCAAqC;AAC9C,SAAS,YAAY;AAYrB,SAAS,iBAAiB,OAAe,aAAiC;AACxE,QAAM,SAAqB,CAAC;AAC5B,MAAI,CAAC,OAAO;AACV,WAAO,KAAK,UAAU;AAAA,EACxB,OAAO;AACL,QAAI,MAAM,SAAS,GAAI,QAAO,KAAK,eAAe;AAClD,QAAI,MAAM,SAAS,GAAI,QAAO,KAAK,cAAc;AAAA,EACnD;AACA,MAAI,CAAC,aAAa;AAChB,WAAO,KAAK,gBAAgB;AAAA,EAC9B,WAAW,YAAY,SAAS,IAAI;AAClC,WAAO,KAAK,qBAAqB;AAAA,EACnC;AACA,SAAO;AACT;AAEe,SAAR,iBAAkC,EAAE,KAAK,GAAoD;AAClG,QAAM,IAAI,KAAK;AACf,QAAM,SAAS,MAAM,SAAS,MAAM,QAAQ,OAAO;AACnD,QAAM,cAAc,MAAM,eAAe;AACzC,QAAM,oBAAoB,MAAM,QAAQ,MAAM,iBAAiB,OAAO,WAAW,GAAG,CAAC,OAAO,WAAW,CAAC;AACxG,QAAM,CAAC,YAAY,aAAa,IAAI,MAAM,SAA0B,EAAE,IAAI,kBAAkB,WAAW,GAAG,QAAQ,kBAAkB,CAAC;AAErI,QAAM,UAAU,MAAM;AACpB,kBAAc,EAAE,IAAI,kBAAkB,WAAW,GAAG,QAAQ,kBAAkB,CAAC;AAAA,EACjF,GAAG,CAAC,iBAAiB,CAAC;AAEtB,QAAM,UAAU,MAAM;AACpB,WAAO,8BAA8B,CAAC,YAAY;AAChD,oBAAc;AAAA,QACZ,IAAI,QAAQ;AAAA,QACZ,QAAQ,QAAQ;AAAA,QAChB,SAAS,QAAQ;AAAA,MACnB,CAAC;AAAA,IACH,CAAC;AAAA,EACH,GAAG,CAAC,CAAC;AAEL,QAAM,aAAa,MAAM,QAAQ,MAAM;AACrC,QAAI,CAAC,MAAO,QAAO,EAAE,MAAM,EAAE,6CAA6C,SAAS,GAAG,OAAO,gBAAgB,IAAI,aAAa,QAAQ,iBAAiB;AACvJ,QAAI,MAAM,SAAS,GAAI,QAAO,EAAE,MAAM,EAAE,8CAA8C,WAAW,GAAG,OAAO,kBAAkB,IAAI,eAAe,QAAQ,mBAAmB;AAC3K,QAAI,MAAM,SAAS,GAAI,QAAO,EAAE,MAAM,EAAE,6CAA6C,UAAU,GAAG,OAAO,kBAAkB,IAAI,eAAe,QAAQ,mBAAmB;AACzK,WAAO,EAAE,MAAM,EAAE,0CAA0C,MAAM,GAAG,OAAO,kBAAkB,IAAI,eAAe,QAAQ,mBAAmB;AAAA,EAC7I,GAAG,CAAC,OAAO,CAAC,CAAC;AAEb,QAAM,YAAY,MAAM,QAAQ,MAAM;AACpC,QAAI,CAAC,YAAa,QAAO,EAAE,MAAM,EAAE,6CAA6C,SAAS,GAAG,OAAO,gBAAgB,IAAI,aAAa,QAAQ,iBAAiB;AAC7J,QAAI,YAAY,SAAS,GAAI,QAAO,EAAE,MAAM,EAAE,8CAA8C,WAAW,GAAG,OAAO,kBAAkB,IAAI,eAAe,QAAQ,mBAAmB;AACjL,WAAO,EAAE,MAAM,EAAE,0CAA0C,MAAM,GAAG,OAAO,kBAAkB,IAAI,eAAe,QAAQ,mBAAmB;AAAA,EAC7I,GAAG,CAAC,aAAa,CAAC,CAAC;AAEnB,QAAM,cAAc,WAAW,KAC7B,oBAAC,UAAK,WAAU,yGACb,YAAE,2CAA2C,OAAO,GACvD,IAEA,oBAAC,UAAK,WAAU,mGACb,YAAE,oDAAoD,iBAAiB,GAC1E;AAGF,QAAM,iBAAiB,CAAC,aAA6B;AACnD,WAAO,EAAE,4CAA4C,QAAQ,IAAI,QAAQ;AAAA,EAC3E;AAEA,SACE,qBAAC,SAAI,WAAU,qDACb;AAAA,yBAAC,SAAI,WAAU,0CACb;AAAA,2BAAC,SACC;AAAA,4BAAC,SAAI,WAAU,yCAAyC,YAAE,2CAA2C,kBAAkB,GAAE;AAAA,QACzH,oBAAC,OAAE,WAAU,iCAAiC,YAAE,0CAA0C,0DAAqD,GAAE;AAAA,SACnJ;AAAA,MACC;AAAA,OACH;AAAA,IAEC,WAAW,WAAW,WAAW,OAAO,SACvC,qBAAC,SAAI,WAAW,iCAAiC,WAAW,KAAK,sDAAsD,6CAA6C,IACjK;AAAA,iBAAW,UAAU,oBAAC,SAAI,WAAU,eAAe,qBAAW,SAAQ,IAAS;AAAA,MAC/E,WAAW,OAAO,SACjB,oBAAC,QAAG,WAAU,iCACX,qBAAW,OAAO,IAAI,CAAC,UACtB,oBAAC,QAAgB,yBAAe,KAAK,KAA5B,KAA8B,CACxC,GACH,IACE;AAAA,OACN,IACE;AAAA,IAEJ,qBAAC,SAAI,WAAU,0CACb;AAAA,2BAAC,SAAI,WAAU,qCACb;AAAA,4BAAC,UAAK,WAAU,yBAAyB,YAAE,gDAAgD,2BAA2B,EAAE,OAAO,MAAM,OAAO,CAAC,GAAE;AAAA,QAC/I,oBAAC,UAAK,WAAW,eAAe,WAAW,KAAK,IAAK,qBAAW,MAAK;AAAA,SACvE;AAAA,MACA,qBAAC,SAAI,WAAU,0CACb;AAAA,4BAAC,UAAK,WAAU,yBAAyB,YAAE,sDAAsD,iCAAiC,EAAE,OAAO,YAAY,OAAO,CAAC,GAAE;AAAA,QACjK,oBAAC,UAAK,WAAW,eAAe,UAAU,KAAK,IAAK,oBAAU,MAAK;AAAA,SACrE;AAAA,OACF;AAAA,IAEA,qBAAC,OAAE,WAAU,
|
|
4
|
+
"sourcesContent": ["\"use client\"\nimport * as React from 'react'\nimport type { InjectionWidgetComponentProps } from '@open-mercato/shared/modules/widgets/injection'\nimport { subscribeProductSeoValidation } from './state'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\n\ntype SeoData = {\n title?: string | null\n name?: string | null\n description?: string | null\n}\n\ntype ValidationState = { ok: boolean; issues: string[]; message?: string }\n\ntype IssueKey = 'addTitle' | 'titleTooShort' | 'titleTooLong' | 'addDescription' | 'descriptionTooShort'\n\nfunction computeIssueKeys(title: string, description: string): IssueKey[] {\n const issues: IssueKey[] = []\n if (!title) {\n issues.push('addTitle')\n } else {\n if (title.length < 10) issues.push('titleTooShort')\n if (title.length > 60) issues.push('titleTooLong')\n }\n if (!description) {\n issues.push('addDescription')\n } else if (description.length < 50) {\n issues.push('descriptionTooShort')\n }\n return issues\n}\n\nexport default function ProductSeoWidget({ data }: InjectionWidgetComponentProps<unknown, SeoData>) {\n const t = useT()\n const title = (data?.title || data?.name || '') ?? ''\n const description = data?.description ?? ''\n const baselineIssueKeys = React.useMemo(() => computeIssueKeys(title, description), [title, description])\n const [validation, setValidation] = React.useState<ValidationState>({ ok: baselineIssueKeys.length === 0, issues: baselineIssueKeys })\n\n React.useEffect(() => {\n setValidation({ ok: baselineIssueKeys.length === 0, issues: baselineIssueKeys })\n }, [baselineIssueKeys])\n\n React.useEffect(() => {\n return subscribeProductSeoValidation((payload) => {\n setValidation({\n ok: payload.ok,\n issues: payload.issues,\n message: payload.message,\n })\n })\n }, [])\n\n const titleScore = React.useMemo(() => {\n if (!title) return { text: t('catalog.products.create.seoWidget.missing', 'Missing'), color: 'text-red-600', bg: 'bg-red-50', border: 'border-red-200' }\n if (title.length < 10) return { text: t('catalog.products.create.seoWidget.tooShort', 'Too short'), color: 'text-amber-600', bg: 'bg-amber-50', border: 'border-amber-200' }\n if (title.length > 60) return { text: t('catalog.products.create.seoWidget.tooLong', 'Too long'), color: 'text-amber-600', bg: 'bg-amber-50', border: 'border-amber-200' }\n return { text: t('catalog.products.create.seoWidget.good', 'Good'), color: 'text-green-600', bg: 'bg-green-50', border: 'border-green-200' }\n }, [title, t])\n\n const descScore = React.useMemo(() => {\n if (!description) return { text: t('catalog.products.create.seoWidget.missing', 'Missing'), color: 'text-red-600', bg: 'bg-red-50', border: 'border-red-200' }\n if (description.length < 50) return { text: t('catalog.products.create.seoWidget.tooShort', 'Too short'), color: 'text-amber-600', bg: 'bg-amber-50', border: 'border-amber-200' }\n return { text: t('catalog.products.create.seoWidget.good', 'Good'), color: 'text-green-600', bg: 'bg-green-50', border: 'border-green-200' }\n }, [description, t])\n\n const statusBadge = validation.ok ? (\n <span className=\"rounded-full bg-emerald-50 px-2 py-0.5 text-xs font-medium text-emerald-700 border border-emerald-200\">\n {t('catalog.products.create.seoWidget.ready', 'Ready')}\n </span>\n ) : (\n <span className=\"rounded-full bg-amber-50 px-2 py-0.5 text-xs font-medium text-amber-700 border border-amber-200\">\n {t('catalog.products.create.seoWidget.needsAttention', 'Needs attention')}\n </span>\n )\n\n const translateIssue = (issueKey: string): string => {\n return t(`catalog.products.create.seoWidget.issues.${issueKey}`, issueKey)\n }\n\n return (\n <div className=\"space-y-3 rounded-lg border bg-card p-4 shadow-sm\">\n <div className=\"flex items-start justify-between gap-3\">\n <div>\n <div className=\"text-sm font-semibold text-foreground\">{t('catalog.products.create.seoWidget.title', 'SEO Optimization')}</div>\n <p className=\"text-xs text-muted-foreground\">{t('catalog.products.create.seoWidget.hint', 'Keep titles 10\u201360 chars and descriptions 50+ chars.')}</p>\n </div>\n {statusBadge}\n </div>\n\n {validation.message || validation.issues.length ? (\n <div className={`rounded-md border p-3 text-xs ${validation.ok ? 'border-emerald-200 bg-emerald-50 text-emerald-800' : 'border-amber-200 bg-amber-50 text-amber-900'}`}>\n {validation.message ? <div className=\"font-medium\">{validation.message}</div> : null}\n {validation.issues.length ? (\n <ul className=\"ml-4 list-disc space-y-1 pt-1\">\n {validation.issues.map((issue) => (\n <li key={issue}>{translateIssue(issue)}</li>\n ))}\n </ul>\n ) : null}\n </div>\n ) : null}\n\n <div className=\"rounded border bg-muted/30 p-3 text-sm\">\n <div className=\"flex items-center justify-between\">\n <span className=\"text-muted-foreground\">{t('catalog.products.create.seoWidget.titleLabel', 'Title ({{count}} chars)', { count: title.length })}</span>\n <span className={`font-medium ${titleScore.color}`}>{titleScore.text}</span>\n </div>\n <div className=\"mt-2 flex items-center justify-between\">\n <span className=\"text-muted-foreground\">{t('catalog.products.create.seoWidget.descriptionLabel', 'Description ({{count}} chars)', { count: description.length })}</span>\n <span className={`font-medium ${descScore.color}`}>{descScore.text}</span>\n </div>\n </div>\n\n <p className=\"text-overline text-muted-foreground\">\n {t('catalog.products.create.seoWidget.footer', 'Example widget powered by the injection system.')}{' '}\n <a className=\"text-primary underline\" href=\"/docs/framework/admin-ui/widget-injection\" target=\"_blank\" rel=\"noreferrer\">\n {t('catalog.products.create.seoWidget.learnMore', 'Learn how to build your own')}\n </a>\n .\n </p>\n </div>\n )\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -252,7 +252,7 @@ function CurrencyFetchingConfig() {
|
|
|
252
252
|
},
|
|
253
253
|
config.id
|
|
254
254
|
)),
|
|
255
|
-
configs.length === 0 && /* @__PURE__ */ jsxs("div", { className: "rounded border bg-background/
|
|
255
|
+
configs.length === 0 && /* @__PURE__ */ jsxs("div", { className: "rounded border bg-background/80 p-6 text-center", children: [
|
|
256
256
|
/* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: t("currencies.fetch.no_providers") }),
|
|
257
257
|
/* @__PURE__ */ jsx(
|
|
258
258
|
Button,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/modules/currencies/components/CurrencyFetchingConfig.tsx"],
|
|
4
|
-
"sourcesContent": ["'use client'\n\nimport { useState, useEffect, useCallback, useMemo } from 'react'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { apiCall } from '@open-mercato/ui/backend/utils/apiCall'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { Spinner } from '@open-mercato/ui/primitives/spinner'\nimport { Switch } from '@open-mercato/ui/primitives/switch'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\n\ninterface FetchConfig {\n id: string\n provider: string\n isEnabled: boolean\n syncTime: string | null\n lastSyncAt: string | null\n lastSyncStatus: string | null\n lastSyncMessage: string | null\n lastSyncCount: number | null\n}\n\nexport default function CurrencyFetchingConfig() {\n const t = useT()\n const [configs, setConfigs] = useState<FetchConfig[]>([])\n const [loading, setLoading] = useState(true)\n const [fetching, setFetching] = useState<string | null>(null)\n const [initializing, setInitializing] = useState(false)\n\n // Available providers that should be configured\n const availableProviders = useMemo(() => ['NBP', 'Raiffeisen Bank Polska'], [])\n\n const createProviderConfig = useCallback(async (provider: string) => {\n try {\n await apiCall('/api/currencies/fetch-configs', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n provider,\n isEnabled: false,\n syncTime: '09:00',\n }),\n })\n } catch (err: any) {\n // Ignore errors for duplicate providers\n if (!err.message?.includes('already configured')) {\n throw err\n }\n }\n }, [])\n\n const initializeMissingProviders = useCallback(async (providers: string[]) => {\n setInitializing(true)\n try {\n for (const provider of providers) {\n await createProviderConfig(provider)\n }\n // Reload configs after initialization\n const { result } = await apiCall('/api/currencies/fetch-configs')\n if (result?.configs) {\n setConfigs(result.configs as FetchConfig[])\n }\n } catch (err: any) {\n console.error('Failed to initialize providers:', err)\n } finally {\n setInitializing(false)\n }\n }, [createProviderConfig])\n\n const loadConfigs = useCallback(async () => {\n try {\n const { result } = await apiCall('/api/currencies/fetch-configs')\n\n if (result?.configs) {\n const existingConfigs = result.configs as FetchConfig[]\n setConfigs(existingConfigs)\n\n // Check if we need to initialize any missing providers\n const existingProviders = existingConfigs.map((c) => c.provider)\n const missingProviders = availableProviders.filter(\n (p) => !existingProviders.includes(p)\n )\n\n // Auto-initialize missing providers\n if (missingProviders.length > 0) {\n await initializeMissingProviders(missingProviders)\n }\n }\n } catch (err: any) {\n flash(err.message || t('currencies.fetch.error_load_configs'), 'error')\n } finally {\n setLoading(false)\n }\n }, [availableProviders, initializeMissingProviders, t])\n\n const toggleEnabled = useCallback(async (configId: string, currentValue: boolean) => {\n try {\n const { result } = await apiCall('/api/currencies/fetch-configs', {\n method: 'PUT',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n id: configId,\n isEnabled: !currentValue,\n }),\n })\n\n if (result?.config) {\n setConfigs((prev) =>\n prev.map((c) => (c.id === configId ? (result.config as FetchConfig) : c))\n )\n flash(\n currentValue\n ? t('currencies.fetch.provider_disabled')\n : t('currencies.fetch.provider_enabled'),\n 'success'\n )\n }\n } catch (err: any) {\n flash(err.message || t('currencies.fetch.error_update_config'), 'error')\n }\n }, [t])\n\n const updateSyncTime = useCallback(async (configId: string, syncTime: string) => {\n try {\n const { result } = await apiCall('/api/currencies/fetch-configs', {\n method: 'PUT',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n id: configId,\n syncTime: syncTime || null,\n }),\n })\n\n if (result?.config) {\n setConfigs((prev) =>\n prev.map((c) => (c.id === configId ? (result.config as FetchConfig) : c))\n )\n }\n } catch (err: any) {\n flash(err.message || t('currencies.fetch.error_update_sync_time'), 'error')\n }\n }, [t])\n\n const fetchNow = useCallback(async (provider: string) => {\n setFetching(provider)\n\n try {\n const { result } = await apiCall('/api/currencies/fetch-rates', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n providers: [provider],\n }),\n })\n\n if (result) {\n const byProvider = result.byProvider as Record<string, { count: number; errors?: string[] }>\n const count = byProvider?.[provider]?.count || 0\n\n if (count === 0) {\n flash(t('currencies.fetch.sync_no_rates'), 'warning')\n } else {\n flash(`${t('currencies.fetch.sync_success')}: ${count} rates fetched`, 'success')\n }\n await loadConfigs()\n }\n } catch (err: any) {\n flash(err.message || t('currencies.fetch.sync_error'), 'error')\n } finally {\n setFetching(null)\n }\n }, [t, loadConfigs])\n\n useEffect(() => {\n loadConfigs()\n }, [loadConfigs])\n\n function formatLastSync(date: string | null): string {\n if (!date) return 'Never'\n return new Date(date).toLocaleString()\n }\n\n function getStatusBadge(status: string | null) {\n if (!status) return null\n\n const styles: Record<string, string> = {\n success: 'bg-green-100 text-green-800 px-2 py-1 rounded text-xs',\n error: 'bg-red-100 text-red-800 px-2 py-1 rounded text-xs',\n partial: 'bg-yellow-100 text-yellow-800 px-2 py-1 rounded text-xs',\n }\n\n return (\n <span className={styles[status] || styles.error}>\n {status}\n </span>\n )\n }\n\n function getProviderName(provider: string): string {\n if (provider === 'NBP') return t('currencies.fetch.provider_nbp')\n if (provider === 'Raiffeisen Bank Polska') return t('currencies.fetch.provider_raiffeisen')\n return provider\n }\n\n function getProviderDescription(provider: string): string {\n if (provider === 'NBP') {\n return t('currencies.fetch.provider_nbp_description')\n }\n if (provider === 'Raiffeisen Bank Polska') {\n return t('currencies.fetch.provider_raiffeisen_description')\n }\n return ''\n }\n\n if (loading || initializing) {\n return (\n <section className=\"space-y-3 rounded-lg border bg-background p-4\">\n <header className=\"space-y-2\">\n <h2 className=\"text-xl font-semibold\">{t('currencies.fetch.title')}</h2>\n <p className=\"text-sm text-muted-foreground\">\n {t('currencies.fetch.description')}\n </p>\n </header>\n <div className=\"flex items-center gap-2 text-sm text-muted-foreground\">\n <Spinner className=\"h-4 w-4\" />\n {initializing ? t('currencies.fetch.initializing_providers') : t('currencies.fetch.loading')}\n </div>\n </section>\n )\n }\n\n return (\n <section className=\"space-y-6 rounded-lg border bg-background p-4\">\n <header className=\"space-y-2\">\n <h2 className=\"text-xl font-semibold\">{t('currencies.fetch.title')}</h2>\n <p className=\"text-sm text-muted-foreground\">\n {t('currencies.fetch.description')}\n </p>\n </header>\n\n <div className=\"space-y-3\">\n {configs.map((config) => (\n <div\n key={config.id}\n className=\"rounded-lg border bg-card p-4\"\n >\n <div className=\"flex items-start justify-between gap-4\">\n <div className=\"flex-1\">\n <h3 className=\"text-sm font-medium\">\n {getProviderName(config.provider)}\n </h3>\n <p className=\"text-xs text-muted-foreground mt-0.5\">\n {getProviderDescription(config.provider)}\n </p>\n </div>\n\n <Switch\n checked={config.isEnabled}\n onCheckedChange={() => toggleEnabled(config.id, config.isEnabled)}\n />\n </div>\n\n {config.isEnabled && (\n <>\n <div className=\"flex items-baseline gap-3 flex-wrap mt-3\">\n <div className=\"flex items-baseline gap-2\">\n <label className=\"text-xs text-muted-foreground whitespace-nowrap\">\n {t('currencies.fetch.sync_time')}:\n </label>\n <input\n type=\"time\"\n value={config.syncTime || '09:00'}\n onChange={(e) => updateSyncTime(config.id, e.target.value)}\n className=\"rounded border bg-background px-2 py-1.5 text-sm\"\n />\n </div>\n\n <div className=\"flex items-baseline gap-2\">\n <label className=\"text-xs text-muted-foreground whitespace-nowrap\">\n {t('currencies.fetch.last_sync')}:\n </label>\n <span className=\"text-xs text-muted-foreground\">\n {formatLastSync(config.lastSyncAt)}\n </span>\n {getStatusBadge(config.lastSyncStatus)}\n </div>\n\n <Button\n onClick={() => fetchNow(config.provider)}\n disabled={fetching === config.provider}\n size=\"default\"\n className=\"ml-auto min-w-32\"\n >\n {fetching === config.provider ? (\n <div>\n <Spinner className=\"mr-1.5\" size='sm' />\n {t('currencies.fetch.fetching')}\n </div>\n ) : (\n t('currencies.fetch.fetch_now')\n )}\n </Button>\n </div>\n\n {config.lastSyncCount !== null && (\n <div className=\"text-xs text-muted-foreground mt-2\">\n {t('currencies.fetch.last_sync_count')}: <span className=\"font-medium\">{config.lastSyncCount}</span> rates\n </div>\n )}\n\n {config.lastSyncMessage && config.lastSyncStatus === 'error' && (\n <div className=\"rounded border border-red-200 bg-red-50 p-2 text-xs text-red-700 mt-2\">\n {config.lastSyncMessage}\n </div>\n )}\n </>\n )}\n </div>\n ))}\n\n {configs.length === 0 && (\n <div className=\"rounded border bg-background/70 p-6 text-center\">\n <p className=\"text-sm text-muted-foreground\">\n {t('currencies.fetch.no_providers')}\n </p>\n <Button\n onClick={() => initializeMissingProviders(availableProviders)}\n >\n {t('currencies.fetch.initialize_providers')}\n </Button>\n </div>\n )}\n </div>\n </section>\n )\n}\n"],
|
|
4
|
+
"sourcesContent": ["'use client'\n\nimport { useState, useEffect, useCallback, useMemo } from 'react'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { apiCall } from '@open-mercato/ui/backend/utils/apiCall'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { Spinner } from '@open-mercato/ui/primitives/spinner'\nimport { Switch } from '@open-mercato/ui/primitives/switch'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\n\ninterface FetchConfig {\n id: string\n provider: string\n isEnabled: boolean\n syncTime: string | null\n lastSyncAt: string | null\n lastSyncStatus: string | null\n lastSyncMessage: string | null\n lastSyncCount: number | null\n}\n\nexport default function CurrencyFetchingConfig() {\n const t = useT()\n const [configs, setConfigs] = useState<FetchConfig[]>([])\n const [loading, setLoading] = useState(true)\n const [fetching, setFetching] = useState<string | null>(null)\n const [initializing, setInitializing] = useState(false)\n\n // Available providers that should be configured\n const availableProviders = useMemo(() => ['NBP', 'Raiffeisen Bank Polska'], [])\n\n const createProviderConfig = useCallback(async (provider: string) => {\n try {\n await apiCall('/api/currencies/fetch-configs', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n provider,\n isEnabled: false,\n syncTime: '09:00',\n }),\n })\n } catch (err: any) {\n // Ignore errors for duplicate providers\n if (!err.message?.includes('already configured')) {\n throw err\n }\n }\n }, [])\n\n const initializeMissingProviders = useCallback(async (providers: string[]) => {\n setInitializing(true)\n try {\n for (const provider of providers) {\n await createProviderConfig(provider)\n }\n // Reload configs after initialization\n const { result } = await apiCall('/api/currencies/fetch-configs')\n if (result?.configs) {\n setConfigs(result.configs as FetchConfig[])\n }\n } catch (err: any) {\n console.error('Failed to initialize providers:', err)\n } finally {\n setInitializing(false)\n }\n }, [createProviderConfig])\n\n const loadConfigs = useCallback(async () => {\n try {\n const { result } = await apiCall('/api/currencies/fetch-configs')\n\n if (result?.configs) {\n const existingConfigs = result.configs as FetchConfig[]\n setConfigs(existingConfigs)\n\n // Check if we need to initialize any missing providers\n const existingProviders = existingConfigs.map((c) => c.provider)\n const missingProviders = availableProviders.filter(\n (p) => !existingProviders.includes(p)\n )\n\n // Auto-initialize missing providers\n if (missingProviders.length > 0) {\n await initializeMissingProviders(missingProviders)\n }\n }\n } catch (err: any) {\n flash(err.message || t('currencies.fetch.error_load_configs'), 'error')\n } finally {\n setLoading(false)\n }\n }, [availableProviders, initializeMissingProviders, t])\n\n const toggleEnabled = useCallback(async (configId: string, currentValue: boolean) => {\n try {\n const { result } = await apiCall('/api/currencies/fetch-configs', {\n method: 'PUT',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n id: configId,\n isEnabled: !currentValue,\n }),\n })\n\n if (result?.config) {\n setConfigs((prev) =>\n prev.map((c) => (c.id === configId ? (result.config as FetchConfig) : c))\n )\n flash(\n currentValue\n ? t('currencies.fetch.provider_disabled')\n : t('currencies.fetch.provider_enabled'),\n 'success'\n )\n }\n } catch (err: any) {\n flash(err.message || t('currencies.fetch.error_update_config'), 'error')\n }\n }, [t])\n\n const updateSyncTime = useCallback(async (configId: string, syncTime: string) => {\n try {\n const { result } = await apiCall('/api/currencies/fetch-configs', {\n method: 'PUT',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n id: configId,\n syncTime: syncTime || null,\n }),\n })\n\n if (result?.config) {\n setConfigs((prev) =>\n prev.map((c) => (c.id === configId ? (result.config as FetchConfig) : c))\n )\n }\n } catch (err: any) {\n flash(err.message || t('currencies.fetch.error_update_sync_time'), 'error')\n }\n }, [t])\n\n const fetchNow = useCallback(async (provider: string) => {\n setFetching(provider)\n\n try {\n const { result } = await apiCall('/api/currencies/fetch-rates', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n providers: [provider],\n }),\n })\n\n if (result) {\n const byProvider = result.byProvider as Record<string, { count: number; errors?: string[] }>\n const count = byProvider?.[provider]?.count || 0\n\n if (count === 0) {\n flash(t('currencies.fetch.sync_no_rates'), 'warning')\n } else {\n flash(`${t('currencies.fetch.sync_success')}: ${count} rates fetched`, 'success')\n }\n await loadConfigs()\n }\n } catch (err: any) {\n flash(err.message || t('currencies.fetch.sync_error'), 'error')\n } finally {\n setFetching(null)\n }\n }, [t, loadConfigs])\n\n useEffect(() => {\n loadConfigs()\n }, [loadConfigs])\n\n function formatLastSync(date: string | null): string {\n if (!date) return 'Never'\n return new Date(date).toLocaleString()\n }\n\n function getStatusBadge(status: string | null) {\n if (!status) return null\n\n const styles: Record<string, string> = {\n success: 'bg-green-100 text-green-800 px-2 py-1 rounded text-xs',\n error: 'bg-red-100 text-red-800 px-2 py-1 rounded text-xs',\n partial: 'bg-yellow-100 text-yellow-800 px-2 py-1 rounded text-xs',\n }\n\n return (\n <span className={styles[status] || styles.error}>\n {status}\n </span>\n )\n }\n\n function getProviderName(provider: string): string {\n if (provider === 'NBP') return t('currencies.fetch.provider_nbp')\n if (provider === 'Raiffeisen Bank Polska') return t('currencies.fetch.provider_raiffeisen')\n return provider\n }\n\n function getProviderDescription(provider: string): string {\n if (provider === 'NBP') {\n return t('currencies.fetch.provider_nbp_description')\n }\n if (provider === 'Raiffeisen Bank Polska') {\n return t('currencies.fetch.provider_raiffeisen_description')\n }\n return ''\n }\n\n if (loading || initializing) {\n return (\n <section className=\"space-y-3 rounded-lg border bg-background p-4\">\n <header className=\"space-y-2\">\n <h2 className=\"text-xl font-semibold\">{t('currencies.fetch.title')}</h2>\n <p className=\"text-sm text-muted-foreground\">\n {t('currencies.fetch.description')}\n </p>\n </header>\n <div className=\"flex items-center gap-2 text-sm text-muted-foreground\">\n <Spinner className=\"h-4 w-4\" />\n {initializing ? t('currencies.fetch.initializing_providers') : t('currencies.fetch.loading')}\n </div>\n </section>\n )\n }\n\n return (\n <section className=\"space-y-6 rounded-lg border bg-background p-4\">\n <header className=\"space-y-2\">\n <h2 className=\"text-xl font-semibold\">{t('currencies.fetch.title')}</h2>\n <p className=\"text-sm text-muted-foreground\">\n {t('currencies.fetch.description')}\n </p>\n </header>\n\n <div className=\"space-y-3\">\n {configs.map((config) => (\n <div\n key={config.id}\n className=\"rounded-lg border bg-card p-4\"\n >\n <div className=\"flex items-start justify-between gap-4\">\n <div className=\"flex-1\">\n <h3 className=\"text-sm font-medium\">\n {getProviderName(config.provider)}\n </h3>\n <p className=\"text-xs text-muted-foreground mt-0.5\">\n {getProviderDescription(config.provider)}\n </p>\n </div>\n\n <Switch\n checked={config.isEnabled}\n onCheckedChange={() => toggleEnabled(config.id, config.isEnabled)}\n />\n </div>\n\n {config.isEnabled && (\n <>\n <div className=\"flex items-baseline gap-3 flex-wrap mt-3\">\n <div className=\"flex items-baseline gap-2\">\n <label className=\"text-xs text-muted-foreground whitespace-nowrap\">\n {t('currencies.fetch.sync_time')}:\n </label>\n <input\n type=\"time\"\n value={config.syncTime || '09:00'}\n onChange={(e) => updateSyncTime(config.id, e.target.value)}\n className=\"rounded border bg-background px-2 py-1.5 text-sm\"\n />\n </div>\n\n <div className=\"flex items-baseline gap-2\">\n <label className=\"text-xs text-muted-foreground whitespace-nowrap\">\n {t('currencies.fetch.last_sync')}:\n </label>\n <span className=\"text-xs text-muted-foreground\">\n {formatLastSync(config.lastSyncAt)}\n </span>\n {getStatusBadge(config.lastSyncStatus)}\n </div>\n\n <Button\n onClick={() => fetchNow(config.provider)}\n disabled={fetching === config.provider}\n size=\"default\"\n className=\"ml-auto min-w-32\"\n >\n {fetching === config.provider ? (\n <div>\n <Spinner className=\"mr-1.5\" size='sm' />\n {t('currencies.fetch.fetching')}\n </div>\n ) : (\n t('currencies.fetch.fetch_now')\n )}\n </Button>\n </div>\n\n {config.lastSyncCount !== null && (\n <div className=\"text-xs text-muted-foreground mt-2\">\n {t('currencies.fetch.last_sync_count')}: <span className=\"font-medium\">{config.lastSyncCount}</span> rates\n </div>\n )}\n\n {config.lastSyncMessage && config.lastSyncStatus === 'error' && (\n <div className=\"rounded border border-red-200 bg-red-50 p-2 text-xs text-red-700 mt-2\">\n {config.lastSyncMessage}\n </div>\n )}\n </>\n )}\n </div>\n ))}\n\n {configs.length === 0 && (\n <div className=\"rounded border bg-background/80 p-6 text-center\">\n <p className=\"text-sm text-muted-foreground\">\n {t('currencies.fetch.no_providers')}\n </p>\n <Button\n onClick={() => initializeMissingProviders(availableProviders)}\n >\n {t('currencies.fetch.initialize_providers')}\n </Button>\n </div>\n )}\n </div>\n </section>\n )\n}\n"],
|
|
5
5
|
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -112,12 +112,12 @@ function CustomerRolesPage() {
|
|
|
112
112
|
{
|
|
113
113
|
accessorKey: "isSystem",
|
|
114
114
|
header: t("customer_accounts.admin.roles.columns.isSystem", "System"),
|
|
115
|
-
cell: ({ row }) => row.original.isSystem ? /* @__PURE__ */ jsx("span", { className: "inline-flex items-center rounded-full bg-
|
|
115
|
+
cell: ({ row }) => row.original.isSystem ? /* @__PURE__ */ jsx("span", { className: "inline-flex items-center rounded-full bg-status-info-bg px-2 py-0.5 text-xs font-medium text-status-info-text", children: t("customer_accounts.admin.roles.system", "System") }) : null
|
|
116
116
|
},
|
|
117
117
|
{
|
|
118
118
|
accessorKey: "isDefault",
|
|
119
119
|
header: t("customer_accounts.admin.roles.columns.isDefault", "Default"),
|
|
120
|
-
cell: ({ row }) => row.original.isDefault ? /* @__PURE__ */ jsx("span", { className: "inline-flex items-center rounded-full bg-
|
|
120
|
+
cell: ({ row }) => row.original.isDefault ? /* @__PURE__ */ jsx("span", { className: "inline-flex items-center rounded-full bg-status-success-bg px-2 py-0.5 text-xs font-medium text-status-success-text", children: t("customer_accounts.admin.roles.default", "Default") }) : null
|
|
121
121
|
},
|
|
122
122
|
{
|
|
123
123
|
accessorKey: "customerAssignable",
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../../src/modules/customer_accounts/backend/customer_accounts/roles/page.tsx"],
|
|
4
|
-
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport Link from 'next/link'\nimport { useRouter } from 'next/navigation'\nimport { Page, PageBody } from '@open-mercato/ui/backend/Page'\nimport { DataTable } from '@open-mercato/ui/backend/DataTable'\nimport type { ColumnDef } from '@tanstack/react-table'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { RowActions } from '@open-mercato/ui/backend/RowActions'\nimport { apiCall, readApiResultOrThrow } from '@open-mercato/ui/backend/utils/apiCall'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { useConfirmDialog } from '@open-mercato/ui/backend/confirm-dialog'\n\ntype RoleRow = {\n id: string\n name: string\n slug: string\n description: string | null\n isSystem: boolean\n isDefault: boolean\n customerAssignable: boolean\n createdAt: string\n}\n\ntype RolesResponse = {\n items?: RoleRow[]\n total?: number\n totalPages?: number\n}\n\nexport default function CustomerRolesPage() {\n const { confirm, ConfirmDialogElement } = useConfirmDialog()\n const t = useT()\n const router = useRouter()\n const [rows, setRows] = React.useState<RoleRow[]>([])\n const [page, setPage] = React.useState(1)\n const [pageSize] = React.useState(50)\n const [total, setTotal] = React.useState(0)\n const [totalPages, setTotalPages] = React.useState(1)\n const [search, setSearch] = React.useState('')\n const [isLoading, setIsLoading] = React.useState(true)\n const [reloadToken, setReloadToken] = React.useState(0)\n\n const queryParams = React.useMemo(() => {\n const params = new URLSearchParams()\n params.set('page', String(page))\n params.set('pageSize', String(pageSize))\n if (search.trim()) params.set('search', search.trim())\n return params.toString()\n }, [page, pageSize, search])\n\n React.useEffect(() => {\n let cancelled = false\n async function load() {\n setIsLoading(true)\n try {\n const fallback: RolesResponse = { items: [], total: 0, totalPages: 1 }\n const payload = await readApiResultOrThrow<RolesResponse>(\n `/api/customer_accounts/admin/roles?${queryParams}`,\n undefined,\n { errorMessage: t('customer_accounts.admin.roles.error.load', 'Failed to load roles'), fallback },\n )\n if (cancelled) return\n const items = Array.isArray(payload?.items) ? payload.items : []\n setRows(items)\n setTotal(typeof payload?.total === 'number' ? payload.total : items.length)\n setTotalPages(typeof payload?.totalPages === 'number' ? payload.totalPages : 1)\n } catch (err) {\n if (!cancelled) {\n const message = err instanceof Error ? err.message : t('customer_accounts.admin.roles.error.load', 'Failed to load roles')\n flash(message, 'error')\n }\n } finally {\n if (!cancelled) setIsLoading(false)\n }\n }\n load()\n return () => { cancelled = true }\n }, [queryParams, reloadToken, t])\n\n const handleDelete = React.useCallback(async (role: RoleRow) => {\n if (role.isSystem) {\n flash(t('customer_accounts.admin.roles.error.deleteSystem', 'System roles cannot be deleted'), 'error')\n return\n }\n const confirmed = await confirm({\n title: t('customer_accounts.admin.roles.confirm.delete', 'Delete role \"{{name}}\"?', { name: role.name }),\n variant: 'destructive',\n })\n if (!confirmed) return\n try {\n const call = await apiCall(\n `/api/customer_accounts/admin/roles/${encodeURIComponent(role.id)}`,\n { method: 'DELETE' },\n )\n if (!call.ok) {\n flash(t('customer_accounts.admin.roles.error.delete', 'Failed to delete role'), 'error')\n return\n }\n flash(t('customer_accounts.admin.roles.flash.deleted', 'Role deleted'), 'success')\n setReloadToken((token) => token + 1)\n } catch (err) {\n const message = err instanceof Error ? err.message : t('customer_accounts.admin.roles.error.delete', 'Failed to delete role')\n flash(message, 'error')\n }\n }, [confirm, t])\n\n const columns = React.useMemo<ColumnDef<RoleRow>[]>(() => [\n {\n accessorKey: 'name',\n header: t('customer_accounts.admin.roles.columns.name', 'Name'),\n cell: ({ row }) => (\n <Link\n href={`/backend/customer_accounts/roles/${row.original.id}`}\n className=\"font-medium hover:underline\"\n >\n {row.original.name}\n </Link>\n ),\n },\n {\n accessorKey: 'slug',\n header: t('customer_accounts.admin.roles.columns.slug', 'Slug'),\n cell: ({ row }) => (\n <code className=\"rounded bg-muted px-1.5 py-0.5 text-xs\">{row.original.slug}</code>\n ),\n },\n {\n accessorKey: 'description',\n header: t('customer_accounts.admin.roles.columns.description', 'Description'),\n cell: ({ row }) => row.original.description || <span className=\"text-muted-foreground\">-</span>,\n },\n {\n accessorKey: 'isSystem',\n header: t('customer_accounts.admin.roles.columns.isSystem', 'System'),\n cell: ({ row }) => (\n row.original.isSystem ? (\n <span className=\"inline-flex items-center rounded-full bg-
|
|
5
|
-
"mappings": "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
|
|
4
|
+
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport Link from 'next/link'\nimport { useRouter } from 'next/navigation'\nimport { Page, PageBody } from '@open-mercato/ui/backend/Page'\nimport { DataTable } from '@open-mercato/ui/backend/DataTable'\nimport type { ColumnDef } from '@tanstack/react-table'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { RowActions } from '@open-mercato/ui/backend/RowActions'\nimport { apiCall, readApiResultOrThrow } from '@open-mercato/ui/backend/utils/apiCall'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { useConfirmDialog } from '@open-mercato/ui/backend/confirm-dialog'\n\ntype RoleRow = {\n id: string\n name: string\n slug: string\n description: string | null\n isSystem: boolean\n isDefault: boolean\n customerAssignable: boolean\n createdAt: string\n}\n\ntype RolesResponse = {\n items?: RoleRow[]\n total?: number\n totalPages?: number\n}\n\nexport default function CustomerRolesPage() {\n const { confirm, ConfirmDialogElement } = useConfirmDialog()\n const t = useT()\n const router = useRouter()\n const [rows, setRows] = React.useState<RoleRow[]>([])\n const [page, setPage] = React.useState(1)\n const [pageSize] = React.useState(50)\n const [total, setTotal] = React.useState(0)\n const [totalPages, setTotalPages] = React.useState(1)\n const [search, setSearch] = React.useState('')\n const [isLoading, setIsLoading] = React.useState(true)\n const [reloadToken, setReloadToken] = React.useState(0)\n\n const queryParams = React.useMemo(() => {\n const params = new URLSearchParams()\n params.set('page', String(page))\n params.set('pageSize', String(pageSize))\n if (search.trim()) params.set('search', search.trim())\n return params.toString()\n }, [page, pageSize, search])\n\n React.useEffect(() => {\n let cancelled = false\n async function load() {\n setIsLoading(true)\n try {\n const fallback: RolesResponse = { items: [], total: 0, totalPages: 1 }\n const payload = await readApiResultOrThrow<RolesResponse>(\n `/api/customer_accounts/admin/roles?${queryParams}`,\n undefined,\n { errorMessage: t('customer_accounts.admin.roles.error.load', 'Failed to load roles'), fallback },\n )\n if (cancelled) return\n const items = Array.isArray(payload?.items) ? payload.items : []\n setRows(items)\n setTotal(typeof payload?.total === 'number' ? payload.total : items.length)\n setTotalPages(typeof payload?.totalPages === 'number' ? payload.totalPages : 1)\n } catch (err) {\n if (!cancelled) {\n const message = err instanceof Error ? err.message : t('customer_accounts.admin.roles.error.load', 'Failed to load roles')\n flash(message, 'error')\n }\n } finally {\n if (!cancelled) setIsLoading(false)\n }\n }\n load()\n return () => { cancelled = true }\n }, [queryParams, reloadToken, t])\n\n const handleDelete = React.useCallback(async (role: RoleRow) => {\n if (role.isSystem) {\n flash(t('customer_accounts.admin.roles.error.deleteSystem', 'System roles cannot be deleted'), 'error')\n return\n }\n const confirmed = await confirm({\n title: t('customer_accounts.admin.roles.confirm.delete', 'Delete role \"{{name}}\"?', { name: role.name }),\n variant: 'destructive',\n })\n if (!confirmed) return\n try {\n const call = await apiCall(\n `/api/customer_accounts/admin/roles/${encodeURIComponent(role.id)}`,\n { method: 'DELETE' },\n )\n if (!call.ok) {\n flash(t('customer_accounts.admin.roles.error.delete', 'Failed to delete role'), 'error')\n return\n }\n flash(t('customer_accounts.admin.roles.flash.deleted', 'Role deleted'), 'success')\n setReloadToken((token) => token + 1)\n } catch (err) {\n const message = err instanceof Error ? err.message : t('customer_accounts.admin.roles.error.delete', 'Failed to delete role')\n flash(message, 'error')\n }\n }, [confirm, t])\n\n const columns = React.useMemo<ColumnDef<RoleRow>[]>(() => [\n {\n accessorKey: 'name',\n header: t('customer_accounts.admin.roles.columns.name', 'Name'),\n cell: ({ row }) => (\n <Link\n href={`/backend/customer_accounts/roles/${row.original.id}`}\n className=\"font-medium hover:underline\"\n >\n {row.original.name}\n </Link>\n ),\n },\n {\n accessorKey: 'slug',\n header: t('customer_accounts.admin.roles.columns.slug', 'Slug'),\n cell: ({ row }) => (\n <code className=\"rounded bg-muted px-1.5 py-0.5 text-xs\">{row.original.slug}</code>\n ),\n },\n {\n accessorKey: 'description',\n header: t('customer_accounts.admin.roles.columns.description', 'Description'),\n cell: ({ row }) => row.original.description || <span className=\"text-muted-foreground\">-</span>,\n },\n {\n accessorKey: 'isSystem',\n header: t('customer_accounts.admin.roles.columns.isSystem', 'System'),\n cell: ({ row }) => (\n row.original.isSystem ? (\n <span className=\"inline-flex items-center rounded-full bg-status-info-bg px-2 py-0.5 text-xs font-medium text-status-info-text\">\n {t('customer_accounts.admin.roles.system', 'System')}\n </span>\n ) : null\n ),\n },\n {\n accessorKey: 'isDefault',\n header: t('customer_accounts.admin.roles.columns.isDefault', 'Default'),\n cell: ({ row }) => (\n row.original.isDefault ? (\n <span className=\"inline-flex items-center rounded-full bg-status-success-bg px-2 py-0.5 text-xs font-medium text-status-success-text\">\n {t('customer_accounts.admin.roles.default', 'Default')}\n </span>\n ) : null\n ),\n },\n {\n accessorKey: 'customerAssignable',\n header: t('customer_accounts.admin.roles.columns.customerAssignable', 'Self-assignable'),\n cell: ({ row }) => (\n row.original.customerAssignable ? (\n <span className=\"inline-flex items-center rounded-full bg-purple-100 px-2 py-0.5 text-xs font-medium text-purple-800 dark:bg-purple-900 dark:text-purple-200\">\n {t('customer_accounts.admin.roles.assignable', 'Yes')}\n </span>\n ) : (\n <span className=\"text-muted-foreground text-sm\">\n {t('customer_accounts.admin.roles.notAssignable', 'No')}\n </span>\n )\n ),\n },\n ], [t])\n\n return (\n <Page>\n <PageBody>\n <DataTable<RoleRow>\n title={t('customer_accounts.admin.roles.title', 'Customer Roles')}\n actions={(\n <Button asChild>\n <Link href=\"/backend/customer_accounts/roles/create\">\n {t('customer_accounts.admin.roles.actions.create', 'Create Role')}\n </Link>\n </Button>\n )}\n columns={columns}\n data={rows}\n searchValue={search}\n onSearchChange={(value) => { setSearch(value); setPage(1) }}\n searchPlaceholder={t('customer_accounts.admin.roles.searchPlaceholder', 'Search roles...')}\n perspective={{ tableId: 'customer_accounts.admin.roles' }}\n onRowClick={(row) => router.push(`/backend/customer_accounts/roles/${row.id}`)}\n rowActions={(row) => (\n <RowActions\n items={[\n {\n id: 'edit',\n label: t('customer_accounts.admin.roles.actions.edit', 'Edit'),\n onSelect: () => { router.push(`/backend/customer_accounts/roles/${row.id}`) },\n },\n ...(!row.isSystem ? [{\n id: 'delete',\n label: t('customer_accounts.admin.roles.actions.delete', 'Delete'),\n destructive: true,\n onSelect: () => { void handleDelete(row) },\n }] : []),\n ]}\n />\n )}\n pagination={{ page, pageSize, total, totalPages, onPageChange: setPage }}\n isLoading={isLoading}\n />\n </PageBody>\n {ConfirmDialogElement}\n </Page>\n )\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -436,10 +436,10 @@ function CustomerUserDetailPage({ params }) {
|
|
|
436
436
|
deleteLabel: t("customer_accounts.admin.detail.actions.delete", "Delete")
|
|
437
437
|
}
|
|
438
438
|
),
|
|
439
|
-
/* @__PURE__ */ jsx("div", { className: "rounded-lg border border-
|
|
440
|
-
/* @__PURE__ */ jsx("h3", { className: "text-sm font-medium text-
|
|
441
|
-
/* @__PURE__ */ jsx("p", { className: "mt-1 text-sm text-
|
|
442
|
-
/* @__PURE__ */ jsx("p", { className: "mt-2 text-xs text-
|
|
439
|
+
/* @__PURE__ */ jsx("div", { className: "rounded-lg border border-status-info-border bg-status-info-bg p-4", children: /* @__PURE__ */ jsx("div", { className: "flex items-start gap-3", children: /* @__PURE__ */ jsxs("div", { className: "flex-1", children: [
|
|
440
|
+
/* @__PURE__ */ jsx("h3", { className: "text-sm font-medium text-status-info-text", children: t("customer_accounts.admin.detail.portalAccess.title", "Customer Portal Access") }),
|
|
441
|
+
/* @__PURE__ */ jsx("p", { className: "mt-1 text-sm text-status-info-text", children: t("customer_accounts.admin.detail.portalAccess.description", "This user can access the customer portal at the URL below. The portal provides self-service access to orders, invoices, quotes, and account management.") }),
|
|
442
|
+
/* @__PURE__ */ jsx("p", { className: "mt-2 text-xs text-status-info-text", children: t("customer_accounts.admin.detail.portalAccess.url", "Portal URL: {url}", {
|
|
443
443
|
url: `${typeof window !== "undefined" ? window.location.origin : ""}/[org-slug]/portal`
|
|
444
444
|
}) })
|
|
445
445
|
] }) }) }),
|
|
@@ -454,7 +454,7 @@ function CustomerUserDetailPage({ params }) {
|
|
|
454
454
|
/* @__PURE__ */ jsxs("div", { className: "flex justify-between items-center", children: [
|
|
455
455
|
/* @__PURE__ */ jsx("dt", { className: "text-muted-foreground", children: t("customer_accounts.admin.detail.fields.emailVerified", "Email Verified") }),
|
|
456
456
|
/* @__PURE__ */ jsxs("dd", { className: "flex items-center gap-2", children: [
|
|
457
|
-
/* @__PURE__ */ jsx("span", { className: `inline-flex items-center rounded-full px-2 py-0.5 text-xs font-medium ${data.emailVerifiedAt ? "bg-
|
|
457
|
+
/* @__PURE__ */ jsx("span", { className: `inline-flex items-center rounded-full px-2 py-0.5 text-xs font-medium ${data.emailVerifiedAt ? "bg-status-success-bg text-status-success-text" : "bg-status-warning-bg text-status-warning-text"}`, children: data.emailVerifiedAt ? t("customer_accounts.admin.verified", "Yes") : t("customer_accounts.admin.unverified", "No") }),
|
|
458
458
|
!data.emailVerifiedAt && /* @__PURE__ */ jsx(
|
|
459
459
|
Button,
|
|
460
460
|
{
|
|
@@ -637,10 +637,10 @@ function CustomerUserDetailPage({ params }) {
|
|
|
637
637
|
}
|
|
638
638
|
)
|
|
639
639
|
] }),
|
|
640
|
-
resetLinkUrl && /* @__PURE__ */ jsxs("div", { className: "rounded-md border border-
|
|
641
|
-
/* @__PURE__ */ jsx("p", { className: "mb-1.5 text-sm font-medium text-
|
|
640
|
+
resetLinkUrl && /* @__PURE__ */ jsxs("div", { className: "rounded-md border border-status-info-border bg-status-info-bg p-3", children: [
|
|
641
|
+
/* @__PURE__ */ jsx("p", { className: "mb-1.5 text-sm font-medium text-status-info-text", children: t("customer_accounts.admin.detail.sendResetLink.linkLabel", "Password reset link (valid for 60 minutes):") }),
|
|
642
642
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
643
|
-
/* @__PURE__ */ jsx("code", { className: "flex-1 break-all rounded bg-
|
|
643
|
+
/* @__PURE__ */ jsx("code", { className: "flex-1 break-all rounded bg-status-info-bg px-2 py-1 text-xs text-status-info-text", children: resetLinkUrl }),
|
|
644
644
|
/* @__PURE__ */ jsx(
|
|
645
645
|
Button,
|
|
646
646
|
{
|
|
@@ -655,7 +655,7 @@ function CustomerUserDetailPage({ params }) {
|
|
|
655
655
|
}
|
|
656
656
|
)
|
|
657
657
|
] }),
|
|
658
|
-
/* @__PURE__ */ jsx("p", { className: "mt-1.5 text-xs text-
|
|
658
|
+
/* @__PURE__ */ jsx("p", { className: "mt-1.5 text-xs text-status-info-text", children: t("customer_accounts.admin.detail.sendResetLink.hint", "Share this link with the customer to let them set a new password.") })
|
|
659
659
|
] })
|
|
660
660
|
] }),
|
|
661
661
|
/* @__PURE__ */ jsxs("div", { className: "rounded-lg border p-4 space-y-3", children: [
|