@doswiftly/cli 0.1.19 → 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.
Files changed (147) hide show
  1. package/dist/commands/deploy.d.ts +20 -0
  2. package/dist/commands/deploy.d.ts.map +1 -1
  3. package/dist/commands/deploy.js +219 -6
  4. package/dist/commands/deploy.js.map +1 -1
  5. package/package.json +4 -4
  6. package/templates/storefront-minimal/.github/workflows/build-template.yml +10 -0
  7. package/templates/storefront-minimal/wrangler.toml +11 -0
  8. package/templates/storefront-nextjs/.github/workflows/build-template.yml +10 -0
  9. package/templates/storefront-nextjs/wrangler.toml +11 -0
  10. package/templates/storefront-nextjs-shadcn/.github/workflows/build-template.yml +10 -0
  11. package/templates/storefront-nextjs-shadcn/CLAUDE.md +29 -5
  12. package/templates/storefront-nextjs-shadcn/app/{about → [locale]/about}/page.tsx +17 -14
  13. package/templates/storefront-nextjs-shadcn/app/{account → [locale]/account}/addresses/page.tsx +19 -15
  14. package/templates/storefront-nextjs-shadcn/app/{account → [locale]/account}/error.tsx +8 -5
  15. package/templates/storefront-nextjs-shadcn/app/{account → [locale]/account}/loyalty/page.tsx +39 -34
  16. package/templates/storefront-nextjs-shadcn/app/{account → [locale]/account}/orders/[id]/page.tsx +9 -7
  17. package/templates/storefront-nextjs-shadcn/app/{account → [locale]/account}/orders/[id]/tracking/page.tsx +27 -25
  18. package/templates/storefront-nextjs-shadcn/app/{account → [locale]/account}/orders/page.tsx +13 -9
  19. package/templates/storefront-nextjs-shadcn/app/{account → [locale]/account}/page.tsx +1 -2
  20. package/templates/storefront-nextjs-shadcn/app/{account → [locale]/account}/settings/page.tsx +1 -1
  21. package/templates/storefront-nextjs-shadcn/app/{auth → [locale]/auth}/forgot-password/page.tsx +14 -12
  22. package/templates/storefront-nextjs-shadcn/app/{auth → [locale]/auth}/login/page.tsx +5 -2
  23. package/templates/storefront-nextjs-shadcn/app/{auth → [locale]/auth}/register/page.tsx +5 -2
  24. package/templates/storefront-nextjs-shadcn/app/{blog → [locale]/blog}/[slug]/page.tsx +1 -1
  25. package/templates/storefront-nextjs-shadcn/app/{cart → [locale]/cart}/page.tsx +14 -10
  26. package/templates/storefront-nextjs-shadcn/app/{categories → [locale]/categories}/[slug]/category-products-client.tsx +4 -2
  27. package/templates/storefront-nextjs-shadcn/app/{categories → [locale]/categories}/page.tsx +13 -8
  28. package/templates/storefront-nextjs-shadcn/app/{checkout → [locale]/checkout}/error.tsx +1 -1
  29. package/templates/storefront-nextjs-shadcn/app/{checkout → [locale]/checkout}/page.tsx +228 -184
  30. package/templates/storefront-nextjs-shadcn/app/{checkout → [locale]/checkout}/success/[orderId]/page.tsx +36 -34
  31. package/templates/storefront-nextjs-shadcn/app/{collections → [locale]/collections}/[handle]/page.tsx +5 -3
  32. package/templates/storefront-nextjs-shadcn/app/{collections → [locale]/collections}/page.tsx +13 -8
  33. package/templates/storefront-nextjs-shadcn/app/{contact → [locale]/contact}/page.tsx +24 -21
  34. package/templates/storefront-nextjs-shadcn/app/{error.tsx → [locale]/error.tsx} +13 -8
  35. package/templates/storefront-nextjs-shadcn/app/[locale]/layout.tsx +92 -0
  36. package/templates/storefront-nextjs-shadcn/app/{not-found.tsx → [locale]/not-found.tsx} +13 -18
  37. package/templates/storefront-nextjs-shadcn/app/{page.tsx → [locale]/page.tsx} +8 -4
  38. package/templates/storefront-nextjs-shadcn/app/{products → [locale]/products}/[slug]/error.tsx +1 -1
  39. package/templates/storefront-nextjs-shadcn/app/{products → [locale]/products}/[slug]/page.tsx +11 -8
  40. package/templates/storefront-nextjs-shadcn/app/{products → [locale]/products}/[slug]/product-client.tsx +3 -1
  41. package/templates/storefront-nextjs-shadcn/app/{products → [locale]/products}/page.tsx +6 -3
  42. package/templates/storefront-nextjs-shadcn/app/{products → [locale]/products}/products-client.tsx +14 -10
  43. package/templates/storefront-nextjs-shadcn/app/{wishlist → [locale]/wishlist}/page.tsx +21 -25
  44. package/templates/storefront-nextjs-shadcn/app/layout.tsx +6 -68
  45. package/templates/storefront-nextjs-shadcn/components/account/address-form.tsx +25 -20
  46. package/templates/storefront-nextjs-shadcn/components/account/address-list.tsx +11 -10
  47. package/templates/storefront-nextjs-shadcn/components/account/order-details.tsx +14 -12
  48. package/templates/storefront-nextjs-shadcn/components/account/order-history.tsx +28 -18
  49. package/templates/storefront-nextjs-shadcn/components/auth/account-menu.tsx +10 -8
  50. package/templates/storefront-nextjs-shadcn/components/auth/login-form.tsx +27 -22
  51. package/templates/storefront-nextjs-shadcn/components/auth/register-form.tsx +48 -43
  52. package/templates/storefront-nextjs-shadcn/components/blog/blog-card.tsx +1 -1
  53. package/templates/storefront-nextjs-shadcn/components/blog/blog-sidebar.tsx +1 -1
  54. package/templates/storefront-nextjs-shadcn/components/brand/brand-card.tsx +1 -1
  55. package/templates/storefront-nextjs-shadcn/components/cart/cart-drawer.tsx +7 -4
  56. package/templates/storefront-nextjs-shadcn/components/cart/cart-icon.tsx +1 -1
  57. package/templates/storefront-nextjs-shadcn/components/cart/cart-item.tsx +7 -5
  58. package/templates/storefront-nextjs-shadcn/components/cart/cart-summary.tsx +9 -7
  59. package/templates/storefront-nextjs-shadcn/components/cart/promo-code-input.tsx +8 -5
  60. package/templates/storefront-nextjs-shadcn/components/cart/shipping-estimator.tsx +18 -15
  61. package/templates/storefront-nextjs-shadcn/components/checkout/payment-method-card.tsx +15 -25
  62. package/templates/storefront-nextjs-shadcn/components/checkout/payment-step.tsx +10 -8
  63. package/templates/storefront-nextjs-shadcn/components/checkout/tax-breakdown.tsx +9 -6
  64. package/templates/storefront-nextjs-shadcn/components/commerce/currency-selector.tsx +5 -3
  65. package/templates/storefront-nextjs-shadcn/components/commerce/pagination.tsx +8 -5
  66. package/templates/storefront-nextjs-shadcn/components/commerce/product-actions.tsx +5 -3
  67. package/templates/storefront-nextjs-shadcn/components/commerce/search-input.tsx +8 -7
  68. package/templates/storefront-nextjs-shadcn/components/common/category-card.tsx +1 -1
  69. package/templates/storefront-nextjs-shadcn/components/common/collection-card.tsx +1 -1
  70. package/templates/storefront-nextjs-shadcn/components/common/social-share.tsx +9 -6
  71. package/templates/storefront-nextjs-shadcn/components/discount/discount-breakdown.tsx +21 -11
  72. package/templates/storefront-nextjs-shadcn/components/discount/discount-code-input.tsx +16 -13
  73. package/templates/storefront-nextjs-shadcn/components/error/error-boundary.tsx +53 -28
  74. package/templates/storefront-nextjs-shadcn/components/filters/dynamic-attribute-filters.tsx +7 -5
  75. package/templates/storefront-nextjs-shadcn/components/gift-card/gift-card-balance.tsx +19 -15
  76. package/templates/storefront-nextjs-shadcn/components/gift-card/gift-card-input.tsx +12 -9
  77. package/templates/storefront-nextjs-shadcn/components/home/category-grid.tsx +8 -5
  78. package/templates/storefront-nextjs-shadcn/components/home/featured-collections.tsx +1 -1
  79. package/templates/storefront-nextjs-shadcn/components/home/featured-products.tsx +12 -8
  80. package/templates/storefront-nextjs-shadcn/components/home/hero-section.tsx +13 -8
  81. package/templates/storefront-nextjs-shadcn/components/home/newsletter-signup.tsx +10 -8
  82. package/templates/storefront-nextjs-shadcn/components/layout/breadcrumbs.tsx +37 -12
  83. package/templates/storefront-nextjs-shadcn/components/layout/currency-selector.tsx +5 -2
  84. package/templates/storefront-nextjs-shadcn/components/layout/footer.tsx +24 -23
  85. package/templates/storefront-nextjs-shadcn/components/layout/header.tsx +20 -12
  86. package/templates/storefront-nextjs-shadcn/components/layout/language-switcher.tsx +54 -0
  87. package/templates/storefront-nextjs-shadcn/components/layout/mobile-menu.tsx +33 -30
  88. package/templates/storefront-nextjs-shadcn/components/layout/navigation.tsx +27 -24
  89. package/templates/storefront-nextjs-shadcn/components/loyalty/referral-section.tsx +23 -24
  90. package/templates/storefront-nextjs-shadcn/components/product/add-to-cart-button.tsx +6 -14
  91. package/templates/storefront-nextjs-shadcn/components/product/b2b-price-display.tsx +1 -1
  92. package/templates/storefront-nextjs-shadcn/components/product/filter-active-pills.tsx +4 -1
  93. package/templates/storefront-nextjs-shadcn/components/product/filter-mobile-sheet.tsx +7 -4
  94. package/templates/storefront-nextjs-shadcn/components/product/filter-price-range.tsx +5 -3
  95. package/templates/storefront-nextjs-shadcn/components/product/product-card.tsx +8 -6
  96. package/templates/storefront-nextjs-shadcn/components/product/product-filters.tsx +3 -1
  97. package/templates/storefront-nextjs-shadcn/components/product/product-image.tsx +3 -7
  98. package/templates/storefront-nextjs-shadcn/components/product/product-sort.tsx +26 -13
  99. package/templates/storefront-nextjs-shadcn/components/product/review-form.tsx +25 -27
  100. package/templates/storefront-nextjs-shadcn/components/providers/language-sync-provider.tsx +27 -0
  101. package/templates/storefront-nextjs-shadcn/components/providers/stores-provider.tsx +40 -7
  102. package/templates/storefront-nextjs-shadcn/components/returns/return-request-form.tsx +56 -70
  103. package/templates/storefront-nextjs-shadcn/components/search/search-bar.tsx +7 -4
  104. package/templates/storefront-nextjs-shadcn/components/shipping/shipping-method-selector.tsx +12 -9
  105. package/templates/storefront-nextjs-shadcn/components/ui/empty-state.tsx +23 -12
  106. package/templates/storefront-nextjs-shadcn/components/wishlist/wishlist-button.tsx +7 -4
  107. package/templates/storefront-nextjs-shadcn/components/wishlist/wishlist-icon.tsx +1 -1
  108. package/templates/storefront-nextjs-shadcn/components/wishlist/wishlist-item.tsx +2 -10
  109. package/templates/storefront-nextjs-shadcn/generated/graphql.ts +1159 -551
  110. package/templates/storefront-nextjs-shadcn/hooks/index.ts +1 -0
  111. package/templates/storefront-nextjs-shadcn/hooks/use-cart-actions.ts +22 -249
  112. package/templates/storefront-nextjs-shadcn/hooks/use-cart-di.ts +67 -0
  113. package/templates/storefront-nextjs-shadcn/hooks/use-cart-sync.ts +3 -3
  114. package/templates/storefront-nextjs-shadcn/i18n/navigation.ts +12 -0
  115. package/templates/storefront-nextjs-shadcn/i18n/request.ts +17 -0
  116. package/templates/storefront-nextjs-shadcn/i18n/routing.ts +17 -0
  117. package/templates/storefront-nextjs-shadcn/lib/graphql/config.ts +1 -0
  118. package/templates/storefront-nextjs-shadcn/lib/graphql/hooks.ts +41 -8
  119. package/templates/storefront-nextjs-shadcn/lib/graphql/query-keys.ts +20 -18
  120. package/templates/storefront-nextjs-shadcn/lib/graphql/server.ts +2 -1
  121. package/templates/storefront-nextjs-shadcn/messages/en.json +869 -0
  122. package/templates/storefront-nextjs-shadcn/messages/pl.json +869 -0
  123. package/templates/storefront-nextjs-shadcn/next.config.ts +6 -5
  124. package/templates/storefront-nextjs-shadcn/package.json +3 -2
  125. package/templates/storefront-nextjs-shadcn/proxy.ts +115 -46
  126. package/templates/storefront-nextjs-shadcn/stores/cart-store.ts +24 -58
  127. package/templates/storefront-nextjs-shadcn/wrangler.toml +11 -0
  128. /package/templates/storefront-nextjs-shadcn/app/{account → [locale]/account}/loading.tsx +0 -0
  129. /package/templates/storefront-nextjs-shadcn/app/{account → [locale]/account}/orders/[id]/loading.tsx +0 -0
  130. /package/templates/storefront-nextjs-shadcn/app/{blog → [locale]/blog}/[slug]/loading.tsx +0 -0
  131. /package/templates/storefront-nextjs-shadcn/app/{blog → [locale]/blog}/loading.tsx +0 -0
  132. /package/templates/storefront-nextjs-shadcn/app/{blog → [locale]/blog}/page.tsx +0 -0
  133. /package/templates/storefront-nextjs-shadcn/app/{brands → [locale]/brands}/[slug]/page.tsx +0 -0
  134. /package/templates/storefront-nextjs-shadcn/app/{brands → [locale]/brands}/page.tsx +0 -0
  135. /package/templates/storefront-nextjs-shadcn/app/{cart → [locale]/cart}/loading.tsx +0 -0
  136. /package/templates/storefront-nextjs-shadcn/app/{categories → [locale]/categories}/[slug]/loading.tsx +0 -0
  137. /package/templates/storefront-nextjs-shadcn/app/{categories → [locale]/categories}/[slug]/page.tsx +0 -0
  138. /package/templates/storefront-nextjs-shadcn/app/{checkout → [locale]/checkout}/loading.tsx +0 -0
  139. /package/templates/storefront-nextjs-shadcn/app/{collections → [locale]/collections}/[handle]/loading.tsx +0 -0
  140. /package/templates/storefront-nextjs-shadcn/app/{collections → [locale]/collections}/loading.tsx +0 -0
  141. /package/templates/storefront-nextjs-shadcn/app/{products → [locale]/products}/[slug]/loading.tsx +0 -0
  142. /package/templates/storefront-nextjs-shadcn/app/{products → [locale]/products}/loading.tsx +0 -0
  143. /package/templates/storefront-nextjs-shadcn/app/{returns → [locale]/returns}/page.tsx +0 -0
  144. /package/templates/storefront-nextjs-shadcn/app/{search → [locale]/search}/loading.tsx +0 -0
  145. /package/templates/storefront-nextjs-shadcn/app/{search → [locale]/search}/page.tsx +0 -0
  146. /package/templates/storefront-nextjs-shadcn/app/{search → [locale]/search}/search-client.tsx +0 -0
  147. /package/templates/storefront-nextjs-shadcn/app/{shipping → [locale]/shipping}/page.tsx +0 -0
@@ -53,14 +53,15 @@ graphql/
53
53
  └── custom.example.graphql # Example custom operations
54
54
 
55
55
  stores/ # Zustand stores (Context pattern via SDK createStoreContext)
56
- ├── cart-store.ts # Cart UI state (cartId, isOpen) — items from server
56
+ ├── cart-store.ts # Re-export facade from SDK (createCartStore with DI)
57
57
  ├── checkout-store.ts # Checkout form state with persistence
58
58
  └── wishlist-store.ts # Wishlist state with persistence
59
59
 
60
60
  hooks/
61
61
  ├── use-auth.ts # Thin wrapper over SDK useAuth + httpOnly cookies
62
62
  ├── use-auth-sync.ts # Detects auth desync (cookie vs store)
63
- ├── use-cart-actions.ts # Cart mutations with debounce + race condition protection
63
+ ├── use-cart-di.ts # CartActions DI implementation (GraphQL transport for SDK cart store)
64
+ ├── use-cart-actions.ts # Cart mutations UX wrapper (debounce, openCart — delegates to SDK store)
64
65
  ├── use-cart-sync.ts # Cart data from server (maps GraphQL → CartItemData)
65
66
  └── use-filter-params.ts # URL-driven filter state
66
67
 
@@ -72,19 +73,22 @@ generated/
72
73
 
73
74
  Template importuje z `@doswiftly/storefront-sdk`:
74
75
  - **Core (`.`)**: `AUTH_COOKIE_NAME`, `matchesRoute`, `createSetTokenHandler`, `createClearTokenHandler`, `createAuthTokenClient`, `formatPrice`, `formatAmount`, `sanitizeHtml`, `normalizeConnection`
75
- - **React (`./react`)**: `StorefrontProvider`, `useAuthStore`, `useCurrencyStore`, `useAuthHydrated`, `useStorefrontClient`, `useHydrated`, `useDebouncedValue`, `createStoreContext`
76
+ - **React (`./react`)**: `StorefrontProvider`, `useAuthStore`, `useCurrencyStore`, `useAuthHydrated`, `useStorefrontClient`, `useHydrated`, `useDebouncedValue`, `createStoreContext`, `createCartStore`, `CartProvider`, `useCartStore`, `useCartStoreApi`
76
77
  - **Server (`./react/server`)**: `getStorefrontClient`, `getCurrencyFromCookieAsync`
77
78
  - **Auth hooks**: `useAuth` (bazowy, owinięty w `hooks/use-auth.ts`)
78
79
 
79
80
  **Store pattern**: Zustand stores używają `createStore()` (vanilla) + React Context via `createStoreContext()` from SDK.
80
81
  Hooki `useAuthStore`/`useCurrencyStore` wymagają owinięcia w `StorefrontProvider`.
81
- Template stores (cart, checkout, wishlist) wymagają `StoresProvider` (wewnątrz `StorefrontProvider`).
82
+ **Cart store** pochodzi z SDK (`createCartStore` z DI pattern) template dostarcza `CartActions` via `hooks/use-cart-di.ts`.
83
+ Template stores (checkout, wishlist) wymagają `StoresProvider` (wewnątrz `StorefrontProvider`).
82
84
  `useAuthHydrated()` zwraca `true` po rehydracji persist z localStorage.
83
85
  `useHydrated()` zwraca `true` po hydration SSR → client.
84
86
 
87
+ **Cart DI pattern**: SDK orchestruje stan (init, mutations, error handling), template dostarcza transport via `CartActions` interface.
88
+ `StoresProvider` wiąże DI: `createCartStore({ getActions: () => actionsRef.current, onMutationSuccess, onMutationError })`.
89
+
85
90
  Template NIE importuje z SDK:
86
91
  - React Query hooks — są LOCAL w `lib/graphql/hooks.ts`
87
- - Cart actions — LOCAL w `hooks/use-cart-actions.ts`
88
92
  - Data types — LOCAL via codegen (`generated/graphql.ts`)
89
93
 
90
94
  ## Data Fetching
@@ -189,6 +193,26 @@ Cart mutations use three layers of protection:
189
193
  8. **Query keys**: Zawsze z `queryKeys.*` (nie hardcoded arrays)
190
194
  9. **Config**: Zawsze z `graphqlConfig` (nie duplikuj env var resolution)
191
195
 
196
+ ## i18n (next-intl + SDK)
197
+
198
+ **Architecture**: Dynamic locales from backend, static defaultLocale from env var.
199
+
200
+ **Env vars**:
201
+ - `NEXT_PUBLIC_DEFAULT_LOCALE` — default locale for URL routing (fallback: `'pl'`). Controls which locale is hidden in URL (`as-needed` prefix). After changing defaultLanguage in admin panel, update this env var in `.env.local` and restart dev server.
202
+
203
+ **Locale flow**:
204
+ - `proxy.ts` middleware fetches `supportedLanguages` from backend (cached 60s) — fully dynamic
205
+ - `routing.ts` exports `defaultLocale` (env+fallback) and `localePrefix` — no `defineRouting`, no hardcoded locales
206
+ - `navigation.ts` uses `createNavigation` without `locales` — any locale string accepted at runtime
207
+ - `layout.tsx` validates locale against `shop.supportedLanguages` from backend
208
+
209
+ **Language cookie**: Single `preferred-language` cookie (1 year) — serves both:
210
+ - next-intl middleware (locale detection/routing, via `localeCookie.name` override in proxy.ts)
211
+ - SDK language store (X-Lang header for backend, framework-agnostic)
212
+ - Cookie name constant: `LANGUAGE_COOKIE_NAME` exported from `@doswiftly/storefront-sdk`
213
+
214
+ **Known limitation**: `defaultLocale` is static (env var). Changing it requires `.env.local` update + restart. Dynamic `supportedLanguages` (add/remove locale) works immediately — no restart needed.
215
+
192
216
  ## GraphQL Operations
193
217
 
194
218
  Operacje i schemat sa zdefiniowane w:
@@ -1,31 +1,34 @@
1
+ import { getTranslations } from "next-intl/server";
1
2
  import { Breadcrumbs } from "@/components/layout/breadcrumbs";
2
3
 
3
- export default function AboutPage() {
4
+ export default async function AboutPage() {
5
+ const t = await getTranslations("about");
6
+
4
7
  return (
5
8
  <div className="container mx-auto px-4 py-8">
6
9
  <Breadcrumbs className="mb-6" />
7
-
10
+
8
11
  <div className="mx-auto max-w-3xl">
9
- <h1 className="mb-6 text-4xl font-bold text-foreground">About Us</h1>
10
-
12
+ <h1 className="mb-6 text-4xl font-bold text-foreground">{t("title")}</h1>
13
+
11
14
  <div className="prose prose-gray max-w-none">
12
15
  <p className="text-lg text-muted-foreground">
13
- Welcome to our store! We are dedicated to providing you with the best products and exceptional customer service.
16
+ {t("welcome")}
14
17
  </p>
15
-
16
- <h2 className="mt-8 text-2xl font-semibold text-foreground">Our Story</h2>
18
+
19
+ <h2 className="mt-8 text-2xl font-semibold text-foreground">{t("ourStory")}</h2>
17
20
  <p className="text-muted-foreground">
18
- Founded with a passion for quality and customer satisfaction, we've been serving customers worldwide with carefully curated products.
21
+ {t("ourStoryText")}
19
22
  </p>
20
-
21
- <h2 className="mt-8 text-2xl font-semibold text-foreground">Our Mission</h2>
23
+
24
+ <h2 className="mt-8 text-2xl font-semibold text-foreground">{t("ourMission")}</h2>
22
25
  <p className="text-muted-foreground">
23
- To provide high-quality products at competitive prices while delivering an exceptional shopping experience.
26
+ {t("ourMissionText")}
24
27
  </p>
25
-
26
- <h2 className="mt-8 text-2xl font-semibold text-foreground">Contact Us</h2>
28
+
29
+ <h2 className="mt-8 text-2xl font-semibold text-foreground">{t("contactTitle")}</h2>
27
30
  <p className="text-muted-foreground">
28
- Have questions? Feel free to reach out to our customer support team.
31
+ {t("contactText")}
29
32
  </p>
30
33
  </div>
31
34
  </div>
@@ -1,7 +1,8 @@
1
1
  "use client";
2
2
 
3
3
  import { useState, useCallback } from "react";
4
- import Link from "next/link";
4
+ import { Link } from "@/i18n/navigation";
5
+ import { useTranslations } from "next-intl";
5
6
  import { Plus } from "lucide-react";
6
7
  import { useAuthStore, useAuthHydrated } from "@doswiftly/storefront-sdk/react";
7
8
  import {
@@ -30,6 +31,7 @@ import {
30
31
  } from "@/components/ui/dialog";
31
32
 
32
33
  export default function AddressesPage() {
34
+ const t = useTranslations("account");
33
35
  const hydrated = useHydrated();
34
36
  const authHydrated = useAuthHydrated();
35
37
  const accessToken = useAuthStore((s) => s.accessToken);
@@ -52,11 +54,11 @@ export default function AddressesPage() {
52
54
 
53
55
  const deleteMutation = useCustomerAddressDelete({
54
56
  onSuccess: (data) => checkUserErrors(data, 'customerAddressDelete'),
55
- onError: () => setMutationError('Failed to delete address'),
57
+ onError: () => setMutationError(t('failedDelete')),
56
58
  });
57
59
  const setDefaultMutation = useCustomerDefaultAddressUpdate({
58
60
  onSuccess: (data) => checkUserErrors(data, 'customerDefaultAddressUpdate'),
59
- onError: () => setMutationError('Failed to set default address'),
61
+ onError: () => setMutationError(t('failedSetDefault')),
60
62
  });
61
63
 
62
64
  const createMutation = useCustomerAddressCreate({
@@ -65,7 +67,7 @@ export default function AddressesPage() {
65
67
  setIsFormOpen(false);
66
68
  }
67
69
  },
68
- onError: () => setMutationError('Failed to create address'),
70
+ onError: () => setMutationError(t('failedCreate')),
69
71
  });
70
72
 
71
73
  const updateMutation = useCustomerAddressUpdate({
@@ -74,7 +76,7 @@ export default function AddressesPage() {
74
76
  setIsFormOpen(false);
75
77
  }
76
78
  },
77
- onError: () => setMutationError('Failed to update address'),
79
+ onError: () => setMutationError(t('failedUpdate')),
78
80
  });
79
81
 
80
82
  const addresses = (customerData?.customer?.addresses ?? []).map((addr) => ({
@@ -103,7 +105,7 @@ export default function AddressesPage() {
103
105
  };
104
106
 
105
107
  const handleDelete = (id: string) => {
106
- if (confirm("Are you sure you want to delete this address?")) {
108
+ if (confirm(t("deleteConfirm"))) {
107
109
  deleteMutation.mutate(id);
108
110
  }
109
111
  };
@@ -162,11 +164,13 @@ export default function AddressesPage() {
162
164
  <div className="container mx-auto px-4 py-8">
163
165
  <Breadcrumbs className="mb-6" />
164
166
  <div className="rounded-lg border border-destructive bg-destructive/10 p-8 text-center text-sm text-destructive">
165
- You need to{" "}
166
- <Link href="/auth/login?redirect=/account/addresses" className="font-medium underline">
167
- sign in
168
- </Link>{" "}
169
- to manage your addresses.
167
+ {t.rich("signInToManage", {
168
+ link: (chunks) => (
169
+ <Link href="/auth/login?redirect=/account/addresses" className="font-medium underline">
170
+ {chunks}
171
+ </Link>
172
+ ),
173
+ })}
170
174
  </div>
171
175
  </div>
172
176
  );
@@ -178,14 +182,14 @@ export default function AddressesPage() {
178
182
 
179
183
  <div className="mb-8 flex items-center justify-between">
180
184
  <div>
181
- <h1 className="text-3xl font-bold text-foreground">Addresses</h1>
185
+ <h1 className="text-3xl font-bold text-foreground">{t("addresses")}</h1>
182
186
  <p className="mt-2 text-muted-foreground">
183
- Manage your shipping and billing addresses
187
+ {t("manageAddresses")}
184
188
  </p>
185
189
  </div>
186
190
  <Button onClick={handleAdd}>
187
191
  <Plus className="mr-2 h-4 w-4" />
188
- Add Address
192
+ {t("addAddress")}
189
193
  </Button>
190
194
  </div>
191
195
 
@@ -207,7 +211,7 @@ export default function AddressesPage() {
207
211
  <DialogContent className="max-w-2xl max-h-[90vh] overflow-y-auto">
208
212
  <DialogHeader>
209
213
  <DialogTitle>
210
- {editingAddress ? "Edit Address" : "Add New Address"}
214
+ {editingAddress ? t("editAddress") : t("addNewAddress")}
211
215
  </DialogTitle>
212
216
  </DialogHeader>
213
217
  <AddressForm
@@ -1,7 +1,8 @@
1
1
  "use client";
2
2
 
3
3
  import { useEffect } from "react";
4
- import Link from "next/link";
4
+ import { useTranslations } from "next-intl";
5
+ import { Link } from "@/i18n/navigation";
5
6
  import { AlertCircle } from "lucide-react";
6
7
  import { Button } from "@/components/ui/button";
7
8
  import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
@@ -13,6 +14,8 @@ export default function AccountError({
13
14
  error: Error & { digest?: string };
14
15
  reset: () => void;
15
16
  }) {
17
+ const t = useTranslations("errors");
18
+
16
19
  useEffect(() => {
17
20
  console.error("[Account Error]", error);
18
21
  }, [error]);
@@ -23,17 +26,17 @@ export default function AccountError({
23
26
  <CardHeader>
24
27
  <CardTitle className="flex items-center gap-2 text-destructive">
25
28
  <AlertCircle className="h-5 w-5" />
26
- Something went wrong
29
+ {t("somethingWentWrong")}
27
30
  </CardTitle>
28
31
  </CardHeader>
29
32
  <CardContent className="space-y-4">
30
33
  <p className="text-sm text-muted-foreground">
31
- We couldn&apos;t load your account data. This might be a temporary issue.
34
+ {t("couldNotLoadAccount")}
32
35
  </p>
33
36
  <div className="flex gap-3">
34
- <Button onClick={reset}>Try again</Button>
37
+ <Button onClick={reset}>{t("tryAgain")}</Button>
35
38
  <Button variant="outline" asChild>
36
- <Link href="/">Go to homepage</Link>
39
+ <Link href="/">{t("goHome")}</Link>
37
40
  </Button>
38
41
  </div>
39
42
  </CardContent>
@@ -15,7 +15,8 @@
15
15
  */
16
16
 
17
17
  import { useState } from 'react';
18
- import Link from 'next/link';
18
+ import { useTranslations } from 'next-intl';
19
+ import { Link } from '@/i18n/navigation';
19
20
  import { ArrowLeft, Gift, Award, History, Loader2, Users, AlertCircle } from 'lucide-react';
20
21
  import { Button } from '@/components/ui/button';
21
22
  import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
@@ -40,6 +41,9 @@ import {
40
41
  } from '@/lib/graphql/hooks';
41
42
 
42
43
  export default function LoyaltyPage() {
44
+ const t = useTranslations("account");
45
+ const tl = useTranslations("loyalty.page");
46
+ const ta = useTranslations("auth");
43
47
  const [activeTab, setActiveTab] = useState('overview');
44
48
  const { isAuthenticated } = useAuthStore();
45
49
  const isHydrated = useAuthHydrated();
@@ -86,7 +90,7 @@ export default function LoyaltyPage() {
86
90
  };
87
91
  }
88
92
 
89
- throw new Error('Nie udało się wymienić nagrody');
93
+ throw new Error(tl('redeemFailed'));
90
94
  } catch (error) {
91
95
  throw error;
92
96
  }
@@ -110,7 +114,7 @@ export default function LoyaltyPage() {
110
114
  <Link href="/account">
111
115
  <Button variant="ghost" size="sm" className="mb-4">
112
116
  <ArrowLeft className="h-4 w-4 mr-2" />
113
- Powrót do konta
117
+ {t("backToAccount")}
114
118
  </Button>
115
119
  </Link>
116
120
 
@@ -118,15 +122,15 @@ export default function LoyaltyPage() {
118
122
  <CardHeader>
119
123
  <CardTitle className="flex items-center gap-2">
120
124
  <Award className="h-5 w-5" />
121
- Program lojalnościowy
125
+ {t("loyalty")}
122
126
  </CardTitle>
123
127
  </CardHeader>
124
128
  <CardContent>
125
129
  <p className="text-muted-foreground mb-4">
126
- Zaloguj się, aby zobaczyć swoje punkty i nagrody.
130
+ {tl("signInToView")}
127
131
  </p>
128
132
  <Link href="/auth/login?redirect=/account/loyalty">
129
- <Button className="w-full">Zaloguj się</Button>
133
+ <Button className="w-full">{ta("signIn")}</Button>
130
134
  </Link>
131
135
  </CardContent>
132
136
  </Card>
@@ -152,14 +156,14 @@ export default function LoyaltyPage() {
152
156
  <Link href="/account">
153
157
  <Button variant="ghost" size="sm" className="mb-4">
154
158
  <ArrowLeft className="h-4 w-4 mr-2" />
155
- Powrót do konta
159
+ {t("backToAccount")}
156
160
  </Button>
157
161
  </Link>
158
162
 
159
163
  <Alert variant="destructive">
160
164
  <AlertCircle className="h-4 w-4" />
161
165
  <AlertDescription>
162
- Nie udało się załadować danych programu lojalnościowego. Spróbuj ponownie później.
166
+ {tl("loadFailed")}
163
167
  </AlertDescription>
164
168
  </Alert>
165
169
  </div>
@@ -173,14 +177,14 @@ export default function LoyaltyPage() {
173
177
  <Link href="/account">
174
178
  <Button variant="ghost" size="sm" className="mb-4">
175
179
  <ArrowLeft className="h-4 w-4 mr-2" />
176
- Powrót do konta
180
+ {t("backToAccount")}
177
181
  </Button>
178
182
  </Link>
179
183
 
180
184
  <Alert>
181
185
  <AlertCircle className="h-4 w-4" />
182
186
  <AlertDescription>
183
- Program lojalnościowy jest obecnie niedostępny. Sprawdź ponownie później.
187
+ {tl("unavailable")}
184
188
  </AlertDescription>
185
189
  </Alert>
186
190
  </div>
@@ -194,7 +198,7 @@ export default function LoyaltyPage() {
194
198
  <Link href="/account">
195
199
  <Button variant="ghost" size="sm" className="mb-4">
196
200
  <ArrowLeft className="h-4 w-4 mr-2" />
197
- Powrót do konta
201
+ {t("backToAccount")}
198
202
  </Button>
199
203
  </Link>
200
204
 
@@ -202,16 +206,15 @@ export default function LoyaltyPage() {
202
206
  <CardHeader>
203
207
  <CardTitle className="flex items-center gap-2">
204
208
  <Award className="h-5 w-5" />
205
- Dołącz do programu lojalnościowego
209
+ {tl("joinTitle")}
206
210
  </CardTitle>
207
211
  </CardHeader>
208
212
  <CardContent>
209
213
  <p className="text-muted-foreground mb-4">
210
- Zbieraj punkty za zakupy i wymieniaj je na nagrody! Zapisanie jest automatyczne
211
- przy pierwszym zakupie.
214
+ {tl("joinDescription")}
212
215
  </p>
213
216
  <Link href="/products">
214
- <Button className="w-full">Przeglądaj produkty</Button>
217
+ <Button className="w-full">{t("browseProducts")}</Button>
215
218
  </Link>
216
219
  </CardContent>
217
220
  </Card>
@@ -229,15 +232,15 @@ export default function LoyaltyPage() {
229
232
  <Link href="/account">
230
233
  <Button variant="ghost" size="sm" className="mb-4">
231
234
  <ArrowLeft className="h-4 w-4 mr-2" />
232
- Powrót do konta
235
+ {t("backToAccount")}
233
236
  </Button>
234
237
  </Link>
235
238
 
236
239
  <div className="flex items-center justify-between">
237
240
  <div>
238
- <h1 className="text-3xl font-bold mb-2">Program lojalnościowy</h1>
241
+ <h1 className="text-3xl font-bold mb-2">{t("loyalty")}</h1>
239
242
  <p className="text-muted-foreground">
240
- Zbieraj {settings?.pointsName ?? 'punkty'} i wymieniaj je na nagrody
243
+ {tl("collectAndRedeem", { pointsName: settings?.pointsName ?? 'punkty' })}
241
244
  </p>
242
245
  </div>
243
246
  {member.tier && <TierBadge tier={member.tier.type} name={member.tier.name} size="lg" />}
@@ -249,20 +252,20 @@ export default function LoyaltyPage() {
249
252
  <TabsList className="mb-6">
250
253
  <TabsTrigger value="overview" className="gap-2">
251
254
  <Award className="h-4 w-4" />
252
- Przegląd
255
+ {tl("tabOverview")}
253
256
  </TabsTrigger>
254
257
  <TabsTrigger value="rewards" className="gap-2">
255
258
  <Gift className="h-4 w-4" />
256
- Nagrody
259
+ {tl("tabRewards")}
257
260
  </TabsTrigger>
258
261
  <TabsTrigger value="history" className="gap-2">
259
262
  <History className="h-4 w-4" />
260
- Historia
263
+ {tl("tabHistory")}
261
264
  </TabsTrigger>
262
265
  {showReferralTab && (
263
266
  <TabsTrigger value="referral" className="gap-2">
264
267
  <Users className="h-4 w-4" />
265
- Polecenia
268
+ {tl("tabReferrals")}
266
269
  </TabsTrigger>
267
270
  )}
268
271
  </TabsList>
@@ -279,11 +282,11 @@ export default function LoyaltyPage() {
279
282
  <div className="p-6 bg-gradient-to-r from-amber-50 to-yellow-50 dark:from-amber-950/30 dark:to-yellow-950/30 rounded-lg border border-amber-200 dark:border-amber-800">
280
283
  <h3 className="font-semibold mb-4 flex items-center gap-2">
281
284
  <Award className="h-5 w-5 text-amber-600" />
282
- Twoje korzyści na poziomie {member.tier.name}
285
+ {tl("tierBenefits", { tierName: member.tier.name })}
283
286
  </h3>
284
287
  <div className="grid grid-cols-1 sm:grid-cols-3 gap-4 text-sm">
285
288
  <div className="p-3 bg-white/50 dark:bg-black/20 rounded-lg">
286
- <div className="font-medium">Mnożnik punktów</div>
289
+ <div className="font-medium">{tl("pointsMultiplier")}</div>
287
290
  <div className="text-2xl font-bold text-amber-600">x{member.tier.pointsMultiplier}</div>
288
291
  </div>
289
292
  {member.tier.customBenefits?.slice(0, 2).map((benefit, idx) => (
@@ -311,16 +314,16 @@ export default function LoyaltyPage() {
311
314
  <div className="flex flex-wrap gap-4">
312
315
  <Button onClick={() => setActiveTab('rewards')}>
313
316
  <Gift className="h-4 w-4 mr-2" />
314
- Przeglądaj nagrody
317
+ {tl("browseRewards")}
315
318
  </Button>
316
319
  <Button variant="outline" onClick={() => setActiveTab('history')}>
317
320
  <History className="h-4 w-4 mr-2" />
318
- Zobacz historię
321
+ {tl("viewHistory")}
319
322
  </Button>
320
323
  {showReferralTab && (
321
324
  <Button variant="outline" onClick={() => setActiveTab('referral')}>
322
325
  <Users className="h-4 w-4 mr-2" />
323
- Poleć znajomego
326
+ {tl("referFriend")}
324
327
  </Button>
325
328
  )}
326
329
  </div>
@@ -329,9 +332,9 @@ export default function LoyaltyPage() {
329
332
  {/* Rewards Tab */}
330
333
  <TabsContent value="rewards">
331
334
  <div className="mb-6">
332
- <h2 className="text-xl font-semibold mb-2">Dostępne nagrody</h2>
335
+ <h2 className="text-xl font-semibold mb-2">{tl("availableRewards")}</h2>
333
336
  <p className="text-muted-foreground">
334
- Wymień swoje {settings?.pointsName ?? 'punkty'} na atrakcyjne nagrody
337
+ {tl("redeemDescription", { pointsName: settings?.pointsName ?? 'punkty' })}
335
338
  </p>
336
339
  </div>
337
340
  <RewardsCatalog
@@ -345,8 +348,8 @@ export default function LoyaltyPage() {
345
348
  {/* History Tab */}
346
349
  <TabsContent value="history">
347
350
  <div className="mb-6">
348
- <h2 className="text-xl font-semibold mb-2">Historia punktów</h2>
349
- <p className="text-muted-foreground">Przegląd wszystkich transakcji punktowych</p>
351
+ <h2 className="text-xl font-semibold mb-2">{tl("pointsHistory")}</h2>
352
+ <p className="text-muted-foreground">{tl("historyDescription")}</p>
350
353
  </div>
351
354
  <PointsHistory transactions={transactions} />
352
355
  </TabsContent>
@@ -355,10 +358,12 @@ export default function LoyaltyPage() {
355
358
  {showReferralTab && referralStats && (
356
359
  <TabsContent value="referral">
357
360
  <div className="mb-6">
358
- <h2 className="text-xl font-semibold mb-2">Poleć znajomego</h2>
361
+ <h2 className="text-xl font-semibold mb-2">{tl("referFriend")}</h2>
359
362
  <p className="text-muted-foreground">
360
- Zaproś znajomych i otrzymaj {settings?.referralPoints ?? 0}{' '}
361
- {settings?.pointsName ?? 'punktów'} za każde polecenie
363
+ {tl("referFriendDescription", {
364
+ points: settings?.referralPoints ?? 0,
365
+ pointsName: settings?.pointsName ?? 'punktów',
366
+ })}
362
367
  </p>
363
368
  </div>
364
369
  <ReferralSection
@@ -1,7 +1,8 @@
1
1
  "use client";
2
2
 
3
3
  import { useParams } from "next/navigation";
4
- import Link from "next/link";
4
+ import { Link } from "@/i18n/navigation";
5
+ import { useTranslations } from "next-intl";
5
6
  import { ChevronLeft } from "lucide-react";
6
7
  import { useCustomerOrder } from "@/lib/graphql/hooks";
7
8
  import { useHydrated } from "@doswiftly/storefront-sdk/react";
@@ -13,6 +14,7 @@ import {
13
14
  } from "@/components/account/order-details";
14
15
 
15
16
  export default function OrderDetailPage() {
17
+ const t = useTranslations("account");
16
18
  const params = useParams();
17
19
  const orderId = params.id as string;
18
20
  const hydrated = useHydrated();
@@ -36,10 +38,10 @@ export default function OrderDetailPage() {
36
38
  return (
37
39
  <div className="container mx-auto px-4 py-8">
38
40
  <div className="text-center">
39
- <h1 className="text-2xl font-bold text-destructive">Failed to load order</h1>
40
- <p className="mt-2 text-muted-foreground">Something went wrong. Please try again later.</p>
41
+ <h1 className="text-2xl font-bold text-destructive">{t("failedLoadOrder")}</h1>
42
+ <p className="mt-2 text-muted-foreground">{t("tryAgainLater")}</p>
41
43
  <Link href="/account/orders">
42
- <Button className="mt-4">Back to Orders</Button>
44
+ <Button className="mt-4">{t("backToOrders")}</Button>
43
45
  </Link>
44
46
  </div>
45
47
  </div>
@@ -50,9 +52,9 @@ export default function OrderDetailPage() {
50
52
  return (
51
53
  <div className="container mx-auto px-4 py-8">
52
54
  <div className="text-center">
53
- <h1 className="text-2xl font-bold">Order not found</h1>
55
+ <h1 className="text-2xl font-bold">{t("orderNotFound")}</h1>
54
56
  <Link href="/account/orders">
55
- <Button className="mt-4">Back to Orders</Button>
57
+ <Button className="mt-4">{t("backToOrders")}</Button>
56
58
  </Link>
57
59
  </div>
58
60
  </div>
@@ -106,7 +108,7 @@ export default function OrderDetailPage() {
106
108
  <Button variant="ghost" asChild>
107
109
  <Link href="/account/orders">
108
110
  <ChevronLeft className="mr-2 h-4 w-4" />
109
- Back to Orders
111
+ {t("backToOrders")}
110
112
  </Link>
111
113
  </Button>
112
114
  </div>