@vendure-platform/create 0.1.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/README.md +70 -0
- package/dist/assets/demo-data/branding/logo.svg +4 -0
- package/dist/assets/demo-data/images/absorbent-pad.webp +0 -0
- package/dist/assets/demo-data/images/adjustable-wrench.webp +0 -0
- package/dist/assets/demo-data/images/anchor-bolt.webp +0 -0
- package/dist/assets/demo-data/images/angle-grinder.webp +0 -0
- package/dist/assets/demo-data/images/barrier-tape.webp +0 -0
- package/dist/assets/demo-data/images/bench-grinder.webp +0 -0
- package/dist/assets/demo-data/images/bracket.webp +0 -0
- package/dist/assets/demo-data/images/broom.webp +0 -0
- package/dist/assets/demo-data/images/bucket.webp +0 -0
- package/dist/assets/demo-data/images/cable-gland.webp +0 -0
- package/dist/assets/demo-data/images/cable-tie.webp +0 -0
- package/dist/assets/demo-data/images/cable-tray.webp +0 -0
- package/dist/assets/demo-data/images/cable.webp +0 -0
- package/dist/assets/demo-data/images/chimney-base-kit.webp +0 -0
- package/dist/assets/demo-data/images/chimney-cowl-bird-guard.webp +0 -0
- package/dist/assets/demo-data/images/chimney-cowl-open.webp +0 -0
- package/dist/assets/demo-data/images/chimney-storm-collar.webp +0 -0
- package/dist/assets/demo-data/images/chimney-wall-bracket.webp +0 -0
- package/dist/assets/demo-data/images/circular-saw.webp +0 -0
- package/dist/assets/demo-data/images/cleaning-spray.webp +0 -0
- package/dist/assets/demo-data/images/conduit.webp +0 -0
- package/dist/assets/demo-data/images/cordless-drill.webp +0 -0
- package/dist/assets/demo-data/images/drill-bits.webp +0 -0
- package/dist/assets/demo-data/images/dust-mask.webp +0 -0
- package/dist/assets/demo-data/images/ear-defenders.webp +0 -0
- package/dist/assets/demo-data/images/ear-plugs.webp +0 -0
- package/dist/assets/demo-data/images/extension-lead.webp +0 -0
- package/dist/assets/demo-data/images/eye-wash-station.webp +0 -0
- package/dist/assets/demo-data/images/face-shield.webp +0 -0
- package/dist/assets/demo-data/images/file-set.webp +0 -0
- package/dist/assets/demo-data/images/first-aid-kit.webp +0 -0
- package/dist/assets/demo-data/images/floor-sign.webp +0 -0
- package/dist/assets/demo-data/images/flue-bend-45.webp +0 -0
- package/dist/assets/demo-data/images/flue-bend-90.webp +0 -0
- package/dist/assets/demo-data/images/flue-section-1000mm.webp +0 -0
- package/dist/assets/demo-data/images/flue-section-250mm.webp +0 -0
- package/dist/assets/demo-data/images/flue-section-500mm.webp +0 -0
- package/dist/assets/demo-data/images/fuse.webp +0 -0
- package/dist/assets/demo-data/images/glove-dispenser.webp +0 -0
- package/dist/assets/demo-data/images/grinding-disc.webp +0 -0
- package/dist/assets/demo-data/images/hacksaw.webp +0 -0
- package/dist/assets/demo-data/images/hammer.webp +0 -0
- package/dist/assets/demo-data/images/hand-sanitiser.webp +0 -0
- package/dist/assets/demo-data/images/head-torch.webp +0 -0
- package/dist/assets/demo-data/images/heat-gun.webp +0 -0
- package/dist/assets/demo-data/images/hex-bolt.webp +0 -0
- package/dist/assets/demo-data/images/hex-key-set.webp +0 -0
- package/dist/assets/demo-data/images/hex-nut.webp +0 -0
- package/dist/assets/demo-data/images/hi-vis-jacket.webp +0 -0
- package/dist/assets/demo-data/images/hi-vis-vest.webp +0 -0
- package/dist/assets/demo-data/images/hose-clamp.webp +0 -0
- package/dist/assets/demo-data/images/impact-driver.webp +0 -0
- package/dist/assets/demo-data/images/jigsaw.webp +0 -0
- package/dist/assets/demo-data/images/junction-box.webp +0 -0
- package/dist/assets/demo-data/images/knee-pads.webp +0 -0
- package/dist/assets/demo-data/images/machine-screw.webp +0 -0
- package/dist/assets/demo-data/images/made-to-order-configurable-chimney.webp +0 -0
- package/dist/assets/demo-data/images/mallet.webp +0 -0
- package/dist/assets/demo-data/images/mcb.webp +0 -0
- package/dist/assets/demo-data/images/mop.webp +0 -0
- package/dist/assets/demo-data/images/paper-towels.webp +0 -0
- package/dist/assets/demo-data/images/pipe-wrench.webp +0 -0
- package/dist/assets/demo-data/images/pliers-set.webp +0 -0
- package/dist/assets/demo-data/images/pry-bar.webp +0 -0
- package/dist/assets/demo-data/images/rcd.webp +0 -0
- package/dist/assets/demo-data/images/reciprocating-saw.webp +0 -0
- package/dist/assets/demo-data/images/rivet.webp +0 -0
- package/dist/assets/demo-data/images/roof-flashing-high-pitch.webp +0 -0
- package/dist/assets/demo-data/images/roof-flashing-low-pitch.webp +0 -0
- package/dist/assets/demo-data/images/safety-boots.webp +0 -0
- package/dist/assets/demo-data/images/safety-glasses.webp +0 -0
- package/dist/assets/demo-data/images/safety-harness.webp +0 -0
- package/dist/assets/demo-data/images/safety-helmet.webp +0 -0
- package/dist/assets/demo-data/images/safety-sign.webp +0 -0
- package/dist/assets/demo-data/images/sanding-disc.webp +0 -0
- package/dist/assets/demo-data/images/saw-blade.webp +0 -0
- package/dist/assets/demo-data/images/screwdriver-set.webp +0 -0
- package/dist/assets/demo-data/images/screwdriver.webp +0 -0
- package/dist/assets/demo-data/images/self-tapping-screw.webp +0 -0
- package/dist/assets/demo-data/images/socket-set.webp +0 -0
- package/dist/assets/demo-data/images/socket.webp +0 -0
- package/dist/assets/demo-data/images/spanner-set.webp +0 -0
- package/dist/assets/demo-data/images/spanner.webp +0 -0
- package/dist/assets/demo-data/images/spill-kit.webp +0 -0
- package/dist/assets/demo-data/images/spirit-level.webp +0 -0
- package/dist/assets/demo-data/images/switch.webp +0 -0
- package/dist/assets/demo-data/images/tape-measure.webp +0 -0
- package/dist/assets/demo-data/images/terminal-block.webp +0 -0
- package/dist/assets/demo-data/images/threaded-rod.webp +0 -0
- package/dist/assets/demo-data/images/torque-wrench.webp +0 -0
- package/dist/assets/demo-data/images/utility-knife.webp +0 -0
- package/dist/assets/demo-data/images/wall-pass-sleeve.webp +0 -0
- package/dist/assets/demo-data/images/wall-plug.webp +0 -0
- package/dist/assets/demo-data/images/washer.webp +0 -0
- package/dist/assets/demo-data/images/waste-bin.webp +0 -0
- package/dist/assets/demo-data/images/wire-connector.webp +0 -0
- package/dist/assets/demo-data/images/wire-cutters.webp +0 -0
- package/dist/assets/demo-data/images/wood-screw.webp +0 -0
- package/dist/assets/demo-data/images/work-gloves.webp +0 -0
- package/dist/assets/demo-data/images/work-light.webp +0 -0
- package/dist/assets/demo-data/initial-data.json +667 -0
- package/dist/assets/demo-data/products.csv +779 -0
- package/dist/assets/storefront/.env.example +14 -0
- package/dist/assets/storefront/.env.local +1 -0
- package/dist/assets/storefront/components.json +22 -0
- package/dist/assets/storefront/eslint.config.mjs +23 -0
- package/dist/assets/storefront/graphql.config.yml +2 -0
- package/dist/assets/storefront/messages/de.json +1196 -0
- package/dist/assets/storefront/messages/en.json +1196 -0
- package/dist/assets/storefront/next-env.d.ts +6 -0
- package/dist/assets/storefront/next.config.ts +73 -0
- package/dist/assets/storefront/package.json +77 -0
- package/dist/assets/storefront/postcss.config.mjs +5 -0
- package/dist/assets/storefront/public/next.svg +22 -0
- package/dist/assets/storefront/public/scenarios/b2b-wholesale/hero/hero-bg.webp +0 -0
- package/dist/assets/storefront/public/scenarios/b2b-wholesale/logo.svg +4 -0
- package/dist/assets/storefront/public/scenarios/default/logo.svg +4 -0
- package/dist/assets/storefront/public/scenarios/kaminpro/hero/hero-bg.webp +0 -0
- package/dist/assets/storefront/public/scenarios/kaminpro/logo.svg +7 -0
- package/dist/assets/storefront/public/vendure.svg +4 -0
- package/dist/assets/storefront/src/app/[locale]/(catalog)/catalog/page.tsx +65 -0
- package/dist/assets/storefront/src/app/[locale]/(catalog)/collection/[slug]/loading.tsx +24 -0
- package/dist/assets/storefront/src/app/[locale]/(catalog)/collection/[slug]/page.tsx +194 -0
- package/dist/assets/storefront/src/app/[locale]/(catalog)/layout.tsx +9 -0
- package/dist/assets/storefront/src/app/[locale]/(catalog)/product/[slug]/actions.ts +139 -0
- package/dist/assets/storefront/src/app/[locale]/(catalog)/product/[slug]/loading.tsx +46 -0
- package/dist/assets/storefront/src/app/[locale]/(catalog)/product/[slug]/page.tsx +330 -0
- package/dist/assets/storefront/src/app/[locale]/(catalog)/search/actions.ts +11 -0
- package/dist/assets/storefront/src/app/[locale]/(catalog)/search/loading.tsx +20 -0
- package/dist/assets/storefront/src/app/[locale]/(catalog)/search/page.tsx +97 -0
- package/dist/assets/storefront/src/app/[locale]/(catalog)/search/search-analytics-tracker.tsx +46 -0
- package/dist/assets/storefront/src/app/[locale]/(catalog)/search/search-results.tsx +53 -0
- package/dist/assets/storefront/src/app/[locale]/(catalog)/search/search-term.tsx +26 -0
- package/dist/assets/storefront/src/app/[locale]/(scenario)/[...path]/page.tsx +31 -0
- package/dist/assets/storefront/src/app/[locale]/accept-invitation/[token]/accept-invitation-client.tsx +56 -0
- package/dist/assets/storefront/src/app/[locale]/accept-invitation/[token]/actions.ts +32 -0
- package/dist/assets/storefront/src/app/[locale]/accept-invitation/[token]/page.tsx +85 -0
- package/dist/assets/storefront/src/app/[locale]/accept-invitation/[token]/sign-out-and-retry.tsx +26 -0
- package/dist/assets/storefront/src/app/[locale]/account/addresses/actions.ts +125 -0
- package/dist/assets/storefront/src/app/[locale]/account/addresses/address-form.tsx +180 -0
- package/dist/assets/storefront/src/app/[locale]/account/addresses/addresses-client.tsx +292 -0
- package/dist/assets/storefront/src/app/[locale]/account/addresses/loading.tsx +48 -0
- package/dist/assets/storefront/src/app/[locale]/account/addresses/page.tsx +54 -0
- package/dist/assets/storefront/src/app/[locale]/account/approvals/actions.ts +28 -0
- package/dist/assets/storefront/src/app/[locale]/account/approvals/page.tsx +340 -0
- package/dist/assets/storefront/src/app/[locale]/account/company/actions.ts +33 -0
- package/dist/assets/storefront/src/app/[locale]/account/company/addresses/actions.ts +83 -0
- package/dist/assets/storefront/src/app/[locale]/account/company/addresses/address-form.tsx +267 -0
- package/dist/assets/storefront/src/app/[locale]/account/company/addresses/addresses-client.tsx +253 -0
- package/dist/assets/storefront/src/app/[locale]/account/company/addresses/page.tsx +75 -0
- package/dist/assets/storefront/src/app/[locale]/account/company/business-units/actions.ts +70 -0
- package/dist/assets/storefront/src/app/[locale]/account/company/business-units/business-unit-form.tsx +153 -0
- package/dist/assets/storefront/src/app/[locale]/account/company/business-units/business-units-client.tsx +210 -0
- package/dist/assets/storefront/src/app/[locale]/account/company/business-units/page.tsx +66 -0
- package/dist/assets/storefront/src/app/[locale]/account/company/business-units/types.ts +8 -0
- package/dist/assets/storefront/src/app/[locale]/account/company/edit-company-form.tsx +57 -0
- package/dist/assets/storefront/src/app/[locale]/account/company/page.tsx +89 -0
- package/dist/assets/storefront/src/app/[locale]/account/company/payment-methods/page.tsx +65 -0
- package/dist/assets/storefront/src/app/[locale]/account/company/payment-methods/payment-methods-client.tsx +55 -0
- package/dist/assets/storefront/src/app/[locale]/account/company/roles/page.tsx +66 -0
- package/dist/assets/storefront/src/app/[locale]/account/company/roles/roles-client.tsx +93 -0
- package/dist/assets/storefront/src/app/[locale]/account/company/tax-ids/actions.ts +51 -0
- package/dist/assets/storefront/src/app/[locale]/account/company/tax-ids/page.tsx +57 -0
- package/dist/assets/storefront/src/app/[locale]/account/company/tax-ids/tax-ids-client.tsx +326 -0
- package/dist/assets/storefront/src/app/[locale]/account/company/users/actions.ts +74 -0
- package/dist/assets/storefront/src/app/[locale]/account/company/users/edit-user-form.tsx +157 -0
- package/dist/assets/storefront/src/app/[locale]/account/company/users/invite-user-form.tsx +133 -0
- package/dist/assets/storefront/src/app/[locale]/account/company/users/page.tsx +104 -0
- package/dist/assets/storefront/src/app/[locale]/account/company/users/users-client.tsx +266 -0
- package/dist/assets/storefront/src/app/[locale]/account/error.tsx +24 -0
- package/dist/assets/storefront/src/app/[locale]/account/layout.tsx +50 -0
- package/dist/assets/storefront/src/app/[locale]/account/orders/[code]/page.tsx +463 -0
- package/dist/assets/storefront/src/app/[locale]/account/orders/[code]/reorder-actions.ts +160 -0
- package/dist/assets/storefront/src/app/[locale]/account/orders/[code]/reorder-dialog.tsx +386 -0
- package/dist/assets/storefront/src/app/[locale]/account/orders/loading.tsx +45 -0
- package/dist/assets/storefront/src/app/[locale]/account/orders/page.tsx +309 -0
- package/dist/assets/storefront/src/app/[locale]/account/profile/actions.ts +106 -0
- package/dist/assets/storefront/src/app/[locale]/account/profile/change-password-form.tsx +82 -0
- package/dist/assets/storefront/src/app/[locale]/account/profile/edit-email-form.tsx +88 -0
- package/dist/assets/storefront/src/app/[locale]/account/profile/edit-profile-form.tsx +80 -0
- package/dist/assets/storefront/src/app/[locale]/account/profile/loading.tsx +54 -0
- package/dist/assets/storefront/src/app/[locale]/account/profile/page.tsx +44 -0
- package/dist/assets/storefront/src/app/[locale]/account/quick-order/actions.ts +103 -0
- package/dist/assets/storefront/src/app/[locale]/account/quick-order/file-upload-tab.tsx +148 -0
- package/dist/assets/storefront/src/app/[locale]/account/quick-order/manual-entry-tab.tsx +112 -0
- package/dist/assets/storefront/src/app/[locale]/account/quick-order/page.tsx +26 -0
- package/dist/assets/storefront/src/app/[locale]/account/quick-order/preview-table.tsx +170 -0
- package/dist/assets/storefront/src/app/[locale]/account/quick-order/quick-order-form.tsx +243 -0
- package/dist/assets/storefront/src/app/[locale]/account/quick-order/save-to-list-dialog.tsx +153 -0
- package/dist/assets/storefront/src/app/[locale]/account/quick-order/sku-autocomplete.tsx +118 -0
- package/dist/assets/storefront/src/app/[locale]/account/quick-order/text-input-tab.tsx +40 -0
- package/dist/assets/storefront/src/app/[locale]/account/quotes/[id]/page.tsx +260 -0
- package/dist/assets/storefront/src/app/[locale]/account/quotes/actions.ts +253 -0
- package/dist/assets/storefront/src/app/[locale]/account/quotes/new/new-quote-form.tsx +28 -0
- package/dist/assets/storefront/src/app/[locale]/account/quotes/new/page.tsx +51 -0
- package/dist/assets/storefront/src/app/[locale]/account/quotes/page.tsx +248 -0
- package/dist/assets/storefront/src/app/[locale]/account/shopping-lists/[id]/loading.tsx +44 -0
- package/dist/assets/storefront/src/app/[locale]/account/shopping-lists/[id]/page.tsx +51 -0
- package/dist/assets/storefront/src/app/[locale]/account/shopping-lists/[id]/shopping-list-detail-client.tsx +562 -0
- package/dist/assets/storefront/src/app/[locale]/account/shopping-lists/actions.ts +235 -0
- package/dist/assets/storefront/src/app/[locale]/account/shopping-lists/create-shopping-list-form.tsx +86 -0
- package/dist/assets/storefront/src/app/[locale]/account/shopping-lists/loading.tsx +48 -0
- package/dist/assets/storefront/src/app/[locale]/account/shopping-lists/page.tsx +176 -0
- package/dist/assets/storefront/src/app/[locale]/account/store-credits/actions.ts +31 -0
- package/dist/assets/storefront/src/app/[locale]/account/store-credits/claim-form.tsx +41 -0
- package/dist/assets/storefront/src/app/[locale]/account/store-credits/page.tsx +190 -0
- package/dist/assets/storefront/src/app/[locale]/account/store-credits/reveal-code.tsx +38 -0
- package/dist/assets/storefront/src/app/[locale]/account/verify-email/page.tsx +117 -0
- package/dist/assets/storefront/src/app/[locale]/cart/actions.ts +128 -0
- package/dist/assets/storefront/src/app/[locale]/cart/cart-items.tsx +228 -0
- package/dist/assets/storefront/src/app/[locale]/cart/cart-line-actions.tsx +55 -0
- package/dist/assets/storefront/src/app/[locale]/cart/cart-quantity-control.tsx +158 -0
- package/dist/assets/storefront/src/app/[locale]/cart/cart-remove-button.tsx +36 -0
- package/dist/assets/storefront/src/app/[locale]/cart/cart.tsx +151 -0
- package/dist/assets/storefront/src/app/[locale]/cart/code-input.tsx +49 -0
- package/dist/assets/storefront/src/app/[locale]/cart/loading.tsx +10 -0
- package/dist/assets/storefront/src/app/[locale]/cart/order-summary.tsx +178 -0
- package/dist/assets/storefront/src/app/[locale]/cart/page.tsx +33 -0
- package/dist/assets/storefront/src/app/[locale]/cart/promotion-code.tsx +142 -0
- package/dist/assets/storefront/src/app/[locale]/cart/reapproval-confirm.tsx +76 -0
- package/dist/assets/storefront/src/app/[locale]/cart/request-quote-button.tsx +80 -0
- package/dist/assets/storefront/src/app/[locale]/checkout/actions.ts +192 -0
- package/dist/assets/storefront/src/app/[locale]/checkout/cancel-quote-checkout-button.tsx +30 -0
- package/dist/assets/storefront/src/app/[locale]/checkout/checkout-flow.tsx +214 -0
- package/dist/assets/storefront/src/app/[locale]/checkout/checkout-provider.tsx +181 -0
- package/dist/assets/storefront/src/app/[locale]/checkout/loading.tsx +90 -0
- package/dist/assets/storefront/src/app/[locale]/checkout/order-summary.tsx +160 -0
- package/dist/assets/storefront/src/app/[locale]/checkout/page.tsx +154 -0
- package/dist/assets/storefront/src/app/[locale]/checkout/steps/delivery-step.tsx +107 -0
- package/dist/assets/storefront/src/app/[locale]/checkout/steps/payment-step.tsx +110 -0
- package/dist/assets/storefront/src/app/[locale]/checkout/steps/review-step.tsx +180 -0
- package/dist/assets/storefront/src/app/[locale]/checkout/steps/shipping-address-step.tsx +518 -0
- package/dist/assets/storefront/src/app/[locale]/checkout/types.ts +7 -0
- package/dist/assets/storefront/src/app/[locale]/error.tsx +28 -0
- package/dist/assets/storefront/src/app/[locale]/forgot-password/actions.ts +28 -0
- package/dist/assets/storefront/src/app/[locale]/forgot-password/forgot-password-form.tsx +128 -0
- package/dist/assets/storefront/src/app/[locale]/forgot-password/page.tsx +24 -0
- package/dist/assets/storefront/src/app/[locale]/globals.css +72 -0
- package/dist/assets/storefront/src/app/[locale]/layout.tsx +202 -0
- package/dist/assets/storefront/src/app/[locale]/not-found.tsx +26 -0
- package/dist/assets/storefront/src/app/[locale]/order-confirmation/[code]/order-confirmation.tsx +296 -0
- package/dist/assets/storefront/src/app/[locale]/order-confirmation/[code]/page.tsx +18 -0
- package/dist/assets/storefront/src/app/[locale]/page.tsx +125 -0
- package/dist/assets/storefront/src/app/[locale]/register/actions.ts +50 -0
- package/dist/assets/storefront/src/app/[locale]/register/page.tsx +82 -0
- package/dist/assets/storefront/src/app/[locale]/register/registration-form.tsx +237 -0
- package/dist/assets/storefront/src/app/[locale]/register-company/actions.ts +40 -0
- package/dist/assets/storefront/src/app/[locale]/register-company/page.tsx +43 -0
- package/dist/assets/storefront/src/app/[locale]/register-company/registration-form.tsx +48 -0
- package/dist/assets/storefront/src/app/[locale]/reset-password/actions.ts +40 -0
- package/dist/assets/storefront/src/app/[locale]/reset-password/page.tsx +32 -0
- package/dist/assets/storefront/src/app/[locale]/reset-password/reset-password-form.tsx +96 -0
- package/dist/assets/storefront/src/app/[locale]/shopping-list/[code]/add-to-cart-button.tsx +31 -0
- package/dist/assets/storefront/src/app/[locale]/shopping-list/[code]/loading.tsx +29 -0
- package/dist/assets/storefront/src/app/[locale]/shopping-list/[code]/page.tsx +157 -0
- package/dist/assets/storefront/src/app/[locale]/sign-in/actions.ts +76 -0
- package/dist/assets/storefront/src/app/[locale]/sign-in/login-form.tsx +177 -0
- package/dist/assets/storefront/src/app/[locale]/sign-in/page.tsx +81 -0
- package/dist/assets/storefront/src/app/[locale]/verify/actions.ts +41 -0
- package/dist/assets/storefront/src/app/[locale]/verify/page.tsx +40 -0
- package/dist/assets/storefront/src/app/[locale]/verify/verify-content.tsx +127 -0
- package/dist/assets/storefront/src/app/[locale]/verify-pending/page.tsx +67 -0
- package/dist/assets/storefront/src/app/api/clear-company-context/route.ts +26 -0
- package/dist/assets/storefront/src/app/api/draft/disable/route.ts +12 -0
- package/dist/assets/storefront/src/app/api/draft/route.ts +17 -0
- package/dist/assets/storefront/src/app/api/revalidate/route.ts +110 -0
- package/dist/assets/storefront/src/app/api/search-analytics/route.ts +16 -0
- package/dist/assets/storefront/src/app/favicon.ico +0 -0
- package/dist/assets/storefront/src/components/commerce/account-nav-items.ts +56 -0
- package/dist/assets/storefront/src/components/commerce/account-sidebar.tsx +112 -0
- package/dist/assets/storefront/src/components/commerce/active-filters.tsx +107 -0
- package/dist/assets/storefront/src/components/commerce/approval-decision-form.tsx +70 -0
- package/dist/assets/storefront/src/components/commerce/approval-status-badge.tsx +37 -0
- package/dist/assets/storefront/src/components/commerce/business-unit-switcher.tsx +56 -0
- package/dist/assets/storefront/src/components/commerce/catalog-search-bar.tsx +83 -0
- package/dist/assets/storefront/src/components/commerce/category-product-section.tsx +69 -0
- package/dist/assets/storefront/src/components/commerce/category-sidebar.tsx +133 -0
- package/dist/assets/storefront/src/components/commerce/collection-hero-banner.tsx +36 -0
- package/dist/assets/storefront/src/components/commerce/company-state-badge.tsx +32 -0
- package/dist/assets/storefront/src/components/commerce/company-switcher.tsx +58 -0
- package/dist/assets/storefront/src/components/commerce/configurator-form.tsx +606 -0
- package/dist/assets/storefront/src/components/commerce/facet-filters.tsx +286 -0
- package/dist/assets/storefront/src/components/commerce/gift-card-pdp.tsx +43 -0
- package/dist/assets/storefront/src/components/commerce/gift-card-product-info.tsx +137 -0
- package/dist/assets/storefront/src/components/commerce/highlighted-text.tsx +30 -0
- package/dist/assets/storefront/src/components/commerce/instant-search-dropdown.tsx +377 -0
- package/dist/assets/storefront/src/components/commerce/order-status-badge.tsx +41 -0
- package/dist/assets/storefront/src/components/commerce/packaging-unit-selector.tsx +146 -0
- package/dist/assets/storefront/src/components/commerce/permission-gate.tsx +20 -0
- package/dist/assets/storefront/src/components/commerce/price-range-filter.tsx +111 -0
- package/dist/assets/storefront/src/components/commerce/price-tiers.tsx +98 -0
- package/dist/assets/storefront/src/components/commerce/price.tsx +29 -0
- package/dist/assets/storefront/src/components/commerce/product-card-list.tsx +121 -0
- package/dist/assets/storefront/src/components/commerce/product-card.tsx +89 -0
- package/dist/assets/storefront/src/components/commerce/product-carousel.tsx +42 -0
- package/dist/assets/storefront/src/components/commerce/product-grid.tsx +54 -0
- package/dist/assets/storefront/src/components/commerce/product-image-carousel.tsx +127 -0
- package/dist/assets/storefront/src/components/commerce/product-info.tsx +525 -0
- package/dist/assets/storefront/src/components/commerce/product-list-expansion.tsx +393 -0
- package/dist/assets/storefront/src/components/commerce/product-list-view.tsx +34 -0
- package/dist/assets/storefront/src/components/commerce/product-sidebar.tsx +175 -0
- package/dist/assets/storefront/src/components/commerce/product-view-switcher.tsx +107 -0
- package/dist/assets/storefront/src/components/commerce/quote-action-buttons.tsx +235 -0
- package/dist/assets/storefront/src/components/commerce/quote-activity-timeline.tsx +320 -0
- package/dist/assets/storefront/src/components/commerce/quote-comment-form.tsx +52 -0
- package/dist/assets/storefront/src/components/commerce/quote-csv-export-button.tsx +97 -0
- package/dist/assets/storefront/src/components/commerce/quote-product-search.tsx +139 -0
- package/dist/assets/storefront/src/components/commerce/quote-request-form.tsx +252 -0
- package/dist/assets/storefront/src/components/commerce/quote-status-badge.tsx +41 -0
- package/dist/assets/storefront/src/components/commerce/quote-validity-indicator.tsx +33 -0
- package/dist/assets/storefront/src/components/commerce/quote-version-comparison.tsx +88 -0
- package/dist/assets/storefront/src/components/commerce/related-products.tsx +56 -0
- package/dist/assets/storefront/src/components/commerce/shop-by-category.tsx +59 -0
- package/dist/assets/storefront/src/components/commerce/shopping-list-toggle.tsx +158 -0
- package/dist/assets/storefront/src/components/commerce/sort-dropdown.tsx +51 -0
- package/dist/assets/storefront/src/components/commerce/store-credit-summary.tsx +45 -0
- package/dist/assets/storefront/src/components/commerce/subcollection-tiles.tsx +56 -0
- package/dist/assets/storefront/src/components/layout/auth-page-shell.tsx +15 -0
- package/dist/assets/storefront/src/components/layout/draft-mode-action.ts +8 -0
- package/dist/assets/storefront/src/components/layout/draft-mode-indicator.tsx +54 -0
- package/dist/assets/storefront/src/components/layout/draft-mode-link.tsx +29 -0
- package/dist/assets/storefront/src/components/layout/footer.tsx +168 -0
- package/dist/assets/storefront/src/components/layout/navbar/actions.ts +33 -0
- package/dist/assets/storefront/src/components/layout/navbar/cart-icon.tsx +50 -0
- package/dist/assets/storefront/src/components/layout/navbar/channel-picker-wrapper.tsx +19 -0
- package/dist/assets/storefront/src/components/layout/navbar/channel-picker.tsx +53 -0
- package/dist/assets/storefront/src/components/layout/navbar/currency-picker-wrapper.tsx +21 -0
- package/dist/assets/storefront/src/components/layout/navbar/currency-picker.tsx +52 -0
- package/dist/assets/storefront/src/components/layout/navbar/language-picker.tsx +51 -0
- package/dist/assets/storefront/src/components/layout/navbar/login-button.tsx +34 -0
- package/dist/assets/storefront/src/components/layout/navbar/mobile-nav.tsx +74 -0
- package/dist/assets/storefront/src/components/layout/navbar/nav-menu-items.tsx +51 -0
- package/dist/assets/storefront/src/components/layout/navbar/navbar-cart.tsx +5 -0
- package/dist/assets/storefront/src/components/layout/navbar/navbar-collections.tsx +291 -0
- package/dist/assets/storefront/src/components/layout/navbar/navbar-link.tsx +28 -0
- package/dist/assets/storefront/src/components/layout/navbar/navbar-user.tsx +147 -0
- package/dist/assets/storefront/src/components/layout/navbar/theme-switcher.tsx +39 -0
- package/dist/assets/storefront/src/components/layout/navbar.tsx +117 -0
- package/dist/assets/storefront/src/components/layout/search-input.tsx +71 -0
- package/dist/assets/storefront/src/components/providers/awaiting-approval-banner.tsx +58 -0
- package/dist/assets/storefront/src/components/providers/cart-count-server.tsx +31 -0
- package/dist/assets/storefront/src/components/providers/company-context-guard.tsx +47 -0
- package/dist/assets/storefront/src/components/providers/company-permissions-server.tsx +14 -0
- package/dist/assets/storefront/src/components/providers/theme-provider.tsx +16 -0
- package/dist/assets/storefront/src/components/shared/breadcrumbs.tsx +39 -0
- package/dist/assets/storefront/src/components/shared/catalog-sidebar-skeleton.tsx +21 -0
- package/dist/assets/storefront/src/components/shared/country-select.tsx +83 -0
- package/dist/assets/storefront/src/components/shared/error-state.tsx +16 -0
- package/dist/assets/storefront/src/components/shared/pagination.tsx +119 -0
- package/dist/assets/storefront/src/components/shared/prefetch-link.tsx +36 -0
- package/dist/assets/storefront/src/components/shared/product-grid-skeleton.tsx +26 -0
- package/dist/assets/storefront/src/components/shared/scenario-paused.tsx +80 -0
- package/dist/assets/storefront/src/components/shared/skeletons/cart-skeleton.tsx +48 -0
- package/dist/assets/storefront/src/components/shared/skeletons/navbar-user-skeleton.tsx +9 -0
- package/dist/assets/storefront/src/components/shared/skeletons/search-input-skeleton.tsx +7 -0
- package/dist/assets/storefront/src/components/shared/skeletons/search-results-skeleton.tsx +17 -0
- package/dist/assets/storefront/src/components/ui/accordion.tsx +66 -0
- package/dist/assets/storefront/src/components/ui/alert-dialog.tsx +157 -0
- package/dist/assets/storefront/src/components/ui/alert.tsx +66 -0
- package/dist/assets/storefront/src/components/ui/aspect-ratio.tsx +11 -0
- package/dist/assets/storefront/src/components/ui/avatar.tsx +53 -0
- package/dist/assets/storefront/src/components/ui/badge.tsx +46 -0
- package/dist/assets/storefront/src/components/ui/breadcrumb.tsx +109 -0
- package/dist/assets/storefront/src/components/ui/button.tsx +58 -0
- package/dist/assets/storefront/src/components/ui/calendar.tsx +213 -0
- package/dist/assets/storefront/src/components/ui/card.tsx +92 -0
- package/dist/assets/storefront/src/components/ui/carousel.tsx +241 -0
- package/dist/assets/storefront/src/components/ui/chart.tsx +357 -0
- package/dist/assets/storefront/src/components/ui/checkbox.tsx +32 -0
- package/dist/assets/storefront/src/components/ui/collapsible.tsx +33 -0
- package/dist/assets/storefront/src/components/ui/command.tsx +184 -0
- package/dist/assets/storefront/src/components/ui/context-menu.tsx +252 -0
- package/dist/assets/storefront/src/components/ui/dialog.tsx +143 -0
- package/dist/assets/storefront/src/components/ui/drawer.tsx +135 -0
- package/dist/assets/storefront/src/components/ui/dropdown-menu.tsx +257 -0
- package/dist/assets/storefront/src/components/ui/empty-state.tsx +37 -0
- package/dist/assets/storefront/src/components/ui/field.tsx +244 -0
- package/dist/assets/storefront/src/components/ui/form.tsx +167 -0
- package/dist/assets/storefront/src/components/ui/hover-card.tsx +44 -0
- package/dist/assets/storefront/src/components/ui/input-otp.tsx +77 -0
- package/dist/assets/storefront/src/components/ui/input.tsx +21 -0
- package/dist/assets/storefront/src/components/ui/kbd-shortcut.tsx +20 -0
- package/dist/assets/storefront/src/components/ui/label.tsx +24 -0
- package/dist/assets/storefront/src/components/ui/menubar.tsx +276 -0
- package/dist/assets/storefront/src/components/ui/navigation-menu.tsx +168 -0
- package/dist/assets/storefront/src/components/ui/pagination.tsx +127 -0
- package/dist/assets/storefront/src/components/ui/popover.tsx +48 -0
- package/dist/assets/storefront/src/components/ui/progress.tsx +31 -0
- package/dist/assets/storefront/src/components/ui/radio-group.tsx +45 -0
- package/dist/assets/storefront/src/components/ui/resizable.tsx +56 -0
- package/dist/assets/storefront/src/components/ui/scroll-area.tsx +58 -0
- package/dist/assets/storefront/src/components/ui/select.tsx +185 -0
- package/dist/assets/storefront/src/components/ui/separator.tsx +28 -0
- package/dist/assets/storefront/src/components/ui/sheet.tsx +139 -0
- package/dist/assets/storefront/src/components/ui/sidebar.tsx +728 -0
- package/dist/assets/storefront/src/components/ui/skeleton.tsx +13 -0
- package/dist/assets/storefront/src/components/ui/slider.tsx +63 -0
- package/dist/assets/storefront/src/components/ui/sonner.tsx +25 -0
- package/dist/assets/storefront/src/components/ui/switch.tsx +31 -0
- package/dist/assets/storefront/src/components/ui/table.tsx +116 -0
- package/dist/assets/storefront/src/components/ui/tabs.tsx +66 -0
- package/dist/assets/storefront/src/components/ui/textarea.tsx +18 -0
- package/dist/assets/storefront/src/components/ui/toggle-group.tsx +73 -0
- package/dist/assets/storefront/src/components/ui/toggle.tsx +47 -0
- package/dist/assets/storefront/src/components/ui/tooltip.tsx +61 -0
- package/dist/assets/storefront/src/config/branding.ts +4 -0
- package/dist/assets/storefront/src/config/scenario-meta.ts +46 -0
- package/dist/assets/storefront/src/config/scenario.ts +313 -0
- package/dist/assets/storefront/src/contexts/auth-context.tsx +35 -0
- package/dist/assets/storefront/src/graphql.ts +14 -0
- package/dist/assets/storefront/src/hooks/use-mobile.ts +19 -0
- package/dist/assets/storefront/src/i18n/locale-utils.ts +33 -0
- package/dist/assets/storefront/src/i18n/locales.ts +18 -0
- package/dist/assets/storefront/src/i18n/navigation.ts +35 -0
- package/dist/assets/storefront/src/i18n/request.ts +19 -0
- package/dist/assets/storefront/src/i18n/routing.ts +19 -0
- package/dist/assets/storefront/src/i18n/server.ts +16 -0
- package/dist/assets/storefront/src/lib/actions/switch-channel.ts +32 -0
- package/dist/assets/storefront/src/lib/actions/switch-currency.ts +25 -0
- package/dist/assets/storefront/src/lib/auth.ts +18 -0
- package/dist/assets/storefront/src/lib/backend-health.ts +38 -0
- package/dist/assets/storefront/src/lib/cart-count-context.tsx +59 -0
- package/dist/assets/storefront/src/lib/cart-guard-utils.ts +10 -0
- package/dist/assets/storefront/src/lib/cart-guard.ts +104 -0
- package/dist/assets/storefront/src/lib/channel-server.ts +28 -0
- package/dist/assets/storefront/src/lib/channel.ts +2 -0
- package/dist/assets/storefront/src/lib/company-context.ts +35 -0
- package/dist/assets/storefront/src/lib/company-permissions-context.tsx +40 -0
- package/dist/assets/storefront/src/lib/currency-server.ts +20 -0
- package/dist/assets/storefront/src/lib/currency.ts +2 -0
- package/dist/assets/storefront/src/lib/format.ts +48 -0
- package/dist/assets/storefront/src/lib/metadata.ts +85 -0
- package/dist/assets/storefront/src/lib/quantity.test.js +20 -0
- package/dist/assets/storefront/src/lib/quantity.ts +20 -0
- package/dist/assets/storefront/src/lib/search-bar-context.tsx +45 -0
- package/dist/assets/storefront/src/lib/search-helpers.ts +69 -0
- package/dist/assets/storefront/src/lib/use-recent-searches.ts +55 -0
- package/dist/assets/storefront/src/lib/utils.ts +6 -0
- package/dist/assets/storefront/src/lib/vendure/actions.ts +96 -0
- package/dist/assets/storefront/src/lib/vendure/api.ts +164 -0
- package/dist/assets/storefront/src/lib/vendure/approval-utils.ts +21 -0
- package/dist/assets/storefront/src/lib/vendure/cached.ts +71 -0
- package/dist/assets/storefront/src/lib/vendure/fragments.ts +213 -0
- package/dist/assets/storefront/src/lib/vendure/instant-search-action.ts +104 -0
- package/dist/assets/storefront/src/lib/vendure/mutations.ts +1136 -0
- package/dist/assets/storefront/src/lib/vendure/queries.ts +1406 -0
- package/dist/assets/storefront/src/middleware.ts +11 -0
- package/dist/assets/storefront/src/scenarios/b2b-wholesale/branding.ts +180 -0
- package/dist/assets/storefront/src/scenarios/b2b-wholesale/hero.tsx +122 -0
- package/dist/assets/storefront/src/scenarios/b2b-wholesale/index.ts +21 -0
- package/dist/assets/storefront/src/scenarios/b2b-wholesale/meta.ts +3 -0
- package/dist/assets/storefront/src/scenarios/default/branding.ts +148 -0
- package/dist/assets/storefront/src/scenarios/default/hero.tsx +107 -0
- package/dist/assets/storefront/src/scenarios/default/index.ts +12 -0
- package/dist/assets/storefront/src/scenarios/default/meta.ts +3 -0
- package/dist/assets/storefront/src/scenarios/kaminpro/auth-gate.tsx +36 -0
- package/dist/assets/storefront/src/scenarios/kaminpro/branding.ts +238 -0
- package/dist/assets/storefront/src/scenarios/kaminpro/cart-items.tsx +359 -0
- package/dist/assets/storefront/src/scenarios/kaminpro/hero.tsx +123 -0
- package/dist/assets/storefront/src/scenarios/kaminpro/index.ts +50 -0
- package/dist/assets/storefront/src/scenarios/kaminpro/meta.ts +9 -0
- package/dist/assets/storefront/src/scenarios/kaminpro/pdp/components/banners.tsx +21 -0
- package/dist/assets/storefront/src/scenarios/kaminpro/pdp/components/bom-basket.tsx +221 -0
- package/dist/assets/storefront/src/scenarios/kaminpro/pdp/components/btn.tsx +38 -0
- package/dist/assets/storefront/src/scenarios/kaminpro/pdp/components/chip.tsx +35 -0
- package/dist/assets/storefront/src/scenarios/kaminpro/pdp/components/config-header.tsx +72 -0
- package/dist/assets/storefront/src/scenarios/kaminpro/pdp/components/field-row.tsx +126 -0
- package/dist/assets/storefront/src/scenarios/kaminpro/pdp/components/info-dot.tsx +38 -0
- package/dist/assets/storefront/src/scenarios/kaminpro/pdp/components/login-notice.tsx +81 -0
- package/dist/assets/storefront/src/scenarios/kaminpro/pdp/components/module-panel.tsx +144 -0
- package/dist/assets/storefront/src/scenarios/kaminpro/pdp/components/quote-dialog.tsx +110 -0
- package/dist/assets/storefront/src/scenarios/kaminpro/pdp/components/schematic.tsx +674 -0
- package/dist/assets/storefront/src/scenarios/kaminpro/pdp/components/why-panel.tsx +66 -0
- package/dist/assets/storefront/src/scenarios/kaminpro/pdp/configurator-pdp.tsx +43 -0
- package/dist/assets/storefront/src/scenarios/kaminpro/pdp/configurator-shell.tsx +320 -0
- package/dist/assets/storefront/src/scenarios/kaminpro/pdp/lib/data.ts +150 -0
- package/dist/assets/storefront/src/scenarios/kaminpro/pdp/lib/types.ts +30 -0
- package/dist/assets/storefront/src/scenarios/kaminpro/pdp/styles.css +104 -0
- package/dist/assets/storefront/src/scenarios/kaminpro/shop-by-system.tsx +97 -0
- package/dist/assets/storefront/src/scenarios/kaminpro/system-finder/decision-tree.ts +261 -0
- package/dist/assets/storefront/src/scenarios/kaminpro/system-finder/page.tsx +21 -0
- package/dist/assets/storefront/src/scenarios/kaminpro/system-finder/wizard.tsx +600 -0
- package/dist/assets/storefront/src/schema.graphql.placeholder +5 -0
- package/dist/assets/storefront/tsconfig.json +37 -0
- package/dist/assets/templates/CLAUDE.md.hbs +63 -0
- package/dist/assets/templates/README.md.hbs +53 -0
- package/dist/assets/templates/custom-populate.ts.hbs +28 -0
- package/dist/assets/templates/docker-compose.yml.hbs +29 -0
- package/dist/assets/templates/env.hbs +44 -0
- package/dist/assets/templates/environment.d.ts.hbs +32 -0
- package/dist/assets/templates/gitignore.hbs +7 -0
- package/dist/assets/templates/index.ts.hbs +16 -0
- package/dist/assets/templates/npmrc.hbs +1 -0
- package/dist/assets/templates/populate.ts.hbs +27 -0
- package/dist/assets/templates/root-package.json.hbs +17 -0
- package/dist/assets/templates/server-package.json.hbs +31 -0
- package/dist/assets/templates/tsconfig.dashboard.json.hbs +17 -0
- package/dist/assets/templates/tsconfig.root.json.hbs +16 -0
- package/dist/assets/templates/tsconfig.server.json.hbs +20 -0
- package/dist/assets/templates/vendure-config.ts.hbs +80 -0
- package/dist/assets/templates/vite.config.mts.hbs +27 -0
- package/dist/bin/run.d.ts +2 -0
- package/dist/bin/run.js +79 -0
- package/dist/bin/run.js.map +1 -0
- package/dist/cli.d.ts +6 -0
- package/dist/cli.js +154 -0
- package/dist/cli.js.map +1 -0
- package/dist/git-init.d.ts +4 -0
- package/dist/git-init.js +60 -0
- package/dist/git-init.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +13 -0
- package/dist/index.js.map +1 -0
- package/dist/plugin-registry.d.ts +10 -0
- package/dist/plugin-registry.js +485 -0
- package/dist/plugin-registry.js.map +1 -0
- package/dist/scaffolder.d.ts +10 -0
- package/dist/scaffolder.js +218 -0
- package/dist/scaffolder.js.map +1 -0
- package/dist/storefront-copier.d.ts +6 -0
- package/dist/storefront-copier.js +68 -0
- package/dist/storefront-copier.js.map +1 -0
- package/dist/template-renderer.d.ts +30 -0
- package/dist/template-renderer.js +177 -0
- package/dist/template-renderer.js.map +1 -0
- package/dist/types.d.ts +60 -0
- package/dist/types.js +3 -0
- package/dist/types.js.map +1 -0
- package/package.json +48 -0
|
@@ -0,0 +1,313 @@
|
|
|
1
|
+
import type { ReactElement } from 'react';
|
|
2
|
+
import type { ConfiguratorSystem, ProductDetail } from '@/lib/vendure/queries';
|
|
3
|
+
import type { ShoppingListInfo } from '@/app/[locale]/account/shopping-lists/actions';
|
|
4
|
+
import type { ProductInfoProps } from '@/components/commerce/product-info';
|
|
5
|
+
import type { ProductSidebarProps } from '@/components/commerce/product-sidebar';
|
|
6
|
+
import type { ProductImageCarouselProps } from '@/components/commerce/product-image-carousel';
|
|
7
|
+
import type { RelatedProductsProps } from '@/components/commerce/related-products';
|
|
8
|
+
import type { ShopByCategoryProps } from '@/components/commerce/shop-by-category';
|
|
9
|
+
import { defaultScenario } from '@/scenarios/default';
|
|
10
|
+
import { bauDirektScenario } from '@/scenarios/b2b-wholesale';
|
|
11
|
+
import { kaminProScenario } from '@/scenarios/kaminpro';
|
|
12
|
+
|
|
13
|
+
// =============================================================
|
|
14
|
+
// Branding — pure serializable data (no React components)
|
|
15
|
+
// =============================================================
|
|
16
|
+
|
|
17
|
+
export interface StorefrontBranding {
|
|
18
|
+
siteName: string;
|
|
19
|
+
siteDescription: string;
|
|
20
|
+
|
|
21
|
+
logo: { src: string; alt: string; width: number; height: number };
|
|
22
|
+
logoInverted?: { src: string; alt: string; width: number; height: number };
|
|
23
|
+
|
|
24
|
+
/** CSS custom property overrides, keyed by variable name (e.g. '--primary', '--radius') */
|
|
25
|
+
colors: {
|
|
26
|
+
dark: Record<string, string>;
|
|
27
|
+
light: Record<string, string>;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
/** Navigation links shown in the top navbar. Defaults to Catalog, Orders, Quotes. */
|
|
31
|
+
navLinks?: Array<{ href: string; label: string }>;
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* By default the Navbar appends an `Unternehmen` link for logged-in
|
|
35
|
+
* company-bound users. Opt out to keep the scenario nav strip fixed —
|
|
36
|
+
* useful for demos that want to keep the nav narrow regardless of
|
|
37
|
+
* who's signed in.
|
|
38
|
+
*/
|
|
39
|
+
hideCompanyNavLink?: boolean;
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Hide the globe / language-picker dropdown in the navbar. Useful for
|
|
43
|
+
* single-locale scenarios where offering a locale switch suggests
|
|
44
|
+
* translations exist when they don't. The underlying routing still
|
|
45
|
+
* carries the locale prefix; only the UI affordance is suppressed.
|
|
46
|
+
*/
|
|
47
|
+
hideLanguagePicker?: boolean;
|
|
48
|
+
|
|
49
|
+
/** Demo accounts shown on the sign-in page for quick login. */
|
|
50
|
+
demoAccounts?: Array<{ email: string; label: string; description: string }>;
|
|
51
|
+
|
|
52
|
+
footer: {
|
|
53
|
+
copyright: string;
|
|
54
|
+
poweredByLinks?: Array<{ label: string; href: string }>;
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
metadata: {
|
|
58
|
+
themeColor: string;
|
|
59
|
+
defaultDescription: string;
|
|
60
|
+
homepageTitle: string;
|
|
61
|
+
homepageDescription: string;
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Optional typography opt-ins. Default scenarios get the standard Geist
|
|
66
|
+
* stack; opting in swaps the whole site onto the scenario's preferred
|
|
67
|
+
* typeface. Kept as a narrow flag set rather than a free-form font
|
|
68
|
+
* descriptor so next/font imports can stay at module scope (required by
|
|
69
|
+
* next/font — dynamic imports are not supported).
|
|
70
|
+
*/
|
|
71
|
+
fonts?: {
|
|
72
|
+
/**
|
|
73
|
+
* Use the "technical documentation" stack: `Inter Tight` for prose
|
|
74
|
+
* and `JetBrains Mono` for code/monospace contexts. Also adds the
|
|
75
|
+
* `.kp-app` class to `<body>` so scenario-scoped utility styles
|
|
76
|
+
* (caps tracking, tabular nums, slim scrollbars) engage.
|
|
77
|
+
*/
|
|
78
|
+
technical?: boolean;
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// =============================================================
|
|
83
|
+
// Component override types
|
|
84
|
+
// =============================================================
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* A component type that supports both sync (Client) and async (Server) components.
|
|
88
|
+
* React's ComponentType can't express async Server Components — see
|
|
89
|
+
* https://github.com/DefinitelyTyped/DefinitelyTyped/discussions/65079
|
|
90
|
+
*/
|
|
91
|
+
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
|
|
92
|
+
export type ServerComponent<P = {}> = (props: P) => ReactElement | Promise<ReactElement>;
|
|
93
|
+
|
|
94
|
+
/** Props for a full PDP page override */
|
|
95
|
+
export interface PdpPageProps {
|
|
96
|
+
product: ProductDetail;
|
|
97
|
+
searchParams: { [key: string]: string | string[] | undefined };
|
|
98
|
+
breadcrumbs: Array<{ name: string; href?: string }>;
|
|
99
|
+
shoppingLists: ShoppingListInfo[] | null;
|
|
100
|
+
currencyCode: string;
|
|
101
|
+
/** Active channel token — pass to publicClient queries for correct channel isolation in 'use cache' functions. */
|
|
102
|
+
channelToken?: string;
|
|
103
|
+
/**
|
|
104
|
+
* Fetched `ConfiguratorSystem` for products carrying a
|
|
105
|
+
* `configuratorSystemCode` custom field. `null` for non-configurable
|
|
106
|
+
* products (a conditional PDP override would normally gate on this).
|
|
107
|
+
*/
|
|
108
|
+
configuratorSystem?: ConfiguratorSystem | null;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/** Slot-level overrides for the default PDP layout */
|
|
112
|
+
export interface PdpSlotOverrides {
|
|
113
|
+
ProductInfo?: ServerComponent<ProductInfoProps>;
|
|
114
|
+
ProductSidebar?: ServerComponent<ProductSidebarProps>;
|
|
115
|
+
ProductImageCarousel?: ServerComponent<ProductImageCarouselProps>;
|
|
116
|
+
RelatedProducts?: ServerComponent<RelatedProductsProps>;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Full-page PDP override that only engages when `shouldApply` returns true
|
|
121
|
+
* for the current product. Non-matching products fall through to the default
|
|
122
|
+
* PDP composition (or to `slots` if both are provided on a future extension).
|
|
123
|
+
*/
|
|
124
|
+
export interface ConditionalPdpOverride {
|
|
125
|
+
shouldApply: (product: ProductDetail) => boolean;
|
|
126
|
+
component: ServerComponent<PdpPageProps>;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/** Slot-level overrides for the default homepage layout */
|
|
130
|
+
export interface HomeSlotOverrides {
|
|
131
|
+
Hero?: ServerComponent;
|
|
132
|
+
/**
|
|
133
|
+
* Replace the default `ShopByCategory` tile row. The base component only
|
|
134
|
+
* renders when every top-level collection has a `featuredAsset`; an
|
|
135
|
+
* override is free to ignore that rule.
|
|
136
|
+
*/
|
|
137
|
+
ShopByCategory?: ServerComponent<ShopByCategoryProps>;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/** Slot-level overrides for the default cart page. */
|
|
141
|
+
export interface CartSlotOverrides {
|
|
142
|
+
/**
|
|
143
|
+
* Replace the `<CartItems>` component inside the cart page. The override
|
|
144
|
+
* receives the same props as the built-in component and is responsible
|
|
145
|
+
* for rendering the entire line-items column (not the order summary /
|
|
146
|
+
* promotion-code sidebar).
|
|
147
|
+
*
|
|
148
|
+
* Typed as `ComponentType<any>` to avoid locking the storefront into the
|
|
149
|
+
* built-in `ActiveOrder` shape — a scenario override can query its own
|
|
150
|
+
* extra fields and declare a stricter type locally.
|
|
151
|
+
*/
|
|
152
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
153
|
+
CartItems?: ServerComponent<any>;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// =============================================================
|
|
157
|
+
// Channel switching
|
|
158
|
+
// =============================================================
|
|
159
|
+
|
|
160
|
+
export interface ChannelOption {
|
|
161
|
+
/** The vendure-token header value */
|
|
162
|
+
token: string;
|
|
163
|
+
/** Human-readable label shown in the picker UI (e.g. "Österreich") */
|
|
164
|
+
label: string;
|
|
165
|
+
/** ISO 3166-1 alpha-2 country code (e.g. "AT", "DE") */
|
|
166
|
+
countryCode: string;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
export interface ChannelsConfig {
|
|
170
|
+
/** Available channels for this scenario */
|
|
171
|
+
available: ChannelOption[];
|
|
172
|
+
/** Token of the channel to use when no cookie is set */
|
|
173
|
+
defaultToken: string;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// =============================================================
|
|
177
|
+
// Scenario Config
|
|
178
|
+
// =============================================================
|
|
179
|
+
|
|
180
|
+
export interface ScenarioConfig {
|
|
181
|
+
branding: StorefrontBranding;
|
|
182
|
+
|
|
183
|
+
/** Multi-channel configuration. When present, enables the ChannelPicker in the navbar. */
|
|
184
|
+
channels?: ChannelsConfig;
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Per-page overrides. Each key accepts EITHER:
|
|
188
|
+
* - A ServerComponent to replace the entire page
|
|
189
|
+
* - An object with slot keys to override individual sections
|
|
190
|
+
*/
|
|
191
|
+
overrides?: {
|
|
192
|
+
pdp?: ServerComponent<PdpPageProps> | ConditionalPdpOverride | PdpSlotOverrides;
|
|
193
|
+
home?: ServerComponent | HomeSlotOverrides;
|
|
194
|
+
cart?: CartSlotOverrides;
|
|
195
|
+
};
|
|
196
|
+
|
|
197
|
+
/** Scenario-specific routes. The route page file checks this to render or return notFound(). */
|
|
198
|
+
routes?: Record<string, ServerComponent>;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* Type guard: is the override a full page component (a function), versus a
|
|
203
|
+
* slot-overrides object?
|
|
204
|
+
*
|
|
205
|
+
* NOTE: this guard discriminates ONLY on `typeof === 'function'`. If a future
|
|
206
|
+
* override variant is added that is itself callable (e.g. a higher-order
|
|
207
|
+
* component returning a slot map), this guard breaks. Only add object-valued
|
|
208
|
+
* variants to the override unions.
|
|
209
|
+
*/
|
|
210
|
+
export function isFullPageOverride(
|
|
211
|
+
override: unknown,
|
|
212
|
+
): override is (...args: unknown[]) => unknown {
|
|
213
|
+
return typeof override === 'function';
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/** Type guard: is the override a conditional `{ shouldApply, component }` form? */
|
|
217
|
+
export function isConditionalPdpOverride(
|
|
218
|
+
override: unknown,
|
|
219
|
+
): override is ConditionalPdpOverride {
|
|
220
|
+
return (
|
|
221
|
+
typeof override === 'object' &&
|
|
222
|
+
override !== null &&
|
|
223
|
+
typeof (override as ConditionalPdpOverride).shouldApply === 'function' &&
|
|
224
|
+
typeof (override as ConditionalPdpOverride).component === 'function'
|
|
225
|
+
);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// =============================================================
|
|
229
|
+
// Registry — add new scenarios here
|
|
230
|
+
// =============================================================
|
|
231
|
+
|
|
232
|
+
const registry: Record<string, ScenarioConfig> = {
|
|
233
|
+
default: defaultScenario,
|
|
234
|
+
'b2b-wholesale': bauDirektScenario,
|
|
235
|
+
kaminpro: kaminProScenario,
|
|
236
|
+
};
|
|
237
|
+
|
|
238
|
+
// Safe to cache at module level: NEXT_PUBLIC_SCENARIO is baked at build time, so
|
|
239
|
+
// the active scenario never changes within a single process/build.
|
|
240
|
+
let _cached: ScenarioConfig | undefined;
|
|
241
|
+
|
|
242
|
+
export function getScenarioConfig(): ScenarioConfig {
|
|
243
|
+
if (_cached) return _cached;
|
|
244
|
+
const scenario = process.env.NEXT_PUBLIC_SCENARIO || 'default';
|
|
245
|
+
if (!registry[scenario]) {
|
|
246
|
+
console.warn(
|
|
247
|
+
`[scenario] Unknown scenario "${scenario}" — falling back to "default". ` +
|
|
248
|
+
`Did you register it in scenario.ts?`,
|
|
249
|
+
);
|
|
250
|
+
}
|
|
251
|
+
_cached = registry[scenario] ?? registry.default;
|
|
252
|
+
return _cached;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
export function getBranding(): StorefrontBranding {
|
|
256
|
+
return getScenarioConfig().branding;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
export function getChannelsConfig(): ChannelsConfig | undefined {
|
|
260
|
+
return getScenarioConfig().channels;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
const _brandingStyleTag = (() => {
|
|
264
|
+
const { colors, fonts } = getBranding();
|
|
265
|
+
const darkVars = Object.entries(colors.dark)
|
|
266
|
+
.map(([k, v]) => `${k}: ${v}`)
|
|
267
|
+
.join('; ') + ';';
|
|
268
|
+
const lightVars = Object.entries(colors.light)
|
|
269
|
+
.map(([k, v]) => `${k}: ${v}`)
|
|
270
|
+
.join('; ') + ';';
|
|
271
|
+
|
|
272
|
+
let base = `:root { ${darkVars} } .light { ${lightVars} }`;
|
|
273
|
+
|
|
274
|
+
// Scenario-scoped typography overrides. The `.kp-app` class is only
|
|
275
|
+
// added to <body> when `fonts.technical === true`, so these rules
|
|
276
|
+
// are dormant for every other scenario.
|
|
277
|
+
if (fonts?.technical) {
|
|
278
|
+
base += `
|
|
279
|
+
.kp-app {
|
|
280
|
+
--font-geist-sans: var(--font-inter-tight);
|
|
281
|
+
--font-geist-mono: var(--font-jetbrains-mono);
|
|
282
|
+
font-feature-settings: 'ss01', 'cv11', 'tnum';
|
|
283
|
+
letter-spacing: -0.005em;
|
|
284
|
+
}
|
|
285
|
+
.kp-app .caps {
|
|
286
|
+
font-size: 10.5px;
|
|
287
|
+
font-weight: 600;
|
|
288
|
+
letter-spacing: 0.08em;
|
|
289
|
+
text-transform: uppercase;
|
|
290
|
+
}
|
|
291
|
+
.kp-app .mono {
|
|
292
|
+
font-family: var(--font-jetbrains-mono), ui-monospace, monospace;
|
|
293
|
+
}
|
|
294
|
+
.kp-app :where(table, .tabular-nums) { font-variant-numeric: tabular-nums; }
|
|
295
|
+
/* Slim scrollbars in scenario-scoped scroll containers (same rules as
|
|
296
|
+
the configurator's scroll-slim utility, just widened in scope). */
|
|
297
|
+
.kp-app *::-webkit-scrollbar { width: 8px; height: 8px; }
|
|
298
|
+
.kp-app *::-webkit-scrollbar-track { background: transparent; }
|
|
299
|
+
.kp-app *::-webkit-scrollbar-thumb {
|
|
300
|
+
background: var(--border);
|
|
301
|
+
border-radius: 4px;
|
|
302
|
+
border: 2px solid var(--background);
|
|
303
|
+
}
|
|
304
|
+
.kp-app *::-webkit-scrollbar-thumb:hover { background: var(--muted-foreground); }
|
|
305
|
+
`;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
return base;
|
|
309
|
+
})();
|
|
310
|
+
|
|
311
|
+
export function getBrandingStyleTag(): string {
|
|
312
|
+
return _brandingStyleTag;
|
|
313
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import {createContext, ReactNode, useContext} from 'react';
|
|
4
|
+
import {ResultOf} from '@/graphql';
|
|
5
|
+
import {ActiveCustomerFragment} from '@/lib/vendure/fragments';
|
|
6
|
+
|
|
7
|
+
type ActiveCustomer = ResultOf<typeof ActiveCustomerFragment>;
|
|
8
|
+
|
|
9
|
+
interface AuthContextType {
|
|
10
|
+
activeCustomer: Promise<ActiveCustomer | null>;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const AuthContext = createContext<AuthContextType | undefined>(undefined);
|
|
14
|
+
|
|
15
|
+
interface AuthProviderProps {
|
|
16
|
+
children: ReactNode;
|
|
17
|
+
initialActiveCustomerPromise: Promise<ActiveCustomer | null>;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function AuthProvider({children, initialActiveCustomerPromise}: AuthProviderProps) {
|
|
21
|
+
|
|
22
|
+
return (
|
|
23
|
+
<AuthContext.Provider value={{activeCustomer: initialActiveCustomerPromise}}>
|
|
24
|
+
{children}
|
|
25
|
+
</AuthContext.Provider>
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function useAuth() {
|
|
30
|
+
const context = useContext(AuthContext);
|
|
31
|
+
if (context === undefined) {
|
|
32
|
+
throw new Error('useAuth must be used within an AuthProvider');
|
|
33
|
+
}
|
|
34
|
+
return context;
|
|
35
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { initGraphQLTada } from 'gql.tada';
|
|
2
|
+
import type { introspection } from './graphql-env.d.ts';
|
|
3
|
+
|
|
4
|
+
export const graphql = initGraphQLTada<{
|
|
5
|
+
introspection: introspection;
|
|
6
|
+
scalars: {
|
|
7
|
+
DateTime: string,
|
|
8
|
+
JSON: Record<string, unknown>,
|
|
9
|
+
Money: number
|
|
10
|
+
}
|
|
11
|
+
}>();
|
|
12
|
+
|
|
13
|
+
export type { FragmentOf, ResultOf, VariablesOf } from 'gql.tada';
|
|
14
|
+
export { readFragment } from 'gql.tada';
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import * as React from "react"
|
|
2
|
+
|
|
3
|
+
const MOBILE_BREAKPOINT = 768
|
|
4
|
+
|
|
5
|
+
export function useIsMobile() {
|
|
6
|
+
const [isMobile, setIsMobile] = React.useState<boolean | undefined>(undefined)
|
|
7
|
+
|
|
8
|
+
React.useEffect(() => {
|
|
9
|
+
const mql = window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT - 1}px)`)
|
|
10
|
+
const onChange = () => {
|
|
11
|
+
setIsMobile(window.innerWidth < MOBILE_BREAKPOINT)
|
|
12
|
+
}
|
|
13
|
+
mql.addEventListener("change", onChange)
|
|
14
|
+
setIsMobile(window.innerWidth < MOBILE_BREAKPOINT)
|
|
15
|
+
return () => mql.removeEventListener("change", onChange)
|
|
16
|
+
}, [])
|
|
17
|
+
|
|
18
|
+
return !!isMobile
|
|
19
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import {routing, type Locale} from './routing';
|
|
2
|
+
|
|
3
|
+
const INTL_LOCALE_MAP: Record<Locale, string> = {en: 'en-US', de: 'de-DE'};
|
|
4
|
+
const OG_LOCALE_MAP: Record<Locale, string> = {en: 'en_US', de: 'de_DE'};
|
|
5
|
+
|
|
6
|
+
/** Map a routing locale to a full BCP-47 tag for Intl APIs. Falls back to the raw locale. */
|
|
7
|
+
export function toIntlLocale(locale: string): string {
|
|
8
|
+
return INTL_LOCALE_MAP[locale as Locale] ?? locale;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/** Map a routing locale to an OpenGraph-style locale. Falls back to the raw locale. */
|
|
12
|
+
export function toOgLocale(locale: string): string {
|
|
13
|
+
return OG_LOCALE_MAP[locale as Locale] ?? locale;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/** Prepend a locale prefix to a path. Ensures no double-prefixing. */
|
|
17
|
+
export function applyLocalePrefix(path: string, locale: string): string {
|
|
18
|
+
const stripped = stripLocalePrefix(path);
|
|
19
|
+
return `/${locale}${stripped === '/' ? '' : stripped}`;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/** Strip locale prefix from a path. Returns the path unchanged if no prefix matches. */
|
|
23
|
+
export function stripLocalePrefix(path: string): string {
|
|
24
|
+
for (const locale of routing.locales) {
|
|
25
|
+
if (path.startsWith(`/${locale}/`)) {
|
|
26
|
+
return path.slice(locale.length + 1) || '/';
|
|
27
|
+
}
|
|
28
|
+
if (path === `/${locale}`) {
|
|
29
|
+
return '/';
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return path;
|
|
33
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Single source of truth for the set of locales the storefront supports.
|
|
3
|
+
*
|
|
4
|
+
* Kept in its own tiny module so modules outside the routing graph
|
|
5
|
+
* (e.g. server actions, helpers that need to narrow `string → Locale`)
|
|
6
|
+
* can import the `Locale` type without pulling `routing.ts`'s full
|
|
7
|
+
* import surface — `routing.ts` itself is imported by client components
|
|
8
|
+
* via `@/i18n/navigation`, so anything it drags in ends up in client
|
|
9
|
+
* bundles.
|
|
10
|
+
*/
|
|
11
|
+
export const LOCALES = ['en', 'de'] as const;
|
|
12
|
+
|
|
13
|
+
export type Locale = (typeof LOCALES)[number];
|
|
14
|
+
|
|
15
|
+
export const localeNames: Record<Locale, string> = {
|
|
16
|
+
en: 'English',
|
|
17
|
+
de: 'Deutsch',
|
|
18
|
+
};
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import {createNavigation} from 'next-intl/navigation';
|
|
2
|
+
import {routing} from './routing';
|
|
3
|
+
|
|
4
|
+
const nav = createNavigation(routing);
|
|
5
|
+
|
|
6
|
+
export const Link = nav.Link;
|
|
7
|
+
export const usePathname = nav.usePathname;
|
|
8
|
+
export const useRouter = nav.useRouter;
|
|
9
|
+
export const getPathname = nav.getPathname;
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Locale-aware redirect that accepts a plain pathname string.
|
|
13
|
+
*
|
|
14
|
+
* next-intl 4.9+ changed nav.redirect() to require {href, locale} instead
|
|
15
|
+
* of a plain string. This wrapper preserves the simpler call-site API used
|
|
16
|
+
* across ~30 server actions and pages: redirect('/path').
|
|
17
|
+
*
|
|
18
|
+
* ⚠ MUST BE AWAITED. This is an async function — a bare `redirect('/path')`
|
|
19
|
+
* call returns a floating Promise and the NEXT_REDIRECT never gets thrown,
|
|
20
|
+
* so the caller keeps executing past the "redirect". Always write:
|
|
21
|
+
*
|
|
22
|
+
* await redirect('/path');
|
|
23
|
+
*
|
|
24
|
+
* (Unlike `redirect` from `next/navigation`, which is synchronous and
|
|
25
|
+
* throws immediately.)
|
|
26
|
+
*
|
|
27
|
+
* Uses a dynamic import for getLocale to avoid pulling next-intl/server
|
|
28
|
+
* into the module's top-level scope (which would break client component
|
|
29
|
+
* bundling since this module also exports Link).
|
|
30
|
+
*/
|
|
31
|
+
export async function redirect(pathname: string, type?: 'push' | 'replace'): Promise<never> {
|
|
32
|
+
const {getLocale} = await import('next-intl/server');
|
|
33
|
+
const locale = await getLocale();
|
|
34
|
+
return nav.redirect({href: pathname, locale}, type);
|
|
35
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import {hasLocale} from 'next-intl';
|
|
2
|
+
import {getRequestConfig} from 'next-intl/server';
|
|
3
|
+
import {routing} from './routing';
|
|
4
|
+
|
|
5
|
+
// Static per-locale imports. When adding a new locale to routing.locales,
|
|
6
|
+
// add a corresponding entry here.
|
|
7
|
+
const messageImports = {
|
|
8
|
+
en: () => import('../../messages/en.json'),
|
|
9
|
+
de: () => import('../../messages/de.json'),
|
|
10
|
+
} as const;
|
|
11
|
+
|
|
12
|
+
export default getRequestConfig(async ({requestLocale}) => {
|
|
13
|
+
const requested = await requestLocale;
|
|
14
|
+
const locale = hasLocale(routing.locales, requested) ? requested : routing.defaultLocale;
|
|
15
|
+
return {
|
|
16
|
+
locale,
|
|
17
|
+
messages: (await messageImports[locale]()).default,
|
|
18
|
+
};
|
|
19
|
+
});
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import {defineRouting} from 'next-intl/routing';
|
|
2
|
+
import {getScenarioMeta} from '@/config/scenario-meta';
|
|
3
|
+
import {LOCALES, type Locale, localeNames} from './locales';
|
|
4
|
+
|
|
5
|
+
// Scenarios declare their preferred default locale in their own
|
|
6
|
+
// `meta.ts` (a client-safe projection that never imports React or
|
|
7
|
+
// server-only APIs). `getScenarioMeta()` resolves the active scenario
|
|
8
|
+
// at build time from `NEXT_PUBLIC_SCENARIO` and returns its meta.
|
|
9
|
+
// Scenarios that don't declare a `defaultLocale` inherit the first
|
|
10
|
+
// entry in `LOCALES`.
|
|
11
|
+
const scenarioDefaultLocale = getScenarioMeta().defaultLocale ?? LOCALES[0];
|
|
12
|
+
|
|
13
|
+
export const routing = defineRouting({
|
|
14
|
+
locales: LOCALES,
|
|
15
|
+
defaultLocale: scenarioDefaultLocale,
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
export type {Locale};
|
|
19
|
+
export {localeNames};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import {getLocale} from 'next-intl/server';
|
|
2
|
+
import {hasLocale} from 'next-intl';
|
|
3
|
+
import {routing, type Locale} from './routing';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Resolve the current locale from request context with validation.
|
|
7
|
+
* Falls back to defaultLocale if the resolved locale isn't in routing config.
|
|
8
|
+
*
|
|
9
|
+
* Use this in non-cached server components and server actions.
|
|
10
|
+
* For 'use cache' scopes or route files (layout/page), prefer rootLocale()
|
|
11
|
+
* from 'next/root-params' directly (the SWC transform only works in route files).
|
|
12
|
+
*/
|
|
13
|
+
export async function getRouteLocale(): Promise<Locale> {
|
|
14
|
+
const loc = await getLocale();
|
|
15
|
+
return hasLocale(routing.locales, loc) ? (loc as Locale) : routing.defaultLocale;
|
|
16
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
'use server';
|
|
2
|
+
|
|
3
|
+
import {cookies} from 'next/headers';
|
|
4
|
+
import {revalidatePath} from 'next/cache';
|
|
5
|
+
import {CHANNEL_COOKIE} from '@/lib/channel';
|
|
6
|
+
import {CURRENCY_COOKIE} from '@/lib/currency';
|
|
7
|
+
import {getChannelsConfig} from '@/config/scenario';
|
|
8
|
+
|
|
9
|
+
export async function switchChannel(token: string) {
|
|
10
|
+
const channelsConfig = getChannelsConfig();
|
|
11
|
+
if (!channelsConfig) {
|
|
12
|
+
return {success: false, message: 'Channel switching not configured'};
|
|
13
|
+
}
|
|
14
|
+
if (!channelsConfig.available.some(c => c.token === token)) {
|
|
15
|
+
return {success: false, message: 'Invalid channel token'};
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const jar = await cookies();
|
|
19
|
+
jar.set(CHANNEL_COOKIE, token, {
|
|
20
|
+
path: '/',
|
|
21
|
+
maxAge: 60 * 60 * 24 * 365,
|
|
22
|
+
sameSite: 'lax',
|
|
23
|
+
httpOnly: true,
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
// Clear currency preference — the new channel may have different available currencies,
|
|
27
|
+
// so let getActiveCurrencyCode() fall back to the new channel's default.
|
|
28
|
+
jar.delete(CURRENCY_COOKIE);
|
|
29
|
+
|
|
30
|
+
revalidatePath('/', 'layout');
|
|
31
|
+
return {success: true};
|
|
32
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
'use server';
|
|
2
|
+
|
|
3
|
+
import {cookies} from 'next/headers';
|
|
4
|
+
import {revalidatePath} from 'next/cache';
|
|
5
|
+
import {CURRENCY_COOKIE} from '@/lib/currency';
|
|
6
|
+
import {getActiveChannelCached} from '@/lib/vendure/cached';
|
|
7
|
+
|
|
8
|
+
export async function switchCurrency(currencyCode: string) {
|
|
9
|
+
const {getActiveChannelToken} = await import('@/lib/channel-server');
|
|
10
|
+
const channelToken = await getActiveChannelToken();
|
|
11
|
+
const channel = await getActiveChannelCached(channelToken);
|
|
12
|
+
if (!channel.availableCurrencyCodes.includes(currencyCode as never)) {
|
|
13
|
+
return {success: false, message: 'Invalid currency code'};
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const jar = await cookies();
|
|
17
|
+
jar.set(CURRENCY_COOKIE, currencyCode, {
|
|
18
|
+
path: '/',
|
|
19
|
+
maxAge: 60 * 60 * 24 * 365,
|
|
20
|
+
sameSite: 'lax',
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
revalidatePath('/', 'layout');
|
|
24
|
+
return {success: true};
|
|
25
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import {cookies} from 'next/headers';
|
|
2
|
+
|
|
3
|
+
const AUTH_TOKEN_COOKIE = process.env.VENDURE_AUTH_TOKEN_COOKIE || 'vendure-auth-token';
|
|
4
|
+
|
|
5
|
+
export async function setAuthToken(token: string) {
|
|
6
|
+
const cookieStore = await cookies();
|
|
7
|
+
cookieStore.set(AUTH_TOKEN_COOKIE, token);
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export async function getAuthToken(): Promise<string | undefined> {
|
|
11
|
+
const cookieStore = await cookies();
|
|
12
|
+
return cookieStore.get(AUTH_TOKEN_COOKIE)?.value;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export async function removeAuthToken() {
|
|
16
|
+
const cookieStore = await cookies();
|
|
17
|
+
cookieStore.delete(AUTH_TOKEN_COOKIE);
|
|
18
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import {cacheLife} from 'next/cache';
|
|
2
|
+
|
|
3
|
+
const HEALTH_CHECK_TIMEOUT_MS = 3000;
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Pings the Vendure Shop API to check whether the backend is reachable.
|
|
7
|
+
*
|
|
8
|
+
* Used to detect when a demo scenario has been paused via the demo control
|
|
9
|
+
* panel — in that case the storefront still resolves (it's hosted separately
|
|
10
|
+
* on Vercel) but every API call fails, leaving the user with a branded but
|
|
11
|
+
* empty shell. When this returns false we render a "Demo Paused" page instead.
|
|
12
|
+
*
|
|
13
|
+
* Cached for 15s (cacheLife('seconds')) so a stop/start of the backend is
|
|
14
|
+
* reflected quickly without hammering the API on every request.
|
|
15
|
+
*/
|
|
16
|
+
export async function isBackendReachable(): Promise<boolean> {
|
|
17
|
+
'use cache';
|
|
18
|
+
cacheLife('seconds');
|
|
19
|
+
|
|
20
|
+
const apiUrl = process.env.VENDURE_SHOP_API_URL || process.env.NEXT_PUBLIC_VENDURE_SHOP_API_URL;
|
|
21
|
+
if (!apiUrl) return true;
|
|
22
|
+
|
|
23
|
+
const controller = new AbortController();
|
|
24
|
+
const timeout = setTimeout(() => controller.abort(), HEALTH_CHECK_TIMEOUT_MS);
|
|
25
|
+
try {
|
|
26
|
+
const res = await fetch(apiUrl, {
|
|
27
|
+
method: 'POST',
|
|
28
|
+
headers: {'Content-Type': 'application/json'},
|
|
29
|
+
body: JSON.stringify({query: '{ __typename }'}),
|
|
30
|
+
signal: controller.signal,
|
|
31
|
+
});
|
|
32
|
+
return res.ok;
|
|
33
|
+
} catch {
|
|
34
|
+
return false;
|
|
35
|
+
} finally {
|
|
36
|
+
clearTimeout(timeout);
|
|
37
|
+
}
|
|
38
|
+
}
|