- Fix newsletter subscribe box centering on homepage - Fix header overlap on product pages (pt-[72px] instead of pt-[100px]) - Add scroll-mt-[72px] for smooth scroll anchor offset - Add HeroVideo component with video hero placeholder - Add REDESIGN_SPECIFICATION.md with 9-phase design plan - Clean up globals.css theme declarations and comments - Update Header with improved sticky behavior and cart - Update ProductDetail with better layout and spacing - Update CartDrawer with improved slide-out cart UI - Add English translations for updated pages - Various CSS refinements across pages
124 lines
3.8 KiB
TypeScript
124 lines
3.8 KiB
TypeScript
import { getProductBySlug, getProducts, getLocalizedProduct } from "@/lib/saleor";
|
|
import Header from "@/components/layout/Header";
|
|
import Footer from "@/components/layout/Footer";
|
|
import ProductDetail from "@/components/product/ProductDetail";
|
|
import type { Product } from "@/types/saleor";
|
|
|
|
interface ProductPageProps {
|
|
params: Promise<{ slug: string; locale?: string }>;
|
|
}
|
|
|
|
// Generate static params for all products
|
|
export async function generateStaticParams() {
|
|
try {
|
|
const products = await getProducts("SR", 100);
|
|
const params: Array<{ slug: string; locale: string }> = [];
|
|
|
|
products.forEach((product: Product) => {
|
|
// Serbian slug
|
|
params.push({ slug: product.slug, locale: "sr" });
|
|
|
|
// English slug (if translation exists)
|
|
if (product.translation?.slug) {
|
|
params.push({ slug: product.translation.slug, locale: "en" });
|
|
}
|
|
});
|
|
|
|
return params;
|
|
} catch (e) {
|
|
return [];
|
|
}
|
|
}
|
|
|
|
export async function generateMetadata({ params }: ProductPageProps) {
|
|
const { slug, locale = "sr" } = await params;
|
|
const product = await getProductBySlug(slug, locale.toUpperCase());
|
|
|
|
if (!product) {
|
|
return {
|
|
title: locale === "en" ? "Product Not Found" : "Proizvod nije pronađen",
|
|
};
|
|
}
|
|
|
|
const localized = getLocalizedProduct(product, locale.toUpperCase());
|
|
|
|
return {
|
|
title: localized.name,
|
|
description: localized.seoDescription || localized.description?.slice(0, 160),
|
|
alternates: {
|
|
canonical: `/products/${product.slug}`,
|
|
languages: {
|
|
"sr": `/products/${product.slug}`,
|
|
"en": product.translation?.slug ? `/products/${product.translation.slug}` : `/products/${product.slug}`,
|
|
},
|
|
},
|
|
openGraph: {
|
|
title: localized.name,
|
|
description: localized.seoDescription || localized.description?.slice(0, 160),
|
|
images: product.media?.[0]?.url ? [product.media[0].url] : [],
|
|
type: 'website',
|
|
},
|
|
};
|
|
}
|
|
|
|
export default async function ProductPage({ params }: ProductPageProps) {
|
|
const { slug, locale = "sr" } = await params;
|
|
const product = await getProductBySlug(slug, locale.toUpperCase());
|
|
|
|
if (!product) {
|
|
return (
|
|
<>
|
|
<Header />
|
|
<main className="min-h-screen bg-white">
|
|
<div className="pt-[180px] lg:pt-[200px] pb-20 text-center px-4">
|
|
<h1 className="text-2xl font-medium mb-4">
|
|
{locale === "en" ? "Product not found" : "Proizvod nije pronađen"}
|
|
</h1>
|
|
<p className="text-[#666666] mb-8">
|
|
{locale === "en"
|
|
? "The product you're looking for doesn't exist or has been removed."
|
|
: "Proizvod koji tražite ne postoji ili je uklonjen."}
|
|
</p>
|
|
<a
|
|
href="/products"
|
|
className="inline-block px-8 py-3 bg-black text-white text-sm uppercase tracking-[0.1em] hover:bg-[#333333] transition-colors"
|
|
>
|
|
{locale === "en" ? "Browse Products" : "Pregledaj proizvode"}
|
|
</a>
|
|
</div>
|
|
</main>
|
|
<Footer />
|
|
</>
|
|
);
|
|
}
|
|
|
|
// Determine language based on which slug matched
|
|
const isEnglishSlug = slug === product.translation?.slug;
|
|
const currentLocale = isEnglishSlug ? "EN" : "SR";
|
|
|
|
// Get related products (same category or just other products)
|
|
let relatedProducts: Product[] = [];
|
|
try {
|
|
const allProducts = await getProducts(currentLocale, 8);
|
|
relatedProducts = allProducts
|
|
.filter((p: Product) => p.id !== product.id)
|
|
.slice(0, 4);
|
|
} catch (e) {
|
|
// Ignore error, just won't show related products
|
|
}
|
|
|
|
return (
|
|
<>
|
|
<Header />
|
|
<main className="min-h-screen bg-white">
|
|
<ProductDetail
|
|
product={product}
|
|
relatedProducts={relatedProducts}
|
|
locale={currentLocale}
|
|
/>
|
|
</main>
|
|
<Footer />
|
|
</>
|
|
);
|
|
}
|