feat: Hero, About, Contact components accept CMS props
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
b9aa6a0a54
commit
3eeea27b92
|
|
@ -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 (
|
return (
|
||||||
<section id="about" className="py-24 px-4 sm:px-6 lg:px-8 relative">
|
<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%)]" />
|
<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 */}
|
{/* Left: Text */}
|
||||||
<div>
|
<div>
|
||||||
<span className="text-orange-400 font-mono text-xs font-bold tracking-[0.25em] uppercase">
|
<span className="text-orange-400 font-mono text-xs font-bold tracking-[0.25em] uppercase">
|
||||||
Über uns
|
{eyebrowText}
|
||||||
</span>
|
</span>
|
||||||
<h2 className="text-4xl sm:text-5xl font-black text-slate-900 dark:text-white mt-3 mb-6 leading-tight">
|
<h2 className="text-4xl sm:text-5xl font-black text-slate-900 dark:text-white mt-3 mb-6 leading-tight">
|
||||||
IT-Expertise,{" "}
|
IT-Expertise,{" "}
|
||||||
<span className="text-gradient">die Sie weiterbringt</span>
|
<span className="text-gradient">die Sie weiterbringt</span>
|
||||||
</h2>
|
</h2>
|
||||||
<p className="text-slate-600 dark:text-slate-400 text-lg leading-relaxed mb-6">
|
<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> —
|
{absatz1}
|
||||||
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>.
|
|
||||||
</p>
|
</p>
|
||||||
<p className="text-slate-600 dark:text-slate-400 text-lg leading-relaxed mb-8">
|
<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
|
{absatz2}
|
||||||
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.
|
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
{/* Tagline callout */}
|
{/* Tagline callout */}
|
||||||
|
|
@ -76,6 +82,17 @@ export default function About() {
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</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>
|
</div>
|
||||||
|
|
||||||
{/* Right: Process steps */}
|
{/* Right: Process steps */}
|
||||||
|
|
|
||||||
|
|
@ -2,45 +2,61 @@
|
||||||
|
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
|
|
||||||
const contactItems = [
|
interface OeffnungsZeit { tag: string; von: string; bis: string }
|
||||||
{
|
|
||||||
icon: (
|
interface ContactProps {
|
||||||
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
telefon?: string;
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M3 5a2 2 0 012-2h3.28a1 1 0 01.948.684l1.498 4.493a1 1 0 01-.502 1.21l-2.257 1.13a11.042 11.042 0 005.516 5.516l1.13-2.257a1 1 0 011.21-.502l4.493 1.498a1 1 0 01.684.949V19a2 2 0 01-2 2h-1C9.716 21 3 14.284 3 6V5z" />
|
email?: string;
|
||||||
</svg>
|
adresseZeile1?: string;
|
||||||
),
|
adresseZeile2?: string;
|
||||||
label: "Telefon",
|
oeffnungszeiten?: OeffnungsZeit[];
|
||||||
value: "+49 171 9345193",
|
}
|
||||||
href: "tel:+4917193451093",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
icon: (
|
|
||||||
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M3 8l7.89 5.26a2 2 0 002.22 0L21 8M5 19h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z" />
|
|
||||||
</svg>
|
|
||||||
),
|
|
||||||
label: "E-Mail",
|
|
||||||
value: "kontakt@mbo-tech-it.de",
|
|
||||||
href: "mailto:kontakt@mbo-tech-it.de",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
icon: (
|
|
||||||
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M17.657 16.657L13.414 20.9a1.998 1.998 0 01-2.827 0l-4.244-4.243a8 8 0 1111.314 0z" />
|
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 11a3 3 0 11-6 0 3 3 0 016 0z" />
|
|
||||||
</svg>
|
|
||||||
),
|
|
||||||
label: "Standort",
|
|
||||||
value: "Crailsheim, 74564",
|
|
||||||
href: "https://maps.google.com/?q=Mörikestr.+2,+74564+Crailsheim",
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
type Status = "idle" | "loading" | "success" | "error";
|
type Status = "idle" | "loading" | "success" | "error";
|
||||||
|
|
||||||
export default function Contact() {
|
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: (
|
||||||
|
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M3 5a2 2 0 012-2h3.28a1 1 0 01.948.684l1.498 4.493a1 1 0 01-.502 1.21l-2.257 1.13a11.042 11.042 0 005.516 5.516l1.13-2.257a1 1 0 011.21-.502l4.493 1.498a1 1 0 01.684.949V19a2 2 0 01-2 2h-1C9.716 21 3 14.284 3 6V5z" />
|
||||||
|
</svg>
|
||||||
|
),
|
||||||
|
label: "Telefon",
|
||||||
|
value: telefon,
|
||||||
|
href: `tel:${telefon.replace(/\s/g, "")}`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: (
|
||||||
|
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M3 8l7.89 5.26a2 2 0 002.22 0L21 8M5 19h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z" />
|
||||||
|
</svg>
|
||||||
|
),
|
||||||
|
label: "E-Mail",
|
||||||
|
value: email,
|
||||||
|
href: `mailto:${email}`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: (
|
||||||
|
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M17.657 16.657L13.414 20.9a1.998 1.998 0 01-2.827 0l-4.244-4.243a8 8 0 1111.314 0z" />
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 11a3 3 0 11-6 0 3 3 0 016 0z" />
|
||||||
|
</svg>
|
||||||
|
),
|
||||||
|
label: "Standort",
|
||||||
|
value: `${adresseZeile1}, ${adresseZeile2}`,
|
||||||
|
href: `https://maps.google.com/?q=${encodeURIComponent(adresseZeile1 + " " + adresseZeile2)}`,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
const [name, setName] = useState("");
|
const [name, setName] = useState("");
|
||||||
const [email, setEmail] = useState("");
|
const [emailInput, setEmailInput] = useState("");
|
||||||
const [betreff, setBetreff] = useState("");
|
const [betreff, setBetreff] = useState("");
|
||||||
const [nachricht, setNachricht] = useState("");
|
const [nachricht, setNachricht] = useState("");
|
||||||
const [status, setStatus] = useState<Status>("idle");
|
const [status, setStatus] = useState<Status>("idle");
|
||||||
|
|
@ -55,7 +71,7 @@ export default function Contact() {
|
||||||
const res = await fetch("/api/contact", {
|
const res = await fetch("/api/contact", {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: { "Content-Type": "application/json" },
|
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();
|
const data = await res.json();
|
||||||
|
|
||||||
|
|
@ -108,7 +124,7 @@ export default function Contact() {
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setStatus("idle");
|
setStatus("idle");
|
||||||
setName("");
|
setName("");
|
||||||
setEmail("");
|
setEmailInput("");
|
||||||
setBetreff("");
|
setBetreff("");
|
||||||
setNachricht("");
|
setNachricht("");
|
||||||
}}
|
}}
|
||||||
|
|
@ -136,8 +152,8 @@ export default function Contact() {
|
||||||
<input
|
<input
|
||||||
type="email"
|
type="email"
|
||||||
required
|
required
|
||||||
value={email}
|
value={emailInput}
|
||||||
onChange={(e) => setEmail(e.target.value)}
|
onChange={(e) => setEmailInput(e.target.value)}
|
||||||
placeholder="max@beispiel.de"
|
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"
|
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>
|
</a>
|
||||||
))}
|
))}
|
||||||
</div>
|
</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>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,33 @@
|
||||||
// components/Hero.tsx
|
|
||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useMemo } from "react";
|
import { useMemo } from "react";
|
||||||
import { useTypewriter } from "@/hooks/useTypewriter";
|
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 words = useMemo(() => ["Professionell.", "DSGVO-konform.", "Zuverlässig.", "Skalierbar."], []);
|
||||||
const typed = useTypewriter(words);
|
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 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" />
|
<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">
|
<div className="relative z-10 max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 text-center">
|
||||||
{/* Tagline badge */}
|
{/* 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">
|
<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" />
|
<span className="w-1.5 h-1.5 bg-orange-400 rounded-full animate-pulse" />
|
||||||
Digital Denken. Lokal Handeln.
|
{eyebrowText}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Headline */}
|
{/* 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">
|
<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">
|
<span className="text-gradient">
|
||||||
{typed}
|
{typed}
|
||||||
<span className="animate-pulse">|</span>
|
<span className="animate-pulse">|</span>
|
||||||
</span>
|
</span>
|
||||||
<br />
|
|
||||||
Skalierbar. Zuverlässig.
|
|
||||||
</h1>
|
</h1>
|
||||||
|
|
||||||
{/* Subheadline */}
|
{/* Subheadline */}
|
||||||
<p className="text-xl sm:text-2xl text-slate-600 dark:text-slate-400 max-w-3xl mx-auto mb-4 leading-relaxed">
|
{subtext1 && (
|
||||||
Ihr IT-Dienstleister in <strong className="text-slate-700 dark:text-slate-300">Crailsheim</strong> — 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.
|
<p className="text-xl sm:text-2xl text-slate-600 dark:text-slate-400 max-w-3xl mx-auto mb-4 leading-relaxed">
|
||||||
</p>
|
{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 */}
|
{/* Tech pills */}
|
||||||
<div className="flex flex-wrap items-center justify-center gap-3 mb-10">
|
<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
|
<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"
|
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>
|
</span>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* CTAs */}
|
{/* CTAs */}
|
||||||
<div className="flex flex-col sm:flex-row items-center justify-center gap-4">
|
<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">
|
<a href={cta1Href} className="btn-primary w-full sm:w-auto px-8 py-4 text-lg">
|
||||||
Projekt anfragen
|
{cta1Text}
|
||||||
</a>
|
</a>
|
||||||
<a href="#services" className="btn-secondary w-full sm:w-auto px-8 py-4 text-lg">
|
<a href={cta2Href} className="btn-secondary w-full sm:w-auto px-8 py-4 text-lg">
|
||||||
Services ansehen
|
{cta2Text}
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue