init
This commit is contained in:
34
.gitignore
vendored
Normal file
34
.gitignore
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
lerna-debug.log*
|
||||
|
||||
node_modules
|
||||
dist
|
||||
dist-ssr
|
||||
*.local
|
||||
|
||||
# Editor directories and files
|
||||
.vscode/*
|
||||
!.vscode/extensions.json
|
||||
.idea
|
||||
.DS_Store
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
|
||||
# Environment variables
|
||||
.env
|
||||
.env.local
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
|
||||
# Docker
|
||||
.dockerignore
|
||||
29
Dockerfile
Normal file
29
Dockerfile
Normal file
@@ -0,0 +1,29 @@
|
||||
# Build Stage
|
||||
FROM node:20-slim AS build
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY package*.json ./
|
||||
RUN npm ci
|
||||
|
||||
COPY . .
|
||||
|
||||
# Build Frontend
|
||||
RUN npm run build:frontend
|
||||
|
||||
# Build Backend
|
||||
RUN npm run build:backend
|
||||
|
||||
# Runtime Stage
|
||||
FROM node:20-slim
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY --from=build /app/package*.json ./
|
||||
RUN npm ci --omit=dev
|
||||
|
||||
COPY --from=build /app/dist ./dist
|
||||
|
||||
EXPOSE 3000
|
||||
|
||||
CMD ["node", "dist/backend/server.js"]
|
||||
38
context/about.md
Normal file
38
context/about.md
Normal file
@@ -0,0 +1,38 @@
|
||||
|
||||
|
||||
Die Köpfe, die Energie zum Laufen bringen
|
||||
Wir verbinden Energie, Know-how und Innovation, um eine nachhaltigere Zukunft zu gestalten.
|
||||
Michael Bodemer
|
||||
„Herausforderungen sind da, um gelöst zu werden – nicht, um über ihre Komplexität zu diskutieren.“
|
||||
|
||||
Michael Bodemer ist unser Mann, wenn es kompliziert wird – und das ist bei Kabelnetzen oft der Fall. Mit seinem scharfen Blick und einem Händchen für praktikable Lösungen ist er eine unserer zentralen Säulen. Michael denkt nicht nur an Details, er treibt Projekte voran – sei es in der Planung, im Kundengespräch oder bei der Auswahl der besten Kabel für jedes Vorhaben.
|
||||
Michael's LinkedIn
|
||||
Verbindungen, die Geschichte schreiben
|
||||
|
||||
Bei KLZ vereinen wir Tradition und Innovation zu zuverlässigen Energielösungen. Unsere Wurzeln reichen tief in die Geschichte der Kabeltechnologie zurück – mit jeder Menge praktischer Erfahrung und einem Blick für zukunftsweisende Entwicklungen.
|
||||
|
||||
In jedem Projekt steckt nicht nur technisches Know-how, sondern auch das Bewusstsein für das Handwerk, das die Welt seit Generationen verbindet. Historische Illustrationen aus den frühen Tagen der Energiebranche erinnern uns daran, wie weit wir gekommen sind – und dass echte Exzellenz immer mit Sorgfalt beginnt.
|
||||
Klaus Mintel
|
||||
„Manchmal braucht es nur einen klaren Kopf und das richtige Kabel, um die Welt ein Stück besser zu machen.“
|
||||
|
||||
Klaus ist der Fels in der Brandung – selbst wenn das Kabelchaos überhandnimmt. Mit jahrzehntelanger Erfahrung und einem stabilen Netzwerk sorgt er dafür, dass alles glatt läuft. Er denkt nicht nur in Lösungen, sondern bringt auch Humor und den nötigen Weitblick mit, um selbst komplexe Themen locker auf den Punkt zu bringen.
|
||||
Klaus' LinkedIn
|
||||
Unser Manifest
|
||||
1
|
||||
Kompetenz
|
||||
Jahrzehntelange Erfahrung und europaweites Know-how kombiniert mit Engagement und neuen Ideen. Produktionspartner bis zu 525 kV und modernsten Anlagen, Testlaboren und investitionsbereit für die Zukunft.
|
||||
2
|
||||
Verfügbarkeit
|
||||
Immer für Sie da – ohne Warten, ohne Verzögerung, einfach schnelle und verlässliche Unterstützung. Vielleicht liegt es daran, dass wir lieben, was wir tun.
|
||||
3
|
||||
Lösungen
|
||||
Für Lösungen braucht es viele Fragen. Diese stellen wir. Ihnen, dem Hersteller und uns selbst. Wer nicht hinterfragt, zahlt später dafür. Das gilt es zu verhindern.
|
||||
4
|
||||
Logistik
|
||||
Überwachung der Fertigung, regelmäßiger Austausch, Fracht-Tracking, Verzollung, Umladung, Zeittunnel der Anlieferung beachten, Rechnung, Lieferscheine – unser Alltag. Wir haben das Team dazu.
|
||||
5
|
||||
Offen für Neues
|
||||
Wir hören zu. Von der Anfrage, über das Angebot bis hin zur Auslieferung. Was besser gemacht werden kann, muss diskutiert werden. Wer seine Prozesse nicht anpasst, fährt irgendwann nicht mehr auf der Autobahn. Sondern in die Sackgasse.
|
||||
6
|
||||
Zuverlässigkeit
|
||||
Wir halten, was wir versprechen – jedes Mal und ohne Ausnahme.
|
||||
14
index.html
Normal file
14
index.html
Normal file
@@ -0,0 +1,14 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/png" href="/assets/logo.png" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>MB Grid Solutions | Energiekabelprojekte bis 110 kV</title>
|
||||
<meta name="description" content="Spezialisierter Partner für Energiekabelprojekte bis 110 kV. Herstellerneutrale technische Beratung und Projektbegleitung." />
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script type="module" src="/src/main.tsx"></script>
|
||||
</body>
|
||||
</html>
|
||||
4617
package-lock.json
generated
Normal file
4617
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
50
package.json
Normal file
50
package.json
Normal file
@@ -0,0 +1,50 @@
|
||||
{
|
||||
"name": "mb-grid-solutions.com",
|
||||
"version": "1.0.0",
|
||||
"type": "module",
|
||||
"main": "dist/backend/server.js",
|
||||
"directories": {
|
||||
"doc": "docs"
|
||||
},
|
||||
"scripts": {
|
||||
"dev:frontend": "vite",
|
||||
"dev:backend": "nodemon --exec ts-node-esm server.ts",
|
||||
"build:frontend": "vite build",
|
||||
"build:backend": "tsc -p tsconfig.server.json",
|
||||
"build": "npm run build:frontend && npm run build:backend",
|
||||
"start": "node dist/backend/server.js",
|
||||
"lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"description": "",
|
||||
"dependencies": {
|
||||
"@vitejs/plugin-react-swc": "^3.11.0",
|
||||
"cors": "^2.8.5",
|
||||
"dotenv": "^17.2.3",
|
||||
"express": "^5.2.1",
|
||||
"express-rate-limit": "^8.2.1",
|
||||
"framer-motion": "^12.26.2",
|
||||
"helmet": "^8.1.0",
|
||||
"lucide-react": "^0.562.0",
|
||||
"nodemailer": "^7.0.12",
|
||||
"react": "^19.2.3",
|
||||
"react-dom": "^19.2.3",
|
||||
"react-router-dom": "^7.12.0",
|
||||
"vite": "^6.4.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/cors": "^2.8.19",
|
||||
"@types/express": "^5.0.6",
|
||||
"@types/node": "^25.0.8",
|
||||
"@types/nodemailer": "^7.0.5",
|
||||
"@types/react": "^19.2.8",
|
||||
"@types/react-dom": "^19.2.3",
|
||||
"nodemon": "^3.1.11",
|
||||
"ts-node": "^10.9.2",
|
||||
"typescript": "^5.9.3",
|
||||
"vite-plugin-checker": "^0.12.0"
|
||||
}
|
||||
}
|
||||
111
server.ts
Normal file
111
server.ts
Normal file
@@ -0,0 +1,111 @@
|
||||
import express from 'express';
|
||||
import path from 'path';
|
||||
import { fileURLToPath } from 'url';
|
||||
import nodemailer from 'nodemailer';
|
||||
import cors from 'cors';
|
||||
import dotenv from 'dotenv';
|
||||
import helmet from 'helmet';
|
||||
import rateLimit from 'express-rate-limit';
|
||||
|
||||
dotenv.config();
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = path.dirname(__filename);
|
||||
|
||||
const app = express();
|
||||
const PORT = process.env.PORT || 3000;
|
||||
|
||||
// Security
|
||||
app.use(helmet({
|
||||
contentSecurityPolicy: {
|
||||
directives: {
|
||||
...helmet.contentSecurityPolicy.getDefaultDirectives(),
|
||||
"img-src": ["'self'", "data:"],
|
||||
},
|
||||
},
|
||||
}));
|
||||
app.use(cors());
|
||||
app.use(express.json());
|
||||
|
||||
// Rate limiting for API
|
||||
const apiLimiter = rateLimit({
|
||||
windowMs: 10 * 60 * 1000, // 10 minutes
|
||||
max: 5, // limit each IP to 5 requests per windowMs
|
||||
message: 'Zu viele Anfragen von dieser IP, bitte versuchen Sie es später erneut.',
|
||||
standardHeaders: true,
|
||||
legacyHeaders: false,
|
||||
});
|
||||
|
||||
// API Endpoint
|
||||
app.post('/api/contact', apiLimiter, async (req, res) => {
|
||||
const { name, email, company, message, website } = req.body;
|
||||
|
||||
// Honeypot check
|
||||
if (website) {
|
||||
console.log('Spam detected (honeypot)');
|
||||
return res.status(200).json({ message: 'Ok' }); // Generic success
|
||||
}
|
||||
|
||||
// Validation
|
||||
if (!name || name.length < 2 || name.length > 100) {
|
||||
return res.status(400).json({ error: 'Ungültiger Name' });
|
||||
}
|
||||
if (!email || !/^\S+@\S+\.\S+$/.test(email)) {
|
||||
return res.status(400).json({ error: 'Ungültige E-Mail' });
|
||||
}
|
||||
if (!message || message.length < 20 || message.length > 4000) {
|
||||
return res.status(400).json({ error: 'Nachricht zu kurz oder zu lang' });
|
||||
}
|
||||
|
||||
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,
|
||||
replyTo: email,
|
||||
subject: `Kontaktanfrage von ${name}`,
|
||||
text: `
|
||||
Name: ${name}
|
||||
Firma: ${company || 'Nicht angegeben'}
|
||||
E-Mail: ${email}
|
||||
Zeitpunkt: ${new Date().toISOString()}
|
||||
|
||||
Nachricht:
|
||||
${message}
|
||||
`,
|
||||
});
|
||||
|
||||
res.status(200).json({ message: 'Ok' });
|
||||
} catch (error) {
|
||||
console.error('SMTP Error:', error);
|
||||
res.status(500).json({ error: 'Interner Serverfehler' });
|
||||
}
|
||||
});
|
||||
|
||||
// Health check
|
||||
app.get('/healthz', (req, res) => {
|
||||
res.status(200).send('OK');
|
||||
});
|
||||
|
||||
// Serve static files from the React app
|
||||
const distPath = path.join(__dirname, 'dist/frontend');
|
||||
app.use(express.static(distPath));
|
||||
|
||||
// The "catchall" handler: for any request that doesn't
|
||||
// match one above, send back React's index.html file.
|
||||
app.get('*', (req, res) => {
|
||||
res.sendFile(path.join(distPath, 'index.html'));
|
||||
});
|
||||
|
||||
app.listen(PORT, () => {
|
||||
console.log(`Server is running on port ${PORT}`);
|
||||
});
|
||||
27
src/App.tsx
Normal file
27
src/App.tsx
Normal file
@@ -0,0 +1,27 @@
|
||||
import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';
|
||||
import Layout from './components/Layout';
|
||||
import Home from './pages/Home';
|
||||
import About from './pages/About';
|
||||
import Contact from './pages/Contact';
|
||||
import Legal from './pages/Legal';
|
||||
import Privacy from './pages/Privacy';
|
||||
import AGB from './pages/AGB';
|
||||
|
||||
function App() {
|
||||
return (
|
||||
<Router>
|
||||
<Layout>
|
||||
<Routes>
|
||||
<Route path="/" element={<Home />} />
|
||||
<Route path="/ueber-uns" element={<About />} />
|
||||
<Route path="/kontakt" element={<Contact />} />
|
||||
<Route path="/impressum" element={<Legal />} />
|
||||
<Route path="/datenschutz" element={<Privacy />} />
|
||||
<Route path="/agb" element={<AGB />} />
|
||||
</Routes>
|
||||
</Layout>
|
||||
</Router>
|
||||
);
|
||||
}
|
||||
|
||||
export default App;
|
||||
67
src/components/Layout.tsx
Normal file
67
src/components/Layout.tsx
Normal file
@@ -0,0 +1,67 @@
|
||||
import React from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
const Layout = ({ children }: { children: React.ReactNode }) => {
|
||||
return (
|
||||
<div className="layout">
|
||||
<header style={{
|
||||
position: 'sticky',
|
||||
top: 0,
|
||||
zIndex: 100,
|
||||
background: 'rgba(248, 249, 250, 0.95)',
|
||||
backdropFilter: 'blur(10px)',
|
||||
borderBottom: '1px solid var(--secondary-bg)'
|
||||
}}>
|
||||
<div className="container" style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
|
||||
<Link to="/" style={{ display: 'flex', alignItems: 'center' }}>
|
||||
<img src="/assets/logo.png" alt="MB Grid Solutions" style={{ height: '60px' }} />
|
||||
</Link>
|
||||
<nav style={{ display: 'flex', gap: '2rem' }}>
|
||||
<Link to="/" className="nav-link">Startseite</Link>
|
||||
<Link to="/ueber-uns" className="nav-link">Über uns</Link>
|
||||
<Link to="/kontakt" className="nav-link">Kontakt</Link>
|
||||
</nav>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main>
|
||||
{children}
|
||||
</main>
|
||||
|
||||
<footer style={{ borderTop: '1px solid var(--secondary-bg)', padding: '4rem 0', marginTop: '4rem', background: 'white' }}>
|
||||
<div className="container">
|
||||
<div className="grid" style={{ gridTemplateColumns: 'repeat(auto-fit, minmax(200px, 1fr))', marginBottom: '3rem' }}>
|
||||
<div>
|
||||
<img src="/assets/logo.png" alt="MB Grid Solutions" style={{ height: '80px', marginBottom: '1.5rem', filter: 'grayscale(1)' }} />
|
||||
<p style={{ color: 'var(--text-secondary)', fontSize: '0.9rem' }}>
|
||||
Ihr Partner für Energiekabelprojekte bis 110 kV.
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<h4 style={{ fontSize: '1rem', marginBottom: '1rem' }}>Navigation</h4>
|
||||
<nav style={{ display: 'flex', flexDirection: 'column', gap: '0.5rem' }}>
|
||||
<Link to="/">Startseite</Link>
|
||||
<Link to="/ueber-uns">Über uns</Link>
|
||||
<Link to="/kontakt">Kontakt</Link>
|
||||
</nav>
|
||||
</div>
|
||||
<div>
|
||||
<h4 style={{ fontSize: '1rem', marginBottom: '1rem' }}>Rechtliches</h4>
|
||||
<nav style={{ display: 'flex', flexDirection: 'column', gap: '0.5rem' }}>
|
||||
<Link to="/impressum">Impressum</Link>
|
||||
<Link to="/datenschutz">Datenschutz</Link>
|
||||
<Link to="/agb">AGB</Link>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
<div style={{ borderTop: '1px solid var(--secondary-bg)', paddingTop: '2rem', display: 'flex', justifyContent: 'space-between', color: 'var(--text-secondary)', fontSize: '0.85rem' }}>
|
||||
<div>© {new Date().getFullYear()} MB Grid Solutions GmbH. Alle Rechte vorbehalten.</div>
|
||||
<div>Made with precision.</div>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Layout;
|
||||
258
src/index.css
Normal file
258
src/index.css
Normal file
@@ -0,0 +1,258 @@
|
||||
:root {
|
||||
--primary-color: #0E2A47;
|
||||
--bg-color: #F8F9FA;
|
||||
--secondary-bg: #E6E9ED;
|
||||
--text-secondary: #6B7280;
|
||||
--text-primary: #1F2933;
|
||||
--accent-green: #2FA66A;
|
||||
--font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
|
||||
--transition-fast: 0.2s ease;
|
||||
--shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
|
||||
--shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
|
||||
--header-height: 80px;
|
||||
}
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
}
|
||||
|
||||
html {
|
||||
scroll-behavior: smooth;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: var(--font-family);
|
||||
background-color: var(--bg-color);
|
||||
color: var(--text-primary);
|
||||
line-height: 1.6;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
color: var(--primary-color);
|
||||
font-weight: 700;
|
||||
line-height: 1.1;
|
||||
margin-bottom: 1.5rem;
|
||||
letter-spacing: -0.03em;
|
||||
}
|
||||
|
||||
h1 { font-size: clamp(2rem, 5vw, 3.5rem); hyphens: auto; }
|
||||
h1.no-underline::after,
|
||||
h2.no-underline::after {
|
||||
display: none !important;
|
||||
}
|
||||
h2 { font-size: clamp(1.8rem, 4vw, 2.5rem); position: relative; display: block; margin-bottom: 2rem; }
|
||||
h2:not(.no-underline)::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
bottom: -10px;
|
||||
left: 0;
|
||||
width: 40px;
|
||||
height: 3px;
|
||||
background: var(--accent-green);
|
||||
}
|
||||
|
||||
h3 { font-size: 1.4rem; letter-spacing: -0.01em; }
|
||||
|
||||
a {
|
||||
color: var(--primary-color);
|
||||
text-decoration: none;
|
||||
transition: all var(--transition-fast);
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
padding: 0 1.5rem;
|
||||
}
|
||||
|
||||
section {
|
||||
padding: 6rem 0;
|
||||
}
|
||||
|
||||
.grid {
|
||||
display: grid;
|
||||
gap: 2.5rem;
|
||||
}
|
||||
|
||||
.cta-button {
|
||||
background-color: var(--primary-color);
|
||||
color: white;
|
||||
padding: 1rem 2rem;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
font-weight: 700;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 0.75rem;
|
||||
transition: all var(--transition-fast);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.1em;
|
||||
font-size: 0.8rem;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.cta-button:hover {
|
||||
background-color: var(--text-primary);
|
||||
color: white;
|
||||
}
|
||||
|
||||
input, textarea {
|
||||
width: 100%;
|
||||
padding: 1rem;
|
||||
border: 1px solid var(--secondary-bg);
|
||||
background: white;
|
||||
font-family: inherit;
|
||||
margin-bottom: 1.5rem;
|
||||
transition: all var(--transition-fast);
|
||||
border-radius: 4px;
|
||||
font-size: 16px; /* Prevent zoom on iOS */
|
||||
}
|
||||
|
||||
input:focus, textarea:focus {
|
||||
outline: none;
|
||||
border-color: var(--primary-color);
|
||||
}
|
||||
|
||||
.card {
|
||||
background: white;
|
||||
padding: 2.5rem;
|
||||
border: 1px solid var(--secondary-bg);
|
||||
transition: all var(--transition-fast);
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.card:hover {
|
||||
border-color: var(--primary-color);
|
||||
}
|
||||
|
||||
.img-placeholder {
|
||||
background: #e2e8f0;
|
||||
width: 100%;
|
||||
aspect-ratio: 16/9;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: var(--text-secondary);
|
||||
font-size: 0.75rem;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.2em;
|
||||
margin-bottom: 2rem;
|
||||
border: 1px solid var(--secondary-bg);
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.nav-link {
|
||||
position: relative;
|
||||
font-weight: 600;
|
||||
font-size: 0.85rem;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.05em;
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.nav-link:hover {
|
||||
color: var(--accent-green);
|
||||
}
|
||||
|
||||
.badge {
|
||||
display: block;
|
||||
color: var(--accent-green);
|
||||
font-size: 0.75rem;
|
||||
font-weight: 700;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.15em;
|
||||
margin-bottom: 0.75rem;
|
||||
}
|
||||
|
||||
header .container {
|
||||
height: var(--header-height);
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
:root {
|
||||
--header-height: 64px;
|
||||
}
|
||||
|
||||
section {
|
||||
padding: 4rem 0;
|
||||
}
|
||||
|
||||
.grid {
|
||||
gap: 1.5rem;
|
||||
}
|
||||
|
||||
header .container {
|
||||
height: var(--header-height);
|
||||
padding: 0 1rem;
|
||||
}
|
||||
|
||||
header img {
|
||||
height: 40px !important;
|
||||
}
|
||||
|
||||
nav {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
background: white;
|
||||
border-top: 1px solid var(--secondary-bg);
|
||||
padding: 0.75rem 1rem;
|
||||
justify-content: space-around !important;
|
||||
gap: 0 !important;
|
||||
box-shadow: 0 -2px 10px rgba(0,0,0,0.05);
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
.nav-link {
|
||||
font-size: 0.7rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.nav-link::after {
|
||||
display: none;
|
||||
}
|
||||
|
||||
main {
|
||||
padding-bottom: 80px; /* Space for bottom nav */
|
||||
}
|
||||
|
||||
.hero {
|
||||
padding: 2rem 0 !important;
|
||||
min-height: auto !important;
|
||||
}
|
||||
|
||||
.hero .grid {
|
||||
grid-template-columns: 1fr !important;
|
||||
}
|
||||
|
||||
.hero div:first-child {
|
||||
width: 100% !important;
|
||||
clip-path: none !important;
|
||||
background: #f1f5f9 !important;
|
||||
padding: 2rem 1rem !important;
|
||||
border-radius: 12px;
|
||||
}
|
||||
|
||||
.hero .img-placeholder {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.cta-button {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.card {
|
||||
padding: 1.5rem;
|
||||
}
|
||||
}
|
||||
10
src/main.tsx
Normal file
10
src/main.tsx
Normal file
@@ -0,0 +1,10 @@
|
||||
import React from 'react'
|
||||
import ReactDOM from 'react-dom/client'
|
||||
import App from './App'
|
||||
import './index.css'
|
||||
|
||||
ReactDOM.createRoot(document.getElementById('root')!).render(
|
||||
<React.StrictMode>
|
||||
<App />
|
||||
</React.StrictMode>,
|
||||
)
|
||||
13
src/pages/AGB.tsx
Normal file
13
src/pages/AGB.tsx
Normal file
@@ -0,0 +1,13 @@
|
||||
const AGB = () => (
|
||||
<div className="container">
|
||||
<section>
|
||||
<h1>Allgemeine Geschäftsbedingungen (AGB)</h1>
|
||||
<div style={{ maxWidth: '800px' }}>
|
||||
<p>Hier finden Sie unsere Allgemeinen Geschäftsbedingungen...</p>
|
||||
<p style={{ marginTop: '1rem' }}>[Platzhalter für AGB-Inhalte]</p>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
);
|
||||
|
||||
export default AGB;
|
||||
135
src/pages/About.tsx
Normal file
135
src/pages/About.tsx
Normal file
@@ -0,0 +1,135 @@
|
||||
import { motion } from 'framer-motion';
|
||||
import { Target, Network, Award, Clock, Lightbulb, Truck, MessageSquare, ShieldCheck } from 'lucide-react';
|
||||
|
||||
const About = () => (
|
||||
<div>
|
||||
<section style={{ background: 'white' }}>
|
||||
<div className="container">
|
||||
<div className="grid" style={{ gridTemplateColumns: 'repeat(auto-fit, minmax(400px, 1fr))', alignItems: 'center', gap: '4rem' }}>
|
||||
<div>
|
||||
<h1 className="no-underline">Über uns</h1>
|
||||
<p style={{ fontSize: '1.25rem', color: 'var(--text-secondary)', marginBottom: '2rem', lineHeight: 1.5 }}>
|
||||
Wir verbinden Energie, Know-how und Innovation, um die Infrastruktur der Zukunft zu gestalten.
|
||||
</p>
|
||||
<p style={{ marginBottom: '1.5rem' }}>
|
||||
MB Grid Solution steht für technische Exzellenz in der Energiekabeltechnologie. Wir verstehen uns als Ihr technischer Lotse, der mit jahrzehntelanger Erfahrung und einem klaren Blick für zukunftsweisende Entwicklungen komplexe Projekte sicher zum Ziel führt.
|
||||
</p>
|
||||
<p>
|
||||
Unsere Wurzeln liegen in der tiefen praktischen Erfahrung. Wir vereinen Tradition mit modernster Innovation, um zuverlässige Energielösungen für Projekte bis 110 kV und darüber hinaus zu realisieren.
|
||||
</p>
|
||||
</div>
|
||||
<div className="img-placeholder" style={{ height: '500px', marginBottom: 0 }}>
|
||||
[ Engineering Excellence Visualization ]
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<div className="container">
|
||||
<div style={{ textAlign: 'center', marginBottom: '4rem' }}>
|
||||
<h2 className="no-underline">Die Köpfe hinter MB Grid Solution</h2>
|
||||
<p style={{ color: 'var(--text-secondary)' }}>Expertise, die Energie zum Laufen bringt.</p>
|
||||
</div>
|
||||
<div className="grid" style={{ gridTemplateColumns: 'repeat(auto-fit, minmax(400px, 1fr))' }}>
|
||||
<div className="card">
|
||||
<div style={{ display: 'flex', gap: '2rem', marginBottom: '1.5rem' }}>
|
||||
<div style={{ width: '120px', height: '120px', background: 'var(--secondary-bg)', flexShrink: 0 }}></div>
|
||||
<div>
|
||||
<h3 style={{ marginBottom: '0.25rem' }}>Michael Bodemer</h3>
|
||||
<p style={{ color: 'var(--accent-green)', fontWeight: 600, fontSize: '0.85rem', textTransform: 'uppercase', letterSpacing: '0.05em' }}>Technische Leitung</p>
|
||||
</div>
|
||||
</div>
|
||||
<p style={{ fontStyle: 'italic', marginBottom: '1.5rem', color: 'var(--primary-color)', fontWeight: 500 }}>
|
||||
„Herausforderungen sind da, um gelöst zu werden – nicht, um über ihre Komplexität zu diskutieren.“
|
||||
</p>
|
||||
<p style={{ fontSize: '0.95rem', color: 'var(--text-secondary)', lineHeight: 1.6 }}>
|
||||
Michael Bodemer ist der Experte für komplexe Kabelnetze. Mit seinem scharfen Blick für praktikable Lösungen treibt er Projekte in der Planung und technischen Umsetzung voran. Er stellt sicher, dass für jedes Vorhaben die optimalen Komponenten ausgewählt werden.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="card">
|
||||
<div style={{ display: 'flex', gap: '2rem', marginBottom: '1.5rem' }}>
|
||||
<div style={{ width: '120px', height: '120px', background: 'var(--secondary-bg)', flexShrink: 0 }}></div>
|
||||
<div>
|
||||
<h3 style={{ marginBottom: '0.25rem' }}>Klaus Mintel</h3>
|
||||
<p style={{ color: 'var(--accent-green)', fontWeight: 600, fontSize: '0.85rem', textTransform: 'uppercase', letterSpacing: '0.05em' }}>Projektmanagement</p>
|
||||
</div>
|
||||
</div>
|
||||
<p style={{ fontStyle: 'italic', marginBottom: '1.5rem', color: 'var(--primary-color)', fontWeight: 500 }}>
|
||||
„Manchmal braucht es nur einen klaren Kopf und das richtige Kabel, um die Welt ein Stück besser zu machen.“
|
||||
</p>
|
||||
<p style={{ fontSize: '0.95rem', color: 'var(--text-secondary)', lineHeight: 1.6 }}>
|
||||
Klaus Mintel bewahrt auch bei komplexesten Anforderungen den Weitblick. Mit jahrzehntelanger Erfahrung und einem stabilen Netzwerk sorgt er für reibungslose Abläufe. Er bringt technische Themen präzise auf den Punkt und sichert den Projekterfolg.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section style={{ background: 'white' }}>
|
||||
<div className="container">
|
||||
<div style={{ textAlign: 'center', marginBottom: '5rem' }}>
|
||||
<h2 className="no-underline">Unser Manifest</h2>
|
||||
<p style={{ color: 'var(--text-secondary)' }}>Werte, die unsere tägliche Arbeit leiten.</p>
|
||||
</div>
|
||||
<div className="grid" style={{ gridTemplateColumns: 'repeat(auto-fit, minmax(300px, 1fr))', gap: '3rem' }}>
|
||||
<div style={{ display: 'flex', gap: '1.5rem' }}>
|
||||
<div style={{ color: 'var(--accent-green)', flexShrink: 0 }}><Award size={32} /></div>
|
||||
<div>
|
||||
<h4 style={{ marginBottom: '0.5rem' }}>1. Kompetenz</h4>
|
||||
<p style={{ fontSize: '0.9rem', color: 'var(--text-secondary)' }}>Jahrzehntelange Erfahrung kombiniert mit europaweitem Know-how. Wir arbeiten mit Partnern für modernste Anlagen und Testlabore bis 525 kV.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div style={{ display: 'flex', gap: '1.5rem' }}>
|
||||
<div style={{ color: 'var(--accent-green)', flexShrink: 0 }}><Clock size={32} /></div>
|
||||
<div>
|
||||
<h4 style={{ marginBottom: '0.5rem' }}>2. Verfügbarkeit</h4>
|
||||
<p style={{ fontSize: '0.9rem', color: 'var(--text-secondary)' }}>Schnelle und verlässliche Unterstützung ohne unnötige Verzögerungen. Wir sind für Sie da, wenn es darauf ankommt.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div style={{ display: 'flex', gap: '1.5rem' }}>
|
||||
<div style={{ color: 'var(--accent-green)', flexShrink: 0 }}><Lightbulb size={32} /></div>
|
||||
<div>
|
||||
<h4 style={{ marginBottom: '0.5rem' }}>3. Lösungen</h4>
|
||||
<p style={{ fontSize: '0.9rem', color: 'var(--text-secondary)' }}>Wir stellen die richtigen Fragen – an Sie, an Hersteller und an uns selbst. Nur wer hinterfragt, findet die technisch und wirtschaftlich beste Lösung.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div style={{ display: 'flex', gap: '1.5rem' }}>
|
||||
<div style={{ color: 'var(--accent-green)', flexShrink: 0 }}><Truck size={32} /></div>
|
||||
<div>
|
||||
<h4 style={{ marginBottom: '0.5rem' }}>4. Logistik & Überwachung</h4>
|
||||
<p style={{ fontSize: '0.9rem', color: 'var(--text-secondary)' }}>Von der Fertigungsüberwachung bis zum Fracht-Tracking und der termingerechten Anlieferung – wir steuern den gesamten Prozess professionell.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div style={{ display: 'flex', gap: '1.5rem' }}>
|
||||
<div style={{ color: 'var(--accent-green)', flexShrink: 0 }}><MessageSquare size={32} /></div>
|
||||
<div>
|
||||
<h4 style={{ marginBottom: '0.5rem' }}>5. Offenheit</h4>
|
||||
<p style={{ fontSize: '0.9rem', color: 'var(--text-secondary)' }}>Wir hören zu und passen unsere Prozesse kontinuierlich an. Stillstand ist für uns keine Option – wir optimieren für Ihren Erfolg.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div style={{ display: 'flex', gap: '1.5rem' }}>
|
||||
<div style={{ color: 'var(--accent-green)', flexShrink: 0 }}><ShieldCheck size={32} /></div>
|
||||
<div>
|
||||
<h4 style={{ marginBottom: '0.5rem' }}>6. Zuverlässigkeit</h4>
|
||||
<p style={{ fontSize: '0.9rem', color: 'var(--text-secondary)' }}>Wir halten, was wir versprechen – ohne Ausnahme. Verbindlichkeit ist das Fundament unserer Zusammenarbeit.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<div className="container">
|
||||
<div className="card" style={{ background: 'var(--primary-color)', color: 'white', textAlign: 'center', padding: '4rem' }}>
|
||||
<h2 style={{ color: 'white', border: 'none', padding: 0, marginBottom: '1.5rem' }}>Bereit für Ihr nächstes Projekt?</h2>
|
||||
<p style={{ marginBottom: '2.5rem', opacity: 0.9, fontSize: '1.1rem' }}>Lassen Sie uns gemeinsam die optimale Lösung für Ihre Energieinfrastruktur finden.</p>
|
||||
<a href="/kontakt" className="cta-button" style={{ background: 'white', color: 'var(--primary-color)' }}>Jetzt Kontakt aufnehmen</a>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
);
|
||||
|
||||
export default About;
|
||||
129
src/pages/Contact.tsx
Normal file
129
src/pages/Contact.tsx
Normal file
@@ -0,0 +1,129 @@
|
||||
import React, { useState } from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { motion } from 'framer-motion';
|
||||
import { Mail, Phone, MapPin, Send, CheckCircle } from 'lucide-react';
|
||||
|
||||
const Contact = () => {
|
||||
const [submitted, setSubmitted] = useState(false);
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
|
||||
e.preventDefault();
|
||||
setLoading(true);
|
||||
const formData = new FormData(e.currentTarget);
|
||||
const data = Object.fromEntries(formData.entries());
|
||||
|
||||
try {
|
||||
const response = await fetch('/api/contact', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(data),
|
||||
});
|
||||
if (response.ok) {
|
||||
setSubmitted(true);
|
||||
} else {
|
||||
const err = await response.json();
|
||||
alert(`Fehler: ${err.error || 'Es gab einen Fehler beim Senden Ihrer Nachricht.'}`);
|
||||
}
|
||||
} catch (error) {
|
||||
alert('Es gab einen Fehler beim Senden Ihrer Nachricht.');
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="container">
|
||||
<section>
|
||||
<div className="grid" style={{ gridTemplateColumns: 'repeat(auto-fit, minmax(400px, 1fr))', gap: '4rem' }}>
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
>
|
||||
<h1>Kontakt</h1>
|
||||
<p style={{ fontSize: '1.1rem', color: 'var(--text-secondary)', marginBottom: '3rem' }}>
|
||||
Haben Sie Fragen zu einem Projekt oder benötigen Sie technische Beratung? Wir freuen uns auf Ihre Nachricht.
|
||||
</p>
|
||||
|
||||
<div style={{ display: 'flex', flexDirection: 'column', gap: '2rem' }}>
|
||||
<div style={{ display: 'flex', gap: '1.5rem', alignItems: 'flex-start' }}>
|
||||
<div style={{ color: 'var(--accent-green)', background: 'white', padding: '1rem', border: '1px solid var(--secondary-bg)' }}><Mail size={24} /></div>
|
||||
<div>
|
||||
<h4 style={{ marginBottom: '0.25rem', fontSize: '1rem' }}>E-Mail</h4>
|
||||
<a href="mailto:info@mb-grid-solutions.com" style={{ fontSize: '1.1rem', fontWeight: 500 }}>info@mb-grid-solutions.com</a>
|
||||
</div>
|
||||
</div>
|
||||
<div style={{ display: 'flex', gap: '1.5rem', alignItems: 'flex-start' }}>
|
||||
<div style={{ color: 'var(--accent-green)', background: 'white', padding: '1rem', border: '1px solid var(--secondary-bg)' }}><Phone size={24} /></div>
|
||||
<div>
|
||||
<h4 style={{ marginBottom: '0.25rem', fontSize: '1rem' }}>Telefon</h4>
|
||||
<a href="tel:+49123456789" style={{ fontSize: '1.1rem', fontWeight: 500 }}>+49 (0) 123 456789</a>
|
||||
</div>
|
||||
</div>
|
||||
<div style={{ display: 'flex', gap: '1.5rem', alignItems: 'flex-start' }}>
|
||||
<div style={{ color: 'var(--accent-green)', background: 'white', padding: '1rem', border: '1px solid var(--secondary-bg)' }}><MapPin size={24} /></div>
|
||||
<div>
|
||||
<h4 style={{ marginBottom: '0.25rem', fontSize: '1rem' }}>Anschrift</h4>
|
||||
<p style={{ fontSize: '1.1rem', fontWeight: 500, color: 'var(--primary-color)' }}>
|
||||
MB Grid Solutions GmbH<br />
|
||||
Raiffeisenstraße 22<br />
|
||||
73630 Remshalden
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</motion.div>
|
||||
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ delay: 0.2 }}
|
||||
className="card"
|
||||
>
|
||||
{submitted ? (
|
||||
<div style={{ textAlign: 'center', padding: '3rem 0' }}>
|
||||
<div style={{ color: 'var(--accent-green)', marginBottom: '1.5rem' }}><CheckCircle size={64} /></div>
|
||||
<h3>Nachricht gesendet</h3>
|
||||
<p style={{ color: 'var(--text-secondary)' }}>Vielen Dank für Ihre Anfrage. Wir werden uns in Kürze bei Ihnen melden.</p>
|
||||
<button onClick={() => setSubmitted(false)} className="cta-button" style={{ marginTop: '2rem' }}>Weitere Nachricht</button>
|
||||
</div>
|
||||
) : (
|
||||
<form onSubmit={handleSubmit} style={{ display: 'flex', flexDirection: 'column' }}>
|
||||
<div className="grid" style={{ gridTemplateColumns: '1fr 1fr', gap: '1rem' }}>
|
||||
<div>
|
||||
<label htmlFor="name" style={{ display: 'block', marginBottom: '0.5rem', fontSize: '0.9rem', fontWeight: 600 }}>Name *</label>
|
||||
<input type="text" id="name" name="name" required minLength={2} maxLength={100} placeholder="Ihr Name" />
|
||||
</div>
|
||||
<div>
|
||||
<label htmlFor="company" style={{ display: 'block', marginBottom: '0.5rem', fontSize: '0.9rem', fontWeight: 600 }}>Firma</label>
|
||||
<input type="text" id="company" name="company" placeholder="Ihr Unternehmen" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<label htmlFor="email" style={{ display: 'block', marginBottom: '0.5rem', fontSize: '0.9rem', fontWeight: 600 }}>E-Mail *</label>
|
||||
<input type="email" id="email" name="email" required placeholder="ihre@email.de" />
|
||||
|
||||
<label htmlFor="message" style={{ display: 'block', marginBottom: '0.5rem', fontSize: '0.9rem', fontWeight: 600 }}>Nachricht *</label>
|
||||
<textarea id="message" name="message" required minLength={20} maxLength={4000} rows={6} placeholder="Wie können wir Ihnen helfen?"></textarea>
|
||||
|
||||
<div className="visually-hidden">
|
||||
<label htmlFor="website">Website (bitte leer lassen)</label>
|
||||
<input type="text" id="website" name="website" tabIndex={-1} autoComplete="off" />
|
||||
</div>
|
||||
|
||||
<button type="submit" disabled={loading} className="cta-button" style={{ alignSelf: 'flex-start' }}>
|
||||
{loading ? 'Wird gesendet...' : 'Nachricht senden'} <Send size={18} />
|
||||
</button>
|
||||
<p style={{ fontSize: '0.75rem', marginTop: '1.5rem', color: 'var(--text-secondary)', lineHeight: 1.4 }}>
|
||||
* Pflichtfelder. Mit dem Absenden erklären Sie sich mit unserer <Link to="/datenschutz" style={{ textDecoration: 'underline' }}>Datenschutzerklärung</Link> einverstanden.
|
||||
</p>
|
||||
</form>
|
||||
)}
|
||||
</motion.div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Contact;
|
||||
145
src/pages/Home.tsx
Normal file
145
src/pages/Home.tsx
Normal file
@@ -0,0 +1,145 @@
|
||||
import { Link } from 'react-router-dom';
|
||||
import { ArrowRight, Shield, Zap, BarChart3, CheckCircle2 } from 'lucide-react';
|
||||
|
||||
const Home = () => (
|
||||
<div className="home-page">
|
||||
<section className="hero" style={{ minHeight: '70vh', display: 'flex', alignItems: 'center', background: 'white', position: 'relative', overflow: 'hidden', padding: '4rem 0' }}>
|
||||
<div style={{ position: 'absolute', top: 0, right: 0, width: '50%', height: '100%', background: '#f1f5f9', clipPath: 'polygon(20% 0, 100% 0, 100% 100%, 0% 100%)', zIndex: 0 }} />
|
||||
<div className="container" style={{ position: 'relative', zIndex: 1 }}>
|
||||
<div className="grid" style={{ gridTemplateColumns: 'repeat(auto-fit, minmax(320px, 1fr))', alignItems: 'center', gap: '4rem' }}>
|
||||
<div>
|
||||
<span className="badge">Engineering Excellence</span>
|
||||
<h1 style={{ marginBottom: '1.5rem' }}>Spezialisierter Partner für Energiekabelprojekte</h1>
|
||||
<p style={{ fontSize: '1.2rem', marginBottom: '2.5rem', color: 'var(--text-secondary)', lineHeight: 1.5, maxWidth: '600px' }}>
|
||||
Herstellerneutrale technische Beratung und Projektbegleitung für Hochspannungsnetze bis 110 kV.
|
||||
</p>
|
||||
<div style={{ display: 'flex', gap: '1.5rem', alignItems: 'center', flexWrap: 'wrap' }}>
|
||||
<Link to="/kontakt" className="cta-button">
|
||||
Projekt anfragen <ArrowRight size={18} />
|
||||
</Link>
|
||||
<Link to="/ueber-uns" style={{ fontWeight: 700, fontSize: '0.8rem', textTransform: 'uppercase', letterSpacing: '0.1em', display: 'flex', alignItems: 'center', gap: '0.5rem' }}>
|
||||
Mehr erfahren <ArrowRight size={16} />
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
<div className="img-placeholder" style={{ height: '400px', marginBottom: 0, boxShadow: '0 20px 40px -10px rgba(0,0,0,0.1)' }}>
|
||||
[ High-Voltage Infrastructure ]
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section style={{ background: '#f8fafc' }}>
|
||||
<div className="container">
|
||||
<div style={{ marginBottom: '5rem' }}>
|
||||
<span className="badge">Portfolio</span>
|
||||
<h2 style={{ border: 'none', padding: 0 }}>Unsere Leistungen</h2>
|
||||
<p style={{ color: 'var(--text-secondary)', maxWidth: '600px' }}>Präzision und Unabhängigkeit in jeder Phase Ihres Projekts.</p>
|
||||
</div>
|
||||
<div className="grid" style={{ gridTemplateColumns: 'repeat(auto-fit, minmax(300px, 1fr))', gap: '2.5rem' }}>
|
||||
{[
|
||||
{ icon: <Zap size={32} />, title: 'Technische Beratung', desc: 'Individuelle Konzepte und technische Spezifikationen für Ihre Kabelinfrastruktur.' },
|
||||
{ icon: <Shield size={32} />, title: 'Projektbegleitung', desc: 'Professionelle Überwachung und Qualitätssicherung während der gesamten Ausführung.' },
|
||||
{ icon: <BarChart3 size={32} />, title: 'Produktbeschaffung', desc: 'Herstellerneutrale Marktanalyse und Unterstützung bei der Komponentenwahl.' }
|
||||
].map((item, i) => (
|
||||
<div key={i} className="card">
|
||||
<div style={{ color: 'var(--accent-green)', marginBottom: '1.5rem' }}>{item.icon}</div>
|
||||
<h3 style={{ marginBottom: '1rem' }}>{item.title}</h3>
|
||||
<p style={{ color: 'var(--text-secondary)', lineHeight: 1.6 }}>{item.desc}</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section style={{ background: 'white' }}>
|
||||
<div className="container">
|
||||
<div className="grid" style={{ gridTemplateColumns: 'repeat(auto-fit, minmax(320px, 1fr))', alignItems: 'center', gap: '4rem' }}>
|
||||
<div className="img-placeholder" style={{ height: '400px', marginBottom: 0 }}>
|
||||
[ Technical Drawing / CAD ]
|
||||
</div>
|
||||
<div>
|
||||
<span className="badge">Expertise</span>
|
||||
<h2>Anwendungen & Zielgruppen</h2>
|
||||
<p style={{ marginBottom: '2.5rem', color: 'var(--text-secondary)', fontSize: '1.1rem' }}>Wir unterstützen Akteure der Energiewende bei der Realisierung komplexer Kabelprojekte.</p>
|
||||
<div className="grid" style={{ gridTemplateColumns: '1fr 1fr', gap: '1.5rem' }}>
|
||||
{[
|
||||
'Energieversorger',
|
||||
'Ingenieurbüros',
|
||||
'Tiefbauunternehmen',
|
||||
'Industrie',
|
||||
'Projektierer EE',
|
||||
'Planungsbüros'
|
||||
].map((item, i) => (
|
||||
<div key={i} style={{ display: 'flex', alignItems: 'center', gap: '0.75rem', fontSize: '0.95rem', fontWeight: 600, color: 'var(--primary-color)' }}>
|
||||
<CheckCircle2 size={18} style={{ color: 'var(--accent-green)' }} />
|
||||
{item}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section style={{ background: '#0f172a', color: 'white' }}>
|
||||
<div className="container">
|
||||
<div style={{ marginBottom: '5rem' }}>
|
||||
<span className="badge" style={{ color: 'white', opacity: 0.6 }}>Expertise</span>
|
||||
<h2 style={{ color: 'white', border: 'none', padding: 0 }}>Technische Spezifikationen</h2>
|
||||
</div>
|
||||
<div className="grid" style={{ gridTemplateColumns: 'repeat(auto-fit, minmax(280px, 1fr))', gap: '2rem' }}>
|
||||
{[
|
||||
{ label: 'Kabeltypen', value: 'N2XS(FL)2Y, N2X(F)KLD2Y, NA2XS(FL)2Y, NA2X(F)KLD2Y', desc: 'Umfassende Expertise in der Spezifikation gängiger Hochspannungskabel.' },
|
||||
{ label: 'Spannungsebenen', value: '64/110 kV & Mittelspannung', desc: 'Spezialisierte Beratung für die 110-kV-Ebene und komplexe Mittelspannung.' },
|
||||
{ label: 'Leitertechnologie', value: 'Massiv-, Mehrdraht- & Millikenleiter', desc: 'Optimierung des Leiterdesigns hinsichtlich Stromtragfähigkeit.' }
|
||||
].map((item, i) => (
|
||||
<div
|
||||
key={i}
|
||||
style={{ background: 'rgba(255,255,255,0.05)', padding: '2.5rem', border: '1px solid rgba(255,255,255,0.1)' }}
|
||||
>
|
||||
<h4 style={{ fontSize: '0.75rem', textTransform: 'uppercase', color: 'var(--accent-green)', marginBottom: '1rem', letterSpacing: '0.2em' }}>{item.label}</h4>
|
||||
<p style={{ fontWeight: 700, fontSize: '1.15rem', marginBottom: '1rem', color: 'white' }}>{item.value}</p>
|
||||
<p style={{ fontSize: '0.9rem', color: 'rgba(255,255,255,0.6)', lineHeight: 1.6 }}>{item.desc}</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section style={{ background: 'white' }}>
|
||||
<div className="container">
|
||||
<div style={{ marginBottom: '5rem' }}>
|
||||
<span className="badge">Werte</span>
|
||||
<h2 style={{ border: 'none', padding: 0 }}>Unsere Leitprinzipien</h2>
|
||||
</div>
|
||||
<div className="grid" style={{ gridTemplateColumns: 'repeat(auto-fit, minmax(300px, 1fr))', gap: '3rem' }}>
|
||||
{[
|
||||
{ title: 'Exzellenz', desc: 'Höchste technische Präzision in jedem Detail. Wir suchen die optimale Lösung jenseits des Standards.' },
|
||||
{ title: 'Nachhaltigkeit', desc: 'Zukunftssichere Lösungen für die Infrastruktur. Wir denken in Lebenszyklen und Zuverlässigkeit.' },
|
||||
{ title: 'Transparenz', desc: 'Ehrliche Beratung auf Augenhöhe. Wir kommunizieren klar und herstellerneutral.' }
|
||||
].map((item, i) => (
|
||||
<div key={i} style={{ borderLeft: '3px solid var(--accent-green)', paddingLeft: '2rem' }}>
|
||||
<h3 style={{ marginBottom: '1rem', textTransform: 'uppercase', letterSpacing: '0.1em', fontSize: '1.1rem' }}>{item.title}</h3>
|
||||
<p style={{ color: 'var(--text-secondary)', lineHeight: 1.7, fontSize: '1rem' }}>{item.desc}</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section style={{ background: 'var(--primary-color)', padding: '6rem 0', position: 'relative', overflow: 'hidden' }}>
|
||||
<div className="container" style={{ position: 'relative', zIndex: 1, textAlign: 'center' }}>
|
||||
<h2 className="no-underline" style={{ color: 'white', border: 'none', padding: 0, marginBottom: '1.5rem', fontSize: 'clamp(1.8rem, 5vw, 2.5rem)' }}>Bereit für Ihr nächstes Projekt?</h2>
|
||||
<p style={{ color: 'rgba(255,255,255,0.7)', marginBottom: '3rem', fontSize: '1.1rem', maxWidth: '700px', margin: '0 auto 3rem' }}>
|
||||
Lassen Sie uns gemeinsam die optimale Lösung für Ihre Energieinfrastruktur finden.
|
||||
</p>
|
||||
<Link to="/kontakt" className="cta-button" style={{ background: 'white', color: 'var(--primary-color)' }}>
|
||||
Jetzt Kontakt aufnehmen <ArrowRight size={18} />
|
||||
</Link>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
);
|
||||
|
||||
export default Home;
|
||||
34
src/pages/Legal.tsx
Normal file
34
src/pages/Legal.tsx
Normal file
@@ -0,0 +1,34 @@
|
||||
const Legal = () => (
|
||||
<div className="container">
|
||||
<section>
|
||||
<h1>Impressum</h1>
|
||||
<div style={{ maxWidth: '800px' }}>
|
||||
<p><strong>Angaben gemäß § 5 TMG</strong></p>
|
||||
<p style={{ marginBottom: '1.5rem' }}>
|
||||
MB Grid Solutions GmbH<br />
|
||||
Raiffeisenstraße 22<br />
|
||||
73630 Remshalden
|
||||
</p>
|
||||
<p><strong>Vertreten durch:</strong></p>
|
||||
<p style={{ marginBottom: '1.5rem' }}>Michael Bodemer</p>
|
||||
<p><strong>Kontakt:</strong></p>
|
||||
<p style={{ marginBottom: '1.5rem' }}>
|
||||
E-Mail: info@mb-grid-solutions.com<br />
|
||||
Web: www.mb-grid-solutions.com
|
||||
</p>
|
||||
<p><strong>Registereintrag:</strong></p>
|
||||
<p style={{ marginBottom: '1.5rem' }}>
|
||||
Eintragung im Handelsregister.<br />
|
||||
Registergericht: Amtsgericht Stuttgart<br />
|
||||
Registernummer: HRB 798037
|
||||
</p>
|
||||
<p><strong>Urheberrecht:</strong></p>
|
||||
<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>
|
||||
</section>
|
||||
</div>
|
||||
);
|
||||
|
||||
export default Legal;
|
||||
22
src/pages/Privacy.tsx
Normal file
22
src/pages/Privacy.tsx
Normal file
@@ -0,0 +1,22 @@
|
||||
const Privacy = () => (
|
||||
<div className="container">
|
||||
<section>
|
||||
<h1>Datenschutzerklärung</h1>
|
||||
<div style={{ maxWidth: '800px' }}>
|
||||
<h2>1. Datenschutz auf einen Blick</h2>
|
||||
<p style={{ marginBottom: '1.5rem' }}>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>
|
||||
|
||||
<h2>2. Hosting</h2>
|
||||
<p style={{ marginBottom: '1.5rem' }}>Unsere Website wird bei Hetzner Online GmbH gehostet. Der Serverstandort ist Deutschland. Wir haben einen Vertrag über Auftragsverarbeitung (AVV) mit Hetzner geschlossen.</p>
|
||||
|
||||
<h2>3. Kontaktformular</h2>
|
||||
<p style={{ marginBottom: '1.5rem' }}>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>
|
||||
|
||||
<h2>4. Server-Log-Dateien</h2>
|
||||
<p style={{ marginBottom: '1.5rem' }}>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>
|
||||
</section>
|
||||
</div>
|
||||
);
|
||||
|
||||
export default Privacy;
|
||||
21
tsconfig.json
Normal file
21
tsconfig.json
Normal file
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ESNext",
|
||||
"useDefineForClassFields": true,
|
||||
"lib": ["DOM", "DOM.Iterable", "ESNext", "ES2019"],
|
||||
"allowJs": false,
|
||||
"skipLibCheck": true,
|
||||
"esModuleInterop": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"strict": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "Bundler",
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"noEmit": true,
|
||||
"jsx": "react-jsx"
|
||||
},
|
||||
"include": ["src"],
|
||||
"references": [{ "path": "./tsconfig.node.json" }]
|
||||
}
|
||||
9
tsconfig.node.json
Normal file
9
tsconfig.node.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"composite": true,
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "Node",
|
||||
"allowSyntheticDefaultImports": true
|
||||
},
|
||||
"include": ["vite.config.ts"]
|
||||
}
|
||||
12
tsconfig.server.json
Normal file
12
tsconfig.server.json
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ESNext",
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "Node",
|
||||
"esModuleInterop": true,
|
||||
"skipLibCheck": true,
|
||||
"strict": true,
|
||||
"outDir": "dist/backend"
|
||||
},
|
||||
"include": ["server.ts"]
|
||||
}
|
||||
15
vite.config.ts
Normal file
15
vite.config.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
import { defineConfig } from 'vite'
|
||||
import react from '@vitejs/plugin-react-swc'
|
||||
|
||||
// https://vitejs.dev/config/
|
||||
export default defineConfig({
|
||||
plugins: [react()],
|
||||
build: {
|
||||
outDir: 'dist/frontend',
|
||||
},
|
||||
server: {
|
||||
proxy: {
|
||||
'/api': 'http://localhost:3000'
|
||||
}
|
||||
}
|
||||
})
|
||||
Reference in New Issue
Block a user