import JsonLd from '@/components/JsonLd'; import { SITE_URL } from '@/lib/schema'; import ProductSidebar from '@/components/ProductSidebar'; import ProductTabs from '@/components/ProductTabs'; import ProductTechnicalData from '@/components/ProductTechnicalData'; import RelatedProducts from '@/components/RelatedProducts'; import DatasheetDownload from '@/components/DatasheetDownload'; import { Badge, Card, Container, Heading, Section } from '@/components/ui'; import { getDatasheetPath } from '@/lib/datasheets'; import { getAllProducts, getProductBySlug } from '@/lib/products'; import { mapFileSlugToTranslated, mapSlugToFileSlug } from '@/lib/slugs'; import { Metadata } from 'next'; import { getTranslations, setRequestLocale } from 'next-intl/server'; import { getProductOGImageMetadata } from '@/lib/metadata'; import Image from 'next/image'; import Link from 'next/link'; import { notFound, redirect } from 'next/navigation'; import ProductEngagementTracker from '@/components/analytics/ProductEngagementTracker'; import PayloadRichText from '@/components/PayloadRichText'; interface ProductPageProps { params: Promise<{ locale: string; slug: string[]; }>; } export async function generateMetadata({ params }: ProductPageProps): Promise { const { locale, slug } = await params; const productSlug = slug[slug.length - 1]; const t = await getTranslations('Products'); // Check if it's a category page const categories = [ 'low-voltage-cables', 'medium-voltage-cables', 'high-voltage-cables', 'solar-cables', ]; const fileSlug = await mapSlugToFileSlug(productSlug, locale); if (categories.includes(fileSlug)) { const categoryKey = fileSlug .replace(/-cables$/, '') .replace(/-([a-z])/g, (g) => g[1].toUpperCase()); const categoryTitle = t.has(`categories.${categoryKey}.title`) ? t(`categories.${categoryKey}.title`) : fileSlug; const categoryDesc = t.has(`categories.${categoryKey}.description`) ? t(`categories.${categoryKey}.description`) : ''; return { title: categoryTitle, description: categoryDesc, alternates: { canonical: `${SITE_URL}/${locale}/${await mapFileSlugToTranslated('products', locale)}/${await mapFileSlugToTranslated(fileSlug, locale)}`, languages: { de: `${SITE_URL}/de/${await mapFileSlugToTranslated('products', 'de')}/${await mapFileSlugToTranslated(fileSlug, 'de')}`, en: `${SITE_URL}/en/${await mapFileSlugToTranslated('products', 'en')}/${await mapFileSlugToTranslated(fileSlug, 'en')}`, 'x-default': `${SITE_URL}/en/${await mapFileSlugToTranslated('products', 'en')}/${await mapFileSlugToTranslated(fileSlug, 'en')}`, }, }, }; } const fileSlugs = await Promise.all(slug.map((s) => mapSlugToFileSlug(s, locale))); const getLocalizedPath = async (lang: string) => { const parts = await Promise.all([ mapFileSlugToTranslated('products', lang), ...fileSlugs.map((fs) => mapFileSlugToTranslated(fs, lang)), ]); return parts.join('/'); }; const product = await getProductBySlug(productSlug, locale); if (!product) return {}; const currentLocalePath = await getLocalizedPath(locale); return { title: product.frontmatter.title, description: product.frontmatter.description, alternates: { canonical: `${SITE_URL}/${locale}/${currentLocalePath}`, languages: { de: `${SITE_URL}/de/${await getLocalizedPath('de')}`, en: `${SITE_URL}/en/${await getLocalizedPath('en')}`, 'x-default': `${SITE_URL}/en/${await getLocalizedPath('en')}`, }, }, openGraph: { title: product.frontmatter.title, description: product.frontmatter.description, type: 'website', url: `${SITE_URL}/${locale}/${currentLocalePath}`, images: getProductOGImageMetadata(productSlug, product.frontmatter.title, locale), }, twitter: { card: 'summary_large_image', title: product.frontmatter.title, description: product.frontmatter.description, }, }; } export default async function ProductPage({ params }: ProductPageProps) { const { locale, slug } = await params; setRequestLocale(locale); const productSlug = slug[slug.length - 1]; const t = await getTranslations('Products'); const productsSlug = await mapFileSlugToTranslated('products', locale); const categories = [ 'low-voltage-cables', 'medium-voltage-cables', 'high-voltage-cables', 'solar-cables', ]; const fileSlugs = await Promise.all(slug.map((s) => mapSlugToFileSlug(s, locale))); const translatedSlugsForLocale = await Promise.all( fileSlugs.map((fs) => mapFileSlugToTranslated(fs, locale)), ); // If the requested slugs don't exactly match the translated slugs for the current locale // (i.e. if the user used the static language switcher but kept the original locale's slugs) if (slug.join('/') !== translatedSlugsForLocale.join('/')) { redirect(`/${locale}/${productsSlug}/${translatedSlugsForLocale.join('/')}`); } const fileSlug = fileSlugs[fileSlugs.length - 1]; if (categories.includes(fileSlug)) { const allProducts = await getAllProducts(locale); const categoryKey = fileSlug .replace(/-cables$/, '') .replace(/-([a-z])/g, (g) => g[1].toUpperCase()); const categoryTitle = t.has(`categories.${categoryKey}.title`) ? t(`categories.${categoryKey}.title`) : fileSlug; const filteredProducts = allProducts.filter((p) => { const firstCat = p.frontmatter.categories[0] || ''; const normalizedCat = firstCat.toLowerCase().replace(/\s+/g, '-'); let pFileSlug = 'low-voltage-cables'; if (normalizedCat === 'hochspannungskabel' || normalizedCat === 'high-voltage-cables') pFileSlug = 'high-voltage-cables'; else if ( normalizedCat === 'mittelspannungskabel' || normalizedCat === 'medium-voltage-cables' ) pFileSlug = 'medium-voltage-cables'; else if ( normalizedCat === 'solarkabel' || normalizedCat === 'solar-cables' || normalizedCat === 'solar' ) pFileSlug = 'solar-cables'; return pFileSlug === fileSlug; }); const productsWithTranslatedSlugs = await Promise.all( filteredProducts.map(async (p) => ({ ...p, translatedSlug: await mapFileSlugToTranslated(p.slug, locale), })), ); return (
{categoryTitle}
{productsWithTranslatedSlugs.map((product) => (
{product.frontmatter.images?.[0] && ( <> {product.frontmatter.title}
)}
{product.frontmatter.categories.map((cat, i) => ( {cat} ))}

{product.frontmatter.title}

{product.frontmatter.description}

{t('details')}
))}
); } const product = await getProductBySlug(productSlug, locale); if (!product) { notFound(); } // Extract technical data natively from the Lexical AST for Schema.org let technicalItems = []; if (product.content?.root?.children) { const productTabsBlock = product.content.root.children.find( (node: any) => node.type === 'block' && node.fields?.blockType === 'productTabs', ); if (productTabsBlock && productTabsBlock.fields?.technicalItems) { technicalItems = productTabsBlock.fields.technicalItems; } } const datasheetPath = getDatasheetPath(productSlug, locale); const isFallback = (product.frontmatter as any).isFallback; const categorySlug = slug[0]; const categoryFileSlug = await mapSlugToFileSlug(categorySlug, locale); const categoryKey = categoryFileSlug .replace(/-cables$/, '') .replace(/-([a-z])/g, (g) => g[1].toUpperCase()); const categoryTitle = t.has(`categories.${categoryKey}.title`) ? t(`categories.${categoryKey}.title`) : categoryFileSlug; // Split content into Description and Technical Data const rootChildren = product.content?.root?.children || []; const technicalBlocks = rootChildren.filter( (node: any) => node.type === 'block' && (node.fields?.blockType === 'productTabs' || node.fields?.blockType === 'productTechnicalData'), ); let descriptionChildren = rootChildren.filter( (node: any) => !( node.type === 'block' && (node.fields?.blockType === 'productTabs' || node.fields?.blockType === 'productTechnicalData') ), ); // If no standalone description nodes, extract from the productTabs block's embedded content if (descriptionChildren.length === 0) { const tabsBlock = rootChildren.find( (node: any) => node.type === 'block' && node.fields?.blockType === 'productTabs', ); if (tabsBlock?.fields?.content?.root?.children) { descriptionChildren = tabsBlock.fields.content.root.children.filter((node: any) => { // Filter out MDX parsing artifacts like `}>` if (node.type === 'paragraph' && node.children?.length === 1) { const text = node.children[0]?.text?.trim(); return text !== '}>' && text !== '{' && text !== '}'; } return true; }); } } console.log(`[DEBUG PAGE] Slug: ${productSlug}, children count: ${descriptionChildren.length}`); const descriptionContent = { root: { ...product.content.root, children: descriptionChildren, }, }; const technicalContent = { root: { ...product.content.root, children: technicalBlocks, }, }; const sidebar = ( ); return (
{/* Product Hero */}
{/* Background Decorative Elements */}
{isFallback && (
{t('englishVersion')}
)}
{product.frontmatter.categories.map((cat, idx) => ( {cat} ))}
{product.frontmatter.title}

{product.frontmatter.description}

{/* Large Product Image Section */} {product.frontmatter.images && product.frontmatter.images.length > 0 && (
{product.frontmatter.title} {/* Subtle reflection/shadow effect */}
{product.frontmatter.images.length > 1 && (
{product.frontmatter.images.slice(0, 5).map((img, idx) => (
))}
)}
)}
{/* Description Area Next to Sidebar */}
{descriptionChildren.length > 0 ? ( ) : product.frontmatter.description ? (

{product.frontmatter.description}

) : null} {product.application?.root?.children?.length > 0 && (
)}
{/* Sidebar Column */}
{sidebar}
{/* Full-width Technical Data Below */}
{/* Datasheet Download Section */} {categoryFileSlug === 'medium-voltage-cables' && datasheetPath && (

{t('downloadDatasheet')}

)} {/* Structured Data (Hidden) */} ({ '@type': 'PropertyValue', name: item.label, value: item.value, })), category: product.frontmatter.categories.join(', '), mainEntityOfPage: { '@type': 'WebPage', '@id': `${SITE_URL}/${locale}/${await mapFileSlugToTranslated('products', locale)}/${slug.join('/')}`, }, } as any } />
{/* Related Products Section */}
); }