@doswiftly/cli 0.1.19 → 0.1.21

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 +225 -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,17 +1,15 @@
1
- import Link from "next/link";
1
+ import { useTranslations } from "next-intl";
2
+ import { Link } from "@/i18n/navigation";
2
3
  import { FileQuestion, Home, ShoppingBag, Search } from "lucide-react";
3
4
  import { Button } from "@/components/ui/button";
4
5
  import { Card, CardContent, CardHeader, CardTitle, CardDescription } from "@/components/ui/card";
5
6
 
6
7
  /**
7
- * 404 Not Found Page
8
- *
9
- * Displayed when a page or resource is not found.
10
- * Provides helpful navigation back to the site.
11
- *
12
- * Requirements: 11.1
8
+ * 404 Not Found Page (locale-aware)
13
9
  */
14
10
  export default function NotFound() {
11
+ const t = useTranslations("notFound");
12
+
15
13
  return (
16
14
  <div className="container mx-auto flex min-h-[60vh] items-center justify-center px-4 py-16">
17
15
  <Card className="w-full max-w-md text-center">
@@ -19,46 +17,43 @@ export default function NotFound() {
19
17
  <div className="mx-auto mb-4 flex h-16 w-16 items-center justify-center rounded-full bg-muted">
20
18
  <FileQuestion className="h-8 w-8 text-muted-foreground" />
21
19
  </div>
22
- <CardTitle className="text-6xl font-bold">404</CardTitle>
20
+ <CardTitle className="text-6xl font-bold">{t("title")}</CardTitle>
23
21
  <CardDescription className="text-lg">
24
- Strona nie została znaleziona
22
+ {t("description")}
25
23
  </CardDescription>
26
24
  </CardHeader>
27
25
  <CardContent className="space-y-6">
28
26
  <p className="text-muted-foreground">
29
- Przepraszamy, nie mogliśmy znaleźć strony, której szukasz.
30
- Mogła zostać przeniesiona lub usunięta.
27
+ {t("message")}
31
28
  </p>
32
29
 
33
- {/* Navigation options */}
34
30
  <div className="flex flex-col gap-3">
35
31
  <Button asChild size="lg" className="w-full">
36
32
  <Link href="/">
37
33
  <Home className="mr-2 h-4 w-4" />
38
- Strona główna
34
+ {t("home")}
39
35
  </Link>
40
36
  </Button>
41
37
  <div className="flex gap-3">
42
38
  <Button asChild variant="outline" size="lg" className="flex-1">
43
39
  <Link href="/products">
44
40
  <ShoppingBag className="mr-2 h-4 w-4" />
45
- Produkty
41
+ {t("products")}
46
42
  </Link>
47
43
  </Button>
48
44
  <Button asChild variant="outline" size="lg" className="flex-1">
49
45
  <Link href="/collections">
50
46
  <Search className="mr-2 h-4 w-4" />
51
- Kolekcje
47
+ {t("collections")}
52
48
  </Link>
53
49
  </Button>
54
50
  </div>
55
51
  </div>
56
52
 
57
- {/* Help text */}
58
53
  <p className="text-sm text-muted-foreground">
59
- Potrzebujesz pomocy?{" "}
54
+ {t("needHelp")}{" "}
60
55
  <Link href="/contact" className="text-primary underline underline-offset-4">
61
- Skontaktuj się z nami
56
+ {t("contactUs")}
62
57
  </Link>
63
58
  </p>
64
59
  </CardContent>
@@ -1,13 +1,17 @@
1
+ import { getTranslations } from "next-intl/server";
1
2
  import { HeroSection } from "@/components/home/hero-section";
2
3
  import { FeaturedProducts } from "@/components/home/featured-products";
3
4
  import { CategoryGrid } from "@/components/home/category-grid";
4
5
  import { NewsletterSignup } from "@/components/home/newsletter-signup";
5
6
  import type { Metadata } from "next";
6
7
 
7
- export const metadata: Metadata = {
8
- title: "Home | " + (process.env.NEXT_PUBLIC_SITE_NAME || "My Store"),
9
- description: "Discover our curated collection of quality products at affordable prices.",
10
- };
8
+ export async function generateMetadata(): Promise<Metadata> {
9
+ const t = await getTranslations("home");
10
+ return {
11
+ title: "Home | " + (process.env.NEXT_PUBLIC_SITE_NAME || "My Store"),
12
+ description: t("metaDescription"),
13
+ };
14
+ }
11
15
 
12
16
  export default function HomePage() {
13
17
  return (
@@ -1,7 +1,7 @@
1
1
  "use client";
2
2
 
3
3
  import { useEffect } from "react";
4
- import Link from "next/link";
4
+ import { Link } from "@/i18n/navigation";
5
5
  import { AlertCircle } from "lucide-react";
6
6
  import { Button } from "@/components/ui/button";
7
7
  import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
@@ -1,5 +1,6 @@
1
1
  import { Metadata } from "next";
2
2
  import { notFound } from "next/navigation";
3
+ import { getTranslations } from "next-intl/server";
3
4
  import { Breadcrumbs } from "@/components/layout/breadcrumbs";
4
5
  import { ProductClient } from "./product-client";
5
6
  import { fetchProduct, fetchProducts } from "@/lib/graphql/server";
@@ -26,22 +27,23 @@ export async function generateMetadata({
26
27
  params: Promise<{ slug: string }>;
27
28
  }): Promise<Metadata> {
28
29
  try {
30
+ const t = await getTranslations("productPage");
29
31
  const resolvedParams = await params;
30
-
32
+
31
33
  if (!resolvedParams?.slug) {
32
34
  return {
33
- title: "Product",
34
- description: "View product details",
35
+ title: t("title"),
36
+ description: t("viewDetails"),
35
37
  };
36
38
  }
37
-
39
+
38
40
  const data = await fetchProduct(resolvedParams.slug);
39
41
  const product = data?.product;
40
42
 
41
43
  if (!product) {
42
44
  return {
43
- title: "Product Not Found",
44
- description: "The product you're looking for doesn't exist.",
45
+ title: t("notFound"),
46
+ description: t("notFoundDescription"),
45
47
  };
46
48
  }
47
49
 
@@ -88,9 +90,10 @@ export async function generateMetadata({
88
90
  };
89
91
  } catch (error) {
90
92
  console.error("Error generating metadata:", error);
93
+ const t = await getTranslations("productPage");
91
94
  return {
92
- title: "Product",
93
- description: "View product details",
95
+ title: t("title"),
96
+ description: t("viewDetails"),
94
97
  };
95
98
  }
96
99
  }
@@ -1,6 +1,7 @@
1
1
  "use client";
2
2
 
3
3
  import { useState, useEffect } from "react";
4
+ import { useTranslations } from "next-intl";
4
5
  import { useProduct } from "@/lib/graphql/hooks";
5
6
  import { useCurrencyStore } from "@doswiftly/storefront-sdk/react";
6
7
  import { ProductGallery } from "@/components/product/product-gallery";
@@ -35,6 +36,7 @@ export interface ProductClientProps {
35
36
  * Requirements: 2.2, 3.2, 6.4
36
37
  */
37
38
  export function ProductClient({ product: initialProduct, similarProducts = [] }: ProductClientProps) {
39
+ const t = useTranslations("productPage");
38
40
  const [selectedVariant, setSelectedVariant] = useState(initialProduct.variants[0]);
39
41
  const [quantity, setQuantity] = useState(1);
40
42
 
@@ -288,7 +290,7 @@ export function ProductClient({ product: initialProduct, similarProducts = [] }:
288
290
  {similarProducts.length > 0 && (
289
291
  <SimilarProducts
290
292
  products={similarProducts}
291
- title="You might also like"
293
+ title={t("youMightAlsoLike")}
292
294
  columns={4}
293
295
  />
294
296
  )}
@@ -1,9 +1,12 @@
1
1
  import { Suspense } from "react";
2
+ import { getTranslations } from "next-intl/server";
2
3
  import { Breadcrumbs } from "@/components/layout/breadcrumbs";
3
4
  import { Spinner } from "@/components/ui/spinner";
4
5
  import { ProductsClient } from "./products-client";
5
6
 
6
- export default function ProductsPage() {
7
+ export default async function ProductsPage() {
8
+ const t = await getTranslations("product");
9
+
7
10
  return (
8
11
  <div className="container mx-auto px-4 py-8">
9
12
  {/* Breadcrumbs */}
@@ -11,9 +14,9 @@ export default function ProductsPage() {
11
14
 
12
15
  {/* Page Header */}
13
16
  <div className="mb-8">
14
- <h1 className="text-3xl font-bold text-foreground">All Products</h1>
17
+ <h1 className="text-3xl font-bold text-foreground">{t("allProducts")}</h1>
15
18
  <p className="mt-2 text-muted-foreground">
16
- Browse our complete collection of products
19
+ {t("browseCollection")}
17
20
  </p>
18
21
  </div>
19
22
 
@@ -1,7 +1,9 @@
1
1
  "use client";
2
2
 
3
3
  import { useCallback, useMemo, useTransition } from "react";
4
- import { useSearchParams, useRouter, usePathname } from "next/navigation";
4
+ import { useSearchParams } from "next/navigation";
5
+ import { useTranslations } from "next-intl";
6
+ import { useRouter, usePathname } from "@/i18n/navigation";
5
7
  import { keepPreviousData } from "@tanstack/react-query";
6
8
  import { ProductGrid } from "@/components/product/product-grid";
7
9
  import { ProductFilters, type FilterGroup } from "@/components/product/product-filters";
@@ -42,6 +44,8 @@ export function ProductsClient() {
42
44
  const router = useRouter();
43
45
  const pathname = usePathname();
44
46
  const [isPending, startTransition] = useTransition();
47
+ const t = useTranslations("product");
48
+ const tFilters = useTranslations("filters");
45
49
 
46
50
  // ========== Parse URL State ==========
47
51
  const page = parseInt(searchParams.get("page") || "1", 10);
@@ -151,7 +155,7 @@ export function ProductsClient() {
151
155
  if (availableFilters?.categories && availableFilters.categories.length > 0) {
152
156
  groups.push({
153
157
  id: "category",
154
- label: "Categories",
158
+ label: t("category"),
155
159
  type: "checkbox",
156
160
  options: availableFilters.categories.map((cat) => ({
157
161
  label: cat.name,
@@ -168,7 +172,7 @@ export function ProductsClient() {
168
172
  if (minPrice !== maxPrice) {
169
173
  groups.push({
170
174
  id: "price",
171
- label: "Price",
175
+ label: t("price"),
172
176
  type: "range",
173
177
  min: Math.floor(minPrice),
174
178
  max: Math.ceil(maxPrice),
@@ -210,7 +214,7 @@ export function ProductsClient() {
210
214
  }
211
215
 
212
216
  return groups;
213
- }, [availableFilters]);
217
+ }, [availableFilters, t]);
214
218
 
215
219
  // ========== Selected Filters (from URL) ==========
216
220
  const selectedFilters: Record<string, string[]> = useMemo(() => {
@@ -239,7 +243,7 @@ export function ProductsClient() {
239
243
  const cat = availableFilters.categories.find((c) => c.id === catId);
240
244
  pills.push({
241
245
  filterId: "category",
242
- label: "Category",
246
+ label: t("category"),
243
247
  value: catId,
244
248
  displayValue: cat?.name || catId,
245
249
  });
@@ -252,7 +256,7 @@ export function ProductsClient() {
252
256
  availableFilters?.priceRange?.min.currencyCode || "PLN";
253
257
  pills.push({
254
258
  filterId: "price",
255
- label: "Price",
259
+ label: t("price"),
256
260
  value: `${priceMin || 0}-${priceMax || "∞"}`,
257
261
  displayValue: `${priceMin || 0} — ${priceMax || "∞"} ${currency}`,
258
262
  });
@@ -275,7 +279,7 @@ export function ProductsClient() {
275
279
  }
276
280
 
277
281
  return pills;
278
- }, [categoryId, priceMin, priceMax, attributeFilters, availableFilters]);
282
+ }, [categoryId, priceMin, priceMax, attributeFilters, availableFilters, t]);
279
283
 
280
284
  const activeFilterCount = activePills.length;
281
285
 
@@ -368,8 +372,8 @@ export function ProductsClient() {
368
372
  />
369
373
  <p className="text-sm text-muted-foreground">
370
374
  {totalCount > 0
371
- ? `${totalCount} product${totalCount !== 1 ? "s" : ""}`
372
- : "No products found"}
375
+ ? tFilters("productCount", { count: totalCount })
376
+ : t("noProducts")}
373
377
  </p>
374
378
  </div>
375
379
  <ProductSort value={sort} onChange={handleSortChange} />
@@ -423,7 +427,7 @@ export function ProductsClient() {
423
427
  columns={3}
424
428
  priorityCount={6}
425
429
  showBadges
426
- emptyMessage="No products match your filters"
430
+ emptyMessage={t("noProductsMatch")}
427
431
  onResetFilters={handleClearAll}
428
432
  />
429
433
  )}
@@ -7,7 +7,8 @@
7
7
  * and quick add to cart functionality.
8
8
  */
9
9
 
10
- import Link from 'next/link';
10
+ import { useTranslations } from 'next-intl';
11
+ import { Link } from '@/i18n/navigation';
11
12
  import { Heart, ShoppingCart, Trash2 } from 'lucide-react';
12
13
  import { Button } from '@/components/ui/button';
13
14
  import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
@@ -20,6 +21,7 @@ import { useCartActions } from '@/hooks/use-cart-actions';
20
21
  import { toast } from 'sonner';
21
22
 
22
23
  export default function WishlistPage() {
24
+ const t = useTranslations('wishlist');
23
25
  const {
24
26
  wishlists,
25
27
  getActiveWishlist,
@@ -35,7 +37,7 @@ export default function WishlistPage() {
35
37
  const handleClearWishlist = () => {
36
38
  if (activeWishlist) {
37
39
  clearWishlist(activeWishlist.id);
38
- toast.success('Lista życzeń została wyczyszczona');
40
+ toast.success(t('cleared'));
39
41
  }
40
42
  };
41
43
 
@@ -44,21 +46,13 @@ export default function WishlistPage() {
44
46
 
45
47
  for (const item of items) {
46
48
  try {
47
- await addToCart({
48
- variantId: item.variantId || item.productId,
49
- productId: item.productId,
50
- productTitle: item.productTitle,
51
- variantTitle: item.variantTitle || '',
52
- price: item.price,
53
- image: item.image,
54
- available: true,
55
- });
49
+ await addToCart(item.variantId || item.productId);
56
50
  } catch {
57
51
  // Individual item errors are handled by addToCart (shows toast)
58
52
  }
59
53
  }
60
54
 
61
- toast.success(`Dodano ${items.length} produktów do koszyka`);
55
+ toast.success(t('addedToCart', { count: items.length }));
62
56
  };
63
57
 
64
58
  // Show loading state while hydrating from localStorage
@@ -78,8 +72,8 @@ export default function WishlistPage() {
78
72
  <div className="container max-w-4xl py-8">
79
73
  <Breadcrumbs
80
74
  items={[
81
- { label: 'Strona główna', href: '/' },
82
- { label: 'Lista życzeń' },
75
+ { label: t('home'), href: '/' },
76
+ { label: t('title') },
83
77
  ]}
84
78
  />
85
79
 
@@ -87,12 +81,12 @@ export default function WishlistPage() {
87
81
  <div>
88
82
  <h1 className="text-3xl font-bold tracking-tight flex items-center gap-3">
89
83
  <Heart className="h-8 w-8 text-red-500" />
90
- Lista życzeń
84
+ {t('title')}
91
85
  </h1>
92
86
  <p className="text-muted-foreground mt-1">
93
87
  {items.length === 0
94
- ? 'Twoja lista życzeń jest pusta'
95
- : `${items.length} ${items.length === 1 ? 'produkt' : 'produktów'}`}
88
+ ? t('empty')
89
+ : t('itemCount', { count: items.length })}
96
90
  </p>
97
91
  </div>
98
92
 
@@ -100,11 +94,11 @@ export default function WishlistPage() {
100
94
  <div className="flex gap-2">
101
95
  <Button variant="outline" size="sm" onClick={handleClearWishlist}>
102
96
  <Trash2 className="h-4 w-4 mr-2" />
103
- Wyczyść
97
+ {t('clearList')}
104
98
  </Button>
105
99
  <Button size="sm" onClick={handleAddAllToCart}>
106
100
  <ShoppingCart className="h-4 w-4 mr-2" />
107
- Dodaj wszystko do koszyka
101
+ {t('addAllToCart')}
108
102
  </Button>
109
103
  </div>
110
104
  )}
@@ -113,11 +107,11 @@ export default function WishlistPage() {
113
107
  {items.length === 0 ? (
114
108
  <EmptyState
115
109
  icon={<Heart className="h-12 w-12" />}
116
- title="Lista życzeń jest pusta"
117
- description="Dodaj produkty do listy życzeń, aby śledzić ich ceny i szybko dodawać do koszyka."
110
+ title={t('empty')}
111
+ description={t('emptyDescription')}
118
112
  action={
119
113
  <Link href="/products">
120
- <Button>Przeglądaj produkty</Button>
114
+ <Button>{t('browseProducts')}</Button>
121
115
  </Link>
122
116
  }
123
117
  />
@@ -144,6 +138,8 @@ export default function WishlistPage() {
144
138
  * Shows summary of price changes for all items
145
139
  */
146
140
  function PriceChangeSummary({ items }: { items: Array<{ price: { amount: string }; priceAtAdd: { amount: string } }> }) {
141
+ const t = useTranslations('wishlist');
142
+
147
143
  const itemsWithPriceDrops = items.filter((item) => {
148
144
  const current = parseFloat(item.price.amount);
149
145
  const original = parseFloat(item.priceAtAdd.amount);
@@ -162,13 +158,13 @@ function PriceChangeSummary({ items }: { items: Array<{ price: { amount: string
162
158
  <Card className="border-green-200 bg-green-50 dark:border-green-900 dark:bg-green-950">
163
159
  <CardHeader className="pb-2">
164
160
  <CardTitle className="text-green-700 dark:text-green-400 text-lg flex items-center gap-2">
165
- 🎉 Dobre wieści!
161
+ {t('goodNews')}
166
162
  </CardTitle>
167
163
  </CardHeader>
168
164
  <CardContent>
169
165
  <p className="text-green-700 dark:text-green-400">
170
- {itemsWithPriceDrops.length} {itemsWithPriceDrops.length === 1 ? 'produkt obniżył' : 'produktów obniżyło'} cenę!
171
- Możesz zaoszczędzić łącznie{' '}
166
+ {t('priceDropped', { count: itemsWithPriceDrops.length })}{' '}
167
+ {t('totalSavings')}{' '}
172
168
  <span className="font-semibold">
173
169
  {new Intl.NumberFormat('pl-PL', {
174
170
  style: 'currency',
@@ -1,19 +1,7 @@
1
1
  import type { Metadata } from "next";
2
2
  import { Inter } from "next/font/google";
3
- import { cookies } from "next/headers";
4
3
  import "./globals.css";
5
- import { Header } from "@/components/layout/header";
6
- import { Footer } from "@/components/layout/footer";
7
- import { QueryProvider } from "@/components/providers/query-provider";
8
- import { StorefrontProvider } from "@doswiftly/storefront-sdk/react";
9
- import { ThemeProvider } from "@/components/providers/theme-provider";
10
- import { StoresProvider } from "@/components/providers/stores-provider";
11
- import { Toaster } from "sonner";
12
- import { fetchShop } from "@/lib/graphql/server";
13
- import type { ShopQuery } from "@/generated/graphql";
14
- import { themeConfig } from "@/lib/theme/theme-config";
15
- import { graphqlConfig } from "@/lib/graphql/config";
16
- import { AUTH_COOKIE_NAME } from "@doswiftly/storefront-sdk";
4
+ import { defaultLocale } from "@/i18n/routing";
17
5
 
18
6
  const inter = Inter({ subsets: ["latin"] });
19
7
 
@@ -26,67 +14,17 @@ export const metadata: Metadata = {
26
14
  description: "Welcome to our online store powered by DoSwiftly Commerce",
27
15
  };
28
16
 
29
- // Enable ISR with 60 second revalidation for shop data (currencies)
30
- export const revalidate = 60;
31
-
32
- const FALLBACK_SHOP: ShopQuery = {
33
- shop: {
34
- id: 'fallback',
35
- name: process.env.NEXT_PUBLIC_SITE_NAME || 'Store',
36
- currencyCode: 'PLN',
37
- supportedCurrencies: ['PLN'],
38
- paymentCurrencies: ['PLN'],
39
- },
40
- };
41
-
42
17
  export default async function RootLayout({
43
18
  children,
19
+ params,
44
20
  }: {
45
21
  children: React.ReactNode;
22
+ params: Promise<{ locale?: string }>;
46
23
  }) {
47
- let shopData: ShopQuery;
48
- try {
49
- shopData = await fetchShop();
50
- } catch (error) {
51
- console.error('[RootLayout] Failed to fetch shop data, using fallback:', error instanceof Error ? error.message : error);
52
- shopData = FALLBACK_SHOP;
53
- }
54
-
55
- // Read httpOnly auth cookie — invisible to JS, so we pass it as a prop
56
- const cookieStore = await cookies();
57
- const hasAuthToken = !!cookieStore.get(AUTH_COOKIE_NAME)?.value;
58
-
24
+ const { locale } = await params;
59
25
  return (
60
- <html lang="en" suppressHydrationWarning>
61
- <body className={inter.className}>
62
- <ThemeProvider
63
- attribute={themeConfig.attribute}
64
- defaultTheme={themeConfig.defaultTheme}
65
- enableSystem={themeConfig.enableSystem}
66
- disableTransitionOnChange={themeConfig.disableTransitionOnChange}
67
- storageKey={themeConfig.storageKey}
68
- >
69
- <QueryProvider>
70
- <StorefrontProvider
71
- config={{
72
- apiUrl: graphqlConfig.apiUrl,
73
- shopSlug: graphqlConfig.shopSlug,
74
- }}
75
- shopData={shopData.shop}
76
- initialIsAuthenticated={hasAuthToken}
77
- >
78
- <StoresProvider>
79
- <div className="flex min-h-screen flex-col">
80
- <Header />
81
- <main className="flex-1">{children}</main>
82
- <Footer />
83
- </div>
84
- <Toaster position="bottom-right" richColors />
85
- </StoresProvider>
86
- </StorefrontProvider>
87
- </QueryProvider>
88
- </ThemeProvider>
89
- </body>
26
+ <html lang={locale || defaultLocale} suppressHydrationWarning>
27
+ <body className={inter.className}>{children}</body>
90
28
  </html>
91
29
  );
92
30
  }