move to gitea

This commit is contained in:
Mamadou Sall
2025-08-24 22:41:31 +02:00
commit 032b1d9452
122 changed files with 28723 additions and 0 deletions

View File

@@ -0,0 +1,181 @@
// src/app/produit/[slug]/page.tsx
import { Metadata } from 'next';
import { notFound } from 'next/navigation';
import { WooCommerceService, formatPrice, isOnSale, getDiscountPercentage } from '@/lib/woocommerce';
import ProductImageGallery from '@/components/product/ProductImageGallery';
import ProductInfo from '@/components/product/ProductInfo';
import RelatedProducts from '@/components/product/RelatedProducts';
import ProductTabs from '@/components/product/ProductTabs';
import Breadcrumb from '@/components/ui/breadcrumb';
interface ProductPageProps {
params: {
slug: string;
};
}
export async function generateMetadata({ params }: ProductPageProps): Promise<Metadata> {
const response = await WooCommerceService.getProductBySlug(params.slug);
if (!response.success || !response.data) {
return {
title: 'Produit non trouvé',
};
}
const product = response.data;
const discountPercentage = getDiscountPercentage(product);
return {
title: `${product.name} - MELHFA`,
description: product.short_description || product.description,
openGraph: {
title: product.name,
description: product.short_description || product.description,
images: product.images.map(img => ({
url: img.src,
width: 800,
height: 1200,
alt: img.alt || product.name,
})),
type: 'product',
},
twitter: {
card: 'summary_large_image',
title: product.name,
description: product.short_description || product.description,
images: [product.images[0]?.src || ''],
},
alternates: {
canonical: `/produit/${product.slug}`,
},
other: {
'product:price:amount': product.price,
'product:price:currency': 'MRU',
'product:availability': product.stock_status === 'instock' ? 'in stock' : 'out of stock',
'product:condition': 'new',
...(isOnSale(product) && {
'product:sale_price:amount': product.sale_price,
'product:sale_price:currency': 'MRU',
}),
},
};
}
export default async function ProductPage({ params }: ProductPageProps): Promise<JSX.Element> {
// Récupérer le produit
const response = await WooCommerceService.getProductBySlug(params.slug);
if (!response.success || !response.data) {
notFound();
}
const product = response.data;
// Récupérer les produits liés
const relatedProductsResponse = await WooCommerceService.getProducts({
per_page: 4,
exclude: [product.id],
category: product.categories[0]?.slug,
});
const relatedProducts = relatedProductsResponse.data || [];
// Breadcrumb items
const breadcrumbItems = [
{ label: 'Accueil', href: '/' },
{ label: 'Boutique', href: '/boutique' },
...(product.categories.length > 0 ? [{
label: product.categories[0].name,
href: `/boutique?category=${product.categories[0].slug}`
}] : []),
{ label: product.name, href: `/produit/${product.slug}` },
];
return (
<div className="min-h-screen bg-white">
{/* Breadcrumb */}
<div className="max-w-[1400px] mx-auto px-6 pt-20 pb-6">
<Breadcrumb items={breadcrumbItems} />
</div>
{/* Product Details */}
<div className="max-w-[1400px] mx-auto px-6 pb-16">
<div className="grid grid-cols-1 lg:grid-cols-2 gap-12 lg:gap-16">
{/* Image Gallery */}
<div className="space-y-4">
<ProductImageGallery
images={product.images}
productName={product.name}
isOnSale={isOnSale(product)}
discountPercentage={getDiscountPercentage(product)}
isFeatured={product.featured}
/>
</div>
{/* Product Info */}
<div className="space-y-8">
<ProductInfo product={product} />
</div>
</div>
</div>
{/* Product Tabs */}
<div className="max-w-[1400px] mx-auto px-6 pb-16">
<ProductTabs product={product} />
</div>
{/* Related Products */}
{relatedProducts.length > 0 && (
<div className="bg-gray-50 py-16">
<div className="max-w-[1400px] mx-auto px-6">
<RelatedProducts products={relatedProducts} />
</div>
</div>
)}
{/* Structured Data for SEO */}
<script
type="application/ld+json"
dangerouslySetInnerHTML={{
__html: JSON.stringify({
'@context': 'https://schema.org',
'@type': 'Product',
name: product.name,
description: product.description,
image: product.images.map(img => img.src),
sku: product.sku,
mpn: product.sku,
brand: {
'@type': 'Brand',
name: 'MELHFA'
},
offers: {
'@type': 'Offer',
price: product.price,
priceCurrency: 'MRU',
availability: product.stock_status === 'instock'
? 'https://schema.org/InStock'
: 'https://schema.org/OutOfStock',
url: `${process.env.NEXT_PUBLIC_SITE_URL}/produit/${product.slug}`,
seller: {
'@type': 'Organization',
name: 'MELHFA'
},
...(isOnSale(product) && {
priceValidUntil: product.date_on_sale_to || '2024-12-31'
})
},
aggregateRating: product.rating_count > 0 ? {
'@type': 'AggregateRating',
ratingValue: product.average_rating,
reviewCount: product.rating_count
} : undefined,
category: product.categories.map(cat => cat.name).join(', '),
})
}}
/>
</div>
);
}