Some checks failed
Build & Deploy / 🔍 Prepare (push) Successful in 5s
Build & Deploy / 🧪 QA (push) Successful in 2m6s
Build & Deploy / 🏗️ Build (push) Failing after 15s
Build & Deploy / 🚀 Deploy (push) Has been skipped
Build & Deploy / 🧪 Post-Deploy Verification (push) Has been skipped
Build & Deploy / 🔔 Notify (push) Successful in 1s
153 lines
6.1 KiB
TypeScript
153 lines
6.1 KiB
TypeScript
import { notFound, redirect, permanentRedirect } from 'next/navigation';
|
|
import { Container, Badge, Heading } from '@/components/ui';
|
|
import { getTranslations, setRequestLocale } from 'next-intl/server';
|
|
import { Metadata } from 'next';
|
|
import { getPageBySlug, getAllPages } from '@/lib/pages';
|
|
import { mapSlugToFileSlug, mapFileSlugToTranslated } from '@/lib/slugs';
|
|
import PayloadRichText from '@/components/PayloadRichText';
|
|
import { SITE_URL } from '@/lib/schema';
|
|
import TrackedLink from '@/components/analytics/TrackedLink';
|
|
|
|
interface PageProps {
|
|
params: Promise<{
|
|
locale: string;
|
|
slug: string;
|
|
}>;
|
|
}
|
|
|
|
export async function generateMetadata({ params }: PageProps): Promise<Metadata> {
|
|
const { locale, slug } = await params;
|
|
const pageData = await getPageBySlug(slug, locale);
|
|
|
|
if (!pageData) return {};
|
|
|
|
const fileSlug = await mapSlugToFileSlug(pageData.slug || slug, locale);
|
|
const deSlug = await mapFileSlugToTranslated(fileSlug, 'de');
|
|
const enSlug = await mapFileSlugToTranslated(fileSlug, 'en');
|
|
|
|
// Determine correct localized slug based on current locale
|
|
const currentLocaleSlug = locale === 'de' ? deSlug : enSlug;
|
|
|
|
return {
|
|
title: pageData.frontmatter.title,
|
|
description: pageData.frontmatter.excerpt || '',
|
|
alternates: {
|
|
canonical: `${SITE_URL}/${locale}/${currentLocaleSlug}`,
|
|
languages: {
|
|
de: `${SITE_URL}/de/${deSlug}`,
|
|
en: `${SITE_URL}/en/${enSlug}`,
|
|
'x-default': `${SITE_URL}/en/${enSlug}`,
|
|
},
|
|
},
|
|
openGraph: {
|
|
title: `${pageData.frontmatter.title} | KLZ Cables`,
|
|
description: pageData.frontmatter.excerpt || '',
|
|
url: `${SITE_URL}/${locale}/${currentLocaleSlug}`,
|
|
},
|
|
twitter: {
|
|
card: 'summary_large_image',
|
|
title: `${pageData.frontmatter.title} | KLZ Cables`,
|
|
description: pageData.frontmatter.excerpt || '',
|
|
},
|
|
};
|
|
}
|
|
|
|
export default async function StandardPage({ params }: PageProps) {
|
|
const { locale, slug } = await params;
|
|
setRequestLocale(locale);
|
|
const pageData = await getPageBySlug(slug, locale);
|
|
const t = await getTranslations('StandardPage');
|
|
|
|
if (!pageData) {
|
|
notFound();
|
|
}
|
|
|
|
// Handle explicit CMS redirects (e.g. /en/terms -> /de/terms)
|
|
if (pageData.redirectUrl) {
|
|
if (pageData.redirectPermanent) {
|
|
permanentRedirect(pageData.redirectUrl);
|
|
} else {
|
|
redirect(pageData.redirectUrl);
|
|
}
|
|
}
|
|
|
|
// Redirect if accessed via a different locale's slug
|
|
const fileSlug = await mapSlugToFileSlug(pageData.slug || slug, locale);
|
|
const correctSlug = await mapFileSlugToTranslated(fileSlug, locale);
|
|
if (correctSlug && correctSlug !== slug) {
|
|
redirect(`/${locale}/${correctSlug}`);
|
|
}
|
|
|
|
// Full-bleed pages render blocks edge-to-edge without the generic article wrapper
|
|
if (pageData.frontmatter.layout === 'fullBleed') {
|
|
return (
|
|
<div className="flex flex-col min-h-screen">
|
|
<PayloadRichText data={pageData.content} className="" />
|
|
</div>
|
|
);
|
|
}
|
|
|
|
// Default article layout with hero, content, and support CTA
|
|
return (
|
|
<div className="flex flex-col min-h-screen bg-white">
|
|
{/* Hero Section */}
|
|
<section className="bg-primary-dark text-white py-20 md:py-32 relative overflow-hidden">
|
|
<div className="absolute inset-0 opacity-20">
|
|
<div className="absolute top-0 left-0 w-full h-full bg-[radial-gradient(circle_at_center,_var(--tw-gradient-stops))] from-accent via-transparent to-transparent" />
|
|
</div>
|
|
<Container className="relative z-10">
|
|
<div className="max-w-4xl">
|
|
<Badge variant="accent" className="mb-4 md:mb-6">
|
|
{t('badge')}
|
|
</Badge>
|
|
<Heading level={1} className="text-white mb-0">
|
|
{pageData.frontmatter.title}
|
|
</Heading>
|
|
</div>
|
|
</Container>
|
|
</section>
|
|
|
|
{/* Main Content Area */}
|
|
<div className="container mx-auto px-4 py-16 md:py-24">
|
|
<div className="max-w-4xl mx-auto">
|
|
{/* Excerpt/Lead paragraph if available */}
|
|
{pageData.frontmatter.excerpt && (
|
|
<div className="mb-16">
|
|
<p className="text-xl md:text-2xl text-text-primary leading-relaxed font-medium border-l-4 border-primary pl-8 py-2 italic">
|
|
{pageData.frontmatter.excerpt}
|
|
</p>
|
|
</div>
|
|
)}
|
|
|
|
{/* Main content with shared blog components */}
|
|
<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-2xl prose-img:shadow-2xl prose-blockquote:border-primary prose-blockquote:bg-primary/5 prose-blockquote:rounded-r-2xl prose-strong:text-primary">
|
|
<PayloadRichText data={pageData.content} />
|
|
</div>
|
|
|
|
{/* Support Section */}
|
|
<div className="mt-24 p-8 md:p-12 bg-primary-dark rounded-3xl text-white shadow-2xl relative overflow-hidden group animate-slight-fade-in-from-bottom">
|
|
<div className="absolute top-0 right-0 w-64 h-full bg-accent/5 -skew-x-12 translate-x-1/2 transition-transform group-hover:translate-x-1/3" />
|
|
<div className="relative z-10 max-w-2xl">
|
|
<h3 className="text-2xl md:text-3xl font-bold mb-4">{t('needHelp')}</h3>
|
|
<p className="text-lg text-white/70 mb-8">{t('supportTeamAvailable')}</p>
|
|
<TrackedLink
|
|
href={`/${locale}/${locale === 'de' ? 'kontakt' : 'contact'}`}
|
|
className="inline-flex items-center px-8 py-4 bg-accent text-primary-dark font-bold rounded-full hover:bg-white transition-all duration-300 group/link"
|
|
eventProperties={{
|
|
location: 'generic_page_support_cta',
|
|
page_slug: slug,
|
|
}}
|
|
>
|
|
{t('contactUs')}
|
|
<span className="ml-2 transition-transform group-hover/link:translate-x-1">
|
|
→
|
|
</span>
|
|
</TrackedLink>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|