feat: migration von directus zu payloadcms
Some checks failed
Build & Deploy / 🔍 Prepare (push) Successful in 1m19s
Build & Deploy / 🧪 QA (push) Failing after 3m32s
Build & Deploy / 🚀 Deploy (push) Has been skipped
Build & Deploy / 🏗️ Build (push) Failing after 7m51s
Build & Deploy / ⚡ Lighthouse (push) Has been skipped
Build & Deploy / 🧪 Smoke Test (push) Has been skipped
Build & Deploy / ♿ WCAG (push) Has been skipped
Build & Deploy / 🛡️ Quality Gates (push) Has been skipped
Build & Deploy / 🔔 Notify (push) Successful in 10s
Some checks failed
Build & Deploy / 🔍 Prepare (push) Successful in 1m19s
Build & Deploy / 🧪 QA (push) Failing after 3m32s
Build & Deploy / 🚀 Deploy (push) Has been skipped
Build & Deploy / 🏗️ Build (push) Failing after 7m51s
Build & Deploy / ⚡ Lighthouse (push) Has been skipped
Build & Deploy / 🧪 Smoke Test (push) Has been skipped
Build & Deploy / ♿ WCAG (push) Has been skipped
Build & Deploy / 🛡️ Quality Gates (push) Has been skipped
Build & Deploy / 🔔 Notify (push) Successful in 10s
This commit is contained in:
@@ -29,6 +29,10 @@ export async function generateMetadata(props: {
|
||||
|
||||
const baseUrl = process.env.CI ? 'http://klz.localhost' : SITE_URL;
|
||||
return {
|
||||
title: {
|
||||
template: '%s | KLZ Cables',
|
||||
default: 'KLZ Cables | Ihr Partner für Kabel & Leitungen',
|
||||
},
|
||||
metadataBase: new URL(baseUrl),
|
||||
manifest: '/manifest.webmanifest',
|
||||
alternates: {
|
||||
@@ -128,7 +132,11 @@ export default async function Layout(props: {
|
||||
const feedbackEnabled = process.env.NEXT_PUBLIC_FEEDBACK_ENABLED === 'true';
|
||||
|
||||
return (
|
||||
<html lang={safeLocale} className={`scroll-smooth overflow-x-hidden ${inter.variable}`}>
|
||||
<html
|
||||
lang={safeLocale}
|
||||
className={`scroll-smooth overflow-x-hidden ${inter.variable}`}
|
||||
data-scroll-behavior="smooth"
|
||||
>
|
||||
<head>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossOrigin="anonymous" />
|
||||
|
||||
@@ -11,9 +11,19 @@ export default function NotFound() {
|
||||
const { trackEvent } = useAnalytics();
|
||||
|
||||
useEffect(() => {
|
||||
const errorUrl = typeof window !== 'undefined' ? window.location.pathname : 'unknown';
|
||||
trackEvent(AnalyticsEvents.ERROR, {
|
||||
type: '404_not_found',
|
||||
path: typeof window !== 'undefined' ? window.location.pathname : 'unknown',
|
||||
path: errorUrl,
|
||||
});
|
||||
|
||||
// Explicitly send the 404 to Sentry so we have visibility into broken links
|
||||
import('@sentry/nextjs').then((Sentry) => {
|
||||
Sentry.withScope((scope) => {
|
||||
scope.setTag('status_code', '404');
|
||||
scope.setTag('path', errorUrl);
|
||||
Sentry.captureMessage(`Route Not Found: ${errorUrl}`, 'warning');
|
||||
});
|
||||
});
|
||||
}, [trackEvent]);
|
||||
|
||||
|
||||
@@ -60,17 +60,6 @@ export async function generateMetadata({ params }: ProductPageProps): Promise<Me
|
||||
'x-default': `${SITE_URL}/en/${await mapFileSlugToTranslated('products', 'en')}/${await mapFileSlugToTranslated(productSlug, 'en')}`,
|
||||
},
|
||||
},
|
||||
openGraph: {
|
||||
title: `${categoryTitle} | KLZ Cables`,
|
||||
description: categoryDesc,
|
||||
url: `${SITE_URL}/${locale}/products/${productSlug}`,
|
||||
images: getProductOGImageMetadata(fileSlug, categoryTitle, locale),
|
||||
},
|
||||
twitter: {
|
||||
card: 'summary_large_image',
|
||||
title: `${categoryTitle} | KLZ Cables`,
|
||||
description: categoryDesc,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@@ -89,7 +78,7 @@ export async function generateMetadata({ params }: ProductPageProps): Promise<Me
|
||||
},
|
||||
},
|
||||
openGraph: {
|
||||
title: `${product.frontmatter.title} | KLZ Cables`,
|
||||
title: product.frontmatter.title,
|
||||
description: product.frontmatter.description,
|
||||
type: 'website',
|
||||
url: `${SITE_URL}/${locale}/products/${slug.join('/')}`,
|
||||
@@ -97,14 +86,12 @@ export async function generateMetadata({ params }: ProductPageProps): Promise<Me
|
||||
},
|
||||
twitter: {
|
||||
card: 'summary_large_image',
|
||||
title: `${product.frontmatter.title} | KLZ Cables`,
|
||||
title: product.frontmatter.title,
|
||||
description: product.frontmatter.description,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
// ... the rest of the file layout ...
|
||||
|
||||
export default async function ProductPage({ params }: ProductPageProps) {
|
||||
const { locale, slug } = await params;
|
||||
setRequestLocale(locale);
|
||||
@@ -121,7 +108,6 @@ export default async function ProductPage({ params }: ProductPageProps) {
|
||||
const fileSlug = await mapSlugToFileSlug(productSlug, locale);
|
||||
|
||||
if (categories.includes(fileSlug)) {
|
||||
// (Skipping category page block, same as before)
|
||||
const allProducts = await getAllProducts(locale);
|
||||
const categoryKey = fileSlug
|
||||
.replace(/-cables$/, '')
|
||||
@@ -264,6 +250,37 @@ export default async function ProductPage({ params }: ProductPageProps) {
|
||||
? 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'),
|
||||
);
|
||||
const descriptionChildren = rootChildren.filter(
|
||||
(node: any) =>
|
||||
!(
|
||||
node.type === 'block' &&
|
||||
(node.fields?.blockType === 'productTabs' ||
|
||||
node.fields?.blockType === 'productTechnicalData')
|
||||
),
|
||||
);
|
||||
|
||||
const descriptionContent = {
|
||||
root: {
|
||||
...product.content.root,
|
||||
children: descriptionChildren,
|
||||
},
|
||||
};
|
||||
|
||||
const technicalContent = {
|
||||
root: {
|
||||
...product.content.root,
|
||||
children: technicalBlocks,
|
||||
},
|
||||
};
|
||||
|
||||
const sidebar = (
|
||||
<ProductSidebar
|
||||
productName={product.frontmatter.title}
|
||||
@@ -351,6 +368,7 @@ export default async function ProductPage({ params }: ProductPageProps) {
|
||||
src={product.frontmatter.images[0]}
|
||||
alt={product.frontmatter.title}
|
||||
fill
|
||||
sizes="(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 33vw"
|
||||
className="object-contain transition-transform duration-1000 hover:scale-105"
|
||||
priority
|
||||
/>
|
||||
@@ -369,6 +387,7 @@ export default async function ProductPage({ params }: ProductPageProps) {
|
||||
src={img}
|
||||
alt=""
|
||||
fill
|
||||
sizes="128px"
|
||||
className="object-contain p-4 transition-transform duration-700 group-hover:scale-110"
|
||||
/>
|
||||
</div>
|
||||
@@ -379,64 +398,74 @@ export default async function ProductPage({ params }: ProductPageProps) {
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="relative">
|
||||
<div className="w-full">
|
||||
{/* Main Content Area */}
|
||||
<div className="max-w-none prose prose-lg mt-8">
|
||||
<PayloadRichText data={product.content} />
|
||||
<div className="grid grid-cols-1 lg:grid-cols-12 gap-12 lg:gap-20">
|
||||
{/* Description Area Next to Sidebar */}
|
||||
<div className="lg:col-span-8">
|
||||
<div className="max-w-none prose prose-primary prose-lg md:prose-xl mb-16 pb-16 border-b border-neutral-dark/5">
|
||||
<PayloadRichText data={descriptionContent} />
|
||||
</div>
|
||||
|
||||
{/* Datasheet Download Section - Only for Medium Voltage for now */}
|
||||
{categoryFileSlug === 'medium-voltage-cables' && datasheetPath && (
|
||||
<div className="mt-24 pt-24 border-t-2 border-neutral-dark/5">
|
||||
<div className="mb-12">
|
||||
<h2 className="text-3xl md:text-4xl font-black text-primary tracking-tighter uppercase mb-4">
|
||||
{t('downloadDatasheet')}
|
||||
</h2>
|
||||
<div className="h-1.5 w-24 bg-accent rounded-full" />
|
||||
</div>
|
||||
<DatasheetDownload datasheetPath={datasheetPath} />
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Structured Data */}
|
||||
<JsonLd
|
||||
id={`jsonld-${product.slug}`}
|
||||
data={
|
||||
{
|
||||
'@context': 'https://schema.org',
|
||||
'@type': 'Product',
|
||||
name: product.frontmatter.title,
|
||||
description: product.frontmatter.description,
|
||||
sku: product.frontmatter.sku || product.slug.toUpperCase(),
|
||||
image: product.frontmatter.images?.[0]
|
||||
? `${SITE_URL}${product.frontmatter.images[0]}`
|
||||
: undefined,
|
||||
brand: {
|
||||
'@type': 'Brand',
|
||||
name: 'KLZ Cables',
|
||||
},
|
||||
offers: {
|
||||
'@type': 'Offer',
|
||||
availability: 'https://schema.org/InStock',
|
||||
priceCurrency: 'EUR',
|
||||
url: `${SITE_URL}/${locale}/${await mapFileSlugToTranslated('products', locale)}/${slug.join('/')}`,
|
||||
itemCondition: 'https://schema.org/NewCondition',
|
||||
},
|
||||
additionalProperty: technicalItems.map((item: any) => ({
|
||||
'@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
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Sidebar Column */}
|
||||
<div className="lg:col-span-4 lg:sticky lg:top-32 h-fit">{sidebar}</div>
|
||||
</div>
|
||||
|
||||
{/* Full-width Technical Data Below */}
|
||||
<div className="mt-16 pt-16 border-t-0">
|
||||
<div className="max-w-none prose prose-primary prose-lg md:prose-xl">
|
||||
<PayloadRichText data={technicalContent} />
|
||||
</div>
|
||||
|
||||
{/* Datasheet Download Section */}
|
||||
{categoryFileSlug === 'medium-voltage-cables' && datasheetPath && (
|
||||
<div className="mt-16 pt-16 border-t-2 border-neutral-dark/5">
|
||||
<div className="mb-8">
|
||||
<h2 className="text-3xl md:text-4xl font-black text-primary tracking-tighter uppercase mb-4">
|
||||
{t('downloadDatasheet')}
|
||||
</h2>
|
||||
<div className="h-1.5 w-24 bg-accent rounded-full" />
|
||||
</div>
|
||||
<DatasheetDownload datasheetPath={datasheetPath} />
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Structured Data (Hidden) */}
|
||||
<JsonLd
|
||||
id={`jsonld-${product.slug}`}
|
||||
data={
|
||||
{
|
||||
'@context': 'https://schema.org',
|
||||
'@type': 'Product',
|
||||
name: product.frontmatter.title,
|
||||
description: product.frontmatter.description,
|
||||
sku: product.frontmatter.sku || product.slug.toUpperCase(),
|
||||
image: product.frontmatter.images?.[0]
|
||||
? `${SITE_URL}${product.frontmatter.images[0]}`
|
||||
: undefined,
|
||||
brand: {
|
||||
'@type': 'Brand',
|
||||
name: 'KLZ Cables',
|
||||
},
|
||||
offers: {
|
||||
'@type': 'Offer',
|
||||
availability: 'https://schema.org/InStock',
|
||||
priceCurrency: 'EUR',
|
||||
url: `${SITE_URL}/${locale}/${await mapFileSlugToTranslated('products', locale)}/${slug.join('/')}`,
|
||||
itemCondition: 'https://schema.org/NewCondition',
|
||||
},
|
||||
additionalProperty: technicalItems.map((item: any) => ({
|
||||
'@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
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Related Products Section */}
|
||||
|
||||
Reference in New Issue
Block a user