247 lines
12 KiB
TypeScript
247 lines
12 KiB
TypeScript
"use client";
|
||
|
||
import { useState } from "react";
|
||
|
||
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: "+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";
|
||
|
||
export default function Contact() {
|
||
const [name, setName] = useState("");
|
||
const [email, setEmail] = useState("");
|
||
const [betreff, setBetreff] = useState("");
|
||
const [nachricht, setNachricht] = useState("");
|
||
const [status, setStatus] = useState<Status>("idle");
|
||
const [errorMsg, setErrorMsg] = useState("");
|
||
|
||
async function handleSubmit(e: React.FormEvent) {
|
||
e.preventDefault();
|
||
setStatus("loading");
|
||
setErrorMsg("");
|
||
|
||
try {
|
||
const res = await fetch("/api/contact", {
|
||
method: "POST",
|
||
headers: { "Content-Type": "application/json" },
|
||
body: JSON.stringify({ name, email, betreff, nachricht }),
|
||
});
|
||
const data = await res.json();
|
||
|
||
if (!res.ok || !data.ok) {
|
||
setErrorMsg(data.error ?? "Unbekannter Fehler");
|
||
setStatus("error");
|
||
} else {
|
||
setStatus("success");
|
||
}
|
||
} catch {
|
||
setErrorMsg("Netzwerkfehler – bitte versuchen Sie es erneut.");
|
||
setStatus("error");
|
||
}
|
||
}
|
||
|
||
return (
|
||
<section id="contact" className="py-24 px-4 sm:px-6 lg:px-8 relative">
|
||
<div className="absolute inset-0 bg-[#e8eef4] dark:bg-[#111925]" />
|
||
<div className="absolute inset-0 bg-grid opacity-40" />
|
||
<div className="absolute inset-0 bg-[radial-gradient(ellipse_at_center,rgba(249,115,22,0.07)_0%,transparent_70%)]" />
|
||
|
||
<div className="max-w-4xl mx-auto relative">
|
||
{/* Header */}
|
||
<div className="text-center mb-12">
|
||
<span className="text-orange-400 font-mono text-xs font-bold tracking-[0.25em] uppercase">
|
||
Kontakt
|
||
</span>
|
||
<h2 className="text-4xl sm:text-5xl font-black text-slate-900 dark:text-white mt-3 mb-4">
|
||
Projekt anfragen
|
||
</h2>
|
||
<p className="text-slate-600 dark:text-slate-400 text-lg max-w-xl mx-auto">
|
||
Sie haben ein IT-Projekt oder eine Frage? Schreiben Sie uns —
|
||
wir melden uns schnellstmöglich bei Ihnen.
|
||
</p>
|
||
</div>
|
||
|
||
<div className="bg-white dark:bg-gray-900 border border-slate-200 dark:border-gray-800 rounded-3xl p-8 sm:p-10">
|
||
{status === "success" ? (
|
||
<div className="flex flex-col items-center justify-center py-10 text-center">
|
||
<div className="w-16 h-16 rounded-full bg-orange-500/10 border border-orange-500/30 flex items-center justify-center mb-6">
|
||
<svg className="w-8 h-8 text-orange-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M5 13l4 4L19 7" />
|
||
</svg>
|
||
</div>
|
||
<h3 className="text-2xl font-bold text-slate-900 dark:text-white mb-2">Anfrage gesendet!</h3>
|
||
<p className="text-slate-600 dark:text-slate-400 mb-8">
|
||
Wir haben Ihre Nachricht erhalten und melden uns schnellstmöglich bei Ihnen.
|
||
</p>
|
||
<button
|
||
onClick={() => {
|
||
setStatus("idle");
|
||
setName("");
|
||
setEmail("");
|
||
setBetreff("");
|
||
setNachricht("");
|
||
}}
|
||
className="btn-secondary px-6 py-2"
|
||
>
|
||
Neue Anfrage
|
||
</button>
|
||
</div>
|
||
) : (
|
||
<form className="space-y-6" onSubmit={handleSubmit}>
|
||
<div className="grid sm:grid-cols-2 gap-6">
|
||
<div>
|
||
<label className="block text-sm font-medium text-slate-700 dark:text-slate-300 mb-2">Name</label>
|
||
<input
|
||
type="text"
|
||
required
|
||
value={name}
|
||
onChange={(e) => setName(e.target.value)}
|
||
placeholder="Max Mustermann"
|
||
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"
|
||
/>
|
||
</div>
|
||
<div>
|
||
<label className="block text-sm font-medium text-slate-700 dark:text-slate-300 mb-2">E-Mail</label>
|
||
<input
|
||
type="email"
|
||
required
|
||
value={email}
|
||
onChange={(e) => setEmail(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"
|
||
/>
|
||
</div>
|
||
</div>
|
||
|
||
<div>
|
||
<label className="block text-sm font-medium text-slate-300 mb-2">Betreff</label>
|
||
<select
|
||
required
|
||
value={betreff}
|
||
onChange={(e) => setBetreff(e.target.value)}
|
||
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 focus:outline-none focus:border-orange-500/60 focus:ring-1 focus:ring-orange-500/20 transition-colors appearance-none"
|
||
>
|
||
<option value="">Thema auswählen...</option>
|
||
<option value="hardware">Hard- & Software</option>
|
||
<option value="netzwerk">Netzwerk & WLAN</option>
|
||
<option value="backup">Datensicherung & Backup</option>
|
||
<option value="docker">Docker Installation</option>
|
||
<option value="fernwartung">Fernwartung & Support</option>
|
||
<option value="hetzner">Hetzner Cloud Infrastruktur</option>
|
||
<option value="beratung">IT-Beratung</option>
|
||
<option value="sicherheit">IT-Sicherheit & Datenschutz</option>
|
||
<option value="kubernetes">Kubernetes Setup</option>
|
||
<option value="proxmox">Proxmox Virtualisierung</option>
|
||
<option value="server">Server & Hosting</option>
|
||
<option value="webseiten">Webseiten & Webanwendungen</option>
|
||
<option value="other">Sonstiges</option>
|
||
</select>
|
||
</div>
|
||
|
||
<div>
|
||
<label className="block text-sm font-medium text-slate-300 mb-2">Nachricht</label>
|
||
<textarea
|
||
rows={5}
|
||
value={nachricht}
|
||
onChange={(e) => setNachricht(e.target.value)}
|
||
placeholder="Beschreiben Sie Ihr Projekt oder Ihre Anfrage..."
|
||
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 resize-none"
|
||
/>
|
||
</div>
|
||
|
||
{status === "error" && (
|
||
<div className="flex items-start gap-3 px-4 py-3 rounded-xl bg-red-500/10 border border-red-500/20 text-red-400 text-sm">
|
||
<svg className="w-5 h-5 mt-0.5 shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
|
||
</svg>
|
||
<span>
|
||
Nachricht konnte nicht gesendet werden. Bitte versuchen Sie es erneut oder
|
||
kontaktieren Sie uns direkt.
|
||
{errorMsg && <span className="block mt-1 text-red-400/70 text-xs">{errorMsg}</span>}
|
||
</span>
|
||
</div>
|
||
)}
|
||
|
||
<button
|
||
type="submit"
|
||
disabled={status === "loading"}
|
||
className="btn-primary w-full py-4 text-lg disabled:opacity-60 disabled:cursor-not-allowed flex items-center justify-center gap-2"
|
||
>
|
||
{status === "loading" ? (
|
||
<>
|
||
<svg className="w-5 h-5 animate-spin" fill="none" viewBox="0 0 24 24">
|
||
<circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4" />
|
||
<path className="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8v8z" />
|
||
</svg>
|
||
Wird gesendet...
|
||
</>
|
||
) : (
|
||
"Nachricht senden"
|
||
)}
|
||
</button>
|
||
</form>
|
||
)}
|
||
|
||
{/* Divider */}
|
||
<div className="flex items-center gap-4 my-8">
|
||
<div className="flex-1 h-px bg-slate-200 dark:bg-gray-800" />
|
||
<span className="text-slate-400 dark:text-slate-600 text-sm">oder direkt</span>
|
||
<div className="flex-1 h-px bg-slate-200 dark:bg-gray-800" />
|
||
</div>
|
||
|
||
{/* Contact info */}
|
||
<div className="flex flex-col sm:flex-row items-center justify-center gap-6">
|
||
{contactItems.map((item) => (
|
||
<a
|
||
key={item.label}
|
||
href={item.href}
|
||
data-source-element="kontakt-section"
|
||
className="flex items-center gap-3 text-slate-700 dark:text-slate-300 hover:text-orange-400 transition-colors duration-200 group"
|
||
>
|
||
<span className="w-10 h-10 rounded-lg bg-orange-500/10 border border-orange-500/20 flex items-center justify-center text-orange-400 group-hover:bg-orange-500/20 transition-colors duration-200">
|
||
{item.icon}
|
||
</span>
|
||
<div>
|
||
<div className="text-xs text-slate-500">{item.label}</div>
|
||
<div className="font-medium">{item.value}</div>
|
||
</div>
|
||
</a>
|
||
))}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
);
|
||
}
|