@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
@@ -12,6 +12,7 @@
12
12
  */
13
13
 
14
14
  import { useState } from 'react';
15
+ import { useTranslations } from 'next-intl';
15
16
  import { Users, Copy, Check, Share2, Gift, UserPlus, Clock, Award } from 'lucide-react';
16
17
  import { Card, CardContent, CardHeader, CardTitle, CardDescription } from '@/components/ui/card';
17
18
  import { Button } from '@/components/ui/button';
@@ -36,6 +37,7 @@ export function ReferralSection({
36
37
  bonusPoints = 0,
37
38
  className,
38
39
  }: ReferralSectionProps) {
40
+ const t = useTranslations("loyalty.referral");
39
41
  const { referralCode, shareUrl } = referralStats;
40
42
  const [copied, setCopied] = useState<'code' | 'url' | null>(null);
41
43
 
@@ -47,10 +49,10 @@ export function ReferralSection({
47
49
  try {
48
50
  await navigator.clipboard.writeText(text);
49
51
  setCopied(type);
50
- toast.success(type === 'code' ? 'Kod skopiowany!' : 'Link skopiowany!');
52
+ toast.success(type === 'code' ? t('codeCopied') : t('linkCopied'));
51
53
  setTimeout(() => setCopied(null), 2000);
52
54
  } catch {
53
- toast.error('Nie udało się skopiować');
55
+ toast.error(t('copyFailed'));
54
56
  }
55
57
  };
56
58
 
@@ -58,14 +60,14 @@ export function ReferralSection({
58
60
  if (navigator.share) {
59
61
  try {
60
62
  await navigator.share({
61
- title: 'Dołącz do programu lojalnościowego',
62
- text: `Zarejestruj się używając mojego kodu ${referralCode} i otrzymaj ${formatNumber(bonusPoints)} ${pointsName}!`,
63
+ title: t('shareFriends'),
64
+ text: t('shareText', { code: referralCode }),
63
65
  url: shareUrl,
64
66
  });
65
67
  } catch (err) {
66
68
  // User cancelled or error
67
69
  if ((err as Error).name !== 'AbortError') {
68
- toast.error('Nie udało się udostępnić');
70
+ toast.error(t('shareFailed'));
69
71
  }
70
72
  }
71
73
  } else {
@@ -81,11 +83,10 @@ export function ReferralSection({
81
83
  <CardHeader>
82
84
  <CardTitle className="flex items-center gap-2">
83
85
  <Users className="h-5 w-5" />
84
- Twój kod polecający
86
+ {t('yourCode')}
85
87
  </CardTitle>
86
88
  <CardDescription>
87
- Podziel się tym kodem ze znajomymi. Gdy dokonają pierwszego zakupu, oboje
88
- otrzymacie punkty!
89
+ {t('shareDescription')}
89
90
  </CardDescription>
90
91
  </CardHeader>
91
92
  <CardContent className="space-y-4">
@@ -132,7 +133,7 @@ export function ReferralSection({
132
133
  {/* Share Button */}
133
134
  <Button onClick={handleShare} className="w-full">
134
135
  <Share2 className="h-4 w-4 mr-2" />
135
- Udostępnij znajomym
136
+ {t('shareFriends')}
136
137
  </Button>
137
138
  </CardContent>
138
139
  </Card>
@@ -140,7 +141,7 @@ export function ReferralSection({
140
141
  {/* How it Works */}
141
142
  <Card>
142
143
  <CardHeader>
143
- <CardTitle className="text-lg">Jak to działa?</CardTitle>
144
+ <CardTitle className="text-lg">{t('howItWorks')}</CardTitle>
144
145
  </CardHeader>
145
146
  <CardContent>
146
147
  <div className="grid grid-cols-1 md:grid-cols-3 gap-4">
@@ -148,9 +149,9 @@ export function ReferralSection({
148
149
  <div className="p-3 bg-primary/10 rounded-full mb-3">
149
150
  <Share2 className="h-6 w-6 text-primary" />
150
151
  </div>
151
- <h4 className="font-medium mb-1">1. Udostępnij kod</h4>
152
+ <h4 className="font-medium mb-1">{t('step1Title')}</h4>
152
153
  <p className="text-sm text-muted-foreground">
153
- Podziel się swoim kodem lub linkiem ze znajomymi
154
+ {t('step1Desc')}
154
155
  </p>
155
156
  </div>
156
157
 
@@ -158,9 +159,9 @@ export function ReferralSection({
158
159
  <div className="p-3 bg-primary/10 rounded-full mb-3">
159
160
  <UserPlus className="h-6 w-6 text-primary" />
160
161
  </div>
161
- <h4 className="font-medium mb-1">2. Znajomy się rejestruje</h4>
162
+ <h4 className="font-medium mb-1">{t('step2Title')}</h4>
162
163
  <p className="text-sm text-muted-foreground">
163
- Znajomy używa Twojego kodu przy rejestracji i dokonuje zakupu
164
+ {t('step2Desc')}
164
165
  </p>
165
166
  </div>
166
167
 
@@ -168,10 +169,9 @@ export function ReferralSection({
168
169
  <div className="p-3 bg-primary/10 rounded-full mb-3">
169
170
  <Gift className="h-6 w-6 text-primary" />
170
171
  </div>
171
- <h4 className="font-medium mb-1">3. Oboje zyskujecie</h4>
172
+ <h4 className="font-medium mb-1">{t('step3Title')}</h4>
172
173
  <p className="text-sm text-muted-foreground">
173
- Ty otrzymujesz {formatNumber(referralPoints)} {pointsName}, a znajomy{' '}
174
- {formatNumber(bonusPoints)} {pointsName}
174
+ {t('step3Desc', { points: formatNumber(referralPoints), name: pointsName })}
175
175
  </p>
176
176
  </div>
177
177
  </div>
@@ -183,7 +183,7 @@ export function ReferralSection({
183
183
  <CardHeader>
184
184
  <CardTitle className="text-lg flex items-center gap-2">
185
185
  <Award className="h-5 w-5" />
186
- Twoje statystyki poleceń
186
+ {t('yourStats')}
187
187
  </CardTitle>
188
188
  </CardHeader>
189
189
  <CardContent>
@@ -192,7 +192,7 @@ export function ReferralSection({
192
192
  <div className="text-2xl font-bold text-primary">
193
193
  {formatNumber(referralStats.totalReferred)}
194
194
  </div>
195
- <div className="text-sm text-muted-foreground">Zaproszonych</div>
195
+ <div className="text-sm text-muted-foreground">{t('invited')}</div>
196
196
  </div>
197
197
 
198
198
  <div className="p-4 bg-muted/50 rounded-lg text-center">
@@ -202,7 +202,7 @@ export function ReferralSection({
202
202
  </div>
203
203
  <Check className="h-5 w-5 text-green-600" />
204
204
  </div>
205
- <div className="text-sm text-muted-foreground">Zrealizowanych</div>
205
+ <div className="text-sm text-muted-foreground">{t('completed')}</div>
206
206
  </div>
207
207
 
208
208
  <div className="p-4 bg-muted/50 rounded-lg text-center">
@@ -212,7 +212,7 @@ export function ReferralSection({
212
212
  </div>
213
213
  <Clock className="h-5 w-5 text-amber-600" />
214
214
  </div>
215
- <div className="text-sm text-muted-foreground">Oczekujących</div>
215
+ <div className="text-sm text-muted-foreground">{t('pending')}</div>
216
216
  </div>
217
217
 
218
218
  <div className="p-4 bg-muted/50 rounded-lg text-center">
@@ -220,7 +220,7 @@ export function ReferralSection({
220
220
  {formatNumber(referralStats.totalPointsEarned)}
221
221
  </div>
222
222
  <div className="text-sm text-muted-foreground">
223
- Zdobytych {pointsName}
223
+ {t('earned', { name: pointsName })}
224
224
  </div>
225
225
  </div>
226
226
  </div>
@@ -229,8 +229,7 @@ export function ReferralSection({
229
229
  <div className="mt-4 p-3 bg-amber-50 dark:bg-amber-950/30 rounded-lg border border-amber-200 dark:border-amber-800">
230
230
  <p className="text-sm text-amber-800 dark:text-amber-200">
231
231
  <Clock className="h-4 w-4 inline mr-1" />
232
- Masz {referralStats.pendingReferrals} oczekujących poleceń. Punkty zostaną
233
- przyznane po dokonaniu pierwszego zakupu przez poleconą osobę.
232
+ {t('pendingReferrals', { count: referralStats.pendingReferrals })}
234
233
  </p>
235
234
  </div>
236
235
  )}
@@ -1,6 +1,7 @@
1
1
  "use client";
2
2
 
3
3
  import { useState, useRef, useEffect } from "react";
4
+ import { useTranslations } from "next-intl";
4
5
  import { ShoppingCart, Check } from "lucide-react";
5
6
  import { Button } from "@/components/ui/button";
6
7
  import { Spinner } from "@/components/ui/spinner";
@@ -74,6 +75,7 @@ export function AddToCartButton({
74
75
  size = "md",
75
76
  fullWidth = false,
76
77
  }: AddToCartButtonProps) {
78
+ const t = useTranslations("product");
77
79
  const [isAdded, setIsAdded] = useState(false);
78
80
  const { addToCart, isLoading } = useCartActions();
79
81
  const timeoutRef = useRef<NodeJS.Timeout | null>(null);
@@ -95,17 +97,7 @@ export function AddToCartButton({
95
97
  if (onAddToCart) {
96
98
  await onAddToCart(variantId, quantity);
97
99
  } else {
98
- await addToCart({
99
- variantId,
100
- productId,
101
- productHandle,
102
- productTitle,
103
- variantTitle,
104
- price,
105
- image,
106
- available,
107
- quantity,
108
- });
100
+ await addToCart(variantId, quantity);
109
101
  }
110
102
 
111
103
  setIsAdded(true);
@@ -143,17 +135,17 @@ export function AddToCartButton({
143
135
  {isLoading ? (
144
136
  <>
145
137
  <Spinner size="sm" className="mr-2" />
146
- Adding...
138
+ {t("adding")}
147
139
  </>
148
140
  ) : isAdded ? (
149
141
  <>
150
142
  <Check className="mr-2 h-5 w-5" />
151
- Added to Cart
143
+ {t("addedToCart")}
152
144
  </>
153
145
  ) : (
154
146
  <>
155
147
  <ShoppingCart className="mr-2 h-5 w-5" />
156
- {!available ? "Out of Stock" : "Add to Cart"}
148
+ {!available ? t("outOfStock") : t("addToCart")}
157
149
  </>
158
150
  )}
159
151
  </Button>
@@ -3,7 +3,7 @@
3
3
  import { cn } from "@/lib/utils";
4
4
  import { CURRENCY_LOCALES } from "@doswiftly/storefront-sdk";
5
5
  import { Tag, LogIn, TrendingDown } from "lucide-react";
6
- import Link from "next/link";
6
+ import { Link } from "@/i18n/navigation";
7
7
 
8
8
  /**
9
9
  * Types matching GraphQL schema
@@ -1,5 +1,6 @@
1
1
  "use client";
2
2
 
3
+ import { useTranslations } from "next-intl";
3
4
  import { X } from "lucide-react";
4
5
  import { Badge } from "@/components/ui/badge";
5
6
  import { Button } from "@/components/ui/button";
@@ -30,6 +31,8 @@ export function FilterActivePills({
30
31
  onRemove,
31
32
  onClearAll,
32
33
  }: FilterActivePillsProps) {
34
+ const t = useTranslations("product");
35
+
33
36
  if (pills.length === 0) return null;
34
37
 
35
38
  return (
@@ -62,7 +65,7 @@ export function FilterActivePills({
62
65
  className="h-6 shrink-0 px-2 text-xs text-muted-foreground"
63
66
  onClick={onClearAll}
64
67
  >
65
- Clear all
68
+ {t("clearAllFilters")}
66
69
  </Button>
67
70
  </div>
68
71
  );
@@ -1,6 +1,7 @@
1
1
  "use client";
2
2
 
3
3
  import { useState } from "react";
4
+ import { useTranslations } from "next-intl";
4
5
  import { SlidersHorizontal } from "lucide-react";
5
6
  import { Button } from "@/components/ui/button";
6
7
  import { Badge } from "@/components/ui/badge";
@@ -34,6 +35,8 @@ export function FilterMobileSheet({
34
35
  ...filterProps
35
36
  }: FilterMobileSheetProps) {
36
37
  const [open, setOpen] = useState(false);
38
+ const t = useTranslations("product");
39
+ const tFilters = useTranslations("filters");
37
40
 
38
41
  return (
39
42
  <Sheet open={open} onOpenChange={setOpen}>
@@ -44,7 +47,7 @@ export function FilterMobileSheet({
44
47
  className="shrink-0 lg:hidden"
45
48
  >
46
49
  <SlidersHorizontal className="mr-2 h-4 w-4" />
47
- Filters
50
+ {t("filters")}
48
51
  {activeFilterCount > 0 && (
49
52
  <Badge variant="secondary" className="ml-2 h-5 px-1.5 py-0 text-xs">
50
53
  {activeFilterCount}
@@ -54,10 +57,10 @@ export function FilterMobileSheet({
54
57
  </SheetTrigger>
55
58
  <SheetContent side="left" className="flex w-[300px] flex-col overflow-hidden">
56
59
  <SheetHeader>
57
- <SheetTitle>Filters</SheetTitle>
60
+ <SheetTitle>{t("filters")}</SheetTitle>
58
61
  <SheetDescription>
59
62
  {totalProducts !== undefined && (
60
- <span>{totalProducts} products</span>
63
+ <span>{tFilters("productCount", { count: totalProducts })}</span>
61
64
  )}
62
65
  </SheetDescription>
63
66
  </SheetHeader>
@@ -74,7 +77,7 @@ export function FilterMobileSheet({
74
77
  setOpen(false);
75
78
  }}
76
79
  >
77
- Clear all filters ({activeFilterCount})
80
+ {t("clearAllFilters")} ({activeFilterCount})
78
81
  </Button>
79
82
  </div>
80
83
  )}
@@ -1,6 +1,7 @@
1
1
  "use client";
2
2
 
3
3
  import { useState, useCallback, useEffect, useRef } from "react";
4
+ import { useTranslations } from "next-intl";
4
5
  import { Slider } from "@/components/ui/slider";
5
6
  import { Input } from "@/components/ui/input";
6
7
 
@@ -28,6 +29,7 @@ export function FilterPriceRange({
28
29
  currency = "PLN",
29
30
  onChange,
30
31
  }: FilterPriceRangeProps) {
32
+ const t = useTranslations("product");
31
33
  const [localMin, setLocalMin] = useState<number>(currentMin ?? min);
32
34
  const [localMax, setLocalMax] = useState<number>(currentMax ?? max);
33
35
  const debounceRef = useRef<ReturnType<typeof setTimeout>>(undefined);
@@ -94,7 +96,7 @@ export function FilterPriceRange({
94
96
  step={step}
95
97
  onValueChange={handleSliderChange}
96
98
  onValueCommit={handleSliderCommit}
97
- aria-label="Price range"
99
+ aria-label={t("priceRange")}
98
100
  />
99
101
 
100
102
  {/* Input fields */}
@@ -109,7 +111,7 @@ export function FilterPriceRange({
109
111
  max={max}
110
112
  step={step}
111
113
  className="pr-12 text-sm"
112
- aria-label="Minimum price"
114
+ aria-label={t("minPrice")}
113
115
  />
114
116
  <span className="pointer-events-none absolute right-3 top-1/2 -translate-y-1/2 text-xs text-muted-foreground">
115
117
  {currency}
@@ -126,7 +128,7 @@ export function FilterPriceRange({
126
128
  max={max}
127
129
  step={step}
128
130
  className="pr-12 text-sm"
129
- aria-label="Maximum price"
131
+ aria-label={t("maxPrice")}
130
132
  />
131
133
  <span className="pointer-events-none absolute right-3 top-1/2 -translate-y-1/2 text-xs text-muted-foreground">
132
134
  {currency}
@@ -1,6 +1,7 @@
1
1
  "use client";
2
2
 
3
- import Link from "next/link";
3
+ import { Link } from "@/i18n/navigation";
4
+ import { useTranslations } from "next-intl";
4
5
  import { Gift } from "lucide-react";
5
6
  import { ProductImage } from "./product-image";
6
7
  import { ProductPrice } from "./product-price";
@@ -35,6 +36,7 @@ export function ProductCard({
35
36
  priority = false,
36
37
  showBadges = true,
37
38
  }: ProductCardProps) {
39
+ const t = useTranslations("product");
38
40
  const isOnSale = product.compareAtPriceRange &&
39
41
  parseFloat(product.compareAtPriceRange.minVariantPrice.amount) >
40
42
  parseFloat(product.priceRange.minVariantPrice.amount);
@@ -70,22 +72,22 @@ export function ProductCard({
70
72
  {isGiftCard && (
71
73
  <Badge variant="default" className="shadow-sm">
72
74
  <Gift className="mr-1 h-3 w-3" />
73
- Karta podarunkowa
75
+ {t("giftCardLabel")}
74
76
  </Badge>
75
77
  )}
76
78
  {isNew && !isGiftCard && (
77
79
  <Badge variant="default" className="shadow-sm">
78
- NEW
80
+ {t("badgeNew")}
79
81
  </Badge>
80
82
  )}
81
83
  {isOnSale && (
82
84
  <Badge variant="destructive" className="shadow-sm">
83
- SALE
85
+ {t("badgeSale")}
84
86
  </Badge>
85
87
  )}
86
88
  {isOutOfStock && (
87
89
  <Badge variant="secondary" className="shadow-sm">
88
- Out of Stock
90
+ {t("outOfStock")}
89
91
  </Badge>
90
92
  )}
91
93
  </div>
@@ -110,7 +112,7 @@ export function ProductCard({
110
112
 
111
113
  {isOutOfStock && (
112
114
  <p className="mt-2 text-xs text-muted-foreground">
113
- Currently unavailable
115
+ {t("currentlyUnavailable")}
114
116
  </p>
115
117
  )}
116
118
  </div>
@@ -1,6 +1,7 @@
1
1
  "use client";
2
2
 
3
3
  import { useMemo } from "react";
4
+ import { useTranslations } from "next-intl";
4
5
  import { Checkbox } from "@/components/ui/checkbox";
5
6
  import { Label } from "@/components/ui/label";
6
7
  import {
@@ -55,6 +56,7 @@ export function ProductFilters({
55
56
  onClearAll,
56
57
  className,
57
58
  }: ProductFiltersProps) {
59
+ const t = useTranslations("product");
58
60
  const handleCheckboxChange = (filterId: string, value: string) => {
59
61
  const current = selectedFilters[filterId] || [];
60
62
  const updated = current.includes(value)
@@ -95,7 +97,7 @@ export function ProductFilters({
95
97
  onClick={onClearAll}
96
98
  className="w-full"
97
99
  >
98
- Clear all filters
100
+ {t("clearAllFilters")}
99
101
  </Button>
100
102
  )}
101
103
 
@@ -3,13 +3,9 @@
3
3
  import { useState } from "react";
4
4
  import Image from "next/image";
5
5
  import { cn } from "@/lib/utils";
6
+ import type { ImageData } from "@doswiftly/storefront-sdk";
6
7
 
7
- export interface ProductImageData {
8
- url: string;
9
- altText?: string | null;
10
- width?: number | null;
11
- height?: number | null;
12
- }
8
+ export type ProductImageData = ImageData;
13
9
 
14
10
  export interface ProductImageProps {
15
11
  image?: ProductImageData | null;
@@ -115,7 +111,7 @@ export function ProductImage({
115
111
 
116
112
  const imageAlt = alt || image.altText || "Product image";
117
113
 
118
- // Common image props
114
+ // Global loaderFile (next.config.ts) handles imgproxy resize — no per-component loader needed
119
115
  const commonProps = {
120
116
  src: image.url,
121
117
  alt: imageAlt,
@@ -1,5 +1,7 @@
1
1
  "use client";
2
2
 
3
+ import { useMemo } from "react";
4
+ import { useTranslations } from "next-intl";
3
5
  import {
4
6
  Select,
5
7
  SelectContent,
@@ -26,36 +28,47 @@ export interface ProductSortProps {
26
28
  className?: string;
27
29
  }
28
30
 
29
- const sortOptions: { value: SortOption; label: string }[] = [
30
- { value: "relevance", label: "Featured" },
31
- { value: "best-selling", label: "Best Selling" },
32
- { value: "price-low-to-high", label: "Price: Low to High" },
33
- { value: "price-high-to-low", label: "Price: High to Low" },
34
- { value: "title-asc", label: "Name: A to Z" },
35
- { value: "title-desc", label: "Name: Z to A" },
36
- { value: "created-desc", label: "Newest First" },
37
- { value: "created-asc", label: "Oldest First" },
31
+ const SORT_OPTION_KEYS: { value: SortOption; labelKey: string }[] = [
32
+ { value: "relevance", labelKey: "sortOptions.relevance" },
33
+ { value: "best-selling", labelKey: "sortOptions.bestSelling" },
34
+ { value: "price-low-to-high", labelKey: "sortOptions.priceLowToHigh" },
35
+ { value: "price-high-to-low", labelKey: "sortOptions.priceHighToLow" },
36
+ { value: "title-asc", labelKey: "sortOptions.titleAsc" },
37
+ { value: "title-desc", labelKey: "sortOptions.titleDesc" },
38
+ { value: "created-desc", labelKey: "sortOptions.newestFirst" },
39
+ { value: "created-asc", labelKey: "sortOptions.oldestFirst" },
38
40
  ];
39
41
 
40
42
  /**
41
43
  * ProductSort - Sorting dropdown for product listings
42
- *
44
+ *
43
45
  * @example
44
46
  * ```tsx
45
47
  * const [sort, setSort] = useState<SortOption>("featured");
46
- *
47
- * <ProductSort
48
+ *
49
+ * <ProductSort
48
50
  * value={sort}
49
51
  * onChange={setSort}
50
52
  * />
51
53
  * ```
52
54
  */
53
55
  export function ProductSort({ value, onChange, className }: ProductSortProps) {
56
+ const t = useTranslations("product");
57
+
58
+ const sortOptions = useMemo(
59
+ () =>
60
+ SORT_OPTION_KEYS.map((opt) => ({
61
+ value: opt.value,
62
+ label: t(opt.labelKey),
63
+ })),
64
+ [t]
65
+ );
66
+
54
67
  return (
55
68
  <div className={className}>
56
69
  <Select value={value} onValueChange={(val) => onChange(val as SortOption)}>
57
70
  <SelectTrigger>
58
- <SelectValue placeholder="Sort by" />
71
+ <SelectValue placeholder={t("sortBy")} />
59
72
  </SelectTrigger>
60
73
  <SelectContent>
61
74
  {sortOptions.map((option) => (