@hartl-services/medusa-base 0.3.0 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.medusa/server/src/admin/index.js +2164 -29
- package/.medusa/server/src/admin/index.mjs +2166 -31
- package/.medusa/server/src/api/admin/email/messages/[id]/resends/route.js +24 -0
- package/.medusa/server/src/api/admin/email/messages/[id]/route.js +12 -0
- package/.medusa/server/src/api/admin/email/messages/route.js +12 -0
- package/.medusa/server/src/api/admin/email/terms-versions/[id]/download/route.js +16 -0
- package/.medusa/server/src/api/admin/email/terms-versions/route.js +47 -0
- package/.medusa/server/src/api/admin/orders/[id]/documents/[document_id]/download/route.js +21 -0
- package/.medusa/server/src/api/admin/orders/[id]/documents/[document_id]/regenerate/route.js +38 -0
- package/.medusa/server/src/api/admin/orders/[id]/documents/regenerate/route.js +32 -0
- package/.medusa/server/src/api/admin/orders/[id]/documents/route.js +36 -0
- package/.medusa/server/src/api/admin/orders/[id]/packing-lists/[fulfillment_id]/download/route.js +24 -0
- package/.medusa/server/src/api/admin/orders/[id]/packing-lists/[fulfillment_id]/retry/route.js +44 -0
- package/.medusa/server/src/api/admin/orders/[id]/packing-lists/route.js +82 -0
- package/.medusa/server/src/api/admin/plugin-features/[plugin_key]/actions/[action_key]/route.js +31 -0
- package/.medusa/server/src/api/admin/plugin-features/[plugin_key]/actions/[action_key]/runs/route.js +16 -0
- package/.medusa/server/src/api/admin/plugin-features/[plugin_key]/route.js +14 -0
- package/.medusa/server/src/api/admin/plugin-features/[plugin_key]/settings/route.js +22 -0
- package/.medusa/server/src/api/admin/plugin-features/route.js +6 -4
- package/.medusa/server/src/api/document-download-response.js +16 -0
- package/.medusa/server/src/api/document-response.js +23 -0
- package/.medusa/server/src/api/files/[asset]/route.js +24 -0
- package/.medusa/server/src/api/files/file-response.js +43 -0
- package/.medusa/server/src/api/files/gateway-options.js +31 -0
- package/.medusa/server/src/api/files/private/[token]/route.js +29 -0
- package/.medusa/server/src/api/files/upload/[token]/route.js +66 -0
- package/.medusa/server/src/api/middlewares.js +186 -5
- package/.medusa/server/src/api/packing-list-response.js +22 -0
- package/.medusa/server/src/api/plugin-actions.js +221 -0
- package/.medusa/server/src/api/plugin-features-http.js +67 -8
- package/.medusa/server/src/api/route.js +27 -0
- package/.medusa/server/src/api/store/documents/[id]/download/route.js +21 -0
- package/.medusa/server/src/api/store/documents/[id]/route.js +15 -0
- package/.medusa/server/src/api/store/documents/route.js +11 -0
- package/.medusa/server/src/api/terms-download-response.js +12 -0
- package/.medusa/server/src/documents/config/index.js +18 -0
- package/.medusa/server/src/documents/paths/documents.js +6 -0
- package/.medusa/server/src/documents/paths/index.js +18 -0
- package/.medusa/server/src/documents/plugin-features.js +24 -0
- package/.medusa/server/src/documents/schemas/documents.js +18 -0
- package/.medusa/server/src/documents/schemas/index.js +18 -0
- package/.medusa/server/src/documents/scripts/verify-documents-file-storage.js +55 -0
- package/.medusa/server/src/documents/templates/financial-document.js +142 -0
- package/.medusa/server/src/documents/templates/format.js +31 -0
- package/.medusa/server/src/documents/templates/html-factory.js +40 -0
- package/.medusa/server/src/documents/templates/html-utils.js +13 -0
- package/.medusa/server/src/documents/templates/layout.js +97 -0
- package/.medusa/server/src/documents/templates/packing-list.js +50 -0
- package/.medusa/server/src/documents/types/admin-api.js +3 -0
- package/.medusa/server/src/documents/types/document-summary.js +3 -0
- package/.medusa/server/src/documents/types/documents.js +3 -0
- package/.medusa/server/src/documents/types/html-factory.js +6 -0
- package/.medusa/server/src/documents/types/index.js +22 -0
- package/.medusa/server/src/documents/types/store-api.js +3 -0
- package/.medusa/server/src/documents/utils/document-merchant.js +22 -0
- package/.medusa/server/src/documents/utils/html-to-pdf.js +61 -0
- package/.medusa/server/src/documents/utils/private-file-storage.js +64 -0
- package/.medusa/server/src/documents/utils/resolve-services.js +7 -0
- package/.medusa/server/src/email/application/handle-invite-event.js +312 -0
- package/.medusa/server/src/email/application/handle-password-reset-event.js +354 -0
- package/.medusa/server/src/email/config/index.js +84 -0
- package/.medusa/server/src/email/config/notification-module.js +20 -0
- package/.medusa/server/src/email/domain/business-keys.js +50 -0
- package/.medusa/server/src/email/domain/documents-fact-fingerprint.js +7 -0
- package/.medusa/server/src/email/domain/event-schemas.js +36 -0
- package/.medusa/server/src/email/domain/failure-classification.js +53 -0
- package/.medusa/server/src/email/plugin-features.js +31 -0
- package/.medusa/server/src/email/rendering/default-templates.js +91 -0
- package/.medusa/server/src/email/rendering/render-email.js +37 -0
- package/.medusa/server/src/email/rendering/resolve-shop-configuration.js +48 -0
- package/.medusa/server/src/email/types/email-kinds.js +18 -0
- package/.medusa/server/src/email/types/index.js +21 -0
- package/.medusa/server/src/email/types/shop-configuration.js +3 -0
- package/.medusa/server/src/email/types/smtp-configuration.js +3 -0
- package/.medusa/server/src/email/types/template-data.js +79 -0
- package/.medusa/server/src/files/config/index.js +256 -0
- package/.medusa/server/src/files/gateway/index.js +115 -0
- package/.medusa/server/src/files/types/index.js +6 -0
- package/.medusa/server/src/files/types/public.js +6 -0
- package/.medusa/server/src/jobs/document-fact-publication-reconciliation.js +18 -0
- package/.medusa/server/src/jobs/document-resolution-reconciliation.js +20 -0
- package/.medusa/server/src/jobs/packing-list-reconciliation.js +49 -0
- package/.medusa/server/src/jobs/reconcile-email-attempts.js +63 -0
- package/.medusa/server/src/jobs/reconcile-pending-email-messages.js +41 -0
- package/.medusa/server/src/jobs/retry-email-messages.js +27 -0
- package/.medusa/server/src/migration-scripts/dismiss-orphaned-cart-promotion-links.js +26 -0
- package/.medusa/server/src/modules/documents/domain/cancellation-snapshot.js +97 -0
- package/.medusa/server/src/modules/documents/domain/document-number.js +36 -0
- package/.medusa/server/src/modules/documents/domain/document-resolution.js +24 -0
- package/.medusa/server/src/modules/documents/domain/issuance-rules.js +37 -0
- package/.medusa/server/src/modules/documents/domain/merchant.js +86 -0
- package/.medusa/server/src/modules/documents/domain/options.js +40 -0
- package/.medusa/server/src/modules/documents/domain/packing-list.js +90 -0
- package/.medusa/server/src/modules/documents/domain/snapshot-normalization.js +17 -0
- package/.medusa/server/src/modules/documents/domain/tax-breakdown.js +66 -0
- package/.medusa/server/src/modules/documents/index.js +15 -0
- package/.medusa/server/src/modules/documents/loaders/assert-private-file-storage.js +20 -0
- package/.medusa/server/src/modules/documents/migrations/Migration20260827061749.js +75 -0
- package/.medusa/server/src/modules/documents/migrations/Migration20260830111207.js +28 -0
- package/.medusa/server/src/modules/documents/models/document-artifact.js +25 -0
- package/.medusa/server/src/modules/documents/models/document-number-sequence.js +29 -0
- package/.medusa/server/src/modules/documents/models/document.js +82 -0
- package/.medusa/server/src/modules/documents/models/order-cancellation-resolution.js +51 -0
- package/.medusa/server/src/modules/documents/models/packing-list-revision.js +36 -0
- package/.medusa/server/src/modules/documents/models/packing-list.js +27 -0
- package/.medusa/server/src/modules/documents/models/refund-document-resolution.js +66 -0
- package/.medusa/server/src/modules/documents/models/shipment-invoice-assignment.js +51 -0
- package/.medusa/server/src/modules/documents/models/shipment-invoice-coordinator.js +44 -0
- package/.medusa/server/src/modules/documents/service.js +1701 -0
- package/.medusa/server/src/modules/email/domain/attachment.js +25 -0
- package/.medusa/server/src/modules/email/domain/message-state.js +69 -0
- package/.medusa/server/src/modules/email/index.js +13 -0
- package/.medusa/server/src/modules/email/migrations/Migration20260826173422.js +32 -0
- package/.medusa/server/src/modules/email/migrations/Migration20260827172945.js +16 -0
- package/.medusa/server/src/modules/email/migrations/Migration20260827194804.js +22 -0
- package/.medusa/server/src/modules/email/migrations/Migration20260906195205.js +16 -0
- package/.medusa/server/src/modules/email/models/email-attempt.js +56 -0
- package/.medusa/server/src/modules/email/models/email-message.js +71 -0
- package/.medusa/server/src/modules/email/models/email-terms-version.js +37 -0
- package/.medusa/server/src/modules/email/service.js +1285 -0
- package/.medusa/server/src/modules/plugin-features/domain/plugin-action-runs.js +92 -0
- package/.medusa/server/src/modules/plugin-features/domain/plugin-settings.js +213 -0
- package/.medusa/server/src/modules/plugin-features/domain/secret-box.js +56 -0
- package/.medusa/server/src/modules/plugin-features/migrations/Migration20260928205031.js +16 -0
- package/.medusa/server/src/modules/plugin-features/migrations/Migration20260928215428.js +17 -0
- package/.medusa/server/src/modules/plugin-features/models/plugin-action-run.js +43 -0
- package/.medusa/server/src/modules/plugin-features/models/plugin-setting-state.js +32 -0
- package/.medusa/server/src/modules/plugin-features/service.js +419 -17
- package/.medusa/server/src/providers/s3-cache/cache/atomic-write.js +25 -0
- package/.medusa/server/src/providers/s3-cache/cache/cache-manager.js +115 -0
- package/.medusa/server/src/providers/s3-cache/cache/keyed-lock.js +28 -0
- package/.medusa/server/src/providers/s3-cache/index.js +8 -0
- package/.medusa/server/src/providers/s3-cache/object-key.js +53 -0
- package/.medusa/server/src/providers/s3-cache/s3-cache-file.js +229 -0
- package/.medusa/server/src/providers/s3-cache/s3-object-store.js +86 -0
- package/.medusa/server/src/providers/smtp/index.js +8 -0
- package/.medusa/server/src/providers/smtp/service.js +236 -0
- package/.medusa/server/src/providers/smtp/types.js +11 -0
- package/.medusa/server/src/public/action-progress.js +67 -0
- package/.medusa/server/src/public/config.js +186 -0
- package/.medusa/server/src/public/descriptor.js +199 -2
- package/.medusa/server/src/public/effective-features.js +9 -5
- package/.medusa/server/src/public/enforcement.js +18 -8
- package/.medusa/server/src/public/gateway.js +19 -0
- package/.medusa/server/src/public/index.js +10 -2
- package/.medusa/server/src/public/instrumentation.js +56 -0
- package/.medusa/server/src/public/module-access.js +14 -0
- package/.medusa/server/src/public/settings.js +17 -0
- package/.medusa/server/src/public/shop-config.js +12 -0
- package/.medusa/server/src/public/shop-master-data.js +95 -0
- package/.medusa/server/src/public/shop-types.js +24 -0
- package/.medusa/server/src/shop-config/build-food-shop-config.js +248 -0
- package/.medusa/server/src/shop-config/options.js +71 -0
- package/.medusa/server/src/subscribers/affiliate-consignment-reminder-due.js +26 -0
- package/.medusa/server/src/subscribers/documents-order-canceled.js +24 -0
- package/.medusa/server/src/subscribers/documents-order-cancellation-ready.js +26 -0
- package/.medusa/server/src/subscribers/documents-refund-ready.js +24 -0
- package/.medusa/server/src/subscribers/documents-shipment-created.js +36 -0
- package/.medusa/server/src/subscribers/documents-shipment-invoice-assignment-decided.js +26 -0
- package/.medusa/server/src/subscribers/email-order-canceled.js +28 -0
- package/.medusa/server/src/subscribers/email-shipment-created.js +32 -0
- package/.medusa/server/src/subscribers/order-placed.js +27 -0
- package/.medusa/server/src/subscribers/packing-list-allocation-revised.js +32 -0
- package/.medusa/server/src/subscribers/packing-list-fulfillment-canceled.js +54 -0
- package/.medusa/server/src/subscribers/packing-list-fulfillment-created.js +31 -0
- package/.medusa/server/src/subscribers/password-reset.js +23 -0
- package/.medusa/server/src/subscribers/payment-refunded.js +38 -0
- package/.medusa/server/src/subscribers/return-received.js +27 -0
- package/.medusa/server/src/subscribers/return-requested.js +27 -0
- package/.medusa/server/src/subscribers/user-invite.js +23 -0
- package/.medusa/server/src/utils/cart-promotion-links.js +101 -0
- package/.medusa/server/src/utils/discover-complete-cart-participants.js +9 -2
- package/.medusa/server/src/utils/discover-plugin-features.js +47 -9
- package/.medusa/server/src/workflows/documents/issue-cancellation-after-refund.js +246 -0
- package/.medusa/server/src/workflows/documents/issue-document.js +163 -0
- package/.medusa/server/src/workflows/documents/issue-invoice-after-shipment.js +521 -0
- package/.medusa/server/src/workflows/documents/issue-packing-list.js +384 -0
- package/.medusa/server/src/workflows/documents/publish-document-facts.js +53 -0
- package/.medusa/server/src/workflows/documents/reconcile-document-resolutions.js +48 -0
- package/.medusa/server/src/workflows/documents/reconcile-order-cancellation.js +92 -0
- package/.medusa/server/src/workflows/documents/regenerate-issued-document.js +61 -0
- package/.medusa/server/src/workflows/email/dispatch-email-message.js +299 -0
- package/.medusa/server/src/workflows/email/documents-required-facts.js +30 -0
- package/.medusa/server/src/workflows/email/prepare-affiliate-reminder-email.js +58 -0
- package/.medusa/server/src/workflows/email/prepare-order-canceled-email.js +64 -0
- package/.medusa/server/src/workflows/email/prepare-order-received-email.js +56 -0
- package/.medusa/server/src/workflows/email/prepare-refund-email.js +47 -0
- package/.medusa/server/src/workflows/email/prepare-return-email.js +75 -0
- package/.medusa/server/src/workflows/email/prepare-shipment-email.js +82 -0
- package/.medusa/server/src/workflows/email/retry-email-messages.js +31 -0
- package/.medusa/server/src/workflows/hooks/complete-cart-participants.js +28 -16
- package/.medusa/server/src/workflows/hooks/dismiss-deleted-promotion-cart-links.js +23 -0
- package/README.md +2004 -11
- package/dist/admin-components/features/plugin-features/components/PluginActionsSection.d.ts +6 -0
- package/dist/admin-components/features/plugin-features/components/PluginActionsSection.js +66 -0
- package/dist/admin-components/features/plugin-features/components/PluginDetailPage.d.ts +2 -0
- package/dist/admin-components/features/plugin-features/components/PluginDetailPage.js +9 -0
- package/dist/admin-components/features/plugin-features/components/PluginFeatureGate.d.ts +22 -0
- package/dist/admin-components/features/plugin-features/components/PluginFeatureGate.js +18 -0
- package/dist/admin-components/features/plugin-features/components/PluginFeatureSections.d.ts +11 -0
- package/dist/admin-components/features/plugin-features/components/PluginFeatureSections.js +19 -0
- package/dist/admin-components/features/plugin-features/components/PluginFeaturesCard.d.ts +12 -0
- package/dist/admin-components/features/plugin-features/components/PluginFeaturesCard.js +34 -0
- package/dist/admin-components/features/plugin-features/components/PluginFeaturesSettingsPage.d.ts +5 -0
- package/dist/admin-components/features/plugin-features/components/PluginFeaturesSettingsPage.js +16 -0
- package/dist/admin-components/features/plugin-features/components/PluginHubCard.d.ts +7 -0
- package/dist/admin-components/features/plugin-features/components/PluginHubCard.js +21 -0
- package/dist/admin-components/features/plugin-features/components/PluginPageShell.d.ts +13 -0
- package/dist/admin-components/features/plugin-features/components/PluginPageShell.js +20 -0
- package/dist/admin-components/features/plugin-features/components/PluginSettingsForm.d.ts +7 -0
- package/dist/admin-components/features/plugin-features/components/PluginSettingsForm.js +39 -0
- package/dist/admin-components/features/plugin-features/components/PluginStatusChips.d.ts +5 -0
- package/dist/admin-components/features/plugin-features/components/PluginStatusChips.js +8 -0
- package/dist/admin-components/features/plugin-features/hooks/usePluginFeatureEnabled.d.ts +114 -0
- package/dist/admin-components/features/plugin-features/hooks/usePluginFeatureEnabled.js +40 -0
- package/dist/admin-components/features/plugin-features/hooks/usePluginFeatures.d.ts +392 -0
- package/dist/admin-components/features/plugin-features/hooks/usePluginFeatures.js +128 -0
- package/dist/admin-components/features/plugin-features/hooks/usePluginSettingsForm.d.ts +93 -0
- package/dist/admin-components/features/plugin-features/hooks/usePluginSettingsForm.js +44 -0
- package/dist/admin-components/features/plugin-features/lib/action-runs.d.ts +19 -0
- package/dist/admin-components/features/plugin-features/lib/action-runs.js +74 -0
- package/dist/admin-components/features/plugin-features/lib/plugin-features-admin-client.d.ts +363 -0
- package/dist/admin-components/features/plugin-features/lib/plugin-features-admin-client.js +9 -0
- package/dist/admin-components/features/plugin-features/lib/plugin-paths.d.ts +4 -0
- package/dist/admin-components/features/plugin-features/lib/plugin-paths.js +4 -0
- package/dist/admin-components/features/plugin-features/lib/settings-draft.d.ts +28 -0
- package/dist/admin-components/features/plugin-features/lib/settings-draft.js +86 -0
- package/dist/admin-components/features/plugin-features/queries/pluginFeatureKeys.d.ts +7 -0
- package/dist/admin-components/features/plugin-features/queries/pluginFeatureKeys.js +7 -0
- package/dist/admin-components/i18n/texts.d.ts +90 -0
- package/dist/admin-components/i18n/texts.js +90 -0
- package/dist/admin-components/index.d.ts +14 -0
- package/dist/admin-components/index.js +14 -0
- package/dist/admin-components/lib/errors.d.ts +6 -0
- package/dist/admin-components/lib/errors.js +27 -0
- package/dist/admin-components/lib/sdk.d.ts +2 -0
- package/dist/admin-components/lib/sdk.js +13 -0
- package/dist/documents/config/index.d.ts +11 -0
- package/dist/documents/config/index.js +17 -0
- package/dist/documents/templates/financial-document.d.ts +3 -0
- package/dist/documents/templates/financial-document.js +141 -0
- package/dist/documents/templates/format.d.ts +16 -0
- package/dist/documents/templates/format.js +30 -0
- package/dist/documents/templates/html-factory.d.ts +11 -0
- package/dist/documents/templates/html-factory.js +39 -0
- package/dist/documents/templates/html-utils.d.ts +2 -0
- package/dist/documents/templates/html-utils.js +12 -0
- package/dist/documents/templates/layout.d.ts +17 -0
- package/dist/documents/templates/layout.js +96 -0
- package/dist/documents/templates/packing-list.d.ts +3 -0
- package/dist/documents/templates/packing-list.js +49 -0
- package/dist/documents/types/admin-api.d.ts +1 -0
- package/dist/documents/types/admin-api.js +2 -0
- package/dist/documents/types/document-summary.d.ts +1 -0
- package/dist/documents/types/document-summary.js +2 -0
- package/dist/documents/types/documents.d.ts +174 -0
- package/dist/documents/types/documents.js +2 -0
- package/dist/documents/types/html-factory.d.ts +7 -0
- package/dist/documents/types/html-factory.js +5 -0
- package/dist/documents/types/index.d.ts +5 -0
- package/dist/documents/types/index.js +21 -0
- package/dist/documents/types/store-api.d.ts +1 -0
- package/dist/documents/types/store-api.js +2 -0
- package/dist/email/config/index.d.ts +17 -0
- package/dist/email/config/index.js +83 -0
- package/dist/email/config/notification-module.d.ts +22 -0
- package/dist/email/config/notification-module.js +19 -0
- package/dist/email/types/email-kinds.d.ts +15 -0
- package/dist/email/types/email-kinds.js +17 -0
- package/dist/email/types/index.d.ts +4 -0
- package/dist/email/types/index.js +20 -0
- package/dist/email/types/shop-configuration.d.ts +43 -0
- package/dist/email/types/shop-configuration.js +2 -0
- package/dist/email/types/smtp-configuration.d.ts +9 -0
- package/dist/email/types/smtp-configuration.js +2 -0
- package/dist/email/types/template-data.d.ts +100 -0
- package/dist/email/types/template-data.js +78 -0
- package/dist/files/config/index.d.ts +32 -0
- package/dist/files/config/index.js +255 -0
- package/dist/files/gateway/index.d.ts +30 -0
- package/dist/files/gateway/index.js +114 -0
- package/dist/files/types/index.d.ts +2 -0
- package/dist/files/types/index.js +5 -0
- package/dist/files/types/public.d.ts +90 -0
- package/dist/files/types/public.js +5 -0
- package/dist/modules/documents/domain/merchant.d.ts +22 -0
- package/dist/modules/documents/domain/merchant.js +85 -0
- package/dist/modules/documents/domain/options.d.ts +34 -0
- package/dist/modules/documents/domain/options.js +39 -0
- package/dist/public/action-progress.d.ts +25 -0
- package/dist/public/action-progress.js +66 -0
- package/dist/public/config.d.ts +59 -0
- package/dist/public/config.js +185 -0
- package/dist/public/descriptor.d.ts +3 -2
- package/dist/public/descriptor.js +198 -1
- package/dist/public/effective-features.d.ts +2 -1
- package/dist/public/effective-features.js +8 -4
- package/dist/public/enforcement.d.ts +7 -0
- package/dist/public/enforcement.js +17 -7
- package/dist/public/gateway.d.ts +2 -0
- package/dist/public/gateway.js +18 -0
- package/dist/public/index.d.ts +5 -2
- package/dist/public/index.js +9 -1
- package/dist/public/instrumentation.d.ts +12 -0
- package/dist/public/instrumentation.js +55 -0
- package/dist/public/module-access.d.ts +3 -0
- package/dist/public/module-access.js +13 -0
- package/dist/public/settings.d.ts +11 -0
- package/dist/public/settings.js +16 -0
- package/dist/public/shop-config.d.ts +8 -0
- package/dist/public/shop-config.js +11 -0
- package/dist/public/shop-master-data.d.ts +25 -0
- package/dist/public/shop-master-data.js +94 -0
- package/dist/public/shop-types.d.ts +7 -0
- package/dist/public/shop-types.js +23 -0
- package/dist/public/types.d.ts +113 -1
- package/dist/shop-config/build-food-shop-config.d.ts +44 -0
- package/dist/shop-config/build-food-shop-config.js +247 -0
- package/dist/shop-config/options.d.ts +41 -0
- package/dist/shop-config/options.js +70 -0
- package/dist/utils/discover-plugin-features.d.ts +28 -0
- package/dist/utils/discover-plugin-features.js +96 -0
- package/package.json +53 -6
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { AdminPluginFeatures } from "@hartl-services/medusa-food-supplements-contracts/types/plugin-features-admin";
|
|
2
|
+
type PluginActionsSectionProps = {
|
|
3
|
+
plugin: AdminPluginFeatures;
|
|
4
|
+
};
|
|
5
|
+
export declare const PluginActionsSection: ({ plugin }: PluginActionsSectionProps) => import("react/jsx-runtime").JSX.Element;
|
|
6
|
+
export {};
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Button, Container, Heading, Text, toast, usePrompt } from "@medusajs/ui";
|
|
3
|
+
import { useEffect, useRef } from "react";
|
|
4
|
+
import { texts } from "../../../i18n/texts";
|
|
5
|
+
import { useLatestPluginActionRun, useRunPluginAction, } from "../hooks/usePluginFeatures";
|
|
6
|
+
import { formatRunTime, progressCount, progressPercent, runTransition, summarizeActionResult, } from "../lib/action-runs";
|
|
7
|
+
/** Progress of a running run: label, "current / total", bar and start time. */
|
|
8
|
+
const RunProgress = ({ run }) => {
|
|
9
|
+
const percent = progressPercent(run.progress);
|
|
10
|
+
const count = progressCount(run.progress);
|
|
11
|
+
const label = run.progress?.label;
|
|
12
|
+
return (_jsxs("div", { className: "mt-2 flex flex-col gap-y-1", role: "status", children: [(label || count) && (_jsx(Text, { size: "small", children: [label, count].filter(Boolean).join(" · ") })), percent !== null && (_jsx("div", { className: "bg-ui-border-base w-full overflow-hidden rounded-full", style: { height: 6 }, role: "progressbar", "aria-valuemin": 0, "aria-valuemax": 100, "aria-valuenow": percent, children: _jsx("div", { className: "bg-ui-fg-interactive h-full rounded-full", style: { width: `${percent}%`, height: "100%" } }) })), _jsx(Text, { size: "small", className: "text-ui-fg-muted", children: texts.actions.runningSince(formatRunTime(run.started_at)) })] }));
|
|
13
|
+
};
|
|
14
|
+
/** Outcome of the last finished run. */
|
|
15
|
+
const RunStatusLine = ({ run }) => {
|
|
16
|
+
if (run.status === "completed") {
|
|
17
|
+
const summary = summarizeActionResult(run.result);
|
|
18
|
+
return (_jsxs("div", { className: "mt-1", children: [_jsx(Text, { size: "small", className: "text-ui-fg-subtle", children: texts.actions.lastCompleted(formatRunTime(run.finished_at ?? run.started_at)) }), summary && (_jsx(Text, { size: "small", className: "text-ui-fg-subtle", children: summary }))] }));
|
|
19
|
+
}
|
|
20
|
+
return (_jsxs("div", { className: "mt-1", children: [_jsx(Text, { size: "small", className: "text-ui-fg-error", children: texts.actions.lastFailed(formatRunTime(run.finished_at ?? run.started_at)) }), run.message && (_jsx(Text, { size: "small", className: "text-ui-fg-error", children: run.message }))] }));
|
|
21
|
+
};
|
|
22
|
+
const PluginActionRow = ({ pluginKey, action, starting, blocked, onRun, }) => {
|
|
23
|
+
const { data: run } = useLatestPluginActionRun(pluginKey, action.key);
|
|
24
|
+
const previous = useRef(undefined);
|
|
25
|
+
const running = run?.status === "running";
|
|
26
|
+
// Toast when a run this page watched while running has finished.
|
|
27
|
+
useEffect(() => {
|
|
28
|
+
const transition = runTransition(previous.current, run);
|
|
29
|
+
previous.current = run;
|
|
30
|
+
if (transition === "completed") {
|
|
31
|
+
toast.success(texts.toasts.actionCompleted(action.label), {
|
|
32
|
+
description: summarizeActionResult(run?.result) ?? undefined,
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
else if (transition === "failed") {
|
|
36
|
+
toast.error(texts.toasts.actionFailed(action.label), {
|
|
37
|
+
description: run?.message ?? texts.errors.unknown,
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
}, [run, action.label]);
|
|
41
|
+
return (_jsxs("div", { className: "flex items-start justify-between gap-4 px-6 py-4", children: [_jsxs("div", { className: "min-w-0 flex-1", children: [_jsx(Text, { size: "small", weight: "plus", children: action.label }), action.description && (_jsx(Text, { size: "small", className: "text-ui-fg-subtle", children: action.description })), run && (running ? _jsx(RunProgress, { run: run }) : _jsx(RunStatusLine, { run: run }))] }), _jsx(Button, { size: "small", variant: "secondary", className: "shrink-0", isLoading: starting || running, disabled: blocked || starting || running, onClick: () => onRun(action), children: action.label })] }));
|
|
42
|
+
};
|
|
43
|
+
export const PluginActionsSection = ({ plugin }) => {
|
|
44
|
+
const mutation = useRunPluginAction(plugin.plugin_key);
|
|
45
|
+
const prompt = usePrompt();
|
|
46
|
+
const startingKey = mutation.isPending ? mutation.variables?.key : undefined;
|
|
47
|
+
const run = async (action) => {
|
|
48
|
+
if (mutation.isPending) {
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
if (action.confirm) {
|
|
52
|
+
const confirmed = await prompt({
|
|
53
|
+
title: texts.actions.confirmTitle(action.label),
|
|
54
|
+
description: action.confirm,
|
|
55
|
+
confirmText: texts.actions.confirm,
|
|
56
|
+
cancelText: texts.actions.cancel,
|
|
57
|
+
variant: "confirmation",
|
|
58
|
+
});
|
|
59
|
+
if (!confirmed) {
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
mutation.mutate(action);
|
|
64
|
+
};
|
|
65
|
+
return (_jsxs(Container, { className: "divide-y p-0", children: [_jsx("div", { className: "px-6 py-4", children: _jsx(Heading, { level: "h2", children: texts.sections.actions }) }), plugin.actions.map((action) => (_jsx(PluginActionRow, { pluginKey: plugin.plugin_key, action: action, starting: startingKey === action.key, blocked: mutation.isPending && startingKey !== action.key, onRun: (entry) => void run(entry) }, action.key)))] }));
|
|
66
|
+
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { useParams } from "react-router-dom";
|
|
3
|
+
import { PluginFeatureSections } from "./PluginFeatureSections";
|
|
4
|
+
import { PluginPageShell } from "./PluginPageShell";
|
|
5
|
+
/** The base's generic page of a plugin without an own `admin_page`. */
|
|
6
|
+
export const PluginDetailPage = () => {
|
|
7
|
+
const { plugin_key: pluginKey = "" } = useParams();
|
|
8
|
+
return (_jsx(PluginPageShell, { pluginKey: pluginKey, children: _jsx(PluginFeatureSections, { pluginKey: pluginKey }) }));
|
|
9
|
+
};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { ReactNode } from "react";
|
|
2
|
+
type PluginFeatureGateProps = {
|
|
3
|
+
pluginKey: string;
|
|
4
|
+
/** All must be on; none = plugin switch only. */
|
|
5
|
+
featureKeys?: readonly string[];
|
|
6
|
+
/** Rendered while loading, on error and when disabled; `null` by default. */
|
|
7
|
+
fallback?: ReactNode;
|
|
8
|
+
children: ReactNode;
|
|
9
|
+
};
|
|
10
|
+
/** Renders `children` only while the plugin and all `featureKeys` are on. */
|
|
11
|
+
export declare const PluginFeatureGate: ({ pluginKey, featureKeys, fallback, children, }: PluginFeatureGateProps) => import("react/jsx-runtime").JSX.Element;
|
|
12
|
+
type PluginFeatureDisabledNoticeProps = {
|
|
13
|
+
pluginKey: string;
|
|
14
|
+
/** Named in the text when given. */
|
|
15
|
+
featureLabel?: string;
|
|
16
|
+
};
|
|
17
|
+
/**
|
|
18
|
+
* For a full page whose sidebar entry cannot be hidden: says the feature is
|
|
19
|
+
* off and links to the hub.
|
|
20
|
+
*/
|
|
21
|
+
export declare const PluginFeatureDisabledNotice: ({ pluginKey, featureLabel, }: PluginFeatureDisabledNoticeProps) => import("react/jsx-runtime").JSX.Element;
|
|
22
|
+
export {};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Container, Text } from "@medusajs/ui";
|
|
3
|
+
import { Link } from "react-router-dom";
|
|
4
|
+
import { texts } from "../../../i18n/texts";
|
|
5
|
+
import { usePluginFeatureEnabled } from "../hooks/usePluginFeatureEnabled";
|
|
6
|
+
import { PLUGIN_HUB_PATH } from "../lib/plugin-paths";
|
|
7
|
+
/** Renders `children` only while the plugin and all `featureKeys` are on. */
|
|
8
|
+
export const PluginFeatureGate = ({ pluginKey, featureKeys = [], fallback = null, children, }) => {
|
|
9
|
+
const { enabled } = usePluginFeatureEnabled(pluginKey, ...featureKeys);
|
|
10
|
+
return _jsx(_Fragment, { children: enabled ? children : fallback });
|
|
11
|
+
};
|
|
12
|
+
/**
|
|
13
|
+
* For a full page whose sidebar entry cannot be hidden: says the feature is
|
|
14
|
+
* off and links to the hub.
|
|
15
|
+
*/
|
|
16
|
+
export const PluginFeatureDisabledNotice = ({ pluginKey, featureLabel, }) => (_jsxs(Container, { className: "flex flex-col gap-2 px-6 py-4", "data-plugin-key": pluginKey, children: [_jsx(Text, { className: "text-ui-fg-subtle", children: featureLabel
|
|
17
|
+
? texts.gate.disabledNamed(featureLabel)
|
|
18
|
+
: texts.gate.disabled }), _jsx("div", { children: _jsx(Link, { to: PLUGIN_HUB_PATH, className: "text-ui-fg-interactive hover:text-ui-fg-interactive-hover txt-compact-small-plus", children: texts.gate.openHub }) })] }));
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
type PluginFeatureSectionsProps = {
|
|
2
|
+
pluginKey: string;
|
|
3
|
+
};
|
|
4
|
+
/**
|
|
5
|
+
* The base's sections of one plugin: Funktionen (unless the plugin has
|
|
6
|
+
* neither a switch nor features), Einstellungen (when the plugin declares
|
|
7
|
+
* settings) and Aktionen (when it declares actions). Renders
|
|
8
|
+
* nothing until the plugin is loaded; `PluginPageShell` shows that state.
|
|
9
|
+
*/
|
|
10
|
+
export declare const PluginFeatureSections: ({ pluginKey }: PluginFeatureSectionsProps) => import("react/jsx-runtime").JSX.Element | null;
|
|
11
|
+
export {};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { texts } from "../../../i18n/texts";
|
|
3
|
+
import { usePluginFeature } from "../hooks/usePluginFeatures";
|
|
4
|
+
import { PluginActionsSection } from "./PluginActionsSection";
|
|
5
|
+
import { PluginFeaturesCard } from "./PluginFeaturesCard";
|
|
6
|
+
import { PluginSettingsForm } from "./PluginSettingsForm";
|
|
7
|
+
/**
|
|
8
|
+
* The base's sections of one plugin: Funktionen (unless the plugin has
|
|
9
|
+
* neither a switch nor features), Einstellungen (when the plugin declares
|
|
10
|
+
* settings) and Aktionen (when it declares actions). Renders
|
|
11
|
+
* nothing until the plugin is loaded; `PluginPageShell` shows that state.
|
|
12
|
+
*/
|
|
13
|
+
export const PluginFeatureSections = ({ pluginKey }) => {
|
|
14
|
+
const plugin = usePluginFeature(pluginKey).data?.plugin;
|
|
15
|
+
if (!plugin) {
|
|
16
|
+
return null;
|
|
17
|
+
}
|
|
18
|
+
return (_jsxs(_Fragment, { children: [(plugin.switchable || plugin.features.length > 0) && (_jsx(PluginFeaturesCard, { plugin: plugin, title: texts.sections.features })), plugin.settings.length > 0 && _jsx(PluginSettingsForm, { plugin: plugin }), plugin.actions.length > 0 && _jsx(PluginActionsSection, { plugin: plugin })] }));
|
|
19
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { AdminPluginFeatures } from "@hartl-services/medusa-food-supplements-contracts/types/plugin-features-admin";
|
|
2
|
+
type PluginFeaturesCardProps = {
|
|
3
|
+
plugin: AdminPluginFeatures;
|
|
4
|
+
/** Heading of the card; the plugin label when omitted. */
|
|
5
|
+
title?: string;
|
|
6
|
+
};
|
|
7
|
+
/**
|
|
8
|
+
* Plugin switch (unless the plugin is not switchable) plus the feature
|
|
9
|
+
* switches of one plugin; saves immediately.
|
|
10
|
+
*/
|
|
11
|
+
export declare const PluginFeaturesCard: ({ plugin, title }: PluginFeaturesCardProps) => import("react/jsx-runtime").JSX.Element;
|
|
12
|
+
export {};
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Container, Heading, Label, Switch, Text } from "@medusajs/ui";
|
|
3
|
+
import { texts } from "../../../i18n/texts";
|
|
4
|
+
import { useUpdatePluginFeatures } from "../hooks/usePluginFeatures";
|
|
5
|
+
const configuredFeatures = (plugin) => Object.fromEntries(plugin.features.map((feature) => [feature.key, feature.enabled]));
|
|
6
|
+
/**
|
|
7
|
+
* Plugin switch (unless the plugin is not switchable) plus the feature
|
|
8
|
+
* switches of one plugin; saves immediately.
|
|
9
|
+
*/
|
|
10
|
+
export const PluginFeaturesCard = ({ plugin, title }) => {
|
|
11
|
+
const mutation = useUpdatePluginFeatures();
|
|
12
|
+
const pending = mutation.isPending;
|
|
13
|
+
const labels = new Map(plugin.features.map((feature) => [feature.key, feature.label]));
|
|
14
|
+
const configured = configuredFeatures(plugin);
|
|
15
|
+
const pluginSwitchId = `plugin-features-${plugin.plugin_key}`;
|
|
16
|
+
const save = (enabled, features) => {
|
|
17
|
+
mutation.mutate({
|
|
18
|
+
plugin_key: plugin.plugin_key,
|
|
19
|
+
expected_version: plugin.version,
|
|
20
|
+
enabled,
|
|
21
|
+
features,
|
|
22
|
+
});
|
|
23
|
+
};
|
|
24
|
+
return (_jsxs(Container, { className: "divide-y p-0", "aria-busy": pending, children: [_jsxs("div", { className: "flex items-start justify-between gap-4 px-6 py-4", children: [_jsxs("div", { className: "min-w-0", children: [_jsx(Heading, { level: "h2", children: title ?? plugin.label }), title === undefined && plugin.description && (_jsx(Text, { size: "small", className: "text-ui-fg-subtle", children: plugin.description })), !plugin.enabled && (_jsx(Text, { size: "small", className: "text-ui-fg-muted mt-1", children: texts.hints.pluginDisabled })), pending && (_jsx(Text, { size: "small", className: "text-ui-fg-muted mt-1", children: texts.hints.saving }))] }), plugin.switchable && (_jsx(Switch, { id: pluginSwitchId, "aria-label": texts.plugin.switchLabel(plugin.label), className: "shrink-0", checked: plugin.enabled, disabled: pending, onCheckedChange: (checked) => save(checked === true, configured) }))] }), plugin.features.map((feature) => {
|
|
25
|
+
const switchId = `${pluginSwitchId}-${feature.key}`;
|
|
26
|
+
const parent = plugin.features.find((candidate) => candidate.key === feature.parent_key);
|
|
27
|
+
const parentOff = parent !== undefined && !parent.enabled;
|
|
28
|
+
const locked = pending || !plugin.enabled || parentOff;
|
|
29
|
+
return (_jsxs("div", { className: `flex items-start justify-between gap-4 py-3 pr-6 ${feature.parent_key ? "pl-12" : "pl-6"}`, children: [_jsxs("div", { className: "min-w-0", children: [_jsx(Label, { htmlFor: switchId, size: "small", weight: "plus", className: locked ? "text-ui-fg-disabled" : undefined, children: feature.label }), feature.description && (_jsx(Text, { size: "small", className: "text-ui-fg-subtle", children: feature.description })), plugin.enabled && parentOff && feature.parent_key && (_jsx(Text, { size: "small", className: "text-ui-fg-muted mt-1", children: texts.hints.parentDisabled(labels.get(feature.parent_key) ?? feature.parent_key) }))] }), _jsx(Switch, { id: switchId, className: "shrink-0", checked: feature.enabled, disabled: locked, onCheckedChange: (checked) => save(plugin.enabled, {
|
|
30
|
+
...configured,
|
|
31
|
+
[feature.key]: checked === true,
|
|
32
|
+
}) })] }, feature.key));
|
|
33
|
+
})] }));
|
|
34
|
+
};
|
package/dist/admin-components/features/plugin-features/components/PluginFeaturesSettingsPage.js
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Button, Container, Heading, Text } from "@medusajs/ui";
|
|
3
|
+
import { texts } from "../../../i18n/texts";
|
|
4
|
+
import { usePluginFeatures } from "../hooks/usePluginFeatures";
|
|
5
|
+
import { PluginHubCard } from "./PluginHubCard";
|
|
6
|
+
/**
|
|
7
|
+
* Hub: one card per installed plugin. Plugins that cannot be switched off
|
|
8
|
+
* (the shop master data) come first; the rest keep the server's order.
|
|
9
|
+
*/
|
|
10
|
+
export const PluginFeaturesSettingsPage = () => {
|
|
11
|
+
const query = usePluginFeatures();
|
|
12
|
+
return (_jsxs("div", { className: "flex flex-col gap-y-3", children: [_jsxs(Container, { className: "px-6 py-4", children: [_jsx(Heading, { level: "h1", children: texts.page.title }), _jsx(Text, { className: "text-ui-fg-subtle", children: texts.page.description })] }), query.isPending ? (_jsx(Container, { children: _jsx(Text, { className: "text-ui-fg-subtle", children: texts.states.loading }) })) : query.isError ? (_jsxs(Container, { className: "flex flex-wrap items-center justify-between gap-3", children: [_jsx(Text, { className: "text-ui-fg-error", children: texts.states.error }), _jsx(Button, { size: "small", variant: "secondary", isLoading: query.isFetching, onClick: () => void query.refetch(), children: texts.states.retry })] })) : query.data.plugins.length === 0 ? (_jsx(Container, { children: _jsx(Text, { className: "text-ui-fg-subtle", children: texts.states.empty }) })) : ([
|
|
13
|
+
...query.data.plugins.filter((plugin) => !plugin.switchable),
|
|
14
|
+
...query.data.plugins.filter((plugin) => plugin.switchable),
|
|
15
|
+
].map((plugin) => (_jsx(PluginHubCard, { plugin: plugin }, plugin.plugin_key))))] }));
|
|
16
|
+
};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { AdminPluginFeatures } from "@hartl-services/medusa-food-supplements-contracts/types/plugin-features-admin";
|
|
2
|
+
type PluginHubCardProps = {
|
|
3
|
+
plugin: AdminPluginFeatures;
|
|
4
|
+
};
|
|
5
|
+
/** One plugin on the hub: status, plugin switch and the link to its page. */
|
|
6
|
+
export declare const PluginHubCard: ({ plugin }: PluginHubCardProps) => import("react/jsx-runtime").JSX.Element;
|
|
7
|
+
export {};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Button, Container, Heading, Switch, Text } from "@medusajs/ui";
|
|
3
|
+
import { Link } from "react-router-dom";
|
|
4
|
+
import { texts } from "../../../i18n/texts";
|
|
5
|
+
import { useUpdatePluginFeatures } from "../hooks/usePluginFeatures";
|
|
6
|
+
import { pluginDetailPath } from "../lib/plugin-paths";
|
|
7
|
+
import { PluginStatusChips } from "./PluginStatusChips";
|
|
8
|
+
/** One plugin on the hub: status, plugin switch and the link to its page. */
|
|
9
|
+
export const PluginHubCard = ({ plugin }) => {
|
|
10
|
+
const mutation = useUpdatePluginFeatures();
|
|
11
|
+
const pending = mutation.isPending;
|
|
12
|
+
const toggle = (enabled) => {
|
|
13
|
+
mutation.mutate({
|
|
14
|
+
plugin_key: plugin.plugin_key,
|
|
15
|
+
expected_version: plugin.version,
|
|
16
|
+
enabled,
|
|
17
|
+
features: Object.fromEntries(plugin.features.map((feature) => [feature.key, feature.enabled])),
|
|
18
|
+
});
|
|
19
|
+
};
|
|
20
|
+
return (_jsxs(Container, { className: "flex flex-wrap items-start justify-between gap-4 px-6 py-4", "aria-busy": pending, children: [_jsxs("div", { className: "min-w-0 flex-1", children: [_jsx(Heading, { level: "h2", children: plugin.label }), plugin.description && (_jsx(Text, { size: "small", className: "text-ui-fg-subtle", children: plugin.description })), _jsx(PluginStatusChips, { plugin: plugin }), pending && (_jsx(Text, { size: "small", className: "text-ui-fg-muted mt-1", children: texts.hints.saving }))] }), _jsxs("div", { className: "flex shrink-0 items-center gap-3", children: [plugin.switchable && (_jsx(Switch, { "aria-label": texts.plugin.switchLabel(plugin.label), checked: plugin.enabled, disabled: pending, onCheckedChange: (checked) => toggle(checked === true) })), _jsx(Button, { size: "small", variant: "secondary", asChild: true, children: _jsx(Link, { to: plugin.admin_page ?? pluginDetailPath(plugin.plugin_key), "aria-label": texts.plugin.openLabel(plugin.label), children: texts.plugin.open }) })] })] }));
|
|
21
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { ReactNode } from "react";
|
|
2
|
+
type PluginPageShellProps = {
|
|
3
|
+
pluginKey: string;
|
|
4
|
+
/** Rendered below the header once the plugin is loaded. */
|
|
5
|
+
children?: ReactNode;
|
|
6
|
+
};
|
|
7
|
+
/**
|
|
8
|
+
* Frame of a plugin's page: back link to the hub, the plugin's label,
|
|
9
|
+
* description and status, then `children`. Shows loading, not-installed and
|
|
10
|
+
* error states instead of `children`.
|
|
11
|
+
*/
|
|
12
|
+
export declare const PluginPageShell: ({ pluginKey, children }: PluginPageShellProps) => import("react/jsx-runtime").JSX.Element;
|
|
13
|
+
export {};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { jsxs as _jsxs, jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { Button, Container, Heading, Text } from "@medusajs/ui";
|
|
3
|
+
import { Link } from "react-router-dom";
|
|
4
|
+
import { texts } from "../../../i18n/texts";
|
|
5
|
+
import { usePluginFeature } from "../hooks/usePluginFeatures";
|
|
6
|
+
import { PLUGIN_HUB_PATH } from "../lib/plugin-paths";
|
|
7
|
+
import { PluginStatusChips } from "./PluginStatusChips";
|
|
8
|
+
const isNotFound = (error) => typeof error === "object" &&
|
|
9
|
+
error !== null &&
|
|
10
|
+
error.status === 404;
|
|
11
|
+
/**
|
|
12
|
+
* Frame of a plugin's page: back link to the hub, the plugin's label,
|
|
13
|
+
* description and status, then `children`. Shows loading, not-installed and
|
|
14
|
+
* error states instead of `children`.
|
|
15
|
+
*/
|
|
16
|
+
export const PluginPageShell = ({ pluginKey, children }) => {
|
|
17
|
+
const query = usePluginFeature(pluginKey);
|
|
18
|
+
const plugin = query.data?.plugin;
|
|
19
|
+
return (_jsxs("div", { className: "flex flex-col gap-y-3", children: [_jsx("div", { children: _jsxs(Link, { to: PLUGIN_HUB_PATH, className: "text-ui-fg-subtle hover:text-ui-fg-base txt-compact-small-plus", children: ["\u2190 ", texts.detail.back] }) }), plugin ? (_jsxs(_Fragment, { children: [_jsxs(Container, { className: "px-6 py-4", children: [_jsx(Heading, { level: "h1", children: plugin.label }), plugin.description && (_jsx(Text, { className: "text-ui-fg-subtle", children: plugin.description })), _jsx(PluginStatusChips, { plugin: plugin })] }), children] })) : query.isPending ? (_jsx(Container, { children: _jsx(Text, { className: "text-ui-fg-subtle", children: texts.states.loading }) })) : isNotFound(query.error) ? (_jsx(Container, { children: _jsx(Text, { className: "text-ui-fg-subtle", children: texts.states.notFound }) })) : (_jsxs(Container, { className: "flex flex-wrap items-center justify-between gap-3", children: [_jsx(Text, { className: "text-ui-fg-error", children: texts.states.error }), _jsx(Button, { size: "small", variant: "secondary", isLoading: query.isFetching, onClick: () => void query.refetch(), children: texts.states.retry })] }))] }));
|
|
20
|
+
};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { AdminPluginFeatures } from "@hartl-services/medusa-food-supplements-contracts/types/plugin-features-admin";
|
|
2
|
+
type PluginSettingsFormProps = {
|
|
3
|
+
plugin: AdminPluginFeatures;
|
|
4
|
+
};
|
|
5
|
+
export declare const missingLabels: (plugin: AdminPluginFeatures) => string;
|
|
6
|
+
export declare const PluginSettingsForm: ({ plugin }: PluginSettingsFormProps) => import("react/jsx-runtime").JSX.Element;
|
|
7
|
+
export {};
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { Alert, Button, Container, Heading, Hint, Input, Label, Select, Switch, Text, } from "@medusajs/ui";
|
|
3
|
+
import { texts } from "../../../i18n/texts";
|
|
4
|
+
import { usePluginSettingsForm } from "../hooks/usePluginSettingsForm";
|
|
5
|
+
const fieldId = (pluginKey, key) => `plugin-setting-${pluginKey}-${key}`;
|
|
6
|
+
export const missingLabels = (plugin) => plugin.missing
|
|
7
|
+
.map((key) => plugin.settings.find((setting) => setting.key === key)?.label ?? key)
|
|
8
|
+
.join(", ");
|
|
9
|
+
/** "API-Key, Passwort" for the secrets a change of `setting` deletes. */
|
|
10
|
+
const invalidatesLabels = (plugin, setting) => setting.invalidates
|
|
11
|
+
.map((key) => plugin.settings.find((candidate) => candidate.key === key)?.label ?? key)
|
|
12
|
+
.join(", ");
|
|
13
|
+
/** "Zwischen 0 und 100." etc.; `undefined` without bounds. */
|
|
14
|
+
const boundsText = (setting) => setting.min !== null && setting.max !== null
|
|
15
|
+
? texts.settings.numberRange(setting.min, setting.max)
|
|
16
|
+
: setting.min !== null
|
|
17
|
+
? texts.settings.numberMin(setting.min)
|
|
18
|
+
: setting.max !== null
|
|
19
|
+
? texts.settings.numberMax(setting.max)
|
|
20
|
+
: undefined;
|
|
21
|
+
const SettingField = ({ pluginKey, setting, invalidatesLabels, value, invalid, disabled, onChange, }) => {
|
|
22
|
+
const id = fieldId(pluginKey, setting.key);
|
|
23
|
+
const hintId = `${id}-hint`;
|
|
24
|
+
const clearPending = setting.type === "secret" && value === null;
|
|
25
|
+
const bounds = setting.type === "number" ? boundsText(setting) : undefined;
|
|
26
|
+
return (_jsxs("div", { className: "grid gap-2", children: [_jsxs("div", { className: "flex items-center justify-between gap-3", children: [_jsxs(Label, { htmlFor: id, size: "small", weight: "plus", children: [setting.label, setting.required && (_jsxs("span", { className: "text-ui-fg-muted", children: [" (", texts.settings.required, ")"] }))] }), _jsx(Text, { size: "xsmall", className: "text-ui-fg-muted shrink-0", children: texts.settings.source[setting.source] })] }), setting.type === "boolean" ? (_jsx(Switch, { id: id, "aria-describedby": hintId, checked: value === true, disabled: disabled, onCheckedChange: (checked) => onChange(checked === true) })) : setting.type === "secret" ? (_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Input, { id: id, "aria-describedby": hintId, className: "flex-1", type: "password", autoComplete: "new-password", placeholder: setting.is_set ? texts.settings.secretPlaceholder : "", value: typeof value === "string" ? value : "", disabled: disabled || clearPending, onChange: (event) => onChange(event.target.value) }), setting.source === "stored" && (_jsx(Button, { type: "button", size: "small", variant: "secondary", disabled: disabled, "aria-label": clearPending
|
|
27
|
+
? texts.settings.secretClearUndoLabel(setting.label)
|
|
28
|
+
: texts.settings.secretClearLabel(setting.label), onClick: () => onChange(clearPending ? "" : null), children: clearPending
|
|
29
|
+
? texts.settings.secretClearUndo
|
|
30
|
+
: texts.settings.secretClear }))] })) : setting.type === "select" ? (_jsxs(Select, { value: typeof value === "string" ? value : "", disabled: disabled, onValueChange: (next) => onChange(next), children: [_jsx(Select.Trigger, { id: id, "aria-describedby": hintId, children: _jsx(Select.Value, { placeholder: texts.settings.selectPlaceholder }) }), _jsx(Select.Content, { children: setting.options.map((option) => (_jsx(Select.Item, { value: option.value, children: option.label }, option.value))) })] })) : setting.type === "number" ? (_jsx(Input, { id: id, "aria-describedby": hintId, "aria-invalid": invalid, type: "number", step: "any", min: setting.min ?? undefined, max: setting.max ?? undefined, value: typeof value === "string" ? value : "", disabled: disabled, onChange: (event) => onChange(event.target.value, event.target.validity.badInput) })) : (_jsx(Input, { id: id, "aria-describedby": hintId, "aria-invalid": invalid, type: setting.type === "url" ? "url" : "text", value: typeof value === "string" ? value : "", disabled: disabled, onChange: (event) => onChange(event.target.value) })), _jsxs("div", { id: hintId, className: "grid gap-1", children: [setting.description && (_jsx(Text, { size: "small", className: "text-ui-fg-subtle", children: setting.description })), clearPending && (_jsx(Hint, { variant: "info", children: texts.settings.secretClearPending })), invalidatesLabels && (_jsx(Text, { size: "small", className: "text-ui-fg-muted", children: texts.settings.invalidates(invalidatesLabels) })), bounds && !invalid && (_jsx(Text, { size: "small", className: "text-ui-fg-muted", children: bounds })), invalid && (_jsx(Hint, { variant: "error", children: bounds ?? texts.settings.invalidNumber }))] })] }));
|
|
31
|
+
};
|
|
32
|
+
export const PluginSettingsForm = ({ plugin }) => {
|
|
33
|
+
const form = usePluginSettingsForm(plugin);
|
|
34
|
+
const pending = form.mutation.isPending;
|
|
35
|
+
const hasTextFields = plugin.settings.some((setting) => setting.type !== "boolean" &&
|
|
36
|
+
setting.type !== "secret" &&
|
|
37
|
+
setting.type !== "select");
|
|
38
|
+
return (_jsxs(Container, { className: "divide-y p-0", "aria-busy": pending, children: [_jsxs("div", { className: "px-6 py-4", children: [_jsx(Heading, { level: "h2", children: texts.sections.settings }), hasTextFields && (_jsx(Text, { size: "small", className: "text-ui-fg-subtle", children: texts.settings.clearHint }))] }), _jsxs("form", { onSubmit: form.submit, className: "grid gap-5 px-6 py-5", children: [!plugin.configured && (_jsx(Alert, { variant: "warning", children: texts.settings.missing(missingLabels(plugin)) })), plugin.settings.map((setting) => (_jsx(SettingField, { pluginKey: plugin.plugin_key, setting: setting, invalidatesLabels: invalidatesLabels(plugin, setting), value: form.draft[setting.key], invalid: form.invalid.includes(setting.key), disabled: pending, onChange: (value, badInput) => form.setValue(setting.key, value, badInput) }, setting.key))), _jsx("div", { className: "flex justify-end", children: _jsx(Button, { type: "submit", size: "small", isLoading: pending, disabled: !form.dirty || form.invalid.length > 0 || pending, children: texts.settings.save }) })] })] }));
|
|
39
|
+
};
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { AdminPluginFeatures } from "@hartl-services/medusa-food-supplements-contracts/types/plugin-features-admin";
|
|
2
|
+
/** aktiv/aus (switchable plugins only), plus konfiguriert/nicht konfiguriert when the plugin has settings. */
|
|
3
|
+
export declare const PluginStatusChips: ({ plugin }: {
|
|
4
|
+
plugin: AdminPluginFeatures;
|
|
5
|
+
}) => import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { StatusBadge } from "@medusajs/ui";
|
|
3
|
+
import { texts } from "../../../i18n/texts";
|
|
4
|
+
import { missingLabels } from "./PluginSettingsForm";
|
|
5
|
+
/** aktiv/aus (switchable plugins only), plus konfiguriert/nicht konfiguriert when the plugin has settings. */
|
|
6
|
+
export const PluginStatusChips = ({ plugin }) => (_jsxs("div", { className: "mt-2 flex flex-wrap gap-2", children: [plugin.switchable && (_jsx(StatusBadge, { color: plugin.enabled ? "green" : "grey", children: plugin.enabled ? texts.chips.enabled : texts.chips.disabled })), plugin.settings.length > 0 && (_jsx(StatusBadge, { color: plugin.configured ? "green" : "orange", children: plugin.configured
|
|
7
|
+
? texts.chips.configured
|
|
8
|
+
: texts.chips.notConfigured(missingLabels(plugin)) }))] }));
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import type { AdminPluginFeaturesListResponse } from "@hartl-services/medusa-food-supplements-contracts/types/plugin-features-admin";
|
|
2
|
+
export type PluginFeatureEnabledState = {
|
|
3
|
+
/** `false` while loading and on error, so gated UI never flashes. */
|
|
4
|
+
enabled: boolean;
|
|
5
|
+
isPending: boolean;
|
|
6
|
+
isError: boolean;
|
|
7
|
+
};
|
|
8
|
+
/** Widgets re-read the toggles at most every 30 s (hub saves update at once). */
|
|
9
|
+
export declare const PLUGIN_FEATURE_ENABLED_STALE_TIME = 30000;
|
|
10
|
+
/**
|
|
11
|
+
* Like the server's `isPluginFeatureEnabled`: the plugin switch and every
|
|
12
|
+
* given key must be effective; an unknown plugin or key is `false`.
|
|
13
|
+
*/
|
|
14
|
+
export declare const isPluginFeatureEnabledIn: (data: AdminPluginFeaturesListResponse, pluginKey: string, featureKeys: readonly string[]) => boolean;
|
|
15
|
+
/**
|
|
16
|
+
* Query options shared with the hub's list (`GET /admin/plugin-features`,
|
|
17
|
+
* same key), so the hub's toggle mutations update gated widgets too.
|
|
18
|
+
*/
|
|
19
|
+
export declare const pluginFeatureEnabledQueryOptions: (pluginKey: string, featureKeys: readonly string[]) => {
|
|
20
|
+
queryKey: readonly ["plugin-features", "list"];
|
|
21
|
+
queryFn: () => Promise<{
|
|
22
|
+
plugins: {
|
|
23
|
+
plugin_key: string;
|
|
24
|
+
label: string;
|
|
25
|
+
description: string | null;
|
|
26
|
+
default_enabled: boolean;
|
|
27
|
+
switchable: boolean;
|
|
28
|
+
enabled: boolean;
|
|
29
|
+
version: number;
|
|
30
|
+
features: {
|
|
31
|
+
key: string;
|
|
32
|
+
label: string;
|
|
33
|
+
description: string | null;
|
|
34
|
+
parent_key: string | null;
|
|
35
|
+
default_enabled: boolean;
|
|
36
|
+
enabled: boolean;
|
|
37
|
+
effective_enabled: boolean;
|
|
38
|
+
}[];
|
|
39
|
+
settings: ({
|
|
40
|
+
type: "secret";
|
|
41
|
+
key: string;
|
|
42
|
+
label: string;
|
|
43
|
+
description: string | null;
|
|
44
|
+
required: boolean;
|
|
45
|
+
is_set: boolean;
|
|
46
|
+
source: "default" | "stored" | "config" | "none";
|
|
47
|
+
invalidates: string[];
|
|
48
|
+
} | {
|
|
49
|
+
type: "select";
|
|
50
|
+
options: {
|
|
51
|
+
value: string;
|
|
52
|
+
label: string;
|
|
53
|
+
}[];
|
|
54
|
+
default: string | null;
|
|
55
|
+
value: string | null;
|
|
56
|
+
key: string;
|
|
57
|
+
label: string;
|
|
58
|
+
description: string | null;
|
|
59
|
+
required: boolean;
|
|
60
|
+
is_set: boolean;
|
|
61
|
+
source: "default" | "stored" | "config" | "none";
|
|
62
|
+
invalidates: string[];
|
|
63
|
+
} | {
|
|
64
|
+
type: "number";
|
|
65
|
+
min: number | null;
|
|
66
|
+
max: number | null;
|
|
67
|
+
default: number | null;
|
|
68
|
+
value: number | null;
|
|
69
|
+
key: string;
|
|
70
|
+
label: string;
|
|
71
|
+
description: string | null;
|
|
72
|
+
required: boolean;
|
|
73
|
+
is_set: boolean;
|
|
74
|
+
source: "default" | "stored" | "config" | "none";
|
|
75
|
+
invalidates: string[];
|
|
76
|
+
} | {
|
|
77
|
+
type: "string" | "boolean" | "url";
|
|
78
|
+
default: string | number | boolean | null;
|
|
79
|
+
value: string | number | boolean | null;
|
|
80
|
+
key: string;
|
|
81
|
+
label: string;
|
|
82
|
+
description: string | null;
|
|
83
|
+
required: boolean;
|
|
84
|
+
is_set: boolean;
|
|
85
|
+
source: "default" | "stored" | "config" | "none";
|
|
86
|
+
invalidates: string[];
|
|
87
|
+
})[];
|
|
88
|
+
configured: boolean;
|
|
89
|
+
missing: string[];
|
|
90
|
+
settings_version: number;
|
|
91
|
+
actions: {
|
|
92
|
+
key: string;
|
|
93
|
+
label: string;
|
|
94
|
+
description: string | null;
|
|
95
|
+
mode: "sync" | "background";
|
|
96
|
+
confirm: string | null;
|
|
97
|
+
}[];
|
|
98
|
+
admin_page: string | null;
|
|
99
|
+
}[];
|
|
100
|
+
}>;
|
|
101
|
+
staleTime: number;
|
|
102
|
+
select: (data: AdminPluginFeaturesListResponse) => boolean;
|
|
103
|
+
};
|
|
104
|
+
export declare const toPluginFeatureEnabledState: (result: {
|
|
105
|
+
data: boolean | undefined;
|
|
106
|
+
isPending: boolean;
|
|
107
|
+
isError: boolean;
|
|
108
|
+
}) => PluginFeatureEnabledState;
|
|
109
|
+
/**
|
|
110
|
+
* Whether a plugin (and optionally some of its features) is effectively on,
|
|
111
|
+
* for hiding widgets and pages of a disabled feature. No keys = plugin
|
|
112
|
+
* switch only; several keys = all must be on.
|
|
113
|
+
*/
|
|
114
|
+
export declare function usePluginFeatureEnabled(pluginKey: string, ...featureKeys: string[]): PluginFeatureEnabledState;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { useQuery } from "@tanstack/react-query";
|
|
2
|
+
import { listPluginFeatures } from "../lib/plugin-features-admin-client";
|
|
3
|
+
import { pluginFeatureKeys } from "../queries/pluginFeatureKeys";
|
|
4
|
+
/** Widgets re-read the toggles at most every 30 s (hub saves update at once). */
|
|
5
|
+
export const PLUGIN_FEATURE_ENABLED_STALE_TIME = 30_000;
|
|
6
|
+
/**
|
|
7
|
+
* Like the server's `isPluginFeatureEnabled`: the plugin switch and every
|
|
8
|
+
* given key must be effective; an unknown plugin or key is `false`.
|
|
9
|
+
*/
|
|
10
|
+
export const isPluginFeatureEnabledIn = (data, pluginKey, featureKeys) => {
|
|
11
|
+
const plugin = data.plugins.find((entry) => entry.plugin_key === pluginKey);
|
|
12
|
+
if (!plugin?.enabled) {
|
|
13
|
+
return false;
|
|
14
|
+
}
|
|
15
|
+
return featureKeys.every((key) => plugin.features.find((feature) => feature.key === key)
|
|
16
|
+
?.effective_enabled === true);
|
|
17
|
+
};
|
|
18
|
+
/**
|
|
19
|
+
* Query options shared with the hub's list (`GET /admin/plugin-features`,
|
|
20
|
+
* same key), so the hub's toggle mutations update gated widgets too.
|
|
21
|
+
*/
|
|
22
|
+
export const pluginFeatureEnabledQueryOptions = (pluginKey, featureKeys) => ({
|
|
23
|
+
queryKey: pluginFeatureKeys.list(),
|
|
24
|
+
queryFn: listPluginFeatures,
|
|
25
|
+
staleTime: PLUGIN_FEATURE_ENABLED_STALE_TIME,
|
|
26
|
+
select: (data) => isPluginFeatureEnabledIn(data, pluginKey, featureKeys),
|
|
27
|
+
});
|
|
28
|
+
export const toPluginFeatureEnabledState = (result) => ({
|
|
29
|
+
enabled: !result.isPending && !result.isError && result.data === true,
|
|
30
|
+
isPending: result.isPending,
|
|
31
|
+
isError: result.isError,
|
|
32
|
+
});
|
|
33
|
+
/**
|
|
34
|
+
* Whether a plugin (and optionally some of its features) is effectively on,
|
|
35
|
+
* for hiding widgets and pages of a disabled feature. No keys = plugin
|
|
36
|
+
* switch only; several keys = all must be on.
|
|
37
|
+
*/
|
|
38
|
+
export function usePluginFeatureEnabled(pluginKey, ...featureKeys) {
|
|
39
|
+
return toPluginFeatureEnabledState(useQuery(pluginFeatureEnabledQueryOptions(pluginKey, featureKeys)));
|
|
40
|
+
}
|