@doswiftly/cli 0.1.18 → 0.1.20
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 +23 -323
- package/dist/commands/check.js +1 -1
- package/dist/commands/check.js.map +1 -1
- package/dist/commands/deploy.d.ts +20 -0
- package/dist/commands/deploy.d.ts.map +1 -1
- package/dist/commands/deploy.js +249 -17
- package/dist/commands/deploy.js.map +1 -1
- package/dist/commands/doctor.js +3 -3
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/init.js +4 -4
- package/dist/commands/sdk.js +5 -5
- package/dist/commands/sdk.js.map +1 -1
- package/dist/commands/template.js +4 -4
- package/dist/commands/template.js.map +1 -1
- package/dist/commands/types.js +5 -5
- package/dist/commands/types.js.map +1 -1
- package/dist/commands/verify.js +2 -2
- package/dist/commands/verify.js.map +1 -1
- package/dist/lib/package-manager.d.ts +1 -1
- package/dist/lib/package-manager.js +1 -1
- package/package.json +4 -4
- package/templates/storefront-minimal/.github/workflows/build-template.yml +10 -0
- package/templates/storefront-minimal/wrangler.toml +11 -0
- package/templates/storefront-nextjs/.github/workflows/build-template.yml +10 -0
- package/templates/storefront-nextjs/README.md +16 -12
- package/templates/storefront-nextjs/app/account/orders/page.tsx +2 -2
- package/templates/storefront-nextjs/app/account/page.tsx +2 -2
- package/templates/storefront-nextjs/app/auth/login/page.tsx +1 -1
- package/templates/storefront-nextjs/app/auth/register/page.tsx +1 -1
- package/templates/storefront-nextjs/app/cart/page.tsx +1 -1
- package/templates/storefront-nextjs/app/categories/[slug]/page.tsx +2 -2
- package/templates/storefront-nextjs/app/categories/page.tsx +1 -1
- package/templates/storefront-nextjs/app/collections/[slug]/page.tsx +1 -1
- package/templates/storefront-nextjs/app/collections/page.tsx +1 -1
- package/templates/storefront-nextjs/app/page.tsx +1 -1
- package/templates/storefront-nextjs/app/products/[slug]/page.tsx +1 -1
- package/templates/storefront-nextjs/app/products/page.tsx +2 -2
- package/templates/storefront-nextjs/app/search/page.tsx +1 -1
- package/templates/storefront-nextjs/components/auth/auth-guard.tsx +1 -1
- package/templates/storefront-nextjs/components/commerce/add-to-cart-button.tsx +1 -1
- package/templates/storefront-nextjs/components/commerce/cart-icon.tsx +1 -1
- package/templates/storefront-nextjs/components/commerce/currency-selector.tsx +2 -2
- package/templates/storefront-nextjs/components/commerce/product-filters.tsx +1 -1
- package/templates/storefront-nextjs/components/commerce/product-price.tsx +1 -1
- package/templates/storefront-nextjs/components/commerce/search-input.tsx +1 -1
- package/templates/storefront-nextjs/components/commerce/sort-select.tsx +1 -1
- package/templates/storefront-nextjs/components/providers.tsx +1 -1
- package/templates/storefront-nextjs/lib/currency.tsx +3 -3
- package/templates/storefront-nextjs/lib/format.ts +1 -1
- package/templates/storefront-nextjs/lib/graphql-queries.ts +3 -3
- package/templates/storefront-nextjs/package.dev.json +1 -1
- package/templates/storefront-nextjs/package.json +1 -1
- package/templates/storefront-nextjs/package.json.template +1 -1
- package/templates/storefront-nextjs/wrangler.toml +11 -0
- package/templates/storefront-nextjs-shadcn/.github/workflows/build-template.yml +10 -0
- package/templates/storefront-nextjs-shadcn/.github/workflows/deploy.yml +47 -0
- package/templates/storefront-nextjs-shadcn/.github/workflows/preview.yml +47 -0
- package/templates/storefront-nextjs-shadcn/CLAUDE.md +172 -35
- package/templates/storefront-nextjs-shadcn/README.md +29 -162
- package/templates/storefront-nextjs-shadcn/app/{about → [locale]/about}/page.tsx +17 -14
- package/templates/storefront-nextjs-shadcn/app/[locale]/account/addresses/page.tsx +226 -0
- package/templates/storefront-nextjs-shadcn/app/[locale]/account/error.tsx +46 -0
- package/templates/storefront-nextjs-shadcn/app/[locale]/account/loading.tsx +19 -0
- package/templates/storefront-nextjs-shadcn/app/{account → [locale]/account}/loyalty/page.tsx +89 -193
- package/templates/storefront-nextjs-shadcn/app/[locale]/account/orders/[id]/loading.tsx +60 -0
- package/templates/storefront-nextjs-shadcn/app/[locale]/account/orders/[id]/page.tsx +119 -0
- package/templates/storefront-nextjs-shadcn/app/{account → [locale]/account}/orders/[id]/tracking/page.tsx +27 -25
- package/templates/storefront-nextjs-shadcn/app/[locale]/account/orders/page.tsx +101 -0
- package/templates/storefront-nextjs-shadcn/app/{account → [locale]/account}/page.tsx +9 -7
- package/templates/storefront-nextjs-shadcn/app/[locale]/account/settings/page.tsx +208 -0
- package/templates/storefront-nextjs-shadcn/app/{auth → [locale]/auth}/forgot-password/page.tsx +24 -17
- package/templates/storefront-nextjs-shadcn/app/{auth → [locale]/auth}/login/page.tsx +5 -2
- package/templates/storefront-nextjs-shadcn/app/{auth → [locale]/auth}/register/page.tsx +5 -2
- package/templates/storefront-nextjs-shadcn/app/[locale]/blog/[slug]/loading.tsx +17 -0
- package/templates/storefront-nextjs-shadcn/app/{blog → [locale]/blog}/[slug]/page.tsx +44 -3
- package/templates/storefront-nextjs-shadcn/app/[locale]/blog/loading.tsx +19 -0
- package/templates/storefront-nextjs-shadcn/app/{brands → [locale]/brands}/page.tsx +2 -1
- package/templates/storefront-nextjs-shadcn/app/[locale]/cart/loading.tsx +26 -0
- package/templates/storefront-nextjs-shadcn/app/{cart → [locale]/cart}/page.tsx +20 -13
- package/templates/storefront-nextjs-shadcn/app/[locale]/categories/[slug]/category-products-client.tsx +58 -0
- package/templates/storefront-nextjs-shadcn/app/[locale]/categories/[slug]/loading.tsx +32 -0
- package/templates/storefront-nextjs-shadcn/app/[locale]/categories/[slug]/page.tsx +95 -0
- package/templates/storefront-nextjs-shadcn/app/{categories → [locale]/categories}/page.tsx +21 -12
- package/templates/storefront-nextjs-shadcn/app/[locale]/checkout/error.tsx +43 -0
- package/templates/storefront-nextjs-shadcn/app/[locale]/checkout/loading.tsx +31 -0
- package/templates/storefront-nextjs-shadcn/app/{checkout → [locale]/checkout}/page.tsx +334 -253
- package/templates/storefront-nextjs-shadcn/app/{checkout → [locale]/checkout}/success/[orderId]/page.tsx +36 -34
- package/templates/storefront-nextjs-shadcn/app/[locale]/collections/[handle]/loading.tsx +19 -0
- package/templates/storefront-nextjs-shadcn/app/{collections → [locale]/collections}/[handle]/page.tsx +6 -4
- package/templates/storefront-nextjs-shadcn/app/[locale]/collections/loading.tsx +18 -0
- package/templates/storefront-nextjs-shadcn/app/{collections → [locale]/collections}/page.tsx +20 -12
- package/templates/storefront-nextjs-shadcn/app/{contact → [locale]/contact}/page.tsx +24 -21
- package/templates/storefront-nextjs-shadcn/app/{error.tsx → [locale]/error.tsx} +13 -8
- package/templates/storefront-nextjs-shadcn/app/[locale]/layout.tsx +92 -0
- package/templates/storefront-nextjs-shadcn/app/{not-found.tsx → [locale]/not-found.tsx} +13 -18
- package/templates/storefront-nextjs-shadcn/app/{page.tsx → [locale]/page.tsx} +8 -4
- package/templates/storefront-nextjs-shadcn/app/[locale]/products/[slug]/error.tsx +43 -0
- package/templates/storefront-nextjs-shadcn/app/[locale]/products/[slug]/loading.tsx +29 -0
- package/templates/storefront-nextjs-shadcn/app/{products → [locale]/products}/[slug]/page.tsx +17 -14
- package/templates/storefront-nextjs-shadcn/app/{products → [locale]/products}/[slug]/product-client.tsx +18 -62
- package/templates/storefront-nextjs-shadcn/app/[locale]/products/loading.tsx +32 -0
- package/templates/storefront-nextjs-shadcn/app/{products → [locale]/products}/page.tsx +6 -3
- package/templates/storefront-nextjs-shadcn/app/[locale]/products/products-client.tsx +450 -0
- package/templates/storefront-nextjs-shadcn/app/[locale]/search/loading.tsx +18 -0
- package/templates/storefront-nextjs-shadcn/app/{wishlist → [locale]/wishlist}/page.tsx +27 -28
- package/templates/storefront-nextjs-shadcn/app/api/auth/clear-token/route.ts +2 -86
- package/templates/storefront-nextjs-shadcn/app/api/auth/set-token/route.ts +2 -124
- package/templates/storefront-nextjs-shadcn/app/global-error.tsx +117 -0
- package/templates/storefront-nextjs-shadcn/app/globals.css +8 -0
- package/templates/storefront-nextjs-shadcn/app/layout.tsx +8 -35
- package/templates/storefront-nextjs-shadcn/codegen.ts +48 -31
- package/templates/storefront-nextjs-shadcn/components/account/address-form.tsx +25 -20
- package/templates/storefront-nextjs-shadcn/components/account/address-list.tsx +11 -10
- package/templates/storefront-nextjs-shadcn/components/account/customer-info.fragment.graphql +36 -0
- package/templates/storefront-nextjs-shadcn/components/account/order-details.tsx +17 -13
- package/templates/storefront-nextjs-shadcn/components/account/order-history.tsx +42 -30
- package/templates/storefront-nextjs-shadcn/components/account/order-summary.fragment.graphql +36 -0
- package/templates/storefront-nextjs-shadcn/components/auth/account-menu.tsx +18 -16
- package/templates/storefront-nextjs-shadcn/components/auth/login-form.tsx +37 -58
- package/templates/storefront-nextjs-shadcn/components/auth/register-form.tsx +85 -66
- package/templates/storefront-nextjs-shadcn/components/blog/blog-card.tsx +1 -1
- package/templates/storefront-nextjs-shadcn/components/blog/blog-sidebar.tsx +1 -1
- package/templates/storefront-nextjs-shadcn/components/brand/brand-card.tsx +1 -1
- package/templates/storefront-nextjs-shadcn/components/cart/cart-drawer.tsx +10 -6
- package/templates/storefront-nextjs-shadcn/components/cart/cart-icon.tsx +9 -6
- package/templates/storefront-nextjs-shadcn/components/cart/cart-item.tsx +8 -6
- package/templates/storefront-nextjs-shadcn/components/cart/cart-line.fragment.graphql +53 -0
- package/templates/storefront-nextjs-shadcn/components/cart/cart-summary.tsx +10 -8
- package/templates/storefront-nextjs-shadcn/components/cart/promo-code-input.tsx +8 -5
- package/templates/storefront-nextjs-shadcn/components/cart/shipping-estimator.tsx +38 -20
- package/templates/storefront-nextjs-shadcn/components/checkout/payment-method-card.tsx +15 -25
- package/templates/storefront-nextjs-shadcn/components/checkout/payment-step.tsx +10 -8
- package/templates/storefront-nextjs-shadcn/components/checkout/tax-breakdown.tsx +9 -6
- package/templates/storefront-nextjs-shadcn/components/commerce/currency-selector.tsx +7 -5
- package/templates/storefront-nextjs-shadcn/components/commerce/pagination.tsx +8 -5
- package/templates/storefront-nextjs-shadcn/components/commerce/product-actions.tsx +6 -4
- package/templates/storefront-nextjs-shadcn/components/commerce/search-input.tsx +10 -9
- package/templates/storefront-nextjs-shadcn/components/common/category-card.tsx +1 -1
- package/templates/storefront-nextjs-shadcn/components/common/collection-card.tsx +1 -1
- package/templates/storefront-nextjs-shadcn/components/common/price-display.tsx +35 -11
- package/templates/storefront-nextjs-shadcn/components/common/social-share.tsx +9 -6
- package/templates/storefront-nextjs-shadcn/components/discount/discount-breakdown.tsx +22 -12
- package/templates/storefront-nextjs-shadcn/components/discount/discount-code-input.tsx +18 -15
- package/templates/storefront-nextjs-shadcn/components/error/error-boundary.tsx +53 -28
- package/templates/storefront-nextjs-shadcn/components/filters/dynamic-attribute-filters.tsx +7 -5
- package/templates/storefront-nextjs-shadcn/components/filters/range-slider-filter.tsx +5 -5
- package/templates/storefront-nextjs-shadcn/components/gift-card/gift-card-balance.tsx +19 -15
- package/templates/storefront-nextjs-shadcn/components/gift-card/gift-card-input.tsx +13 -10
- package/templates/storefront-nextjs-shadcn/components/home/category-grid.tsx +10 -6
- package/templates/storefront-nextjs-shadcn/components/home/collection-card.fragment.graphql +21 -0
- package/templates/storefront-nextjs-shadcn/components/home/featured-collections.tsx +3 -13
- package/templates/storefront-nextjs-shadcn/components/home/featured-products.tsx +12 -8
- package/templates/storefront-nextjs-shadcn/components/home/hero-section.tsx +13 -8
- package/templates/storefront-nextjs-shadcn/components/home/index.ts +0 -1
- package/templates/storefront-nextjs-shadcn/components/home/newsletter-signup.tsx +10 -8
- package/templates/storefront-nextjs-shadcn/components/hydrated.tsx +24 -0
- package/templates/storefront-nextjs-shadcn/components/layout/breadcrumbs.tsx +41 -16
- package/templates/storefront-nextjs-shadcn/components/layout/category-node.fragment.graphql +22 -0
- package/templates/storefront-nextjs-shadcn/components/layout/currency-selector.tsx +7 -4
- package/templates/storefront-nextjs-shadcn/components/layout/footer.tsx +24 -23
- package/templates/storefront-nextjs-shadcn/components/layout/header.tsx +52 -34
- package/templates/storefront-nextjs-shadcn/components/layout/language-switcher.tsx +54 -0
- package/templates/storefront-nextjs-shadcn/components/layout/mobile-menu.tsx +33 -30
- package/templates/storefront-nextjs-shadcn/components/layout/navigation.tsx +27 -24
- package/templates/storefront-nextjs-shadcn/components/loyalty/points-balance.tsx +2 -11
- package/templates/storefront-nextjs-shadcn/components/loyalty/points-history.tsx +8 -25
- package/templates/storefront-nextjs-shadcn/components/loyalty/referral-section.tsx +32 -42
- package/templates/storefront-nextjs-shadcn/components/loyalty/rewards-catalog.tsx +17 -41
- package/templates/storefront-nextjs-shadcn/components/loyalty/tier-progress.tsx +2 -29
- package/templates/storefront-nextjs-shadcn/components/order/index.ts +6 -1
- package/templates/storefront-nextjs-shadcn/components/product/add-to-cart-button.tsx +6 -14
- package/templates/storefront-nextjs-shadcn/components/product/b2b-price-display.tsx +4 -2
- package/templates/storefront-nextjs-shadcn/components/product/filter-active-pills.tsx +72 -0
- package/templates/storefront-nextjs-shadcn/components/product/filter-mobile-sheet.tsx +87 -0
- package/templates/storefront-nextjs-shadcn/components/product/filter-price-range.tsx +140 -0
- package/templates/storefront-nextjs-shadcn/components/product/index.ts +9 -2
- package/templates/storefront-nextjs-shadcn/components/product/product-card.fragment.graphql +49 -0
- package/templates/storefront-nextjs-shadcn/components/product/product-card.tsx +11 -37
- package/templates/storefront-nextjs-shadcn/components/product/product-detail.fragment.graphql +52 -0
- package/templates/storefront-nextjs-shadcn/components/product/product-filters.tsx +179 -124
- package/templates/storefront-nextjs-shadcn/components/product/product-grid.tsx +3 -5
- package/templates/storefront-nextjs-shadcn/components/product/product-image.tsx +3 -7
- package/templates/storefront-nextjs-shadcn/components/product/product-price.tsx +2 -2
- package/templates/storefront-nextjs-shadcn/components/product/product-reviews.tsx +5 -4
- package/templates/storefront-nextjs-shadcn/components/product/product-sort.tsx +44 -19
- package/templates/storefront-nextjs-shadcn/components/product/product-variant-selector.tsx +8 -23
- package/templates/storefront-nextjs-shadcn/components/product/product-variant.fragment.graphql +51 -0
- package/templates/storefront-nextjs-shadcn/components/product/review-card.tsx +1 -1
- package/templates/storefront-nextjs-shadcn/components/product/review-form.tsx +26 -34
- package/templates/storefront-nextjs-shadcn/components/product/savings-display.tsx +17 -2
- package/templates/storefront-nextjs-shadcn/components/product/similar-products.tsx +3 -2
- package/templates/storefront-nextjs-shadcn/components/providers/index.ts +1 -1
- package/templates/storefront-nextjs-shadcn/components/providers/language-sync-provider.tsx +27 -0
- package/templates/storefront-nextjs-shadcn/components/providers/stores-provider.tsx +63 -0
- package/templates/storefront-nextjs-shadcn/components/providers/theme-provider.tsx +1 -1
- package/templates/storefront-nextjs-shadcn/components/returns/index.ts +2 -2
- package/templates/storefront-nextjs-shadcn/components/returns/return-request-form.tsx +59 -72
- package/templates/storefront-nextjs-shadcn/components/search/search-bar.tsx +7 -4
- package/templates/storefront-nextjs-shadcn/components/search/search-results.tsx +3 -2
- package/templates/storefront-nextjs-shadcn/components/shipping/shipping-method-selector.tsx +12 -9
- package/templates/storefront-nextjs-shadcn/components/ui/empty-state.tsx +23 -12
- package/templates/storefront-nextjs-shadcn/components/ui/form.tsx +174 -0
- package/templates/storefront-nextjs-shadcn/components/ui/index.ts +30 -2
- package/templates/storefront-nextjs-shadcn/components/ui/progress.tsx +40 -0
- package/templates/storefront-nextjs-shadcn/components/ui/sheet.tsx +107 -0
- package/templates/storefront-nextjs-shadcn/components/ui/slider.tsx +33 -0
- package/templates/storefront-nextjs-shadcn/components/ui/textarea.tsx +24 -0
- package/templates/storefront-nextjs-shadcn/components/wishlist/wishlist-button.tsx +7 -4
- package/templates/storefront-nextjs-shadcn/components/wishlist/wishlist-icon.tsx +4 -2
- package/templates/storefront-nextjs-shadcn/components/wishlist/wishlist-item.tsx +2 -10
- package/templates/storefront-nextjs-shadcn/generated/graphql.ts +13387 -0
- package/templates/storefront-nextjs-shadcn/graphql/custom.example.graphql +159 -0
- package/templates/storefront-nextjs-shadcn/hooks/index.ts +3 -0
- package/templates/storefront-nextjs-shadcn/hooks/use-auth-sync.ts +42 -0
- package/templates/storefront-nextjs-shadcn/hooks/use-auth.ts +17 -295
- package/templates/storefront-nextjs-shadcn/hooks/use-cart-actions.ts +34 -229
- package/templates/storefront-nextjs-shadcn/hooks/use-cart-di.ts +67 -0
- package/templates/storefront-nextjs-shadcn/hooks/use-cart-sync.ts +16 -12
- package/templates/storefront-nextjs-shadcn/i18n/navigation.ts +12 -0
- package/templates/storefront-nextjs-shadcn/i18n/request.ts +17 -0
- package/templates/storefront-nextjs-shadcn/i18n/routing.ts +17 -0
- package/templates/storefront-nextjs-shadcn/lib/auth/routes.ts +4 -17
- package/templates/storefront-nextjs-shadcn/lib/graphql/client.ts +22 -99
- package/templates/storefront-nextjs-shadcn/lib/graphql/config.ts +33 -0
- package/templates/storefront-nextjs-shadcn/lib/graphql/fragments.ts +34 -0
- package/templates/storefront-nextjs-shadcn/lib/graphql/hooks.ts +720 -632
- package/templates/storefront-nextjs-shadcn/lib/graphql/query-keys.ts +88 -0
- package/templates/storefront-nextjs-shadcn/lib/graphql/server.ts +132 -182
- package/templates/storefront-nextjs-shadcn/lib/graphql/types.ts +62 -0
- package/templates/storefront-nextjs-shadcn/lib/theme/theme-config.ts +0 -17
- package/templates/storefront-nextjs-shadcn/messages/en.json +869 -0
- package/templates/storefront-nextjs-shadcn/messages/pl.json +869 -0
- package/templates/storefront-nextjs-shadcn/next-env.d.ts +6 -0
- package/templates/storefront-nextjs-shadcn/next.config.ts +6 -5
- package/templates/storefront-nextjs-shadcn/package.dev.json +1 -3
- package/templates/storefront-nextjs-shadcn/package.json +14 -14
- package/templates/storefront-nextjs-shadcn/package.json.template +6 -7
- package/templates/storefront-nextjs-shadcn/proxy.ts +115 -47
- package/templates/storefront-nextjs-shadcn/stores/cart-store.ts +24 -56
- package/templates/storefront-nextjs-shadcn/stores/checkout-store.ts +64 -75
- package/templates/storefront-nextjs-shadcn/stores/wishlist-store.ts +178 -177
- package/templates/storefront-nextjs-shadcn/tsconfig.json +23 -5
- package/templates/storefront-nextjs-shadcn/wrangler.toml +11 -0
- package/templates/storefront-nextjs-shadcn/CART_INTEGRATION.md +0 -282
- package/templates/storefront-nextjs-shadcn/GRAPHQL_DOCUMENT_NAMES.md +0 -190
- package/templates/storefront-nextjs-shadcn/GRAPHQL_ERROR_HANDLING.md +0 -263
- package/templates/storefront-nextjs-shadcn/GRAPHQL_FIXES_SUMMARY.md +0 -135
- package/templates/storefront-nextjs-shadcn/GRAPHQL_INTEGRATION_COMPLETE.md +0 -142
- package/templates/storefront-nextjs-shadcn/INTEGRATION_CHECKLIST.md +0 -448
- package/templates/storefront-nextjs-shadcn/PRODUCT_DETAIL_PAGE_IMPLEMENTATION.md +0 -307
- package/templates/storefront-nextjs-shadcn/THEME_CUSTOMIZATION.md +0 -245
- package/templates/storefront-nextjs-shadcn/app/account/addresses/page.tsx +0 -215
- package/templates/storefront-nextjs-shadcn/app/account/orders/[id]/page.tsx +0 -128
- package/templates/storefront-nextjs-shadcn/app/account/orders/page.tsx +0 -80
- package/templates/storefront-nextjs-shadcn/app/account/settings/page.tsx +0 -171
- package/templates/storefront-nextjs-shadcn/app/categories/[slug]/page.tsx +0 -78
- package/templates/storefront-nextjs-shadcn/app/products/products-client.tsx +0 -192
- package/templates/storefront-nextjs-shadcn/components/providers/currency-provider.tsx +0 -103
- package/templates/storefront-nextjs-shadcn/graphql/collections.example.ts +0 -168
- package/templates/storefront-nextjs-shadcn/graphql/products.example.ts +0 -160
- package/templates/storefront-nextjs-shadcn/lib/auth/cookies.ts +0 -220
- package/templates/storefront-nextjs-shadcn/lib/config.ts +0 -46
- package/templates/storefront-nextjs-shadcn/lib/currency/IMPLEMENTATION_SUMMARY.md +0 -254
- package/templates/storefront-nextjs-shadcn/lib/currency/README.md +0 -464
- package/templates/storefront-nextjs-shadcn/lib/currency/cookie-manager.test.ts +0 -328
- package/templates/storefront-nextjs-shadcn/lib/currency/cookie-manager.ts +0 -295
- package/templates/storefront-nextjs-shadcn/lib/currency/index.ts +0 -27
- package/templates/storefront-nextjs-shadcn/lib/format.ts +0 -226
- package/templates/storefront-nextjs-shadcn/lib/hooks.ts +0 -30
- package/templates/storefront-nextjs-shadcn/stores/auth-store.ts +0 -66
- package/templates/storefront-nextjs-shadcn/stores/currency-store.ts +0 -103
- /package/templates/storefront-nextjs-shadcn/app/{blog → [locale]/blog}/page.tsx +0 -0
- /package/templates/storefront-nextjs-shadcn/app/{brands → [locale]/brands}/[slug]/page.tsx +0 -0
- /package/templates/storefront-nextjs-shadcn/app/{returns → [locale]/returns}/page.tsx +0 -0
- /package/templates/storefront-nextjs-shadcn/app/{search → [locale]/search}/page.tsx +0 -0
- /package/templates/storefront-nextjs-shadcn/app/{search → [locale]/search}/search-client.tsx +0 -0
- /package/templates/storefront-nextjs-shadcn/app/{shipping → [locale]/shipping}/page.tsx +0 -0
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
3
|
import { useState, useEffect } from "react";
|
|
4
|
-
import { useRouter } from "
|
|
5
|
-
import
|
|
4
|
+
import { useRouter, Link } from "@/i18n/navigation";
|
|
5
|
+
import { StorefrontError } from "@doswiftly/storefront-sdk";
|
|
6
6
|
import { useCartStore } from "@/stores/cart-store";
|
|
7
7
|
import { useCartSync } from "@/hooks/use-cart-sync";
|
|
8
8
|
import {
|
|
@@ -15,10 +15,13 @@ import {
|
|
|
15
15
|
useCheckoutDiscountCodeRemove,
|
|
16
16
|
useCheckoutGiftCardApply,
|
|
17
17
|
useCheckoutGiftCardRemove,
|
|
18
|
+
useCheckoutGiftCardRecipientUpdate,
|
|
18
19
|
useCheckoutComplete,
|
|
19
20
|
useCheckout,
|
|
20
21
|
} from "@/lib/graphql/hooks";
|
|
21
|
-
import {
|
|
22
|
+
import { useQueryClient } from "@tanstack/react-query";
|
|
23
|
+
import { queryKeys } from "@/lib/graphql/query-keys";
|
|
24
|
+
import { formatAmount } from "@doswiftly/storefront-sdk";
|
|
22
25
|
import { Button } from "@/components/ui/button";
|
|
23
26
|
import { Input } from "@/components/ui/input";
|
|
24
27
|
import { Label } from "@/components/ui/label";
|
|
@@ -37,6 +40,7 @@ import { PaymentStep } from "@/components/checkout/payment-step";
|
|
|
37
40
|
import type { PaymentMethod } from "@/components/checkout/payment-method-card";
|
|
38
41
|
import { toast } from "sonner";
|
|
39
42
|
import { z } from "zod";
|
|
43
|
+
import { useTranslations } from "next-intl";
|
|
40
44
|
import {
|
|
41
45
|
Loader2,
|
|
42
46
|
ChevronLeft,
|
|
@@ -57,45 +61,7 @@ import {
|
|
|
57
61
|
// CONSTANTS
|
|
58
62
|
// ============================================================================
|
|
59
63
|
|
|
60
|
-
const
|
|
61
|
-
{ code: "PL", name: "Polska" },
|
|
62
|
-
{ code: "DE", name: "Niemcy" },
|
|
63
|
-
{ code: "CZ", name: "Czechy" },
|
|
64
|
-
{ code: "SK", name: "Słowacja" },
|
|
65
|
-
{ code: "AT", name: "Austria" },
|
|
66
|
-
{ code: "FR", name: "Francja" },
|
|
67
|
-
{ code: "NL", name: "Holandia" },
|
|
68
|
-
{ code: "BE", name: "Belgia" },
|
|
69
|
-
{ code: "IT", name: "Włochy" },
|
|
70
|
-
{ code: "ES", name: "Hiszpania" },
|
|
71
|
-
{ code: "GB", name: "Wielka Brytania" },
|
|
72
|
-
{ code: "US", name: "Stany Zjednoczone" },
|
|
73
|
-
];
|
|
74
|
-
|
|
75
|
-
// ============================================================================
|
|
76
|
-
// VALIDATION SCHEMAS
|
|
77
|
-
// ============================================================================
|
|
78
|
-
|
|
79
|
-
const contactSchema = z.object({
|
|
80
|
-
email: z.string().email("Podaj prawidłowy adres email"),
|
|
81
|
-
phone: z
|
|
82
|
-
.string()
|
|
83
|
-
.min(9, "Numer telefonu jest wymagany")
|
|
84
|
-
.regex(/^\+?[0-9\s\-]{9,15}$/, "Nieprawidłowy format numeru telefonu"),
|
|
85
|
-
});
|
|
86
|
-
|
|
87
|
-
const addressSchema = z.object({
|
|
88
|
-
firstName: z.string().min(1, "Imię jest wymagane"),
|
|
89
|
-
lastName: z.string().min(1, "Nazwisko jest wymagane"),
|
|
90
|
-
address1: z.string().min(1, "Adres jest wymagany"),
|
|
91
|
-
address2: z.string().optional(),
|
|
92
|
-
city: z.string().min(1, "Miasto jest wymagane"),
|
|
93
|
-
province: z.string().optional(),
|
|
94
|
-
zip: z.string().min(1, "Kod pocztowy jest wymagany"),
|
|
95
|
-
country: z.string().min(2, "Kraj jest wymagany"),
|
|
96
|
-
phone: z.string().optional(),
|
|
97
|
-
company: z.string().optional(),
|
|
98
|
-
});
|
|
64
|
+
const COUNTRY_CODES = ["PL", "DE", "CZ", "SK", "AT", "FR", "NL", "BE", "IT", "ES", "GB", "US"] as const;
|
|
99
65
|
|
|
100
66
|
// ============================================================================
|
|
101
67
|
// TYPES
|
|
@@ -165,11 +131,20 @@ interface AddressFormFieldsProps {
|
|
|
165
131
|
}
|
|
166
132
|
|
|
167
133
|
function AddressFormFields({ prefix, values, onChange, errors }: AddressFormFieldsProps) {
|
|
134
|
+
const t = useTranslations("checkout");
|
|
135
|
+
const tc = useTranslations("common");
|
|
136
|
+
const tCountries = useTranslations("countries");
|
|
137
|
+
|
|
138
|
+
const countries = COUNTRY_CODES.map((code) => ({
|
|
139
|
+
code,
|
|
140
|
+
name: tCountries(code),
|
|
141
|
+
}));
|
|
142
|
+
|
|
168
143
|
return (
|
|
169
144
|
<div className="space-y-4">
|
|
170
145
|
<div className="grid gap-4 sm:grid-cols-2">
|
|
171
146
|
<div>
|
|
172
|
-
<Label htmlFor={`${prefix}_firstName`}>
|
|
147
|
+
<Label htmlFor={`${prefix}_firstName`}>{t("address.firstName")} *</Label>
|
|
173
148
|
<Input
|
|
174
149
|
id={`${prefix}_firstName`}
|
|
175
150
|
value={values.firstName}
|
|
@@ -181,7 +156,7 @@ function AddressFormFields({ prefix, values, onChange, errors }: AddressFormFiel
|
|
|
181
156
|
)}
|
|
182
157
|
</div>
|
|
183
158
|
<div>
|
|
184
|
-
<Label htmlFor={`${prefix}_lastName`}>
|
|
159
|
+
<Label htmlFor={`${prefix}_lastName`}>{t("address.lastName")} *</Label>
|
|
185
160
|
<Input
|
|
186
161
|
id={`${prefix}_lastName`}
|
|
187
162
|
value={values.lastName}
|
|
@@ -195,7 +170,7 @@ function AddressFormFields({ prefix, values, onChange, errors }: AddressFormFiel
|
|
|
195
170
|
</div>
|
|
196
171
|
|
|
197
172
|
<div>
|
|
198
|
-
<Label htmlFor={`${prefix}_company`}>
|
|
173
|
+
<Label htmlFor={`${prefix}_company`}>{t("address.company")} ({tc("optional")})</Label>
|
|
199
174
|
<Input
|
|
200
175
|
id={`${prefix}_company`}
|
|
201
176
|
value={values.company}
|
|
@@ -204,10 +179,10 @@ function AddressFormFields({ prefix, values, onChange, errors }: AddressFormFiel
|
|
|
204
179
|
</div>
|
|
205
180
|
|
|
206
181
|
<div>
|
|
207
|
-
<Label htmlFor={`${prefix}_address1`}>
|
|
182
|
+
<Label htmlFor={`${prefix}_address1`}>{t("address.address1")} *</Label>
|
|
208
183
|
<Input
|
|
209
184
|
id={`${prefix}_address1`}
|
|
210
|
-
placeholder="
|
|
185
|
+
placeholder={t("address.address1Placeholder")}
|
|
211
186
|
value={values.address1}
|
|
212
187
|
onChange={(e) => onChange("address1", e.target.value)}
|
|
213
188
|
className={errors[`${prefix}_address1`] ? "border-destructive" : ""}
|
|
@@ -218,7 +193,7 @@ function AddressFormFields({ prefix, values, onChange, errors }: AddressFormFiel
|
|
|
218
193
|
</div>
|
|
219
194
|
|
|
220
195
|
<div>
|
|
221
|
-
<Label htmlFor={`${prefix}_address2`}>
|
|
196
|
+
<Label htmlFor={`${prefix}_address2`}>{t("address.address2")} ({tc("optional")})</Label>
|
|
222
197
|
<Input
|
|
223
198
|
id={`${prefix}_address2`}
|
|
224
199
|
value={values.address2}
|
|
@@ -228,10 +203,10 @@ function AddressFormFields({ prefix, values, onChange, errors }: AddressFormFiel
|
|
|
228
203
|
|
|
229
204
|
<div className="grid gap-4 sm:grid-cols-2">
|
|
230
205
|
<div>
|
|
231
|
-
<Label htmlFor={`${prefix}_zip`}>
|
|
206
|
+
<Label htmlFor={`${prefix}_zip`}>{t("address.zip")} *</Label>
|
|
232
207
|
<Input
|
|
233
208
|
id={`${prefix}_zip`}
|
|
234
|
-
placeholder="
|
|
209
|
+
placeholder={t("address.zipPlaceholder")}
|
|
235
210
|
value={values.zip}
|
|
236
211
|
onChange={(e) => onChange("zip", e.target.value)}
|
|
237
212
|
className={errors[`${prefix}_zip`] ? "border-destructive" : ""}
|
|
@@ -241,7 +216,7 @@ function AddressFormFields({ prefix, values, onChange, errors }: AddressFormFiel
|
|
|
241
216
|
)}
|
|
242
217
|
</div>
|
|
243
218
|
<div>
|
|
244
|
-
<Label htmlFor={`${prefix}_city`}>
|
|
219
|
+
<Label htmlFor={`${prefix}_city`}>{t("address.city")} *</Label>
|
|
245
220
|
<Input
|
|
246
221
|
id={`${prefix}_city`}
|
|
247
222
|
value={values.city}
|
|
@@ -256,13 +231,13 @@ function AddressFormFields({ prefix, values, onChange, errors }: AddressFormFiel
|
|
|
256
231
|
|
|
257
232
|
<div className="grid gap-4 sm:grid-cols-2">
|
|
258
233
|
<div>
|
|
259
|
-
<Label htmlFor={`${prefix}_country`}>
|
|
234
|
+
<Label htmlFor={`${prefix}_country`}>{t("address.country")} *</Label>
|
|
260
235
|
<Select value={values.country} onValueChange={(value) => onChange("country", value)}>
|
|
261
236
|
<SelectTrigger className={errors[`${prefix}_country`] ? "border-destructive" : ""}>
|
|
262
|
-
<SelectValue placeholder="
|
|
237
|
+
<SelectValue placeholder={t("address.countryPlaceholder")} />
|
|
263
238
|
</SelectTrigger>
|
|
264
239
|
<SelectContent>
|
|
265
|
-
{
|
|
240
|
+
{countries.map((country) => (
|
|
266
241
|
<SelectItem key={country.code} value={country.code}>
|
|
267
242
|
{country.name}
|
|
268
243
|
</SelectItem>
|
|
@@ -274,7 +249,7 @@ function AddressFormFields({ prefix, values, onChange, errors }: AddressFormFiel
|
|
|
274
249
|
)}
|
|
275
250
|
</div>
|
|
276
251
|
<div>
|
|
277
|
-
<Label htmlFor={`${prefix}_province`}>
|
|
252
|
+
<Label htmlFor={`${prefix}_province`}>{t("address.province")} ({tc("optional")})</Label>
|
|
278
253
|
<Input
|
|
279
254
|
id={`${prefix}_province`}
|
|
280
255
|
value={values.province}
|
|
@@ -295,6 +270,42 @@ export default function CheckoutPage() {
|
|
|
295
270
|
const { cartId, clearCart } = useCartStore();
|
|
296
271
|
const { items, isLoading: isCartLoading } = useCartSync();
|
|
297
272
|
|
|
273
|
+
const t = useTranslations("checkout");
|
|
274
|
+
const tc = useTranslations("common");
|
|
275
|
+
const tCountries = useTranslations("countries");
|
|
276
|
+
const tCart = useTranslations("cart");
|
|
277
|
+
|
|
278
|
+
// ============================================================================
|
|
279
|
+
// VALIDATION SCHEMAS (inside component to use translations)
|
|
280
|
+
// ============================================================================
|
|
281
|
+
|
|
282
|
+
const contactSchema = z.object({
|
|
283
|
+
email: z.string().email(t("validation.emailRequired")),
|
|
284
|
+
phone: z
|
|
285
|
+
.string()
|
|
286
|
+
.min(9, t("validation.phoneRequired"))
|
|
287
|
+
.regex(/^\+?[0-9\s\-]{9,15}$/, t("validation.phoneInvalid")),
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
const addressSchema = z.object({
|
|
291
|
+
firstName: z.string().min(1, t("validation.firstNameRequired")),
|
|
292
|
+
lastName: z.string().min(1, t("validation.lastNameRequired")),
|
|
293
|
+
address1: z.string().min(1, t("validation.addressRequired")),
|
|
294
|
+
address2: z.string().optional(),
|
|
295
|
+
city: z.string().min(1, t("validation.cityRequired")),
|
|
296
|
+
province: z.string().optional(),
|
|
297
|
+
zip: z.string().min(1, t("validation.zipRequired")),
|
|
298
|
+
country: z.string().min(2, t("validation.countryRequired")),
|
|
299
|
+
phone: z.string().optional(),
|
|
300
|
+
company: z.string().optional(),
|
|
301
|
+
});
|
|
302
|
+
|
|
303
|
+
// Countries for review section
|
|
304
|
+
const countries = COUNTRY_CODES.map((code) => ({
|
|
305
|
+
code,
|
|
306
|
+
name: tCountries(code),
|
|
307
|
+
}));
|
|
308
|
+
|
|
298
309
|
// Step management
|
|
299
310
|
const [currentStep, setCurrentStep] = useState<CheckoutStep>("contact");
|
|
300
311
|
const [checkoutId, setCheckoutId] = useState<string | null>(null);
|
|
@@ -337,7 +348,9 @@ export default function CheckoutPage() {
|
|
|
337
348
|
const removeDiscount = useCheckoutDiscountCodeRemove();
|
|
338
349
|
const applyGiftCard = useCheckoutGiftCardApply();
|
|
339
350
|
const removeGiftCard = useCheckoutGiftCardRemove();
|
|
351
|
+
const updateGiftCardRecipient = useCheckoutGiftCardRecipientUpdate();
|
|
340
352
|
const completeCheckout = useCheckoutComplete();
|
|
353
|
+
const queryClient = useQueryClient();
|
|
341
354
|
|
|
342
355
|
// Query checkout for shipping rates
|
|
343
356
|
const { data: checkoutData, refetch: refetchCheckout } = useCheckout(checkoutId);
|
|
@@ -345,8 +358,8 @@ export default function CheckoutPage() {
|
|
|
345
358
|
|
|
346
359
|
// Sync discount codes from checkout (transferred from cart) to form state
|
|
347
360
|
useEffect(() => {
|
|
348
|
-
if (checkout?.discountCodes?.length > 0 && !formState.appliedDiscountCode) {
|
|
349
|
-
const applicableCode = checkout
|
|
361
|
+
if ((checkout?.discountCodes?.length ?? 0) > 0 && !formState.appliedDiscountCode) {
|
|
362
|
+
const applicableCode = checkout?.discountCodes?.find((dc) => dc.applicable);
|
|
350
363
|
if (applicableCode) {
|
|
351
364
|
setFormState((prev) => ({
|
|
352
365
|
...prev,
|
|
@@ -367,6 +380,7 @@ export default function CheckoutPage() {
|
|
|
367
380
|
removeDiscount.isPending ||
|
|
368
381
|
applyGiftCard.isPending ||
|
|
369
382
|
removeGiftCard.isPending ||
|
|
383
|
+
updateGiftCardRecipient.isPending ||
|
|
370
384
|
completeCheckout.isPending;
|
|
371
385
|
|
|
372
386
|
// Calculate totals from checkout or cart
|
|
@@ -398,6 +412,8 @@ export default function CheckoutPage() {
|
|
|
398
412
|
setErrors({});
|
|
399
413
|
|
|
400
414
|
try {
|
|
415
|
+
let newCheckoutId = checkoutId;
|
|
416
|
+
|
|
401
417
|
// Create checkout if not exists
|
|
402
418
|
if (!checkoutId) {
|
|
403
419
|
const createResult = await createCheckout.mutateAsync({
|
|
@@ -413,7 +429,8 @@ export default function CheckoutPage() {
|
|
|
413
429
|
}
|
|
414
430
|
|
|
415
431
|
if (createResult.checkoutCreate.checkout) {
|
|
416
|
-
|
|
432
|
+
newCheckoutId = createResult.checkoutCreate.checkout.id;
|
|
433
|
+
setCheckoutId(newCheckoutId);
|
|
417
434
|
}
|
|
418
435
|
} else {
|
|
419
436
|
// Update email on existing checkout
|
|
@@ -428,9 +445,38 @@ export default function CheckoutPage() {
|
|
|
428
445
|
}
|
|
429
446
|
}
|
|
430
447
|
|
|
448
|
+
// Send gift card recipient data (if any)
|
|
449
|
+
if (hasGiftCards && newCheckoutId) {
|
|
450
|
+
const giftCardItems = items.filter((item) => item.productType === "GIFT_CARD");
|
|
451
|
+
const recipientMutations = giftCardItems
|
|
452
|
+
.filter((gc) => giftCardRecipients[gc.lineId]?.recipientEmail)
|
|
453
|
+
.map((gc) =>
|
|
454
|
+
updateGiftCardRecipient.mutateAsync({
|
|
455
|
+
input: {
|
|
456
|
+
checkoutId: newCheckoutId!,
|
|
457
|
+
lineItemId: gc.lineId,
|
|
458
|
+
...giftCardRecipients[gc.lineId],
|
|
459
|
+
},
|
|
460
|
+
})
|
|
461
|
+
);
|
|
462
|
+
|
|
463
|
+
if (recipientMutations.length > 0) {
|
|
464
|
+
const results = await Promise.allSettled(recipientMutations);
|
|
465
|
+
const failed = results.filter((r) => r.status === "rejected");
|
|
466
|
+
if (failed.length > 0) {
|
|
467
|
+
toast.warning(
|
|
468
|
+
t("giftCard.recipientSaveError", { count: failed.length })
|
|
469
|
+
);
|
|
470
|
+
}
|
|
471
|
+
// Refetch checkout after recipient updates — prevents stale UI
|
|
472
|
+
await queryClient.refetchQueries({ queryKey: queryKeys.checkout.detail(newCheckoutId, currencyCode) });
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
|
|
431
476
|
setCurrentStep(allGiftCards ? "payment" : "shipping");
|
|
432
|
-
} catch (error:
|
|
433
|
-
|
|
477
|
+
} catch (error: unknown) {
|
|
478
|
+
const message = error instanceof Error ? error.message : t("errors.contactProcessing");
|
|
479
|
+
toast.error(message);
|
|
434
480
|
}
|
|
435
481
|
};
|
|
436
482
|
|
|
@@ -470,7 +516,7 @@ export default function CheckoutPage() {
|
|
|
470
516
|
setErrors({});
|
|
471
517
|
|
|
472
518
|
if (!checkoutId) {
|
|
473
|
-
toast.error("
|
|
519
|
+
toast.error(t("errors.checkoutNotFound"));
|
|
474
520
|
return;
|
|
475
521
|
}
|
|
476
522
|
|
|
@@ -510,8 +556,9 @@ export default function CheckoutPage() {
|
|
|
510
556
|
// Refetch checkout to get shipping rates
|
|
511
557
|
await refetchCheckout();
|
|
512
558
|
setCurrentStep("delivery");
|
|
513
|
-
} catch (error:
|
|
514
|
-
|
|
559
|
+
} catch (error: unknown) {
|
|
560
|
+
const message = error instanceof Error ? error.message : t("errors.addressUpdate");
|
|
561
|
+
toast.error(message);
|
|
515
562
|
}
|
|
516
563
|
};
|
|
517
564
|
|
|
@@ -521,13 +568,13 @@ export default function CheckoutPage() {
|
|
|
521
568
|
|
|
522
569
|
const handleDeliverySubmit = async () => {
|
|
523
570
|
if (!formState.selectedShippingRate) {
|
|
524
|
-
setErrors({ shipping: "
|
|
571
|
+
setErrors({ shipping: t("delivery.selectMethod") });
|
|
525
572
|
return;
|
|
526
573
|
}
|
|
527
574
|
setErrors({});
|
|
528
575
|
|
|
529
576
|
if (!checkoutId) {
|
|
530
|
-
toast.error("
|
|
577
|
+
toast.error(t("errors.checkoutNotFound"));
|
|
531
578
|
return;
|
|
532
579
|
}
|
|
533
580
|
|
|
@@ -544,8 +591,9 @@ export default function CheckoutPage() {
|
|
|
544
591
|
|
|
545
592
|
await refetchCheckout();
|
|
546
593
|
setCurrentStep("payment");
|
|
547
|
-
} catch (error:
|
|
548
|
-
|
|
594
|
+
} catch (error: unknown) {
|
|
595
|
+
const message = error instanceof Error ? error.message : t("errors.deliverySelect");
|
|
596
|
+
toast.error(message);
|
|
549
597
|
}
|
|
550
598
|
};
|
|
551
599
|
|
|
@@ -556,14 +604,14 @@ export default function CheckoutPage() {
|
|
|
556
604
|
const handlePaymentMethodSelect = (paymentMethodId: string) => {
|
|
557
605
|
// Validate payment method supports current currency
|
|
558
606
|
const selectedMethod = checkout?.availablePaymentMethods?.find(
|
|
559
|
-
(pm
|
|
607
|
+
(pm) => pm.id === paymentMethodId
|
|
560
608
|
);
|
|
561
609
|
|
|
562
|
-
if (selectedMethod?.supportedCurrencies
|
|
610
|
+
if (selectedMethod?.supportedCurrencies && selectedMethod.supportedCurrencies.length > 0) {
|
|
563
611
|
const supportsCurrency = selectedMethod.supportedCurrencies.includes(currencyCode);
|
|
564
612
|
if (!supportsCurrency) {
|
|
565
613
|
setErrors({
|
|
566
|
-
payment:
|
|
614
|
+
payment: t("payment.unsupportedCurrency", { currency: currencyCode }),
|
|
567
615
|
});
|
|
568
616
|
return;
|
|
569
617
|
}
|
|
@@ -575,27 +623,27 @@ export default function CheckoutPage() {
|
|
|
575
623
|
|
|
576
624
|
const handlePaymentSubmit = async () => {
|
|
577
625
|
if (!formState.selectedPaymentMethodId) {
|
|
578
|
-
setErrors({ payment: "
|
|
626
|
+
setErrors({ payment: t("payment.selectMethod") });
|
|
579
627
|
return;
|
|
580
628
|
}
|
|
581
629
|
|
|
582
630
|
// Validate selected payment method still exists and is valid
|
|
583
631
|
const selectedMethod = checkout?.availablePaymentMethods?.find(
|
|
584
|
-
(pm
|
|
632
|
+
(pm) => pm.id === formState.selectedPaymentMethodId
|
|
585
633
|
);
|
|
586
634
|
|
|
587
635
|
if (!selectedMethod) {
|
|
588
|
-
setErrors({ payment: "
|
|
636
|
+
setErrors({ payment: t("payment.unavailable") });
|
|
589
637
|
setFormState((prev) => ({ ...prev, selectedPaymentMethodId: null }));
|
|
590
638
|
return;
|
|
591
639
|
}
|
|
592
640
|
|
|
593
641
|
// Validate currency support
|
|
594
|
-
if (selectedMethod.supportedCurrencies
|
|
642
|
+
if (selectedMethod.supportedCurrencies && selectedMethod.supportedCurrencies.length > 0) {
|
|
595
643
|
const supportsCurrency = selectedMethod.supportedCurrencies.includes(currencyCode);
|
|
596
644
|
if (!supportsCurrency) {
|
|
597
645
|
setErrors({
|
|
598
|
-
payment:
|
|
646
|
+
payment: t("payment.unsupportedCurrency", { currency: currencyCode }),
|
|
599
647
|
});
|
|
600
648
|
return;
|
|
601
649
|
}
|
|
@@ -615,7 +663,7 @@ export default function CheckoutPage() {
|
|
|
615
663
|
}
|
|
616
664
|
|
|
617
665
|
if (!checkoutId) {
|
|
618
|
-
toast.error("
|
|
666
|
+
toast.error(t("errors.checkoutNotFound"));
|
|
619
667
|
return;
|
|
620
668
|
}
|
|
621
669
|
|
|
@@ -633,7 +681,7 @@ export default function CheckoutPage() {
|
|
|
633
681
|
}
|
|
634
682
|
|
|
635
683
|
if (!payload?.checkout) {
|
|
636
|
-
toast.error("
|
|
684
|
+
toast.error(t("errors.discountApply"));
|
|
637
685
|
return;
|
|
638
686
|
}
|
|
639
687
|
|
|
@@ -643,9 +691,10 @@ export default function CheckoutPage() {
|
|
|
643
691
|
discountCode: "",
|
|
644
692
|
}));
|
|
645
693
|
await refetchCheckout();
|
|
646
|
-
toast.success("
|
|
647
|
-
} catch (error:
|
|
648
|
-
|
|
694
|
+
toast.success(t("discount.applied"));
|
|
695
|
+
} catch (error: unknown) {
|
|
696
|
+
const message = error instanceof Error ? error.message : t("errors.discountApply");
|
|
697
|
+
toast.error(message);
|
|
649
698
|
}
|
|
650
699
|
};
|
|
651
700
|
|
|
@@ -670,9 +719,10 @@ export default function CheckoutPage() {
|
|
|
670
719
|
appliedDiscountCode: null,
|
|
671
720
|
}));
|
|
672
721
|
await refetchCheckout();
|
|
673
|
-
toast.success("
|
|
674
|
-
} catch (error:
|
|
675
|
-
|
|
722
|
+
toast.success(t("discount.removed"));
|
|
723
|
+
} catch (error: unknown) {
|
|
724
|
+
const message = error instanceof Error ? error.message : t("errors.discountRemove");
|
|
725
|
+
toast.error(message);
|
|
676
726
|
}
|
|
677
727
|
};
|
|
678
728
|
|
|
@@ -686,7 +736,7 @@ export default function CheckoutPage() {
|
|
|
686
736
|
}
|
|
687
737
|
|
|
688
738
|
if (!checkoutId) {
|
|
689
|
-
toast.error("
|
|
739
|
+
toast.error(t("errors.checkoutNotFound"));
|
|
690
740
|
return;
|
|
691
741
|
}
|
|
692
742
|
|
|
@@ -700,13 +750,13 @@ export default function CheckoutPage() {
|
|
|
700
750
|
const error = result.checkoutGiftCardApply.userErrors[0];
|
|
701
751
|
// Translate common error codes to user-friendly messages
|
|
702
752
|
const errorMessages: Record<string, string> = {
|
|
703
|
-
GIFT_CARD_NOT_FOUND: "
|
|
704
|
-
GIFT_CARD_EXPIRED: "
|
|
705
|
-
GIFT_CARD_DISABLED: "
|
|
706
|
-
|
|
707
|
-
|
|
753
|
+
GIFT_CARD_NOT_FOUND: t("errors.giftCardNotFound"),
|
|
754
|
+
GIFT_CARD_EXPIRED: t("errors.giftCardExpired"),
|
|
755
|
+
GIFT_CARD_DISABLED: t("errors.giftCardDisabled"),
|
|
756
|
+
GIFT_CARD_DEPLETED: t("errors.giftCardDepleted"),
|
|
757
|
+
GIFT_CARD_UNUSABLE: t("errors.giftCardUnusable"),
|
|
708
758
|
};
|
|
709
|
-
toast.error(errorMessages[error.code] || error.message);
|
|
759
|
+
toast.error((error.code && errorMessages[error.code]) || error.message);
|
|
710
760
|
return;
|
|
711
761
|
}
|
|
712
762
|
|
|
@@ -715,9 +765,10 @@ export default function CheckoutPage() {
|
|
|
715
765
|
giftCardCode: "",
|
|
716
766
|
}));
|
|
717
767
|
await refetchCheckout();
|
|
718
|
-
toast.success("
|
|
719
|
-
} catch (error:
|
|
720
|
-
|
|
768
|
+
toast.success(tCart("giftCardApplied"));
|
|
769
|
+
} catch (error: unknown) {
|
|
770
|
+
const message = error instanceof Error ? error.message : t("errors.giftCardApply");
|
|
771
|
+
toast.error(message);
|
|
721
772
|
}
|
|
722
773
|
};
|
|
723
774
|
|
|
@@ -738,9 +789,10 @@ export default function CheckoutPage() {
|
|
|
738
789
|
}
|
|
739
790
|
|
|
740
791
|
await refetchCheckout();
|
|
741
|
-
toast.success("
|
|
742
|
-
} catch (error:
|
|
743
|
-
|
|
792
|
+
toast.success(tCart("giftCardRemoved"));
|
|
793
|
+
} catch (error: unknown) {
|
|
794
|
+
const message = error instanceof Error ? error.message : t("errors.giftCardRemove");
|
|
795
|
+
toast.error(message);
|
|
744
796
|
}
|
|
745
797
|
};
|
|
746
798
|
|
|
@@ -750,12 +802,12 @@ export default function CheckoutPage() {
|
|
|
750
802
|
|
|
751
803
|
const handleCompleteCheckout = async () => {
|
|
752
804
|
if (!formState.acceptTerms) {
|
|
753
|
-
setErrors({ terms: "
|
|
805
|
+
setErrors({ terms: t("review.mustAcceptTerms") });
|
|
754
806
|
return;
|
|
755
807
|
}
|
|
756
808
|
|
|
757
809
|
if (!formState.selectedPaymentMethodId) {
|
|
758
|
-
setErrors({ payment: "
|
|
810
|
+
setErrors({ payment: t("payment.selectMethod") });
|
|
759
811
|
setCurrentStep("payment");
|
|
760
812
|
return;
|
|
761
813
|
}
|
|
@@ -763,7 +815,7 @@ export default function CheckoutPage() {
|
|
|
763
815
|
setErrors({});
|
|
764
816
|
|
|
765
817
|
if (!checkoutId) {
|
|
766
|
-
toast.error("
|
|
818
|
+
toast.error(t("errors.checkoutNotFound"));
|
|
767
819
|
return;
|
|
768
820
|
}
|
|
769
821
|
|
|
@@ -781,31 +833,24 @@ export default function CheckoutPage() {
|
|
|
781
833
|
const errorMessage = error.message;
|
|
782
834
|
|
|
783
835
|
// Handle specific payment-related error codes
|
|
784
|
-
|
|
836
|
+
// Cast to string since backend may return codes not in the generated enum
|
|
837
|
+
const codeStr = errorCode as string | null | undefined;
|
|
838
|
+
switch (codeStr) {
|
|
785
839
|
case "INVALID_PAYMENT_METHOD":
|
|
786
|
-
case "
|
|
787
|
-
setErrors({ payment: "
|
|
840
|
+
case "NO_PAYMENT_METHODS":
|
|
841
|
+
setErrors({ payment: t("payment.unavailable") });
|
|
788
842
|
setFormState((prev) => ({ ...prev, selectedPaymentMethodId: null }));
|
|
789
843
|
setCurrentStep("payment");
|
|
790
|
-
toast.error("
|
|
844
|
+
toast.error(t("errors.paymentUnavailable"));
|
|
791
845
|
break;
|
|
792
|
-
case "
|
|
793
|
-
|
|
794
|
-
setCurrentStep("payment");
|
|
795
|
-
toast.error("Waluta nie jest obsługiwana przez tę metodę płatności");
|
|
846
|
+
case "ALREADY_COMPLETED":
|
|
847
|
+
toast.error(t("errors.sessionExpired"));
|
|
796
848
|
break;
|
|
797
|
-
case "
|
|
798
|
-
toast.error("
|
|
799
|
-
setCurrentStep("payment");
|
|
800
|
-
break;
|
|
801
|
-
case "CHECKOUT_EXPIRED":
|
|
802
|
-
toast.error("Sesja checkout wygasła. Proszę odświeżyć stronę i spróbować ponownie.");
|
|
803
|
-
break;
|
|
804
|
-
case "INVENTORY_NOT_AVAILABLE":
|
|
805
|
-
toast.error("Niektóre produkty są niedostępne. Sprawdź swój koszyk.");
|
|
849
|
+
case "NOT_ENOUGH_IN_STOCK":
|
|
850
|
+
toast.error(t("errors.outOfStock"));
|
|
806
851
|
break;
|
|
807
852
|
default:
|
|
808
|
-
toast.error(errorMessage || "
|
|
853
|
+
toast.error(errorMessage || t("errors.checkoutComplete"));
|
|
809
854
|
}
|
|
810
855
|
return;
|
|
811
856
|
}
|
|
@@ -821,7 +866,7 @@ export default function CheckoutPage() {
|
|
|
821
866
|
const orderId = completeResult.checkoutComplete.order.id;
|
|
822
867
|
const paymentType = formState.selectedPaymentMethodId
|
|
823
868
|
? checkout?.availablePaymentMethods?.find(
|
|
824
|
-
(pm
|
|
869
|
+
(pm) => pm.id === formState.selectedPaymentMethodId
|
|
825
870
|
)?.type
|
|
826
871
|
: null;
|
|
827
872
|
|
|
@@ -830,10 +875,11 @@ export default function CheckoutPage() {
|
|
|
830
875
|
router.push(`/checkout/success/${orderId}${searchParams}`);
|
|
831
876
|
} else {
|
|
832
877
|
// Fallback: no payment URL and no order - show error
|
|
833
|
-
toast.error("
|
|
878
|
+
toast.error(t("errors.unexpected"));
|
|
834
879
|
}
|
|
835
|
-
} catch (error:
|
|
836
|
-
|
|
880
|
+
} catch (error: unknown) {
|
|
881
|
+
const message = error instanceof Error ? error.message : t("errors.checkoutComplete");
|
|
882
|
+
toast.error(message);
|
|
837
883
|
}
|
|
838
884
|
};
|
|
839
885
|
|
|
@@ -863,7 +909,7 @@ export default function CheckoutPage() {
|
|
|
863
909
|
return (
|
|
864
910
|
<div className="container mx-auto px-4 py-16 text-center">
|
|
865
911
|
<Loader2 className="mx-auto h-8 w-8 animate-spin text-muted-foreground" />
|
|
866
|
-
<p className="mt-4 text-muted-foreground"
|
|
912
|
+
<p className="mt-4 text-muted-foreground">{tc("loading")}</p>
|
|
867
913
|
</div>
|
|
868
914
|
);
|
|
869
915
|
}
|
|
@@ -873,12 +919,12 @@ export default function CheckoutPage() {
|
|
|
873
919
|
return (
|
|
874
920
|
<div className="container mx-auto px-4 py-16 text-center">
|
|
875
921
|
<ShoppingBag className="mx-auto h-16 w-16 text-muted-foreground" />
|
|
876
|
-
<h1 className="mt-4 text-2xl font-bold">
|
|
922
|
+
<h1 className="mt-4 text-2xl font-bold">{tCart("empty")}</h1>
|
|
877
923
|
<p className="mt-2 text-muted-foreground">
|
|
878
|
-
|
|
924
|
+
{tCart("emptyDescription")}
|
|
879
925
|
</p>
|
|
880
926
|
<Button className="mt-6" asChild>
|
|
881
|
-
<Link href="/products">
|
|
927
|
+
<Link href="/products">{tCart("browseProducts")}</Link>
|
|
882
928
|
</Button>
|
|
883
929
|
</div>
|
|
884
930
|
);
|
|
@@ -889,11 +935,11 @@ export default function CheckoutPage() {
|
|
|
889
935
|
// ============================================================================
|
|
890
936
|
|
|
891
937
|
const allSteps: { id: CheckoutStep; label: string; icon: React.ReactNode }[] = [
|
|
892
|
-
{ id: "contact", label: "
|
|
893
|
-
{ id: "shipping", label: "
|
|
894
|
-
{ id: "delivery", label: "
|
|
895
|
-
{ id: "payment", label: "
|
|
896
|
-
{ id: "review", label: "
|
|
938
|
+
{ id: "contact", label: t("steps.contact"), icon: <User className="h-4 w-4" /> },
|
|
939
|
+
{ id: "shipping", label: t("steps.shipping"), icon: <Package className="h-4 w-4" /> },
|
|
940
|
+
{ id: "delivery", label: t("steps.delivery"), icon: <Truck className="h-4 w-4" /> },
|
|
941
|
+
{ id: "payment", label: t("steps.payment"), icon: <CreditCard className="h-4 w-4" /> },
|
|
942
|
+
{ id: "review", label: t("steps.review"), icon: <ClipboardCheck className="h-4 w-4" /> },
|
|
897
943
|
];
|
|
898
944
|
// Skip shipping and delivery steps when all items are gift cards
|
|
899
945
|
const steps = allGiftCards
|
|
@@ -911,9 +957,9 @@ export default function CheckoutPage() {
|
|
|
911
957
|
<Breadcrumbs className="mb-6" />
|
|
912
958
|
|
|
913
959
|
<div className="mb-8 flex items-center justify-between">
|
|
914
|
-
<h1 className="text-3xl font-bold">
|
|
960
|
+
<h1 className="text-3xl font-bold">{t("title")}</h1>
|
|
915
961
|
<Link href="/auth/login" className="text-sm text-primary hover:underline">
|
|
916
|
-
|
|
962
|
+
{t("haveAccount")}
|
|
917
963
|
</Link>
|
|
918
964
|
</div>
|
|
919
965
|
|
|
@@ -961,19 +1007,19 @@ export default function CheckoutPage() {
|
|
|
961
1007
|
<CardHeader>
|
|
962
1008
|
<CardTitle className="flex items-center gap-2">
|
|
963
1009
|
<User className="h-5 w-5" />
|
|
964
|
-
|
|
1010
|
+
{t("contact.title")}
|
|
965
1011
|
</CardTitle>
|
|
966
1012
|
<CardDescription>
|
|
967
|
-
|
|
1013
|
+
{t("contact.description")}
|
|
968
1014
|
</CardDescription>
|
|
969
1015
|
</CardHeader>
|
|
970
1016
|
<CardContent className="space-y-4">
|
|
971
1017
|
<div>
|
|
972
|
-
<Label htmlFor="email">
|
|
1018
|
+
<Label htmlFor="email">{t("contact.email")} *</Label>
|
|
973
1019
|
<Input
|
|
974
1020
|
id="email"
|
|
975
1021
|
type="email"
|
|
976
|
-
placeholder="
|
|
1022
|
+
placeholder={t("contact.emailPlaceholder")}
|
|
977
1023
|
value={formState.email}
|
|
978
1024
|
onChange={(e) =>
|
|
979
1025
|
setFormState((prev) => ({ ...prev, email: e.target.value }))
|
|
@@ -986,11 +1032,11 @@ export default function CheckoutPage() {
|
|
|
986
1032
|
</div>
|
|
987
1033
|
|
|
988
1034
|
<div>
|
|
989
|
-
<Label htmlFor="phone">
|
|
1035
|
+
<Label htmlFor="phone">{t("contact.phone")} *</Label>
|
|
990
1036
|
<Input
|
|
991
1037
|
id="phone"
|
|
992
1038
|
type="tel"
|
|
993
|
-
placeholder="
|
|
1039
|
+
placeholder={t("contact.phonePlaceholder")}
|
|
994
1040
|
value={formState.phone}
|
|
995
1041
|
onChange={(e) =>
|
|
996
1042
|
setFormState((prev) => ({ ...prev, phone: e.target.value }))
|
|
@@ -1001,7 +1047,7 @@ export default function CheckoutPage() {
|
|
|
1001
1047
|
<p className="mt-1 text-sm text-destructive">{errors.phone}</p>
|
|
1002
1048
|
)}
|
|
1003
1049
|
<p className="mt-1 text-xs text-muted-foreground">
|
|
1004
|
-
|
|
1050
|
+
{t("contact.phoneHint")}
|
|
1005
1051
|
</p>
|
|
1006
1052
|
</div>
|
|
1007
1053
|
|
|
@@ -1014,7 +1060,7 @@ export default function CheckoutPage() {
|
|
|
1014
1060
|
}
|
|
1015
1061
|
/>
|
|
1016
1062
|
<Label htmlFor="marketing" className="text-sm font-normal cursor-pointer">
|
|
1017
|
-
|
|
1063
|
+
{t("contact.marketing")}
|
|
1018
1064
|
</Label>
|
|
1019
1065
|
</div>
|
|
1020
1066
|
|
|
@@ -1023,28 +1069,28 @@ export default function CheckoutPage() {
|
|
|
1023
1069
|
<div className="space-y-4 border-t pt-4">
|
|
1024
1070
|
<div className="flex items-center gap-2 text-sm font-medium">
|
|
1025
1071
|
<Gift className="h-4 w-4" />
|
|
1026
|
-
|
|
1072
|
+
{t("giftCard.recipientTitle")}
|
|
1027
1073
|
</div>
|
|
1028
1074
|
<p className="text-xs text-muted-foreground">
|
|
1029
|
-
|
|
1075
|
+
{t("giftCard.recipientDescription")}
|
|
1030
1076
|
</p>
|
|
1031
1077
|
{items
|
|
1032
1078
|
.filter((item) => item.productType === "GIFT_CARD")
|
|
1033
1079
|
.map((item) => (
|
|
1034
|
-
<div key={item.
|
|
1080
|
+
<div key={item.lineId} className="space-y-3 rounded-lg border p-4">
|
|
1035
1081
|
<p className="text-sm font-medium">{item.productTitle} — {item.variantTitle}</p>
|
|
1036
1082
|
<div>
|
|
1037
|
-
<Label htmlFor={`recipient-email-${item.
|
|
1083
|
+
<Label htmlFor={`recipient-email-${item.lineId}`}>{t("giftCard.recipientEmail")}</Label>
|
|
1038
1084
|
<Input
|
|
1039
|
-
id={`recipient-email-${item.
|
|
1085
|
+
id={`recipient-email-${item.lineId}`}
|
|
1040
1086
|
type="email"
|
|
1041
|
-
placeholder="
|
|
1042
|
-
value={giftCardRecipients[item.
|
|
1087
|
+
placeholder={t("giftCard.recipientEmailPlaceholder")}
|
|
1088
|
+
value={giftCardRecipients[item.lineId]?.recipientEmail || ""}
|
|
1043
1089
|
onChange={(e) =>
|
|
1044
1090
|
setGiftCardRecipients((prev) => ({
|
|
1045
1091
|
...prev,
|
|
1046
|
-
[item.
|
|
1047
|
-
...prev[item.
|
|
1092
|
+
[item.lineId]: {
|
|
1093
|
+
...prev[item.lineId],
|
|
1048
1094
|
recipientEmail: e.target.value,
|
|
1049
1095
|
},
|
|
1050
1096
|
}))
|
|
@@ -1052,17 +1098,17 @@ export default function CheckoutPage() {
|
|
|
1052
1098
|
/>
|
|
1053
1099
|
</div>
|
|
1054
1100
|
<div>
|
|
1055
|
-
<Label htmlFor={`recipient-name-${item.
|
|
1101
|
+
<Label htmlFor={`recipient-name-${item.lineId}`}>{t("giftCard.recipientName")}</Label>
|
|
1056
1102
|
<Input
|
|
1057
|
-
id={`recipient-name-${item.
|
|
1103
|
+
id={`recipient-name-${item.lineId}`}
|
|
1058
1104
|
type="text"
|
|
1059
|
-
placeholder="
|
|
1060
|
-
value={giftCardRecipients[item.
|
|
1105
|
+
placeholder={t("giftCard.recipientNamePlaceholder")}
|
|
1106
|
+
value={giftCardRecipients[item.lineId]?.recipientName || ""}
|
|
1061
1107
|
onChange={(e) =>
|
|
1062
1108
|
setGiftCardRecipients((prev) => ({
|
|
1063
1109
|
...prev,
|
|
1064
|
-
[item.
|
|
1065
|
-
...prev[item.
|
|
1110
|
+
[item.lineId]: {
|
|
1111
|
+
...prev[item.lineId],
|
|
1066
1112
|
recipientName: e.target.value,
|
|
1067
1113
|
},
|
|
1068
1114
|
}))
|
|
@@ -1070,17 +1116,17 @@ export default function CheckoutPage() {
|
|
|
1070
1116
|
/>
|
|
1071
1117
|
</div>
|
|
1072
1118
|
<div>
|
|
1073
|
-
<Label htmlFor={`recipient-message-${item.
|
|
1119
|
+
<Label htmlFor={`recipient-message-${item.lineId}`}>{t("giftCard.message")}</Label>
|
|
1074
1120
|
<Input
|
|
1075
|
-
id={`recipient-message-${item.
|
|
1121
|
+
id={`recipient-message-${item.lineId}`}
|
|
1076
1122
|
type="text"
|
|
1077
|
-
placeholder="
|
|
1078
|
-
value={giftCardRecipients[item.
|
|
1123
|
+
placeholder={t("giftCard.messagePlaceholder")}
|
|
1124
|
+
value={giftCardRecipients[item.lineId]?.message || ""}
|
|
1079
1125
|
onChange={(e) =>
|
|
1080
1126
|
setGiftCardRecipients((prev) => ({
|
|
1081
1127
|
...prev,
|
|
1082
|
-
[item.
|
|
1083
|
-
...prev[item.
|
|
1128
|
+
[item.lineId]: {
|
|
1129
|
+
...prev[item.lineId],
|
|
1084
1130
|
message: e.target.value,
|
|
1085
1131
|
},
|
|
1086
1132
|
}))
|
|
@@ -1098,7 +1144,7 @@ export default function CheckoutPage() {
|
|
|
1098
1144
|
) : (
|
|
1099
1145
|
<ChevronRight className="mr-2 h-4 w-4" />
|
|
1100
1146
|
)}
|
|
1101
|
-
{allGiftCards ? "
|
|
1147
|
+
{allGiftCards ? t("contact.nextPayment") : t("contact.nextShipping")}
|
|
1102
1148
|
</Button>
|
|
1103
1149
|
</CardContent>
|
|
1104
1150
|
</Card>
|
|
@@ -1111,9 +1157,9 @@ export default function CheckoutPage() {
|
|
|
1111
1157
|
<CardHeader>
|
|
1112
1158
|
<CardTitle className="flex items-center gap-2">
|
|
1113
1159
|
<Package className="h-5 w-5" />
|
|
1114
|
-
|
|
1160
|
+
{t("shipping.title")}
|
|
1115
1161
|
</CardTitle>
|
|
1116
|
-
<CardDescription>
|
|
1162
|
+
<CardDescription>{t("shipping.description")}</CardDescription>
|
|
1117
1163
|
</CardHeader>
|
|
1118
1164
|
<CardContent>
|
|
1119
1165
|
<AddressFormFields
|
|
@@ -1134,7 +1180,7 @@ export default function CheckoutPage() {
|
|
|
1134
1180
|
<CardHeader>
|
|
1135
1181
|
<CardTitle className="flex items-center gap-2">
|
|
1136
1182
|
<CreditCard className="h-5 w-5" />
|
|
1137
|
-
|
|
1183
|
+
{t("shipping.billingTitle")}
|
|
1138
1184
|
</CardTitle>
|
|
1139
1185
|
</CardHeader>
|
|
1140
1186
|
<CardContent className="space-y-4">
|
|
@@ -1147,7 +1193,7 @@ export default function CheckoutPage() {
|
|
|
1147
1193
|
}
|
|
1148
1194
|
/>
|
|
1149
1195
|
<Label htmlFor="sameAsBilling" className="cursor-pointer">
|
|
1150
|
-
|
|
1196
|
+
{t("shipping.sameAsShipping")}
|
|
1151
1197
|
</Label>
|
|
1152
1198
|
</div>
|
|
1153
1199
|
|
|
@@ -1170,7 +1216,7 @@ export default function CheckoutPage() {
|
|
|
1170
1216
|
<div className="flex gap-4">
|
|
1171
1217
|
<Button variant="outline" onClick={goBack} size="lg">
|
|
1172
1218
|
<ChevronLeft className="mr-2 h-4 w-4" />
|
|
1173
|
-
|
|
1219
|
+
{tc("back")}
|
|
1174
1220
|
</Button>
|
|
1175
1221
|
<Button onClick={handleShippingSubmit} disabled={isLoading} className="flex-1" size="lg">
|
|
1176
1222
|
{isLoading ? (
|
|
@@ -1178,7 +1224,7 @@ export default function CheckoutPage() {
|
|
|
1178
1224
|
) : (
|
|
1179
1225
|
<ChevronRight className="mr-2 h-4 w-4" />
|
|
1180
1226
|
)}
|
|
1181
|
-
|
|
1227
|
+
{t("shipping.nextDelivery")}
|
|
1182
1228
|
</Button>
|
|
1183
1229
|
</div>
|
|
1184
1230
|
</>
|
|
@@ -1190,23 +1236,23 @@ export default function CheckoutPage() {
|
|
|
1190
1236
|
<CardHeader>
|
|
1191
1237
|
<CardTitle className="flex items-center gap-2">
|
|
1192
1238
|
<Truck className="h-5 w-5" />
|
|
1193
|
-
|
|
1239
|
+
{t("delivery.title")}
|
|
1194
1240
|
</CardTitle>
|
|
1195
|
-
<CardDescription>
|
|
1241
|
+
<CardDescription>{t("delivery.description")}</CardDescription>
|
|
1196
1242
|
</CardHeader>
|
|
1197
1243
|
<CardContent className="space-y-4">
|
|
1198
1244
|
{!shippingRatesReady ? (
|
|
1199
1245
|
<div className="flex items-center justify-center py-8">
|
|
1200
1246
|
<Loader2 className="h-6 w-6 animate-spin text-muted-foreground" />
|
|
1201
|
-
<span className="ml-2 text-muted-foreground"
|
|
1247
|
+
<span className="ml-2 text-muted-foreground">{t("delivery.loading")}</span>
|
|
1202
1248
|
</div>
|
|
1203
1249
|
) : availableShippingRates.length === 0 ? (
|
|
1204
1250
|
<div className="rounded-lg border border-destructive/50 bg-destructive/10 p-4 text-center">
|
|
1205
1251
|
<p className="text-destructive">
|
|
1206
|
-
|
|
1252
|
+
{t("delivery.noMethods")}
|
|
1207
1253
|
</p>
|
|
1208
1254
|
<Button variant="outline" onClick={goBack} className="mt-4">
|
|
1209
|
-
|
|
1255
|
+
{t("delivery.changeAddress")}
|
|
1210
1256
|
</Button>
|
|
1211
1257
|
</div>
|
|
1212
1258
|
) : (
|
|
@@ -1249,7 +1295,7 @@ export default function CheckoutPage() {
|
|
|
1249
1295
|
<div className="flex gap-4 pt-4">
|
|
1250
1296
|
<Button variant="outline" onClick={goBack} size="lg">
|
|
1251
1297
|
<ChevronLeft className="mr-2 h-4 w-4" />
|
|
1252
|
-
|
|
1298
|
+
{tc("back")}
|
|
1253
1299
|
</Button>
|
|
1254
1300
|
<Button
|
|
1255
1301
|
onClick={handleDeliverySubmit}
|
|
@@ -1262,7 +1308,7 @@ export default function CheckoutPage() {
|
|
|
1262
1308
|
) : (
|
|
1263
1309
|
<ChevronRight className="mr-2 h-4 w-4" />
|
|
1264
1310
|
)}
|
|
1265
|
-
|
|
1311
|
+
{t("delivery.nextPayment")}
|
|
1266
1312
|
</Button>
|
|
1267
1313
|
</div>
|
|
1268
1314
|
</CardContent>
|
|
@@ -1275,15 +1321,15 @@ export default function CheckoutPage() {
|
|
|
1275
1321
|
<CardHeader>
|
|
1276
1322
|
<CardTitle className="flex items-center gap-2">
|
|
1277
1323
|
<CreditCard className="h-5 w-5" />
|
|
1278
|
-
|
|
1324
|
+
{t("payment.title")}
|
|
1279
1325
|
</CardTitle>
|
|
1280
|
-
<CardDescription>
|
|
1326
|
+
<CardDescription>{t("payment.description")}</CardDescription>
|
|
1281
1327
|
</CardHeader>
|
|
1282
1328
|
<CardContent className="space-y-4">
|
|
1283
1329
|
{/* PaymentStep component handles payment method selection */}
|
|
1284
1330
|
<PaymentStep
|
|
1285
1331
|
availablePaymentMethods={(checkout.availablePaymentMethods || []).map(
|
|
1286
|
-
(pm
|
|
1332
|
+
(pm): PaymentMethod => ({
|
|
1287
1333
|
id: pm.id,
|
|
1288
1334
|
name: pm.name,
|
|
1289
1335
|
provider: pm.provider,
|
|
@@ -1307,7 +1353,7 @@ export default function CheckoutPage() {
|
|
|
1307
1353
|
<div className="flex gap-4 pt-4">
|
|
1308
1354
|
<Button variant="outline" onClick={goBack} size="lg">
|
|
1309
1355
|
<ChevronLeft className="mr-2 h-4 w-4" />
|
|
1310
|
-
|
|
1356
|
+
{tc("back")}
|
|
1311
1357
|
</Button>
|
|
1312
1358
|
<Button
|
|
1313
1359
|
onClick={handlePaymentSubmit}
|
|
@@ -1320,7 +1366,7 @@ export default function CheckoutPage() {
|
|
|
1320
1366
|
) : (
|
|
1321
1367
|
<ChevronRight className="mr-2 h-4 w-4" />
|
|
1322
1368
|
)}
|
|
1323
|
-
|
|
1369
|
+
{t("payment.nextReview")}
|
|
1324
1370
|
</Button>
|
|
1325
1371
|
</div>
|
|
1326
1372
|
</CardContent>
|
|
@@ -1333,15 +1379,15 @@ export default function CheckoutPage() {
|
|
|
1333
1379
|
<CardHeader>
|
|
1334
1380
|
<CardTitle className="flex items-center gap-2">
|
|
1335
1381
|
<ClipboardCheck className="h-5 w-5" />
|
|
1336
|
-
|
|
1382
|
+
{t("review.title")}
|
|
1337
1383
|
</CardTitle>
|
|
1338
|
-
<CardDescription>
|
|
1384
|
+
<CardDescription>{t("review.description")}</CardDescription>
|
|
1339
1385
|
</CardHeader>
|
|
1340
1386
|
<CardContent className="space-y-6">
|
|
1341
1387
|
{/* Contact */}
|
|
1342
1388
|
<div className="flex items-start justify-between rounded-lg border p-4">
|
|
1343
1389
|
<div>
|
|
1344
|
-
<h3 className="mb-1 font-medium">
|
|
1390
|
+
<h3 className="mb-1 font-medium">{t("review.contact")}</h3>
|
|
1345
1391
|
<p className="text-sm text-muted-foreground">{checkout.email}</p>
|
|
1346
1392
|
<p className="text-sm text-muted-foreground">{formState.phone}</p>
|
|
1347
1393
|
</div>
|
|
@@ -1350,7 +1396,7 @@ export default function CheckoutPage() {
|
|
|
1350
1396
|
size="sm"
|
|
1351
1397
|
onClick={() => setCurrentStep("contact")}
|
|
1352
1398
|
>
|
|
1353
|
-
|
|
1399
|
+
{t("review.change")}
|
|
1354
1400
|
</Button>
|
|
1355
1401
|
</div>
|
|
1356
1402
|
|
|
@@ -1358,7 +1404,7 @@ export default function CheckoutPage() {
|
|
|
1358
1404
|
{!allGiftCards && (
|
|
1359
1405
|
<div className="flex items-start justify-between rounded-lg border p-4">
|
|
1360
1406
|
<div>
|
|
1361
|
-
<h3 className="mb-1 font-medium">
|
|
1407
|
+
<h3 className="mb-1 font-medium">{t("review.shippingAddress")}</h3>
|
|
1362
1408
|
{checkout.shippingAddress && (
|
|
1363
1409
|
<p className="text-sm text-muted-foreground">
|
|
1364
1410
|
{checkout.shippingAddress.firstName} {checkout.shippingAddress.lastName}
|
|
@@ -1373,7 +1419,7 @@ export default function CheckoutPage() {
|
|
|
1373
1419
|
<br />
|
|
1374
1420
|
{checkout.shippingAddress.zip} {checkout.shippingAddress.city}
|
|
1375
1421
|
<br />
|
|
1376
|
-
{
|
|
1422
|
+
{countries.find((c) => c.code === checkout.shippingAddress?.country)?.name ||
|
|
1377
1423
|
checkout.shippingAddress.country}
|
|
1378
1424
|
</p>
|
|
1379
1425
|
)}
|
|
@@ -1383,7 +1429,7 @@ export default function CheckoutPage() {
|
|
|
1383
1429
|
size="sm"
|
|
1384
1430
|
onClick={() => setCurrentStep("shipping")}
|
|
1385
1431
|
>
|
|
1386
|
-
|
|
1432
|
+
{t("review.change")}
|
|
1387
1433
|
</Button>
|
|
1388
1434
|
</div>
|
|
1389
1435
|
)}
|
|
@@ -1392,7 +1438,7 @@ export default function CheckoutPage() {
|
|
|
1392
1438
|
{!allGiftCards && (
|
|
1393
1439
|
<div className="flex items-start justify-between rounded-lg border p-4">
|
|
1394
1440
|
<div>
|
|
1395
|
-
<h3 className="mb-1 font-medium">
|
|
1441
|
+
<h3 className="mb-1 font-medium">{t("review.shippingMethod")}</h3>
|
|
1396
1442
|
{checkout.shippingLine && (
|
|
1397
1443
|
<p className="text-sm text-muted-foreground">
|
|
1398
1444
|
{checkout.shippingLine.title} -{" "}
|
|
@@ -1405,7 +1451,7 @@ export default function CheckoutPage() {
|
|
|
1405
1451
|
size="sm"
|
|
1406
1452
|
onClick={() => setCurrentStep("delivery")}
|
|
1407
1453
|
>
|
|
1408
|
-
|
|
1454
|
+
{t("review.change")}
|
|
1409
1455
|
</Button>
|
|
1410
1456
|
</div>
|
|
1411
1457
|
)}
|
|
@@ -1413,12 +1459,12 @@ export default function CheckoutPage() {
|
|
|
1413
1459
|
{/* Payment Method */}
|
|
1414
1460
|
<div className="flex items-start justify-between rounded-lg border p-4">
|
|
1415
1461
|
<div>
|
|
1416
|
-
<h3 className="mb-1 font-medium">
|
|
1462
|
+
<h3 className="mb-1 font-medium">{t("review.paymentMethod")}</h3>
|
|
1417
1463
|
{formState.selectedPaymentMethodId && (
|
|
1418
1464
|
<p className="text-sm text-muted-foreground">
|
|
1419
1465
|
{checkout.availablePaymentMethods?.find(
|
|
1420
|
-
(pm
|
|
1421
|
-
)?.name || "
|
|
1466
|
+
(pm) => pm.id === formState.selectedPaymentMethodId
|
|
1467
|
+
)?.name || t("review.paymentMethod")}
|
|
1422
1468
|
</p>
|
|
1423
1469
|
)}
|
|
1424
1470
|
</div>
|
|
@@ -1427,7 +1473,7 @@ export default function CheckoutPage() {
|
|
|
1427
1473
|
size="sm"
|
|
1428
1474
|
onClick={() => setCurrentStep("payment")}
|
|
1429
1475
|
>
|
|
1430
|
-
|
|
1476
|
+
{t("review.change")}
|
|
1431
1477
|
</Button>
|
|
1432
1478
|
</div>
|
|
1433
1479
|
|
|
@@ -1444,13 +1490,13 @@ export default function CheckoutPage() {
|
|
|
1444
1490
|
/>
|
|
1445
1491
|
<div className="grid gap-1.5 leading-none">
|
|
1446
1492
|
<Label htmlFor="terms" className="cursor-pointer">
|
|
1447
|
-
|
|
1493
|
+
{t("review.acceptTerms")}{" "}
|
|
1448
1494
|
<Link href="/terms" className="text-primary hover:underline">
|
|
1449
|
-
|
|
1495
|
+
{t("review.termsLink")}
|
|
1450
1496
|
</Link>{" "}
|
|
1451
|
-
|
|
1497
|
+
{t("review.and")}{" "}
|
|
1452
1498
|
<Link href="/privacy" className="text-primary hover:underline">
|
|
1453
|
-
|
|
1499
|
+
{t("review.privacyLink")}
|
|
1454
1500
|
</Link>{" "}
|
|
1455
1501
|
*
|
|
1456
1502
|
</Label>
|
|
@@ -1464,7 +1510,7 @@ export default function CheckoutPage() {
|
|
|
1464
1510
|
<div className="flex gap-4">
|
|
1465
1511
|
<Button variant="outline" onClick={goBack} size="lg">
|
|
1466
1512
|
<ChevronLeft className="mr-2 h-4 w-4" />
|
|
1467
|
-
|
|
1513
|
+
{tc("back")}
|
|
1468
1514
|
</Button>
|
|
1469
1515
|
<Button
|
|
1470
1516
|
onClick={handleCompleteCheckout}
|
|
@@ -1477,7 +1523,7 @@ export default function CheckoutPage() {
|
|
|
1477
1523
|
) : (
|
|
1478
1524
|
<CreditCard className="mr-2 h-4 w-4" />
|
|
1479
1525
|
)}
|
|
1480
|
-
|
|
1526
|
+
{t("review.placeOrder")}
|
|
1481
1527
|
</Button>
|
|
1482
1528
|
</div>
|
|
1483
1529
|
</CardContent>
|
|
@@ -1491,49 +1537,84 @@ export default function CheckoutPage() {
|
|
|
1491
1537
|
<CardHeader>
|
|
1492
1538
|
<CardTitle className="flex items-center gap-2">
|
|
1493
1539
|
<ShoppingBag className="h-5 w-5" />
|
|
1494
|
-
|
|
1540
|
+
{tCart("yourOrder")}
|
|
1495
1541
|
</CardTitle>
|
|
1496
1542
|
</CardHeader>
|
|
1497
1543
|
<CardContent className="space-y-4">
|
|
1498
1544
|
{/* Line Items */}
|
|
1499
1545
|
<div className="space-y-3">
|
|
1500
|
-
{(checkout?.lineItems
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1546
|
+
{(checkout?.lineItems ?? []).length > 0
|
|
1547
|
+
? checkout!.lineItems.map((item) => (
|
|
1548
|
+
<div
|
|
1549
|
+
key={item.id}
|
|
1550
|
+
className="flex gap-3 text-sm"
|
|
1551
|
+
>
|
|
1552
|
+
<div className="relative h-16 w-16 flex-shrink-0 overflow-hidden rounded-md border bg-muted">
|
|
1553
|
+
{item.image?.url ? (
|
|
1554
|
+
<img
|
|
1555
|
+
src={item.image.url}
|
|
1556
|
+
alt={item.title}
|
|
1557
|
+
className="h-full w-full object-cover"
|
|
1558
|
+
/>
|
|
1559
|
+
) : (
|
|
1560
|
+
<div className="flex h-full w-full items-center justify-center">
|
|
1561
|
+
<Package className="h-6 w-6 text-muted-foreground" />
|
|
1562
|
+
</div>
|
|
1563
|
+
)}
|
|
1564
|
+
<span className="absolute -right-2 -top-2 flex h-5 w-5 items-center justify-center rounded-full bg-primary text-xs text-primary-foreground">
|
|
1565
|
+
{item.quantity}
|
|
1566
|
+
</span>
|
|
1515
1567
|
</div>
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
item.
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1568
|
+
<div className="flex-1">
|
|
1569
|
+
<p className="font-medium line-clamp-2">
|
|
1570
|
+
{item.title}
|
|
1571
|
+
</p>
|
|
1572
|
+
{item.variantTitle && item.variantTitle !== "Default" && (
|
|
1573
|
+
<p className="text-xs text-muted-foreground">{item.variantTitle}</p>
|
|
1574
|
+
)}
|
|
1575
|
+
</div>
|
|
1576
|
+
<p className="font-medium">
|
|
1577
|
+
{formatPrice(item.totalPrice.amount)}
|
|
1578
|
+
</p>
|
|
1579
|
+
</div>
|
|
1580
|
+
))
|
|
1581
|
+
: items.map((item) => (
|
|
1582
|
+
<div
|
|
1583
|
+
key={item.lineId}
|
|
1584
|
+
className="flex gap-3 text-sm"
|
|
1585
|
+
>
|
|
1586
|
+
<div className="relative h-16 w-16 flex-shrink-0 overflow-hidden rounded-md border bg-muted">
|
|
1587
|
+
{item.image?.url ? (
|
|
1588
|
+
<img
|
|
1589
|
+
src={item.image.url}
|
|
1590
|
+
alt={item.productTitle}
|
|
1591
|
+
className="h-full w-full object-cover"
|
|
1592
|
+
/>
|
|
1593
|
+
) : (
|
|
1594
|
+
<div className="flex h-full w-full items-center justify-center">
|
|
1595
|
+
<Package className="h-6 w-6 text-muted-foreground" />
|
|
1596
|
+
</div>
|
|
1597
|
+
)}
|
|
1598
|
+
<span className="absolute -right-2 -top-2 flex h-5 w-5 items-center justify-center rounded-full bg-primary text-xs text-primary-foreground">
|
|
1599
|
+
{item.quantity}
|
|
1600
|
+
</span>
|
|
1601
|
+
</div>
|
|
1602
|
+
<div className="flex-1">
|
|
1603
|
+
<p className="font-medium line-clamp-2">
|
|
1604
|
+
{item.productTitle}
|
|
1605
|
+
</p>
|
|
1606
|
+
{item.variantTitle && item.variantTitle !== "Default" && (
|
|
1607
|
+
<p className="text-xs text-muted-foreground">{item.variantTitle}</p>
|
|
1608
|
+
)}
|
|
1609
|
+
</div>
|
|
1610
|
+
<p className="font-medium">
|
|
1611
|
+
{formatPrice(
|
|
1612
|
+
parseFloat(item.price.amount) * item.quantity
|
|
1613
|
+
)}
|
|
1614
|
+
</p>
|
|
1615
|
+
</div>
|
|
1616
|
+
))
|
|
1617
|
+
}
|
|
1537
1618
|
</div>
|
|
1538
1619
|
|
|
1539
1620
|
{/* Discount Code */}
|
|
@@ -1560,7 +1641,7 @@ export default function CheckoutPage() {
|
|
|
1560
1641
|
) : (
|
|
1561
1642
|
<div className="flex gap-2">
|
|
1562
1643
|
<Input
|
|
1563
|
-
placeholder="
|
|
1644
|
+
placeholder={t("discount.codePlaceholder")}
|
|
1564
1645
|
value={formState.discountCode}
|
|
1565
1646
|
onChange={(e) =>
|
|
1566
1647
|
setFormState((prev) => ({ ...prev, discountCode: e.target.value }))
|
|
@@ -1575,7 +1656,7 @@ export default function CheckoutPage() {
|
|
|
1575
1656
|
{applyDiscount.isPending ? (
|
|
1576
1657
|
<Loader2 className="h-4 w-4 animate-spin" />
|
|
1577
1658
|
) : (
|
|
1578
|
-
"
|
|
1659
|
+
tc("apply")
|
|
1579
1660
|
)}
|
|
1580
1661
|
</Button>
|
|
1581
1662
|
</div>
|
|
@@ -1586,12 +1667,12 @@ export default function CheckoutPage() {
|
|
|
1586
1667
|
{/* Gift Card */}
|
|
1587
1668
|
{checkoutId && (
|
|
1588
1669
|
<div className="border-t pt-4 space-y-3">
|
|
1589
|
-
<p className="text-sm font-medium">
|
|
1670
|
+
<p className="text-sm font-medium">{tCart("giftCardLabel")}</p>
|
|
1590
1671
|
|
|
1591
1672
|
{/* Applied Gift Cards */}
|
|
1592
1673
|
{checkout?.appliedGiftCards && checkout.appliedGiftCards.length > 0 && (
|
|
1593
1674
|
<div className="space-y-2">
|
|
1594
|
-
{checkout.appliedGiftCards.map((giftCard
|
|
1675
|
+
{checkout.appliedGiftCards.map((giftCard) => (
|
|
1595
1676
|
<div
|
|
1596
1677
|
key={giftCard.lastCharacters}
|
|
1597
1678
|
className="flex items-center justify-between rounded-lg bg-purple-50 p-3 dark:bg-purple-950"
|
|
@@ -1624,7 +1705,7 @@ export default function CheckoutPage() {
|
|
|
1624
1705
|
{/* Gift Card Input */}
|
|
1625
1706
|
<div className="flex gap-2">
|
|
1626
1707
|
<Input
|
|
1627
|
-
placeholder="
|
|
1708
|
+
placeholder={tCart("giftCardPlaceholder")}
|
|
1628
1709
|
value={formState.giftCardCode}
|
|
1629
1710
|
onChange={(e) =>
|
|
1630
1711
|
setFormState((prev) => ({ ...prev, giftCardCode: e.target.value.toUpperCase() }))
|
|
@@ -1645,7 +1726,7 @@ export default function CheckoutPage() {
|
|
|
1645
1726
|
{applyGiftCard.isPending ? (
|
|
1646
1727
|
<Loader2 className="h-4 w-4 animate-spin" />
|
|
1647
1728
|
) : (
|
|
1648
|
-
"
|
|
1729
|
+
tc("apply")
|
|
1649
1730
|
)}
|
|
1650
1731
|
</Button>
|
|
1651
1732
|
</div>
|
|
@@ -1656,7 +1737,7 @@ export default function CheckoutPage() {
|
|
|
1656
1737
|
<div className="border-t pt-4 space-y-2">
|
|
1657
1738
|
{/* Subtotal */}
|
|
1658
1739
|
<div className="flex justify-between text-sm">
|
|
1659
|
-
<span className="text-muted-foreground">
|
|
1740
|
+
<span className="text-muted-foreground">{tCart("products")}</span>
|
|
1660
1741
|
<span>
|
|
1661
1742
|
{formatPrice(
|
|
1662
1743
|
checkout?.subtotalPrice?.amount ||
|
|
@@ -1671,20 +1752,20 @@ export default function CheckoutPage() {
|
|
|
1671
1752
|
{/* Shipping */}
|
|
1672
1753
|
{checkout?.shippingLine ? (
|
|
1673
1754
|
<div className="flex justify-between text-sm">
|
|
1674
|
-
<span className="text-muted-foreground">
|
|
1755
|
+
<span className="text-muted-foreground">{tCart("shipping")}</span>
|
|
1675
1756
|
<span>{formatPrice(checkout.totalShippingPrice?.amount || "0")}</span>
|
|
1676
1757
|
</div>
|
|
1677
1758
|
) : (
|
|
1678
1759
|
<div className="flex justify-between text-sm">
|
|
1679
|
-
<span className="text-muted-foreground">
|
|
1680
|
-
<span className="text-muted-foreground">
|
|
1760
|
+
<span className="text-muted-foreground">{tCart("shipping")}</span>
|
|
1761
|
+
<span className="text-muted-foreground">{tCart("calculatedNextStep")}</span>
|
|
1681
1762
|
</div>
|
|
1682
1763
|
)}
|
|
1683
1764
|
|
|
1684
1765
|
{/* Discount */}
|
|
1685
1766
|
{checkout?.totalDiscounts && parseFloat(checkout.totalDiscounts.amount) > 0 && (
|
|
1686
1767
|
<div className="flex justify-between text-sm text-green-600">
|
|
1687
|
-
<span>
|
|
1768
|
+
<span>{tCart("discount")}</span>
|
|
1688
1769
|
<span>-{formatPrice(checkout.totalDiscounts.amount)}</span>
|
|
1689
1770
|
</div>
|
|
1690
1771
|
)}
|
|
@@ -1692,7 +1773,7 @@ export default function CheckoutPage() {
|
|
|
1692
1773
|
{/* Tax */}
|
|
1693
1774
|
{checkout?.totalTax && parseFloat(checkout.totalTax.amount) > 0 && (
|
|
1694
1775
|
<div className="flex justify-between text-sm">
|
|
1695
|
-
<span className="text-muted-foreground">
|
|
1776
|
+
<span className="text-muted-foreground">{tCart("tax")}</span>
|
|
1696
1777
|
<span>{formatPrice(checkout.totalTax.amount)}</span>
|
|
1697
1778
|
</div>
|
|
1698
1779
|
)}
|
|
@@ -1700,14 +1781,14 @@ export default function CheckoutPage() {
|
|
|
1700
1781
|
{/* Gift Card Deduction */}
|
|
1701
1782
|
{checkout?.totalGiftCardAmount && parseFloat(checkout.totalGiftCardAmount.amount) > 0 && (
|
|
1702
1783
|
<div className="flex justify-between text-sm text-purple-600">
|
|
1703
|
-
<span>
|
|
1784
|
+
<span>{tCart("giftCard")}</span>
|
|
1704
1785
|
<span>-{formatPrice(checkout.totalGiftCardAmount.amount)}</span>
|
|
1705
1786
|
</div>
|
|
1706
1787
|
)}
|
|
1707
1788
|
|
|
1708
1789
|
{/* Total */}
|
|
1709
1790
|
<div className="flex justify-between border-t pt-2 text-lg font-semibold">
|
|
1710
|
-
<span>
|
|
1791
|
+
<span>{tCart("total")}</span>
|
|
1711
1792
|
<span>
|
|
1712
1793
|
{formatPrice(
|
|
1713
1794
|
checkout?.totalPrice?.amount ||
|
|
@@ -1724,7 +1805,7 @@ export default function CheckoutPage() {
|
|
|
1724
1805
|
checkout?.totalPrice &&
|
|
1725
1806
|
parseFloat(checkout.paymentDue.amount) !== parseFloat(checkout.totalPrice.amount) && (
|
|
1726
1807
|
<div className="flex justify-between border-t pt-2 text-lg font-bold text-primary">
|
|
1727
|
-
<span>
|
|
1808
|
+
<span>{tCart("paymentDue")}</span>
|
|
1728
1809
|
<span>{formatPrice(checkout.paymentDue.amount)}</span>
|
|
1729
1810
|
</div>
|
|
1730
1811
|
)}
|
|
@@ -1735,11 +1816,11 @@ export default function CheckoutPage() {
|
|
|
1735
1816
|
<div className="flex items-center justify-center gap-4 text-xs text-muted-foreground">
|
|
1736
1817
|
<div className="flex items-center gap-1">
|
|
1737
1818
|
<Check className="h-3 w-3" />
|
|
1738
|
-
|
|
1819
|
+
{tc("securePayment")}
|
|
1739
1820
|
</div>
|
|
1740
1821
|
<div className="flex items-center gap-1">
|
|
1741
1822
|
<Check className="h-3 w-3" />
|
|
1742
|
-
|
|
1823
|
+
{tc("encryptedData")}
|
|
1743
1824
|
</div>
|
|
1744
1825
|
</div>
|
|
1745
1826
|
</div>
|