@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
@@ -1,8 +1,7 @@
1
1
  "use client";
2
2
 
3
3
  import { useState, useEffect } from "react";
4
- import { useRouter } from "next/navigation";
5
- import Link from "next/link";
4
+ import { useRouter, Link } from "@/i18n/navigation";
6
5
  import { StorefrontError } from "@doswiftly/storefront-sdk";
7
6
  import { useCartStore } from "@/stores/cart-store";
8
7
  import { useCartSync } from "@/hooks/use-cart-sync";
@@ -16,9 +15,12 @@ import {
16
15
  useCheckoutDiscountCodeRemove,
17
16
  useCheckoutGiftCardApply,
18
17
  useCheckoutGiftCardRemove,
18
+ useCheckoutGiftCardRecipientUpdate,
19
19
  useCheckoutComplete,
20
20
  useCheckout,
21
21
  } from "@/lib/graphql/hooks";
22
+ import { useQueryClient } from "@tanstack/react-query";
23
+ import { queryKeys } from "@/lib/graphql/query-keys";
22
24
  import { formatAmount } from "@doswiftly/storefront-sdk";
23
25
  import { Button } from "@/components/ui/button";
24
26
  import { Input } from "@/components/ui/input";
@@ -38,6 +40,7 @@ import { PaymentStep } from "@/components/checkout/payment-step";
38
40
  import type { PaymentMethod } from "@/components/checkout/payment-method-card";
39
41
  import { toast } from "sonner";
40
42
  import { z } from "zod";
43
+ import { useTranslations } from "next-intl";
41
44
  import {
42
45
  Loader2,
43
46
  ChevronLeft,
@@ -58,45 +61,7 @@ import {
58
61
  // CONSTANTS
59
62
  // ============================================================================
60
63
 
61
- const COUNTRIES = [
62
- { code: "PL", name: "Polska" },
63
- { code: "DE", name: "Niemcy" },
64
- { code: "CZ", name: "Czechy" },
65
- { code: "SK", name: "Słowacja" },
66
- { code: "AT", name: "Austria" },
67
- { code: "FR", name: "Francja" },
68
- { code: "NL", name: "Holandia" },
69
- { code: "BE", name: "Belgia" },
70
- { code: "IT", name: "Włochy" },
71
- { code: "ES", name: "Hiszpania" },
72
- { code: "GB", name: "Wielka Brytania" },
73
- { code: "US", name: "Stany Zjednoczone" },
74
- ];
75
-
76
- // ============================================================================
77
- // VALIDATION SCHEMAS
78
- // ============================================================================
79
-
80
- const contactSchema = z.object({
81
- email: z.string().email("Podaj prawidłowy adres email"),
82
- phone: z
83
- .string()
84
- .min(9, "Numer telefonu jest wymagany")
85
- .regex(/^\+?[0-9\s\-]{9,15}$/, "Nieprawidłowy format numeru telefonu"),
86
- });
87
-
88
- const addressSchema = z.object({
89
- firstName: z.string().min(1, "Imię jest wymagane"),
90
- lastName: z.string().min(1, "Nazwisko jest wymagane"),
91
- address1: z.string().min(1, "Adres jest wymagany"),
92
- address2: z.string().optional(),
93
- city: z.string().min(1, "Miasto jest wymagane"),
94
- province: z.string().optional(),
95
- zip: z.string().min(1, "Kod pocztowy jest wymagany"),
96
- country: z.string().min(2, "Kraj jest wymagany"),
97
- phone: z.string().optional(),
98
- company: z.string().optional(),
99
- });
64
+ const COUNTRY_CODES = ["PL", "DE", "CZ", "SK", "AT", "FR", "NL", "BE", "IT", "ES", "GB", "US"] as const;
100
65
 
101
66
  // ============================================================================
102
67
  // TYPES
@@ -166,11 +131,20 @@ interface AddressFormFieldsProps {
166
131
  }
167
132
 
168
133
  function AddressFormFields({ prefix, values, onChange, errors }: AddressFormFieldsProps) {
134
+ const t = useTranslations("checkout");
135
+ const tc = useTranslations("common");
136
+ const tCountries = useTranslations("countries");
137
+
138
+ const countries = COUNTRY_CODES.map((code) => ({
139
+ code,
140
+ name: tCountries(code),
141
+ }));
142
+
169
143
  return (
170
144
  <div className="space-y-4">
171
145
  <div className="grid gap-4 sm:grid-cols-2">
172
146
  <div>
173
- <Label htmlFor={`${prefix}_firstName`}>Imię *</Label>
147
+ <Label htmlFor={`${prefix}_firstName`}>{t("address.firstName")} *</Label>
174
148
  <Input
175
149
  id={`${prefix}_firstName`}
176
150
  value={values.firstName}
@@ -182,7 +156,7 @@ function AddressFormFields({ prefix, values, onChange, errors }: AddressFormFiel
182
156
  )}
183
157
  </div>
184
158
  <div>
185
- <Label htmlFor={`${prefix}_lastName`}>Nazwisko *</Label>
159
+ <Label htmlFor={`${prefix}_lastName`}>{t("address.lastName")} *</Label>
186
160
  <Input
187
161
  id={`${prefix}_lastName`}
188
162
  value={values.lastName}
@@ -196,7 +170,7 @@ function AddressFormFields({ prefix, values, onChange, errors }: AddressFormFiel
196
170
  </div>
197
171
 
198
172
  <div>
199
- <Label htmlFor={`${prefix}_company`}>Firma (opcjonalnie)</Label>
173
+ <Label htmlFor={`${prefix}_company`}>{t("address.company")} ({tc("optional")})</Label>
200
174
  <Input
201
175
  id={`${prefix}_company`}
202
176
  value={values.company}
@@ -205,10 +179,10 @@ function AddressFormFields({ prefix, values, onChange, errors }: AddressFormFiel
205
179
  </div>
206
180
 
207
181
  <div>
208
- <Label htmlFor={`${prefix}_address1`}>Adres *</Label>
182
+ <Label htmlFor={`${prefix}_address1`}>{t("address.address1")} *</Label>
209
183
  <Input
210
184
  id={`${prefix}_address1`}
211
- placeholder="Ulica i numer"
185
+ placeholder={t("address.address1Placeholder")}
212
186
  value={values.address1}
213
187
  onChange={(e) => onChange("address1", e.target.value)}
214
188
  className={errors[`${prefix}_address1`] ? "border-destructive" : ""}
@@ -219,7 +193,7 @@ function AddressFormFields({ prefix, values, onChange, errors }: AddressFormFiel
219
193
  </div>
220
194
 
221
195
  <div>
222
- <Label htmlFor={`${prefix}_address2`}>Mieszkanie, piętro (opcjonalnie)</Label>
196
+ <Label htmlFor={`${prefix}_address2`}>{t("address.address2")} ({tc("optional")})</Label>
223
197
  <Input
224
198
  id={`${prefix}_address2`}
225
199
  value={values.address2}
@@ -229,10 +203,10 @@ function AddressFormFields({ prefix, values, onChange, errors }: AddressFormFiel
229
203
 
230
204
  <div className="grid gap-4 sm:grid-cols-2">
231
205
  <div>
232
- <Label htmlFor={`${prefix}_zip`}>Kod pocztowy *</Label>
206
+ <Label htmlFor={`${prefix}_zip`}>{t("address.zip")} *</Label>
233
207
  <Input
234
208
  id={`${prefix}_zip`}
235
- placeholder="00-000"
209
+ placeholder={t("address.zipPlaceholder")}
236
210
  value={values.zip}
237
211
  onChange={(e) => onChange("zip", e.target.value)}
238
212
  className={errors[`${prefix}_zip`] ? "border-destructive" : ""}
@@ -242,7 +216,7 @@ function AddressFormFields({ prefix, values, onChange, errors }: AddressFormFiel
242
216
  )}
243
217
  </div>
244
218
  <div>
245
- <Label htmlFor={`${prefix}_city`}>Miasto *</Label>
219
+ <Label htmlFor={`${prefix}_city`}>{t("address.city")} *</Label>
246
220
  <Input
247
221
  id={`${prefix}_city`}
248
222
  value={values.city}
@@ -257,13 +231,13 @@ function AddressFormFields({ prefix, values, onChange, errors }: AddressFormFiel
257
231
 
258
232
  <div className="grid gap-4 sm:grid-cols-2">
259
233
  <div>
260
- <Label htmlFor={`${prefix}_country`}>Kraj *</Label>
234
+ <Label htmlFor={`${prefix}_country`}>{t("address.country")} *</Label>
261
235
  <Select value={values.country} onValueChange={(value) => onChange("country", value)}>
262
236
  <SelectTrigger className={errors[`${prefix}_country`] ? "border-destructive" : ""}>
263
- <SelectValue placeholder="Wybierz kraj" />
237
+ <SelectValue placeholder={t("address.countryPlaceholder")} />
264
238
  </SelectTrigger>
265
239
  <SelectContent>
266
- {COUNTRIES.map((country) => (
240
+ {countries.map((country) => (
267
241
  <SelectItem key={country.code} value={country.code}>
268
242
  {country.name}
269
243
  </SelectItem>
@@ -275,7 +249,7 @@ function AddressFormFields({ prefix, values, onChange, errors }: AddressFormFiel
275
249
  )}
276
250
  </div>
277
251
  <div>
278
- <Label htmlFor={`${prefix}_province`}>Województwo (opcjonalnie)</Label>
252
+ <Label htmlFor={`${prefix}_province`}>{t("address.province")} ({tc("optional")})</Label>
279
253
  <Input
280
254
  id={`${prefix}_province`}
281
255
  value={values.province}
@@ -296,6 +270,42 @@ export default function CheckoutPage() {
296
270
  const { cartId, clearCart } = useCartStore();
297
271
  const { items, isLoading: isCartLoading } = useCartSync();
298
272
 
273
+ const t = useTranslations("checkout");
274
+ const tc = useTranslations("common");
275
+ const tCountries = useTranslations("countries");
276
+ const tCart = useTranslations("cart");
277
+
278
+ // ============================================================================
279
+ // VALIDATION SCHEMAS (inside component to use translations)
280
+ // ============================================================================
281
+
282
+ const contactSchema = z.object({
283
+ email: z.string().email(t("validation.emailRequired")),
284
+ phone: z
285
+ .string()
286
+ .min(9, t("validation.phoneRequired"))
287
+ .regex(/^\+?[0-9\s\-]{9,15}$/, t("validation.phoneInvalid")),
288
+ });
289
+
290
+ const addressSchema = z.object({
291
+ firstName: z.string().min(1, t("validation.firstNameRequired")),
292
+ lastName: z.string().min(1, t("validation.lastNameRequired")),
293
+ address1: z.string().min(1, t("validation.addressRequired")),
294
+ address2: z.string().optional(),
295
+ city: z.string().min(1, t("validation.cityRequired")),
296
+ province: z.string().optional(),
297
+ zip: z.string().min(1, t("validation.zipRequired")),
298
+ country: z.string().min(2, t("validation.countryRequired")),
299
+ phone: z.string().optional(),
300
+ company: z.string().optional(),
301
+ });
302
+
303
+ // Countries for review section
304
+ const countries = COUNTRY_CODES.map((code) => ({
305
+ code,
306
+ name: tCountries(code),
307
+ }));
308
+
299
309
  // Step management
300
310
  const [currentStep, setCurrentStep] = useState<CheckoutStep>("contact");
301
311
  const [checkoutId, setCheckoutId] = useState<string | null>(null);
@@ -338,7 +348,9 @@ export default function CheckoutPage() {
338
348
  const removeDiscount = useCheckoutDiscountCodeRemove();
339
349
  const applyGiftCard = useCheckoutGiftCardApply();
340
350
  const removeGiftCard = useCheckoutGiftCardRemove();
351
+ const updateGiftCardRecipient = useCheckoutGiftCardRecipientUpdate();
341
352
  const completeCheckout = useCheckoutComplete();
353
+ const queryClient = useQueryClient();
342
354
 
343
355
  // Query checkout for shipping rates
344
356
  const { data: checkoutData, refetch: refetchCheckout } = useCheckout(checkoutId);
@@ -368,6 +380,7 @@ export default function CheckoutPage() {
368
380
  removeDiscount.isPending ||
369
381
  applyGiftCard.isPending ||
370
382
  removeGiftCard.isPending ||
383
+ updateGiftCardRecipient.isPending ||
371
384
  completeCheckout.isPending;
372
385
 
373
386
  // Calculate totals from checkout or cart
@@ -399,6 +412,8 @@ export default function CheckoutPage() {
399
412
  setErrors({});
400
413
 
401
414
  try {
415
+ let newCheckoutId = checkoutId;
416
+
402
417
  // Create checkout if not exists
403
418
  if (!checkoutId) {
404
419
  const createResult = await createCheckout.mutateAsync({
@@ -414,7 +429,8 @@ export default function CheckoutPage() {
414
429
  }
415
430
 
416
431
  if (createResult.checkoutCreate.checkout) {
417
- setCheckoutId(createResult.checkoutCreate.checkout.id);
432
+ newCheckoutId = createResult.checkoutCreate.checkout.id;
433
+ setCheckoutId(newCheckoutId);
418
434
  }
419
435
  } else {
420
436
  // Update email on existing checkout
@@ -429,9 +445,37 @@ export default function CheckoutPage() {
429
445
  }
430
446
  }
431
447
 
448
+ // Send gift card recipient data (if any)
449
+ if (hasGiftCards && newCheckoutId) {
450
+ const giftCardItems = items.filter((item) => item.productType === "GIFT_CARD");
451
+ const recipientMutations = giftCardItems
452
+ .filter((gc) => giftCardRecipients[gc.lineId]?.recipientEmail)
453
+ .map((gc) =>
454
+ updateGiftCardRecipient.mutateAsync({
455
+ input: {
456
+ checkoutId: newCheckoutId!,
457
+ lineItemId: gc.lineId,
458
+ ...giftCardRecipients[gc.lineId],
459
+ },
460
+ })
461
+ );
462
+
463
+ if (recipientMutations.length > 0) {
464
+ const results = await Promise.allSettled(recipientMutations);
465
+ const failed = results.filter((r) => r.status === "rejected");
466
+ if (failed.length > 0) {
467
+ toast.warning(
468
+ t("giftCard.recipientSaveError", { count: failed.length })
469
+ );
470
+ }
471
+ // Refetch checkout after recipient updates — prevents stale UI
472
+ await queryClient.refetchQueries({ queryKey: queryKeys.checkout.detail(newCheckoutId, currencyCode) });
473
+ }
474
+ }
475
+
432
476
  setCurrentStep(allGiftCards ? "payment" : "shipping");
433
477
  } catch (error: unknown) {
434
- const message = error instanceof Error ? error.message : "Nie udało się przetworzyć danych kontaktowych";
478
+ const message = error instanceof Error ? error.message : t("errors.contactProcessing");
435
479
  toast.error(message);
436
480
  }
437
481
  };
@@ -472,7 +516,7 @@ export default function CheckoutPage() {
472
516
  setErrors({});
473
517
 
474
518
  if (!checkoutId) {
475
- toast.error("Checkout nie znaleziony. Wróć i spróbuj ponownie.");
519
+ toast.error(t("errors.checkoutNotFound"));
476
520
  return;
477
521
  }
478
522
 
@@ -513,7 +557,7 @@ export default function CheckoutPage() {
513
557
  await refetchCheckout();
514
558
  setCurrentStep("delivery");
515
559
  } catch (error: unknown) {
516
- const message = error instanceof Error ? error.message : "Nie udało się zaktualizować adresu";
560
+ const message = error instanceof Error ? error.message : t("errors.addressUpdate");
517
561
  toast.error(message);
518
562
  }
519
563
  };
@@ -524,13 +568,13 @@ export default function CheckoutPage() {
524
568
 
525
569
  const handleDeliverySubmit = async () => {
526
570
  if (!formState.selectedShippingRate) {
527
- setErrors({ shipping: "Wybierz metodę dostawy" });
571
+ setErrors({ shipping: t("delivery.selectMethod") });
528
572
  return;
529
573
  }
530
574
  setErrors({});
531
575
 
532
576
  if (!checkoutId) {
533
- toast.error("Checkout nie znaleziony. Wróć i spróbuj ponownie.");
577
+ toast.error(t("errors.checkoutNotFound"));
534
578
  return;
535
579
  }
536
580
 
@@ -548,7 +592,7 @@ export default function CheckoutPage() {
548
592
  await refetchCheckout();
549
593
  setCurrentStep("payment");
550
594
  } catch (error: unknown) {
551
- const message = error instanceof Error ? error.message : "Nie udało się wybrać metody dostawy";
595
+ const message = error instanceof Error ? error.message : t("errors.deliverySelect");
552
596
  toast.error(message);
553
597
  }
554
598
  };
@@ -567,7 +611,7 @@ export default function CheckoutPage() {
567
611
  const supportsCurrency = selectedMethod.supportedCurrencies.includes(currencyCode);
568
612
  if (!supportsCurrency) {
569
613
  setErrors({
570
- payment: `Ta metoda płatności nie obsługuje waluty ${currencyCode}. Wybierz inną metodę.`,
614
+ payment: t("payment.unsupportedCurrency", { currency: currencyCode }),
571
615
  });
572
616
  return;
573
617
  }
@@ -579,7 +623,7 @@ export default function CheckoutPage() {
579
623
 
580
624
  const handlePaymentSubmit = async () => {
581
625
  if (!formState.selectedPaymentMethodId) {
582
- setErrors({ payment: "Wybierz metodę płatności" });
626
+ setErrors({ payment: t("payment.selectMethod") });
583
627
  return;
584
628
  }
585
629
 
@@ -589,7 +633,7 @@ export default function CheckoutPage() {
589
633
  );
590
634
 
591
635
  if (!selectedMethod) {
592
- setErrors({ payment: "Wybrana metoda płatności jest niedostępna. Wybierz inną metodę." });
636
+ setErrors({ payment: t("payment.unavailable") });
593
637
  setFormState((prev) => ({ ...prev, selectedPaymentMethodId: null }));
594
638
  return;
595
639
  }
@@ -599,7 +643,7 @@ export default function CheckoutPage() {
599
643
  const supportsCurrency = selectedMethod.supportedCurrencies.includes(currencyCode);
600
644
  if (!supportsCurrency) {
601
645
  setErrors({
602
- payment: `Metoda "${selectedMethod.name}" nie obsługuje waluty ${currencyCode}. Wybierz inną metodę.`,
646
+ payment: t("payment.unsupportedCurrency", { currency: currencyCode }),
603
647
  });
604
648
  return;
605
649
  }
@@ -619,7 +663,7 @@ export default function CheckoutPage() {
619
663
  }
620
664
 
621
665
  if (!checkoutId) {
622
- toast.error("Checkout nie znaleziony");
666
+ toast.error(t("errors.checkoutNotFound"));
623
667
  return;
624
668
  }
625
669
 
@@ -637,7 +681,7 @@ export default function CheckoutPage() {
637
681
  }
638
682
 
639
683
  if (!payload?.checkout) {
640
- toast.error("Nie udało się zastosować kodu rabatowego");
684
+ toast.error(t("errors.discountApply"));
641
685
  return;
642
686
  }
643
687
 
@@ -647,9 +691,9 @@ export default function CheckoutPage() {
647
691
  discountCode: "",
648
692
  }));
649
693
  await refetchCheckout();
650
- toast.success("Kod rabatowy został zastosowany");
694
+ toast.success(t("discount.applied"));
651
695
  } catch (error: unknown) {
652
- const message = error instanceof Error ? error.message : "Nie udało się zastosować kodu rabatowego";
696
+ const message = error instanceof Error ? error.message : t("errors.discountApply");
653
697
  toast.error(message);
654
698
  }
655
699
  };
@@ -675,9 +719,9 @@ export default function CheckoutPage() {
675
719
  appliedDiscountCode: null,
676
720
  }));
677
721
  await refetchCheckout();
678
- toast.success("Kod rabatowy został usunięty");
722
+ toast.success(t("discount.removed"));
679
723
  } catch (error: unknown) {
680
- const message = error instanceof Error ? error.message : "Nie udało się usunąć kodu rabatowego";
724
+ const message = error instanceof Error ? error.message : t("errors.discountRemove");
681
725
  toast.error(message);
682
726
  }
683
727
  };
@@ -692,7 +736,7 @@ export default function CheckoutPage() {
692
736
  }
693
737
 
694
738
  if (!checkoutId) {
695
- toast.error("Checkout nie znaleziony");
739
+ toast.error(t("errors.checkoutNotFound"));
696
740
  return;
697
741
  }
698
742
 
@@ -706,11 +750,11 @@ export default function CheckoutPage() {
706
750
  const error = result.checkoutGiftCardApply.userErrors[0];
707
751
  // Translate common error codes to user-friendly messages
708
752
  const errorMessages: Record<string, string> = {
709
- GIFT_CARD_NOT_FOUND: "Nie znaleziono karty podarunkowej o podanym kodzie",
710
- GIFT_CARD_EXPIRED: "Ta karta podarunkowa wygasła",
711
- GIFT_CARD_DISABLED: "Ta karta podarunkowa jest wyłączona",
712
- GIFT_CARD_DEPLETED: "Ta karta podarunkowa nie ma dostępnego salda",
713
- GIFT_CARD_UNUSABLE: "Ta karta jest już zastosowana",
753
+ GIFT_CARD_NOT_FOUND: t("errors.giftCardNotFound"),
754
+ GIFT_CARD_EXPIRED: t("errors.giftCardExpired"),
755
+ GIFT_CARD_DISABLED: t("errors.giftCardDisabled"),
756
+ GIFT_CARD_DEPLETED: t("errors.giftCardDepleted"),
757
+ GIFT_CARD_UNUSABLE: t("errors.giftCardUnusable"),
714
758
  };
715
759
  toast.error((error.code && errorMessages[error.code]) || error.message);
716
760
  return;
@@ -721,9 +765,9 @@ export default function CheckoutPage() {
721
765
  giftCardCode: "",
722
766
  }));
723
767
  await refetchCheckout();
724
- toast.success("Karta podarunkowa została zastosowana");
768
+ toast.success(tCart("giftCardApplied"));
725
769
  } catch (error: unknown) {
726
- const message = error instanceof Error ? error.message : "Nie udało się zastosować karty podarunkowej";
770
+ const message = error instanceof Error ? error.message : t("errors.giftCardApply");
727
771
  toast.error(message);
728
772
  }
729
773
  };
@@ -745,9 +789,9 @@ export default function CheckoutPage() {
745
789
  }
746
790
 
747
791
  await refetchCheckout();
748
- toast.success("Karta podarunkowa została usunięta");
792
+ toast.success(tCart("giftCardRemoved"));
749
793
  } catch (error: unknown) {
750
- const message = error instanceof Error ? error.message : "Nie udało się usunąć karty podarunkowej";
794
+ const message = error instanceof Error ? error.message : t("errors.giftCardRemove");
751
795
  toast.error(message);
752
796
  }
753
797
  };
@@ -758,12 +802,12 @@ export default function CheckoutPage() {
758
802
 
759
803
  const handleCompleteCheckout = async () => {
760
804
  if (!formState.acceptTerms) {
761
- setErrors({ terms: "Musisz zaakceptować regulamin" });
805
+ setErrors({ terms: t("review.mustAcceptTerms") });
762
806
  return;
763
807
  }
764
808
 
765
809
  if (!formState.selectedPaymentMethodId) {
766
- setErrors({ payment: "Wybierz metodę płatności" });
810
+ setErrors({ payment: t("payment.selectMethod") });
767
811
  setCurrentStep("payment");
768
812
  return;
769
813
  }
@@ -771,7 +815,7 @@ export default function CheckoutPage() {
771
815
  setErrors({});
772
816
 
773
817
  if (!checkoutId) {
774
- toast.error("Checkout nie znaleziony. Wróć i spróbuj ponownie.");
818
+ toast.error(t("errors.checkoutNotFound"));
775
819
  return;
776
820
  }
777
821
 
@@ -794,19 +838,19 @@ export default function CheckoutPage() {
794
838
  switch (codeStr) {
795
839
  case "INVALID_PAYMENT_METHOD":
796
840
  case "NO_PAYMENT_METHODS":
797
- setErrors({ payment: "Wybrana metoda płatności jest niedostępna. Wybierz inną metodę." });
841
+ setErrors({ payment: t("payment.unavailable") });
798
842
  setFormState((prev) => ({ ...prev, selectedPaymentMethodId: null }));
799
843
  setCurrentStep("payment");
800
- toast.error("Metoda płatności jest niedostępna");
844
+ toast.error(t("errors.paymentUnavailable"));
801
845
  break;
802
846
  case "ALREADY_COMPLETED":
803
- toast.error("Sesja checkout wygasła. Proszę odświeżyć stronę i spróbować ponownie.");
847
+ toast.error(t("errors.sessionExpired"));
804
848
  break;
805
849
  case "NOT_ENOUGH_IN_STOCK":
806
- toast.error("Niektóre produkty są niedostępne. Sprawdź swój koszyk.");
850
+ toast.error(t("errors.outOfStock"));
807
851
  break;
808
852
  default:
809
- toast.error(errorMessage || "Nie udało się sfinalizować zamówienia");
853
+ toast.error(errorMessage || t("errors.checkoutComplete"));
810
854
  }
811
855
  return;
812
856
  }
@@ -831,10 +875,10 @@ export default function CheckoutPage() {
831
875
  router.push(`/checkout/success/${orderId}${searchParams}`);
832
876
  } else {
833
877
  // Fallback: no payment URL and no order - show error
834
- toast.error("Wystąpił nieoczekiwany błąd. Skontaktuj się z obsługą.");
878
+ toast.error(t("errors.unexpected"));
835
879
  }
836
880
  } catch (error: unknown) {
837
- const message = error instanceof Error ? error.message : "Nie udało się sfinalizować zamówienia";
881
+ const message = error instanceof Error ? error.message : t("errors.checkoutComplete");
838
882
  toast.error(message);
839
883
  }
840
884
  };
@@ -865,7 +909,7 @@ export default function CheckoutPage() {
865
909
  return (
866
910
  <div className="container mx-auto px-4 py-16 text-center">
867
911
  <Loader2 className="mx-auto h-8 w-8 animate-spin text-muted-foreground" />
868
- <p className="mt-4 text-muted-foreground">Ładowanie...</p>
912
+ <p className="mt-4 text-muted-foreground">{tc("loading")}</p>
869
913
  </div>
870
914
  );
871
915
  }
@@ -875,12 +919,12 @@ export default function CheckoutPage() {
875
919
  return (
876
920
  <div className="container mx-auto px-4 py-16 text-center">
877
921
  <ShoppingBag className="mx-auto h-16 w-16 text-muted-foreground" />
878
- <h1 className="mt-4 text-2xl font-bold">Twój koszyk jest pusty</h1>
922
+ <h1 className="mt-4 text-2xl font-bold">{tCart("empty")}</h1>
879
923
  <p className="mt-2 text-muted-foreground">
880
- Dodaj produkty do koszyka, aby kontynuować zakupy.
924
+ {tCart("emptyDescription")}
881
925
  </p>
882
926
  <Button className="mt-6" asChild>
883
- <Link href="/products">Przeglądaj produkty</Link>
927
+ <Link href="/products">{tCart("browseProducts")}</Link>
884
928
  </Button>
885
929
  </div>
886
930
  );
@@ -891,11 +935,11 @@ export default function CheckoutPage() {
891
935
  // ============================================================================
892
936
 
893
937
  const allSteps: { id: CheckoutStep; label: string; icon: React.ReactNode }[] = [
894
- { id: "contact", label: "Kontakt", icon: <User className="h-4 w-4" /> },
895
- { id: "shipping", label: "Adres", icon: <Package className="h-4 w-4" /> },
896
- { id: "delivery", label: "Dostawa", icon: <Truck className="h-4 w-4" /> },
897
- { id: "payment", label: "Płatność", icon: <CreditCard className="h-4 w-4" /> },
898
- { id: "review", label: "Podsumowanie", icon: <ClipboardCheck className="h-4 w-4" /> },
938
+ { id: "contact", label: t("steps.contact"), icon: <User className="h-4 w-4" /> },
939
+ { id: "shipping", label: t("steps.shipping"), icon: <Package className="h-4 w-4" /> },
940
+ { id: "delivery", label: t("steps.delivery"), icon: <Truck className="h-4 w-4" /> },
941
+ { id: "payment", label: t("steps.payment"), icon: <CreditCard className="h-4 w-4" /> },
942
+ { id: "review", label: t("steps.review"), icon: <ClipboardCheck className="h-4 w-4" /> },
899
943
  ];
900
944
  // Skip shipping and delivery steps when all items are gift cards
901
945
  const steps = allGiftCards
@@ -913,9 +957,9 @@ export default function CheckoutPage() {
913
957
  <Breadcrumbs className="mb-6" />
914
958
 
915
959
  <div className="mb-8 flex items-center justify-between">
916
- <h1 className="text-3xl font-bold">Checkout</h1>
960
+ <h1 className="text-3xl font-bold">{t("title")}</h1>
917
961
  <Link href="/auth/login" className="text-sm text-primary hover:underline">
918
- Masz konto? Zaloguj się
962
+ {t("haveAccount")}
919
963
  </Link>
920
964
  </div>
921
965
 
@@ -963,19 +1007,19 @@ export default function CheckoutPage() {
963
1007
  <CardHeader>
964
1008
  <CardTitle className="flex items-center gap-2">
965
1009
  <User className="h-5 w-5" />
966
- Dane kontaktowe
1010
+ {t("contact.title")}
967
1011
  </CardTitle>
968
1012
  <CardDescription>
969
- Podaj swój email i telefon - wyślemy na nie potwierdzenie zamówienia
1013
+ {t("contact.description")}
970
1014
  </CardDescription>
971
1015
  </CardHeader>
972
1016
  <CardContent className="space-y-4">
973
1017
  <div>
974
- <Label htmlFor="email">Email *</Label>
1018
+ <Label htmlFor="email">{t("contact.email")} *</Label>
975
1019
  <Input
976
1020
  id="email"
977
1021
  type="email"
978
- placeholder="twoj@email.pl"
1022
+ placeholder={t("contact.emailPlaceholder")}
979
1023
  value={formState.email}
980
1024
  onChange={(e) =>
981
1025
  setFormState((prev) => ({ ...prev, email: e.target.value }))
@@ -988,11 +1032,11 @@ export default function CheckoutPage() {
988
1032
  </div>
989
1033
 
990
1034
  <div>
991
- <Label htmlFor="phone">Telefon *</Label>
1035
+ <Label htmlFor="phone">{t("contact.phone")} *</Label>
992
1036
  <Input
993
1037
  id="phone"
994
1038
  type="tel"
995
- placeholder="+48 123 456 789"
1039
+ placeholder={t("contact.phonePlaceholder")}
996
1040
  value={formState.phone}
997
1041
  onChange={(e) =>
998
1042
  setFormState((prev) => ({ ...prev, phone: e.target.value }))
@@ -1003,7 +1047,7 @@ export default function CheckoutPage() {
1003
1047
  <p className="mt-1 text-sm text-destructive">{errors.phone}</p>
1004
1048
  )}
1005
1049
  <p className="mt-1 text-xs text-muted-foreground">
1006
- Kurier może potrzebować kontaktu w sprawie dostawy
1050
+ {t("contact.phoneHint")}
1007
1051
  </p>
1008
1052
  </div>
1009
1053
 
@@ -1016,7 +1060,7 @@ export default function CheckoutPage() {
1016
1060
  }
1017
1061
  />
1018
1062
  <Label htmlFor="marketing" className="text-sm font-normal cursor-pointer">
1019
- Chcę otrzymywać informacje o promocjach i nowościach
1063
+ {t("contact.marketing")}
1020
1064
  </Label>
1021
1065
  </div>
1022
1066
 
@@ -1025,28 +1069,28 @@ export default function CheckoutPage() {
1025
1069
  <div className="space-y-4 border-t pt-4">
1026
1070
  <div className="flex items-center gap-2 text-sm font-medium">
1027
1071
  <Gift className="h-4 w-4" />
1028
- Dane odbiorcy karty podarunkowej
1072
+ {t("giftCard.recipientTitle")}
1029
1073
  </div>
1030
1074
  <p className="text-xs text-muted-foreground">
1031
- Opcjonalnie: podaj dane osoby, która otrzyma kartę podarunkową
1075
+ {t("giftCard.recipientDescription")}
1032
1076
  </p>
1033
1077
  {items
1034
1078
  .filter((item) => item.productType === "GIFT_CARD")
1035
1079
  .map((item) => (
1036
- <div key={item.variantId} className="space-y-3 rounded-lg border p-4">
1080
+ <div key={item.lineId} className="space-y-3 rounded-lg border p-4">
1037
1081
  <p className="text-sm font-medium">{item.productTitle} — {item.variantTitle}</p>
1038
1082
  <div>
1039
- <Label htmlFor={`recipient-email-${item.variantId}`}>E-mail odbiorcy</Label>
1083
+ <Label htmlFor={`recipient-email-${item.lineId}`}>{t("giftCard.recipientEmail")}</Label>
1040
1084
  <Input
1041
- id={`recipient-email-${item.variantId}`}
1085
+ id={`recipient-email-${item.lineId}`}
1042
1086
  type="email"
1043
- placeholder="odbiorca@email.pl"
1044
- value={giftCardRecipients[item.variantId]?.recipientEmail || ""}
1087
+ placeholder={t("giftCard.recipientEmailPlaceholder")}
1088
+ value={giftCardRecipients[item.lineId]?.recipientEmail || ""}
1045
1089
  onChange={(e) =>
1046
1090
  setGiftCardRecipients((prev) => ({
1047
1091
  ...prev,
1048
- [item.variantId]: {
1049
- ...prev[item.variantId],
1092
+ [item.lineId]: {
1093
+ ...prev[item.lineId],
1050
1094
  recipientEmail: e.target.value,
1051
1095
  },
1052
1096
  }))
@@ -1054,17 +1098,17 @@ export default function CheckoutPage() {
1054
1098
  />
1055
1099
  </div>
1056
1100
  <div>
1057
- <Label htmlFor={`recipient-name-${item.variantId}`}>Imię odbiorcy</Label>
1101
+ <Label htmlFor={`recipient-name-${item.lineId}`}>{t("giftCard.recipientName")}</Label>
1058
1102
  <Input
1059
- id={`recipient-name-${item.variantId}`}
1103
+ id={`recipient-name-${item.lineId}`}
1060
1104
  type="text"
1061
- placeholder="Jan"
1062
- value={giftCardRecipients[item.variantId]?.recipientName || ""}
1105
+ placeholder={t("giftCard.recipientNamePlaceholder")}
1106
+ value={giftCardRecipients[item.lineId]?.recipientName || ""}
1063
1107
  onChange={(e) =>
1064
1108
  setGiftCardRecipients((prev) => ({
1065
1109
  ...prev,
1066
- [item.variantId]: {
1067
- ...prev[item.variantId],
1110
+ [item.lineId]: {
1111
+ ...prev[item.lineId],
1068
1112
  recipientName: e.target.value,
1069
1113
  },
1070
1114
  }))
@@ -1072,17 +1116,17 @@ export default function CheckoutPage() {
1072
1116
  />
1073
1117
  </div>
1074
1118
  <div>
1075
- <Label htmlFor={`recipient-message-${item.variantId}`}>Wiadomość</Label>
1119
+ <Label htmlFor={`recipient-message-${item.lineId}`}>{t("giftCard.message")}</Label>
1076
1120
  <Input
1077
- id={`recipient-message-${item.variantId}`}
1121
+ id={`recipient-message-${item.lineId}`}
1078
1122
  type="text"
1079
- placeholder="Wszystkiego najlepszego!"
1080
- value={giftCardRecipients[item.variantId]?.message || ""}
1123
+ placeholder={t("giftCard.messagePlaceholder")}
1124
+ value={giftCardRecipients[item.lineId]?.message || ""}
1081
1125
  onChange={(e) =>
1082
1126
  setGiftCardRecipients((prev) => ({
1083
1127
  ...prev,
1084
- [item.variantId]: {
1085
- ...prev[item.variantId],
1128
+ [item.lineId]: {
1129
+ ...prev[item.lineId],
1086
1130
  message: e.target.value,
1087
1131
  },
1088
1132
  }))
@@ -1100,7 +1144,7 @@ export default function CheckoutPage() {
1100
1144
  ) : (
1101
1145
  <ChevronRight className="mr-2 h-4 w-4" />
1102
1146
  )}
1103
- {allGiftCards ? "Dalej: Płatność" : "Dalej: Adres dostawy"}
1147
+ {allGiftCards ? t("contact.nextPayment") : t("contact.nextShipping")}
1104
1148
  </Button>
1105
1149
  </CardContent>
1106
1150
  </Card>
@@ -1113,9 +1157,9 @@ export default function CheckoutPage() {
1113
1157
  <CardHeader>
1114
1158
  <CardTitle className="flex items-center gap-2">
1115
1159
  <Package className="h-5 w-5" />
1116
- Adres dostawy
1160
+ {t("shipping.title")}
1117
1161
  </CardTitle>
1118
- <CardDescription>Podaj adres, na który mamy wysłać zamówienie</CardDescription>
1162
+ <CardDescription>{t("shipping.description")}</CardDescription>
1119
1163
  </CardHeader>
1120
1164
  <CardContent>
1121
1165
  <AddressFormFields
@@ -1136,7 +1180,7 @@ export default function CheckoutPage() {
1136
1180
  <CardHeader>
1137
1181
  <CardTitle className="flex items-center gap-2">
1138
1182
  <CreditCard className="h-5 w-5" />
1139
- Adres rozliczeniowy
1183
+ {t("shipping.billingTitle")}
1140
1184
  </CardTitle>
1141
1185
  </CardHeader>
1142
1186
  <CardContent className="space-y-4">
@@ -1149,7 +1193,7 @@ export default function CheckoutPage() {
1149
1193
  }
1150
1194
  />
1151
1195
  <Label htmlFor="sameAsBilling" className="cursor-pointer">
1152
- Taki sam jak adres dostawy
1196
+ {t("shipping.sameAsShipping")}
1153
1197
  </Label>
1154
1198
  </div>
1155
1199
 
@@ -1172,7 +1216,7 @@ export default function CheckoutPage() {
1172
1216
  <div className="flex gap-4">
1173
1217
  <Button variant="outline" onClick={goBack} size="lg">
1174
1218
  <ChevronLeft className="mr-2 h-4 w-4" />
1175
- Wstecz
1219
+ {tc("back")}
1176
1220
  </Button>
1177
1221
  <Button onClick={handleShippingSubmit} disabled={isLoading} className="flex-1" size="lg">
1178
1222
  {isLoading ? (
@@ -1180,7 +1224,7 @@ export default function CheckoutPage() {
1180
1224
  ) : (
1181
1225
  <ChevronRight className="mr-2 h-4 w-4" />
1182
1226
  )}
1183
- Dalej: Metoda dostawy
1227
+ {t("shipping.nextDelivery")}
1184
1228
  </Button>
1185
1229
  </div>
1186
1230
  </>
@@ -1192,23 +1236,23 @@ export default function CheckoutPage() {
1192
1236
  <CardHeader>
1193
1237
  <CardTitle className="flex items-center gap-2">
1194
1238
  <Truck className="h-5 w-5" />
1195
- Metoda dostawy
1239
+ {t("delivery.title")}
1196
1240
  </CardTitle>
1197
- <CardDescription>Wybierz sposób dostarczenia zamówienia</CardDescription>
1241
+ <CardDescription>{t("delivery.description")}</CardDescription>
1198
1242
  </CardHeader>
1199
1243
  <CardContent className="space-y-4">
1200
1244
  {!shippingRatesReady ? (
1201
1245
  <div className="flex items-center justify-center py-8">
1202
1246
  <Loader2 className="h-6 w-6 animate-spin text-muted-foreground" />
1203
- <span className="ml-2 text-muted-foreground">Ładowanie metod dostawy...</span>
1247
+ <span className="ml-2 text-muted-foreground">{t("delivery.loading")}</span>
1204
1248
  </div>
1205
1249
  ) : availableShippingRates.length === 0 ? (
1206
1250
  <div className="rounded-lg border border-destructive/50 bg-destructive/10 p-4 text-center">
1207
1251
  <p className="text-destructive">
1208
- Brak dostępnych metod dostawy dla podanego adresu.
1252
+ {t("delivery.noMethods")}
1209
1253
  </p>
1210
1254
  <Button variant="outline" onClick={goBack} className="mt-4">
1211
- Zmień adres dostawy
1255
+ {t("delivery.changeAddress")}
1212
1256
  </Button>
1213
1257
  </div>
1214
1258
  ) : (
@@ -1251,7 +1295,7 @@ export default function CheckoutPage() {
1251
1295
  <div className="flex gap-4 pt-4">
1252
1296
  <Button variant="outline" onClick={goBack} size="lg">
1253
1297
  <ChevronLeft className="mr-2 h-4 w-4" />
1254
- Wstecz
1298
+ {tc("back")}
1255
1299
  </Button>
1256
1300
  <Button
1257
1301
  onClick={handleDeliverySubmit}
@@ -1264,7 +1308,7 @@ export default function CheckoutPage() {
1264
1308
  ) : (
1265
1309
  <ChevronRight className="mr-2 h-4 w-4" />
1266
1310
  )}
1267
- Dalej: Płatność
1311
+ {t("delivery.nextPayment")}
1268
1312
  </Button>
1269
1313
  </div>
1270
1314
  </CardContent>
@@ -1277,9 +1321,9 @@ export default function CheckoutPage() {
1277
1321
  <CardHeader>
1278
1322
  <CardTitle className="flex items-center gap-2">
1279
1323
  <CreditCard className="h-5 w-5" />
1280
- Metoda płatności
1324
+ {t("payment.title")}
1281
1325
  </CardTitle>
1282
- <CardDescription>Wybierz sposób zapłaty za zamówienie</CardDescription>
1326
+ <CardDescription>{t("payment.description")}</CardDescription>
1283
1327
  </CardHeader>
1284
1328
  <CardContent className="space-y-4">
1285
1329
  {/* PaymentStep component handles payment method selection */}
@@ -1309,7 +1353,7 @@ export default function CheckoutPage() {
1309
1353
  <div className="flex gap-4 pt-4">
1310
1354
  <Button variant="outline" onClick={goBack} size="lg">
1311
1355
  <ChevronLeft className="mr-2 h-4 w-4" />
1312
- Wstecz
1356
+ {tc("back")}
1313
1357
  </Button>
1314
1358
  <Button
1315
1359
  onClick={handlePaymentSubmit}
@@ -1322,7 +1366,7 @@ export default function CheckoutPage() {
1322
1366
  ) : (
1323
1367
  <ChevronRight className="mr-2 h-4 w-4" />
1324
1368
  )}
1325
- Dalej: Podsumowanie
1369
+ {t("payment.nextReview")}
1326
1370
  </Button>
1327
1371
  </div>
1328
1372
  </CardContent>
@@ -1335,15 +1379,15 @@ export default function CheckoutPage() {
1335
1379
  <CardHeader>
1336
1380
  <CardTitle className="flex items-center gap-2">
1337
1381
  <ClipboardCheck className="h-5 w-5" />
1338
- Podsumowanie zamówienia
1382
+ {t("review.title")}
1339
1383
  </CardTitle>
1340
- <CardDescription>Sprawdź wszystkie dane przed finalizacją</CardDescription>
1384
+ <CardDescription>{t("review.description")}</CardDescription>
1341
1385
  </CardHeader>
1342
1386
  <CardContent className="space-y-6">
1343
1387
  {/* Contact */}
1344
1388
  <div className="flex items-start justify-between rounded-lg border p-4">
1345
1389
  <div>
1346
- <h3 className="mb-1 font-medium">Kontakt</h3>
1390
+ <h3 className="mb-1 font-medium">{t("review.contact")}</h3>
1347
1391
  <p className="text-sm text-muted-foreground">{checkout.email}</p>
1348
1392
  <p className="text-sm text-muted-foreground">{formState.phone}</p>
1349
1393
  </div>
@@ -1352,7 +1396,7 @@ export default function CheckoutPage() {
1352
1396
  size="sm"
1353
1397
  onClick={() => setCurrentStep("contact")}
1354
1398
  >
1355
- Zmień
1399
+ {t("review.change")}
1356
1400
  </Button>
1357
1401
  </div>
1358
1402
 
@@ -1360,7 +1404,7 @@ export default function CheckoutPage() {
1360
1404
  {!allGiftCards && (
1361
1405
  <div className="flex items-start justify-between rounded-lg border p-4">
1362
1406
  <div>
1363
- <h3 className="mb-1 font-medium">Adres dostawy</h3>
1407
+ <h3 className="mb-1 font-medium">{t("review.shippingAddress")}</h3>
1364
1408
  {checkout.shippingAddress && (
1365
1409
  <p className="text-sm text-muted-foreground">
1366
1410
  {checkout.shippingAddress.firstName} {checkout.shippingAddress.lastName}
@@ -1375,7 +1419,7 @@ export default function CheckoutPage() {
1375
1419
  <br />
1376
1420
  {checkout.shippingAddress.zip} {checkout.shippingAddress.city}
1377
1421
  <br />
1378
- {COUNTRIES.find((c) => c.code === checkout.shippingAddress?.country)?.name ||
1422
+ {countries.find((c) => c.code === checkout.shippingAddress?.country)?.name ||
1379
1423
  checkout.shippingAddress.country}
1380
1424
  </p>
1381
1425
  )}
@@ -1385,7 +1429,7 @@ export default function CheckoutPage() {
1385
1429
  size="sm"
1386
1430
  onClick={() => setCurrentStep("shipping")}
1387
1431
  >
1388
- Zmień
1432
+ {t("review.change")}
1389
1433
  </Button>
1390
1434
  </div>
1391
1435
  )}
@@ -1394,7 +1438,7 @@ export default function CheckoutPage() {
1394
1438
  {!allGiftCards && (
1395
1439
  <div className="flex items-start justify-between rounded-lg border p-4">
1396
1440
  <div>
1397
- <h3 className="mb-1 font-medium">Metoda dostawy</h3>
1441
+ <h3 className="mb-1 font-medium">{t("review.shippingMethod")}</h3>
1398
1442
  {checkout.shippingLine && (
1399
1443
  <p className="text-sm text-muted-foreground">
1400
1444
  {checkout.shippingLine.title} -{" "}
@@ -1407,7 +1451,7 @@ export default function CheckoutPage() {
1407
1451
  size="sm"
1408
1452
  onClick={() => setCurrentStep("delivery")}
1409
1453
  >
1410
- Zmień
1454
+ {t("review.change")}
1411
1455
  </Button>
1412
1456
  </div>
1413
1457
  )}
@@ -1415,12 +1459,12 @@ export default function CheckoutPage() {
1415
1459
  {/* Payment Method */}
1416
1460
  <div className="flex items-start justify-between rounded-lg border p-4">
1417
1461
  <div>
1418
- <h3 className="mb-1 font-medium">Metoda płatności</h3>
1462
+ <h3 className="mb-1 font-medium">{t("review.paymentMethod")}</h3>
1419
1463
  {formState.selectedPaymentMethodId && (
1420
1464
  <p className="text-sm text-muted-foreground">
1421
1465
  {checkout.availablePaymentMethods?.find(
1422
1466
  (pm) => pm.id === formState.selectedPaymentMethodId
1423
- )?.name || "Wybrana metoda płatności"}
1467
+ )?.name || t("review.paymentMethod")}
1424
1468
  </p>
1425
1469
  )}
1426
1470
  </div>
@@ -1429,7 +1473,7 @@ export default function CheckoutPage() {
1429
1473
  size="sm"
1430
1474
  onClick={() => setCurrentStep("payment")}
1431
1475
  >
1432
- Zmień
1476
+ {t("review.change")}
1433
1477
  </Button>
1434
1478
  </div>
1435
1479
 
@@ -1446,13 +1490,13 @@ export default function CheckoutPage() {
1446
1490
  />
1447
1491
  <div className="grid gap-1.5 leading-none">
1448
1492
  <Label htmlFor="terms" className="cursor-pointer">
1449
- Akceptuję{" "}
1493
+ {t("review.acceptTerms")}{" "}
1450
1494
  <Link href="/terms" className="text-primary hover:underline">
1451
- regulamin sklepu
1495
+ {t("review.termsLink")}
1452
1496
  </Link>{" "}
1453
- oraz{" "}
1497
+ {t("review.and")}{" "}
1454
1498
  <Link href="/privacy" className="text-primary hover:underline">
1455
- politykę prywatności
1499
+ {t("review.privacyLink")}
1456
1500
  </Link>{" "}
1457
1501
  *
1458
1502
  </Label>
@@ -1466,7 +1510,7 @@ export default function CheckoutPage() {
1466
1510
  <div className="flex gap-4">
1467
1511
  <Button variant="outline" onClick={goBack} size="lg">
1468
1512
  <ChevronLeft className="mr-2 h-4 w-4" />
1469
- Wstecz
1513
+ {tc("back")}
1470
1514
  </Button>
1471
1515
  <Button
1472
1516
  onClick={handleCompleteCheckout}
@@ -1479,7 +1523,7 @@ export default function CheckoutPage() {
1479
1523
  ) : (
1480
1524
  <CreditCard className="mr-2 h-4 w-4" />
1481
1525
  )}
1482
- Zapłać i zamów
1526
+ {t("review.placeOrder")}
1483
1527
  </Button>
1484
1528
  </div>
1485
1529
  </CardContent>
@@ -1493,7 +1537,7 @@ export default function CheckoutPage() {
1493
1537
  <CardHeader>
1494
1538
  <CardTitle className="flex items-center gap-2">
1495
1539
  <ShoppingBag className="h-5 w-5" />
1496
- Twoje zamówienie
1540
+ {tCart("yourOrder")}
1497
1541
  </CardTitle>
1498
1542
  </CardHeader>
1499
1543
  <CardContent className="space-y-4">
@@ -1597,7 +1641,7 @@ export default function CheckoutPage() {
1597
1641
  ) : (
1598
1642
  <div className="flex gap-2">
1599
1643
  <Input
1600
- placeholder="Kod rabatowy"
1644
+ placeholder={t("discount.codePlaceholder")}
1601
1645
  value={formState.discountCode}
1602
1646
  onChange={(e) =>
1603
1647
  setFormState((prev) => ({ ...prev, discountCode: e.target.value }))
@@ -1612,7 +1656,7 @@ export default function CheckoutPage() {
1612
1656
  {applyDiscount.isPending ? (
1613
1657
  <Loader2 className="h-4 w-4 animate-spin" />
1614
1658
  ) : (
1615
- "Zastosuj"
1659
+ tc("apply")
1616
1660
  )}
1617
1661
  </Button>
1618
1662
  </div>
@@ -1623,7 +1667,7 @@ export default function CheckoutPage() {
1623
1667
  {/* Gift Card */}
1624
1668
  {checkoutId && (
1625
1669
  <div className="border-t pt-4 space-y-3">
1626
- <p className="text-sm font-medium">Karta podarunkowa</p>
1670
+ <p className="text-sm font-medium">{tCart("giftCardLabel")}</p>
1627
1671
 
1628
1672
  {/* Applied Gift Cards */}
1629
1673
  {checkout?.appliedGiftCards && checkout.appliedGiftCards.length > 0 && (
@@ -1661,7 +1705,7 @@ export default function CheckoutPage() {
1661
1705
  {/* Gift Card Input */}
1662
1706
  <div className="flex gap-2">
1663
1707
  <Input
1664
- placeholder="Kod karty podarunkowej"
1708
+ placeholder={tCart("giftCardPlaceholder")}
1665
1709
  value={formState.giftCardCode}
1666
1710
  onChange={(e) =>
1667
1711
  setFormState((prev) => ({ ...prev, giftCardCode: e.target.value.toUpperCase() }))
@@ -1682,7 +1726,7 @@ export default function CheckoutPage() {
1682
1726
  {applyGiftCard.isPending ? (
1683
1727
  <Loader2 className="h-4 w-4 animate-spin" />
1684
1728
  ) : (
1685
- "Zastosuj"
1729
+ tc("apply")
1686
1730
  )}
1687
1731
  </Button>
1688
1732
  </div>
@@ -1693,7 +1737,7 @@ export default function CheckoutPage() {
1693
1737
  <div className="border-t pt-4 space-y-2">
1694
1738
  {/* Subtotal */}
1695
1739
  <div className="flex justify-between text-sm">
1696
- <span className="text-muted-foreground">Produkty</span>
1740
+ <span className="text-muted-foreground">{tCart("products")}</span>
1697
1741
  <span>
1698
1742
  {formatPrice(
1699
1743
  checkout?.subtotalPrice?.amount ||
@@ -1708,20 +1752,20 @@ export default function CheckoutPage() {
1708
1752
  {/* Shipping */}
1709
1753
  {checkout?.shippingLine ? (
1710
1754
  <div className="flex justify-between text-sm">
1711
- <span className="text-muted-foreground">Dostawa</span>
1755
+ <span className="text-muted-foreground">{tCart("shipping")}</span>
1712
1756
  <span>{formatPrice(checkout.totalShippingPrice?.amount || "0")}</span>
1713
1757
  </div>
1714
1758
  ) : (
1715
1759
  <div className="flex justify-between text-sm">
1716
- <span className="text-muted-foreground">Dostawa</span>
1717
- <span className="text-muted-foreground">Obliczona w następnym kroku</span>
1760
+ <span className="text-muted-foreground">{tCart("shipping")}</span>
1761
+ <span className="text-muted-foreground">{tCart("calculatedNextStep")}</span>
1718
1762
  </div>
1719
1763
  )}
1720
1764
 
1721
1765
  {/* Discount */}
1722
1766
  {checkout?.totalDiscounts && parseFloat(checkout.totalDiscounts.amount) > 0 && (
1723
1767
  <div className="flex justify-between text-sm text-green-600">
1724
- <span>Rabat</span>
1768
+ <span>{tCart("discount")}</span>
1725
1769
  <span>-{formatPrice(checkout.totalDiscounts.amount)}</span>
1726
1770
  </div>
1727
1771
  )}
@@ -1729,7 +1773,7 @@ export default function CheckoutPage() {
1729
1773
  {/* Tax */}
1730
1774
  {checkout?.totalTax && parseFloat(checkout.totalTax.amount) > 0 && (
1731
1775
  <div className="flex justify-between text-sm">
1732
- <span className="text-muted-foreground">Podatek VAT</span>
1776
+ <span className="text-muted-foreground">{tCart("tax")}</span>
1733
1777
  <span>{formatPrice(checkout.totalTax.amount)}</span>
1734
1778
  </div>
1735
1779
  )}
@@ -1737,14 +1781,14 @@ export default function CheckoutPage() {
1737
1781
  {/* Gift Card Deduction */}
1738
1782
  {checkout?.totalGiftCardAmount && parseFloat(checkout.totalGiftCardAmount.amount) > 0 && (
1739
1783
  <div className="flex justify-between text-sm text-purple-600">
1740
- <span>Karta podarunkowa</span>
1784
+ <span>{tCart("giftCard")}</span>
1741
1785
  <span>-{formatPrice(checkout.totalGiftCardAmount.amount)}</span>
1742
1786
  </div>
1743
1787
  )}
1744
1788
 
1745
1789
  {/* Total */}
1746
1790
  <div className="flex justify-between border-t pt-2 text-lg font-semibold">
1747
- <span>Razem</span>
1791
+ <span>{tCart("total")}</span>
1748
1792
  <span>
1749
1793
  {formatPrice(
1750
1794
  checkout?.totalPrice?.amount ||
@@ -1761,7 +1805,7 @@ export default function CheckoutPage() {
1761
1805
  checkout?.totalPrice &&
1762
1806
  parseFloat(checkout.paymentDue.amount) !== parseFloat(checkout.totalPrice.amount) && (
1763
1807
  <div className="flex justify-between border-t pt-2 text-lg font-bold text-primary">
1764
- <span>Do zapłaty</span>
1808
+ <span>{tCart("paymentDue")}</span>
1765
1809
  <span>{formatPrice(checkout.paymentDue.amount)}</span>
1766
1810
  </div>
1767
1811
  )}
@@ -1772,11 +1816,11 @@ export default function CheckoutPage() {
1772
1816
  <div className="flex items-center justify-center gap-4 text-xs text-muted-foreground">
1773
1817
  <div className="flex items-center gap-1">
1774
1818
  <Check className="h-3 w-3" />
1775
- Bezpieczna płatność
1819
+ {tc("securePayment")}
1776
1820
  </div>
1777
1821
  <div className="flex items-center gap-1">
1778
1822
  <Check className="h-3 w-3" />
1779
- Szyfrowane dane
1823
+ {tc("encryptedData")}
1780
1824
  </div>
1781
1825
  </div>
1782
1826
  </div>