login, forgot/change password bug fixed
This commit is contained in:
parent
f4c59bf3da
commit
c257cf356c
@ -13,6 +13,7 @@ const ToggleableInput = ({
|
|||||||
toggleShow,
|
toggleShow,
|
||||||
placeholder,
|
placeholder,
|
||||||
required = false,
|
required = false,
|
||||||
|
error,
|
||||||
}) => (
|
}) => (
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-1">
|
<label className="block text-sm font-medium text-gray-700 mb-1">
|
||||||
@ -25,8 +26,9 @@ const ToggleableInput = ({
|
|||||||
value={value}
|
value={value}
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
placeholder={placeholder}
|
placeholder={placeholder}
|
||||||
required={required}
|
className={`block w-full h-10 rounded-md border-2 ${
|
||||||
className="block w-full h-10 rounded-md border-2 border-[#92722A] focus:border-[#92722A] focus:ring-0 px-4 pr-11 text-sm bg-white"
|
error ? "border-red-500" : "border-[#92722A]"
|
||||||
|
} focus:border-[#92722A] focus:ring-0 px-4 pr-11 text-sm bg-white`}
|
||||||
/>
|
/>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
@ -36,12 +38,8 @@ const ToggleableInput = ({
|
|||||||
>
|
>
|
||||||
{show ? (
|
{show ? (
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" className="h-4 w-4 text-[#92722A]" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
<svg xmlns="http://www.w3.org/2000/svg" className="h-4 w-4 text-[#92722A]" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||||
<path
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2"
|
||||||
strokeLinecap="round"
|
d="M13.875 18.825A10.05 10.05 0 0 1 12 19c-5.523 0-10-4.477-10-10a9.96 9.96 0 0 1 2.122-6.21m3.086-1.955A9.953 9.953 0 0 1 12 3c5.523 0 10 4.477 10 10 0 1.591-.372 3.093-1.034 4.432M9.88 9.88a3 3 0 1 0 4.24 4.24" />
|
||||||
strokeLinejoin="round"
|
|
||||||
strokeWidth="2"
|
|
||||||
d="M13.875 18.825A10.05 10.05 0 0 1 12 19c-5.523 0-10-4.477-10-10a9.96 9.96 0 0 1 2.122-6.21m3.086-1.955A9.953 9.953 0 0 1 12 3c5.523 0 10 4.477 10 10 0 1.591-.372 3.093-1.034 4.432M9.88 9.88a3 3 0 1 0 4.24 4.24"
|
|
||||||
/>
|
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="m3 3 18 18" />
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="m3 3 18 18" />
|
||||||
</svg>
|
</svg>
|
||||||
) : (
|
) : (
|
||||||
@ -52,10 +50,10 @@ const ToggleableInput = ({
|
|||||||
)}
|
)}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
{error && <p className="text-xs text-red-600 mt-1">{error}</p>}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
const ChangePassword = () => {
|
const ChangePassword = () => {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
@ -71,88 +69,95 @@ const ChangePassword = () => {
|
|||||||
const [loading, setLoading] = React.useState(false);
|
const [loading, setLoading] = React.useState(false);
|
||||||
const [error, setError] = React.useState("");
|
const [error, setError] = React.useState("");
|
||||||
const [success, setSuccess] = React.useState("");
|
const [success, setSuccess] = React.useState("");
|
||||||
|
const [otpError, setOtpError] = React.useState("");
|
||||||
|
const [newPasswordError, setNewPasswordError] = React.useState("");
|
||||||
|
const [confirmPasswordError, setConfirmPasswordError] = React.useState("");
|
||||||
|
const validatePassword = (password) => {
|
||||||
|
const isStrong =
|
||||||
|
/.{8,}/.test(password) &&
|
||||||
|
/[A-Z]/.test(password) &&
|
||||||
|
/[a-z]/.test(password) &&
|
||||||
|
/[0-9]/.test(password) &&
|
||||||
|
/[!@#$%^&*(),.?":{}|<>]/.test(password);
|
||||||
|
|
||||||
const validatePassword = (password) => {
|
if (!isStrong) {
|
||||||
const minLength = /.{8,}/;
|
return "Password must meet complexity requirements.";
|
||||||
const uppercase = /[A-Z]/;
|
}
|
||||||
const lowercase = /[a-z]/;
|
return "";
|
||||||
const number = /[0-9]/;
|
};
|
||||||
const specialChar = /[!@#$%^&*(),.?":{}|<>]/;
|
|
||||||
|
|
||||||
if (!minLength.test(password))
|
const handleSubmit = async (e) => {
|
||||||
return "Password must be at least 8 characters long.";
|
e.preventDefault();
|
||||||
if (!uppercase.test(password))
|
|
||||||
return "Password must contain at least one uppercase letter.";
|
|
||||||
if (!lowercase.test(password))
|
|
||||||
return "Password must contain at least one lowercase letter.";
|
|
||||||
if (!number.test(password))
|
|
||||||
return "Password must contain at least one number.";
|
|
||||||
if (!specialChar.test(password))
|
|
||||||
return "Password must contain at least one special character.";
|
|
||||||
|
|
||||||
return "";
|
setError("");
|
||||||
};
|
setSuccess("");
|
||||||
|
|
||||||
const handleSubmit = async (e) => {
|
setOtpError("");
|
||||||
e.preventDefault();
|
setNewPasswordError("");
|
||||||
setError("");
|
setConfirmPasswordError("");
|
||||||
setSuccess("");
|
|
||||||
|
|
||||||
if (!registeredEmail) {
|
let hasError = false;
|
||||||
setError("Missing registered email. Please restart the process.");
|
|
||||||
return;
|
if (!otp.trim()) {
|
||||||
|
setOtpError("Verification code is required.");
|
||||||
|
hasError = true;
|
||||||
|
}
|
||||||
|
if (!newPassword.trim()) {
|
||||||
|
setNewPasswordError("New password is required.");
|
||||||
|
hasError = true;
|
||||||
|
}
|
||||||
|
if (!confirmPassword.trim()) {
|
||||||
|
setConfirmPasswordError("Confirm password is required.");
|
||||||
|
hasError = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasError) return;
|
||||||
|
|
||||||
|
const passwordError = validatePassword(newPassword);
|
||||||
|
if (passwordError) {
|
||||||
|
setNewPasswordError(passwordError);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newPassword !== confirmPassword) {
|
||||||
|
setConfirmPasswordError("Passwords do not match.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setLoading(true);
|
||||||
|
try {
|
||||||
|
const payload = {
|
||||||
|
registered_email: registeredEmail,
|
||||||
|
otp: otp.trim(),
|
||||||
|
password: newPassword,
|
||||||
|
confirm_password: confirmPassword,
|
||||||
|
};
|
||||||
|
|
||||||
|
const response = await apiClient.post("/forgot-password/verify-otp", payload);
|
||||||
|
|
||||||
|
if (response?.data) {
|
||||||
|
setSuccess("Password changed successfully. Redirecting to login...");
|
||||||
|
setTimeout(() => {
|
||||||
|
navigate("/login", {
|
||||||
|
state: {
|
||||||
|
showSuccessToast: true,
|
||||||
|
successMessage:
|
||||||
|
"Password reset successful. Please login with your new password.",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}, 1500);
|
||||||
|
} else {
|
||||||
|
setError("Something went wrong. Please try again.");
|
||||||
}
|
}
|
||||||
|
} catch (err) {
|
||||||
if (!otp || !newPassword || !confirmPassword) {
|
const msg =
|
||||||
setError("Please fill in all fields.");
|
err.response?.data?.message ||
|
||||||
return;
|
err.message ||
|
||||||
}
|
"Password change failed. Please verify the details.";
|
||||||
|
setError(msg);
|
||||||
const passwordError = validatePassword(newPassword);
|
} finally {
|
||||||
if (passwordError) {
|
setLoading(false);
|
||||||
setError(passwordError);
|
}
|
||||||
return;
|
};
|
||||||
}
|
|
||||||
|
|
||||||
if (newPassword !== confirmPassword) {
|
|
||||||
setError("Passwords do not match.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
setLoading(true);
|
|
||||||
try {
|
|
||||||
const payload = {
|
|
||||||
registered_email: registeredEmail,
|
|
||||||
otp: otp.trim(),
|
|
||||||
password: newPassword,
|
|
||||||
confirm_password: confirmPassword,
|
|
||||||
};
|
|
||||||
|
|
||||||
const response = await apiClient.post("/forgot-password/verify-otp", payload);
|
|
||||||
|
|
||||||
if (response?.data) {
|
|
||||||
setSuccess("Password changed successfully. Redirecting to login...");
|
|
||||||
setTimeout(() => {
|
|
||||||
navigate("/login", {
|
|
||||||
state: {
|
|
||||||
showSuccessToast: true,
|
|
||||||
successMessage: "Password reset successful. Please login with your new password."
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}, 1500);
|
|
||||||
} else {
|
|
||||||
setError("Something went wrong. Please try again.");
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
const msg =
|
|
||||||
err.response?.data?.message ||
|
|
||||||
err.message ||
|
|
||||||
"Password change failed. Please verify the details.";
|
|
||||||
setError(msg);
|
|
||||||
} finally {
|
|
||||||
setLoading(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen bg-[#F7F7F7] flex items-center justify-center px-4">
|
<div className="min-h-screen bg-[#F7F7F7] flex items-center justify-center px-4">
|
||||||
@ -193,12 +198,12 @@ const ChangePassword = () => {
|
|||||||
<ToggleableInput
|
<ToggleableInput
|
||||||
label="Verification Code"
|
label="Verification Code"
|
||||||
required
|
required
|
||||||
type="password"
|
|
||||||
value={otp}
|
value={otp}
|
||||||
onChange={(e) => setOtp(e.target.value)}
|
onChange={(e) => setOtp(e.target.value)}
|
||||||
show={showOtp}
|
show={showOtp}
|
||||||
toggleShow={() => setShowOtp((v) => !v)}
|
toggleShow={() => setShowOtp((v) => !v)}
|
||||||
placeholder="Enter 6-digit code"
|
placeholder="Enter 6-digit code"
|
||||||
|
error={otpError}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<ToggleableInput
|
<ToggleableInput
|
||||||
@ -209,6 +214,7 @@ const ChangePassword = () => {
|
|||||||
show={showNewPassword}
|
show={showNewPassword}
|
||||||
toggleShow={() => setShowNewPassword((v) => !v)}
|
toggleShow={() => setShowNewPassword((v) => !v)}
|
||||||
placeholder="Enter new password"
|
placeholder="Enter new password"
|
||||||
|
error={newPasswordError}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<ToggleableInput
|
<ToggleableInput
|
||||||
@ -219,6 +225,7 @@ const ChangePassword = () => {
|
|||||||
show={showConfirmPassword}
|
show={showConfirmPassword}
|
||||||
toggleShow={() => setShowConfirmPassword((v) => !v)}
|
toggleShow={() => setShowConfirmPassword((v) => !v)}
|
||||||
placeholder="Re-enter new password"
|
placeholder="Re-enter new password"
|
||||||
|
error={confirmPasswordError}
|
||||||
/>
|
/>
|
||||||
{newPassword && (
|
{newPassword && (
|
||||||
<ul className="text-xs text-gray-600 list-disc pl-5 space-y-1">
|
<ul className="text-xs text-gray-600 list-disc pl-5 space-y-1">
|
||||||
|
|||||||
@ -26,7 +26,6 @@ const Login = () => {
|
|||||||
const toastTimeoutRef = React.useRef(null);
|
const toastTimeoutRef = React.useRef(null);
|
||||||
const navigateTimeoutRef = React.useRef(null);
|
const navigateTimeoutRef = React.useRef(null);
|
||||||
|
|
||||||
// Generate CAPTCHA
|
|
||||||
const generateCaptcha = React.useCallback(() => {
|
const generateCaptcha = React.useCallback(() => {
|
||||||
const chars = 'ABCDEFGHJKLMNPQRSTUVWXYZ23456789';
|
const chars = 'ABCDEFGHJKLMNPQRSTUVWXYZ23456789';
|
||||||
let code = '';
|
let code = '';
|
||||||
@ -37,7 +36,6 @@ const Login = () => {
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
|
||||||
// Toast helpers
|
|
||||||
const closeToast = React.useCallback(() => {
|
const closeToast = React.useCallback(() => {
|
||||||
if (toastTimeoutRef.current) {
|
if (toastTimeoutRef.current) {
|
||||||
clearTimeout(toastTimeoutRef.current);
|
clearTimeout(toastTimeoutRef.current);
|
||||||
@ -59,7 +57,6 @@ const Login = () => {
|
|||||||
}, 4000);
|
}, 4000);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
// Navigate delay
|
|
||||||
const scheduleNavigate = React.useCallback(
|
const scheduleNavigate = React.useCallback(
|
||||||
(path) => {
|
(path) => {
|
||||||
if (!path) return;
|
if (!path) return;
|
||||||
@ -105,7 +102,6 @@ const Login = () => {
|
|||||||
setCaptchaError('');
|
setCaptchaError('');
|
||||||
};
|
};
|
||||||
|
|
||||||
// Email format check
|
|
||||||
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
||||||
|
|
||||||
const validateForm = () => {
|
const validateForm = () => {
|
||||||
@ -113,20 +109,23 @@ const Login = () => {
|
|||||||
setEmailError('');
|
setEmailError('');
|
||||||
setPasswordError('');
|
setPasswordError('');
|
||||||
setCaptchaError('');
|
setCaptchaError('');
|
||||||
|
|
||||||
if (!email.trim()) {
|
const trimmedEmail = email.trim();
|
||||||
setEmailError('Enter your email address.');
|
const trimmedPassword = password.trim();
|
||||||
|
|
||||||
|
if (!trimmedEmail) {
|
||||||
|
setEmailError('Email is required');
|
||||||
valid = false;
|
valid = false;
|
||||||
} else if (!emailRegex.test(email)) {
|
} else if (!emailRegex.test(trimmedEmail)) {
|
||||||
setEmailError('Enter a valid email address.');
|
setEmailError('Enter a valid email address.');
|
||||||
valid = false;
|
valid = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!password.trim()) {
|
if (!trimmedPassword) {
|
||||||
setPasswordError('Enter your password.');
|
setPasswordError('Password is required');
|
||||||
valid = false;
|
valid = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!userCaptcha.trim()) {
|
if (!userCaptcha.trim()) {
|
||||||
setCaptchaError('Please enter the CAPTCHA.');
|
setCaptchaError('Please enter the CAPTCHA.');
|
||||||
valid = false;
|
valid = false;
|
||||||
@ -134,9 +133,9 @@ const Login = () => {
|
|||||||
setCaptchaError('Invalid. Try again or refresh for a new code.');
|
setCaptchaError('Invalid. Try again or refresh for a new code.');
|
||||||
valid = false;
|
valid = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return valid;
|
return valid;
|
||||||
};
|
};
|
||||||
|
|
||||||
const submit = async (e) => {
|
const submit = async (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
@ -260,7 +259,6 @@ const Login = () => {
|
|||||||
|
|
||||||
|
|
||||||
<form onSubmit={submit} className="px-7 py-8 space-y-4">
|
<form onSubmit={submit} className="px-7 py-8 space-y-4">
|
||||||
{/* Email */}
|
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-1">Email address</label>
|
<label className="block text-sm font-medium text-gray-700 mb-1">Email address</label>
|
||||||
<input
|
<input
|
||||||
@ -268,7 +266,7 @@ const Login = () => {
|
|||||||
value={email}
|
value={email}
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
setEmail(e.target.value);
|
setEmail(e.target.value);
|
||||||
if (emailError) setEmailError(''); // 👈 clears error while typing
|
if (emailError) setEmailError('');
|
||||||
}}
|
}}
|
||||||
placeholder="name@company.com"
|
placeholder="name@company.com"
|
||||||
className={`block w-full h-10 rounded-md border-2 px-4 text-sm focus:ring-0 ${
|
className={`block w-full h-10 rounded-md border-2 px-4 text-sm focus:ring-0 ${
|
||||||
@ -278,7 +276,6 @@ const Login = () => {
|
|||||||
{emailError && <p className="text-xs text-red-600 mt-1">{emailError}</p>}
|
{emailError && <p className="text-xs text-red-600 mt-1">{emailError}</p>}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Password */}
|
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-1">Password</label>
|
<label className="block text-sm font-medium text-gray-700 mb-1">Password</label>
|
||||||
<div className="relative">
|
<div className="relative">
|
||||||
@ -312,19 +309,16 @@ const Login = () => {
|
|||||||
{passwordError && <p className="text-xs text-red-600 mt-1">{passwordError}</p>}
|
{passwordError && <p className="text-xs text-red-600 mt-1">{passwordError}</p>}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* CAPTCHA */}
|
|
||||||
<div className="mt-4">
|
<div className="mt-4">
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-1">
|
<label className="block text-sm font-medium text-gray-700 mb-1">
|
||||||
CAPTCHA Verification <span className="text-red-500">*</span>
|
CAPTCHA Verification <span className="text-red-500">*</span>
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
{/* CAPTCHA text box */}
|
|
||||||
<div className="flex justify-center items-center font-mono text-lg tracking-widest bg-gray-100 border-2 border-[#92722A] rounded-md px-4 py-2 select-none h-10">
|
<div className="flex justify-center items-center font-mono text-lg tracking-widest bg-gray-100 border-2 border-[#92722A] rounded-md px-4 py-2 select-none h-10">
|
||||||
{captcha}
|
{captcha}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Refresh icon + text */}
|
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={refreshCaptcha}
|
onClick={refreshCaptcha}
|
||||||
@ -336,12 +330,9 @@ const Login = () => {
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Info text */}
|
|
||||||
<p className="text-xs text-gray-500 mt-1">
|
<p className="text-xs text-gray-500 mt-1">
|
||||||
Enter the characters in the image. Can't read it? Refresh for a new code.
|
Enter the characters in the image. Can't read it? Refresh for a new code.
|
||||||
</p>
|
</p>
|
||||||
{/* </div> */}
|
|
||||||
|
|
||||||
|
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
@ -357,7 +348,6 @@ const Login = () => {
|
|||||||
{captchaError && <p className="text-xs text-red-600 mt-1">{captchaError}</p>}
|
{captchaError && <p className="text-xs text-red-600 mt-1">{captchaError}</p>}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Remember me */}
|
|
||||||
<div className="flex items-center justify-between text-sm">
|
<div className="flex items-center justify-between text-sm">
|
||||||
<label className="inline-flex items-center gap-2 cursor-pointer">
|
<label className="inline-flex items-center gap-2 cursor-pointer">
|
||||||
<input
|
<input
|
||||||
@ -377,7 +367,6 @@ const Login = () => {
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Submit */}
|
|
||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
disabled={loading || !userCaptcha.trim()}
|
disabled={loading || !userCaptcha.trim()}
|
||||||
|
|||||||
@ -143,7 +143,7 @@ const PublicContactForm = () => {
|
|||||||
|
|
||||||
const handleConfirmOtp = async () => {
|
const handleConfirmOtp = async () => {
|
||||||
if (!otp.trim()) {
|
if (!otp.trim()) {
|
||||||
setOtpError("Please enter the verification code.");
|
setOtpError("Verification code is required");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -160,22 +160,34 @@ const PublicContactForm = () => {
|
|||||||
|
|
||||||
const response = await apiClient.post("/forgot-password/verify-otp", payload);
|
const response = await apiClient.post("/forgot-password/verify-otp", payload);
|
||||||
|
|
||||||
if (response?.data) {
|
if (response?.data?.status === "success") {
|
||||||
setSuccess("OTP verified successfully. Redirecting to change password...");
|
setSuccess("OTP verified successfully. Redirecting to change password...");
|
||||||
setTimeout(() => navigate("/change-password"), 1500);
|
setTimeout(() => navigate("/change-password"), 1500);
|
||||||
} else {
|
} else {
|
||||||
setOtpError("Invalid verification code. Please try again.");
|
const serverMsg = response?.data?.message?.toLowerCase() || "";
|
||||||
|
|
||||||
|
if (serverMsg.includes("expired")) {
|
||||||
|
setOtpError("Verification code has expired. Please request a new one.");
|
||||||
|
} else if (serverMsg.includes("invalid")) {
|
||||||
|
setOtpError("Invalid verification code");
|
||||||
|
} else {
|
||||||
|
setOtpError("Invalid verification code");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
const msg =
|
const serverMsg = err.response?.data?.message?.toLowerCase() || "";
|
||||||
err.response?.data?.message ||
|
|
||||||
err.message ||
|
if (serverMsg.includes("expired")) {
|
||||||
"Verification failed. Please check the code and try again.";
|
setOtpError("Verification code has expired. Please request a new one.");
|
||||||
setOtpError(msg);
|
} else if (serverMsg.includes("invalid")) {
|
||||||
|
setOtpError("Invalid verification code");
|
||||||
|
} else {
|
||||||
|
setOtpError("Verification code is required");
|
||||||
|
}
|
||||||
} finally {
|
} finally {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleResendOtp = () => {
|
const handleResendOtp = () => {
|
||||||
setOtpMsg("A new OTP has been sent to your registered email.");
|
setOtpMsg("A new OTP has been sent to your registered email.");
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user