feat: Integrate Directus CMS, add i18n with next-intl, and configure project tooling with pnpm, husky, and commitlint.**
This commit is contained in:
@@ -1,44 +1,50 @@
|
||||
import { Download } from 'lucide-react';
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import { Download } from "lucide-react";
|
||||
import fs from "fs";
|
||||
import path from "path";
|
||||
|
||||
export default function AGB() {
|
||||
const filePath = path.join(process.cwd(), 'context/agbs.md');
|
||||
const fileContent = fs.readFileSync(filePath, 'utf8');
|
||||
const filePath = path.join(process.cwd(), "context/agbs.md");
|
||||
const fileContent = fs.readFileSync(filePath, "utf8");
|
||||
|
||||
// Split by double newlines to get major blocks (headers + their first paragraphs, or subsequent paragraphs)
|
||||
const blocks = fileContent.split(/\n\s*\n/).map(b => b.trim()).filter(b => b !== '');
|
||||
|
||||
const title = blocks[0] || 'Liefer- und Zahlungsbedingungen';
|
||||
const stand = blocks[1] || 'Stand Januar 2026';
|
||||
|
||||
const blocks = fileContent
|
||||
.split(/\n\s*\n/)
|
||||
.map((b) => b.trim())
|
||||
.filter((b) => b !== "");
|
||||
|
||||
const title = blocks[0] || "Liefer- und Zahlungsbedingungen";
|
||||
const stand = blocks[1] || "Stand Januar 2026";
|
||||
|
||||
const sections: { title: string; content: string[] }[] = [];
|
||||
let currentSection: { title: string; content: string[] } | null = null;
|
||||
|
||||
// Skip title and stand
|
||||
blocks.slice(2).forEach(block => {
|
||||
const lines = block.split('\n').map(l => l.trim()).filter(l => l !== '');
|
||||
blocks.slice(2).forEach((block) => {
|
||||
const lines = block
|
||||
.split("\n")
|
||||
.map((l) => l.trim())
|
||||
.filter((l) => l !== "");
|
||||
if (lines.length === 0) return;
|
||||
|
||||
const firstLine = lines[0];
|
||||
|
||||
|
||||
if (/^\d+\./.test(firstLine)) {
|
||||
// New section
|
||||
if (currentSection) sections.push(currentSection);
|
||||
|
||||
|
||||
currentSection = { title: firstLine, content: [] };
|
||||
|
||||
|
||||
// If there are more lines in this block, they form the first paragraph(s)
|
||||
if (lines.length > 1) {
|
||||
// Join subsequent lines as they might be part of the same paragraph
|
||||
// In this MD, we'll assume lines in the same block belong together
|
||||
// unless they are clearly separate paragraphs (but we already split by double newline)
|
||||
const remainingText = lines.slice(1).join(' ');
|
||||
const remainingText = lines.slice(1).join(" ");
|
||||
if (remainingText) currentSection.content.push(remainingText);
|
||||
}
|
||||
} else if (currentSection) {
|
||||
// Continuation of current section
|
||||
const blockText = lines.join(' ');
|
||||
const blockText = lines.join(" ");
|
||||
if (blockText) currentSection.content.push(blockText);
|
||||
}
|
||||
});
|
||||
@@ -49,7 +55,7 @@ export default function AGB() {
|
||||
if (sections.length > 0) {
|
||||
const lastSection = sections[sections.length - 1];
|
||||
if (lastSection.content.includes(footer) || lastSection.title === footer) {
|
||||
lastSection.content = lastSection.content.filter(c => c !== footer);
|
||||
lastSection.content = lastSection.content.filter((c) => c !== footer);
|
||||
if (sections[sections.length - 1].title === footer) {
|
||||
sections.pop();
|
||||
}
|
||||
@@ -57,12 +63,14 @@ export default function AGB() {
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="bg-slate-50 min-h-screen pt-28 pb-20">
|
||||
<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>
|
||||
<h1 className="text-4xl font-extrabold text-primary mb-2">
|
||||
{title}
|
||||
</h1>
|
||||
<p className="text-slate-500 font-medium">{stand}</p>
|
||||
</div>
|
||||
<a
|
||||
@@ -74,11 +82,13 @@ export default function AGB() {
|
||||
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>
|
||||
<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}</p>
|
||||
66
app/[locale]/datenschutz/page.tsx
Normal file
66
app/[locale]/datenschutz/page.tsx
Normal file
@@ -0,0 +1,66 @@
|
||||
export default function Privacy() {
|
||||
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>
|
||||
);
|
||||
}
|
||||
@@ -1,9 +1,9 @@
|
||||
'use client';
|
||||
"use client";
|
||||
|
||||
import { useEffect } from 'react';
|
||||
import { motion } from 'framer-motion';
|
||||
import { RefreshCcw, Home } from 'lucide-react';
|
||||
import Link from 'next/link';
|
||||
import { useEffect } from "react";
|
||||
import { motion } from "framer-motion";
|
||||
import { RefreshCcw, Home } from "lucide-react";
|
||||
import Link from "next/link";
|
||||
|
||||
export default function Error({
|
||||
error,
|
||||
@@ -27,17 +27,19 @@ export default function Error({
|
||||
>
|
||||
500
|
||||
</motion.div>
|
||||
|
||||
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ delay: 0.2, duration: 0.5 }}
|
||||
>
|
||||
<h1 className="text-4xl font-bold text-primary mb-4">Etwas ist schiefgelaufen</h1>
|
||||
<h1 className="text-4xl font-bold text-primary mb-4">
|
||||
Etwas ist schiefgelaufen
|
||||
</h1>
|
||||
<p className="text-slate-600 text-lg mb-12 max-w-md mx-auto">
|
||||
Es gab ein technisches Problem. Wir arbeiten bereits an der Lösung.
|
||||
</p>
|
||||
|
||||
|
||||
<div className="flex flex-col sm:flex-row items-center justify-center gap-4">
|
||||
<button
|
||||
onClick={() => reset()}
|
||||
@@ -46,7 +48,10 @@ export default function Error({
|
||||
<RefreshCcw size={18} />
|
||||
Erneut versuchen
|
||||
</button>
|
||||
<Link href="/" className="btn-primary bg-slate-100 !text-primary hover:bg-slate-200 flex items-center gap-2">
|
||||
<Link
|
||||
href="/"
|
||||
className="btn-primary bg-slate-100 !text-primary hover:bg-slate-200 flex items-center gap-2"
|
||||
>
|
||||
<Home size={18} />
|
||||
Zur Startseite
|
||||
</Link>
|
||||
@@ -1,11 +1,11 @@
|
||||
'use client';
|
||||
"use client";
|
||||
|
||||
import { motion } from 'framer-motion';
|
||||
import { TechBackground } from '@/components/TechBackground';
|
||||
import { motion } from "framer-motion";
|
||||
import { TechBackground } from "@/components/TechBackground";
|
||||
|
||||
export default function Legal() {
|
||||
return (
|
||||
<div className="bg-slate-50 min-h-screen pt-28 pb-20 relative overflow-hidden">
|
||||
<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
|
||||
@@ -16,23 +16,32 @@ export default function Legal() {
|
||||
>
|
||||
<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>
|
||||
|
||||
|
||||
<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>
|
||||
<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 />
|
||||
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>
|
||||
<h2 className="text-xl font-bold text-primary mb-4">
|
||||
Vertreten durch
|
||||
</h2>
|
||||
<p>
|
||||
Michael Bodemer<br />
|
||||
Michael Bodemer
|
||||
<br />
|
||||
Klaus Mintel
|
||||
</p>
|
||||
</div>
|
||||
@@ -40,24 +49,48 @@ export default function Legal() {
|
||||
<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>
|
||||
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>
|
||||
<h2 className="text-xl font-bold text-primary mb-4">
|
||||
Registereintrag
|
||||
</h2>
|
||||
<p>
|
||||
Eintragung im Handelsregister.<br />
|
||||
Registergericht: Amtsgericht Stuttgart<br />
|
||||
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>
|
||||
<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.
|
||||
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>
|
||||
@@ -3,7 +3,8 @@ import ContactContent from "@/components/ContactContent";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "Kontakt",
|
||||
description: "Haben Sie Fragen zu einem Projekt oder benötigen Sie technische Beratung? Wir freuen uns auf Ihre Nachricht.",
|
||||
description:
|
||||
"Haben Sie Fragen zu einem Projekt oder benötigen Sie technische Beratung? Wir freuen uns auf Ihre Nachricht.",
|
||||
};
|
||||
|
||||
export default function Page() {
|
||||
123
app/[locale]/layout.tsx
Normal file
123
app/[locale]/layout.tsx
Normal file
@@ -0,0 +1,123 @@
|
||||
import Layout from "@/components/Layout";
|
||||
import type { Metadata } from "next";
|
||||
import { Inter } from "next/font/google";
|
||||
import "../globals.css";
|
||||
import { NextIntlClientProvider } from "next-intl";
|
||||
import { getMessages } from "next-intl/server";
|
||||
import { notFound } from "next/navigation";
|
||||
|
||||
const inter = Inter({
|
||||
subsets: ["latin"],
|
||||
display: "swap",
|
||||
variable: "--font-inter",
|
||||
});
|
||||
|
||||
export const metadata: Metadata = {
|
||||
metadataBase: new URL("https://www.mb-grid-solutions.com"),
|
||||
title: {
|
||||
default: "MB Grid Solutions | Energiekabelprojekte & Technische Beratung",
|
||||
template: "%s | MB Grid Solutions",
|
||||
},
|
||||
description:
|
||||
"Ihr spezialisierter Partner für herstellerneutrale technische Beratung und Projektbegleitung bei Energiekabelprojekten bis 110 kV. Expertise in Mittel- und Hochspannungsnetzen.",
|
||||
keywords: [
|
||||
"Energiekabel",
|
||||
"Hochspannung",
|
||||
"Mittelspannung",
|
||||
"Kabelprojekte",
|
||||
"Technische Beratung",
|
||||
"Engineering",
|
||||
"Energiewende",
|
||||
"110 kV",
|
||||
],
|
||||
authors: [{ name: "MB Grid Solutions & Services GmbH" }],
|
||||
creator: "MB Grid Solutions & Services GmbH",
|
||||
publisher: "MB Grid Solutions & Services GmbH",
|
||||
formatDetection: {
|
||||
email: false,
|
||||
address: false,
|
||||
telephone: false,
|
||||
},
|
||||
openGraph: {
|
||||
type: "website",
|
||||
locale: "de_DE",
|
||||
url: "https://www.mb-grid-solutions.com",
|
||||
siteName: "MB Grid Solutions",
|
||||
title: "MB Grid Solutions | Energiekabelprojekte & Technische Beratung",
|
||||
description:
|
||||
"Spezialisierter Partner für Energiekabelprojekte bis 110 kV. Herstellerneutrale technische Beratung und Projektbegleitung.",
|
||||
},
|
||||
twitter: {
|
||||
card: "summary_large_image",
|
||||
title: "MB Grid Solutions | Energiekabelprojekte & Technische Beratung",
|
||||
description: "Spezialisierter Partner für Energiekabelprojekte bis 110 kV.",
|
||||
},
|
||||
robots: {
|
||||
index: true,
|
||||
follow: true,
|
||||
googleBot: {
|
||||
index: true,
|
||||
follow: true,
|
||||
"max-video-preview": -1,
|
||||
"max-image-preview": "large",
|
||||
"max-snippet": -1,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export default async function RootLayout({
|
||||
children,
|
||||
params,
|
||||
}: {
|
||||
children: React.ReactNode;
|
||||
params: Promise<{ locale: string }>;
|
||||
}) {
|
||||
const { locale } = await params;
|
||||
|
||||
// Validate that the incoming `locale` is supported
|
||||
if (locale !== "de") {
|
||||
notFound();
|
||||
}
|
||||
|
||||
// Providing all messages to the client
|
||||
// side is the easiest way to get started
|
||||
const messages = await getMessages();
|
||||
|
||||
const jsonLd = {
|
||||
"@context": "https://schema.org",
|
||||
"@type": "Organization",
|
||||
name: "MB Grid Solutions & Services GmbH",
|
||||
url: "https://www.mb-grid-solutions.com",
|
||||
logo: "https://www.mb-grid-solutions.com/assets/logo.png",
|
||||
description:
|
||||
"Ihr spezialisierter Partner für herstellerneutrale technische Beratung und Projektbegleitung bei Energiekabelprojekten bis 110 kV.",
|
||||
address: {
|
||||
"@type": "PostalAddress",
|
||||
streetAddress: "Raiffeisenstraße 22",
|
||||
addressLocality: "Remshalden",
|
||||
postalCode: "73630",
|
||||
addressCountry: "DE",
|
||||
},
|
||||
contactPoint: {
|
||||
"@type": "ContactPoint",
|
||||
email: "info@mb-grid-solutions.com",
|
||||
contactType: "customer service",
|
||||
},
|
||||
};
|
||||
|
||||
return (
|
||||
<html lang={locale} className={`${inter.variable}`}>
|
||||
<head>
|
||||
<script
|
||||
type="application/ld+json"
|
||||
dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }}
|
||||
/>
|
||||
</head>
|
||||
<body className="antialiased">
|
||||
<NextIntlClientProvider messages={messages}>
|
||||
<Layout>{children}</Layout>
|
||||
</NextIntlClientProvider>
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
'use client';
|
||||
"use client";
|
||||
|
||||
import Link from 'next/link';
|
||||
import { motion } from 'framer-motion';
|
||||
import { Home, ArrowLeft } from 'lucide-react';
|
||||
import Link from "next/link";
|
||||
import { motion } from "framer-motion";
|
||||
import { Home, ArrowLeft } from "lucide-react";
|
||||
|
||||
export default function NotFound() {
|
||||
return (
|
||||
@@ -16,23 +16,26 @@ export default function NotFound() {
|
||||
>
|
||||
404
|
||||
</motion.div>
|
||||
|
||||
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ delay: 0.2, duration: 0.5 }}
|
||||
>
|
||||
<h1 className="text-4xl font-bold text-primary mb-4">Seite nicht gefunden</h1>
|
||||
<h1 className="text-4xl font-bold text-primary mb-4">
|
||||
Seite nicht gefunden
|
||||
</h1>
|
||||
<p className="text-slate-600 text-lg mb-12 max-w-md mx-auto">
|
||||
Die von Ihnen gesuchte Seite scheint nicht zu existieren oder wurde verschoben.
|
||||
Die von Ihnen gesuchte Seite scheint nicht zu existieren oder wurde
|
||||
verschoben.
|
||||
</p>
|
||||
|
||||
|
||||
<div className="flex flex-col sm:flex-row items-center justify-center gap-4">
|
||||
<Link href="/" className="btn-primary flex items-center gap-2">
|
||||
<Home size={18} />
|
||||
Zur Startseite
|
||||
</Link>
|
||||
<button
|
||||
<button
|
||||
onClick={() => window.history.back()}
|
||||
className="btn-primary bg-slate-100 !text-primary hover:bg-slate-200 flex items-center gap-2"
|
||||
>
|
||||
175
app/[locale]/opengraph-image.tsx
Normal file
175
app/[locale]/opengraph-image.tsx
Normal file
@@ -0,0 +1,175 @@
|
||||
import { ImageResponse } from "next/og";
|
||||
|
||||
export const runtime = "edge";
|
||||
|
||||
export const alt =
|
||||
"MB Grid Solutions | Energiekabelprojekte & Technische Beratung";
|
||||
export const size = {
|
||||
width: 1200,
|
||||
height: 630,
|
||||
};
|
||||
|
||||
export const contentType = "image/png";
|
||||
|
||||
export default async function Image() {
|
||||
return new ImageResponse(
|
||||
<div
|
||||
style={{
|
||||
background: "#ffffff",
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
position: "relative",
|
||||
fontFamily: "sans-serif",
|
||||
}}
|
||||
>
|
||||
{/* Grid Pattern Background - matching .grid-pattern in globals.css */}
|
||||
<div
|
||||
style={{
|
||||
position: "absolute",
|
||||
top: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
backgroundImage:
|
||||
"radial-gradient(circle, #e2e8f0 1.5px, transparent 1.5px)",
|
||||
backgroundSize: "40px 40px",
|
||||
zIndex: 0,
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* Content Container - matching .card-modern / .glass-panel style */}
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
backgroundColor: "rgba(255, 255, 255, 0.95)",
|
||||
padding: "60px 80px",
|
||||
borderRadius: "48px",
|
||||
border: "1px solid #e2e8f0",
|
||||
boxShadow: "0 25px 50px -12px rgba(0, 0, 0, 0.1)",
|
||||
zIndex: 1,
|
||||
position: "relative",
|
||||
}}
|
||||
>
|
||||
{/* Engineering Excellence Badge */}
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
gap: "12px",
|
||||
padding: "8px 20px",
|
||||
backgroundColor: "rgba(16, 185, 129, 0.1)",
|
||||
borderRadius: "100px",
|
||||
marginBottom: "32px",
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
width: "10px",
|
||||
height: "10px",
|
||||
backgroundColor: "#10b981",
|
||||
borderRadius: "50%",
|
||||
}}
|
||||
/>
|
||||
<div
|
||||
style={{
|
||||
fontSize: "14px",
|
||||
fontWeight: "bold",
|
||||
color: "#10b981",
|
||||
textTransform: "uppercase",
|
||||
letterSpacing: "0.1em",
|
||||
}}
|
||||
>
|
||||
Engineering Excellence
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Brand Mark */}
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
width: "100px",
|
||||
height: "100px",
|
||||
backgroundColor: "#0f172a",
|
||||
borderRadius: "24px",
|
||||
marginBottom: "32px",
|
||||
boxShadow: "0 10px 15px -3px rgba(15, 23, 42, 0.3)",
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
fontSize: "48px",
|
||||
fontWeight: "bold",
|
||||
color: "#10b981",
|
||||
}}
|
||||
>
|
||||
MB
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Title */}
|
||||
<div
|
||||
style={{
|
||||
fontSize: "72px",
|
||||
fontWeight: "900",
|
||||
color: "#0f172a",
|
||||
marginBottom: "16px",
|
||||
textAlign: "center",
|
||||
letterSpacing: "-0.02em",
|
||||
}}
|
||||
>
|
||||
MB Grid <span style={{ color: "#10b981" }}>Solutions</span>
|
||||
</div>
|
||||
|
||||
{/* Subtitle */}
|
||||
<div
|
||||
style={{
|
||||
fontSize: "32px",
|
||||
fontWeight: "500",
|
||||
color: "#64748b",
|
||||
textAlign: "center",
|
||||
maxWidth: "800px",
|
||||
lineHeight: 1.4,
|
||||
}}
|
||||
>
|
||||
Energiekabelprojekte & Technische Beratung
|
||||
<br />
|
||||
bis 110 kV
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Tech Lines - matching .tech-line style */}
|
||||
<div
|
||||
style={{
|
||||
position: "absolute",
|
||||
top: "10%",
|
||||
left: 0,
|
||||
width: "200px",
|
||||
height: "1px",
|
||||
backgroundColor: "rgba(16, 185, 129, 0.2)",
|
||||
}}
|
||||
/>
|
||||
<div
|
||||
style={{
|
||||
position: "absolute",
|
||||
bottom: "15%",
|
||||
right: 0,
|
||||
width: "300px",
|
||||
height: "1px",
|
||||
backgroundColor: "rgba(16, 185, 129, 0.2)",
|
||||
}}
|
||||
/>
|
||||
</div>,
|
||||
{
|
||||
...size,
|
||||
},
|
||||
);
|
||||
}
|
||||
@@ -3,7 +3,8 @@ import HomeContent from "@/components/HomeContent";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "MB Grid Solutions | Energiekabelprojekte & Technische Beratung",
|
||||
description: "Ihr spezialisierter Partner für herstellerneutrale technische Beratung und Projektbegleitung bei Energiekabelprojekten bis 110 kV.",
|
||||
description:
|
||||
"Ihr spezialisierter Partner für herstellerneutrale technische Beratung und Projektbegleitung bei Energiekabelprojekten bis 110 kV.",
|
||||
};
|
||||
|
||||
export default function Page() {
|
||||
175
app/[locale]/twitter-image.tsx
Normal file
175
app/[locale]/twitter-image.tsx
Normal file
@@ -0,0 +1,175 @@
|
||||
import { ImageResponse } from "next/og";
|
||||
|
||||
export const runtime = "edge";
|
||||
|
||||
export const alt =
|
||||
"MB Grid Solutions | Energiekabelprojekte & Technische Beratung";
|
||||
export const size = {
|
||||
width: 1200,
|
||||
height: 630,
|
||||
};
|
||||
|
||||
export const contentType = "image/png";
|
||||
|
||||
export default async function Image() {
|
||||
return new ImageResponse(
|
||||
<div
|
||||
style={{
|
||||
background: "#ffffff",
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
position: "relative",
|
||||
fontFamily: "sans-serif",
|
||||
}}
|
||||
>
|
||||
{/* Grid Pattern Background */}
|
||||
<div
|
||||
style={{
|
||||
position: "absolute",
|
||||
top: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
backgroundImage:
|
||||
"radial-gradient(circle, #e2e8f0 1.5px, transparent 1.5px)",
|
||||
backgroundSize: "40px 40px",
|
||||
zIndex: 0,
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* Content Container */}
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
backgroundColor: "rgba(255, 255, 255, 0.95)",
|
||||
padding: "60px 80px",
|
||||
borderRadius: "48px",
|
||||
border: "1px solid #e2e8f0",
|
||||
boxShadow: "0 25px 50px -12px rgba(0, 0, 0, 0.1)",
|
||||
zIndex: 1,
|
||||
position: "relative",
|
||||
}}
|
||||
>
|
||||
{/* Engineering Excellence Badge */}
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
gap: "12px",
|
||||
padding: "8px 20px",
|
||||
backgroundColor: "rgba(16, 185, 129, 0.1)",
|
||||
borderRadius: "100px",
|
||||
marginBottom: "32px",
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
width: "10px",
|
||||
height: "10px",
|
||||
backgroundColor: "#10b981",
|
||||
borderRadius: "50%",
|
||||
}}
|
||||
/>
|
||||
<div
|
||||
style={{
|
||||
fontSize: "14px",
|
||||
fontWeight: "bold",
|
||||
color: "#10b981",
|
||||
textTransform: "uppercase",
|
||||
letterSpacing: "0.1em",
|
||||
}}
|
||||
>
|
||||
Engineering Excellence
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Brand Mark */}
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
width: "100px",
|
||||
height: "100px",
|
||||
backgroundColor: "#0f172a",
|
||||
borderRadius: "24px",
|
||||
marginBottom: "32px",
|
||||
boxShadow: "0 10px 15px -3px rgba(15, 23, 42, 0.3)",
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
fontSize: "48px",
|
||||
fontWeight: "bold",
|
||||
color: "#10b981",
|
||||
}}
|
||||
>
|
||||
MB
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Title */}
|
||||
<div
|
||||
style={{
|
||||
fontSize: "72px",
|
||||
fontWeight: "900",
|
||||
color: "#0f172a",
|
||||
marginBottom: "16px",
|
||||
textAlign: "center",
|
||||
letterSpacing: "-0.02em",
|
||||
}}
|
||||
>
|
||||
MB Grid <span style={{ color: "#10b981" }}>Solutions</span>
|
||||
</div>
|
||||
|
||||
{/* Subtitle */}
|
||||
<div
|
||||
style={{
|
||||
fontSize: "32px",
|
||||
fontWeight: "500",
|
||||
color: "#64748b",
|
||||
textAlign: "center",
|
||||
maxWidth: "800px",
|
||||
lineHeight: 1.4,
|
||||
}}
|
||||
>
|
||||
Energiekabelprojekte & Technische Beratung
|
||||
<br />
|
||||
bis 110 kV
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Tech Lines */}
|
||||
<div
|
||||
style={{
|
||||
position: "absolute",
|
||||
top: "10%",
|
||||
left: 0,
|
||||
width: "200px",
|
||||
height: "1px",
|
||||
backgroundColor: "rgba(16, 185, 129, 0.2)",
|
||||
}}
|
||||
/>
|
||||
<div
|
||||
style={{
|
||||
position: "absolute",
|
||||
bottom: "15%",
|
||||
right: 0,
|
||||
width: "300px",
|
||||
height: "1px",
|
||||
backgroundColor: "rgba(16, 185, 129, 0.2)",
|
||||
}}
|
||||
/>
|
||||
</div>,
|
||||
{
|
||||
...size,
|
||||
},
|
||||
);
|
||||
}
|
||||
@@ -3,7 +3,8 @@ import AboutContent from "@/components/AboutContent";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "Über uns",
|
||||
description: "Erfahren Sie mehr über MB Grid Solutions, unsere Expertise und unser Manifest für technische Exzellenz.",
|
||||
description:
|
||||
"Erfahren Sie mehr über MB Grid Solutions, unsere Expertise und unser Manifest für technische Exzellenz.",
|
||||
};
|
||||
|
||||
export default function Page() {
|
||||
@@ -1,56 +1,122 @@
|
||||
import { NextResponse } from 'next/server';
|
||||
import * as nodemailer from 'nodemailer';
|
||||
import { NextResponse } from "next/server";
|
||||
import * as nodemailer from "nodemailer";
|
||||
import directus, { ensureAuthenticated } from "@/lib/directus";
|
||||
import { createItem } from "@directus/sdk";
|
||||
import { getServerAppServices } from "@/lib/services/create-services.server";
|
||||
|
||||
export async function POST(req: Request) {
|
||||
const services = getServerAppServices();
|
||||
const logger = services.logger.child({ action: "contact_submission" });
|
||||
|
||||
try {
|
||||
const { name, email, company, message, website } = await req.json();
|
||||
|
||||
// Honeypot check
|
||||
if (website) {
|
||||
console.log('Spam detected (honeypot)');
|
||||
return NextResponse.json({ message: 'Ok' });
|
||||
logger.info("Spam detected (honeypot)");
|
||||
return NextResponse.json({ message: "Ok" });
|
||||
}
|
||||
|
||||
// Validation
|
||||
if (!name || name.length < 2 || name.length > 100) {
|
||||
return NextResponse.json({ error: 'Ungültiger Name' }, { status: 400 });
|
||||
return NextResponse.json({ error: "Ungültiger Name" }, { status: 400 });
|
||||
}
|
||||
if (!email || !/^\S+@\S+\.\S+$/.test(email)) {
|
||||
return NextResponse.json({ error: 'Ungültige E-Mail' }, { status: 400 });
|
||||
return NextResponse.json({ error: "Ungültige E-Mail" }, { status: 400 });
|
||||
}
|
||||
if (!message || message.length < 20 || message.length > 4000) {
|
||||
return NextResponse.json({ error: 'Nachricht zu kurz oder zu lang' }, { status: 400 });
|
||||
if (!message || message.length < 20) {
|
||||
return NextResponse.json({ error: "message_too_short" }, { status: 400 });
|
||||
}
|
||||
|
||||
const transporter = nodemailer.createTransport({
|
||||
host: process.env.SMTP_HOST,
|
||||
port: parseInt(process.env.SMTP_PORT || '587'),
|
||||
secure: process.env.SMTP_SECURE === 'true',
|
||||
auth: {
|
||||
user: process.env.SMTP_USER,
|
||||
pass: process.env.SMTP_PASS,
|
||||
},
|
||||
});
|
||||
if (message.length > 4000) {
|
||||
return NextResponse.json({ error: "message_too_long" }, { status: 400 });
|
||||
}
|
||||
|
||||
await transporter.sendMail({
|
||||
from: process.env.SMTP_FROM,
|
||||
to: process.env.CONTACT_RECIPIENT,
|
||||
replyTo: email,
|
||||
subject: `Kontaktanfrage von ${name}`,
|
||||
text: `
|
||||
// 1. Directus save
|
||||
let directusSaved = false;
|
||||
try {
|
||||
await ensureAuthenticated();
|
||||
await directus.request(
|
||||
createItem("contact_submissions", {
|
||||
name,
|
||||
email,
|
||||
company: company || "Nicht angegeben",
|
||||
message,
|
||||
}),
|
||||
);
|
||||
logger.info("Contact submission saved to Directus");
|
||||
directusSaved = true;
|
||||
} catch (directusError) {
|
||||
logger.error("Failed to save to Directus", { error: directusError });
|
||||
services.errors.captureException(directusError, {
|
||||
phase: "directus_save",
|
||||
});
|
||||
// We still try to send the email even if Directus fails
|
||||
}
|
||||
|
||||
// 2. Email sending
|
||||
try {
|
||||
const transporter = nodemailer.createTransport({
|
||||
host: process.env.SMTP_HOST,
|
||||
port: parseInt(process.env.SMTP_PORT || "587"),
|
||||
secure: process.env.SMTP_SECURE === "true",
|
||||
auth: {
|
||||
user: process.env.SMTP_USER,
|
||||
pass: process.env.SMTP_PASS,
|
||||
},
|
||||
});
|
||||
|
||||
await transporter.sendMail({
|
||||
from: process.env.SMTP_FROM,
|
||||
to: process.env.CONTACT_RECIPIENT || "info@mb-grid-solutions.com",
|
||||
replyTo: email,
|
||||
subject: `Kontaktanfrage von ${name}`,
|
||||
text: `
|
||||
Name: ${name}
|
||||
Firma: ${company || 'Nicht angegeben'}
|
||||
Firma: ${company || "Nicht angegeben"}
|
||||
E-Mail: ${email}
|
||||
Zeitpunkt: ${new Date().toISOString()}
|
||||
|
||||
Nachricht:
|
||||
${message}
|
||||
`,
|
||||
});
|
||||
`,
|
||||
});
|
||||
|
||||
return NextResponse.json({ message: 'Ok' });
|
||||
logger.info("Email sent successfully");
|
||||
|
||||
// Notify success for important leads
|
||||
await services.notifications.notify({
|
||||
title: "📩 Neue Kontaktanfrage",
|
||||
message: `Anfrage von ${name} (${email}) erhalten.\nFirma: ${company || "Nicht angegeben"}`,
|
||||
priority: 5,
|
||||
});
|
||||
} catch (smtpError) {
|
||||
logger.error("SMTP Error", { error: smtpError });
|
||||
services.errors.captureException(smtpError, { phase: "smtp_send" });
|
||||
|
||||
// If Directus failed AND SMTP failed, then we really have a problem
|
||||
if (!directusSaved) {
|
||||
return NextResponse.json(
|
||||
{ error: "Systemfehler (Speicherung und Versand fehlgeschlagen)" },
|
||||
{ status: 500 },
|
||||
);
|
||||
}
|
||||
|
||||
// If Directus was successful, we tell the user "Ok" but we know internally it was a partial failure
|
||||
await services.notifications.notify({
|
||||
title: "🚨 SMTP Fehler (Kontaktformular)",
|
||||
message: `Anfrage von ${name} (${email}) in Directus gespeichert, aber E-Mail-Versand fehlgeschlagen: ${smtpError instanceof Error ? smtpError.message : String(smtpError)}`,
|
||||
priority: 8,
|
||||
});
|
||||
}
|
||||
|
||||
return NextResponse.json({ message: "Ok" });
|
||||
} catch (error) {
|
||||
console.error('SMTP Error:', error);
|
||||
return NextResponse.json({ error: 'Interner Serverfehler' }, { status: 500 });
|
||||
logger.error("Global API Error", { error });
|
||||
services.errors.captureException(error, { phase: "api_global" });
|
||||
return NextResponse.json(
|
||||
{ error: "Interner Serverfehler" },
|
||||
{ status: 500 },
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
export default function Privacy() {
|
||||
return (
|
||||
<div className="bg-slate-50 min-h-screen pt-28 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>
|
||||
);
|
||||
}
|
||||
@@ -10,14 +10,18 @@
|
||||
--color-text-main: #0f172a;
|
||||
--color-text-muted: #64748b;
|
||||
--color-border: #e2e8f0;
|
||||
|
||||
--font-sans: var(--font-inter), -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
||||
|
||||
|
||||
--font-sans:
|
||||
var(--font-inter), -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
|
||||
sans-serif;
|
||||
|
||||
--radius-xl: 1rem;
|
||||
--radius-2xl: 1.5rem;
|
||||
|
||||
--shadow-soft: 0 4px 6px -1px rgb(0 0 0 / 0.05), 0 2px 4px -2px rgb(0 0 0 / 0.05);
|
||||
--shadow-card: 0 10px 15px -3px rgb(0 0 0 / 0.03), 0 4px 6px -4px rgb(0 0 0 / 0.03);
|
||||
|
||||
--shadow-soft:
|
||||
0 4px 6px -1px rgb(0 0 0 / 0.05), 0 2px 4px -2px rgb(0 0 0 / 0.05);
|
||||
--shadow-card:
|
||||
0 10px 15px -3px rgb(0 0 0 / 0.03), 0 4px 6px -4px rgb(0 0 0 / 0.03);
|
||||
}
|
||||
|
||||
:root {
|
||||
@@ -43,7 +47,11 @@
|
||||
}
|
||||
|
||||
.grid-pattern {
|
||||
background-image: radial-gradient(circle, var(--color-border) 1px, transparent 1px);
|
||||
background-image: radial-gradient(
|
||||
circle,
|
||||
var(--color-border) 1px,
|
||||
transparent 1px
|
||||
);
|
||||
background-size: 40px 40px;
|
||||
}
|
||||
|
||||
@@ -56,7 +64,11 @@
|
||||
background-image:
|
||||
radial-gradient(at 0% 0%, rgba(16, 185, 129, 0.05) 0px, transparent 50%),
|
||||
radial-gradient(at 100% 0%, rgba(15, 23, 42, 0.05) 0px, transparent 50%),
|
||||
radial-gradient(at 100% 100%, rgba(16, 185, 129, 0.05) 0px, transparent 50%),
|
||||
radial-gradient(
|
||||
at 100% 100%,
|
||||
rgba(16, 185, 129, 0.05) 0px,
|
||||
transparent 50%
|
||||
),
|
||||
radial-gradient(at 0% 100%, rgba(15, 23, 42, 0.05) 0px, transparent 50%);
|
||||
}
|
||||
|
||||
@@ -78,7 +90,7 @@
|
||||
}
|
||||
|
||||
.tech-card-border::before {
|
||||
content: '';
|
||||
content: "";
|
||||
@apply absolute -inset-px bg-gradient-to-br from-accent/20 via-transparent to-accent/20 rounded-[inherit] opacity-0 transition-opacity duration-500;
|
||||
}
|
||||
|
||||
@@ -86,11 +98,20 @@
|
||||
@apply opacity-100;
|
||||
}
|
||||
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6 {
|
||||
@apply font-bold tracking-tight text-primary;
|
||||
text-wrap: balance;
|
||||
}
|
||||
|
||||
button {
|
||||
@apply cursor-pointer;
|
||||
}
|
||||
|
||||
section {
|
||||
@apply py-20 md:py-32;
|
||||
}
|
||||
@@ -102,11 +123,11 @@
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
@apply inline-flex items-center justify-center px-6 py-3 rounded-lg bg-primary text-white font-semibold transition-all hover:bg-primary-light hover:shadow-lg active:scale-[0.98] disabled:opacity-50;
|
||||
@apply inline-flex items-center justify-center px-6 py-3 rounded-lg bg-primary text-white font-semibold transition-all hover:bg-primary-light hover:shadow-lg active:scale-[0.98] cursor-pointer disabled:opacity-50 disabled:cursor-not-allowed;
|
||||
}
|
||||
|
||||
.btn-accent {
|
||||
@apply inline-flex items-center justify-center px-6 py-3 rounded-lg bg-accent text-white font-semibold transition-all hover:bg-accent-hover hover:shadow-lg active:scale-[0.98] disabled:opacity-50;
|
||||
@apply inline-flex items-center justify-center px-6 py-3 rounded-lg bg-accent text-white font-semibold transition-all hover:bg-accent-hover hover:shadow-lg active:scale-[0.98] cursor-pointer disabled:opacity-50 disabled:cursor-not-allowed;
|
||||
}
|
||||
|
||||
.glass-panel {
|
||||
|
||||
@@ -1,95 +0,0 @@
|
||||
import Layout from "@/components/Layout";
|
||||
import type { Metadata } from "next";
|
||||
import { Inter } from "next/font/google";
|
||||
import "./globals.css";
|
||||
|
||||
const inter = Inter({
|
||||
subsets: ["latin"],
|
||||
display: "swap",
|
||||
variable: "--font-inter",
|
||||
});
|
||||
|
||||
export const metadata: Metadata = {
|
||||
metadataBase: new URL("https://www.mb-grid-solutions.com"),
|
||||
title: {
|
||||
default: "MB Grid Solutions | Energiekabelprojekte & Technische Beratung",
|
||||
template: "%s | MB Grid Solutions"
|
||||
},
|
||||
description: "Ihr spezialisierter Partner für herstellerneutrale technische Beratung und Projektbegleitung bei Energiekabelprojekten bis 110 kV. Expertise in Mittel- und Hochspannungsnetzen.",
|
||||
keywords: ["Energiekabel", "Hochspannung", "Mittelspannung", "Kabelprojekte", "Technische Beratung", "Engineering", "Energiewende", "110 kV"],
|
||||
authors: [{ name: "MB Grid Solutions & Services GmbH" }],
|
||||
creator: "MB Grid Solutions & Services GmbH",
|
||||
publisher: "MB Grid Solutions & Services GmbH",
|
||||
formatDetection: {
|
||||
email: false,
|
||||
address: false,
|
||||
telephone: false,
|
||||
},
|
||||
openGraph: {
|
||||
type: "website",
|
||||
locale: "de_DE",
|
||||
url: "https://www.mb-grid-solutions.com",
|
||||
siteName: "MB Grid Solutions",
|
||||
title: "MB Grid Solutions | Energiekabelprojekte & Technische Beratung",
|
||||
description: "Spezialisierter Partner für Energiekabelprojekte bis 110 kV. Herstellerneutrale technische Beratung und Projektbegleitung.",
|
||||
},
|
||||
twitter: {
|
||||
card: "summary_large_image",
|
||||
title: "MB Grid Solutions | Energiekabelprojekte & Technische Beratung",
|
||||
description: "Spezialisierter Partner für Energiekabelprojekte bis 110 kV.",
|
||||
},
|
||||
robots: {
|
||||
index: true,
|
||||
follow: true,
|
||||
googleBot: {
|
||||
index: true,
|
||||
follow: true,
|
||||
'max-video-preview': -1,
|
||||
'max-image-preview': 'large',
|
||||
'max-snippet': -1,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export default function RootLayout({
|
||||
children,
|
||||
}: Readonly<{
|
||||
children: React.ReactNode;
|
||||
}>) {
|
||||
const jsonLd = {
|
||||
"@context": "https://schema.org",
|
||||
"@type": "Organization",
|
||||
"name": "MB Grid Solutions & Services GmbH",
|
||||
"url": "https://www.mb-grid-solutions.com",
|
||||
"logo": "https://www.mb-grid-solutions.com/assets/logo.png",
|
||||
"description": "Ihr spezialisierter Partner für herstellerneutrale technische Beratung und Projektbegleitung bei Energiekabelprojekten bis 110 kV.",
|
||||
"address": {
|
||||
"@type": "PostalAddress",
|
||||
"streetAddress": "Raiffeisenstraße 22",
|
||||
"addressLocality": "Remshalden",
|
||||
"postalCode": "73630",
|
||||
"addressCountry": "DE"
|
||||
},
|
||||
"contactPoint": {
|
||||
"@type": "ContactPoint",
|
||||
"email": "info@mb-grid-solutions.com",
|
||||
"contactType": "customer service"
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<html lang="de" className={`${inter.variable}`}>
|
||||
<head>
|
||||
<script
|
||||
type="application/ld+json"
|
||||
dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }}
|
||||
/>
|
||||
</head>
|
||||
<body className="antialiased">
|
||||
<Layout>
|
||||
{children}
|
||||
</Layout>
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
}
|
||||
@@ -1,175 +0,0 @@
|
||||
import { ImageResponse } from 'next/og';
|
||||
|
||||
export const runtime = 'edge';
|
||||
|
||||
export const alt = 'MB Grid Solutions | Energiekabelprojekte & Technische Beratung';
|
||||
export const size = {
|
||||
width: 1200,
|
||||
height: 630,
|
||||
};
|
||||
|
||||
export const contentType = 'image/png';
|
||||
|
||||
export default async function Image() {
|
||||
return new ImageResponse(
|
||||
(
|
||||
<div
|
||||
style={{
|
||||
background: '#ffffff',
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
position: 'relative',
|
||||
fontFamily: 'sans-serif',
|
||||
}}
|
||||
>
|
||||
{/* Grid Pattern Background - matching .grid-pattern in globals.css */}
|
||||
<div
|
||||
style={{
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
backgroundImage: 'radial-gradient(circle, #e2e8f0 1.5px, transparent 1.5px)',
|
||||
backgroundSize: '40px 40px',
|
||||
zIndex: 0,
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* Content Container - matching .card-modern / .glass-panel style */}
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
backgroundColor: 'rgba(255, 255, 255, 0.95)',
|
||||
padding: '60px 80px',
|
||||
borderRadius: '48px',
|
||||
border: '1px solid #e2e8f0',
|
||||
boxShadow: '0 25px 50px -12px rgba(0, 0, 0, 0.1)',
|
||||
zIndex: 1,
|
||||
position: 'relative',
|
||||
}}
|
||||
>
|
||||
{/* Engineering Excellence Badge */}
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: '12px',
|
||||
padding: '8px 20px',
|
||||
backgroundColor: 'rgba(16, 185, 129, 0.1)',
|
||||
borderRadius: '100px',
|
||||
marginBottom: '32px',
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
width: '10px',
|
||||
height: '10px',
|
||||
backgroundColor: '#10b981',
|
||||
borderRadius: '50%',
|
||||
}}
|
||||
/>
|
||||
<div
|
||||
style={{
|
||||
fontSize: '14px',
|
||||
fontWeight: 'bold',
|
||||
color: '#10b981',
|
||||
textTransform: 'uppercase',
|
||||
letterSpacing: '0.1em',
|
||||
}}
|
||||
>
|
||||
Engineering Excellence
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Brand Mark */}
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
width: '100px',
|
||||
height: '100px',
|
||||
backgroundColor: '#0f172a',
|
||||
borderRadius: '24px',
|
||||
marginBottom: '32px',
|
||||
boxShadow: '0 10px 15px -3px rgba(15, 23, 42, 0.3)',
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
fontSize: '48px',
|
||||
fontWeight: 'bold',
|
||||
color: '#10b981',
|
||||
}}
|
||||
>
|
||||
MB
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Title */}
|
||||
<div
|
||||
style={{
|
||||
fontSize: '72px',
|
||||
fontWeight: '900',
|
||||
color: '#0f172a',
|
||||
marginBottom: '16px',
|
||||
textAlign: 'center',
|
||||
letterSpacing: '-0.02em',
|
||||
}}
|
||||
>
|
||||
MB Grid <span style={{ color: '#10b981' }}>Solutions</span>
|
||||
</div>
|
||||
|
||||
{/* Subtitle */}
|
||||
<div
|
||||
style={{
|
||||
fontSize: '32px',
|
||||
fontWeight: '500',
|
||||
color: '#64748b',
|
||||
textAlign: 'center',
|
||||
maxWidth: '800px',
|
||||
lineHeight: 1.4,
|
||||
}}
|
||||
>
|
||||
Energiekabelprojekte & Technische Beratung
|
||||
<br />
|
||||
bis 110 kV
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Tech Lines - matching .tech-line style */}
|
||||
<div
|
||||
style={{
|
||||
position: 'absolute',
|
||||
top: '10%',
|
||||
left: 0,
|
||||
width: '200px',
|
||||
height: '1px',
|
||||
backgroundColor: 'rgba(16, 185, 129, 0.2)',
|
||||
}}
|
||||
/>
|
||||
<div
|
||||
style={{
|
||||
position: 'absolute',
|
||||
bottom: '15%',
|
||||
right: 0,
|
||||
width: '300px',
|
||||
height: '1px',
|
||||
backgroundColor: 'rgba(16, 185, 129, 0.2)',
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
),
|
||||
{
|
||||
...size,
|
||||
}
|
||||
);
|
||||
}
|
||||
@@ -1,175 +0,0 @@
|
||||
import { ImageResponse } from 'next/og';
|
||||
|
||||
export const runtime = 'edge';
|
||||
|
||||
export const alt = 'MB Grid Solutions | Energiekabelprojekte & Technische Beratung';
|
||||
export const size = {
|
||||
width: 1200,
|
||||
height: 630,
|
||||
};
|
||||
|
||||
export const contentType = 'image/png';
|
||||
|
||||
export default async function Image() {
|
||||
return new ImageResponse(
|
||||
(
|
||||
<div
|
||||
style={{
|
||||
background: '#ffffff',
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
position: 'relative',
|
||||
fontFamily: 'sans-serif',
|
||||
}}
|
||||
>
|
||||
{/* Grid Pattern Background */}
|
||||
<div
|
||||
style={{
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
backgroundImage: 'radial-gradient(circle, #e2e8f0 1.5px, transparent 1.5px)',
|
||||
backgroundSize: '40px 40px',
|
||||
zIndex: 0,
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* Content Container */}
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
backgroundColor: 'rgba(255, 255, 255, 0.95)',
|
||||
padding: '60px 80px',
|
||||
borderRadius: '48px',
|
||||
border: '1px solid #e2e8f0',
|
||||
boxShadow: '0 25px 50px -12px rgba(0, 0, 0, 0.1)',
|
||||
zIndex: 1,
|
||||
position: 'relative',
|
||||
}}
|
||||
>
|
||||
{/* Engineering Excellence Badge */}
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: '12px',
|
||||
padding: '8px 20px',
|
||||
backgroundColor: 'rgba(16, 185, 129, 0.1)',
|
||||
borderRadius: '100px',
|
||||
marginBottom: '32px',
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
width: '10px',
|
||||
height: '10px',
|
||||
backgroundColor: '#10b981',
|
||||
borderRadius: '50%',
|
||||
}}
|
||||
/>
|
||||
<div
|
||||
style={{
|
||||
fontSize: '14px',
|
||||
fontWeight: 'bold',
|
||||
color: '#10b981',
|
||||
textTransform: 'uppercase',
|
||||
letterSpacing: '0.1em',
|
||||
}}
|
||||
>
|
||||
Engineering Excellence
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Brand Mark */}
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
width: '100px',
|
||||
height: '100px',
|
||||
backgroundColor: '#0f172a',
|
||||
borderRadius: '24px',
|
||||
marginBottom: '32px',
|
||||
boxShadow: '0 10px 15px -3px rgba(15, 23, 42, 0.3)',
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
fontSize: '48px',
|
||||
fontWeight: 'bold',
|
||||
color: '#10b981',
|
||||
}}
|
||||
>
|
||||
MB
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Title */}
|
||||
<div
|
||||
style={{
|
||||
fontSize: '72px',
|
||||
fontWeight: '900',
|
||||
color: '#0f172a',
|
||||
marginBottom: '16px',
|
||||
textAlign: 'center',
|
||||
letterSpacing: '-0.02em',
|
||||
}}
|
||||
>
|
||||
MB Grid <span style={{ color: '#10b981' }}>Solutions</span>
|
||||
</div>
|
||||
|
||||
{/* Subtitle */}
|
||||
<div
|
||||
style={{
|
||||
fontSize: '32px',
|
||||
fontWeight: '500',
|
||||
color: '#64748b',
|
||||
textAlign: 'center',
|
||||
maxWidth: '800px',
|
||||
lineHeight: 1.4,
|
||||
}}
|
||||
>
|
||||
Energiekabelprojekte & Technische Beratung
|
||||
<br />
|
||||
bis 110 kV
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Tech Lines */}
|
||||
<div
|
||||
style={{
|
||||
position: 'absolute',
|
||||
top: '10%',
|
||||
left: 0,
|
||||
width: '200px',
|
||||
height: '1px',
|
||||
backgroundColor: 'rgba(16, 185, 129, 0.2)',
|
||||
}}
|
||||
/>
|
||||
<div
|
||||
style={{
|
||||
position: 'absolute',
|
||||
bottom: '15%',
|
||||
right: 0,
|
||||
width: '300px',
|
||||
height: '1px',
|
||||
backgroundColor: 'rgba(16, 185, 129, 0.2)',
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
),
|
||||
{
|
||||
...size,
|
||||
}
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user