feat(contact-us): Add hCaptcha verification to contact form

- Install @hcaptcha/react-hcaptcha dependency for CAPTCHA protection
- Add hCaptcha component integration with site key configuration
- Implement captcha token state management with verification callbacks
- Add token validation before form submission with user feedback
- Reset captcha after successful form submission
- Include captcha in form submission payload as hcaptcha_token
- Update Next.js to version 15.5.7 for compatibility
- Enhance form security by requiring CAPTCHA verification before contact submission
This commit is contained in:
AmirReza Jamali
2025-12-08 16:56:14 +03:30
parent 1204b2d3db
commit 2182386f9c
3 changed files with 102 additions and 43 deletions
+30 -2
View File
@@ -1,6 +1,7 @@
"use client";
import api from "@/service/api";
import { useState } from "react";
import HCaptcha from "@hcaptcha/react-hcaptcha";
import { useRef, useState } from "react";
import { useForm } from "react-hook-form";
import { toast } from "react-toastify";
import InputGroup from "../_components/InputGroup";
@@ -22,18 +23,37 @@ const ContactUsForm = ({
columnsPerRow = 1,
}: IProps) => {
const [isLoading, setIsLoading] = useState(false);
const [hcaptchaToken, setHcaptchaToken] = useState<string | null>(null);
const captchaRef = useRef<HCaptcha>(null);
const {
register,
handleSubmit,
formState: { errors },
reset,
} = useForm<TContactUsForm>();
const onCaptchaVerify = (token: string) => {
setHcaptchaToken(token);
};
const onCaptchaExpire = () => {
setHcaptchaToken(null);
};
const onSubmit = async (data: TContactUsForm) => {
if (!hcaptchaToken) {
toast.error("Please complete the captcha verification");
return;
}
setIsLoading(() => true);
try {
const response = (await api.post("contacts", { ...data })).data;
const response = (
await api.post("contacts", { ...data, hcaptcha_token: hcaptchaToken })
).data;
toast.success(response.message);
reset();
setHcaptchaToken(null);
captchaRef.current?.resetCaptcha();
} catch (error) {
console.log(error);
toast.error("Something went wrong");
@@ -143,6 +163,14 @@ const ContactUsForm = ({
},
}}
/>
<div className={`${colSpanClass}`}>
<HCaptcha
sitekey={process.env.NEXT_PUBLIC_HCAPTCHA_SITE_KEY || ""}
onVerify={onCaptchaVerify}
onExpire={onCaptchaExpire}
ref={captchaRef}
/>
</div>
<div className={`${colSpanClass} flex justify-end`}>
<button className="bg-(--primary) cursor-pointer w-fit text-white rounded-full py-4 px-6 flex justify-center items-center text-center">
{isLoading ? <Loading /> : buttonText}