"use client"; import { useRef, useState, type FormEvent } from "react"; type Status = { kind: "idle" } | { kind: "sending" } | { kind: "sent" } | { kind: "error"; message: string }; type Fields = Record; const FIELD_IDS = ["name", "email", "phone", "availability", "message", "privacy"] as const; /** * Anfrage fuer ein unverbindliches Erstgespraech (POST /api/contact). Die * Pruefung passiert auf dem Server; Fehler erscheinen am jeweiligen Feld, * der Fokus springt auf das erste fehlerhafte. Das Feld "website" ist eine * fuer Menschen unsichtbare Falle fuer Bots. */ export function ContactSection() { const formRef = useRef(null); const [status, setStatus] = useState({ kind: "idle" }); const [errors, setErrors] = useState({}); async function submit(event: FormEvent) { event.preventDefault(); const form = new FormData(event.currentTarget); const text = (key: string) => String(form.get(key) ?? ""); setStatus({ kind: "sending" }); setErrors({}); try { const response = await fetch("/api/contact", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ name: text("name"), email: text("email"), phone: text("phone"), availability: text("availability"), message: text("message"), privacy: form.get("privacy") === "on", website: text("website"), }), }); const result = (await response.json().catch(() => null)) as { success: boolean; error?: { message: string; fields?: Fields } } | null; if (response.ok && result?.success) { setStatus({ kind: "sent" }); formRef.current?.reset(); return; } const fields = result?.error?.fields ?? {}; setErrors(fields); setStatus({ kind: "error", message: result?.error?.message ?? "Senden hat nicht geklappt. Bitte versuche es erneut." }); const first = FIELD_IDS.find((id) => fields[id]); if (first) requestAnimationFrame(() => document.getElementById(`ct-${first}`)?.focus()); } catch { setStatus({ kind: "error", message: "Keine Verbindung. Bitte prüfe dein Internet und versuche es erneut." }); } } const fieldProps = (id: (typeof FIELD_IDS)[number]) => ({ id: `ct-${id}`, name: id, "aria-invalid": errors[id] ? true : undefined, "aria-describedby": errors[id] ? `ct-${id}-error` : undefined, }); const error = (id: string) => errors[id] ? (

{errors[id]}

) : null; return (

Erstgespräch

Du bist dir noch unsicher, ob das Modelcamp zu dir passt? Lass uns unverbindlich sprechen. Schreib uns kurz, wann du erreichbar bist, wir melden uns bei dir.

{status.kind === "sent" ? (

Danke, deine Anfrage ist angekommen.

Wir melden uns in den nächsten Tagen bei dir.

) : (
{error("name")}
{error("email")}
{error("phone")}
{error("availability")}