150 lines
4.1 KiB
TypeScript
150 lines
4.1 KiB
TypeScript
import { Metadata } from 'next';
|
|
import { notFound } from 'next/navigation';
|
|
import { getProducts, getProductBySlug } from '@/content/products';
|
|
import { ProductGallery } from '@/components/shop/ProductGallery';
|
|
import { ProductInfo } from '@/components/shop/ProductInfo';
|
|
import { Tabs } from '@/components/shop/Tabs';
|
|
import { ProductCard } from '@/components/shop/ProductCard';
|
|
import { ArrowLeft } from 'lucide-react';
|
|
import Link from 'next/link';
|
|
|
|
interface ProductPageProps {
|
|
params: {
|
|
slug: string;
|
|
};
|
|
}
|
|
|
|
export async function generateStaticParams() {
|
|
const products = getProducts();
|
|
return products.map((product) => ({
|
|
slug: product.slug,
|
|
}));
|
|
}
|
|
|
|
export async function generateMetadata({ params }: ProductPageProps): Promise<Metadata> {
|
|
const { slug } = await params;
|
|
const product = getProductBySlug(slug);
|
|
|
|
if (!product) {
|
|
return { title: 'Produkt nie znaleziony' };
|
|
}
|
|
|
|
return {
|
|
title: `${product.brand} ${product.name} | Sklep AirProject`,
|
|
description: product.description,
|
|
alternates: {
|
|
canonical: `/sklep/${product.slug}`,
|
|
},
|
|
openGraph: {
|
|
title: `${product.brand} ${product.name}`,
|
|
description: product.description,
|
|
type: 'website',
|
|
images: [
|
|
{
|
|
url: product.image || '/hero.jpg',
|
|
},
|
|
],
|
|
},
|
|
};
|
|
}
|
|
|
|
export default async function ProductPage({ params }: ProductPageProps) {
|
|
const { slug } = await params;
|
|
const product = getProductBySlug(slug);
|
|
|
|
if (!product) {
|
|
notFound();
|
|
}
|
|
|
|
const allProducts = getProducts();
|
|
// Dobieramy 4 podobne produkty
|
|
const relatedProducts = allProducts
|
|
.filter(p => p.category === product.category && p.slug !== product.slug)
|
|
.slice(0, 4);
|
|
|
|
// JSON-LD
|
|
const jsonLdProduct = {
|
|
'@context': 'https://schema.org',
|
|
'@type': 'Product',
|
|
name: `${product.brand} ${product.name}`,
|
|
image: product.image,
|
|
description: product.description,
|
|
brand: {
|
|
'@type': 'Brand',
|
|
name: product.brand,
|
|
},
|
|
offers: {
|
|
'@type': 'Offer',
|
|
url: `https://thermcool.pl/sklep/${product.slug}`,
|
|
priceCurrency: 'PLN',
|
|
price: product.price,
|
|
itemCondition: 'https://schema.org/NewCondition',
|
|
availability: 'https://schema.org/InStock',
|
|
},
|
|
};
|
|
|
|
const jsonLdBreadcrumb = {
|
|
'@context': 'https://schema.org',
|
|
'@type': 'BreadcrumbList',
|
|
itemListElement: [
|
|
{
|
|
'@type': 'ListItem',
|
|
position: 1,
|
|
name: 'Sklep',
|
|
item: 'https://thermcool.pl/sklep',
|
|
},
|
|
{
|
|
'@type': 'ListItem',
|
|
position: 2,
|
|
name: product.name,
|
|
item: `https://thermcool.pl/sklep/${product.slug}`,
|
|
},
|
|
],
|
|
};
|
|
|
|
return (
|
|
<main className="bg-slate-50 pt-24 pb-16 min-h-screen">
|
|
<script
|
|
type="application/ld+json"
|
|
dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLdProduct) }}
|
|
/>
|
|
<script
|
|
type="application/ld+json"
|
|
dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLdBreadcrumb) }}
|
|
/>
|
|
|
|
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
|
{/* Nawigacja powrotu */}
|
|
<Link
|
|
href="/sklep"
|
|
className="inline-flex items-center gap-2 text-sm font-semibold text-text-muted hover:text-primary transition-colors mb-8"
|
|
>
|
|
<ArrowLeft className="w-4 h-4" />
|
|
Wróć do sklepu
|
|
</Link>
|
|
|
|
{/* Górna sekcja */}
|
|
<div className="grid grid-cols-1 lg:grid-cols-2 gap-12 lg:gap-16">
|
|
<ProductGallery image={product.image || ''} name={product.name} />
|
|
<ProductInfo product={product} />
|
|
</div>
|
|
|
|
{/* Zakładki z detalami */}
|
|
<Tabs product={product} />
|
|
|
|
{/* Podobne produkty */}
|
|
{relatedProducts.length > 0 && (
|
|
<section className="mt-24">
|
|
<h2 className="text-2xl font-bold text-primary font-heading mb-8">Podobne produkty</h2>
|
|
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-6">
|
|
{relatedProducts.map(relProduct => (
|
|
<ProductCard key={relProduct.slug} product={relProduct} />
|
|
))}
|
|
</div>
|
|
</section>
|
|
)}
|
|
</div>
|
|
</main>
|
|
);
|
|
}
|