diff --git a/app/[locale]/blog/[slug]/page.tsx b/app/[locale]/blog/[slug]/page.tsx index 63a57c5c..a298bde3 100644 --- a/app/[locale]/blog/[slug]/page.tsx +++ b/app/[locale]/blog/[slug]/page.tsx @@ -156,92 +156,99 @@ export default async function BlogPost({ params: { locale, slug } }: BlogPostPro } return ( -
+
{/* Featured Image Header */} {post.frontmatter.featuredImage && ( -
- {post.frontmatter.title} +
-
+
{/* Title overlay on image */} -
-
+
+
{post.frontmatter.category && ( - + {post.frontmatter.category} )} -

+

{post.frontmatter.title}

- +
+ + + KLZ Cables +
)} {/* Content */} -
+
{/* If no featured image, show header here */} {!post.frontmatter.featuredImage && ( -
+
{post.frontmatter.category && ( -
- +
+ {post.frontmatter.category}
)} -

+

{post.frontmatter.title}

- +
+ + + KLZ Cables +
)} {/* Excerpt/Lead paragraph if available */} {post.frontmatter.excerpt && ( -
-

+

+

{post.frontmatter.excerpt}

)} {/* Main content with enhanced styling */} -
-
- -
+
+
{/* Power CTA */} - +
+ +
{/* Post Navigation */} {/* Back to blog link */} -
+
diff --git a/app/[locale]/blog/page.tsx b/app/[locale]/blog/page.tsx index 7f450a9f..a386c31a 100644 --- a/app/[locale]/blog/page.tsx +++ b/app/[locale]/blog/page.tsx @@ -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 ( -
-
-

- {locale === 'de' ? 'Neuigkeiten zu Kabeln und Energielösungen' : 'News on Cables and Energy Solutions'} -

-

- {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.'} -

+
+ {/* Hero Section */} +
+
+
+

+ {locale === 'de' ? 'KLZ Blog' : 'KLZ Blog'} +

+

+ {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.'} +

+
+
- {/* Category filter - could be made interactive with client component */} - {categories.length > 0 && ( -
- {categories.map((category) => ( - - {category} - - ))} -
- )} - - {/* Masonry-style grid */} -
- {posts.map((post) => ( - -
- {post.frontmatter.featuredImage && ( -
+
+ {/* Featured Post */} + {featuredPost && ( + +
+ {featuredPost.frontmatter.featuredImage && ( +
{post.frontmatter.title} - {post.frontmatter.category && ( - - {post.frontmatter.category} - - )} +
)} -
-
- {new Date(post.frontmatter.date).toLocaleDateString(locale, { - year: 'numeric', - month: 'long', - day: 'numeric' - })} -
-

- {post.frontmatter.title} -

- {post.frontmatter.excerpt && ( -

- {post.frontmatter.excerpt} -

+
+ {featuredPost.frontmatter.category && ( + + {featuredPost.frontmatter.category} + )} - - {locale === 'de' ? 'Weiterlesen' : 'Read more'} → - +

+ {featuredPost.frontmatter.title} +

+

+ {featuredPost.frontmatter.excerpt} +

+
+
+ KLZ +
+
+
KLZ Cables
+
+ {new Date(featuredPost.frontmatter.date).toLocaleDateString(locale, { + year: 'numeric', + month: 'long', + day: 'numeric' + })} +
+
+
- ))} + )} + + {/* Grid for remaining posts */} +
+ {remainingPosts.map((post) => ( + +
+ {post.frontmatter.featuredImage && ( +
+ {post.frontmatter.title} + {post.frontmatter.category && ( + + {post.frontmatter.category} + + )} +
+ )} +
+
+ {new Date(post.frontmatter.date).toLocaleDateString(locale, { + year: 'numeric', + month: 'long', + day: 'numeric' + })} +
+

+ {post.frontmatter.title} +

+

+ {post.frontmatter.excerpt} +

+
+ + {locale === 'de' ? 'Weiterlesen' : 'Read more'} + + + + +
+
+
+ + ))} +
); diff --git a/app/[locale]/layout.tsx b/app/[locale]/layout.tsx index 4cfdffe6..2d178bce 100644 --- a/app/[locale]/layout.tsx +++ b/app/[locale]/layout.tsx @@ -18,7 +18,7 @@ export default async function LocaleLayout({ return ( - +
{children} diff --git a/app/[locale]/products/[...slug]/page.tsx b/app/[locale]/products/[...slug]/page.tsx index 6b4c3eda..4481b91e 100644 --- a/app/[locale]/products/[...slug]/page.tsx +++ b/app/[locale]/products/[...slug]/page.tsx @@ -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) =>
, + table: (props: any) => ( +
+ + + ), }; 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 ( +
+

{categoryTitle}

+
+ {filteredProducts.map((product) => ( + +
+ {product.frontmatter.images?.[0] && ( + {product.frontmatter.title} + )} +
+
+

+ {product.frontmatter.title} +

+

+ {product.frontmatter.description} +

+
+ + ))} +
+
+ ); + } const product = await getProductBySlug(productSlug, locale); @@ -41,42 +96,46 @@ export default async function ProductPage({ params }: ProductPageProps) {
-
- + {/* Main Content Area */} +
+
+ + {/* Related Products */} +
- {product.frontmatter.images && product.frontmatter.images.length > 0 && ( -
+
+ {/* Image Gallery */} + {product.frontmatter.images && product.frontmatter.images.length > 0 && (
-
- {/* 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. */} +
{product.frontmatter.title}
-
- {product.frontmatter.images.slice(1, 5).map((img, idx) => ( -
- -
- ))} -
+ {product.frontmatter.images.length > 1 && ( +
+ {product.frontmatter.images.slice(1, 5).map((img, idx) => ( +
+ +
+ ))} +
+ )}
- -
-

Contact Us

-

Need more information about {product.frontmatter.title}?

- -
-
- )} + )} + + {/* Request Quote Form */} + +
diff --git a/app/[locale]/products/page.tsx b/app/[locale]/products/page.tsx index 288669ed..81c2fa57 100644 --- a/app/[locale]/products/page.tsx +++ b/app/[locale]/products/page.tsx @@ -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 sun’s 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` } ]; diff --git a/components/ProductTabs.tsx b/components/ProductTabs.tsx new file mode 100644 index 00000000..595a84ac --- /dev/null +++ b/components/ProductTabs.tsx @@ -0,0 +1,25 @@ +import React from 'react'; + +interface ProductTabsProps { + children: React.ReactNode; + technicalData?: React.ReactNode; +} + +export default function ProductTabs({ children, technicalData }: ProductTabsProps) { + return ( +
+
+ {children} +
+ + {technicalData && ( +
+

Technical Specifications

+
+ {technicalData} +
+
+ )} +
+ ); +} diff --git a/components/ProductTechnicalData.tsx b/components/ProductTechnicalData.tsx index 23c5a38f..97cefacd 100644 --- a/components/ProductTechnicalData.tsx +++ b/components/ProductTechnicalData.tsx @@ -21,7 +21,8 @@ interface ProductTechnicalDataProps { } export default function ProductTechnicalData({ data }: ProductTechnicalDataProps) { - const { technicalItems, voltageTables } = data; + if (!data) return null; + const { technicalItems = [], voltageTables = [] } = data; return (
diff --git a/components/RelatedProducts.tsx b/components/RelatedProducts.tsx new file mode 100644 index 00000000..7888071d --- /dev/null +++ b/components/RelatedProducts.tsx @@ -0,0 +1,63 @@ +import Link from 'next/link'; +import { getAllProducts } from '@/lib/mdx'; + +interface RelatedProductsProps { + currentSlug: string; + categories: string[]; + locale: string; +} + +export default async function RelatedProducts({ currentSlug, categories, locale }: RelatedProductsProps) { + const allProducts = await getAllProducts(locale); + + // Filter products: same category, not current product + const related = allProducts + .filter(p => + p.slug !== currentSlug && + p.frontmatter.categories.some(cat => categories.includes(cat)) + ) + .slice(0, 4); // Limit to 4 + + if (related.length === 0) return null; + + return ( +
+

Related Products

+
+ {related.map((product) => ( + +
+ {product.frontmatter.images?.[0] ? ( + {product.frontmatter.title} + ) : ( +
+ No Image +
+ )} +
+
+

+ {product.frontmatter.title} +

+
+ {product.frontmatter.categories.slice(0, 1).map((cat, idx) => ( + + {cat} + + ))} +
+
+ + ))} +
+
+ ); +} diff --git a/components/RequestQuoteForm.tsx b/components/RequestQuoteForm.tsx new file mode 100644 index 00000000..d12d032a --- /dev/null +++ b/components/RequestQuoteForm.tsx @@ -0,0 +1,101 @@ +'use client'; + +import React, { useState } from 'react'; + +interface RequestQuoteFormProps { + productName: string; +} + +export default function RequestQuoteForm({ productName }: RequestQuoteFormProps) { + const [email, setEmail] = useState(''); + const [request, setRequest] = useState(''); + const [status, setStatus] = useState<'idle' | 'submitting' | 'success' | 'error'>('idle'); + + const handleSubmit = async (e: React.FormEvent) => { + e.preventDefault(); + setStatus('submitting'); + + // Simulate API call + await new Promise((resolve) => setTimeout(resolve, 1000)); + + // Here you would typically send the data to your backend + console.log('Form submitted:', { productName, email, request }); + + setStatus('success'); + setEmail(''); + setRequest(''); + }; + + if (status === 'success') { + return ( +
+

Request Sent!

+

Thank you for your interest in {productName}. We will get back to you shortly.

+ +
+ ); + } + + return ( +
+

Request Cable

+
+
+ + +
+ +
+ + setEmail(e.target.value)} + className="w-full px-3 py-2 bg-white border border-neutral-dark rounded focus:outline-none focus:ring-2 focus:ring-primary focus:border-transparent" + placeholder="your@email.com" + /> +
+ +
+ +