Files
sportbox-reutte-2026-01-30-v1/components/ui/ContactForm.tsx

172 lines
5.2 KiB
TypeScript

"use client";
import { useState } from "react";
import { Button } from "./Button";
interface FormState {
name: string;
email: string;
phone: string;
message: string;
}
interface FormErrors {
name?: string;
email?: string;
message?: string;
}
export function ContactForm() {
const [form, setForm] = useState<FormState>({
name: "",
email: "",
phone: "",
message: "",
});
const [errors, setErrors] = useState<FormErrors>({});
const [submitted, setSubmitted] = useState(false);
function validate(): FormErrors {
const newErrors: FormErrors = {};
if (!form.name.trim()) newErrors.name = "Bitte geben Sie Ihren Namen ein.";
if (!form.email.trim()) {
newErrors.email = "Bitte geben Sie Ihre E-Mail-Adresse ein.";
} else if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(form.email)) {
newErrors.email = "Bitte geben Sie eine gültige E-Mail-Adresse ein.";
}
if (!form.message.trim())
newErrors.message = "Bitte geben Sie eine Nachricht ein.";
return newErrors;
}
function handleSubmit(e: React.FormEvent) {
e.preventDefault();
const newErrors = validate();
setErrors(newErrors);
if (Object.keys(newErrors).length === 0) {
setSubmitted(true);
}
}
function handleChange(
e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>
) {
const { name, value } = e.target;
setForm((prev) => ({ ...prev, [name]: value }));
if (errors[name as keyof FormErrors]) {
setErrors((prev) => ({ ...prev, [name]: undefined }));
}
}
if (submitted) {
return (
<div
className="border border-success p-8 text-center"
style={{ borderRadius: "var(--radius-md)" }}
role="status"
aria-live="polite"
>
<p className="text-lg font-bold mb-2">Vielen Dank für Ihre Nachricht!</p>
<p className="text-muted">
Wir werden uns so schnell wie möglich bei Ihnen melden.
</p>
</div>
);
}
return (
<form onSubmit={handleSubmit} noValidate className="space-y-6">
<div>
<label htmlFor="contact-name" className="block text-sm font-medium mb-1.5">
Name <span aria-hidden="true">*</span>
</label>
<input
id="contact-name"
name="name"
type="text"
autoComplete="name"
aria-required="true"
aria-invalid={!!errors.name}
aria-describedby={errors.name ? "name-error" : undefined}
value={form.name}
onChange={handleChange}
className="w-full border border-border px-4 py-3 text-sm bg-background text-foreground transition-colors focus:border-primary focus:outline-none"
style={{ borderRadius: "var(--radius-sm)" }}
/>
{errors.name && (
<p id="name-error" className="mt-1.5 text-sm text-error" role="alert">
{errors.name}
</p>
)}
</div>
<div>
<label htmlFor="contact-email" className="block text-sm font-medium mb-1.5">
E-Mail <span aria-hidden="true">*</span>
</label>
<input
id="contact-email"
name="email"
type="email"
autoComplete="email"
aria-required="true"
aria-invalid={!!errors.email}
aria-describedby={errors.email ? "email-error" : undefined}
value={form.email}
onChange={handleChange}
className="w-full border border-border px-4 py-3 text-sm bg-background text-foreground transition-colors focus:border-primary focus:outline-none"
style={{ borderRadius: "var(--radius-sm)" }}
/>
{errors.email && (
<p id="email-error" className="mt-1.5 text-sm text-error" role="alert">
{errors.email}
</p>
)}
</div>
<div>
<label htmlFor="contact-phone" className="block text-sm font-medium mb-1.5">
Telefon
</label>
<input
id="contact-phone"
name="phone"
type="tel"
autoComplete="tel"
value={form.phone}
onChange={handleChange}
className="w-full border border-border px-4 py-3 text-sm bg-background text-foreground transition-colors focus:border-primary focus:outline-none"
style={{ borderRadius: "var(--radius-sm)" }}
/>
</div>
<div>
<label htmlFor="contact-message" className="block text-sm font-medium mb-1.5">
Nachricht <span aria-hidden="true">*</span>
</label>
<textarea
id="contact-message"
name="message"
rows={5}
aria-required="true"
aria-invalid={!!errors.message}
aria-describedby={errors.message ? "message-error" : undefined}
value={form.message}
onChange={handleChange}
className="w-full border border-border px-4 py-3 text-sm bg-background text-foreground transition-colors focus:border-primary focus:outline-none resize-y"
style={{ borderRadius: "var(--radius-sm)" }}
/>
{errors.message && (
<p id="message-error" className="mt-1.5 text-sm text-error" role="alert">
{errors.message}
</p>
)}
</div>
<Button type="submit" variant="primary">
Nachricht senden
</Button>
</form>
);
}