import ProductPage from "../../../components/ProductPage"; import { productService } from "../../../lib/services/productService"; import type { Metadata } from 'next'; import { Suspense } from 'react'; import { getSeoConfig, formatPageTitle } from "../../../lib/seo"; export const revalidate = 3600; export async function generateMetadata( { params }: { params: Promise<{ slug: string }> } ): Promise { const resolvedParams = await params; const product = await productService.getProductBySlug(resolvedParams.slug); const config = await getSeoConfig(); if (!product) { return { title: formatPageTitle('مکمل درمانی یافت نشد', config.brandNameFa, config.titleSeparator, config.titlePosition), }; } const nameFa = product.nameFa || product.name; const nameEn = product.nameEn ? ` (${product.nameEn})` : ''; const rawTitle = `${nameFa}${nameEn}`; const title = product.metaTitle || formatPageTitle(rawTitle, config.brandNameFa, config.titleSeparator, config.titlePosition, false); const description = product.metaDescription || (product.shortDescription || product.description || '').substring(0, 155).trim() || config.defaultMetaDescription; const rawKeywords = product.keywords; const keywords = typeof rawKeywords === 'string' ? rawKeywords.split(',').map(k => k.trim()) : (product.main_ingredients?.length ? product.main_ingredients : config.keywords); const baseCanonical = config.canonicalBaseUrl.replace(/\/$/, ''); const canonicalUrl = product.canonicalUrl?.startsWith('http') ? product.canonicalUrl : `${baseCanonical}${product.canonicalUrl?.startsWith('/') ? product.canonicalUrl : `/shop/${resolvedParams.slug}`}`; const primaryImage = product.ogImage || product.image || config.ogImageUrl; const imageAlt = product.featuredImageAlt || nameFa; const galleryImages = [ { url: primaryImage, width: 1200, height: 630, alt: imageAlt, }, ...(product.images || []) .filter(img => img && img !== primaryImage) .map(img => ({ url: img, width: 800, height: 800, alt: `${nameFa} - گالری تصویر`, })) ]; return { title, description, keywords, robots: { index: !product.noIndex, follow: !product.noFollow, googleBot: { index: !product.noIndex, follow: !product.noFollow, 'max-video-preview': -1, 'max-image-preview': 'large', 'max-snippet': -1, }, }, openGraph: { title, description, images: galleryImages, url: canonicalUrl, siteName: config.brandNameFa, type: "website", }, twitter: { card: "summary_large_image", title, description, images: [primaryImage], }, alternates: { canonical: canonicalUrl, } }; } export default async function ShopProductPage({ params }: { params: Promise<{ slug: string }> }) { const resolvedParams = await params; const product = await productService.getProductBySlug(resolvedParams.slug); if (!product) { return ; } const siteUrl = process.env.NEXT_PUBLIC_SITE_URL || 'https://canina.ir'; const productUrl = `${siteUrl}/shop/${resolvedParams.slug}`; const priceInRial = product.priceValue * 10; // Map stock status to Schema.org availability let availabilityUrl = 'https://schema.org/InStock'; if (product.stockStatus === 'OUT_OF_STOCK') { availabilityUrl = 'https://schema.org/OutOfStock'; } else if (product.stockStatus === 'PRE_ORDER') { availabilityUrl = 'https://schema.org/PreOrder'; } else if (product.stockStatus === 'DISCONTINUED') { availabilityUrl = 'https://schema.org/Discontinued'; } // Collect unique images const allImages = Array.from(new Set([ product.image, product.ogImage, ...(product.images || []) ])).filter(Boolean) as string[]; // 1 year valid price offer based on product timestamp const baseTime = product.updatedAt ? new Date(product.updatedAt).getTime() : 1771939200000; const priceValidUntil = new Date(baseTime + 365 * 24 * 60 * 60 * 1000).toISOString().split('T')[0]; // Process approved reviews and ratings const approvedReviews = (product.reviews || []).filter(r => r.status === 'APPROVED' || !r.status); const totalReviewsCount = approvedReviews.length; const averageRating = totalReviewsCount > 0 ? Number((approvedReviews.reduce((sum, r) => sum + (Number(r.rating) || 5), 0) / totalReviewsCount).toFixed(1)) : 5; const productJsonLd: Record = { '@context': 'https://schema.org', '@type': 'Product', name: product.nameEn ? `${product.nameFa || product.name} (${product.nameEn})` : (product.nameFa || product.name), image: allImages.length > 0 ? allImages : [product.image], description: product.metaDescription || product.shortDescription || product.description, sku: product.artNo, mpn: product.artNo, ...(product.barcode ? { gtin: product.barcode, gtin13: product.barcode } : {}), brand: { '@type': 'Brand', name: 'Canina pharma GmbH', url: 'https://canina.de' }, category: product.category, offers: { '@type': 'Offer', url: productUrl, priceCurrency: 'IRR', price: priceInRial, priceValidUntil, availability: availabilityUrl, itemCondition: 'https://schema.org/NewCondition', seller: { '@type': 'Organization', name: 'کنینا ایران' } } }; // Add aggregateRating & reviews only when reviews exist if (totalReviewsCount > 0) { productJsonLd.aggregateRating = { '@type': 'AggregateRating', ratingValue: averageRating, reviewCount: totalReviewsCount, bestRating: 5, worstRating: 1 }; productJsonLd.review = approvedReviews.slice(0, 10).map(rev => ({ '@type': 'Review', author: { '@type': 'Person', name: rev.userName || 'کاربر کنینا' }, datePublished: rev.createdAt ? new Date(rev.createdAt).toISOString().split('T')[0] : new Date().toISOString().split('T')[0], reviewRating: { '@type': 'Rating', ratingValue: rev.rating || 5, bestRating: 5, worstRating: 1 }, reviewBody: rev.comment })); } // Multi-level BreadcrumbList Schema const breadcrumbElements = [ { '@type': 'ListItem', position: 1, name: 'صفحه اصلی', item: siteUrl }, { '@type': 'ListItem', position: 2, name: 'فروشگاه مکمل‌ها', item: `${siteUrl}/shop` } ]; if (product.category) { breadcrumbElements.push({ '@type': 'ListItem', position: 3, name: product.category, item: `${siteUrl}/shop?category=${product.categorySlug || 'all'}` }); breadcrumbElements.push({ '@type': 'ListItem', position: 4, name: product.nameFa || product.name, item: productUrl }); } else { breadcrumbElements.push({ '@type': 'ListItem', position: 3, name: product.nameFa || product.name, item: productUrl }); } const breadcrumbJsonLd = { '@context': 'https://schema.org', '@type': 'BreadcrumbList', itemListElement: breadcrumbElements }; const safeProductJsonLd = JSON.stringify(productJsonLd).replace(/