From 29c96f45ab037dc1222d92ec3becb376f6200363 Mon Sep 17 00:00:00 2001 From: Pritesh Date: Wed, 3 Jun 2026 04:19:55 +0530 Subject: [PATCH] feat(lead-gen): inline math captcha field Co-Authored-By: Claude Opus 4.8 (1M context) --- ui/src/components/lead-forms/MathCaptcha.tsx | 45 ++++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 ui/src/components/lead-forms/MathCaptcha.tsx diff --git a/ui/src/components/lead-forms/MathCaptcha.tsx b/ui/src/components/lead-forms/MathCaptcha.tsx new file mode 100644 index 00000000..3db73cc6 --- /dev/null +++ b/ui/src/components/lead-forms/MathCaptcha.tsx @@ -0,0 +1,45 @@ +"use client"; + +import { useEffect, useState } from "react"; + +import { Input } from "@/components/ui/input"; +import { Label } from "@/components/ui/label"; + +interface MathCaptchaProps { + // Called whenever validity changes, so the parent can enable/disable submit. + onValidChange: (valid: boolean) => void; + id?: string; +} + +// Dead-simple anti-spam: "What is X + Y?". Generated client-side on mount. +// Math.random is allowed in browser runtime (this is not a workflow script). +export function MathCaptcha({ onValidChange, id = "math-captcha" }: MathCaptchaProps) { + const [a, setA] = useState(0); + const [b, setB] = useState(0); + const [answer, setAnswer] = useState(""); + + useEffect(() => { + setA(Math.floor(Math.random() * 8) + 1); + setB(Math.floor(Math.random() * 8) + 1); + }, []); + + useEffect(() => { + onValidChange(answer.trim() !== "" && parseInt(answer, 10) === a + b); + }, [answer, a, b, onValidChange]); + + return ( +
+ + setAnswer(e.target.value)} + placeholder="Answer" + className="w-32" + /> +
+ ); +}