feat: Hero, About, Contact components accept CMS props

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
MBO-Tech-IT 2026-06-02 20:35:23 +02:00
parent b9aa6a0a54
commit 3eeea27b92
3 changed files with 149 additions and 65 deletions

View File

@ -19,7 +19,19 @@ const highlights = [
},
];
export default function About() {
interface AboutProps {
eyebrowText?: string;
absatz1?: string;
absatz2?: string;
stats?: { wert: string; label: string }[];
}
export default function About({
eyebrowText = "Über uns",
absatz1 = "MBO-Tech-IT steht für über 30 Jahre IT-Erfahrung — angefangen bei der einfachen Hardware-Wartung, über den Aufbau großer Client-Server-Netzwerke, bis hin zu mehr als 20 Jahren Spezialisierung in der IT-Security.",
absatz2 = "Heute liegt der Fokus auf modernen Container-Technologien, Cloud-nativer Infrastruktur und Virtualisierung. Dieses breite Fundament ermöglicht es, Lösungen zu entwickeln, die nicht nur funktionieren — sondern auch sicher und langfristig tragfähig sind.",
stats,
}: AboutProps = {}) {
return (
<section id="about" className="py-24 px-4 sm:px-6 lg:px-8 relative">
<div className="absolute inset-0 bg-[radial-gradient(ellipse_at_bottom_right,rgba(37,99,235,0.06)_0%,transparent_60%)]" />
@ -29,23 +41,17 @@ export default function About() {
{/* Left: Text */}
<div>
<span className="text-orange-400 font-mono text-xs font-bold tracking-[0.25em] uppercase">
Über uns
{eyebrowText}
</span>
<h2 className="text-4xl sm:text-5xl font-black text-slate-900 dark:text-white mt-3 mb-6 leading-tight">
IT-Expertise,{" "}
<span className="text-gradient">die Sie weiterbringt</span>
</h2>
<p className="text-slate-600 dark:text-slate-400 text-lg leading-relaxed mb-6">
MBO-Tech-IT steht für über <strong className="text-slate-900 dark:text-white">30 Jahre IT-Erfahrung</strong> &mdash;
angefangen bei der einfachen Hardware-Wartung, über den Aufbau großer
Client-Server-Netzwerke, bis hin zu mehr als 20 Jahren Spezialisierung
in der <strong className="text-slate-900 dark:text-white">IT-Security</strong>.
{absatz1}
</p>
<p className="text-slate-600 dark:text-slate-400 text-lg leading-relaxed mb-8">
Heute liegt der Fokus auf modernen Container-Technologien, Cloud-nativer
Infrastruktur und Virtualisierung. Dieses breite Fundament ermöglicht es,
Lösungen zu entwickeln, die nicht nur funktionieren &mdash; sondern auch
sicher und langfristig tragfähig sind.
{absatz2}
</p>
{/* Tagline callout */}
@ -76,6 +82,17 @@ export default function About() {
</div>
))}
</div>
{stats && stats.length > 0 && (
<div className="flex gap-8 mt-4">
{stats.map((s) => (
<div key={s.label}>
<div className="text-3xl font-black text-orange-400">{s.wert}</div>
<div className="text-sm text-slate-500 dark:text-slate-400">{s.label}</div>
</div>
))}
</div>
)}
</div>
{/* Right: Process steps */}

View File

@ -2,6 +2,25 @@
import { useState } from "react";
interface OeffnungsZeit { tag: string; von: string; bis: string }
interface ContactProps {
telefon?: string;
email?: string;
adresseZeile1?: string;
adresseZeile2?: string;
oeffnungszeiten?: OeffnungsZeit[];
}
type Status = "idle" | "loading" | "success" | "error";
export default function Contact({
telefon = "+49 171 9345193",
email = "kontakt@mbo-tech-it.de",
adresseZeile1 = "Mörikestr. 2",
adresseZeile2 = "74564 Crailsheim",
oeffnungszeiten,
}: ContactProps = {}) {
const contactItems = [
{
icon: (
@ -10,8 +29,8 @@ const contactItems = [
</svg>
),
label: "Telefon",
value: "+49 171 9345193",
href: "tel:+4917193451093",
value: telefon,
href: `tel:${telefon.replace(/\s/g, "")}`,
},
{
icon: (
@ -20,8 +39,8 @@ const contactItems = [
</svg>
),
label: "E-Mail",
value: "kontakt@mbo-tech-it.de",
href: "mailto:kontakt@mbo-tech-it.de",
value: email,
href: `mailto:${email}`,
},
{
icon: (
@ -31,16 +50,13 @@ const contactItems = [
</svg>
),
label: "Standort",
value: "Crailsheim, 74564",
href: "https://maps.google.com/?q=Mörikestr.+2,+74564+Crailsheim",
value: `${adresseZeile1}, ${adresseZeile2}`,
href: `https://maps.google.com/?q=${encodeURIComponent(adresseZeile1 + " " + adresseZeile2)}`,
},
];
type Status = "idle" | "loading" | "success" | "error";
export default function Contact() {
const [name, setName] = useState("");
const [email, setEmail] = useState("");
const [emailInput, setEmailInput] = useState("");
const [betreff, setBetreff] = useState("");
const [nachricht, setNachricht] = useState("");
const [status, setStatus] = useState<Status>("idle");
@ -55,7 +71,7 @@ export default function Contact() {
const res = await fetch("/api/contact", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ name, email, betreff, nachricht }),
body: JSON.stringify({ name, email: emailInput, betreff, nachricht }),
});
const data = await res.json();
@ -108,7 +124,7 @@ export default function Contact() {
onClick={() => {
setStatus("idle");
setName("");
setEmail("");
setEmailInput("");
setBetreff("");
setNachricht("");
}}
@ -136,8 +152,8 @@ export default function Contact() {
<input
type="email"
required
value={email}
onChange={(e) => setEmail(e.target.value)}
value={emailInput}
onChange={(e) => setEmailInput(e.target.value)}
placeholder="max@beispiel.de"
className="w-full px-4 py-3 rounded-xl bg-slate-50 dark:bg-[#111925] border border-slate-300 dark:border-gray-700 text-slate-900 dark:text-white placeholder-slate-400 dark:placeholder-slate-600 focus:outline-none focus:border-orange-500/60 focus:ring-1 focus:ring-orange-500/20 transition-colors"
/>
@ -239,6 +255,22 @@ export default function Contact() {
</a>
))}
</div>
{oeffnungszeiten && oeffnungszeiten.length > 0 && (
<div className="mt-6 pt-6 border-t border-slate-200 dark:border-slate-700">
<h4 className="text-sm font-semibold text-slate-500 dark:text-slate-400 uppercase tracking-wider mb-3">
Öffnungszeiten
</h4>
<ul className="space-y-1">
{oeffnungszeiten.map((oz) => (
<li key={oz.tag} className="flex justify-between text-sm text-slate-600 dark:text-slate-400">
<span>{oz.tag}</span>
<span>{oz.von} {oz.bis}</span>
</li>
))}
</ul>
</div>
)}
</div>
</div>
</section>

View File

@ -1,10 +1,33 @@
// components/Hero.tsx
"use client";
import { useMemo } from "react";
import { useTypewriter } from "@/hooks/useTypewriter";
export default function Hero() {
interface HeroProps {
eyebrowText?: string;
headline1?: string;
subtext1?: string;
subtext2?: string;
cta1Text?: string;
cta1Href?: string;
cta2Text?: string;
cta2Href?: string;
bgImagePath?: string | null;
badges?: string[];
}
export default function Hero({
eyebrowText = "Digital Denken. Lokal Handeln.",
headline1 = "Ihre IT-Infrastruktur.",
subtext1 = "Ihr IT-Dienstleister in Crailsheim — egal ob zu Hause oder im Büro. Von Hard- & Software über Netzwerk & WLAN bis hin zu Webseiten & Webanwendungen: Wir lösen Ihre IT-Probleme persönlich und zum fairen Preis.",
subtext2 = "",
cta1Text = "Jetzt anfragen",
cta1Href = "#contact",
cta2Text = "Unsere Leistungen",
cta2Href = "#services",
bgImagePath = null,
badges = ["Docker", "Kubernetes", "Proxmox", "Hetzner Cloud", "Linux"],
}: HeroProps = {}) {
const words = useMemo(() => ["Professionell.", "DSGVO-konform.", "Zuverlässig.", "Skalierbar."], []);
const typed = useTypewriter(words);
@ -20,48 +43,60 @@ export default function Hero() {
<div className="absolute top-1/4 left-1/4 w-72 h-72 bg-orange-500/5 rounded-full blur-3xl animate-pulse-slow" />
<div className="absolute bottom-1/4 right-1/4 w-80 h-80 bg-blue-600/5 rounded-full blur-3xl animate-pulse-slow" />
{bgImagePath && (
<div
className="absolute inset-0 bg-cover bg-center opacity-20"
style={{ backgroundImage: `url(${process.env.NEXT_PUBLIC_SUPABASE_URL}/storage/v1/object/public/hero-bilder/${bgImagePath})` }}
/>
)}
<div className="relative z-10 max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 text-center">
{/* Tagline badge */}
<div className="inline-flex items-center gap-2 px-5 py-2 rounded-full border border-orange-500/40 bg-orange-500/10 text-orange-400 text-xs font-black tracking-[0.3em] uppercase mb-8">
<span className="w-1.5 h-1.5 bg-orange-400 rounded-full animate-pulse" />
Digital Denken. Lokal Handeln.
{eyebrowText}
</div>
{/* Headline */}
<h1 className="text-5xl sm:text-6xl lg:text-7xl font-black text-slate-900 dark:text-white leading-tight mb-6 tracking-tight">
Ihre IT-Infrastruktur.{" "}
{headline1}{" "}
<span className="text-gradient">
{typed}
<span className="animate-pulse">|</span>
</span>
<br />
Skalierbar. Zuverlässig.
</h1>
{/* Subheadline */}
{subtext1 && (
<p className="text-xl sm:text-2xl text-slate-600 dark:text-slate-400 max-w-3xl mx-auto mb-4 leading-relaxed">
Ihr IT-Dienstleister in <strong className="text-slate-700 dark:text-slate-300">Crailsheim</strong> &mdash; egal ob zu Hause oder im Büro. Von Hard- &amp; Software über Netzwerk &amp; WLAN bis hin zu Webseiten &amp; Webanwendungen: Wir lösen Ihre IT-Probleme persönlich und zum fairen Preis.
{subtext1}
</p>
)}
{subtext2 && (
<p className="text-xl sm:text-2xl text-slate-600 dark:text-slate-400 max-w-3xl mx-auto mb-4 leading-relaxed">
{subtext2}
</p>
)}
{/* Tech pills */}
<div className="flex flex-wrap items-center justify-center gap-3 mb-10">
{["Docker", "Kubernetes", "Proxmox", "Hetzner Cloud", "Linux"].map((tech) => (
{badges.map((badge) => (
<span
key={tech}
key={badge}
className="px-3 py-1 rounded-md bg-slate-100 dark:bg-gray-900 border border-slate-200 dark:border-gray-700 text-slate-700 dark:text-slate-300 text-sm font-mono"
>
{tech}
{badge}
</span>
))}
</div>
{/* CTAs */}
<div className="flex flex-col sm:flex-row items-center justify-center gap-4">
<a href="#contact" className="btn-primary w-full sm:w-auto px-8 py-4 text-lg">
Projekt anfragen
<a href={cta1Href} className="btn-primary w-full sm:w-auto px-8 py-4 text-lg">
{cta1Text}
</a>
<a href="#services" className="btn-secondary w-full sm:w-auto px-8 py-4 text-lg">
Services ansehen
<a href={cta2Href} className="btn-secondary w-full sm:w-auto px-8 py-4 text-lg">
{cta2Text}
</a>
</div>