This commit is contained in:
2026-01-17 01:50:54 +01:00
parent c12b32ed5e
commit c8f61257c9
62 changed files with 1297 additions and 1028 deletions

View File

@@ -156,92 +156,99 @@ export default async function BlogPost({ params: { locale, slug } }: BlogPostPro
}
return (
<article className="bg-gradient-to-b from-neutral-light/30 to-white min-h-screen">
<article className="bg-white min-h-screen font-sans">
{/* Featured Image Header */}
{post.frontmatter.featuredImage && (
<div className="relative w-full h-[300px] md:h-[500px] overflow-hidden">
<img
src={post.frontmatter.featuredImage}
alt={post.frontmatter.title}
className="w-full h-full object-cover"
<div className="relative w-full h-[60vh] min-h-[400px] overflow-hidden group">
<div
className="absolute inset-0 bg-cover bg-center transition-transform duration-[2s] ease-out scale-105 group-hover:scale-100"
style={{ backgroundImage: `url(${post.frontmatter.featuredImage})` }}
/>
<div className="absolute inset-0 bg-gradient-to-t from-black/70 via-black/30 to-transparent" />
<div className="absolute inset-0 bg-gradient-to-t from-black/80 via-black/40 to-transparent" />
{/* Title overlay on image */}
<div className="absolute bottom-0 left-0 right-0 p-8 md:p-12">
<div className="container mx-auto max-w-4xl">
<div className="absolute inset-0 flex flex-col justify-end p-8 md:p-16 lg:p-24">
<div className="container mx-auto max-w-3xl">
{post.frontmatter.category && (
<span className="inline-block px-4 py-2 bg-primary text-white text-sm font-medium rounded-full mb-4">
<span className="inline-block px-4 py-1.5 bg-primary/90 backdrop-blur-sm text-white text-sm font-bold uppercase tracking-wider rounded-full mb-6 shadow-lg transform transition-transform hover:scale-105">
{post.frontmatter.category}
</span>
)}
<h1 className="text-3xl md:text-5xl lg:text-6xl font-bold text-white mb-4 leading-tight drop-shadow-lg">
<h1 className="text-4xl md:text-5xl lg:text-6xl font-extrabold text-white mb-6 leading-tight drop-shadow-xl">
{post.frontmatter.title}
</h1>
<time dateTime={post.frontmatter.date} className="text-white/90 text-sm md:text-base">
{new Date(post.frontmatter.date).toLocaleDateString(locale, {
year: 'numeric',
month: 'long',
day: 'numeric'
})}
</time>
<div className="flex items-center gap-4 text-white/90 text-sm md:text-base font-medium">
<time dateTime={post.frontmatter.date}>
{new Date(post.frontmatter.date).toLocaleDateString(locale, {
year: 'numeric',
month: 'long',
day: 'numeric'
})}
</time>
<span className="w-1.5 h-1.5 bg-primary rounded-full" />
<span>KLZ Cables</span>
</div>
</div>
</div>
</div>
)}
{/* Content */}
<div className="container mx-auto px-4 py-12 md:py-16 max-w-4xl">
<div className="container mx-auto px-4 py-16 md:py-24 max-w-3xl">
{/* If no featured image, show header here */}
{!post.frontmatter.featuredImage && (
<header className="mb-12">
<header className="mb-16 text-center">
{post.frontmatter.category && (
<div className="mb-4">
<span className="inline-block px-4 py-2 bg-primary text-white text-sm font-medium rounded-full">
<div className="mb-6">
<span className="inline-block px-4 py-1.5 bg-primary/10 text-primary text-sm font-bold uppercase tracking-wider rounded-full">
{post.frontmatter.category}
</span>
</div>
)}
<h1 className="text-4xl md:text-5xl lg:text-6xl font-bold text-text-primary mb-6 leading-tight">
<h1 className="text-4xl md:text-5xl lg:text-6xl font-extrabold text-text-primary mb-8 leading-tight">
{post.frontmatter.title}
</h1>
<time dateTime={post.frontmatter.date} className="text-text-secondary">
{new Date(post.frontmatter.date).toLocaleDateString(locale, {
year: 'numeric',
month: 'long',
day: 'numeric'
})}
</time>
<div className="flex items-center justify-center gap-4 text-text-secondary font-medium">
<time dateTime={post.frontmatter.date}>
{new Date(post.frontmatter.date).toLocaleDateString(locale, {
year: 'numeric',
month: 'long',
day: 'numeric'
})}
</time>
<span className="w-1.5 h-1.5 bg-primary rounded-full" />
<span>KLZ Cables</span>
</div>
</header>
)}
{/* Excerpt/Lead paragraph if available */}
{post.frontmatter.excerpt && (
<div className="mb-12 p-6 md:p-8 bg-white rounded-xl shadow-sm border-l-4 border-primary">
<p className="text-xl md:text-2xl text-text-primary leading-relaxed font-light">
<div className="mb-16">
<p className="text-xl md:text-2xl text-text-primary leading-relaxed font-medium border-l-4 border-primary pl-6 py-2">
{post.frontmatter.excerpt}
</p>
</div>
)}
{/* Main content with enhanced styling */}
<div className="bg-white rounded-xl shadow-sm p-6 md:p-10">
<div className="prose prose-lg max-w-none">
<MDXRemote source={post.content} components={components} />
</div>
<div className="prose prose-lg md:prose-xl max-w-none prose-headings:font-bold prose-headings:text-text-primary prose-p:text-text-secondary prose-p:leading-relaxed prose-a:text-primary prose-a:no-underline hover:prose-a:underline prose-img:rounded-xl prose-img:shadow-lg">
<MDXRemote source={post.content} components={components} />
</div>
{/* Power CTA */}
<PowerCTA locale={locale} />
<div className="mt-20">
<PowerCTA locale={locale} />
</div>
{/* Post Navigation */}
<PostNavigation prev={prev} next={next} locale={locale} />
{/* Back to blog link */}
<div className="mt-12 pt-8 border-t border-neutral-dark/20 text-center">
<div className="mt-16 pt-10 border-t border-neutral-200 text-center">
<Link
href={`/${locale}/blog`}
className="inline-flex items-center gap-2 text-primary hover:underline font-medium text-lg group"
className="inline-flex items-center gap-2 text-text-secondary hover:text-primary font-medium text-lg transition-colors group"
>
<svg className="w-5 h-5 transition-transform group-hover:-translate-x-1" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 19l-7-7 7-7" />

View File

@@ -9,8 +9,6 @@ interface BlogIndexProps {
}
export async function generateMetadata({ params: { locale } }: BlogIndexProps) {
const t = await getTranslations({ locale, namespace: 'blog' });
return {
title: locale === 'de' ? 'Neuigkeiten zu Kabeln und Energielösungen' : 'News on Cables and Energy Solutions',
description: locale === 'de'
@@ -21,84 +19,126 @@ export async function generateMetadata({ params: { locale } }: BlogIndexProps) {
export default async function BlogIndex({ params: { locale } }: BlogIndexProps) {
const posts = await getAllPosts(locale);
const t = await getTranslations({ locale, namespace: 'blog' });
// Sort posts by date descending
const sortedPosts = [...posts].sort((a, b) =>
new Date(b.frontmatter.date).getTime() - new Date(a.frontmatter.date).getTime()
);
// Get unique categories
const categories = Array.from(new Set(posts.map(post => post.frontmatter.category).filter(Boolean)));
const featuredPost = sortedPosts[0];
const remainingPosts = sortedPosts.slice(1);
return (
<div className="container mx-auto px-4 py-12">
<div className="text-center mb-12">
<h1 className="text-4xl md:text-5xl font-bold text-primary mb-4">
{locale === 'de' ? 'Neuigkeiten zu Kabeln und Energielösungen' : 'News on Cables and Energy Solutions'}
</h1>
<p className="text-lg text-text-secondary max-w-3xl mx-auto">
{locale === 'de'
? 'Bleiben Sie auf dem Laufenden! Lesen Sie aktuelle Themen und Insights zu Kabeltechnologie, Energielösungen und branchenspezifischen Innovationen.'
: 'Stay up to date! Read current topics and insights on cable technology, energy solutions and industry-specific innovations.'}
</p>
<div className="bg-neutral-50 min-h-screen">
{/* Hero Section */}
<div className="bg-primary text-white py-20">
<div className="container mx-auto px-4">
<div className="max-w-3xl">
<h1 className="text-5xl md:text-6xl font-extrabold mb-6 leading-tight">
{locale === 'de' ? 'KLZ Blog' : 'KLZ Blog'}
</h1>
<p className="text-xl text-white/80 leading-relaxed">
{locale === 'de'
? 'Insights, News und technisches Know-how aus der Welt der Kabelinfrastruktur und erneuerbaren Energien.'
: 'Insights, news and technical know-how from the world of cable infrastructure and renewable energies.'}
</p>
</div>
</div>
</div>
{/* Category filter - could be made interactive with client component */}
{categories.length > 0 && (
<div className="mb-8 flex flex-wrap gap-2 justify-center">
{categories.map((category) => (
<span
key={category}
className="px-4 py-2 bg-neutral-light text-text-primary rounded-full text-sm font-medium"
>
{category}
</span>
))}
</div>
)}
{/* Masonry-style grid */}
<div className="columns-1 md:columns-2 gap-8 space-y-8">
{posts.map((post) => (
<Link
key={post.slug}
href={`/${locale}/blog/${post.slug}`}
className="group block break-inside-avoid mb-8"
>
<article className="bg-white rounded-lg shadow-sm overflow-hidden border border-neutral-dark transition-all hover:shadow-md hover:-translate-y-1">
{post.frontmatter.featuredImage && (
<div className="relative overflow-hidden">
<div className="container mx-auto px-4 -mt-10 pb-20">
{/* Featured Post */}
{featuredPost && (
<Link href={`/${locale}/blog/${featuredPost.slug}`} className="group block mb-16">
<article className="bg-white rounded-2xl shadow-xl overflow-hidden flex flex-col lg:flex-row transition-all hover:shadow-2xl">
{featuredPost.frontmatter.featuredImage && (
<div className="lg:w-1/2 relative h-64 lg:h-auto overflow-hidden">
<img
src={post.frontmatter.featuredImage}
alt={post.frontmatter.title}
className="w-full h-auto object-cover transition-transform group-hover:scale-105"
src={featuredPost.frontmatter.featuredImage}
alt={featuredPost.frontmatter.title}
className="absolute inset-0 w-full h-full object-cover transition-transform duration-700 group-hover:scale-105"
/>
{post.frontmatter.category && (
<span className="absolute top-4 left-4 px-3 py-1 bg-primary text-white text-xs font-medium rounded-full">
{post.frontmatter.category}
</span>
)}
<div className="absolute inset-0 bg-gradient-to-r from-black/20 to-transparent" />
</div>
)}
<div className="p-6">
<div className="text-sm text-text-secondary mb-2">
{new Date(post.frontmatter.date).toLocaleDateString(locale, {
year: 'numeric',
month: 'long',
day: 'numeric'
})}
</div>
<h2 className="text-xl font-bold text-text-primary mb-3 group-hover:text-primary transition-colors line-clamp-2">
{post.frontmatter.title}
</h2>
{post.frontmatter.excerpt && (
<p className="text-text-secondary line-clamp-3 mb-4">
{post.frontmatter.excerpt}
</p>
<div className="lg:w-1/2 p-8 lg:p-12 flex flex-col justify-center">
{featuredPost.frontmatter.category && (
<span className="inline-block px-3 py-1 bg-primary/10 text-primary text-xs font-bold uppercase tracking-widest rounded-full mb-6">
{featuredPost.frontmatter.category}
</span>
)}
<span className="text-primary font-medium group-hover:underline inline-flex items-center">
{locale === 'de' ? 'Weiterlesen' : 'Read more'}
</span>
<h2 className="text-3xl md:text-4xl font-bold text-text-primary mb-6 group-hover:text-primary transition-colors">
{featuredPost.frontmatter.title}
</h2>
<p className="text-lg text-text-secondary mb-8 line-clamp-3">
{featuredPost.frontmatter.excerpt}
</p>
<div className="flex items-center gap-4 mt-auto">
<div className="w-10 h-10 rounded-full bg-primary/10 flex items-center justify-center text-primary font-bold">
KLZ
</div>
<div>
<div className="text-sm font-bold text-text-primary">KLZ Cables</div>
<div className="text-xs text-text-secondary">
{new Date(featuredPost.frontmatter.date).toLocaleDateString(locale, {
year: 'numeric',
month: 'long',
day: 'numeric'
})}
</div>
</div>
</div>
</div>
</article>
</Link>
))}
)}
{/* Grid for remaining posts */}
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-10">
{remainingPosts.map((post) => (
<Link key={post.slug} href={`/${locale}/blog/${post.slug}`} className="group block">
<article className="bg-white rounded-xl shadow-sm overflow-hidden border border-neutral-200 h-full flex flex-col transition-all hover:shadow-lg hover:-translate-y-1">
{post.frontmatter.featuredImage && (
<div className="relative h-56 overflow-hidden">
<img
src={post.frontmatter.featuredImage}
alt={post.frontmatter.title}
className="w-full h-full object-cover transition-transform duration-500 group-hover:scale-110"
/>
{post.frontmatter.category && (
<span className="absolute top-4 left-4 px-3 py-1 bg-white/90 backdrop-blur-sm text-primary text-[10px] font-bold uppercase tracking-wider rounded-md shadow-sm">
{post.frontmatter.category}
</span>
)}
</div>
)}
<div className="p-6 flex flex-col flex-1">
<div className="text-xs text-text-secondary mb-3">
{new Date(post.frontmatter.date).toLocaleDateString(locale, {
year: 'numeric',
month: 'long',
day: 'numeric'
})}
</div>
<h3 className="text-xl font-bold text-text-primary mb-4 group-hover:text-primary transition-colors line-clamp-2">
{post.frontmatter.title}
</h3>
<p className="text-text-secondary text-sm line-clamp-3 mb-6">
{post.frontmatter.excerpt}
</p>
<div className="mt-auto pt-4 border-t border-neutral-100 flex items-center justify-between">
<span className="text-primary text-sm font-bold group-hover:underline">
{locale === 'de' ? 'Weiterlesen' : 'Read more'}
</span>
<svg className="w-5 h-5 text-primary transition-transform group-hover:translate-x-1" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M17 8l4 4m0 0l-4 4m4-4H3" />
</svg>
</div>
</div>
</article>
</Link>
))}
</div>
</div>
</div>
);

View File

@@ -18,7 +18,7 @@ export default async function LocaleLayout({
return (
<html lang={locale}>
<body className="flex flex-col min-h-screen">
<NextIntlClientProvider messages={messages}>
<NextIntlClientProvider messages={messages} locale={locale}>
<Header />
<main className="flex-grow">
{children}

View File

@@ -1,8 +1,11 @@
import { notFound } from 'next/navigation';
import { MDXRemote } from 'next-mdx-remote/rsc';
import { getProductBySlug } from '@/lib/mdx';
import { getProductBySlug, getAllProducts } from '@/lib/mdx';
import ProductTechnicalData from '@/components/ProductTechnicalData';
import Image from 'next/image';
import ProductTabs from '@/components/ProductTabs';
import RequestQuoteForm from '@/components/RequestQuoteForm';
import RelatedProducts from '@/components/RelatedProducts';
import Link from 'next/link';
interface ProductPageProps {
params: {
@@ -13,12 +16,64 @@ interface ProductPageProps {
const components = {
ProductTechnicalData,
ProductTabs,
p: (props: any) => <div {...props} className="mb-4" />,
table: (props: any) => (
<div className="overflow-x-auto my-8">
<table {...props} className="min-w-full divide-y divide-neutral-dark border border-neutral-dark" />
</div>
),
};
export default async function ProductPage({ params }: ProductPageProps) {
const { locale, slug } = params;
const productSlug = slug[slug.length - 1]; // Use the last segment as the slug
const productSlug = slug[slug.length - 1];
// Check if it's a category page
const categories = ['low-voltage-cables', 'medium-voltage-cables', 'high-voltage-cables', 'solar-cables'];
if (categories.includes(productSlug)) {
const allProducts = await getAllProducts(locale);
const categoryTitle = productSlug.split('-').map(word => word.charAt(0).toUpperCase() + word.slice(1)).join(' ');
// Filter products for this category
// Note: MDX categories are like "Low Voltage Cables"
const filteredProducts = allProducts.filter(p =>
p.frontmatter.categories.some(cat => cat.toLowerCase().replace(/\s+/g, '-') === productSlug)
);
return (
<div className="container mx-auto px-4 py-12">
<h1 className="text-4xl font-bold text-primary mb-8">{categoryTitle}</h1>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8">
{filteredProducts.map((product) => (
<Link
key={product.slug}
href={`/${locale}/products/${productSlug}/${product.slug}`}
className="group block bg-white rounded-lg overflow-hidden shadow-sm hover:shadow-md transition-all border border-neutral-dark"
>
<div className="aspect-[4/3] relative bg-neutral-light">
{product.frontmatter.images?.[0] && (
<img
src={product.frontmatter.images[0]}
alt={product.frontmatter.title}
className="w-full h-full object-contain p-4 group-hover:scale-105 transition-transform duration-300"
/>
)}
</div>
<div className="p-6">
<h2 className="text-xl font-bold text-text-primary group-hover:text-primary transition-colors">
{product.frontmatter.title}
</h2>
<p className="text-text-secondary mt-2 line-clamp-2 text-sm">
{product.frontmatter.description}
</p>
</div>
</Link>
))}
</div>
</div>
);
}
const product = await getProductBySlug(productSlug, locale);
@@ -41,42 +96,46 @@ export default async function ProductPage({ params }: ProductPageProps) {
<div className="grid grid-cols-1 lg:grid-cols-3 gap-8">
<div className="lg:col-span-2">
<div className="prose max-w-none mb-8">
<MDXRemote source={product.content} components={components} />
{/* Main Content Area */}
<div className="bg-white p-6 rounded-lg shadow-sm border border-neutral-dark">
<MDXRemote source={product.content} components={components} />
</div>
{/* Related Products */}
<RelatedProducts
currentSlug={productSlug}
categories={product.frontmatter.categories}
locale={locale}
/>
</div>
<div className="lg:col-span-1">
{product.frontmatter.images && product.frontmatter.images.length > 0 && (
<div className="sticky top-4">
<div className="sticky top-4 space-y-6">
{/* Image Gallery */}
{product.frontmatter.images && product.frontmatter.images.length > 0 && (
<div className="bg-white p-4 rounded-lg shadow-sm border border-neutral-dark">
<div className="relative aspect-square mb-4">
{/* Note: Images from WC might be external URLs. Next/Image requires configuration for external domains. */}
{/* For now using standard img tag if domain not configured, or configure domains. */}
<div className="relative aspect-square mb-4 bg-neutral-light rounded overflow-hidden">
<img
src={product.frontmatter.images[0]}
alt={product.frontmatter.title}
className="w-full h-full object-contain"
/>
</div>
<div className="grid grid-cols-4 gap-2">
{product.frontmatter.images.slice(1, 5).map((img, idx) => (
<div key={idx} className="relative aspect-square border border-neutral-dark rounded overflow-hidden">
<img src={img} alt="" className="w-full h-full object-cover" />
</div>
))}
</div>
{product.frontmatter.images.length > 1 && (
<div className="grid grid-cols-4 gap-2">
{product.frontmatter.images.slice(1, 5).map((img, idx) => (
<div key={idx} className="relative aspect-square border border-neutral-dark rounded overflow-hidden bg-neutral-light">
<img src={img} alt="" className="w-full h-full object-cover" />
</div>
))}
</div>
)}
</div>
<div className="mt-6 bg-primary-light p-6 rounded-lg">
<h3 className="text-lg font-semibold text-primary-dark mb-2">Contact Us</h3>
<p className="text-text-secondary mb-4">Need more information about {product.frontmatter.title}?</p>
<button className="w-full bg-primary text-white py-2 px-4 rounded hover:bg-primary-dark transition-colors">
Request Quote
</button>
</div>
</div>
)}
)}
{/* Request Quote Form */}
<RequestQuoteForm productName={product.frontmatter.title} />
</div>
</div>
</div>
</div>

View File

@@ -3,7 +3,13 @@ import Image from 'next/image';
import { useTranslations } from 'next-intl';
import { Section, Container } from '@/components/ui';
export default function ProductsPage() {
interface ProductsPageProps {
params: {
locale: string;
};
}
export default function ProductsPage({ params }: ProductsPageProps) {
const t = useTranslations('Navigation');
const categories = [
@@ -12,28 +18,28 @@ export default function ProductsPage() {
desc: 'Powering everyday essentials with reliability and safety.',
img: '/uploads/2024/12/low-voltage-scaled.webp',
icon: '/uploads/2024/11/Low-Voltage.svg',
href: '/products/low-voltage-cables'
href: `/${params.locale}/products/low-voltage-cables`
},
{
title: 'Medium Voltage Cables',
desc: 'The perfect balance between power and performance for industrial and urban grids.',
img: '/uploads/2024/12/medium-voltage-scaled.webp',
icon: '/uploads/2024/11/Medium-Voltage.svg',
href: '/products/medium-voltage-cables'
href: `/${params.locale}/products/medium-voltage-cables`
},
{
title: 'High Voltage Cables',
desc: 'Delivering maximum power over long distances—without compromise.',
img: '/uploads/2025/06/na2xsfl2y-rendered.webp',
icon: '/uploads/2024/11/High-Voltage.svg',
href: '/products/high-voltage-cables'
href: `/${params.locale}/products/high-voltage-cables`
},
{
title: 'Solar Cables',
desc: 'Connecting the suns energy to your sustainable future.',
img: '/uploads/2025/04/3.webp',
icon: '/uploads/2024/11/Solar.svg',
href: '/products/solar-cables'
href: `/${params.locale}/products/solar-cables`
}
];