feat: complete migration to static MDX, stabilize local infrastructure and fix i18n issues
Some checks failed
🚀 Build & Deploy / 🔍 Prepare (push) Successful in 4s
🚀 Build & Deploy / 🚀 Deploy (push) Has been cancelled
🚀 Build & Deploy / 🧪 Post-Deploy Verification (push) Has been cancelled
🚀 Build & Deploy / 🏗️ Build (push) Has been cancelled
🚀 Build & Deploy / 🔔 Notify (push) Has been cancelled
🚀 Build & Deploy / 🧪 QA (push) Has been cancelled
Some checks failed
🚀 Build & Deploy / 🔍 Prepare (push) Successful in 4s
🚀 Build & Deploy / 🚀 Deploy (push) Has been cancelled
🚀 Build & Deploy / 🧪 Post-Deploy Verification (push) Has been cancelled
🚀 Build & Deploy / 🏗️ Build (push) Has been cancelled
🚀 Build & Deploy / 🔔 Notify (push) Has been cancelled
🚀 Build & Deploy / 🧪 QA (push) Has been cancelled
This commit is contained in:
@@ -1,112 +1,37 @@
|
||||
import React from "react";
|
||||
import { Download } from "lucide-react";
|
||||
import fs from "fs";
|
||||
import path from "path";
|
||||
import { notFound } from "next/navigation";
|
||||
import { LegalLayout } from "@/components/LegalLayout";
|
||||
|
||||
export default function AVB() {
|
||||
const filePath = path.join(process.cwd(), "context/avbs.md");
|
||||
const fileContent = fs.readFileSync(filePath, "utf8");
|
||||
interface PageProps {
|
||||
params: Promise<{ locale: string }>;
|
||||
}
|
||||
|
||||
// Split by double newlines to get major blocks
|
||||
const rawBlocks = fileContent
|
||||
.split(/\n\s*\n/)
|
||||
.map((b) => b.trim())
|
||||
.filter((b) => b !== "");
|
||||
export default async function Page({ params }: PageProps) {
|
||||
const { locale } = await params;
|
||||
|
||||
// Extract title and stand more robustly
|
||||
const title =
|
||||
rawBlocks.find((b) => b.startsWith("# "))?.replace(/^#\s+/, "") ||
|
||||
"Allgemeine Verkaufsbedingungen";
|
||||
const stand =
|
||||
rawBlocks.find((b) => b.toLowerCase().startsWith("stand:")) ||
|
||||
"Stand: April 2026";
|
||||
let Content: React.ComponentType;
|
||||
try {
|
||||
const mdx = await import(`@/content/${locale}/avb.mdx`);
|
||||
Content = mdx.default;
|
||||
} catch (e) {
|
||||
notFound();
|
||||
}
|
||||
|
||||
const sections: { title: string; content: string[] }[] = [];
|
||||
let currentSection: { title: string; content: string[] } | null = null;
|
||||
|
||||
// Process sections, skipping title and stand blocks
|
||||
rawBlocks.forEach((block) => {
|
||||
if (block.startsWith("# ") || block.toLowerCase().startsWith("stand:"))
|
||||
return;
|
||||
|
||||
if (block.startsWith("##")) {
|
||||
// New section header: e.g. "## 1. Geltungsbereich"
|
||||
if (currentSection) sections.push(currentSection);
|
||||
currentSection = {
|
||||
title: block.replace(/^##\s+/, "").trim(),
|
||||
content: [],
|
||||
};
|
||||
} else if (currentSection) {
|
||||
// Clean up bold markers for better display
|
||||
const cleanedBlock = block.replace(/\*\*(.*?)\*\*/g, "$1");
|
||||
currentSection.content.push(cleanedBlock);
|
||||
}
|
||||
});
|
||||
if (currentSection) sections.push(currentSection);
|
||||
|
||||
// The very last block might be a footer/schlussbestimmung if it's not in a section
|
||||
// In our MD, everything is in a section, so we just use the last block of the last section as potential footer if we want,
|
||||
// but the current UI expects a separate 'footer' variable.
|
||||
const footer = "MB Grid Solutions & Services";
|
||||
const downloadButton = (
|
||||
<a
|
||||
href="/assets/avb-mb-grid-4-2026.pdf"
|
||||
download
|
||||
className="btn-primary !py-3 !px-6 flex items-center gap-2"
|
||||
>
|
||||
<Download size={18} />
|
||||
Als PDF herunterladen
|
||||
</a>
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="bg-slate-50 min-h-screen pt-40 pb-20">
|
||||
<div className="container-custom">
|
||||
<div className="max-w-4xl mx-auto bg-white p-8 md:p-12 rounded-[2.5rem] shadow-sm border border-slate-100">
|
||||
<div className="flex flex-col md:flex-row md:items-center justify-between gap-6 mb-8">
|
||||
<div>
|
||||
<h1 className="text-4xl font-extrabold text-primary mb-2">
|
||||
{title}
|
||||
</h1>
|
||||
<p className="text-slate-500 font-medium">{stand}</p>
|
||||
</div>
|
||||
<a
|
||||
href="/assets/avb-mb-grid-4-2026.pdf"
|
||||
download
|
||||
className="btn-primary !py-3 !px-6 flex items-center gap-2"
|
||||
>
|
||||
<Download size={18} />
|
||||
Als PDF herunterladen
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div className="space-y-8 text-slate-600 leading-relaxed">
|
||||
{sections.map((section, index) => (
|
||||
<div key={index}>
|
||||
<h2 className="text-2xl font-bold text-primary mb-4">
|
||||
{section.title}
|
||||
</h2>
|
||||
<div className="space-y-4">
|
||||
{section.content.map((paragraph, pIndex) => (
|
||||
<p key={pIndex}>
|
||||
{paragraph.split(/(\[.*?\]\(.*?\))/g).map((part, i) => {
|
||||
const match = part.match(/\[(.*?)\]\((.*?)\)/);
|
||||
if (match) {
|
||||
return (
|
||||
<a
|
||||
key={i}
|
||||
href={match[2]}
|
||||
className="text-primary hover:underline font-medium"
|
||||
download={match[2].endsWith(".pdf")}
|
||||
>
|
||||
{match[1]}
|
||||
</a>
|
||||
);
|
||||
}
|
||||
return part;
|
||||
})}
|
||||
</p>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
|
||||
<div className="pt-8 border-t border-slate-100 flex justify-between items-center text-slate-400 text-sm italic">
|
||||
<p>{footer}</p>
|
||||
<p>{stand}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<LegalLayout title="AVB" heroAction={downloadButton}>
|
||||
<Content />
|
||||
</LegalLayout>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,66 +1,25 @@
|
||||
export default function Privacy() {
|
||||
import React from "react";
|
||||
import { notFound } from "next/navigation";
|
||||
import { LegalLayout } from "@/components/LegalLayout";
|
||||
|
||||
interface PageProps {
|
||||
params: Promise<{ locale: string }>;
|
||||
}
|
||||
|
||||
export default async function Page({ params }: PageProps) {
|
||||
const { locale } = await params;
|
||||
|
||||
let Content: React.ComponentType;
|
||||
try {
|
||||
const mdx = await import(`@/content/${locale}/datenschutz.mdx`);
|
||||
Content = mdx.default;
|
||||
} catch (e) {
|
||||
notFound();
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="bg-slate-50 min-h-screen pt-40 pb-20">
|
||||
<div className="container-custom">
|
||||
<div className="max-w-4xl mx-auto bg-white p-8 md:p-12 rounded-[2.5rem] shadow-sm border border-slate-100">
|
||||
<h1 className="text-4xl font-extrabold text-primary mb-8">
|
||||
Datenschutzerklärung
|
||||
</h1>
|
||||
|
||||
<div className="space-y-8 text-slate-600 leading-relaxed">
|
||||
<div>
|
||||
<h2 className="text-2xl font-bold text-primary mb-4">
|
||||
1. Datenschutz auf einen Blick
|
||||
</h2>
|
||||
<p>
|
||||
Wir nehmen den Schutz Ihrer persönlichen Daten sehr ernst. Wir
|
||||
behandeln Ihre personenbezogenen Daten vertraulich und
|
||||
entsprechend der gesetzlichen Datenschutzvorschriften sowie
|
||||
dieser Datenschutzerklärung.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h2 className="text-2xl font-bold text-primary mb-4">
|
||||
2. Hosting
|
||||
</h2>
|
||||
<p>
|
||||
Unsere Website wird bei Hetzner Online GmbH gehostet. Der
|
||||
Serverstandort ist Deutschland. Wir haben einen Vertrag über
|
||||
Auftragsverarbeitung (AVV) mit Hetzner geschlossen.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h2 className="text-2xl font-bold text-primary mb-4">
|
||||
3. Kontaktformular
|
||||
</h2>
|
||||
<p>
|
||||
Wenn Sie uns per Kontaktformular Anfragen zukommen lassen,
|
||||
werden Ihre Angaben aus dem Anfrageformular inklusive der von
|
||||
Ihnen dort angegebenen Kontaktdaten zwecks Bearbeitung der
|
||||
Anfrage und für den Fall von Anschlussfragen bei uns
|
||||
gespeichert. Diese Daten geben wir nicht ohne Ihre Einwilligung
|
||||
weiter.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h2 className="text-2xl font-bold text-primary mb-4">
|
||||
4. Server-Log-Dateien
|
||||
</h2>
|
||||
<p>
|
||||
Der Provider der Seiten erhebt und speichert automatisch
|
||||
Informationen in sogenannten Server-Log-Dateien, die Ihr Browser
|
||||
automatisch an uns übermittelt. Dies sind: Browsertyp und
|
||||
Browserversion, verwendetes Betriebssystem, Referrer URL,
|
||||
Hostname des zugreifenden Rechners, Uhrzeit der Serveranfrage,
|
||||
IP-Adresse.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<LegalLayout title="Datenschutz">
|
||||
<Content />
|
||||
</LegalLayout>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,101 +1,25 @@
|
||||
"use client";
|
||||
import React from "react";
|
||||
import { notFound } from "next/navigation";
|
||||
import { LegalLayout } from "@/components/LegalLayout";
|
||||
|
||||
import { motion } from "framer-motion";
|
||||
import { TechBackground } from "@/components/TechBackground";
|
||||
interface PageProps {
|
||||
params: Promise<{ locale: string }>;
|
||||
}
|
||||
|
||||
export default async function Page({ params }: PageProps) {
|
||||
const { locale } = await params;
|
||||
|
||||
let Content: React.ComponentType;
|
||||
try {
|
||||
const mdx = await import(`@/content/${locale}/impressum.mdx`);
|
||||
Content = mdx.default;
|
||||
} catch (e) {
|
||||
notFound();
|
||||
}
|
||||
|
||||
export default function Legal() {
|
||||
return (
|
||||
<div className="bg-slate-50 min-h-screen pt-40 pb-20 relative overflow-hidden">
|
||||
<TechBackground />
|
||||
<div className="container-custom relative z-10">
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ type: "spring", stiffness: 50, damping: 20 }}
|
||||
className="max-w-4xl mx-auto bg-white p-8 md:p-12 rounded-[2.5rem] shadow-sm border border-slate-100 relative overflow-hidden group"
|
||||
>
|
||||
<div className="tech-corner top-8 left-8 border-t-2 border-l-2 opacity-20" />
|
||||
<div className="tech-corner bottom-8 right-8 border-b-2 border-r-2 opacity-20" />
|
||||
|
||||
<h1 className="text-4xl font-extrabold text-primary mb-8 relative z-10">
|
||||
Impressum
|
||||
</h1>
|
||||
|
||||
<div className="space-y-8 text-slate-600 leading-relaxed relative z-10">
|
||||
<div>
|
||||
<h2 className="text-xl font-bold text-primary mb-4">
|
||||
Angaben gemäß § 5 TMG
|
||||
</h2>
|
||||
<p>
|
||||
MB Grid Solutions & Services GmbH
|
||||
<br />
|
||||
Raiffeisenstraße 22
|
||||
<br />
|
||||
73630 Remshalden
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h2 className="text-xl font-bold text-primary mb-4">
|
||||
Vertreten durch
|
||||
</h2>
|
||||
<p>
|
||||
Michael Bodemer
|
||||
<br />
|
||||
Klaus Mintel
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h2 className="text-xl font-bold text-primary mb-4">Kontakt</h2>
|
||||
<p>
|
||||
E-Mail:{" "}
|
||||
<a
|
||||
href="mailto:info@mb-grid-solutions.com"
|
||||
className="text-accent hover:underline"
|
||||
>
|
||||
info@mb-grid-solutions.com
|
||||
</a>
|
||||
<br />
|
||||
Web:{" "}
|
||||
<a
|
||||
href="https://www.mb-grid-solutions.com"
|
||||
className="text-accent hover:underline"
|
||||
>
|
||||
www.mb-grid-solutions.com
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h2 className="text-xl font-bold text-primary mb-4">
|
||||
Registereintrag
|
||||
</h2>
|
||||
<p>
|
||||
Eintragung im Handelsregister.
|
||||
<br />
|
||||
Registergericht: Amtsgericht Stuttgart
|
||||
<br />
|
||||
Registernummer: HRB 803379
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h2 className="text-xl font-bold text-primary mb-4">
|
||||
Urheberrecht
|
||||
</h2>
|
||||
<p>
|
||||
Alle auf der Website veröffentlichten Texte, Bilder und
|
||||
sonstigen Informationen unterliegen – sofern nicht anders
|
||||
gekennzeichnet – dem Urheberrecht. Jede Vervielfältigung,
|
||||
Verbreitung, Speicherung, Übermittlung, Wiedergabe bzw.
|
||||
Weitergabe der Inhalte ohne schriftliche Genehmigung ist
|
||||
ausdrücklich untersagt.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</motion.div>
|
||||
</div>
|
||||
</div>
|
||||
<LegalLayout title="Impressum">
|
||||
<Content />
|
||||
</LegalLayout>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@ import "../globals.css";
|
||||
import { NextIntlClientProvider } from "next-intl";
|
||||
import { getMessages } from "next-intl/server";
|
||||
import { notFound } from "next/navigation";
|
||||
import { LazyMotion, domAnimation } from "framer-motion";
|
||||
import AnalyticsProvider from "@/components/analytics/AnalyticsProvider";
|
||||
import { config } from "@/lib/config";
|
||||
|
||||
@@ -143,9 +142,7 @@ export default async function RootLayout({
|
||||
<body className="antialiased">
|
||||
<NextIntlClientProvider messages={messages}>
|
||||
<AnalyticsProvider websiteId={config.analytics.umami.websiteId} />
|
||||
<LazyMotion features={domAnimation}>
|
||||
<Layout>{children}</Layout>
|
||||
</LazyMotion>
|
||||
<Layout>{children}</Layout>
|
||||
</NextIntlClientProvider>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
import React from "react";
|
||||
import { Metadata } from "next";
|
||||
import { notFound } from "next/navigation";
|
||||
import HomeContent from "@/components/HomeContent";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
@@ -7,6 +9,24 @@ export const metadata: Metadata = {
|
||||
"Ihr spezialisierter Partner für herstellerneutrale technische Beratung und Projektbegleitung bei Energiekabelprojekten bis 110 kV.",
|
||||
};
|
||||
|
||||
export default function Page() {
|
||||
return <HomeContent />;
|
||||
interface PageProps {
|
||||
params: Promise<{ locale: string }>;
|
||||
}
|
||||
|
||||
export default async function Page({ params }: PageProps) {
|
||||
const { locale } = await params;
|
||||
|
||||
// Dynamically import the MDX content based on locale
|
||||
let Content: React.ComponentType;
|
||||
try {
|
||||
const mdx = await import(`@/content/${locale}/index.mdx`);
|
||||
Content = mdx.default;
|
||||
} catch (e) {
|
||||
// Fallback if MDX is not yet created for this locale
|
||||
return <HomeContent />;
|
||||
}
|
||||
|
||||
// Render the MDX content.
|
||||
// We can use mdx-components.tsx to map MDX tags to our high-fidelity sections.
|
||||
return <Content />;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import React from "react";
|
||||
import { Metadata } from "next";
|
||||
import AboutContent from "@/components/AboutContent";
|
||||
import { notFound } from "next/navigation";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "Über uns",
|
||||
@@ -7,6 +8,21 @@ export const metadata: Metadata = {
|
||||
"Erfahren Sie mehr über MB Grid Solutions, unsere Expertise und unser Manifest für technische Exzellenz.",
|
||||
};
|
||||
|
||||
export default function Page() {
|
||||
return <AboutContent />;
|
||||
interface PageProps {
|
||||
params: Promise<{ locale: string }>;
|
||||
}
|
||||
|
||||
export default async function Page({ params }: PageProps) {
|
||||
const { locale } = await params;
|
||||
|
||||
// Dynamically import the MDX content based on locale
|
||||
let Content: React.ComponentType;
|
||||
try {
|
||||
const mdx = await import(`@/content/${locale}/about.mdx`);
|
||||
Content = mdx.default;
|
||||
} catch (e) {
|
||||
notFound();
|
||||
}
|
||||
|
||||
return <Content />;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user