ISSUE 4: add recaptcha before submission in sign up form and email validation

This commit is contained in:
Ayobami
2025-07-01 11:00:18 +01:00
parent 05cd9c8318
commit e51d369ba1
+73 -38
View File
@@ -8,6 +8,7 @@ import { useSignUpContext } from "./signUpContext";
import { callCustomAPI, oauthLoginApi } from "@/utils/callCustomAPI"; import { callCustomAPI, oauthLoginApi } from "@/utils/callCustomAPI";
import { LoadingButton } from "@/components/frontend"; import { LoadingButton } from "@/components/frontend";
import TLDs from "@/assets/json/email-tlds.json"; import TLDs from "@/assets/json/email-tlds.json";
import ReCAPTCHA from "react-google-recaptcha";
const SignUpForm = () => { const SignUpForm = () => {
const navigate = useNavigate(); const navigate = useNavigate();
@@ -15,9 +16,18 @@ const SignUpForm = () => {
const role = signUpData.role; const role = signUpData.role;
const schema = yup.object({ const schema = yup.object({
email: yup email: yup
.string(), .string()
.required("Email is required")
.email("Invalid email address")
.test("tld-check", "Invalid email TLD", (value) => {
if (!value) return false;
const tld = value.split(".").pop();
return TLDs.includes(tld);
}),
}); });
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const [recaptchaValue, setRecaptchaValue] = useState(null);
const [recaptchaError, setRecaptchaError] = useState("");
const { const {
register, register,
@@ -32,12 +42,20 @@ const SignUpForm = () => {
}); });
const onSubmit = async (data) => { const onSubmit = async (data) => {
setRecaptchaError("");
if (!recaptchaValue) {
setRecaptchaError("Please complete the recaptcha.");
return;
}
setLoading(true); setLoading(true);
try { try {
const result = await callCustomAPI("email-exist", "post", { email: data.email }, ""); const result = await callCustomAPI(
"email-exist",
"post",
{ email: data.email },
""
);
if (result.error || result.exist) throw new Error("User already exists"); if (result.error || result.exist) throw new Error("User already exists");
dispatch({ type: "SET_EMAIL", payload: data.email }); dispatch({ type: "SET_EMAIL", payload: data.email });
navigate("/signup/details" + "?role=" + role); navigate("/signup/details" + "?role=" + role);
} catch (err) { } catch (err) {
@@ -61,77 +79,87 @@ const SignUpForm = () => {
window.open(result.data, "_self"); window.open(result.data, "_self");
}; };
if (!signUpData.role) return <Navigate to={"/signup/select-role"} />; if (!signUpData.role) return <Navigate to={"/signup/select-role"} />;
return ( return (
<> <>
<section className="flex w-full flex-col items-center justify-center bg-white md:w-1/2"> <section className='flex w-full flex-col items-center justify-center bg-white md:w-1/2'>
<form <form
className="flex w-full max-w-md flex-col px-6" className='flex w-full max-w-md flex-col px-6'
onSubmit={handleSubmit(onSubmit)} onSubmit={handleSubmit(onSubmit)}
autoComplete="off" autoComplete='off'
> >
<h1 className="mb-8 text-center text-3xl font-semibold md:text-5xl md:font-bold">{role == "host" ? "Become a host" : "Sign up"}</h1> <h1 className='mb-8 text-center text-3xl font-semibold md:text-5xl md:font-bold'>
{role == "host" ? "Become a host" : "Sign up"}
</h1>
<input <input
autoComplete="off" autoComplete='off'
{...register("email")} {...register("email")}
type="text" type='text'
className="mb-8 resize-none rounded-sm border-2 bg-transparent p-2 px-4 focus:outline-none active:outline-none" className='mb-8 resize-none rounded-sm border-2 bg-transparent p-2 px-4 focus:outline-none active:outline-none'
placeholder="Email" placeholder='Email'
/> />
{Object.entries(errors).length > 0 ? ( {Object.entries(errors).length > 0 ? (
<p className="error-vibrate my-3 rounded-md border border-[#C42945] bg-white py-2 px-3 text-center text-sm normal-case text-[#C42945]">{Object.values(errors)[0].message}</p> <p className='error-vibrate my-3 rounded-md border border-[#C42945] bg-white px-3 py-2 text-center text-sm normal-case text-[#C42945]'>
{Object.values(errors)[0].message}
</p>
) : ( ) : (
<></> <></>
)} )}
<div className='mb-4 flex justify-center'>
<ReCAPTCHA
sitekey={import.meta.env.VITE_RECAPTCHA_SITE_KEY}
onChange={(val) => {
setRecaptchaValue(val);
setRecaptchaError("");
}}
/>
</div>
{recaptchaError && (
<p className='mb-2 text-center text-xs italic text-red-500'>
{recaptchaError}
</p>
)}
<LoadingButton <LoadingButton
loading={loading} loading={loading}
type="submit" type='submit'
className={`login-btn-gradient rounded tracking-wide text-white outline-none focus:outline-none ${loading ? "py-1" : "py-2"}`} className={`login-btn-gradient rounded tracking-wide text-white outline-none focus:outline-none ${
loading ? "py-1" : "py-2"
}`}
disabled={!recaptchaValue}
> >
Continue Continue
</LoadingButton> </LoadingButton>
</form> </form>
<div className="hr my-6 text-center">OR</div> <div className='hr my-6 text-center'>OR</div>
<div className="oauth flex w-full max-w-md flex-col gap-4 px-6 text-[#344054]"> <div className='oauth flex w-full max-w-md flex-col gap-4 px-6 text-[#344054]'>
<button <button
onClick={() => handleGoogleLogin()} onClick={() => handleGoogleLogin()}
className="flex items-center justify-center gap-2 border-2 py-[10px]" className='flex items-center justify-center gap-2 border-2 py-[10px]'
> >
<img <img src='/google-icon.png' className='h-[18px] w-[18px]' />
src="/google-icon.png"
className="h-[18px] w-[18px]"
/>
<span>Sign Up With Google</span> <span>Sign Up With Google</span>
</button> </button>
<button <button
onClick={() => handleFacebookLogin()} onClick={() => handleFacebookLogin()}
className="flex items-center justify-center gap-2 border-2 py-[10px]" className='flex items-center justify-center gap-2 border-2 py-[10px]'
> >
<img <img src='/facebook-icon.png' className='h-[16px] w-[16px]' />
src="/facebook-icon.png"
className="h-[16px] w-[16px]"
/>
<span>Sign Up With Facebook</span> <span>Sign Up With Facebook</span>
</button> </button>
<button <button
onClick={() => handleAppleLogin()} onClick={() => handleAppleLogin()}
className="flex items-center justify-center gap-2 border-2 py-[10px]" className='flex items-center justify-center gap-2 border-2 py-[10px]'
> >
<img <img src='/apple-icon.png' className='h-[16px] w-[16px]' />
src="/apple-icon.png"
className="h-[16px] w-[16px]"
/>
<span>Sign Up With Apple</span> <span>Sign Up With Apple</span>
</button> </button>
<div> <div>
<h3 className="text-center text-sm normal-case text-gray-800"> <h3 className='text-center text-sm normal-case text-gray-800'>
Already have an account?{" "} Already have an account?{" "}
<Link <Link
to={"/login" + "?role=" + role} to={"/login" + "?role=" + role}
className="my-text-gradient mb-8 self-end text-sm font-semibold" className='my-text-gradient mb-8 self-end text-sm font-semibold'
> >
Log In Log In
</Link>{" "} </Link>{" "}
@@ -140,8 +168,15 @@ const SignUpForm = () => {
</div> </div>
</section> </section>
<section <section
style={{ backgroundImage: `url(${role == "host" ? "/jumbotron1.jpg" : "/sign-up-bg.jpg"})`, backgroundSize: "cover", backgroundRepeat: "no-repeat", backgroundPosition: "center" }} style={{
className="hidden w-1/2 md:block bg-contain" backgroundImage: `url(${
role == "host" ? "/jumbotron1.jpg" : "/sign-up-bg.jpg"
})`,
backgroundSize: "cover",
backgroundRepeat: "no-repeat",
backgroundPosition: "center",
}}
className='hidden w-1/2 bg-contain md:block'
></section> ></section>
</> </>
); );