@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
|
@@ -4,6 +4,7 @@ import { MapPin, Edit, Trash2, Plus } from "lucide-react";
|
|
|
4
4
|
import { Button } from "@/components/ui/button";
|
|
5
5
|
import { Badge } from "@/components/ui/badge";
|
|
6
6
|
import { Card } from "@/components/ui/card";
|
|
7
|
+
import { useTranslations } from "next-intl";
|
|
7
8
|
|
|
8
9
|
export interface Address {
|
|
9
10
|
id: string;
|
|
@@ -40,11 +41,11 @@ export function AddressList({
|
|
|
40
41
|
onAdd,
|
|
41
42
|
className,
|
|
42
43
|
}: AddressListProps) {
|
|
44
|
+
const t = useTranslations("account");
|
|
45
|
+
|
|
43
46
|
const handleDelete = (id: string, address: Address) => {
|
|
44
47
|
if (
|
|
45
|
-
confirm(
|
|
46
|
-
`Are you sure you want to delete the address at ${address.address1}?`
|
|
47
|
-
)
|
|
48
|
+
confirm(t("deleteAddressConfirm", { address: address.address1 }))
|
|
48
49
|
) {
|
|
49
50
|
onDelete?.(id);
|
|
50
51
|
}
|
|
@@ -55,15 +56,15 @@ export function AddressList({
|
|
|
55
56
|
<div className="rounded-lg border border-border bg-muted/50 p-12 text-center">
|
|
56
57
|
<MapPin className="mx-auto h-12 w-12 text-muted-foreground mb-4" />
|
|
57
58
|
<h2 className="text-xl font-semibold text-foreground mb-2">
|
|
58
|
-
|
|
59
|
+
{t("noAddressesSaved")}
|
|
59
60
|
</h2>
|
|
60
61
|
<p className="text-muted-foreground mb-6">
|
|
61
|
-
|
|
62
|
+
{t("addAddressDescription")}
|
|
62
63
|
</p>
|
|
63
64
|
{onAdd && (
|
|
64
65
|
<Button onClick={onAdd}>
|
|
65
66
|
<Plus className="mr-2 h-4 w-4" />
|
|
66
|
-
|
|
67
|
+
{t("addFirstAddress")}
|
|
67
68
|
</Button>
|
|
68
69
|
)}
|
|
69
70
|
</div>
|
|
@@ -78,7 +79,7 @@ export function AddressList({
|
|
|
78
79
|
<div className="mb-4 flex items-start justify-between">
|
|
79
80
|
<div className="flex items-center gap-2">
|
|
80
81
|
<MapPin className="h-5 w-5 text-muted-foreground" />
|
|
81
|
-
{address.isDefault && <Badge variant="default">
|
|
82
|
+
{address.isDefault && <Badge variant="default">{t("defaultBadge")}</Badge>}
|
|
82
83
|
</div>
|
|
83
84
|
<div className="flex gap-2">
|
|
84
85
|
{onEdit && (
|
|
@@ -87,7 +88,7 @@ export function AddressList({
|
|
|
87
88
|
size="sm"
|
|
88
89
|
className="h-8 w-8 p-0"
|
|
89
90
|
onClick={() => onEdit(address)}
|
|
90
|
-
aria-label="
|
|
91
|
+
aria-label={t("editAddress")}
|
|
91
92
|
>
|
|
92
93
|
<Edit className="h-4 w-4" />
|
|
93
94
|
</Button>
|
|
@@ -98,7 +99,7 @@ export function AddressList({
|
|
|
98
99
|
size="sm"
|
|
99
100
|
className="h-8 w-8 p-0 text-destructive hover:text-destructive"
|
|
100
101
|
onClick={() => handleDelete(address.id, address)}
|
|
101
|
-
aria-label="
|
|
102
|
+
aria-label={t("deleteAddress")}
|
|
102
103
|
>
|
|
103
104
|
<Trash2 className="h-4 w-4" />
|
|
104
105
|
</Button>
|
|
@@ -133,7 +134,7 @@ export function AddressList({
|
|
|
133
134
|
className="mt-4 w-full"
|
|
134
135
|
onClick={() => onSetDefault(address.id)}
|
|
135
136
|
>
|
|
136
|
-
|
|
137
|
+
{t("setAsDefault")}
|
|
137
138
|
</Button>
|
|
138
139
|
)}
|
|
139
140
|
</Card>
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# Fragment for CustomerInfo / AccountDashboard components.
|
|
2
|
+
#
|
|
3
|
+
# Customer profile data for account pages.
|
|
4
|
+
# Does NOT include orders or addresses (fetch separately).
|
|
5
|
+
#
|
|
6
|
+
# Usage in components:
|
|
7
|
+
# import type { CustomerInfoFieldsFragment } from '@/generated/graphql';
|
|
8
|
+
# interface Props { customer: CustomerInfoFieldsFragment }
|
|
9
|
+
|
|
10
|
+
fragment CustomerInfoFields on Customer {
|
|
11
|
+
id
|
|
12
|
+
email
|
|
13
|
+
firstName
|
|
14
|
+
lastName
|
|
15
|
+
displayName
|
|
16
|
+
phone
|
|
17
|
+
emailVerified
|
|
18
|
+
ordersCount
|
|
19
|
+
totalSpent {
|
|
20
|
+
amount
|
|
21
|
+
currencyCode
|
|
22
|
+
}
|
|
23
|
+
defaultAddress {
|
|
24
|
+
id
|
|
25
|
+
firstName
|
|
26
|
+
lastName
|
|
27
|
+
address1
|
|
28
|
+
address2
|
|
29
|
+
city
|
|
30
|
+
province
|
|
31
|
+
zip
|
|
32
|
+
country
|
|
33
|
+
phone
|
|
34
|
+
}
|
|
35
|
+
createdAt
|
|
36
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
3
|
import { Package, Truck, CheckCircle, XCircle } from "lucide-react";
|
|
4
|
+
import { useTranslations } from "next-intl";
|
|
4
5
|
import { Badge } from "@/components/ui/badge";
|
|
5
6
|
import { Card } from "@/components/ui/card";
|
|
6
7
|
import { Separator } from "@/components/ui/separator";
|
|
@@ -39,7 +40,7 @@ export interface OrderDetailsData {
|
|
|
39
40
|
tax: string;
|
|
40
41
|
total: string;
|
|
41
42
|
currency: string;
|
|
42
|
-
shippingAddress
|
|
43
|
+
shippingAddress?: OrderAddress;
|
|
43
44
|
billingAddress?: OrderAddress;
|
|
44
45
|
trackingNumber?: string;
|
|
45
46
|
estimatedDelivery?: string;
|
|
@@ -54,6 +55,7 @@ export interface OrderDetailsProps {
|
|
|
54
55
|
* OrderDetails - Display detailed information about a single order
|
|
55
56
|
*/
|
|
56
57
|
export function OrderDetails({ order, className }: OrderDetailsProps) {
|
|
58
|
+
const t = useTranslations("account");
|
|
57
59
|
const getStatusIcon = (status: OrderDetailsData["status"]) => {
|
|
58
60
|
switch (status) {
|
|
59
61
|
case "delivered":
|
|
@@ -131,10 +133,10 @@ export function OrderDetails({ order, className }: OrderDetailsProps) {
|
|
|
131
133
|
<div className="flex items-start justify-between mb-4">
|
|
132
134
|
<div>
|
|
133
135
|
<h2 className="text-2xl font-bold text-foreground mb-2">
|
|
134
|
-
|
|
136
|
+
{t("orderNumber", { number: order.orderNumber })}
|
|
135
137
|
</h2>
|
|
136
138
|
<p className="text-sm text-muted-foreground">
|
|
137
|
-
|
|
139
|
+
{t("placedOn")} {formatDate(order.date)}
|
|
138
140
|
</p>
|
|
139
141
|
</div>
|
|
140
142
|
<div className="flex items-center gap-2">
|
|
@@ -146,7 +148,7 @@ export function OrderDetails({ order, className }: OrderDetailsProps) {
|
|
|
146
148
|
{order.trackingNumber && (
|
|
147
149
|
<div className="mt-4 p-4 bg-muted/50 rounded-lg">
|
|
148
150
|
<p className="text-sm font-medium text-foreground mb-1">
|
|
149
|
-
|
|
151
|
+
{t("trackingNumber")}
|
|
150
152
|
</p>
|
|
151
153
|
<p className="text-sm text-muted-foreground font-mono">
|
|
152
154
|
{order.trackingNumber}
|
|
@@ -157,7 +159,7 @@ export function OrderDetails({ order, className }: OrderDetailsProps) {
|
|
|
157
159
|
{order.estimatedDelivery && (
|
|
158
160
|
<div className="mt-4 p-4 bg-muted/50 rounded-lg">
|
|
159
161
|
<p className="text-sm font-medium text-foreground mb-1">
|
|
160
|
-
|
|
162
|
+
{t("estimatedDelivery")}
|
|
161
163
|
</p>
|
|
162
164
|
<p className="text-sm text-muted-foreground">
|
|
163
165
|
{formatDate(order.estimatedDelivery)}
|
|
@@ -169,7 +171,7 @@ export function OrderDetails({ order, className }: OrderDetailsProps) {
|
|
|
169
171
|
{/* Order Items */}
|
|
170
172
|
<Card className="p-6 mb-6">
|
|
171
173
|
<h3 className="text-lg font-semibold text-foreground mb-4">
|
|
172
|
-
|
|
174
|
+
{t("orderItems")}
|
|
173
175
|
</h3>
|
|
174
176
|
<div className="space-y-4">
|
|
175
177
|
{order.items.map((item) => (
|
|
@@ -191,7 +193,7 @@ export function OrderDetails({ order, className }: OrderDetailsProps) {
|
|
|
191
193
|
</p>
|
|
192
194
|
)}
|
|
193
195
|
<p className="text-sm text-muted-foreground">
|
|
194
|
-
|
|
196
|
+
{t("itemCount", { count: item.quantity })}
|
|
195
197
|
</p>
|
|
196
198
|
</div>
|
|
197
199
|
<div className="text-right">
|
|
@@ -208,26 +210,26 @@ export function OrderDetails({ order, className }: OrderDetailsProps) {
|
|
|
208
210
|
{/* Order Summary */}
|
|
209
211
|
<div className="space-y-2">
|
|
210
212
|
<div className="flex justify-between text-sm">
|
|
211
|
-
<span className="text-muted-foreground">
|
|
213
|
+
<span className="text-muted-foreground">{t("subtotal")}</span>
|
|
212
214
|
<span className="text-foreground">
|
|
213
215
|
{formatPrice(order.subtotal, order.currency)}
|
|
214
216
|
</span>
|
|
215
217
|
</div>
|
|
216
218
|
<div className="flex justify-between text-sm">
|
|
217
|
-
<span className="text-muted-foreground">
|
|
219
|
+
<span className="text-muted-foreground">{t("shippingLabel")}</span>
|
|
218
220
|
<span className="text-foreground">
|
|
219
221
|
{formatPrice(order.shipping, order.currency)}
|
|
220
222
|
</span>
|
|
221
223
|
</div>
|
|
222
224
|
<div className="flex justify-between text-sm">
|
|
223
|
-
<span className="text-muted-foreground">
|
|
225
|
+
<span className="text-muted-foreground">{t("taxLabel")}</span>
|
|
224
226
|
<span className="text-foreground">
|
|
225
227
|
{formatPrice(order.tax, order.currency)}
|
|
226
228
|
</span>
|
|
227
229
|
</div>
|
|
228
230
|
<Separator className="my-2" />
|
|
229
231
|
<div className="flex justify-between text-base font-semibold">
|
|
230
|
-
<span className="text-foreground">
|
|
232
|
+
<span className="text-foreground">{t("orderTotal")}</span>
|
|
231
233
|
<span className="text-foreground">
|
|
232
234
|
{formatPrice(order.total, order.currency)}
|
|
233
235
|
</span>
|
|
@@ -237,17 +239,19 @@ export function OrderDetails({ order, className }: OrderDetailsProps) {
|
|
|
237
239
|
|
|
238
240
|
{/* Addresses */}
|
|
239
241
|
<div className="grid gap-6 md:grid-cols-2">
|
|
242
|
+
{order.shippingAddress && (
|
|
240
243
|
<Card className="p-6">
|
|
241
244
|
<h3 className="text-lg font-semibold text-foreground mb-4">
|
|
242
|
-
|
|
245
|
+
{t("shippingAddress")}
|
|
243
246
|
</h3>
|
|
244
247
|
{formatAddress(order.shippingAddress)}
|
|
245
248
|
</Card>
|
|
249
|
+
)}
|
|
246
250
|
|
|
247
251
|
{order.billingAddress && (
|
|
248
252
|
<Card className="p-6">
|
|
249
253
|
<h3 className="text-lg font-semibold text-foreground mb-4">
|
|
250
|
-
|
|
254
|
+
{t("billingAddress")}
|
|
251
255
|
</h3>
|
|
252
256
|
{formatAddress(order.billingAddress)}
|
|
253
257
|
</Card>
|
|
@@ -1,23 +1,25 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
|
-
import Link from "
|
|
3
|
+
import { Link } from "@/i18n/navigation";
|
|
4
|
+
import { useTranslations } from "next-intl";
|
|
5
|
+
import { useLocale } from "next-intl";
|
|
4
6
|
import { Package, ChevronRight } from "lucide-react";
|
|
5
7
|
import { Badge } from "@/components/ui/badge";
|
|
6
8
|
import { Button } from "@/components/ui/button";
|
|
7
|
-
import { formatAmount } from "
|
|
9
|
+
import { formatAmount } from "@doswiftly/storefront-sdk";
|
|
10
|
+
import type { OrderSummaryFields } from "@/lib/graphql/fragments";
|
|
8
11
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
}
|
|
12
|
+
const STATUS_KEY_MAP: Record<string, string> = {
|
|
13
|
+
DELIVERED: "delivered",
|
|
14
|
+
FULFILLED: "fulfilled",
|
|
15
|
+
IN_TRANSIT: "shipped",
|
|
16
|
+
PARTIALLY_FULFILLED: "partiallyFulfilled",
|
|
17
|
+
UNFULFILLED: "processing",
|
|
18
|
+
RETURNED: "returned",
|
|
19
|
+
};
|
|
18
20
|
|
|
19
21
|
export interface OrderHistoryProps {
|
|
20
|
-
orders:
|
|
22
|
+
orders: OrderSummaryFields[];
|
|
21
23
|
className?: string;
|
|
22
24
|
}
|
|
23
25
|
|
|
@@ -25,23 +27,33 @@ export interface OrderHistoryProps {
|
|
|
25
27
|
* OrderHistory - Display list of customer orders
|
|
26
28
|
*/
|
|
27
29
|
export function OrderHistory({ orders, className }: OrderHistoryProps) {
|
|
28
|
-
const
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
30
|
+
const t = useTranslations("account");
|
|
31
|
+
const locale = useLocale();
|
|
32
|
+
|
|
33
|
+
const getStatusBadge = (status: string) => {
|
|
34
|
+
const variants: Record<string, "default" | "secondary" | "success" | "warning"> = {
|
|
35
|
+
DELIVERED: "success",
|
|
36
|
+
FULFILLED: "success",
|
|
37
|
+
IN_TRANSIT: "default",
|
|
38
|
+
PARTIALLY_FULFILLED: "warning",
|
|
39
|
+
UNFULFILLED: "warning",
|
|
40
|
+
RETURNED: "secondary",
|
|
34
41
|
};
|
|
35
42
|
|
|
43
|
+
const statusKey = STATUS_KEY_MAP[status];
|
|
44
|
+
const label = statusKey
|
|
45
|
+
? t(`orderStatuses.${statusKey}`)
|
|
46
|
+
: status;
|
|
47
|
+
|
|
36
48
|
return (
|
|
37
|
-
<Badge variant={variants[status]}>
|
|
38
|
-
{
|
|
49
|
+
<Badge variant={variants[status] ?? "secondary"}>
|
|
50
|
+
{label}
|
|
39
51
|
</Badge>
|
|
40
52
|
);
|
|
41
53
|
};
|
|
42
54
|
|
|
43
55
|
const formatDate = (dateString: string) => {
|
|
44
|
-
return new Date(dateString).toLocaleDateString(
|
|
56
|
+
return new Date(dateString).toLocaleDateString(locale, {
|
|
45
57
|
year: "numeric",
|
|
46
58
|
month: "long",
|
|
47
59
|
day: "numeric",
|
|
@@ -53,13 +65,13 @@ export function OrderHistory({ orders, className }: OrderHistoryProps) {
|
|
|
53
65
|
<div className="rounded-lg border border-border bg-muted/50 p-12 text-center">
|
|
54
66
|
<Package className="mx-auto h-12 w-12 text-muted-foreground mb-4" />
|
|
55
67
|
<h2 className="text-xl font-semibold text-foreground mb-2">
|
|
56
|
-
|
|
68
|
+
{t("noOrders")}
|
|
57
69
|
</h2>
|
|
58
70
|
<p className="text-muted-foreground mb-6">
|
|
59
|
-
|
|
71
|
+
{t("startShoppingDescription")}
|
|
60
72
|
</p>
|
|
61
73
|
<Button asChild>
|
|
62
|
-
<Link href="/products">
|
|
74
|
+
<Link href="/products">{t("browseProducts")}</Link>
|
|
63
75
|
</Button>
|
|
64
76
|
</div>
|
|
65
77
|
);
|
|
@@ -78,22 +90,22 @@ export function OrderHistory({ orders, className }: OrderHistoryProps) {
|
|
|
78
90
|
<div className="flex-1">
|
|
79
91
|
<div className="flex items-center gap-3 mb-2">
|
|
80
92
|
<h3 className="text-lg font-semibold text-foreground">
|
|
81
|
-
|
|
93
|
+
{t("orderNumber", { number: order.orderNumber })}
|
|
82
94
|
</h3>
|
|
83
|
-
{getStatusBadge(order.
|
|
95
|
+
{getStatusBadge(order.fulfillmentStatus)}
|
|
84
96
|
</div>
|
|
85
97
|
<div className="space-y-1 text-sm text-muted-foreground">
|
|
86
|
-
<p>
|
|
98
|
+
<p>{t("placedOn")} {formatDate(order.processedAt)}</p>
|
|
87
99
|
<p>
|
|
88
|
-
{
|
|
100
|
+
{t("itemCount", { count: order.lineItemsCount })}
|
|
89
101
|
</p>
|
|
90
102
|
</div>
|
|
91
103
|
</div>
|
|
92
104
|
<div className="flex items-center gap-4">
|
|
93
105
|
<div className="text-right">
|
|
94
|
-
<p className="text-sm text-muted-foreground">
|
|
106
|
+
<p className="text-sm text-muted-foreground">{t("orderTotal")}</p>
|
|
95
107
|
<p className="text-lg font-semibold text-foreground">
|
|
96
|
-
{formatAmount(order.
|
|
108
|
+
{formatAmount(order.totalPrice.amount, order.totalPrice.currencyCode)}
|
|
97
109
|
</p>
|
|
98
110
|
</div>
|
|
99
111
|
<ChevronRight className="h-5 w-5 text-muted-foreground" />
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# Fragment for OrderCard component in account pages.
|
|
2
|
+
#
|
|
3
|
+
# Minimal order data for listing view. Full order details
|
|
4
|
+
# require the Order fragment from storefront-operations.
|
|
5
|
+
#
|
|
6
|
+
# Usage in components:
|
|
7
|
+
# import type { OrderSummaryFieldsFragment } from '@/generated/graphql';
|
|
8
|
+
# interface Props { order: OrderSummaryFieldsFragment }
|
|
9
|
+
|
|
10
|
+
fragment OrderSummaryFields on Order {
|
|
11
|
+
id
|
|
12
|
+
orderNumber
|
|
13
|
+
status
|
|
14
|
+
financialStatus
|
|
15
|
+
fulfillmentStatus
|
|
16
|
+
processedAt
|
|
17
|
+
lineItemsCount
|
|
18
|
+
totalPrice {
|
|
19
|
+
amount
|
|
20
|
+
currencyCode
|
|
21
|
+
}
|
|
22
|
+
subtotalPrice {
|
|
23
|
+
amount
|
|
24
|
+
currencyCode
|
|
25
|
+
}
|
|
26
|
+
totalShipping {
|
|
27
|
+
amount
|
|
28
|
+
currencyCode
|
|
29
|
+
}
|
|
30
|
+
shippingAddress {
|
|
31
|
+
firstName
|
|
32
|
+
lastName
|
|
33
|
+
city
|
|
34
|
+
country
|
|
35
|
+
}
|
|
36
|
+
}
|
|
@@ -1,30 +1,31 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
3
|
import { useState, useRef, useEffect } from "react";
|
|
4
|
-
import Link from "
|
|
5
|
-
import {
|
|
4
|
+
import { Link, useRouter } from "@/i18n/navigation";
|
|
5
|
+
import { useTranslations } from "next-intl";
|
|
6
6
|
import { User, LogOut, Package, MapPin, Settings } from "lucide-react";
|
|
7
7
|
import { Button } from "@/components/ui/button";
|
|
8
8
|
import { cn } from "@/lib/utils";
|
|
9
|
+
import { useAuth } from "@/hooks/use-auth";
|
|
9
10
|
|
|
10
11
|
export interface AccountMenuProps {
|
|
11
12
|
customerName?: string;
|
|
12
13
|
className?: string;
|
|
13
14
|
}
|
|
14
15
|
|
|
15
|
-
/**
|
|
16
|
-
* AccountMenu - User account dropdown menu
|
|
17
|
-
*
|
|
18
|
-
* Shows user profile and quick links to account pages
|
|
19
|
-
*/
|
|
20
16
|
export function AccountMenu({ customerName, className }: AccountMenuProps) {
|
|
17
|
+
const t = useTranslations("account");
|
|
18
|
+
const tAuth = useTranslations("auth");
|
|
21
19
|
const router = useRouter();
|
|
22
20
|
const [isOpen, setIsOpen] = useState(false);
|
|
23
21
|
const menuRef = useRef<HTMLDivElement>(null);
|
|
22
|
+
const { logout, isLoggingOut } = useAuth();
|
|
24
23
|
|
|
25
24
|
const handleLogout = async () => {
|
|
26
|
-
|
|
27
|
-
|
|
25
|
+
const result = await logout();
|
|
26
|
+
if (result.success) {
|
|
27
|
+
router.push("/");
|
|
28
|
+
}
|
|
28
29
|
setIsOpen(false);
|
|
29
30
|
};
|
|
30
31
|
|
|
@@ -47,22 +48,22 @@ export function AccountMenu({ customerName, className }: AccountMenuProps) {
|
|
|
47
48
|
|
|
48
49
|
const menuItems = [
|
|
49
50
|
{
|
|
50
|
-
label: "
|
|
51
|
+
label: t("title"),
|
|
51
52
|
href: "/account",
|
|
52
53
|
icon: User,
|
|
53
54
|
},
|
|
54
55
|
{
|
|
55
|
-
label: "
|
|
56
|
+
label: t("orders"),
|
|
56
57
|
href: "/account/orders",
|
|
57
58
|
icon: Package,
|
|
58
59
|
},
|
|
59
60
|
{
|
|
60
|
-
label: "
|
|
61
|
+
label: t("addresses"),
|
|
61
62
|
href: "/account/addresses",
|
|
62
63
|
icon: MapPin,
|
|
63
64
|
},
|
|
64
65
|
{
|
|
65
|
-
label: "
|
|
66
|
+
label: t("settings"),
|
|
66
67
|
href: "/account/settings",
|
|
67
68
|
icon: Settings,
|
|
68
69
|
},
|
|
@@ -76,7 +77,7 @@ export function AccountMenu({ customerName, className }: AccountMenuProps) {
|
|
|
76
77
|
size="sm"
|
|
77
78
|
onClick={() => setIsOpen(!isOpen)}
|
|
78
79
|
className="flex items-center gap-2"
|
|
79
|
-
aria-label="
|
|
80
|
+
aria-label={t("title")}
|
|
80
81
|
aria-expanded={isOpen}
|
|
81
82
|
>
|
|
82
83
|
<User className="h-5 w-5" />
|
|
@@ -119,10 +120,11 @@ export function AccountMenu({ customerName, className }: AccountMenuProps) {
|
|
|
119
120
|
<div className="border-t border-border py-2">
|
|
120
121
|
<button
|
|
121
122
|
onClick={handleLogout}
|
|
122
|
-
|
|
123
|
+
disabled={isLoggingOut}
|
|
124
|
+
className="flex w-full items-center gap-3 px-4 py-2 text-sm text-destructive hover:bg-muted transition-colors disabled:opacity-50"
|
|
123
125
|
>
|
|
124
126
|
<LogOut className="h-4 w-4" />
|
|
125
|
-
|
|
127
|
+
{isLoggingOut ? tAuth("signingOut") : tAuth("signOut")}
|
|
126
128
|
</button>
|
|
127
129
|
</div>
|
|
128
130
|
</div>
|
|
@@ -1,59 +1,56 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
3
|
import { useState } from "react";
|
|
4
|
-
import {
|
|
5
|
-
import Link from "
|
|
4
|
+
import { useSearchParams } from "next/navigation";
|
|
5
|
+
import { useRouter, Link } from "@/i18n/navigation";
|
|
6
|
+
import { useTranslations } from "next-intl";
|
|
6
7
|
import { z } from "zod";
|
|
7
8
|
import { Button } from "@/components/ui/button";
|
|
8
9
|
import { Input } from "@/components/ui/input";
|
|
9
|
-
import {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
.min(1, "Email is required")
|
|
16
|
-
.email("Please enter a valid email address"),
|
|
17
|
-
password: z
|
|
18
|
-
.string()
|
|
19
|
-
.min(1, "Password is required")
|
|
20
|
-
.min(8, "Password must be at least 8 characters"),
|
|
21
|
-
});
|
|
22
|
-
|
|
23
|
-
type LoginFormData = z.infer<typeof loginSchema>;
|
|
10
|
+
import { useAuth } from "@/hooks/use-auth";
|
|
11
|
+
|
|
12
|
+
type LoginFormData = {
|
|
13
|
+
email: string;
|
|
14
|
+
password: string;
|
|
15
|
+
};
|
|
24
16
|
|
|
25
17
|
export interface LoginFormProps {
|
|
26
18
|
onSuccess?: () => void;
|
|
27
19
|
redirectTo?: string;
|
|
28
20
|
}
|
|
29
21
|
|
|
30
|
-
/**
|
|
31
|
-
* LoginForm - Reusable login form component with Zod validation
|
|
32
|
-
*
|
|
33
|
-
* Validates user input before submission and displays field-level errors
|
|
34
|
-
*/
|
|
35
22
|
export function LoginForm({ onSuccess, redirectTo }: LoginFormProps) {
|
|
23
|
+
const t = useTranslations("auth");
|
|
36
24
|
const router = useRouter();
|
|
37
25
|
const searchParams = useSearchParams();
|
|
38
26
|
const defaultRedirect = redirectTo || searchParams?.get("redirect") || "/account";
|
|
39
27
|
|
|
28
|
+
const loginSchema = z.object({
|
|
29
|
+
email: z
|
|
30
|
+
.string()
|
|
31
|
+
.min(1, t("validation.emailInvalid"))
|
|
32
|
+
.email(t("validation.emailInvalid")),
|
|
33
|
+
password: z
|
|
34
|
+
.string()
|
|
35
|
+
.min(1, t("validation.passwordMinLength"))
|
|
36
|
+
.min(8, t("validation.passwordMinLength")),
|
|
37
|
+
});
|
|
38
|
+
|
|
40
39
|
const [email, setEmail] = useState("");
|
|
41
40
|
const [password, setPassword] = useState("");
|
|
42
41
|
const [error, setError] = useState("");
|
|
43
42
|
const [fieldErrors, setFieldErrors] = useState<Partial<Record<keyof LoginFormData, string>>>({});
|
|
44
43
|
|
|
45
|
-
const
|
|
44
|
+
const { login, isLoggingIn } = useAuth();
|
|
46
45
|
|
|
47
46
|
const handleSubmit = async (e: React.FormEvent) => {
|
|
48
47
|
e.preventDefault();
|
|
49
48
|
setError("");
|
|
50
49
|
setFieldErrors({});
|
|
51
50
|
|
|
52
|
-
// Validate form data with Zod
|
|
53
51
|
const result = loginSchema.safeParse({ email, password });
|
|
54
52
|
|
|
55
53
|
if (!result.success) {
|
|
56
|
-
// Extract field-level errors from Zod validation
|
|
57
54
|
const errors: Partial<Record<keyof LoginFormData, string>> = {};
|
|
58
55
|
result.error.errors.forEach((err) => {
|
|
59
56
|
if (err.path[0]) {
|
|
@@ -65,38 +62,20 @@ export function LoginForm({ onSuccess, redirectTo }: LoginFormProps) {
|
|
|
65
62
|
}
|
|
66
63
|
|
|
67
64
|
try {
|
|
68
|
-
const
|
|
69
|
-
input: { email, password },
|
|
70
|
-
});
|
|
65
|
+
const loginResult = await login(email, password);
|
|
71
66
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
if (userErrors.length > 0) {
|
|
76
|
-
setError(userErrors[0].message || "Login failed");
|
|
67
|
+
if (!loginResult.success) {
|
|
68
|
+
setError(loginResult.userErrors[0]?.message || t("loginFailed"));
|
|
77
69
|
return;
|
|
78
70
|
}
|
|
79
71
|
|
|
80
|
-
if (
|
|
81
|
-
|
|
82
|
-
await fetch("/api/auth/set-token", {
|
|
83
|
-
method: "POST",
|
|
84
|
-
headers: { "Content-Type": "application/json" },
|
|
85
|
-
body: JSON.stringify({
|
|
86
|
-
token: customerAccessTokenCreate.customerAccessToken.accessToken,
|
|
87
|
-
}),
|
|
88
|
-
});
|
|
89
|
-
|
|
90
|
-
if (onSuccess) {
|
|
91
|
-
onSuccess();
|
|
92
|
-
} else {
|
|
93
|
-
router.push(defaultRedirect);
|
|
94
|
-
}
|
|
72
|
+
if (onSuccess) {
|
|
73
|
+
onSuccess();
|
|
95
74
|
} else {
|
|
96
|
-
|
|
75
|
+
router.push(defaultRedirect);
|
|
97
76
|
}
|
|
98
|
-
} catch
|
|
99
|
-
setError("
|
|
77
|
+
} catch {
|
|
78
|
+
setError(t("unexpectedError"));
|
|
100
79
|
}
|
|
101
80
|
};
|
|
102
81
|
|
|
@@ -113,7 +92,7 @@ export function LoginForm({ onSuccess, redirectTo }: LoginFormProps) {
|
|
|
113
92
|
htmlFor="email"
|
|
114
93
|
className="text-sm font-medium text-foreground"
|
|
115
94
|
>
|
|
116
|
-
|
|
95
|
+
{t("email")}
|
|
117
96
|
</label>
|
|
118
97
|
<Input
|
|
119
98
|
id="email"
|
|
@@ -139,13 +118,13 @@ export function LoginForm({ onSuccess, redirectTo }: LoginFormProps) {
|
|
|
139
118
|
htmlFor="password"
|
|
140
119
|
className="text-sm font-medium text-foreground"
|
|
141
120
|
>
|
|
142
|
-
|
|
121
|
+
{t("password")}
|
|
143
122
|
</label>
|
|
144
123
|
<Link
|
|
145
124
|
href="/auth/forgot-password"
|
|
146
125
|
className="text-xs text-primary hover:underline"
|
|
147
126
|
>
|
|
148
|
-
|
|
127
|
+
{t("forgotPassword")}
|
|
149
128
|
</Link>
|
|
150
129
|
</div>
|
|
151
130
|
<Input
|
|
@@ -169,18 +148,18 @@ export function LoginForm({ onSuccess, redirectTo }: LoginFormProps) {
|
|
|
169
148
|
<Button
|
|
170
149
|
type="submit"
|
|
171
150
|
className="w-full"
|
|
172
|
-
disabled={
|
|
151
|
+
disabled={isLoggingIn}
|
|
173
152
|
>
|
|
174
|
-
{
|
|
153
|
+
{isLoggingIn ? t("signingIn") : t("signIn")}
|
|
175
154
|
</Button>
|
|
176
155
|
|
|
177
156
|
<div className="text-center text-sm text-muted-foreground">
|
|
178
|
-
|
|
157
|
+
{t("noAccount")}{" "}
|
|
179
158
|
<Link
|
|
180
159
|
href="/auth/register"
|
|
181
160
|
className="font-medium text-primary hover:underline"
|
|
182
161
|
>
|
|
183
|
-
|
|
162
|
+
{t("signUp")}
|
|
184
163
|
</Link>
|
|
185
164
|
</div>
|
|
186
165
|
</form>
|