/** * Author: Simon-Pierre Boucher * Contact: contact@spboucher.ai * Project: Hilmacorp.ai — Web Platform * File: components/ContactForm.tsx * Description: Client-side contact form with inline validation, posting to /api/contact (light theme) */ "use client"; import { useState, type FormEvent } from "react"; interface FormStrings { name: string; namePlaceholder: string; organization: string; organizationPlaceholder: string; email: string; emailPlaceholder: string; message: string; messagePlaceholder: string; submit: string; sending: string; success: string; error: string; errName: string; errEmail: string; errMessage: string; } type Status = "idle" | "sending" | "success" | "error"; const EMAIL_RE = /^[^\s@]+@[^\s@]+\.[^\s@]{2,}$/; export default function ContactForm({ strings }: { strings: FormStrings }) { const [status, setStatus] = useState("idle"); const [errors, setErrors] = useState>({}); async function handleSubmit(event: FormEvent) { event.preventDefault(); const form = event.currentTarget; const payload = { name: (form.elements.namedItem("name") as HTMLInputElement).value.trim(), organization: (form.elements.namedItem("organization") as HTMLInputElement).value.trim(), email: (form.elements.namedItem("email") as HTMLInputElement).value.trim(), message: (form.elements.namedItem("message") as HTMLTextAreaElement).value.trim(), }; const nextErrors: Record = {}; if (payload.name.length < 2) nextErrors.name = strings.errName; if (!EMAIL_RE.test(payload.email)) nextErrors.email = strings.errEmail; if (payload.message.length < 10) nextErrors.message = strings.errMessage; setErrors(nextErrors); if (Object.keys(nextErrors).length > 0) return; setStatus("sending"); try { const response = await fetch("/api/contact", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(payload), }); if (!response.ok) throw new Error(`HTTP ${response.status}`); setStatus("success"); form.reset(); } catch { setStatus("error"); } } const fieldClass = "w-full rounded-xl border border-line-strong bg-card px-4 py-3 text-sm text-ink placeholder:text-ink-faint focus:border-accent focus:outline-none"; return (
{errors.name ?

{errors.name}

: null}
{errors.email ?

{errors.email}

: null}