import { useForm } from "react-hook-form"; import { z } from "zod"; import { zodResolver } from "@hookform/resolvers/zod"; import { useState, useRef, useEffect } from "react"; import { useNavigate } from "react-router-dom"; import showToast from "../../services/toastService"; import { AuthWrapper } from "./AuthWrapper"; import { OTP_EXPIRY_SECONDS } from "../../utils/constants"; import AuthRepository from "../../repositories/AuthRepository"; const otpSchema = z.object({ otp1: z.string().min(1, "Required"), otp2: z.string().min(1, "Required"), otp3: z.string().min(1, "Required"), otp4: z.string().min(1, "Required"), }); const LoginWithOtp = () => { const navigate = useNavigate(); const [ loading, setLoading ] = useState( false ); const [ timeLeft, setTimeLeft ] = useState( 0 ); const inputRefs = useRef([]); const { register, handleSubmit, formState: { errors, isSubmitted }, getValues, } = useForm({ resolver: zodResolver(otpSchema), }); const onSubmit = async (data) => { const finalOtp = data.otp1 + data.otp2 + data.otp3 + data.otp4; const username = localStorage.getItem( "otpUsername" ); console.log(username) setLoading(true); try { let requestedData = { email: username, otp:finalOtp } const response = await AuthRepository.verifyOTP( requestedData ) localStorage.setItem("jwtToken", response.data.token); localStorage.setItem("refreshToken", response.data.refreshToken); setLoading( false ); localStorage.removeItem( "otpUsername" ); localStorage.removeItem( "otpSentTime" ); navigate( "/dashboard" ); } catch (err) { showToast( "Invalid or expired OTP.", "error" ); setLoading(false); } }; const formatTime = (seconds) => { const min = Math.floor(seconds / 60).toString().padStart(2, "0"); const sec = (seconds % 60).toString().padStart(2, "0"); return `${min}:${sec}`; }; useEffect(() => { const otpSentTime = localStorage.getItem("otpSentTime"); const now = Date.now(); if (otpSentTime) { const elapsed = Math.floor((now - Number(otpSentTime)) / 1000); // in seconds const remaining = Math.max(OTP_EXPIRY_SECONDS - elapsed, 0); // prevent negatives setTimeLeft(remaining); } }, []); useEffect(() => { if (timeLeft <= 0) return; const timer = setInterval(() => { setTimeLeft((prev) => { if (prev <= 1) { clearInterval(timer); localStorage.removeItem( "otpSentTime" ); localStorage.removeItem("otpUsername"); return 0; } return prev - 1; }); }, 1000); return () => clearInterval(timer); }, [timeLeft]); return (

Verify Your OTP

Please enter the 4-digit code sent to your email.

{[1, 2, 3, 4].map((num, idx) => { const { ref, onChange, ...rest } = register(`otp${num}`); return ( { inputRefs.current[idx] = el; ref(el); }} onChange={(e) => { const val = e.target.value; onChange(e); if (/^\d$/.test(val) && idx < 3) { inputRefs.current[idx + 1]?.focus(); } }} onKeyDown={(e) => { if ( e.key === "Backspace" && !getValues()[`otp${num}`] && idx > 0 ) { inputRefs.current[idx - 1]?.focus(); } }} style={{ width: "40px", height: "40px", fontSize: "15px" }} {...rest} /> ); })}
{isSubmitted && Object.values(errors).some((e) => e?.message) && (
Please fill all four digits.
)} {timeLeft > 0 ? (

This OTP will expire in {formatTime(timeLeft)}

) : (

OTP has expired. Please request a new one.

navigate('/auth/login')}>Try Again
)}
); }; export default LoginWithOtp;