This commit is contained in:
2026-01-19 18:07:39 +01:00
parent 1f624f3d7f
commit e8b6b13a3b
5 changed files with 120 additions and 76 deletions

View File

@@ -1,17 +1,17 @@
import { notFound } from 'next/navigation';
import { MDXRemote } from 'next-mdx-remote/rsc';
import { getProductBySlug, getAllProducts } from '@/lib/mdx';
import { getDatasheetPath } from '@/lib/datasheets';
import ProductTechnicalData from '@/components/ProductTechnicalData';
import ProductTabs from '@/components/ProductTabs';
import RequestQuoteForm from '@/components/RequestQuoteForm';
import RelatedProducts from '@/components/RelatedProducts';
import ProductSidebar from '@/components/ProductSidebar';
import Link from 'next/link';
import Image from 'next/image';
import { getTranslations } from 'next-intl/server';
import { Section, Container, Badge } from '@/components/ui';
import ProductTabs from '@/components/ProductTabs';
import ProductTechnicalData from '@/components/ProductTechnicalData';
import RelatedProducts from '@/components/RelatedProducts';
import RequestQuoteForm from '@/components/RequestQuoteForm';
import { Badge, Container, Section } from '@/components/ui';
import { getDatasheetPath } from '@/lib/datasheets';
import { getAllProducts, getProductBySlug } from '@/lib/mdx';
import { Metadata } from 'next';
import { getTranslations } from 'next-intl/server';
import { MDXRemote } from 'next-mdx-remote/rsc';
import Image from 'next/image';
import Link from 'next/link';
import { notFound } from 'next/navigation';
interface ProductPageProps {
params: {
@@ -66,15 +66,16 @@ export async function generateMetadata({ params }: ProductPageProps): Promise<Me
const components = {
ProductTechnicalData,
ProductTabs,
p: (props: any) => <p {...props} className="text-lg md:text-xl text-text-secondary leading-relaxed mb-8 font-medium" />,
p: (props: any) => <p {...props} className="text-lg md:text-xl text-text-secondary leading-relaxed mb-6 font-medium block !mb-6" />,
h2: (props: any) => (
<div className="relative mt-24 mb-12">
<h2 {...props} className="text-4xl md:text-5xl font-black text-primary tracking-tighter uppercase" />
<div className="mt-4 w-20 h-1.5 bg-accent rounded-full" />
<div className="relative mt-20 mb-10 block !mt-20 !mb-10">
<h2 {...props} className="text-4xl md:text-5xl font-black text-primary tracking-tighter uppercase mb-6 block !mb-6" />
<div className="w-20 h-1.5 bg-accent rounded-full" />
</div>
),
h3: (props: any) => <h3 {...props} className="text-2xl md:text-3xl font-black text-primary mt-16 mb-6 tracking-tight uppercase" />,
ul: (props: any) => <ul {...props} className="list-none pl-0 mb-12 space-y-4" />,
h3: (props: any) => <h3 {...props} className="text-2xl md:text-3xl font-black text-primary mt-16 mb-6 tracking-tight uppercase block !mt-16 !mb-6" />,
ul: (props: any) => <ul {...props} className="list-none pl-0 mt-4 mb-8 space-y-4 block !mt-4 !mb-8" />,
section: (props: any) => <div {...props} className="block" />,
li: (props: any) => (
<li className="flex items-start gap-4 group">
<div className="mt-2.5 w-2 h-2 rounded-full bg-accent flex-shrink-0 group-hover:scale-125 transition-transform" />
@@ -133,7 +134,7 @@ export default async function ProductPage({ params }: ProductPageProps) {
</Container>
</section>
<Section className="bg-neutral-light relative z-20 -mt-12 rounded-t-[48px] md:rounded-t-[64px]">
<Section className="bg-neutral-light relative">
<Container>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8">
{filteredProducts.map((product) => (
@@ -245,7 +246,7 @@ export default async function ProductPage({ params }: ProductPageProps) {
</Container>
</section>
<Section className="bg-white relative z-20 -mt-12 pt-0 rounded-t-[48px] md:rounded-t-[64px]">
<Section className="bg-white relative">
<Container className="relative">
{/* Large Product Image Section */}
{product.frontmatter.images && product.frontmatter.images.length > 0 && (
@@ -319,7 +320,7 @@ export default async function ProductPage({ params }: ProductPageProps) {
<div className="w-full">
{/* Main Content Area */}
<div className="max-w-none">
<div className="max-w-none [&_h3]:mt-16 [&_h3]:mb-6 [&_p]:mb-6 [&_ul]:mt-4 [&_ul]:mb-8 [&_li]:mb-4">
<MDXRemote source={product.content} components={components} />
</div>
@@ -358,7 +359,7 @@ export default async function ProductPage({ params }: ProductPageProps) {
</div>
{/* Related Products Section */}
<div className="mt-32 pt-32">
<div className="mt-16 pt-16 border-t border-neutral-dark/5">
<RelatedProducts
currentSlug={productSlug}
categories={product.frontmatter.categories}

View File

@@ -1,4 +1,7 @@
import React from 'react';
'use client';
import React, { useState } from 'react';
import { useTranslations } from 'next-intl';
interface KeyValueItem {
label: string;
@@ -21,9 +24,19 @@ interface ProductTechnicalDataProps {
}
export default function ProductTechnicalData({ data }: ProductTechnicalDataProps) {
const t = useTranslations('Products');
const [expandedTables, setExpandedTables] = useState<Record<number, boolean>>({});
if (!data) return null;
const { technicalItems = [], voltageTables = [] } = data;
const toggleTable = (idx: number) => {
setExpandedTables(prev => ({
...prev,
[idx]: !prev[idx]
}));
};
return (
<div className="space-y-16">
{technicalItems.length > 0 && (
@@ -45,58 +58,84 @@ export default function ProductTechnicalData({ data }: ProductTechnicalDataProps
</div>
)}
{voltageTables.map((table, idx) => (
<div key={idx} className="bg-white p-8 md:p-12 rounded-[32px] shadow-sm border border-neutral-dark/5 overflow-hidden">
<h3 className="text-2xl font-bold text-primary mb-8 flex items-center gap-3">
<div className="w-2 h-8 bg-accent rounded-full" />
{table.voltageLabel !== 'Voltage unknown' && table.voltageLabel !== 'Spannung unbekannt'
? table.voltageLabel
: 'Technical Specifications'}
</h3>
{table.metaItems.length > 0 && (
<dl className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-8 mb-12 bg-neutral-light/50 p-8 rounded-2xl border border-neutral-dark/5">
{table.metaItems.map((item, mIdx) => (
<div key={mIdx}>
<dt className="text-[10px] font-black uppercase tracking-[0.2em] text-primary/40 mb-1">{item.label}</dt>
<dd className="font-bold text-primary">{item.value} {item.unit}</dd>
</div>
))}
</dl>
)}
<div className="overflow-x-auto -mx-8 md:-mx-12 px-8 md:px-12">
<table className="min-w-full border-separate border-spacing-0">
<thead>
<tr>
<th scope="col" className="px-3 py-3 text-left text-[10px] font-black text-primary/40 uppercase tracking-[0.2em] sticky left-0 bg-white z-10 border-b border-neutral-dark/10">
Config.
</th>
{table.columns.map((col, cIdx) => (
<th key={cIdx} scope="col" className="px-3 py-3 text-left text-[10px] font-black text-primary/40 uppercase tracking-[0.2em] whitespace-nowrap border-b border-neutral-dark/10">
{col.label}
</th>
))}
</tr>
</thead>
<tbody className="divide-y divide-neutral-dark/5">
{table.rows.map((row, rIdx) => (
<tr key={rIdx} className="hover:bg-neutral-light/50 transition-colors group">
<td className="px-3 py-2 text-xs font-bold text-primary sticky left-0 bg-white group-hover:bg-neutral-light/50 z-10 whitespace-nowrap">
{row.configuration}
</td>
{row.cells.map((cell, cellIdx) => (
<td key={cellIdx} className="px-3 py-2 text-xs text-text-secondary whitespace-nowrap">
{cell}
</td>
))}
</tr>
{voltageTables.map((table, idx) => {
const isExpanded = expandedTables[idx];
const hasManyRows = table.rows.length > 10;
return (
<div key={idx} className="bg-white p-8 md:p-12 rounded-[32px] shadow-sm border border-neutral-dark/5 overflow-hidden">
<h3 className="text-2xl font-bold text-primary mb-8 flex items-center gap-3">
<div className="w-2 h-8 bg-accent rounded-full" />
{table.voltageLabel !== 'Voltage unknown' && table.voltageLabel !== 'Spannung unbekannt'
? table.voltageLabel
: 'Technical Specifications'}
</h3>
{table.metaItems.length > 0 && (
<dl className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-8 mb-12 bg-neutral-light/50 p-8 rounded-2xl border border-neutral-dark/5">
{table.metaItems.map((item, mIdx) => (
<div key={mIdx}>
<dt className="text-[10px] font-black uppercase tracking-[0.2em] text-primary/40 mb-1">{item.label}</dt>
<dd className="font-bold text-primary">{item.value} {item.unit}</dd>
</div>
))}
</tbody>
</table>
</dl>
)}
<div className="relative">
<div
className={`overflow-x-auto -mx-8 md:-mx-12 px-8 md:px-12 transition-all duration-500 ease-in-out ${
!isExpanded && hasManyRows ? 'max-h-[400px] overflow-y-hidden' : 'max-h-[none]'
}`}
>
<table className="min-w-full border-separate border-spacing-0">
<thead>
<tr>
<th scope="col" className="px-3 py-3 text-left text-[10px] font-black text-primary/40 uppercase tracking-[0.2em] sticky left-0 bg-white z-10 border-b border-neutral-dark/10">
Config.
</th>
{table.columns.map((col, cIdx) => (
<th key={cIdx} scope="col" className="px-3 py-3 text-left text-[10px] font-black text-primary/40 uppercase tracking-[0.2em] whitespace-nowrap border-b border-neutral-dark/10">
{col.label}
</th>
))}
</tr>
</thead>
<tbody className="divide-y divide-neutral-dark/5">
{table.rows.map((row, rIdx) => (
<tr key={rIdx} className="hover:bg-neutral-light/50 transition-colors group">
<td className="px-3 py-2 text-xs font-bold text-primary sticky left-0 bg-white group-hover:bg-neutral-light/50 z-10 whitespace-nowrap">
{row.configuration}
</td>
{row.cells.map((cell, cellIdx) => (
<td key={cellIdx} className="px-3 py-2 text-xs text-text-secondary whitespace-nowrap">
{cell}
</td>
))}
</tr>
))}
</tbody>
</table>
</div>
{!isExpanded && hasManyRows && (
<div className="absolute bottom-0 left-0 right-0 h-32 bg-gradient-to-t from-white via-white/80 to-transparent z-20 pointer-events-none" />
)}
</div>
{hasManyRows && (
<div className="mt-8 flex justify-center">
<button
onClick={() => toggleTable(idx)}
className="px-8 py-3 rounded-full bg-primary text-white text-sm font-bold uppercase tracking-widest hover:bg-accent hover:text-primary transition-all duration-300 shadow-lg hover:shadow-accent/20"
>
{isExpanded ? t('showLess') : t('showMore')}
</button>
</div>
)}
</div>
</div>
))}
);
})}
</div>
);
}

View File

@@ -1,7 +1,7 @@
import Link from 'next/link';
import Image from 'next/image';
import { getAllProducts } from '@/lib/mdx';
import { getTranslations } from 'next-intl/server';
import Image from 'next/image';
import Link from 'next/link';
interface RelatedProductsProps {
currentSlug: string;
@@ -24,7 +24,7 @@ export default async function RelatedProducts({ currentSlug, categories, locale
if (related.length === 0) return null;
return (
<div className="mt-32 pt-32 border-t border-neutral-dark/10">
<div className="">
<div className="flex items-end justify-between mb-12">
<div>
<h2 className="text-3xl md:text-4xl font-extrabold text-primary tracking-tight mb-4">

View File

@@ -172,6 +172,8 @@
"privacyNote": "Mit dem Absenden erklären Sie sich mit unserer Datenschutzerklärung einverstanden"
},
"englishVersion": "Englische Version",
"showMore": "Mehr anzeigen",
"showLess": "Weniger anzeigen",
"categories": {
"lowVoltage": {
"title": "Niederspannungskabel",

View File

@@ -172,6 +172,8 @@
"privacyNote": "By submitting you agree to our privacy policy"
},
"englishVersion": "English Version",
"showMore": "Show More",
"showLess": "Show Less",
"categories": {
"lowVoltage": {
"title": "Low Voltage Cables",