// Aanmeldformulier voor CV De Goeie Orde const { useState: useStateF, useRef: useRefF, useEffect: useEffectF } = React; const TURNSTILE_SITE_KEY = "0x4AAAAAADXR2JUtqpoemNEn"; function FormSection({ t }) { const [data, setData] = useStateF({ voornaam: "", achternaam: "", woonplaats: "", geboortedatum: "", email: "", telefoon: "", status: "", reglement: false, }); const [errors, setErrors] = useStateF({}); const [submitted, setSubmitted] = useStateF(false); const [submitting, setSubmitting] = useStateF(false); const [serverError, setServerError] = useStateF(""); const [hasSig, setHasSig] = useStateF(false); const [turnstileToken, setTurnstileToken] = useStateF(""); const sigRef = useRefF(null); const turnstileRef = useRefF(null); const turnstileIdRef = useRefF(null); const update = (k) => (v) => { setData((d) => ({ ...d, [k]: v })); setErrors((e) => ({ ...e, [k]: undefined })); }; // ---- Turnstile explicit rendering ---- // Poll for window.turnstile since the async script may load after this effect runs. useEffectF(() => { let timer = null; const tryRender = () => { if (turnstileIdRef.current !== null) return; if (!window.turnstile || !turnstileRef.current) { timer = setTimeout(tryRender, 50); return; } turnstileIdRef.current = window.turnstile.render(turnstileRef.current, { sitekey: TURNSTILE_SITE_KEY, callback: (token) => setTurnstileToken(token), "expired-callback": () => setTurnstileToken(""), }); }; tryRender(); return () => { clearTimeout(timer); if (turnstileIdRef.current !== null && window.turnstile) { window.turnstile.remove(turnstileIdRef.current); } turnstileIdRef.current = null; }; }, []); // ---- signature pad ---- useEffectF(() => { const canvas = sigRef.current; if (!canvas) return; const ctx = canvas.getContext("2d"); const dpr = window.devicePixelRatio || 1; const resize = () => { const rect = canvas.getBoundingClientRect(); canvas.width = rect.width * dpr; canvas.height = rect.height * dpr; ctx.scale(dpr, dpr); ctx.lineCap = "round"; ctx.lineJoin = "round"; ctx.lineWidth = 2.2; ctx.strokeStyle = "#2b1810"; }; resize(); window.addEventListener("resize", resize); let drawing = false; let last = null; const getPos = (ev) => { const rect = canvas.getBoundingClientRect(); const touch = ev.touches?.[0]; return { x: (touch ? touch.clientX : ev.clientX) - rect.left, y: (touch ? touch.clientY : ev.clientY) - rect.top, }; }; const start = (ev) => { ev.preventDefault(); drawing = true; last = getPos(ev); setHasSig(true); }; const move = (ev) => { if (!drawing) return; ev.preventDefault(); const p = getPos(ev); ctx.beginPath(); ctx.moveTo(last.x, last.y); ctx.lineTo(p.x, p.y); ctx.stroke(); last = p; }; const end = () => { drawing = false; }; canvas.addEventListener("mousedown", start); canvas.addEventListener("mousemove", move); window.addEventListener("mouseup", end); canvas.addEventListener("touchstart", start, { passive: false }); canvas.addEventListener("touchmove", move, { passive: false }); canvas.addEventListener("touchend", end); return () => { window.removeEventListener("resize", resize); canvas.removeEventListener("mousedown", start); canvas.removeEventListener("mousemove", move); window.removeEventListener("mouseup", end); canvas.removeEventListener("touchstart", start); canvas.removeEventListener("touchmove", move); canvas.removeEventListener("touchend", end); }; }, []); const clearSig = () => { const c = sigRef.current; c.getContext("2d").clearRect(0, 0, c.width, c.height); setHasSig(false); }; const validate = () => { const e = {}; if (!data.voornaam.trim()) e.voornaam = t.form.errReq; if (!data.achternaam.trim()) e.achternaam = t.form.errReq; if (!data.woonplaats.trim()) e.woonplaats = t.form.errReq; if (!data.geboortedatum) { e.geboortedatum = t.form.errAge; } else { const dob = new Date(data.geboortedatum); const turns18 = new Date(dob.getFullYear() + 18, dob.getMonth(), dob.getDate()); if (turns18 > new Date()) e.geboortedatum = t.form.errMinAge; } if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(data.email.trim())) e.email = t.form.errEmail; if (!data.telefoon.trim()) e.telefoon = t.form.errReq; if (!data.status) e.status = t.form.errReq; if (!hasSig) e.signature = t.form.errSig; if (!data.reglement) e.reglement = t.form.errAccept; if (!turnstileToken) e.turnstile = t.form.errTurnstile; setErrors(e); return Object.keys(e).length === 0; }; const submit = async (ev) => { ev.preventDefault(); if (!validate()) return; setSubmitting(true); setServerError(""); try { const res = await fetch("submit.php", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ voornaam: data.voornaam, achternaam: data.achternaam, woonplaats: data.woonplaats, geboortedatum: data.geboortedatum, email: data.email, telefoon: data.telefoon, status: data.status, signature: sigRef.current?.toDataURL("image/png") ?? "", "cf-turnstile-response": turnstileToken, }), }); const result = await res.json(); if (result.success) { setSubmitted(true); } else { setServerError(result.error || t.form.errServer); if (window.turnstile) window.turnstile.reset(turnstileIdRef.current); setTurnstileToken(""); } } catch { setServerError(t.form.errServer); if (window.turnstile) window.turnstile.reset(turnstileIdRef.current); setTurnstileToken(""); } finally { setSubmitting(false); } }; const reset = () => { setData({ voornaam: "", achternaam: "", woonplaats: "", geboortedatum: "", email: "", telefoon: "", status: "", reglement: false }); clearSig(); setErrors({}); setSubmitted(false); setServerError(""); setTurnstileToken(""); if (window.turnstile) window.turnstile.reset(turnstileIdRef.current); }; return (
{t.carnaval.formLead}