@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
|
@@ -11,6 +11,7 @@ import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
|
|
11
11
|
import { Badge } from "@/components/ui/badge";
|
|
12
12
|
import { Progress } from "@/components/ui/progress";
|
|
13
13
|
import { cn } from "@/lib/utils";
|
|
14
|
+
import { useTranslations } from "next-intl";
|
|
14
15
|
|
|
15
16
|
/**
|
|
16
17
|
* Gift card status
|
|
@@ -72,35 +73,35 @@ function getStatusConfig(status: GiftCardStatus) {
|
|
|
72
73
|
switch (status) {
|
|
73
74
|
case "ACTIVE":
|
|
74
75
|
return {
|
|
75
|
-
|
|
76
|
+
translationKey: "active",
|
|
76
77
|
variant: "default" as const,
|
|
77
78
|
color: "text-green-600",
|
|
78
79
|
bgColor: "bg-green-100 dark:bg-green-900/30",
|
|
79
80
|
};
|
|
80
81
|
case "USED":
|
|
81
82
|
return {
|
|
82
|
-
|
|
83
|
+
translationKey: "used",
|
|
83
84
|
variant: "secondary" as const,
|
|
84
85
|
color: "text-gray-600",
|
|
85
86
|
bgColor: "bg-gray-100 dark:bg-gray-900/30",
|
|
86
87
|
};
|
|
87
88
|
case "EXPIRED":
|
|
88
89
|
return {
|
|
89
|
-
|
|
90
|
+
translationKey: "expired",
|
|
90
91
|
variant: "destructive" as const,
|
|
91
92
|
color: "text-red-600",
|
|
92
93
|
bgColor: "bg-red-100 dark:bg-red-900/30",
|
|
93
94
|
};
|
|
94
95
|
case "DISABLED":
|
|
95
96
|
return {
|
|
96
|
-
|
|
97
|
+
translationKey: "disabled",
|
|
97
98
|
variant: "secondary" as const,
|
|
98
99
|
color: "text-gray-600",
|
|
99
100
|
bgColor: "bg-gray-100 dark:bg-gray-900/30",
|
|
100
101
|
};
|
|
101
102
|
default:
|
|
102
103
|
return {
|
|
103
|
-
|
|
104
|
+
translationKey: "unknown",
|
|
104
105
|
variant: "secondary" as const,
|
|
105
106
|
color: "text-gray-600",
|
|
106
107
|
bgColor: "bg-gray-100 dark:bg-gray-900/30",
|
|
@@ -136,6 +137,7 @@ export function GiftCardBalance({
|
|
|
136
137
|
giftCard,
|
|
137
138
|
className = "",
|
|
138
139
|
}: GiftCardBalanceProps) {
|
|
140
|
+
const t = useTranslations("giftCardStatus");
|
|
139
141
|
const statusConfig = getStatusConfig(giftCard.status);
|
|
140
142
|
const initialAmount = parseFloat(giftCard.initialAmount.amount);
|
|
141
143
|
const currentBalance = parseFloat(giftCard.balance.amount);
|
|
@@ -153,8 +155,8 @@ export function GiftCardBalance({
|
|
|
153
155
|
</div>
|
|
154
156
|
<div>
|
|
155
157
|
<CardTitle className="text-lg flex items-center gap-2">
|
|
156
|
-
|
|
157
|
-
<Badge variant={statusConfig.variant}>{statusConfig.
|
|
158
|
+
{t("title")}
|
|
159
|
+
<Badge variant={statusConfig.variant}>{t(statusConfig.translationKey as any)}</Badge>
|
|
158
160
|
</CardTitle>
|
|
159
161
|
<p className="text-sm text-muted-foreground font-mono mt-1">
|
|
160
162
|
{giftCard.maskedCode}
|
|
@@ -168,7 +170,7 @@ export function GiftCardBalance({
|
|
|
168
170
|
{/* Balance display */}
|
|
169
171
|
<div className="space-y-2">
|
|
170
172
|
<div className="flex justify-between items-baseline">
|
|
171
|
-
<span className="text-sm text-muted-foreground">
|
|
173
|
+
<span className="text-sm text-muted-foreground">{t("availableBalance")}</span>
|
|
172
174
|
<span className="text-2xl font-bold">
|
|
173
175
|
{formatAmount(
|
|
174
176
|
giftCard.balance.amount,
|
|
@@ -182,14 +184,14 @@ export function GiftCardBalance({
|
|
|
182
184
|
|
|
183
185
|
<div className="flex justify-between text-xs text-muted-foreground">
|
|
184
186
|
<span>
|
|
185
|
-
|
|
187
|
+
{t("usedAmount")}{" "}
|
|
186
188
|
{formatAmount(
|
|
187
189
|
(initialAmount - currentBalance).toFixed(2),
|
|
188
190
|
giftCard.balance.currencyCode
|
|
189
191
|
)}
|
|
190
192
|
</span>
|
|
191
193
|
<span>
|
|
192
|
-
|
|
194
|
+
{t("initialAmount")}{" "}
|
|
193
195
|
{formatAmount(
|
|
194
196
|
giftCard.initialAmount.amount,
|
|
195
197
|
giftCard.initialAmount.currencyCode
|
|
@@ -214,8 +216,9 @@ export function GiftCardBalance({
|
|
|
214
216
|
<Calendar className="h-4 w-4 text-muted-foreground" />
|
|
215
217
|
)}
|
|
216
218
|
<span>
|
|
217
|
-
{expiringSoon
|
|
218
|
-
|
|
219
|
+
{expiringSoon
|
|
220
|
+
? t("expiresSoon", { date: formatDate(giftCard.expiresAt) })
|
|
221
|
+
: t("expiresOn", { date: formatDate(giftCard.expiresAt) })}
|
|
219
222
|
</span>
|
|
220
223
|
</div>
|
|
221
224
|
)}
|
|
@@ -224,7 +227,7 @@ export function GiftCardBalance({
|
|
|
224
227
|
{!giftCard.expiresAt && giftCard.status === "ACTIVE" && (
|
|
225
228
|
<div className="flex items-center gap-2 text-sm text-muted-foreground">
|
|
226
229
|
<CheckCircle className="h-4 w-4 text-green-500" />
|
|
227
|
-
<span>
|
|
230
|
+
<span>{t("noExpiration")}</span>
|
|
228
231
|
</div>
|
|
229
232
|
)}
|
|
230
233
|
|
|
@@ -233,7 +236,7 @@ export function GiftCardBalance({
|
|
|
233
236
|
<div className="pt-3 border-t border-border space-y-2">
|
|
234
237
|
{giftCard.recipientName && (
|
|
235
238
|
<p className="text-sm">
|
|
236
|
-
<span className="text-muted-foreground">
|
|
239
|
+
<span className="text-muted-foreground">{t("recipientLabel")} </span>
|
|
237
240
|
<span className="font-medium">{giftCard.recipientName}</span>
|
|
238
241
|
</p>
|
|
239
242
|
)}
|
|
@@ -297,6 +300,7 @@ export function GiftCardBalanceCompact({
|
|
|
297
300
|
status,
|
|
298
301
|
className = "",
|
|
299
302
|
}: GiftCardBalanceCompactProps) {
|
|
303
|
+
const t = useTranslations("giftCardStatus");
|
|
300
304
|
const statusConfig = getStatusConfig(status);
|
|
301
305
|
|
|
302
306
|
return (
|
|
@@ -310,7 +314,7 @@ export function GiftCardBalanceCompact({
|
|
|
310
314
|
<CreditCard className="h-4 w-4 text-muted-foreground" />
|
|
311
315
|
<span className="font-mono text-sm">{maskedCode}</span>
|
|
312
316
|
<Badge variant={statusConfig.variant} className="text-xs">
|
|
313
|
-
{statusConfig.
|
|
317
|
+
{t(statusConfig.translationKey as any)}
|
|
314
318
|
</Badge>
|
|
315
319
|
</div>
|
|
316
320
|
<span className="font-semibold">
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
3
|
import { useState, useCallback } from "react";
|
|
4
|
+
import { useTranslations } from "next-intl";
|
|
4
5
|
import { Input } from "@/components/ui/input";
|
|
5
6
|
import { Button } from "@/components/ui/button";
|
|
6
7
|
import { Badge } from "@/components/ui/badge";
|
|
@@ -89,6 +90,8 @@ export function GiftCardInput({
|
|
|
89
90
|
disabled = false,
|
|
90
91
|
className = "",
|
|
91
92
|
}: GiftCardInputProps) {
|
|
93
|
+
const t = useTranslations("cart");
|
|
94
|
+
const tc = useTranslations("common");
|
|
92
95
|
const [code, setCode] = useState("");
|
|
93
96
|
const [isValidating, setIsValidating] = useState(false);
|
|
94
97
|
const [validation, setValidation] = useState<GiftCardValidationResult | null>(
|
|
@@ -124,13 +127,13 @@ export function GiftCardInput({
|
|
|
124
127
|
*/
|
|
125
128
|
const handleValidate = useCallback(async () => {
|
|
126
129
|
if (!code || code.length < 4) {
|
|
127
|
-
setError("
|
|
130
|
+
setError(t("giftCardInvalidCode"));
|
|
128
131
|
return;
|
|
129
132
|
}
|
|
130
133
|
|
|
131
134
|
// Check if already applied
|
|
132
135
|
if (appliedCodes.includes(code)) {
|
|
133
|
-
setError("
|
|
136
|
+
setError(t("giftCardAlreadyApplied"));
|
|
134
137
|
return;
|
|
135
138
|
}
|
|
136
139
|
|
|
@@ -144,8 +147,8 @@ export function GiftCardInput({
|
|
|
144
147
|
if (!result.valid && result.error) {
|
|
145
148
|
setError(result.error.message);
|
|
146
149
|
}
|
|
147
|
-
} catch (err:
|
|
148
|
-
setError(err
|
|
150
|
+
} catch (err: unknown) {
|
|
151
|
+
setError(err instanceof Error ? err.message : t("giftCardValidationFailed"));
|
|
149
152
|
} finally {
|
|
150
153
|
setIsValidating(false);
|
|
151
154
|
}
|
|
@@ -187,7 +190,7 @@ export function GiftCardInput({
|
|
|
187
190
|
<Gift className="absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground" />
|
|
188
191
|
<Input
|
|
189
192
|
type="text"
|
|
190
|
-
placeholder="
|
|
193
|
+
placeholder={t("giftCardCodePlaceholder")}
|
|
191
194
|
value={code}
|
|
192
195
|
onChange={handleCodeChange}
|
|
193
196
|
onKeyDown={handleKeyDown}
|
|
@@ -207,10 +210,10 @@ export function GiftCardInput({
|
|
|
207
210
|
) : validation?.valid ? (
|
|
208
211
|
<>
|
|
209
212
|
<Check className="mr-2 h-4 w-4" />
|
|
210
|
-
|
|
213
|
+
{tc("apply")}
|
|
211
214
|
</>
|
|
212
215
|
) : (
|
|
213
|
-
"
|
|
216
|
+
t("giftCardCheck")
|
|
214
217
|
)}
|
|
215
218
|
</Button>
|
|
216
219
|
</div>
|
|
@@ -229,7 +232,7 @@ export function GiftCardInput({
|
|
|
229
232
|
<div className="flex items-center justify-between">
|
|
230
233
|
<div className="flex items-center gap-2">
|
|
231
234
|
<Check className="h-4 w-4" />
|
|
232
|
-
<span>
|
|
235
|
+
<span>{t("giftCardValid")}</span>
|
|
233
236
|
</div>
|
|
234
237
|
{validation.availableBalance && (
|
|
235
238
|
<span className="font-semibold">
|
|
@@ -244,7 +247,7 @@ export function GiftCardInput({
|
|
|
244
247
|
) : (
|
|
245
248
|
<div className="flex items-center gap-2">
|
|
246
249
|
<X className="h-4 w-4" />
|
|
247
|
-
<span>{validation.error?.message || "
|
|
250
|
+
<span>{validation.error?.message || t("giftCardInvalid")}</span>
|
|
248
251
|
</div>
|
|
249
252
|
)}
|
|
250
253
|
</div>
|
|
@@ -262,7 +265,7 @@ export function GiftCardInput({
|
|
|
262
265
|
{appliedCodes.length > 0 && (
|
|
263
266
|
<div className="space-y-2">
|
|
264
267
|
<p className="text-sm font-medium text-muted-foreground">
|
|
265
|
-
|
|
268
|
+
{t("appliedGiftCards")}
|
|
266
269
|
</p>
|
|
267
270
|
<div className="flex flex-wrap gap-2">
|
|
268
271
|
{appliedCodes.map((appliedCode) => (
|
|
@@ -1,14 +1,18 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
|
-
import
|
|
3
|
+
import { useTranslations } from "next-intl";
|
|
4
|
+
import { Link } from "@/i18n/navigation";
|
|
4
5
|
import { cn } from "@/lib/utils";
|
|
5
6
|
import { useCollections } from "@/lib/graphql/hooks";
|
|
7
|
+
import type { CollectionCardFields } from "@/lib/graphql/fragments";
|
|
6
8
|
|
|
7
9
|
export interface CategoryGridProps {
|
|
8
10
|
className?: string;
|
|
9
11
|
}
|
|
10
12
|
|
|
11
13
|
export function CategoryGrid({ className }: CategoryGridProps) {
|
|
14
|
+
const t = useTranslations("home");
|
|
15
|
+
|
|
12
16
|
// Use collections for homepage marketing (not categories)
|
|
13
17
|
// Collections are better for homepage because they're curated and flexible
|
|
14
18
|
const { data, isLoading, error } = useCollections({
|
|
@@ -21,9 +25,9 @@ export function CategoryGrid({ className }: CategoryGridProps) {
|
|
|
21
25
|
return (
|
|
22
26
|
<section className={cn("container mx-auto px-4", className)}>
|
|
23
27
|
<div className="mb-8 text-center">
|
|
24
|
-
<h2 className="text-3xl font-bold text-foreground">
|
|
28
|
+
<h2 className="text-3xl font-bold text-foreground">{t("shopByCategory")}</h2>
|
|
25
29
|
<p className="mt-2 text-muted-foreground">
|
|
26
|
-
|
|
30
|
+
{t("shopByCategoryDescription")}
|
|
27
31
|
</p>
|
|
28
32
|
</div>
|
|
29
33
|
<div className="grid grid-cols-2 gap-4 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-4">
|
|
@@ -45,14 +49,14 @@ export function CategoryGrid({ className }: CategoryGridProps) {
|
|
|
45
49
|
return (
|
|
46
50
|
<section className={cn("container mx-auto px-4", className)}>
|
|
47
51
|
<div className="mb-8 text-center">
|
|
48
|
-
<h2 className="text-3xl font-bold text-foreground">
|
|
52
|
+
<h2 className="text-3xl font-bold text-foreground">{t("shopByCategory")}</h2>
|
|
49
53
|
<p className="mt-2 text-muted-foreground">
|
|
50
|
-
|
|
54
|
+
{t("shopByCategoryDescription")}
|
|
51
55
|
</p>
|
|
52
56
|
</div>
|
|
53
57
|
|
|
54
58
|
<div className="grid grid-cols-2 gap-4 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-4">
|
|
55
|
-
{collections.map((collection:
|
|
59
|
+
{collections.map((collection: CollectionCardFields) => (
|
|
56
60
|
<Link
|
|
57
61
|
key={collection.id}
|
|
58
62
|
href={`/collections/${collection.handle}`}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# Fragment for CollectionCard component.
|
|
2
|
+
#
|
|
3
|
+
# Minimal collection data for listing views: homepage,
|
|
4
|
+
# collection grid, navigation menus.
|
|
5
|
+
#
|
|
6
|
+
# Usage in components:
|
|
7
|
+
# import type { CollectionCardFieldsFragment } from '@/generated/graphql';
|
|
8
|
+
# interface Props { collection: CollectionCardFieldsFragment }
|
|
9
|
+
|
|
10
|
+
fragment CollectionCardFields on Collection {
|
|
11
|
+
id
|
|
12
|
+
handle
|
|
13
|
+
title
|
|
14
|
+
description
|
|
15
|
+
image {
|
|
16
|
+
url
|
|
17
|
+
altText
|
|
18
|
+
width
|
|
19
|
+
height
|
|
20
|
+
}
|
|
21
|
+
}
|
|
@@ -1,20 +1,10 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
|
-
import Link from "
|
|
3
|
+
import { Link } from "@/i18n/navigation";
|
|
4
4
|
import { Button } from "@/components/ui/button";
|
|
5
5
|
import { Card, CardContent } from "@/components/ui/card";
|
|
6
6
|
import { useCollections } from "@/lib/graphql/hooks";
|
|
7
|
-
|
|
8
|
-
export interface Collection {
|
|
9
|
-
id: string;
|
|
10
|
-
handle: string;
|
|
11
|
-
title: string;
|
|
12
|
-
description?: string;
|
|
13
|
-
image?: {
|
|
14
|
-
url: string;
|
|
15
|
-
altText?: string | null;
|
|
16
|
-
} | null;
|
|
17
|
-
}
|
|
7
|
+
import type { CollectionCardFields } from "@/lib/graphql/fragments";
|
|
18
8
|
|
|
19
9
|
export function FeaturedCollections() {
|
|
20
10
|
// Fetch collections using GraphQL
|
|
@@ -61,7 +51,7 @@ export function FeaturedCollections() {
|
|
|
61
51
|
|
|
62
52
|
{collections.length > 0 ? (
|
|
63
53
|
<div className="grid gap-6 md:grid-cols-2 lg:grid-cols-3">
|
|
64
|
-
{collections.map((collection:
|
|
54
|
+
{collections.map((collection: CollectionCardFields) => (
|
|
65
55
|
<Link
|
|
66
56
|
key={collection.id}
|
|
67
57
|
href={`/collections/${collection.handle}`}
|
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
|
-
import
|
|
3
|
+
import { useTranslations } from "next-intl";
|
|
4
|
+
import { Link } from "@/i18n/navigation";
|
|
4
5
|
import { ProductGrid } from "@/components/product/product-grid";
|
|
5
6
|
import { Button } from "@/components/ui/button";
|
|
6
7
|
import { useProducts } from "@/lib/graphql/hooks";
|
|
7
8
|
|
|
8
9
|
export function FeaturedProducts() {
|
|
10
|
+
const t = useTranslations("home");
|
|
11
|
+
const tc = useTranslations("common");
|
|
12
|
+
|
|
9
13
|
// Fetch featured products using GraphQL
|
|
10
14
|
const { data, isLoading, error } = useProducts({
|
|
11
15
|
first: 8,
|
|
@@ -19,9 +23,9 @@ export function FeaturedProducts() {
|
|
|
19
23
|
<section className="container mx-auto px-4">
|
|
20
24
|
<div className="mb-8 flex items-center justify-between">
|
|
21
25
|
<div>
|
|
22
|
-
<h2 className="text-3xl font-bold text-foreground">
|
|
26
|
+
<h2 className="text-3xl font-bold text-foreground">{t("featuredProducts")}</h2>
|
|
23
27
|
<p className="mt-2 text-muted-foreground">
|
|
24
|
-
|
|
28
|
+
{t("featuredDescription")}
|
|
25
29
|
</p>
|
|
26
30
|
</div>
|
|
27
31
|
</div>
|
|
@@ -53,13 +57,13 @@ export function FeaturedProducts() {
|
|
|
53
57
|
<section className="container mx-auto px-4">
|
|
54
58
|
<div className="mb-8 flex items-center justify-between">
|
|
55
59
|
<div>
|
|
56
|
-
<h2 className="text-3xl font-bold text-foreground">
|
|
60
|
+
<h2 className="text-3xl font-bold text-foreground">{t("featuredProducts")}</h2>
|
|
57
61
|
<p className="mt-2 text-muted-foreground">
|
|
58
|
-
|
|
62
|
+
{t("featuredDescription")}
|
|
59
63
|
</p>
|
|
60
64
|
</div>
|
|
61
65
|
<Button variant="outline" asChild>
|
|
62
|
-
<Link href="/products">
|
|
66
|
+
<Link href="/products">{tc("viewAll")}</Link>
|
|
63
67
|
</Button>
|
|
64
68
|
</div>
|
|
65
69
|
|
|
@@ -73,10 +77,10 @@ export function FeaturedProducts() {
|
|
|
73
77
|
) : (
|
|
74
78
|
<div className="rounded-lg border border-border bg-muted/50 p-12 text-center">
|
|
75
79
|
<p className="text-muted-foreground">
|
|
76
|
-
|
|
80
|
+
{t("noFeaturedProducts")}
|
|
77
81
|
</p>
|
|
78
82
|
<Button className="mt-4" asChild>
|
|
79
|
-
<Link href="/products">
|
|
83
|
+
<Link href="/products">{t("browseAllProducts")}</Link>
|
|
80
84
|
</Button>
|
|
81
85
|
</div>
|
|
82
86
|
)}
|
|
@@ -1,26 +1,31 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { useTranslations } from "next-intl";
|
|
2
|
+
import { Link } from "@/i18n/navigation";
|
|
2
3
|
import { Button } from "@/components/ui/button";
|
|
3
4
|
|
|
4
5
|
export function HeroSection() {
|
|
6
|
+
const t = useTranslations("home");
|
|
7
|
+
|
|
5
8
|
return (
|
|
6
9
|
<section className="container mx-auto px-4">
|
|
7
10
|
<div className="relative overflow-hidden rounded-2xl bg-gradient-to-r from-primary/10 via-primary/5 to-background p-8 md:p-16">
|
|
8
11
|
<div className="relative z-10 mx-auto max-w-3xl text-center">
|
|
9
12
|
<h1 className="mb-4 text-4xl font-bold tracking-tight text-foreground sm:text-5xl md:text-6xl">
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
13
|
+
{t.rich("welcomeTo", {
|
|
14
|
+
storeName: process.env.NEXT_PUBLIC_SITE_NAME || "Store",
|
|
15
|
+
highlight: (chunks) => (
|
|
16
|
+
<span className="text-primary">{chunks}</span>
|
|
17
|
+
),
|
|
18
|
+
})}
|
|
14
19
|
</h1>
|
|
15
20
|
<p className="mx-auto mb-8 max-w-2xl text-lg text-muted-foreground">
|
|
16
|
-
|
|
21
|
+
{t("heroDescription")}
|
|
17
22
|
</p>
|
|
18
23
|
<div className="flex flex-col justify-center gap-4 sm:flex-row">
|
|
19
24
|
<Button size="lg" asChild>
|
|
20
|
-
<Link href="/products">
|
|
25
|
+
<Link href="/products">{t("shopNow")}</Link>
|
|
21
26
|
</Button>
|
|
22
27
|
<Button size="lg" variant="outline" asChild>
|
|
23
|
-
<Link href="/products">
|
|
28
|
+
<Link href="/products">{t("browseProducts")}</Link>
|
|
24
29
|
</Button>
|
|
25
30
|
</div>
|
|
26
31
|
</div>
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
export { HeroSection } from './hero-section';
|
|
2
2
|
export { FeaturedProducts } from './featured-products';
|
|
3
3
|
export { FeaturedCollections } from './featured-collections';
|
|
4
|
-
export type { Collection } from './featured-collections';
|
|
5
4
|
export { CategoryGrid } from './category-grid';
|
|
6
5
|
export type { CategoryGridProps } from './category-grid';
|
|
7
6
|
export { NewsletterSignup } from './newsletter-signup';
|
|
@@ -5,12 +5,14 @@ import { Mail, Check } from "lucide-react";
|
|
|
5
5
|
import { Input } from "@/components/ui/input";
|
|
6
6
|
import { Button } from "@/components/ui/button";
|
|
7
7
|
import { cn } from "@/lib/utils";
|
|
8
|
+
import { useTranslations } from "next-intl";
|
|
8
9
|
|
|
9
10
|
export interface NewsletterSignupProps {
|
|
10
11
|
className?: string;
|
|
11
12
|
}
|
|
12
13
|
|
|
13
14
|
export function NewsletterSignup({ className }: NewsletterSignupProps) {
|
|
15
|
+
const t = useTranslations("footer");
|
|
14
16
|
const [email, setEmail] = useState("");
|
|
15
17
|
const [isLoading, setIsLoading] = useState(false);
|
|
16
18
|
const [isSuccess, setIsSuccess] = useState(false);
|
|
@@ -20,7 +22,7 @@ export function NewsletterSignup({ className }: NewsletterSignupProps) {
|
|
|
20
22
|
e.preventDefault();
|
|
21
23
|
|
|
22
24
|
if (!email || !email.includes("@")) {
|
|
23
|
-
setError("
|
|
25
|
+
setError(t("invalidEmail"));
|
|
24
26
|
return;
|
|
25
27
|
}
|
|
26
28
|
|
|
@@ -42,7 +44,7 @@ export function NewsletterSignup({ className }: NewsletterSignupProps) {
|
|
|
42
44
|
setIsSuccess(false);
|
|
43
45
|
}, 3000);
|
|
44
46
|
} catch (err) {
|
|
45
|
-
setError("
|
|
47
|
+
setError(t("failedSubscribe"));
|
|
46
48
|
} finally {
|
|
47
49
|
setIsLoading(false);
|
|
48
50
|
}
|
|
@@ -57,17 +59,17 @@ export function NewsletterSignup({ className }: NewsletterSignupProps) {
|
|
|
57
59
|
</div>
|
|
58
60
|
|
|
59
61
|
<h2 className="mb-2 text-3xl font-bold text-foreground">
|
|
60
|
-
|
|
62
|
+
{t("stayInLoop")}
|
|
61
63
|
</h2>
|
|
62
64
|
<p className="mb-6 text-muted-foreground">
|
|
63
|
-
|
|
65
|
+
{t("newsletterLongDescription")}
|
|
64
66
|
</p>
|
|
65
67
|
|
|
66
68
|
{isSuccess ? (
|
|
67
69
|
<div className="flex items-center justify-center gap-2 rounded-lg bg-green-50 p-4 text-green-700 dark:bg-green-950 dark:text-green-400">
|
|
68
70
|
<Check className="h-5 w-5" />
|
|
69
71
|
<span className="font-medium">
|
|
70
|
-
|
|
72
|
+
{t("thanksForSubscribing")}
|
|
71
73
|
</span>
|
|
72
74
|
</div>
|
|
73
75
|
) : (
|
|
@@ -75,7 +77,7 @@ export function NewsletterSignup({ className }: NewsletterSignupProps) {
|
|
|
75
77
|
<div className="flex flex-col gap-3 sm:flex-row">
|
|
76
78
|
<Input
|
|
77
79
|
type="email"
|
|
78
|
-
placeholder="
|
|
80
|
+
placeholder={t("emailPlaceholder")}
|
|
79
81
|
value={email}
|
|
80
82
|
onChange={(e) => setEmail(e.target.value)}
|
|
81
83
|
disabled={isLoading}
|
|
@@ -88,7 +90,7 @@ export function NewsletterSignup({ className }: NewsletterSignupProps) {
|
|
|
88
90
|
size="lg"
|
|
89
91
|
className="sm:w-auto"
|
|
90
92
|
>
|
|
91
|
-
{isLoading ? "
|
|
93
|
+
{isLoading ? t("subscribing") : t("subscribe")}
|
|
92
94
|
</Button>
|
|
93
95
|
</div>
|
|
94
96
|
|
|
@@ -97,7 +99,7 @@ export function NewsletterSignup({ className }: NewsletterSignupProps) {
|
|
|
97
99
|
)}
|
|
98
100
|
|
|
99
101
|
<p className="mt-3 text-xs text-muted-foreground">
|
|
100
|
-
|
|
102
|
+
{t("privacyConsent")}
|
|
101
103
|
</p>
|
|
102
104
|
</form>
|
|
103
105
|
)}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { useHydrated } from "@doswiftly/storefront-sdk/react";
|
|
4
|
+
import type { ReactNode } from "react";
|
|
5
|
+
|
|
6
|
+
interface HydratedProps {
|
|
7
|
+
children: ReactNode;
|
|
8
|
+
fallback?: ReactNode;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Wrapper component that only renders children after client hydration.
|
|
13
|
+
* Use for content that depends on browser-only state (localStorage, Zustand persist).
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* ```tsx
|
|
17
|
+
* <Hydrated fallback={<Skeleton className="w-20 h-8" />}>
|
|
18
|
+
* <CartIcon />
|
|
19
|
+
* </Hydrated>
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
export function Hydrated({ children, fallback = null }: HydratedProps) {
|
|
23
|
+
return useHydrated() ? children : fallback;
|
|
24
|
+
}
|