@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/translations/components/TranslationDrawerAction.tsx"],
|
|
4
|
-
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { Languages, X } from 'lucide-react'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { TranslationManager } from './TranslationManager'\n\nexport type TranslationDrawerActionConfig = {\n entityType: string\n recordId: string\n baseValues?: Record<string, unknown>\n}\n\nexport type TranslationDrawerActionProps = {\n config: TranslationDrawerActionConfig | null\n}\n\nexport function TranslationDrawerAction({ config }: TranslationDrawerActionProps) {\n const t = useT()\n const [open, setOpen] = React.useState(false)\n\n const canRender = Boolean(\n config?.entityType && config?.recordId && String(config.recordId).trim().length > 0,\n )\n\n React.useEffect(() => {\n if (!open || !canRender) return\n const prev = document.body.style.overflow\n document.body.style.overflow = 'hidden'\n return () => {\n document.body.style.overflow = prev\n }\n }, [canRender, open])\n\n React.useEffect(() => {\n if (!open || !canRender) return\n const handleKeyDown = (event: KeyboardEvent) => {\n if (event.key === 'Escape') {\n setOpen(false)\n }\n }\n document.addEventListener('keydown', handleKeyDown)\n return () => document.removeEventListener('keydown', handleKeyDown)\n }, [canRender, open])\n\n if (!canRender) return null\n\n return (\n <>\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon\"\n onClick={() => setOpen(true)}\n aria-label={t('translations.widgets.translationManager.fullManager', 'Translation manager')}\n title={t('translations.widgets.translationManager.fullManager', 'Translation manager')}\n >\n <Languages className=\"size-4\" />\n </Button>\n {open ? (\n <>\n <div\n className=\"fixed inset-0 z-
|
|
4
|
+
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { Languages, X } from 'lucide-react'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { TranslationManager } from './TranslationManager'\n\nexport type TranslationDrawerActionConfig = {\n entityType: string\n recordId: string\n baseValues?: Record<string, unknown>\n}\n\nexport type TranslationDrawerActionProps = {\n config: TranslationDrawerActionConfig | null\n}\n\nexport function TranslationDrawerAction({ config }: TranslationDrawerActionProps) {\n const t = useT()\n const [open, setOpen] = React.useState(false)\n\n const canRender = Boolean(\n config?.entityType && config?.recordId && String(config.recordId).trim().length > 0,\n )\n\n React.useEffect(() => {\n if (!open || !canRender) return\n const prev = document.body.style.overflow\n document.body.style.overflow = 'hidden'\n return () => {\n document.body.style.overflow = prev\n }\n }, [canRender, open])\n\n React.useEffect(() => {\n if (!open || !canRender) return\n const handleKeyDown = (event: KeyboardEvent) => {\n if (event.key === 'Escape') {\n setOpen(false)\n }\n }\n document.addEventListener('keydown', handleKeyDown)\n return () => document.removeEventListener('keydown', handleKeyDown)\n }, [canRender, open])\n\n if (!canRender) return null\n\n return (\n <>\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon\"\n onClick={() => setOpen(true)}\n aria-label={t('translations.widgets.translationManager.fullManager', 'Translation manager')}\n title={t('translations.widgets.translationManager.fullManager', 'Translation manager')}\n >\n <Languages className=\"size-4\" />\n </Button>\n {open ? (\n <>\n <div\n className=\"fixed inset-0 z-overlay bg-black/20\"\n onClick={() => setOpen(false)}\n aria-hidden=\"true\"\n />\n <div\n className=\"fixed right-0 top-0 z-modal h-full w-full max-w-4xl border-l bg-background shadow-lg\"\n role=\"dialog\"\n aria-modal=\"true\"\n aria-label={t('translations.widgets.translationManager.groupLabel', 'Translations')}\n >\n <div className=\"flex h-full flex-col\">\n <div className=\"flex items-start justify-between gap-3 border-b px-4 py-3\">\n <div className=\"space-y-1\">\n <h2 className=\"font-semibold\">\n {t('translations.widgets.translationManager.groupLabel', 'Translations')}\n </h2>\n <p className=\"text-sm text-muted-foreground\">\n {t('translations.widgets.translationManager.groupDescription', 'Manage translations for this record across supported locales.')}\n </p>\n </div>\n <Button\n variant=\"ghost\"\n size=\"icon\"\n onClick={() => setOpen(false)}\n aria-label={t('ui.dialog.close.ariaLabel', 'Close')}\n >\n <X className=\"size-4\" />\n </Button>\n </div>\n <div className=\"flex-1 overflow-y-auto px-4 py-4\">\n <TranslationManager\n mode=\"embedded\"\n entityType={config!.entityType}\n recordId={config!.recordId}\n baseValues={config!.baseValues}\n />\n </div>\n </div>\n </div>\n </>\n ) : null}\n </>\n )\n}\n"],
|
|
5
5
|
"mappings": ";AA0DQ,SAGA,UAHA,KAiBQ,YAjBR;AAxDR,YAAY,WAAW;AACvB,SAAS,WAAW,SAAS;AAC7B,SAAS,cAAc;AACvB,SAAS,YAAY;AACrB,SAAS,0BAA0B;AAY5B,SAAS,wBAAwB,EAAE,OAAO,GAAiC;AAChF,QAAM,IAAI,KAAK;AACf,QAAM,CAAC,MAAM,OAAO,IAAI,MAAM,SAAS,KAAK;AAE5C,QAAM,YAAY;AAAA,IAChB,QAAQ,cAAc,QAAQ,YAAY,OAAO,OAAO,QAAQ,EAAE,KAAK,EAAE,SAAS;AAAA,EACpF;AAEA,QAAM,UAAU,MAAM;AACpB,QAAI,CAAC,QAAQ,CAAC,UAAW;AACzB,UAAM,OAAO,SAAS,KAAK,MAAM;AACjC,aAAS,KAAK,MAAM,WAAW;AAC/B,WAAO,MAAM;AACX,eAAS,KAAK,MAAM,WAAW;AAAA,IACjC;AAAA,EACF,GAAG,CAAC,WAAW,IAAI,CAAC;AAEpB,QAAM,UAAU,MAAM;AACpB,QAAI,CAAC,QAAQ,CAAC,UAAW;AACzB,UAAM,gBAAgB,CAAC,UAAyB;AAC9C,UAAI,MAAM,QAAQ,UAAU;AAC1B,gBAAQ,KAAK;AAAA,MACf;AAAA,IACF;AACA,aAAS,iBAAiB,WAAW,aAAa;AAClD,WAAO,MAAM,SAAS,oBAAoB,WAAW,aAAa;AAAA,EACpE,GAAG,CAAC,WAAW,IAAI,CAAC;AAEpB,MAAI,CAAC,UAAW,QAAO;AAEvB,SACE,iCACE;AAAA;AAAA,MAAC;AAAA;AAAA,QACC,MAAK;AAAA,QACL,SAAQ;AAAA,QACR,MAAK;AAAA,QACL,SAAS,MAAM,QAAQ,IAAI;AAAA,QAC3B,cAAY,EAAE,uDAAuD,qBAAqB;AAAA,QAC1F,OAAO,EAAE,uDAAuD,qBAAqB;AAAA,QAErF,8BAAC,aAAU,WAAU,UAAS;AAAA;AAAA,IAChC;AAAA,IACC,OACC,iCACE;AAAA;AAAA,QAAC;AAAA;AAAA,UACC,WAAU;AAAA,UACV,SAAS,MAAM,QAAQ,KAAK;AAAA,UAC5B,eAAY;AAAA;AAAA,MACd;AAAA,MACA;AAAA,QAAC;AAAA;AAAA,UACC,WAAU;AAAA,UACV,MAAK;AAAA,UACL,cAAW;AAAA,UACX,cAAY,EAAE,sDAAsD,cAAc;AAAA,UAElF,+BAAC,SAAI,WAAU,wBACb;AAAA,iCAAC,SAAI,WAAU,6DACb;AAAA,mCAAC,SAAI,WAAU,aACb;AAAA,oCAAC,QAAG,WAAU,iBACX,YAAE,sDAAsD,cAAc,GACzE;AAAA,gBACA,oBAAC,OAAE,WAAU,iCACV,YAAE,4DAA4D,+DAA+D,GAChI;AAAA,iBACF;AAAA,cACA;AAAA,gBAAC;AAAA;AAAA,kBACC,SAAQ;AAAA,kBACR,MAAK;AAAA,kBACL,SAAS,MAAM,QAAQ,KAAK;AAAA,kBAC5B,cAAY,EAAE,6BAA6B,OAAO;AAAA,kBAElD,8BAAC,KAAE,WAAU,UAAS;AAAA;AAAA,cACxB;AAAA,eACF;AAAA,YACA,oBAAC,SAAI,WAAU,oCACb;AAAA,cAAC;AAAA;AAAA,gBACC,MAAK;AAAA,gBACL,YAAY,OAAQ;AAAA,gBACpB,UAAU,OAAQ;AAAA,gBAClB,YAAY,OAAQ;AAAA;AAAA,YACtB,GACF;AAAA,aACF;AAAA;AAAA,MACF;AAAA,OACF,IACE;AAAA,KACN;AAEJ;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -254,7 +254,7 @@ function TranslationManager({
|
|
|
254
254
|
}) });
|
|
255
255
|
const renderFieldTable = () => {
|
|
256
256
|
if (!entityType || !recordId) {
|
|
257
|
-
return /* @__PURE__ */ jsx("div", { className: "rounded border bg-background/
|
|
257
|
+
return /* @__PURE__ */ jsx("div", { className: "rounded border bg-background/80 p-4 text-sm text-muted-foreground", children: t("translations.manager.selectFirst", "Select an entity and record to manage translations.") });
|
|
258
258
|
}
|
|
259
259
|
if (loadingTranslation || loadingFieldDefs) {
|
|
260
260
|
return /* @__PURE__ */ jsx(
|
|
@@ -275,7 +275,7 @@ function TranslationManager({
|
|
|
275
275
|
);
|
|
276
276
|
}
|
|
277
277
|
if (!fieldList.length) {
|
|
278
|
-
return /* @__PURE__ */ jsx("div", { className: "rounded border bg-background/
|
|
278
|
+
return /* @__PURE__ */ jsx("div", { className: "rounded border bg-background/80 p-4 text-sm text-muted-foreground", children: t("translations.manager.noFields", "No translatable fields found for this entity type.") });
|
|
279
279
|
}
|
|
280
280
|
const localeTranslations = editedTranslations[activeLocale] ?? {};
|
|
281
281
|
return /* @__PURE__ */ jsx("div", { className: "overflow-x-auto", children: /* @__PURE__ */ jsxs("table", { className: "w-full min-w-[480px] text-sm", children: [
|
|
@@ -373,7 +373,7 @@ function TranslationManager({
|
|
|
373
373
|
] }),
|
|
374
374
|
renderRecordPicker()
|
|
375
375
|
] }) }),
|
|
376
|
-
/* @__PURE__ */ jsxs("div", { className: "rounded-lg border bg-background/
|
|
376
|
+
/* @__PURE__ */ jsxs("div", { className: "rounded-lg border bg-background/80 p-4", children: [
|
|
377
377
|
renderLocaleTabs(),
|
|
378
378
|
/* @__PURE__ */ jsx("div", { className: "mt-3", children: renderFieldTable() })
|
|
379
379
|
] }),
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/modules/translations/components/TranslationManager.tsx"],
|
|
4
|
-
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { Input } from '@open-mercato/ui/primitives/input'\nimport { ComboboxInput } from '@open-mercato/ui/backend/inputs'\nimport { LoadingMessage, ErrorMessage } from '@open-mercato/ui/backend/detail'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { apiCall, readApiResultOrThrow } from '@open-mercato/ui/backend/utils/apiCall'\nimport { useCustomFieldDefs } from '@open-mercato/ui/backend/utils/customFieldDefs'\nimport { Save, Plus, X } from 'lucide-react'\nimport { useOrganizationScopeVersion } from '@open-mercato/shared/lib/frontend/useOrganizationScope'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { locales as defaultLocales } from '@open-mercato/shared/lib/i18n/config'\nimport { ISO_639_1, isValidIso639, getIso639Label } from '@open-mercato/shared/lib/i18n/iso639'\nimport { formatEntityLabel, buildEntityListUrl, getRecordLabel, resolveBaseValue } from '../lib/helpers'\nimport { resolveFieldList } from '../lib/resolve-field-list'\nimport type { ResolvedField } from '../lib/resolve-field-list'\n\ntype TranslationManagerProps = {\n entityType?: string\n recordId?: string\n baseValues?: Record<string, unknown>\n translatableFields?: string[]\n mode?: 'standalone' | 'embedded'\n compact?: boolean\n}\n\ntype EntityOption = { entityId: string; label?: string; source?: string }\n\ntype TranslationsResponse = {\n entityType: string\n entityId: string\n translations: Record<string, Record<string, unknown>>\n createdAt?: string\n updatedAt?: string\n}\n\nfunction useTranslationLocales() {\n return useQuery<string[]>({\n queryKey: ['translation-locales'],\n queryFn: async () => {\n const res = await apiCall<{ locales: string[] }>('/api/translations/locales')\n if (!res.ok) return [...defaultLocales]\n return Array.isArray(res.result?.locales) && res.result.locales.length > 0\n ? res.result.locales\n : [...defaultLocales]\n },\n staleTime: 60_000,\n })\n}\n\nexport function TranslationManager({\n entityType: propEntityType,\n recordId: propRecordId,\n baseValues: propBaseValues,\n translatableFields: propTranslatableFields,\n mode = 'standalone',\n compact = false,\n}: TranslationManagerProps) {\n const t = useT()\n const scopeVersion = useOrganizationScopeVersion()\n const isEmbedded = mode === 'embedded'\n\n const [selectedEntityType, setSelectedEntityType] = React.useState(propEntityType ?? '')\n const [selectedRecordId, setSelectedRecordId] = React.useState(propRecordId ?? '')\n const [activeLocale, setActiveLocale] = React.useState('')\n const [editedTranslations, setEditedTranslations] = React.useState<Record<string, Record<string, string>>>({})\n const [hasUserEdited, setHasUserEdited] = React.useState(false)\n\n const entityType = isEmbedded ? (propEntityType ?? '') : selectedEntityType\n const recordId = isEmbedded ? (propRecordId ?? '') : selectedRecordId\n\n const { data: locales = [...defaultLocales] } = useTranslationLocales()\n\n React.useEffect(() => {\n if (locales.length > 0 && (!activeLocale || !locales.includes(activeLocale))) {\n setActiveLocale(locales[0])\n }\n }, [locales, activeLocale])\n\n React.useEffect(() => {\n if (isEmbedded && propEntityType) setSelectedEntityType(propEntityType)\n }, [isEmbedded, propEntityType])\n\n React.useEffect(() => {\n if (isEmbedded && propRecordId) setSelectedRecordId(propRecordId)\n }, [isEmbedded, propRecordId])\n\n const { data: entities, isLoading: loadingEntities, error: entitiesError } = useQuery<{ items: EntityOption[] }>({\n queryKey: ['entities-list', scopeVersion],\n enabled: !isEmbedded,\n queryFn: async () =>\n readApiResultOrThrow('/api/entities/entities', undefined, {\n errorMessage: t('translations.manager.errors.loadEntities', 'Failed to load entities'),\n }),\n })\n\n const entitySuggestions = React.useMemo(\n () =>\n (entities?.items || []).map((item) => ({\n value: item.entityId,\n label: formatEntityLabel(item.entityId, item.label),\n description: item.entityId,\n })),\n [entities],\n )\n\n const resolveEntityLabel = React.useCallback(\n (value: string) => {\n const match = entities?.items?.find((e) => e.entityId === value)\n return match ? formatEntityLabel(match.entityId, match.label) : formatEntityLabel(value)\n },\n [entities],\n )\n\n const listUrl = React.useMemo(() => entityType ? buildEntityListUrl(entityType) : null, [entityType])\n\n const loadRecordSuggestions = React.useCallback(\n async (query?: string) => {\n if (!entityType || !listUrl) return []\n const url = `${listUrl}?pageSize=20${query ? `&search=${encodeURIComponent(query)}` : ''}`\n const res = await apiCall<{ items: Array<Record<string, unknown>> }>(url)\n if (!res.ok) return []\n const items = res.result?.items ?? []\n return items.map((item) => ({\n value: String(item.id ?? ''),\n label: getRecordLabel(item),\n }))\n },\n [entityType, listUrl],\n )\n\n const { data: recordData } = useQuery<Record<string, unknown> | null>({\n queryKey: ['translation-record-data', entityType, recordId, listUrl, scopeVersion],\n enabled: !isEmbedded && !!entityType && !!recordId && !!listUrl,\n queryFn: async () => {\n const res = await apiCall<{ items: Array<Record<string, unknown>> }>(\n // Some APIs filter by `id` (catalog), others by `ids` (resources) \u2014 send both so the one recognized by the target route's buildFilters is applied\n `${listUrl}?id=${encodeURIComponent(recordId)}&ids=${encodeURIComponent(recordId)}&pageSize=1`,\n )\n if (!res.ok) return null\n const items = res.result?.items\n return Array.isArray(items) && items.length > 0 ? items[0] : null\n },\n })\n\n const baseValues = isEmbedded ? (propBaseValues ?? {}) : (recordData ?? {})\n\n const resolveRecordLabel = React.useCallback(\n (value: string) => {\n if (recordData) return getRecordLabel(recordData)\n return value\n },\n [recordData],\n )\n\n const { data: fieldDefs = [], isLoading: loadingFieldDefs } = useCustomFieldDefs(entityType ? [entityType] : [], {\n enabled: !!entityType,\n })\n\n const fieldList = React.useMemo(\n () => resolveFieldList(entityType, propTranslatableFields, fieldDefs as Array<{ key: string; kind: string; label?: string }>),\n [entityType, propTranslatableFields, fieldDefs],\n )\n\n const {\n data: translationData,\n isLoading: loadingTranslation,\n isError: translationError,\n refetch: refetchTranslation,\n } = useQuery<TranslationsResponse | null>({\n queryKey: ['entity-translation', entityType, recordId, scopeVersion],\n enabled: !!entityType && !!recordId,\n queryFn: async () => {\n const res = await apiCall<TranslationsResponse>(\n `/api/translations/${encodeURIComponent(entityType)}/${encodeURIComponent(recordId)}`,\n )\n if (!res.ok) {\n if (res.response?.status === 404) return null\n return null\n }\n return res.result ?? null\n },\n })\n\n const translationSignature = React.useMemo(() => JSON.stringify(translationData ?? null), [translationData])\n const lastTranslationSignatureRef = React.useRef<string | null>(null)\n\n React.useEffect(() => {\n const sig = translationSignature\n if (sig === lastTranslationSignatureRef.current && hasUserEdited) return\n lastTranslationSignatureRef.current = sig\n\n if (!translationData?.translations) {\n if (!hasUserEdited) setEditedTranslations({})\n return\n }\n\n const parsed: Record<string, Record<string, string>> = {}\n for (const [locale, fields] of Object.entries(translationData.translations)) {\n if (!fields || typeof fields !== 'object') continue\n parsed[locale] = {}\n for (const [key, val] of Object.entries(fields)) {\n parsed[locale][key] = typeof val === 'string' ? val : ''\n }\n }\n if (!hasUserEdited) setEditedTranslations(parsed)\n }, [translationSignature, translationData, hasUserEdited])\n\n const mutation = useMutation({\n mutationFn: async () => {\n if (!entityType || !recordId) {\n throw new Error(t('translations.manager.errors.selectRecord', 'Select an entity and record before saving'))\n }\n const body: Record<string, Record<string, string | null>> = {}\n for (const [locale, fields] of Object.entries(editedTranslations)) {\n const localeFields: Record<string, string | null> = {}\n let hasValues = false\n for (const [key, val] of Object.entries(fields)) {\n if (val && val.trim().length > 0) {\n localeFields[key] = val.trim()\n hasValues = true\n }\n }\n if (hasValues) body[locale] = localeFields\n }\n if (Object.keys(body).length === 0) {\n console.warn('[translations] Save skipped: payload is empty \u2014 no locale contains any non-empty field')\n throw new Error(t('translations.manager.errors.nothingToSave', 'Nothing to save \u2014 enter a translation first'))\n }\n const res = await apiCall(\n `/api/translations/${encodeURIComponent(entityType)}/${encodeURIComponent(recordId)}`,\n {\n method: 'PUT',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify(body),\n },\n )\n if (!res.ok) {\n throw new Error(t('translations.manager.errors.save', 'Failed to save translations'))\n }\n return true\n },\n onSuccess: () => {\n flash(t('translations.manager.flash.saved', 'Translations saved'), 'success')\n setHasUserEdited(false)\n void refetchTranslation()\n },\n onError: (err: unknown) => {\n const message = err instanceof Error ? err.message : t('translations.manager.errors.save', 'Failed to save translations')\n flash(message, 'error')\n },\n })\n\n const updateFieldValue = (locale: string, fieldKey: string, value: string) => {\n setHasUserEdited(true)\n setEditedTranslations((prev) => ({\n ...prev,\n [locale]: {\n ...prev[locale],\n [fieldKey]: value,\n },\n }))\n }\n\n const getBaseValue = (fieldKey: string): string => resolveBaseValue(baseValues, fieldKey)\n\n const renderRecordPicker = () => {\n if (isEmbedded) return null\n\n return (\n <div className=\"space-y-2\">\n <label className=\"text-xs text-muted-foreground\">\n {t('translations.manager.selectRecord', 'Select record')}\n </label>\n <ComboboxInput\n value={selectedRecordId}\n onChange={(next) => {\n setSelectedRecordId(next)\n setHasUserEdited(false)\n }}\n placeholder={t('translations.manager.searchRecords', 'Search records...')}\n loadSuggestions={loadRecordSuggestions}\n resolveLabel={resolveRecordLabel}\n allowCustomValues\n disabled={!entityType}\n />\n </div>\n )\n }\n\n const renderLocaleTabs = () => (\n <div className=\"flex gap-1 border-b\">\n {locales.map((locale) => {\n const isActive = activeLocale === locale\n return (\n <button\n key={locale}\n type=\"button\"\n data-state={isActive ? 'active' : 'inactive'}\n data-locale={locale}\n className={`px-3 py-1.5 text-sm font-medium transition-colors ${\n isActive\n ? 'border-b-2 border-primary text-primary'\n : 'text-muted-foreground hover:text-foreground'\n }`}\n onClick={() => setActiveLocale(locale)}\n >\n {locale.toUpperCase()}\n </button>\n )\n })}\n </div>\n )\n\n const renderFieldTable = () => {\n if (!entityType || !recordId) {\n return (\n <div className=\"rounded border bg-background/70 p-4 text-sm text-muted-foreground\">\n {t('translations.manager.selectFirst', 'Select an entity and record to manage translations.')}\n </div>\n )\n }\n if (loadingTranslation || loadingFieldDefs) {\n return (\n <LoadingMessage\n label={t('translations.manager.loadingTranslations', 'Loading translations...')}\n className=\"border-0 bg-transparent p-4\"\n />\n )\n }\n if (translationError) {\n return (\n <ErrorMessage\n label={t('translations.manager.errors.loadTranslation', 'Failed to load translations')}\n action={(\n <Button variant=\"outline\" size=\"sm\" onClick={() => void refetchTranslation()}>\n {t('translations.manager.actions.retry', 'Retry')}\n </Button>\n )}\n />\n )\n }\n if (!fieldList.length) {\n return (\n <div className=\"rounded border bg-background/70 p-4 text-sm text-muted-foreground\">\n {t('translations.manager.noFields', 'No translatable fields found for this entity type.')}\n </div>\n )\n }\n\n const localeTranslations = editedTranslations[activeLocale] ?? {}\n\n return (\n <div className=\"overflow-x-auto\">\n <table className=\"w-full min-w-[480px] text-sm\">\n <thead>\n <tr className=\"text-xs uppercase tracking-wide text-muted-foreground\">\n <th className=\"px-3 py-2 text-left w-[140px]\">\n {t('translations.manager.fields.field', 'Field')}\n </th>\n {!compact && (\n <th className=\"px-3 py-2 text-left\">\n {t('translations.manager.fields.baseValue', 'Base value')}\n </th>\n )}\n <th className=\"px-3 py-2 text-left\">\n {t('translations.manager.fields.translation', 'Translation')} ({activeLocale.toUpperCase()})\n </th>\n </tr>\n </thead>\n <tbody>\n {fieldList.map((field) => {\n const baseVal = getBaseValue(field.key)\n const translatedVal = localeTranslations[field.key] ?? ''\n\n return (\n <tr key={field.key} className=\"border-t\">\n <td className=\"px-3 py-2 align-top text-xs font-medium text-muted-foreground\">\n {field.label}\n </td>\n {!compact && (\n <td className=\"px-3 py-2 align-top text-xs text-muted-foreground max-w-[200px]\">\n {baseVal ? (\n <span className=\"line-clamp-3\">{baseVal}</span>\n ) : (\n <span className=\"text-muted-foreground/50\">-</span>\n )}\n </td>\n )}\n <td className=\"px-3 py-2 align-top\">\n {field.multiline ? (\n <textarea\n className=\"flex w-full rounded-md border border-input bg-background px-3 py-2 text-sm shadow-sm transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50\"\n rows={3}\n value={translatedVal}\n onChange={(e) => updateFieldValue(activeLocale, field.key, e.target.value)}\n placeholder={baseVal || field.label}\n />\n ) : (\n <Input\n value={translatedVal}\n onChange={(e) => updateFieldValue(activeLocale, field.key, e.target.value)}\n placeholder={baseVal || field.label}\n />\n )}\n </td>\n </tr>\n )\n })}\n </tbody>\n </table>\n </div>\n )\n }\n\n React.useEffect(() => {\n const handler = (e: KeyboardEvent) => {\n if ((e.metaKey || e.ctrlKey) && e.key === 'Enter') {\n e.preventDefault()\n if (entityType && recordId && !mutation.isPending) mutation.mutate()\n }\n }\n document.addEventListener('keydown', handler)\n return () => document.removeEventListener('keydown', handler)\n }, [entityType, recordId, mutation])\n\n if (compact) {\n return (\n <div className=\"space-y-3\">\n {renderLocaleTabs()}\n {renderFieldTable()}\n <div className=\"flex justify-end\">\n <Button\n type=\"button\"\n size=\"sm\"\n onClick={() => mutation.mutate()}\n disabled={mutation.isPending || !entityType || !recordId}\n data-testid=\"translations-save\"\n >\n <Save className=\"mr-2 h-3 w-3\" />\n {mutation.isPending\n ? t('translations.manager.actions.saving', 'Saving...')\n : t('translations.manager.actions.save', 'Save translations')}\n </Button>\n </div>\n </div>\n )\n }\n\n return (\n <div className=\"space-y-6\">\n <div className=\"flex flex-col gap-3 rounded-lg border bg-card p-4 shadow-sm\">\n <div className=\"space-y-2\">\n <h2 className=\"text-xl font-semibold\">{t('translations.manager.title', 'Translations')}</h2>\n <p className=\"text-sm text-muted-foreground\">\n {t('translations.manager.description', 'Manage translations for entity records across supported locales.')}\n </p>\n </div>\n\n {!isEmbedded && (\n <div className=\"flex flex-col gap-4 sm:flex-row sm:items-start\">\n <div className=\"flex-1 space-y-3\">\n <div>\n <label className=\"text-xs text-muted-foreground\">\n {t('translations.manager.selectEntity', 'Choose entity')}\n </label>\n <div className=\"mt-1\">\n <ComboboxInput\n value={selectedEntityType}\n onChange={(next) => {\n setSelectedEntityType(next)\n setSelectedRecordId('')\n setHasUserEdited(false)\n }}\n placeholder={t('translations.manager.placeholder', 'Select an entity')}\n suggestions={entitySuggestions}\n resolveLabel={resolveEntityLabel}\n disabled={loadingEntities || !!entitiesError}\n />\n </div>\n {entitiesError && (\n <p className=\"mt-1 text-xs text-red-600\">\n {t('translations.manager.errors.loadEntities', 'Failed to load entities')}\n </p>\n )}\n </div>\n {renderRecordPicker()}\n </div>\n </div>\n )}\n\n <div className=\"rounded-lg border bg-background/70 p-4\">\n {renderLocaleTabs()}\n <div className=\"mt-3\">\n {renderFieldTable()}\n </div>\n </div>\n\n <div className=\"flex justify-end\">\n <Button\n type=\"button\"\n onClick={() => mutation.mutate()}\n disabled={mutation.isPending || loadingEntities || !!entitiesError || !entityType || !recordId}\n data-testid=\"translations-save\"\n >\n <Save className=\"mr-2 h-4 w-4\" />\n {mutation.isPending\n ? t('translations.manager.actions.saving', 'Saving...')\n : t('translations.manager.actions.save', 'Save translations')}\n </Button>\n </div>\n </div>\n </div>\n )\n}\n\nexport function LocaleManager() {\n const t = useT()\n const queryClient = useQueryClient()\n const { data: locales = [], isLoading } = useTranslationLocales()\n const [newLocale, setNewLocale] = React.useState('')\n\n const mutation = useMutation({\n mutationFn: async (updatedLocales: string[]) => {\n const res = await apiCall<{ locales: string[] }>('/api/translations/locales', {\n method: 'PUT',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ locales: updatedLocales }),\n })\n if (!res.ok) throw new Error('Failed to save locales')\n return res.result?.locales ?? updatedLocales\n },\n onSuccess: (result) => {\n queryClient.setQueryData(['translation-locales'], result)\n flash(t('translations.locales.flash.saved', 'Locales updated'), 'success')\n },\n onError: () => {\n flash(t('translations.locales.flash.error', 'Failed to update locales'), 'error')\n },\n })\n\n const availableLocales = React.useMemo(\n () => ISO_639_1.filter((entry) => !locales.includes(entry.code)).map((entry) => ({\n value: entry.code,\n label: `${entry.code.toUpperCase()} \u2014 ${entry.label}`,\n })),\n [locales],\n )\n\n const addLocale = () => {\n const code = newLocale.toLowerCase().trim()\n if (!code || !isValidIso639(code) || locales.includes(code)) return\n mutation.mutate([...locales, code])\n setNewLocale('')\n }\n\n const removeLocale = (locale: string) => {\n if (locales.length <= 1) return\n mutation.mutate(locales.filter((l) => l !== locale))\n }\n\n if (isLoading) {\n return <LoadingMessage label={t('translations.locales.loading', 'Loading locales...')} className=\"border-0 bg-transparent p-4\" />\n }\n\n return (\n <div className=\"flex flex-col gap-3 rounded-lg border bg-card p-4 shadow-sm\">\n <div className=\"space-y-1\">\n <h3 className=\"text-lg font-semibold\">{t('translations.locales.title', 'Supported locales')}</h3>\n <p className=\"text-sm text-muted-foreground\">\n {t('translations.locales.description', 'Configure which locales are available for translations. Add ISO language codes (e.g. fr, it, ja, zh).')}\n </p>\n </div>\n\n <div className=\"flex flex-wrap gap-2\">\n {locales.map((locale) => (\n <span\n key={locale}\n className=\"inline-flex items-center gap-1.5 rounded-full border bg-muted/50 px-3 py-1 text-sm font-medium\"\n title={getIso639Label(locale) ?? locale}\n >\n {locale.toUpperCase()}{getIso639Label(locale) ? ` \u2014 ${getIso639Label(locale)}` : ''}\n {locales.length > 1 && (\n <button\n type=\"button\"\n className=\"rounded-full p-0.5 text-muted-foreground hover:text-foreground transition-colors\"\n onClick={() => removeLocale(locale)}\n disabled={mutation.isPending}\n >\n <X className=\"h-3 w-3\" />\n </button>\n )}\n </span>\n ))}\n </div>\n\n <div className=\"flex gap-2 items-center\">\n <div className=\"max-w-[240px] flex-1\">\n <ComboboxInput\n value={newLocale}\n onChange={setNewLocale}\n placeholder={t('translations.locales.addPlaceholder', 'Search language...')}\n suggestions={availableLocales}\n resolveLabel={(value) => {\n const label = getIso639Label(value)\n return label ? `${value.toUpperCase()} \u2014 ${label}` : value.toUpperCase()\n }}\n />\n </div>\n <Button\n variant=\"outline\"\n size=\"sm\"\n onClick={addLocale}\n disabled={mutation.isPending || !newLocale.trim() || !isValidIso639(newLocale) || locales.includes(newLocale.toLowerCase().trim())}\n >\n <Plus className=\"mr-1 h-3 w-3\" />\n {t('translations.locales.add', 'Add')}\n </Button>\n </div>\n </div>\n )\n}\n"],
|
|
4
|
+
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { Input } from '@open-mercato/ui/primitives/input'\nimport { ComboboxInput } from '@open-mercato/ui/backend/inputs'\nimport { LoadingMessage, ErrorMessage } from '@open-mercato/ui/backend/detail'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { apiCall, readApiResultOrThrow } from '@open-mercato/ui/backend/utils/apiCall'\nimport { useCustomFieldDefs } from '@open-mercato/ui/backend/utils/customFieldDefs'\nimport { Save, Plus, X } from 'lucide-react'\nimport { useOrganizationScopeVersion } from '@open-mercato/shared/lib/frontend/useOrganizationScope'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { locales as defaultLocales } from '@open-mercato/shared/lib/i18n/config'\nimport { ISO_639_1, isValidIso639, getIso639Label } from '@open-mercato/shared/lib/i18n/iso639'\nimport { formatEntityLabel, buildEntityListUrl, getRecordLabel, resolveBaseValue } from '../lib/helpers'\nimport { resolveFieldList } from '../lib/resolve-field-list'\nimport type { ResolvedField } from '../lib/resolve-field-list'\n\ntype TranslationManagerProps = {\n entityType?: string\n recordId?: string\n baseValues?: Record<string, unknown>\n translatableFields?: string[]\n mode?: 'standalone' | 'embedded'\n compact?: boolean\n}\n\ntype EntityOption = { entityId: string; label?: string; source?: string }\n\ntype TranslationsResponse = {\n entityType: string\n entityId: string\n translations: Record<string, Record<string, unknown>>\n createdAt?: string\n updatedAt?: string\n}\n\nfunction useTranslationLocales() {\n return useQuery<string[]>({\n queryKey: ['translation-locales'],\n queryFn: async () => {\n const res = await apiCall<{ locales: string[] }>('/api/translations/locales')\n if (!res.ok) return [...defaultLocales]\n return Array.isArray(res.result?.locales) && res.result.locales.length > 0\n ? res.result.locales\n : [...defaultLocales]\n },\n staleTime: 60_000,\n })\n}\n\nexport function TranslationManager({\n entityType: propEntityType,\n recordId: propRecordId,\n baseValues: propBaseValues,\n translatableFields: propTranslatableFields,\n mode = 'standalone',\n compact = false,\n}: TranslationManagerProps) {\n const t = useT()\n const scopeVersion = useOrganizationScopeVersion()\n const isEmbedded = mode === 'embedded'\n\n const [selectedEntityType, setSelectedEntityType] = React.useState(propEntityType ?? '')\n const [selectedRecordId, setSelectedRecordId] = React.useState(propRecordId ?? '')\n const [activeLocale, setActiveLocale] = React.useState('')\n const [editedTranslations, setEditedTranslations] = React.useState<Record<string, Record<string, string>>>({})\n const [hasUserEdited, setHasUserEdited] = React.useState(false)\n\n const entityType = isEmbedded ? (propEntityType ?? '') : selectedEntityType\n const recordId = isEmbedded ? (propRecordId ?? '') : selectedRecordId\n\n const { data: locales = [...defaultLocales] } = useTranslationLocales()\n\n React.useEffect(() => {\n if (locales.length > 0 && (!activeLocale || !locales.includes(activeLocale))) {\n setActiveLocale(locales[0])\n }\n }, [locales, activeLocale])\n\n React.useEffect(() => {\n if (isEmbedded && propEntityType) setSelectedEntityType(propEntityType)\n }, [isEmbedded, propEntityType])\n\n React.useEffect(() => {\n if (isEmbedded && propRecordId) setSelectedRecordId(propRecordId)\n }, [isEmbedded, propRecordId])\n\n const { data: entities, isLoading: loadingEntities, error: entitiesError } = useQuery<{ items: EntityOption[] }>({\n queryKey: ['entities-list', scopeVersion],\n enabled: !isEmbedded,\n queryFn: async () =>\n readApiResultOrThrow('/api/entities/entities', undefined, {\n errorMessage: t('translations.manager.errors.loadEntities', 'Failed to load entities'),\n }),\n })\n\n const entitySuggestions = React.useMemo(\n () =>\n (entities?.items || []).map((item) => ({\n value: item.entityId,\n label: formatEntityLabel(item.entityId, item.label),\n description: item.entityId,\n })),\n [entities],\n )\n\n const resolveEntityLabel = React.useCallback(\n (value: string) => {\n const match = entities?.items?.find((e) => e.entityId === value)\n return match ? formatEntityLabel(match.entityId, match.label) : formatEntityLabel(value)\n },\n [entities],\n )\n\n const listUrl = React.useMemo(() => entityType ? buildEntityListUrl(entityType) : null, [entityType])\n\n const loadRecordSuggestions = React.useCallback(\n async (query?: string) => {\n if (!entityType || !listUrl) return []\n const url = `${listUrl}?pageSize=20${query ? `&search=${encodeURIComponent(query)}` : ''}`\n const res = await apiCall<{ items: Array<Record<string, unknown>> }>(url)\n if (!res.ok) return []\n const items = res.result?.items ?? []\n return items.map((item) => ({\n value: String(item.id ?? ''),\n label: getRecordLabel(item),\n }))\n },\n [entityType, listUrl],\n )\n\n const { data: recordData } = useQuery<Record<string, unknown> | null>({\n queryKey: ['translation-record-data', entityType, recordId, listUrl, scopeVersion],\n enabled: !isEmbedded && !!entityType && !!recordId && !!listUrl,\n queryFn: async () => {\n const res = await apiCall<{ items: Array<Record<string, unknown>> }>(\n // Some APIs filter by `id` (catalog), others by `ids` (resources) \u2014 send both so the one recognized by the target route's buildFilters is applied\n `${listUrl}?id=${encodeURIComponent(recordId)}&ids=${encodeURIComponent(recordId)}&pageSize=1`,\n )\n if (!res.ok) return null\n const items = res.result?.items\n return Array.isArray(items) && items.length > 0 ? items[0] : null\n },\n })\n\n const baseValues = isEmbedded ? (propBaseValues ?? {}) : (recordData ?? {})\n\n const resolveRecordLabel = React.useCallback(\n (value: string) => {\n if (recordData) return getRecordLabel(recordData)\n return value\n },\n [recordData],\n )\n\n const { data: fieldDefs = [], isLoading: loadingFieldDefs } = useCustomFieldDefs(entityType ? [entityType] : [], {\n enabled: !!entityType,\n })\n\n const fieldList = React.useMemo(\n () => resolveFieldList(entityType, propTranslatableFields, fieldDefs as Array<{ key: string; kind: string; label?: string }>),\n [entityType, propTranslatableFields, fieldDefs],\n )\n\n const {\n data: translationData,\n isLoading: loadingTranslation,\n isError: translationError,\n refetch: refetchTranslation,\n } = useQuery<TranslationsResponse | null>({\n queryKey: ['entity-translation', entityType, recordId, scopeVersion],\n enabled: !!entityType && !!recordId,\n queryFn: async () => {\n const res = await apiCall<TranslationsResponse>(\n `/api/translations/${encodeURIComponent(entityType)}/${encodeURIComponent(recordId)}`,\n )\n if (!res.ok) {\n if (res.response?.status === 404) return null\n return null\n }\n return res.result ?? null\n },\n })\n\n const translationSignature = React.useMemo(() => JSON.stringify(translationData ?? null), [translationData])\n const lastTranslationSignatureRef = React.useRef<string | null>(null)\n\n React.useEffect(() => {\n const sig = translationSignature\n if (sig === lastTranslationSignatureRef.current && hasUserEdited) return\n lastTranslationSignatureRef.current = sig\n\n if (!translationData?.translations) {\n if (!hasUserEdited) setEditedTranslations({})\n return\n }\n\n const parsed: Record<string, Record<string, string>> = {}\n for (const [locale, fields] of Object.entries(translationData.translations)) {\n if (!fields || typeof fields !== 'object') continue\n parsed[locale] = {}\n for (const [key, val] of Object.entries(fields)) {\n parsed[locale][key] = typeof val === 'string' ? val : ''\n }\n }\n if (!hasUserEdited) setEditedTranslations(parsed)\n }, [translationSignature, translationData, hasUserEdited])\n\n const mutation = useMutation({\n mutationFn: async () => {\n if (!entityType || !recordId) {\n throw new Error(t('translations.manager.errors.selectRecord', 'Select an entity and record before saving'))\n }\n const body: Record<string, Record<string, string | null>> = {}\n for (const [locale, fields] of Object.entries(editedTranslations)) {\n const localeFields: Record<string, string | null> = {}\n let hasValues = false\n for (const [key, val] of Object.entries(fields)) {\n if (val && val.trim().length > 0) {\n localeFields[key] = val.trim()\n hasValues = true\n }\n }\n if (hasValues) body[locale] = localeFields\n }\n if (Object.keys(body).length === 0) {\n console.warn('[translations] Save skipped: payload is empty \u2014 no locale contains any non-empty field')\n throw new Error(t('translations.manager.errors.nothingToSave', 'Nothing to save \u2014 enter a translation first'))\n }\n const res = await apiCall(\n `/api/translations/${encodeURIComponent(entityType)}/${encodeURIComponent(recordId)}`,\n {\n method: 'PUT',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify(body),\n },\n )\n if (!res.ok) {\n throw new Error(t('translations.manager.errors.save', 'Failed to save translations'))\n }\n return true\n },\n onSuccess: () => {\n flash(t('translations.manager.flash.saved', 'Translations saved'), 'success')\n setHasUserEdited(false)\n void refetchTranslation()\n },\n onError: (err: unknown) => {\n const message = err instanceof Error ? err.message : t('translations.manager.errors.save', 'Failed to save translations')\n flash(message, 'error')\n },\n })\n\n const updateFieldValue = (locale: string, fieldKey: string, value: string) => {\n setHasUserEdited(true)\n setEditedTranslations((prev) => ({\n ...prev,\n [locale]: {\n ...prev[locale],\n [fieldKey]: value,\n },\n }))\n }\n\n const getBaseValue = (fieldKey: string): string => resolveBaseValue(baseValues, fieldKey)\n\n const renderRecordPicker = () => {\n if (isEmbedded) return null\n\n return (\n <div className=\"space-y-2\">\n <label className=\"text-xs text-muted-foreground\">\n {t('translations.manager.selectRecord', 'Select record')}\n </label>\n <ComboboxInput\n value={selectedRecordId}\n onChange={(next) => {\n setSelectedRecordId(next)\n setHasUserEdited(false)\n }}\n placeholder={t('translations.manager.searchRecords', 'Search records...')}\n loadSuggestions={loadRecordSuggestions}\n resolveLabel={resolveRecordLabel}\n allowCustomValues\n disabled={!entityType}\n />\n </div>\n )\n }\n\n const renderLocaleTabs = () => (\n <div className=\"flex gap-1 border-b\">\n {locales.map((locale) => {\n const isActive = activeLocale === locale\n return (\n <button\n key={locale}\n type=\"button\"\n data-state={isActive ? 'active' : 'inactive'}\n data-locale={locale}\n className={`px-3 py-1.5 text-sm font-medium transition-colors ${\n isActive\n ? 'border-b-2 border-primary text-primary'\n : 'text-muted-foreground hover:text-foreground'\n }`}\n onClick={() => setActiveLocale(locale)}\n >\n {locale.toUpperCase()}\n </button>\n )\n })}\n </div>\n )\n\n const renderFieldTable = () => {\n if (!entityType || !recordId) {\n return (\n <div className=\"rounded border bg-background/80 p-4 text-sm text-muted-foreground\">\n {t('translations.manager.selectFirst', 'Select an entity and record to manage translations.')}\n </div>\n )\n }\n if (loadingTranslation || loadingFieldDefs) {\n return (\n <LoadingMessage\n label={t('translations.manager.loadingTranslations', 'Loading translations...')}\n className=\"border-0 bg-transparent p-4\"\n />\n )\n }\n if (translationError) {\n return (\n <ErrorMessage\n label={t('translations.manager.errors.loadTranslation', 'Failed to load translations')}\n action={(\n <Button variant=\"outline\" size=\"sm\" onClick={() => void refetchTranslation()}>\n {t('translations.manager.actions.retry', 'Retry')}\n </Button>\n )}\n />\n )\n }\n if (!fieldList.length) {\n return (\n <div className=\"rounded border bg-background/80 p-4 text-sm text-muted-foreground\">\n {t('translations.manager.noFields', 'No translatable fields found for this entity type.')}\n </div>\n )\n }\n\n const localeTranslations = editedTranslations[activeLocale] ?? {}\n\n return (\n <div className=\"overflow-x-auto\">\n <table className=\"w-full min-w-[480px] text-sm\">\n <thead>\n <tr className=\"text-xs uppercase tracking-wide text-muted-foreground\">\n <th className=\"px-3 py-2 text-left w-[140px]\">\n {t('translations.manager.fields.field', 'Field')}\n </th>\n {!compact && (\n <th className=\"px-3 py-2 text-left\">\n {t('translations.manager.fields.baseValue', 'Base value')}\n </th>\n )}\n <th className=\"px-3 py-2 text-left\">\n {t('translations.manager.fields.translation', 'Translation')} ({activeLocale.toUpperCase()})\n </th>\n </tr>\n </thead>\n <tbody>\n {fieldList.map((field) => {\n const baseVal = getBaseValue(field.key)\n const translatedVal = localeTranslations[field.key] ?? ''\n\n return (\n <tr key={field.key} className=\"border-t\">\n <td className=\"px-3 py-2 align-top text-xs font-medium text-muted-foreground\">\n {field.label}\n </td>\n {!compact && (\n <td className=\"px-3 py-2 align-top text-xs text-muted-foreground max-w-[200px]\">\n {baseVal ? (\n <span className=\"line-clamp-3\">{baseVal}</span>\n ) : (\n <span className=\"text-muted-foreground/50\">-</span>\n )}\n </td>\n )}\n <td className=\"px-3 py-2 align-top\">\n {field.multiline ? (\n <textarea\n className=\"flex w-full rounded-md border border-input bg-background px-3 py-2 text-sm shadow-sm transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50\"\n rows={3}\n value={translatedVal}\n onChange={(e) => updateFieldValue(activeLocale, field.key, e.target.value)}\n placeholder={baseVal || field.label}\n />\n ) : (\n <Input\n value={translatedVal}\n onChange={(e) => updateFieldValue(activeLocale, field.key, e.target.value)}\n placeholder={baseVal || field.label}\n />\n )}\n </td>\n </tr>\n )\n })}\n </tbody>\n </table>\n </div>\n )\n }\n\n React.useEffect(() => {\n const handler = (e: KeyboardEvent) => {\n if ((e.metaKey || e.ctrlKey) && e.key === 'Enter') {\n e.preventDefault()\n if (entityType && recordId && !mutation.isPending) mutation.mutate()\n }\n }\n document.addEventListener('keydown', handler)\n return () => document.removeEventListener('keydown', handler)\n }, [entityType, recordId, mutation])\n\n if (compact) {\n return (\n <div className=\"space-y-3\">\n {renderLocaleTabs()}\n {renderFieldTable()}\n <div className=\"flex justify-end\">\n <Button\n type=\"button\"\n size=\"sm\"\n onClick={() => mutation.mutate()}\n disabled={mutation.isPending || !entityType || !recordId}\n data-testid=\"translations-save\"\n >\n <Save className=\"mr-2 h-3 w-3\" />\n {mutation.isPending\n ? t('translations.manager.actions.saving', 'Saving...')\n : t('translations.manager.actions.save', 'Save translations')}\n </Button>\n </div>\n </div>\n )\n }\n\n return (\n <div className=\"space-y-6\">\n <div className=\"flex flex-col gap-3 rounded-lg border bg-card p-4 shadow-sm\">\n <div className=\"space-y-2\">\n <h2 className=\"text-xl font-semibold\">{t('translations.manager.title', 'Translations')}</h2>\n <p className=\"text-sm text-muted-foreground\">\n {t('translations.manager.description', 'Manage translations for entity records across supported locales.')}\n </p>\n </div>\n\n {!isEmbedded && (\n <div className=\"flex flex-col gap-4 sm:flex-row sm:items-start\">\n <div className=\"flex-1 space-y-3\">\n <div>\n <label className=\"text-xs text-muted-foreground\">\n {t('translations.manager.selectEntity', 'Choose entity')}\n </label>\n <div className=\"mt-1\">\n <ComboboxInput\n value={selectedEntityType}\n onChange={(next) => {\n setSelectedEntityType(next)\n setSelectedRecordId('')\n setHasUserEdited(false)\n }}\n placeholder={t('translations.manager.placeholder', 'Select an entity')}\n suggestions={entitySuggestions}\n resolveLabel={resolveEntityLabel}\n disabled={loadingEntities || !!entitiesError}\n />\n </div>\n {entitiesError && (\n <p className=\"mt-1 text-xs text-red-600\">\n {t('translations.manager.errors.loadEntities', 'Failed to load entities')}\n </p>\n )}\n </div>\n {renderRecordPicker()}\n </div>\n </div>\n )}\n\n <div className=\"rounded-lg border bg-background/80 p-4\">\n {renderLocaleTabs()}\n <div className=\"mt-3\">\n {renderFieldTable()}\n </div>\n </div>\n\n <div className=\"flex justify-end\">\n <Button\n type=\"button\"\n onClick={() => mutation.mutate()}\n disabled={mutation.isPending || loadingEntities || !!entitiesError || !entityType || !recordId}\n data-testid=\"translations-save\"\n >\n <Save className=\"mr-2 h-4 w-4\" />\n {mutation.isPending\n ? t('translations.manager.actions.saving', 'Saving...')\n : t('translations.manager.actions.save', 'Save translations')}\n </Button>\n </div>\n </div>\n </div>\n )\n}\n\nexport function LocaleManager() {\n const t = useT()\n const queryClient = useQueryClient()\n const { data: locales = [], isLoading } = useTranslationLocales()\n const [newLocale, setNewLocale] = React.useState('')\n\n const mutation = useMutation({\n mutationFn: async (updatedLocales: string[]) => {\n const res = await apiCall<{ locales: string[] }>('/api/translations/locales', {\n method: 'PUT',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ locales: updatedLocales }),\n })\n if (!res.ok) throw new Error('Failed to save locales')\n return res.result?.locales ?? updatedLocales\n },\n onSuccess: (result) => {\n queryClient.setQueryData(['translation-locales'], result)\n flash(t('translations.locales.flash.saved', 'Locales updated'), 'success')\n },\n onError: () => {\n flash(t('translations.locales.flash.error', 'Failed to update locales'), 'error')\n },\n })\n\n const availableLocales = React.useMemo(\n () => ISO_639_1.filter((entry) => !locales.includes(entry.code)).map((entry) => ({\n value: entry.code,\n label: `${entry.code.toUpperCase()} \u2014 ${entry.label}`,\n })),\n [locales],\n )\n\n const addLocale = () => {\n const code = newLocale.toLowerCase().trim()\n if (!code || !isValidIso639(code) || locales.includes(code)) return\n mutation.mutate([...locales, code])\n setNewLocale('')\n }\n\n const removeLocale = (locale: string) => {\n if (locales.length <= 1) return\n mutation.mutate(locales.filter((l) => l !== locale))\n }\n\n if (isLoading) {\n return <LoadingMessage label={t('translations.locales.loading', 'Loading locales...')} className=\"border-0 bg-transparent p-4\" />\n }\n\n return (\n <div className=\"flex flex-col gap-3 rounded-lg border bg-card p-4 shadow-sm\">\n <div className=\"space-y-1\">\n <h3 className=\"text-lg font-semibold\">{t('translations.locales.title', 'Supported locales')}</h3>\n <p className=\"text-sm text-muted-foreground\">\n {t('translations.locales.description', 'Configure which locales are available for translations. Add ISO language codes (e.g. fr, it, ja, zh).')}\n </p>\n </div>\n\n <div className=\"flex flex-wrap gap-2\">\n {locales.map((locale) => (\n <span\n key={locale}\n className=\"inline-flex items-center gap-1.5 rounded-full border bg-muted/50 px-3 py-1 text-sm font-medium\"\n title={getIso639Label(locale) ?? locale}\n >\n {locale.toUpperCase()}{getIso639Label(locale) ? ` \u2014 ${getIso639Label(locale)}` : ''}\n {locales.length > 1 && (\n <button\n type=\"button\"\n className=\"rounded-full p-0.5 text-muted-foreground hover:text-foreground transition-colors\"\n onClick={() => removeLocale(locale)}\n disabled={mutation.isPending}\n >\n <X className=\"h-3 w-3\" />\n </button>\n )}\n </span>\n ))}\n </div>\n\n <div className=\"flex gap-2 items-center\">\n <div className=\"max-w-[240px] flex-1\">\n <ComboboxInput\n value={newLocale}\n onChange={setNewLocale}\n placeholder={t('translations.locales.addPlaceholder', 'Search language...')}\n suggestions={availableLocales}\n resolveLabel={(value) => {\n const label = getIso639Label(value)\n return label ? `${value.toUpperCase()} \u2014 ${label}` : value.toUpperCase()\n }}\n />\n </div>\n <Button\n variant=\"outline\"\n size=\"sm\"\n onClick={addLocale}\n disabled={mutation.isPending || !newLocale.trim() || !isValidIso639(newLocale) || locales.includes(newLocale.toLowerCase().trim())}\n >\n <Plus className=\"mr-1 h-3 w-3\" />\n {t('translations.locales.add', 'Add')}\n </Button>\n </div>\n </div>\n )\n}\n"],
|
|
5
5
|
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -71,7 +71,7 @@ function TranslationWidget({ context, data }) {
|
|
|
71
71
|
/* @__PURE__ */ jsx(
|
|
72
72
|
"div",
|
|
73
73
|
{
|
|
74
|
-
className: "fixed inset-0 z-
|
|
74
|
+
className: "fixed inset-0 z-overlay bg-black/20",
|
|
75
75
|
onClick: () => setOpen(false),
|
|
76
76
|
"aria-hidden": "true"
|
|
77
77
|
}
|
|
@@ -79,7 +79,7 @@ function TranslationWidget({ context, data }) {
|
|
|
79
79
|
/* @__PURE__ */ jsx(
|
|
80
80
|
"div",
|
|
81
81
|
{
|
|
82
|
-
className: "fixed right-0 top-0 z-
|
|
82
|
+
className: "fixed right-0 top-0 z-modal h-full w-full max-w-4xl border-l bg-background shadow-lg",
|
|
83
83
|
role: "dialog",
|
|
84
84
|
"aria-modal": "true",
|
|
85
85
|
"aria-label": t("translations.widgets.translationManager.groupLabel", "Translations"),
|
package/dist/modules/translations/widgets/injection/translation-manager/widget.client.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../../src/modules/translations/widgets/injection/translation-manager/widget.client.tsx"],
|
|
4
|
-
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { useParams } from 'next/navigation'\nimport Link from 'next/link'\nimport { ExternalLink, Languages, X } from 'lucide-react'\nimport type { InjectionWidgetComponentProps } from '@open-mercato/shared/modules/widgets/injection'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { TranslationManager } from '../../../components/TranslationManager'\nimport { extractRecordId } from '../../../lib/extract-record-id'\n\ntype WidgetContext = { entityId?: string; recordId?: string }\ntype WidgetData = Record<string, unknown> & { id?: string | number }\n\nfunction useTranslationAccess(): boolean {\n const [hasAccess, setHasAccess] = React.useState(false)\n React.useEffect(() => {\n let mounted = true\n // Use the original fetch to bypass the global apiFetch wrapper\n // that redirects to login on 403. This lets us gracefully hide the widget\n // when the user lacks translations.view instead of crashing the page.\n const nativeFetch = ((window as any).__omOriginalFetch as typeof fetch) || fetch\n nativeFetch('/api/translations/locales', { credentials: 'include' })\n .then((res) => { if (mounted) setHasAccess(res.ok) })\n .catch(() => { if (mounted) setHasAccess(false) })\n return () => { mounted = false }\n }, [])\n return hasAccess\n}\n\nexport default function TranslationWidget({ context, data }: InjectionWidgetComponentProps<WidgetContext, WidgetData>) {\n const entityType = context?.entityId\n const params = useParams()\n const t = useT()\n const [open, setOpen] = React.useState(false)\n const hasAccess = useTranslationAccess()\n\n const contextRecordId = typeof context?.recordId === 'string' && context.recordId.trim().length > 0\n ? context.recordId.trim()\n : undefined\n const dataRecordId = data?.id === undefined || data.id === null ? undefined : String(data.id)\n const routeRecordId = params ? extractRecordId(params as Record<string, string | string[]>) : undefined\n const recordId = contextRecordId ?? dataRecordId ?? routeRecordId\n const canRender = Boolean(entityType && recordId && hasAccess)\n\n React.useEffect(() => {\n if (!open || !canRender) return\n const prev = document.body.style.overflow\n document.body.style.overflow = 'hidden'\n return () => {\n document.body.style.overflow = prev\n }\n }, [canRender, open])\n\n React.useEffect(() => {\n if (!open || !canRender) return\n const handleKeyDown = (event: KeyboardEvent) => {\n if (event.key === 'Escape') {\n setOpen(false)\n }\n }\n document.addEventListener('keydown', handleKeyDown)\n return () => document.removeEventListener('keydown', handleKeyDown)\n }, [canRender, open])\n\n if (!canRender) return null\n\n return (\n <>\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon\"\n onClick={() => setOpen(true)}\n aria-label={t('translations.widgets.translationManager.fullManager', 'Translation manager')}\n title={t('translations.widgets.translationManager.fullManager', 'Translation manager')}\n >\n <Languages className=\"size-4\" />\n </Button>\n {open ? (\n <>\n <div\n className=\"fixed inset-0 z-
|
|
4
|
+
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { useParams } from 'next/navigation'\nimport Link from 'next/link'\nimport { ExternalLink, Languages, X } from 'lucide-react'\nimport type { InjectionWidgetComponentProps } from '@open-mercato/shared/modules/widgets/injection'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { TranslationManager } from '../../../components/TranslationManager'\nimport { extractRecordId } from '../../../lib/extract-record-id'\n\ntype WidgetContext = { entityId?: string; recordId?: string }\ntype WidgetData = Record<string, unknown> & { id?: string | number }\n\nfunction useTranslationAccess(): boolean {\n const [hasAccess, setHasAccess] = React.useState(false)\n React.useEffect(() => {\n let mounted = true\n // Use the original fetch to bypass the global apiFetch wrapper\n // that redirects to login on 403. This lets us gracefully hide the widget\n // when the user lacks translations.view instead of crashing the page.\n const nativeFetch = ((window as any).__omOriginalFetch as typeof fetch) || fetch\n nativeFetch('/api/translations/locales', { credentials: 'include' })\n .then((res) => { if (mounted) setHasAccess(res.ok) })\n .catch(() => { if (mounted) setHasAccess(false) })\n return () => { mounted = false }\n }, [])\n return hasAccess\n}\n\nexport default function TranslationWidget({ context, data }: InjectionWidgetComponentProps<WidgetContext, WidgetData>) {\n const entityType = context?.entityId\n const params = useParams()\n const t = useT()\n const [open, setOpen] = React.useState(false)\n const hasAccess = useTranslationAccess()\n\n const contextRecordId = typeof context?.recordId === 'string' && context.recordId.trim().length > 0\n ? context.recordId.trim()\n : undefined\n const dataRecordId = data?.id === undefined || data.id === null ? undefined : String(data.id)\n const routeRecordId = params ? extractRecordId(params as Record<string, string | string[]>) : undefined\n const recordId = contextRecordId ?? dataRecordId ?? routeRecordId\n const canRender = Boolean(entityType && recordId && hasAccess)\n\n React.useEffect(() => {\n if (!open || !canRender) return\n const prev = document.body.style.overflow\n document.body.style.overflow = 'hidden'\n return () => {\n document.body.style.overflow = prev\n }\n }, [canRender, open])\n\n React.useEffect(() => {\n if (!open || !canRender) return\n const handleKeyDown = (event: KeyboardEvent) => {\n if (event.key === 'Escape') {\n setOpen(false)\n }\n }\n document.addEventListener('keydown', handleKeyDown)\n return () => document.removeEventListener('keydown', handleKeyDown)\n }, [canRender, open])\n\n if (!canRender) return null\n\n return (\n <>\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon\"\n onClick={() => setOpen(true)}\n aria-label={t('translations.widgets.translationManager.fullManager', 'Translation manager')}\n title={t('translations.widgets.translationManager.fullManager', 'Translation manager')}\n >\n <Languages className=\"size-4\" />\n </Button>\n {open ? (\n <>\n <div\n className=\"fixed inset-0 z-overlay bg-black/20\"\n onClick={() => setOpen(false)}\n aria-hidden=\"true\"\n />\n <div\n className=\"fixed right-0 top-0 z-modal h-full w-full max-w-4xl border-l bg-background shadow-lg\"\n role=\"dialog\"\n aria-modal=\"true\"\n aria-label={t('translations.widgets.translationManager.groupLabel', 'Translations')}\n >\n <div className=\"flex h-full flex-col\">\n <div className=\"flex items-start justify-between gap-3 border-b px-4 py-3\">\n <div className=\"space-y-1\">\n <h2 className=\"font-semibold\">\n {t('translations.widgets.translationManager.groupLabel', 'Translations')}\n </h2>\n <p className=\"text-sm text-muted-foreground\">\n {t('translations.widgets.translationManager.groupDescription', 'Manage translations for this record across supported locales.')}\n </p>\n </div>\n <Button\n variant=\"ghost\"\n size=\"icon\"\n onClick={() => setOpen(false)}\n aria-label={t('ui.dialog.close.ariaLabel', 'Close')}\n >\n <X className=\"size-4\" />\n </Button>\n </div>\n <div className=\"flex-1 overflow-y-auto px-4 py-4\">\n <TranslationManager\n mode=\"embedded\"\n compact\n entityType={entityType}\n recordId={recordId}\n baseValues={data}\n />\n </div>\n <div className=\"flex flex-wrap gap-x-4 gap-y-1 border-t px-4 py-3\">\n <Link\n href={`/backend/entities/system/${encodeURIComponent(entityType!)}`}\n className=\"inline-flex items-center gap-1.5 text-xs text-muted-foreground hover:text-foreground transition-colors\"\n >\n <Languages className=\"size-3\" />\n {t('translations.widgets.translationManager.customFieldLabels', 'Custom fields translations')}\n <ExternalLink className=\"size-2.5\" />\n </Link>\n <Link\n href=\"/backend/config/translations\"\n className=\"inline-flex items-center gap-1.5 text-xs text-muted-foreground hover:text-foreground transition-colors\"\n >\n <Languages className=\"size-3\" />\n {t('translations.widgets.translationManager.fullManager', 'Translation manager')}\n <ExternalLink className=\"size-2.5\" />\n </Link>\n </div>\n </div>\n </div>\n </>\n ) : null}\n </>\n )\n}\n"],
|
|
5
5
|
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -92,15 +92,15 @@ function EditWorkflowDefinitionPage() {
|
|
|
92
92
|
return null;
|
|
93
93
|
}
|
|
94
94
|
return /* @__PURE__ */ jsx(Page, { children: /* @__PURE__ */ jsxs(PageBody, { children: [
|
|
95
|
-
/* @__PURE__ */ jsxs("div", { className: "mb-4 p-4 bg-
|
|
95
|
+
/* @__PURE__ */ jsxs("div", { className: "mb-4 p-4 bg-status-info-bg border border-status-info-border rounded-lg flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between", children: [
|
|
96
96
|
/* @__PURE__ */ jsxs("div", { className: "flex items-start gap-3", children: [
|
|
97
|
-
/* @__PURE__ */ jsx("svg", { className: "w-5 h-5 text-
|
|
97
|
+
/* @__PURE__ */ jsx("svg", { className: "w-5 h-5 text-status-info-icon", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M13 10V3L4 14h7v7l9-11h-7z" }) }),
|
|
98
98
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
99
|
-
/* @__PURE__ */ jsx("p", { className: "text-sm font-medium text-
|
|
100
|
-
/* @__PURE__ */ jsx("p", { className: "text-xs text-
|
|
99
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm font-medium text-status-info-text", children: t("workflows.edit.visualEditorAvailable") }),
|
|
100
|
+
/* @__PURE__ */ jsx("p", { className: "text-xs text-status-info-text mt-0.5", children: t("workflows.edit.visualEditorDescription") })
|
|
101
101
|
] })
|
|
102
102
|
] }),
|
|
103
|
-
/* @__PURE__ */ jsx(Button, { asChild: true, variant: "outline", size: "sm", className: "w-full sm:w-auto border-
|
|
103
|
+
/* @__PURE__ */ jsx(Button, { asChild: true, variant: "outline", size: "sm", className: "w-full sm:w-auto border-status-info-border text-status-info-text hover:bg-status-info-bg", children: /* @__PURE__ */ jsx("a", { href: `/backend/definitions/visual-editor?id=${definitionId}`, children: t("workflows.actions.openVisualEditor") }) })
|
|
104
104
|
] }),
|
|
105
105
|
/* @__PURE__ */ jsx(
|
|
106
106
|
CrudForm,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../../src/modules/workflows/backend/definitions/%5Bid%5D/page.tsx"],
|
|
4
|
-
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { useRouter, useParams } from 'next/navigation'\nimport { useQuery } from '@tanstack/react-query'\nimport { Page, PageBody } from '@open-mercato/ui/backend/Page'\nimport { CrudForm } from '@open-mercato/ui/backend/CrudForm'\nimport { Spinner } from '@open-mercato/ui/primitives/spinner'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { apiFetch } from '@open-mercato/ui/backend/utils/api'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport {\n workflowDefinitionFormSchema,\n createFormGroups,\n createFieldDefinitions,\n parseWorkflowToFormValues,\n buildWorkflowPayload,\n type WorkflowDefinitionFormValues,\n} from '../../../components/formConfig'\nimport { StepsEditor } from '../../../components/StepsEditor'\nimport { TransitionsEditor } from '../../../components/TransitionsEditor'\nimport { DefinitionTriggersEditor } from '../../../components/DefinitionTriggersEditor'\nimport { MobileDefinitionDetail } from '../../../components/mobile/MobileDefinitionDetail'\nimport { useIsMobile } from '@open-mercato/ui/hooks/useIsMobile'\nimport type { WorkflowDefinitionTrigger } from '../../../data/entities'\n\nexport default function EditWorkflowDefinitionPage() {\n const router = useRouter()\n const params = useParams()\n const t = useT()\n const isMobile = useIsMobile()\n\n // Handle catch-all route: params.slug = ['definitions', 'uuid']\n let definitionId: string | undefined\n if (params?.slug && Array.isArray(params.slug)) {\n definitionId = params.slug[1] // Second element is the ID\n } else if (params?.id) {\n definitionId = Array.isArray(params.id) ? params.id[0] : params.id\n }\n\n const { data: definition, isLoading, error } = useQuery({\n queryKey: ['workflow-definition', definitionId],\n queryFn: async () => {\n const response = await apiFetch(`/api/workflows/definitions/${definitionId}`)\n if (!response.ok) {\n throw new Error(t('workflows.errors.fetchFailed'))\n }\n const result = await response.json()\n return result.data\n },\n enabled: !!definitionId,\n })\n\n const initialValues = React.useMemo(() => {\n if (definition) {\n return parseWorkflowToFormValues(definition)\n }\n return null\n }, [definition])\n\n const [triggers, setTriggers] = React.useState<WorkflowDefinitionTrigger[]>([])\n\n React.useEffect(() => {\n setTriggers(initialValues?.triggers ?? [])\n }, [initialValues])\n\n const handleSubmit = async (values: WorkflowDefinitionFormValues) => {\n const payload = buildWorkflowPayload({ ...values, triggers })\n\n const response = await apiFetch(`/api/workflows/definitions/${definitionId}`, {\n method: 'PUT',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(payload),\n })\n\n if (!response.ok) {\n const error = await response.json()\n throw new Error(error.error || t('workflows.errors.updateFailed'))\n }\n\n router.push('/backend/definitions')\n router.refresh()\n }\n\n const fields = React.useMemo(() => createFieldDefinitions(t), [t])\n\n const formGroups = React.useMemo(\n () => isMobile ? [] : createFormGroups(t, StepsEditor, TransitionsEditor),\n [t, isMobile]\n )\n\n const navigateToVisualEditor = React.useCallback(() => {\n router.push(`/backend/definitions/visual-editor?id=${definitionId}`)\n }, [router, definitionId])\n\n if (isLoading) {\n return (\n <Page>\n <PageBody>\n <div className=\"flex h-[50vh] flex-col items-center justify-center gap-2 text-muted-foreground\">\n <Spinner className=\"h-6 w-6\" />\n <span>{t('workflows.edit.loading')}</span>\n </div>\n </PageBody>\n </Page>\n )\n }\n\n if (error || !definition) {\n return (\n <Page>\n <PageBody>\n <div className=\"flex h-[50vh] flex-col items-center justify-center gap-2 text-muted-foreground\">\n <p>{t('workflows.errors.loadFailed')}</p>\n <Button asChild variant=\"outline\">\n <a href=\"/backend/definitions\">{t('workflows.backToList')}</a>\n </Button>\n </div>\n </PageBody>\n </Page>\n )\n }\n\n if (!initialValues) {\n return null\n }\n\n return (\n <Page>\n <PageBody>\n <div className=\"mb-4 p-4 bg-
|
|
5
|
-
"mappings": "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
|
|
4
|
+
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { useRouter, useParams } from 'next/navigation'\nimport { useQuery } from '@tanstack/react-query'\nimport { Page, PageBody } from '@open-mercato/ui/backend/Page'\nimport { CrudForm } from '@open-mercato/ui/backend/CrudForm'\nimport { Spinner } from '@open-mercato/ui/primitives/spinner'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { apiFetch } from '@open-mercato/ui/backend/utils/api'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport {\n workflowDefinitionFormSchema,\n createFormGroups,\n createFieldDefinitions,\n parseWorkflowToFormValues,\n buildWorkflowPayload,\n type WorkflowDefinitionFormValues,\n} from '../../../components/formConfig'\nimport { StepsEditor } from '../../../components/StepsEditor'\nimport { TransitionsEditor } from '../../../components/TransitionsEditor'\nimport { DefinitionTriggersEditor } from '../../../components/DefinitionTriggersEditor'\nimport { MobileDefinitionDetail } from '../../../components/mobile/MobileDefinitionDetail'\nimport { useIsMobile } from '@open-mercato/ui/hooks/useIsMobile'\nimport type { WorkflowDefinitionTrigger } from '../../../data/entities'\n\nexport default function EditWorkflowDefinitionPage() {\n const router = useRouter()\n const params = useParams()\n const t = useT()\n const isMobile = useIsMobile()\n\n // Handle catch-all route: params.slug = ['definitions', 'uuid']\n let definitionId: string | undefined\n if (params?.slug && Array.isArray(params.slug)) {\n definitionId = params.slug[1] // Second element is the ID\n } else if (params?.id) {\n definitionId = Array.isArray(params.id) ? params.id[0] : params.id\n }\n\n const { data: definition, isLoading, error } = useQuery({\n queryKey: ['workflow-definition', definitionId],\n queryFn: async () => {\n const response = await apiFetch(`/api/workflows/definitions/${definitionId}`)\n if (!response.ok) {\n throw new Error(t('workflows.errors.fetchFailed'))\n }\n const result = await response.json()\n return result.data\n },\n enabled: !!definitionId,\n })\n\n const initialValues = React.useMemo(() => {\n if (definition) {\n return parseWorkflowToFormValues(definition)\n }\n return null\n }, [definition])\n\n const [triggers, setTriggers] = React.useState<WorkflowDefinitionTrigger[]>([])\n\n React.useEffect(() => {\n setTriggers(initialValues?.triggers ?? [])\n }, [initialValues])\n\n const handleSubmit = async (values: WorkflowDefinitionFormValues) => {\n const payload = buildWorkflowPayload({ ...values, triggers })\n\n const response = await apiFetch(`/api/workflows/definitions/${definitionId}`, {\n method: 'PUT',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(payload),\n })\n\n if (!response.ok) {\n const error = await response.json()\n throw new Error(error.error || t('workflows.errors.updateFailed'))\n }\n\n router.push('/backend/definitions')\n router.refresh()\n }\n\n const fields = React.useMemo(() => createFieldDefinitions(t), [t])\n\n const formGroups = React.useMemo(\n () => isMobile ? [] : createFormGroups(t, StepsEditor, TransitionsEditor),\n [t, isMobile]\n )\n\n const navigateToVisualEditor = React.useCallback(() => {\n router.push(`/backend/definitions/visual-editor?id=${definitionId}`)\n }, [router, definitionId])\n\n if (isLoading) {\n return (\n <Page>\n <PageBody>\n <div className=\"flex h-[50vh] flex-col items-center justify-center gap-2 text-muted-foreground\">\n <Spinner className=\"h-6 w-6\" />\n <span>{t('workflows.edit.loading')}</span>\n </div>\n </PageBody>\n </Page>\n )\n }\n\n if (error || !definition) {\n return (\n <Page>\n <PageBody>\n <div className=\"flex h-[50vh] flex-col items-center justify-center gap-2 text-muted-foreground\">\n <p>{t('workflows.errors.loadFailed')}</p>\n <Button asChild variant=\"outline\">\n <a href=\"/backend/definitions\">{t('workflows.backToList')}</a>\n </Button>\n </div>\n </PageBody>\n </Page>\n )\n }\n\n if (!initialValues) {\n return null\n }\n\n return (\n <Page>\n <PageBody>\n <div className=\"mb-4 p-4 bg-status-info-bg border border-status-info-border rounded-lg flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between\">\n <div className=\"flex items-start gap-3\">\n <svg className=\"w-5 h-5 text-status-info-icon\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M13 10V3L4 14h7v7l9-11h-7z\" />\n </svg>\n <div>\n <p className=\"text-sm font-medium text-status-info-text\">\n {t('workflows.edit.visualEditorAvailable')}\n </p>\n <p className=\"text-xs text-status-info-text mt-0.5\">\n {t('workflows.edit.visualEditorDescription')}\n </p>\n </div>\n </div>\n <Button asChild variant=\"outline\" size=\"sm\" className=\"w-full sm:w-auto border-status-info-border text-status-info-text hover:bg-status-info-bg\">\n <a href={`/backend/definitions/visual-editor?id=${definitionId}`}>\n {t('workflows.actions.openVisualEditor')}\n </a>\n </Button>\n </div>\n <CrudForm\n key={definitionId}\n title={t('workflows.edit.title')}\n backHref=\"/backend/definitions\"\n schema={workflowDefinitionFormSchema}\n fields={fields}\n initialValues={initialValues}\n onSubmit={handleSubmit}\n cancelHref=\"/backend/definitions\"\n groups={formGroups}\n submitLabel={t('workflows.form.update')}\n />\n\n {/* Mobile Steps & Transitions View */}\n {isMobile && initialValues && (\n <div className=\"mt-4\">\n <MobileDefinitionDetail\n values={initialValues}\n onEditStep={navigateToVisualEditor}\n onDeleteStep={navigateToVisualEditor}\n onAddStep={navigateToVisualEditor}\n onEditTransition={navigateToVisualEditor}\n onDeleteTransition={navigateToVisualEditor}\n onAddTransition={navigateToVisualEditor}\n />\n </div>\n )}\n\n {/* Event Triggers Section */}\n <div className=\"mt-8\">\n <DefinitionTriggersEditor\n value={triggers}\n onChange={setTriggers}\n />\n </div>\n </PageBody>\n </Page>\n )\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["error"]
|
|
7
7
|
}
|
|
@@ -598,7 +598,7 @@ function VisualEditorPage() {
|
|
|
598
598
|
className: "h-8 text-sm"
|
|
599
599
|
}
|
|
600
600
|
),
|
|
601
|
-
definitionId && /* @__PURE__ */ jsx("p", { className: "text-
|
|
601
|
+
definitionId && /* @__PURE__ */ jsx("p", { className: "text-overline text-muted-foreground", children: "Read-only" })
|
|
602
602
|
] }),
|
|
603
603
|
/* @__PURE__ */ jsxs("div", { className: "min-w-0 space-y-1", children: [
|
|
604
604
|
/* @__PURE__ */ jsx(Label, { htmlFor: "workflowName", className: "text-xs", children: "Name *" }),
|
|
@@ -768,7 +768,7 @@ function VisualEditorPage() {
|
|
|
768
768
|
"button",
|
|
769
769
|
{
|
|
770
770
|
onClick: () => handleAddNode(nodeType),
|
|
771
|
-
className: "flex shrink-0 items-center gap-1 rounded-md border bg-background px-2 py-1 text-xs hover:bg-muted active:bg-muted/
|
|
771
|
+
className: "flex shrink-0 items-center gap-1 rounded-md border bg-background px-2 py-1 text-xs hover:bg-muted active:bg-muted/50",
|
|
772
772
|
children: [
|
|
773
773
|
/* @__PURE__ */ jsx(Icon, { className: "h-3.5 w-3.5" }),
|
|
774
774
|
/* @__PURE__ */ jsx("span", { children: NODE_TYPE_LABELS[nodeType].title })
|