Compare commits

..

2 Commits

Author SHA1 Message Date
2f7a985074 fix(ci): support branch deploys and disable turbopack
Some checks failed
Build & Deploy / 🔍 Prepare (push) Successful in 21s
Build & Deploy / 🧪 QA (push) Failing after 38s
Build & Deploy / 🚀 Deploy (push) Has been cancelled
Build & Deploy / 🏗️ Build (push) Has been cancelled
Build & Deploy / 🧪 Smoke Test (push) Has been cancelled
Build & Deploy / ⚡ Lighthouse (push) Has been cancelled
Build & Deploy / 🔔 Notify (push) Has been cancelled
2026-02-24 16:33:13 +01:00
bfccd57849 fix(products): resolve 500 mapping error by bypassing MDXRemote for complex JSX 2026-02-24 16:28:09 +01:00
4 changed files with 108 additions and 92 deletions

View File

@@ -102,6 +102,8 @@ jobs:
echo "directus_url=https://cms.$PRIMARY_HOST"
if [[ "$TARGET" == "production" ]]; then
echo "project_name=klz-cablescom"
elif [[ "$TARGET" == "branch" ]]; then
echo "project_name=$PRJ-branch-$SLUG"
else
echo "project_name=$PRJ-$TARGET"
fi
@@ -354,7 +356,13 @@ jobs:
ssh-keyscan -H alpha.mintel.me >> ~/.ssh/known_hosts 2>/dev/null
# Transfer and Restart
SITE_DIR="/home/deploy/sites/klz-cables.com"
if [[ "$TARGET" == "production" ]]; then
SITE_DIR="/home/deploy/sites/klz-cables.com"
elif [[ "$TARGET" == "testing" ]]; then
SITE_DIR="/home/deploy/sites/testing.klz-cables.com"
else
SITE_DIR="/home/deploy/sites/branch.klz-cables.com/${SLUG:-unknown}"
fi
ssh root@alpha.mintel.me "mkdir -p $SITE_DIR/directus/schema $SITE_DIR/directus/uploads $SITE_DIR/directus/extensions"
scp .env.deploy root@alpha.mintel.me:$SITE_DIR/$ENV_FILE

View File

@@ -12,7 +12,7 @@ import { mapFileSlugToTranslated, mapSlugToFileSlug } from '@/lib/slugs';
import { Metadata } from 'next';
import { getTranslations, setRequestLocale } from 'next-intl/server';
import { getProductOGImageMetadata } from '@/lib/metadata';
import { MDXRemote } from 'next-mdx-remote/rsc';
import Image from 'next/image';
import Link from 'next/link';
import { notFound } from 'next/navigation';
@@ -103,70 +103,70 @@ 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"
/>
),
h2: (props: any) => (
<div className="relative mb-16">
<h2
{...props}
className="text-3xl md:text-4xl font-black text-primary tracking-tighter uppercase 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 mb-10 tracking-tight uppercase"
/>
),
ul: (props: any) => <ul {...props} className="list-none pl-0 mb-10" />,
section: (props: any) => <div {...props} className="block" />,
li: (props: any) => (
<li className="flex items-start gap-4 group mb-4 last:mb-0">
<div className="mt-2.5 w-2 h-2 rounded-full bg-accent flex-shrink-0 group-hover:scale-125 transition-transform" />
<span
{...props}
className="text-lg md:text-xl text-text-secondary leading-relaxed font-medium"
/>
</li>
),
strong: (props: any) => <strong {...props} className="font-black text-primary" />,
table: (props: any) => (
<div className="overflow-x-auto my-20 rounded-[32px] border border-neutral-dark/10 shadow-xl bg-white p-1">
<table {...props} className="min-w-full divide-y divide-neutral-dark/10" />
</div>
),
th: (props: any) => (
<th
{...props}
className="px-8 py-6 bg-neutral-light/50 text-left text-[10px] font-black uppercase tracking-[0.25em] text-primary/60"
/>
),
td: (props: any) => (
<td
{...props}
className="px-8 py-6 text-text-secondary border-t border-neutral-dark/5 text-lg md:text-xl font-medium"
/>
),
hr: () => <hr className="my-24 border-t-2 border-neutral-dark/5" />,
blockquote: (props: any) => (
<div className="my-20 p-10 md:p-16 bg-primary-dark rounded-[40px] relative overflow-hidden group">
<div className="absolute top-0 right-0 w-64 h-64 bg-accent/10 rounded-full -translate-y-1/2 translate-x-1/2 blur-3xl group-hover:bg-accent/20 transition-colors duration-700" />
<div
className="relative z-10 italic text-2xl md:text-3xl text-white/90 leading-relaxed font-black tracking-tight"
{...props}
/>
</div>
),
};
// const components = {
// ProductTechnicalData,
// ProductTabs,
// p: (props: any) => (
// <p
// {...props}
// className="text-lg md:text-xl text-text-secondary leading-relaxed mb-8 font-medium"
// />
// ),
// h2: (props: any) => (
// <div className="relative mb-16">
// <h2
// {...props}
// className="text-3xl md:text-4xl font-black text-primary tracking-tighter uppercase 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 mb-10 tracking-tight uppercase"
// />
// ),
// ul: (props: any) => <ul {...props} className="list-none pl-0 mb-10" />,
// section: (props: any) => <div {...props} className="block" />,
// li: (props: any) => (
// <li className="flex items-start gap-4 group mb-4 last:mb-0">
// <div className="mt-2.5 w-2 h-2 rounded-full bg-accent flex-shrink-0 group-hover:scale-125 transition-transform" />
// <span
// {...props}
// className="text-lg md:text-xl text-text-secondary leading-relaxed font-medium"
// />
// </li>
// ),
// strong: (props: any) => <strong {...props} className="font-black text-primary" />,
// table: (props: any) => (
// <div className="overflow-x-auto my-20 rounded-[32px] border border-neutral-dark/10 shadow-xl bg-white p-1">
// <table {...props} className="min-w-full divide-y divide-neutral-dark/10" />
// </div>
// ),
// th: (props: any) => (
// <th
// {...props}
// className="px-8 py-6 bg-neutral-light/50 text-left text-[10px] font-black uppercase tracking-[0.25em] text-primary/60"
// />
// ),
// td: (props: any) => (
// <td
// {...props}
// className="px-8 py-6 text-text-secondary border-t border-neutral-dark/5 text-lg md:text-xl font-medium"
// />
// ),
// hr: () => <hr className="my-24 border-t-2 border-neutral-dark/5" />,
// blockquote: (props: any) => (
// <div className="my-20 p-10 md:p-16 bg-primary-dark rounded-[40px] relative overflow-hidden group">
// <div className="absolute top-0 right-0 w-64 h-64 bg-accent/10 rounded-full -translate-y-1/2 translate-x-1/2 blur-3xl group-hover:bg-accent/20 transition-colors duration-700" />
// <div
// className="relative z-10 italic text-2xl md:text-3xl text-white/90 leading-relaxed font-black tracking-tight"
// {...props}
// />
// </div>
// ),
// };
export default async function ProductPage({ params }: ProductPageProps) {
const { locale, slug } = await params;
@@ -307,20 +307,25 @@ export default async function ProductPage({ params }: ProductPageProps) {
notFound();
}
// Extract technical data for schema
// Extract technical data from MDX content (bypass MDXRemote which crashes on complex JSX-in-MDX)
const technicalDataMatch = product.content.match(
/technicalData=\{<ProductTechnicalData data=\{(.*?)\}\s*\/>\}/s,
);
let technicalItems = [];
let technicalData: any = null;
let technicalItems: any[] = [];
if (technicalDataMatch) {
try {
const data = JSON.parse(technicalDataMatch[1]);
technicalItems = data.technicalItems || [];
technicalData = JSON.parse(technicalDataMatch[1]);
technicalItems = technicalData.technicalItems || [];
} catch (e) {
console.error('Failed to parse technical data for schema', e);
console.error('Failed to parse technical data', e);
}
}
// Extract section HTML content
const sectionMatch = product.content.match(/<section>([\s\S]*?)<\/section>/);
const sectionHtml = sectionMatch ? sectionMatch[1] : '';
const datasheetPath = getDatasheetPath(productSlug, locale);
const isFallback = (product.frontmatter as any).isFallback;
const categorySlug = slug[0];
@@ -340,22 +345,6 @@ export default async function ProductPage({ params }: ProductPageProps) {
/>
);
const productComponents = {
...components,
ProductTabs: (props: any) => <ProductTabs {...props} sidebar={sidebar} />,
};
// Pre-process content to convert raw HTML tags to Markdown so they use our custom components
const processedContent = product.content
.replace(/<h2[^>]*>(.*?)<\/h2>/g, '\n## $1\n')
.replace(/<h3[^>]*>(.*?)<\/h3>/g, '\n### $1\n')
.replace(/<p[^>]*>(.*?)<\/p>/g, '\n$1\n')
.replace(/<ul[^>]*>(.*?)<\/ul>/gs, '\n$1\n')
.replace(/<li[^>]*>(.*?)<\/li>/g, '\n- $1\n')
.replace(/<strong[^>]*>(.*?)<\/strong>/g, '**$1**')
.replace(/<section[^>]*>/g, '')
.replace(/<\/section>/g, '');
return (
<div className="flex flex-col min-h-screen bg-white relative">
{/* Product Hero */}
@@ -465,9 +454,25 @@ export default async function ProductPage({ params }: ProductPageProps) {
<div className="relative">
<div className="w-full">
{/* Main Content Area */}
{/* Main Content Area - Direct rendering (bypasses MDXRemote crash) */}
<div className="max-w-none">
<MDXRemote source={processedContent} components={productComponents} />
<ProductTabs
technicalData={
technicalData ? <ProductTechnicalData data={technicalData} /> : null
}
sidebar={sidebar}
>
{sectionHtml && (
<div
className="prose prose-lg max-w-none
[&_h2]:text-3xl [&_h2]:md:text-4xl [&_h2]:font-black [&_h2]:text-primary [&_h2]:tracking-tighter [&_h2]:uppercase [&_h2]:mb-6
[&_h3]:text-2xl [&_h3]:md:text-3xl [&_h3]:font-black [&_h3]:text-primary [&_h3]:mb-10 [&_h3]:tracking-tight [&_h3]:uppercase
[&_p]:text-lg [&_p]:md:text-xl [&_p]:text-text-secondary [&_p]:leading-relaxed [&_p]:mb-8 [&_p]:font-medium
[&_strong]:font-black [&_strong]:text-primary"
dangerouslySetInnerHTML={{ __html: sectionHtml }}
/>
)}
</ProductTabs>
</div>
{/* Datasheet Download Section - Only for Medium Voltage for now */}

View File

@@ -45,7 +45,11 @@ export default function middleware(request: NextRequest) {
const hostHeader =
headers.get('x-forwarded-host') || headers.get('host') || 'testing.klz-cables.com';
const [publicHostname] = hostHeader.split(':');
urlObj.protocol = proto;
urlObj.hostname = publicHostname;
urlObj.port = ''; // Explicitly clear internal port (3000)
effectiveRequest = new NextRequest(urlObj, {
headers: request.headers,
@@ -95,8 +99,7 @@ export default function middleware(request: NextRequest) {
export const config = {
matcher: [
'/((?!api|_next/static|_next/image|_img|favicon.ico|manifest.webmanifest|.*\\.(?:svg|png|jpg|jpeg|gif|webp|pdf|txt|vcf|xml|webm|mp4|map)$).*)',
'/(de|en)/:path*',
'/((?!api|_next/static|_next/image|_img|favicon.ico|admin|manifest.webmanifest|.*\\.(?:svg|png|jpg|jpeg|gif|webp|pdf|txt|vcf|xml|webm|mp4|map)$).*)',
'/(de|en)/:path*',
],
};

View File

@@ -87,7 +87,7 @@
"scripts": {
"dev": "docker network create infra 2>/dev/null || true && echo '\\n🚀 Development Environment Starting...\\n\\n📱 App (Next.js): http://localhost:3000\\n📱 App (Traefik): http://klz.localhost\\n🗄 CMS: http://cms.klz.localhost/admin\\n🚦 Traefik: http://localhost:8080\\n\\n(Press Ctrl+C to stop)\\n' && docker-compose down --remove-orphans && docker-compose up --build klz-app klz-cms klz-db klz-gatekeeper",
"dev:infra": "docker network create infra 2>/dev/null || true && docker-compose up -d klz-cms klz-db klz-gatekeeper",
"dev:local": "next dev",
"dev:local": "next dev --no-turbo",
"build": "next build",
"start": "next start",
"lint": "eslint .",