feat: complete website implementation
This commit is contained in:
@@ -0,0 +1,149 @@
|
||||
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://aeroservice-demo.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://aeroservice-demo.pl/sklep',
|
||||
},
|
||||
{
|
||||
'@type': 'ListItem',
|
||||
position: 2,
|
||||
name: product.name,
|
||||
item: `https://aeroservice-demo.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>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
import { Metadata } from 'next';
|
||||
import { HeroSection } from '@/components/sections/HeroSection';
|
||||
import { ProductsGrid } from '@/components/shop/ProductsGrid';
|
||||
import { getProducts } from '@/content/products';
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: 'Sklep z klimatyzatorami | AirProject',
|
||||
description: 'Wybierz idealny klimatyzator do swojego domu. Oferujemy urządzenia renomowanych marek z możliwością profesjonalnego montażu.',
|
||||
alternates: {
|
||||
canonical: '/sklep',
|
||||
}
|
||||
};
|
||||
|
||||
export default function SklepPage() {
|
||||
const products = getProducts();
|
||||
|
||||
return (
|
||||
<main>
|
||||
<HeroSection
|
||||
eyebrow="E-commerce"
|
||||
title="Sklep z klimatyzatorami"
|
||||
subtitle="Znajdź urządzenie idealnie dopasowane do Twoich potrzeb. Zamów samo urządzenie lub wybierz kompleksową usługę z naszym profesjonalnym montażem."
|
||||
ctaText="Skontaktuj się z doradcą"
|
||||
ctaLink="/kontakt"
|
||||
backgroundImage="https://images.unsplash.com/photo-1527689638836-411945a2b57c?w=1600&q=80"
|
||||
/>
|
||||
|
||||
<section className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<ProductsGrid products={products} />
|
||||
</section>
|
||||
|
||||
{/* SEO Blok na dole */}
|
||||
<section className="bg-slate-50 py-16 border-t border-border mt-12">
|
||||
<div className="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8 prose prose-slate">
|
||||
<h2 className="text-2xl font-bold font-heading text-primary">Klimatyzatory ścienne, multisplit i przenośne</h2>
|
||||
<p>
|
||||
Wybór odpowiedniego klimatyzatora to decyzja na lata. W naszym sklepie oferujemy wyłącznie sprawdzone modele od liderów rynku, takich jak Rotenso, Daikin, Mitsubishi, Gree czy LG. Niezależnie od tego, czy szukasz prostego urządzenia ściennego (split), zaawansowanego systemu dla kilku pomieszczeń (multisplit), czy rozwiązania do biura (klimatyzatory kasetonowe), znajdziesz je u nas w atrakcyjnych cenach.
|
||||
</p>
|
||||
<p>
|
||||
Pamiętaj, że gwarancją niezawodności jest nie tylko sam sprzęt, ale przede wszystkim jego <strong>poprawny montaż</strong>. Zachęcamy do wyboru opcji "Kup z montażem" – nasi certyfikowani instalatorzy F-gaz zapewnią pełen profesjonalizm i bezpieczeństwo Twojej nowej instalacji, a Ty zyskasz spokój i pełną gwarancję producenta.
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user