removed otp

This commit is contained in:
Malini 2026-01-02 12:04:59 +05:30
parent 45906a55bc
commit e2da3f439d
3 changed files with 363 additions and 399 deletions

View File

@ -9,7 +9,7 @@ import Configuration from '@/pages/Admin/Configuration';
import Survey from '@/pages/Survey/Survey'; import Survey from '@/pages/Survey/Survey';
import Overview from '@/pages/Overview/Overview'; import Overview from '@/pages/Overview/Overview';
import Login from '@/pages/Login/Login'; import Login from '@/pages/Login/Login';
import OTPVerification from '@/pages/OTPVerification/OTPVerification'; // import OTPVerification from '@/pages/OTPVerification/OTPVerification';
import History from '@/pages/History/History'; import History from '@/pages/History/History';
import ChangePassword from '@/pages/ChangePassword/ChangePassword'; import ChangePassword from '@/pages/ChangePassword/ChangePassword';
import ForgotPassword from '@/pages/ForgotPassword/ForgotPassword'; import ForgotPassword from '@/pages/ForgotPassword/ForgotPassword';
@ -187,14 +187,14 @@ function App() {
<Routes> <Routes>
<Route path="/login" element={<Login />} /> <Route path="/login" element={<Login />} />
<Route {/* <Route
path="/verify-otp" path="/verify-otp"
element={ element={
<RequireRole allowedRoles={['EstablishmentUser', 'Admin']}> <RequireRole allowedRoles={['EstablishmentUser', 'Admin']}>
<OTPVerification /> <OTPVerification />
</RequireRole> </RequireRole>
} }
/> /> */}
<Route <Route
path="/history" path="/history"
element={ element={

View File

@ -206,56 +206,20 @@ const submit = async (e) => {
countdownIntervalRef.current = null; countdownIntervalRef.current = null;
} }
try {
// Request OTP after successful login
const otpResponse = await requestOtp(email);
if (otpResponse.status === 'success' && otpResponse.message === 'OTP sent successfully to your registered email') {
// Store user data in localStorage
localStorage.setItem('user_role', userData.role);
localStorage.setItem('user_profile', JSON.stringify({
id: userData.id,
name: userData.name,
email: userData.email
}));
// Store establishment_id if user is an EstablishmentUser
if (userData.role === 'EstablishmentUser' && userData.establishment_id) {
localStorage.setItem('establishment_id', userData.establishment_id);
}
// Show success message // Show success message
showToast('success', 'OTP sent successfully to your registered email'); showToast('success', 'Login successful!');
// Add a small delay before navigation to allow toast to be visible // Add a small delay before navigation to allow toast to be visible
setTimeout(() => { setTimeout(() => {
// Calculate OTP expiration time (3 minutes from now) try {
const otpExpiryTime = Date.now() + (3 * 60 * 1000); // Redirect based on user role
const redirectPath = userData.role === 'Admin' ? '/admin/dashboard' : '/dashboard';
// Navigate to OTP verification with email and expiry time navigate(location.state?.from || redirectPath);
navigate('/verify-otp', { } catch (err) {
state: { console.error('Navigation failed:', err);
email, showToast('error', 'Failed to redirect. Please try again.');
otpExpiryTime,
from: location.state?.from || (userData.role === 'Admin' ? '/admin/dashboard' : '/dashboard')
} }
});
}, 1000); // 1 second delay }, 1000); // 1 second delay
} else {
throw new Error('Failed to send OTP');
}
} catch (otpError) {
console.error('OTP request failed:', otpError);
showToast('error', 'Login successful but failed to send OTP. Please try again.');
// Still navigate to OTP page but show error
navigate('/verify-otp', {
state: {
email,
from: location.state?.from || (userData.role === 'Admin' ? '/admin/dashboard' : '/dashboard'),
error: 'Failed to send OTP. Please request a new one.'
}
});
}
} else { } else {
throw new Error(response?.message || 'Invalid credentials'); throw new Error(response?.message || 'Invalid credentials');
} }

View File

@ -1,402 +1,402 @@
import React, { useState, useEffect, useRef, useCallback } from 'react'; // import React, { useState, useEffect, useRef, useCallback } from 'react';
import { requestOtp, verifyOtp } from '@/services/auth/authService'; // import { requestOtp, verifyOtp } from '@/services/auth/authService';
import { useNavigate, useLocation } from 'react-router-dom'; // import { useNavigate, useLocation } from 'react-router-dom';
import { AlertCircle, Clock, CheckCircle } from "lucide-react"; // import { AlertCircle, Clock, CheckCircle } from "lucide-react";
const logoSrc = '/assets/images/FCSCLogo.svg'; // const logoSrc = '/assets/images/FCSCLogo.svg';
const OTPVerification = () => { // const OTPVerification = () => {
const navigate = useNavigate(); // const navigate = useNavigate();
const location = useLocation(); // const location = useLocation();
const [otp, setOtp] = useState(['', '', '', '', '', '']); // const [otp, setOtp] = useState(['', '', '', '', '', '']);
const [error, setError] = useState(''); // const [error, setError] = useState('');
const [loading, setLoading] = useState(false); // const [loading, setLoading] = useState(false);
const [toast, setToast] = useState(null); // const [toast, setToast] = useState(null);
const [resendDisabled, setResendDisabled] = useState(false); // const [resendDisabled, setResendDisabled] = useState(false);
const [countdown, setCountdown] = useState(600); // const [countdown, setCountdown] = useState(600);
const [attemptsRemaining, setAttemptsRemaining] = useState(3); // const [attemptsRemaining, setAttemptsRemaining] = useState(3);
const inputRefs = useRef(Array(6).fill(null).map(() => React.createRef())); // const inputRefs = useRef(Array(6).fill(null).map(() => React.createRef()));
const toastTimeoutRef = useRef(null); // const toastTimeoutRef = useRef(null);
const countdownRef = useRef(null); // const countdownRef = useRef(null);
// Get email from location state or use a default // // Get email from location state or use a default
const maskedEmail = location.state?.email ? // const maskedEmail = location.state?.email ?
`${location.state.email.split('@')[0].substring(0, 2)}***@${location.state.email.split('@')[1]}` : // `${location.state.email.split('@')[0].substring(0, 2)}***@${location.state.email.split('@')[1]}` :
'na***@company.com'; // 'na***@company.com';
// Toast handling // // Toast handling
const closeToast = useCallback(() => { // const closeToast = useCallback(() => {
if (toastTimeoutRef.current) { // if (toastTimeoutRef.current) {
clearTimeout(toastTimeoutRef.current); // clearTimeout(toastTimeoutRef.current);
toastTimeoutRef.current = null; // toastTimeoutRef.current = null;
} // }
setToast(null); // setToast(null);
}, []); // }, []);
const showToast = useCallback((type, message) => { // const showToast = useCallback((type, message) => {
if (!message) return; // if (!message) return;
if (toastTimeoutRef.current) { // if (toastTimeoutRef.current) {
clearTimeout(toastTimeoutRef.current); // clearTimeout(toastTimeoutRef.current);
toastTimeoutRef.current = null; // toastTimeoutRef.current = null;
} // }
setToast({ type, message }); // setToast({ type, message });
toastTimeoutRef.current = setTimeout(() => { // toastTimeoutRef.current = setTimeout(() => {
setToast(null); // setToast(null);
toastTimeoutRef.current = null; // toastTimeoutRef.current = null;
}, 4000); // }, 4000);
}, []); // }, []);
// Handle OTP input change // // Handle OTP input change
const handleOtpChange = (e, index) => { // const handleOtpChange = (e, index) => {
const value = e.target.value; // const value = e.target.value;
// Only allow numbers and limit to 1 character // // Only allow numbers and limit to 1 character
if (value && !/^\d*$/.test(value)) return; // if (value && !/^\d*$/.test(value)) return;
const newOtp = [...otp]; // const newOtp = [...otp];
newOtp[index] = value.slice(-1); // newOtp[index] = value.slice(-1);
setOtp(newOtp); // setOtp(newOtp);
// Move to next input if there's a value and we're not on the last input // // Move to next input if there's a value and we're not on the last input
if (value && index < 5 && inputRefs.current[index + 1]?.current) { // if (value && index < 5 && inputRefs.current[index + 1]?.current) {
inputRefs.current[index + 1].current.focus(); // inputRefs.current[index + 1].current.focus();
} // }
}; // };
// Handle backspace // // Handle backspace
const handleKeyDown = (e, index) => { // const handleKeyDown = (e, index) => {
if (e.key === 'Backspace' && !otp[index] && index > 0 && inputRefs.current[index - 1]?.current) { // if (e.key === 'Backspace' && !otp[index] && index > 0 && inputRefs.current[index - 1]?.current) {
// Move to previous input on backspace // // Move to previous input on backspace
inputRefs.current[index - 1].current.focus(); // inputRefs.current[index - 1].current.focus();
} // }
}; // };
// Handle paste // // Handle paste
const handlePaste = (e) => { // const handlePaste = (e) => {
e.preventDefault(); // e.preventDefault();
const pasteData = e.clipboardData.getData('text/plain').trim(); // const pasteData = e.clipboardData.getData('text/plain').trim();
if (/^\d{6}$/.test(pasteData)) { // if (/^\d{6}$/.test(pasteData)) {
const newOtp = pasteData.split('').slice(0, 6); // const newOtp = pasteData.split('').slice(0, 6);
setOtp([...newOtp, ...Array(6 - newOtp.length).fill('')]); // setOtp([...newOtp, ...Array(6 - newOtp.length).fill('')]);
} // }
}; // };
// Handle verify OTP // // Handle verify OTP
const handleVerify = async (e) => { // const handleVerify = async (e) => {
e.preventDefault(); // e.preventDefault();
const otpValue = otp.join(''); // const otpValue = otp.join('');
if (otpValue.length !== 6) { // if (otpValue.length !== 6) {
setError('Please enter a valid 6-digit code'); // setError('Please enter a valid 6-digit code');
return; // return;
} // }
setLoading(true); // setLoading(true);
setError(''); // setError('');
try { // try {
const response = await verifyOtp({ // const response = await verifyOtp({
registered_email: location.state?.email, // registered_email: location.state?.email,
otp: otpValue // otp: otpValue
}); // });
if (response?.status === 'success') { // if (response?.status === 'success') {
// Show success message immediately // // Show success message immediately
showToast('success', 'Verified! You are signed in. Redirecting to your dashboard.'); // showToast('success', 'Verified! You are signed in. Redirecting to your dashboard.');
// Check for admin role in multiple possible locations // // Check for admin role in multiple possible locations
const userRole = localStorage.getItem('user_role') || // const userRole = localStorage.getItem('user_role') ||
response?.data?.user?.role || // response?.data?.user?.role ||
response?.data?.role; // response?.data?.role;
// Check if user is admin (case-insensitive check) // // Check if user is admin (case-insensitive check)
const isAdmin = userRole?.toLowerCase() === 'admin' || // const isAdmin = userRole?.toLowerCase() === 'admin' ||
response?.data?.isAdmin === true; // response?.data?.isAdmin === true;
// Use the redirect path from location state if available, otherwise determine based on role // // Use the redirect path from location state if available, otherwise determine based on role
const redirectPath = location.state?.from || // const redirectPath = location.state?.from ||
(isAdmin ? '/admin/dashboard' : '/dashboard'); // (isAdmin ? '/admin/dashboard' : '/dashboard');
// Wait for 5 seconds before redirecting // // Wait for 5 seconds before redirecting
setLoading(true); // setLoading(true);
setTimeout(() => { // setTimeout(() => {
navigate(redirectPath, { replace: true }); // navigate(redirectPath, { replace: true });
}, 1000); // }, 1000);
} else { // } else {
throw new Error(response?.message || 'Invalid OTP'); // throw new Error(response?.message || 'Invalid OTP');
} // }
} catch (err) { // } catch (err) {
console.error('OTP verification failed', err); // console.error('OTP verification failed', err);
const errorMessage = err.response?.data?.message || // const errorMessage = err.response?.data?.message ||
err.message || // err.message ||
'An error occurred during OTP verification. Please try again.'; // 'An error occurred during OTP verification. Please try again.';
const newAttempts = attemptsRemaining - 1; // const newAttempts = attemptsRemaining - 1;
setAttemptsRemaining(newAttempts); // setAttemptsRemaining(newAttempts);
// Set the error message for incorrect OTP // // Set the error message for incorrect OTP
const displayError = errorMessage || 'Invalid verification code'; // const displayError = errorMessage || 'Invalid verification code';
setError(displayError); // setError(displayError);
if (newAttempts <= 0) { // if (newAttempts <= 0) {
setOtp(Array(6).fill('')); // setOtp(Array(6).fill(''));
// Show error message and redirect to login after a short delay // // Show error message and redirect to login after a short delay
setError('Maximum attempts reached. Redirecting to login...'); // setError('Maximum attempts reached. Redirecting to login...');
setTimeout(() => { // setTimeout(() => {
navigate('/login', { replace: true }); // navigate('/login', { replace: true });
}, 2000); // }, 2000);
} else { // } else {
setOtp(Array(6).fill('')); // setOtp(Array(6).fill(''));
// Focus the first input if it exists // // Focus the first input if it exists
if (inputRefs.current[0]) { // if (inputRefs.current[0]) {
inputRefs.current[0].focus(); // inputRefs.current[0].focus();
} // }
} // }
} finally { // } finally {
setLoading(false); // setLoading(false);
} // }
}; // };
// Handle resend OTP // // Handle resend OTP
const handleResend = async () => { // const handleResend = async () => {
if (resendDisabled) return; // if (resendDisabled) return;
// Show loading state immediately // // Show loading state immediately
setResendDisabled(true); // setResendDisabled(true);
setLoading(true); // setLoading(true);
try { // try {
// Call the API to resend OTP // // Call the API to resend OTP
const response = await requestOtp(location.state?.email); // const response = await requestOtp(location.state?.email);
// Update state and show success toast immediately // // Update state and show success toast immediately
const tenMinutesInSeconds = 10 * 60; // const tenMinutesInSeconds = 10 * 60;
setCountdown(tenMinutesInSeconds); // setCountdown(tenMinutesInSeconds);
setAttemptsRemaining(3); // setAttemptsRemaining(3);
setError(''); // setError('');
setOtp(Array(6).fill('')); // setOtp(Array(6).fill(''));
// Use setTimeout to ensure the toast shows up in the next tick // // Use setTimeout to ensure the toast shows up in the next tick
setTimeout(() => { // setTimeout(() => {
showToast('success', response.message || 'A new OTP has been sent to your email.'); // showToast('success', response.message || 'A new OTP has been sent to your email.');
}, 0); // }, 0);
} catch (error) { // } catch (error) {
console.error('Error resending OTP:', error); // console.error('Error resending OTP:', error);
showToast('error', 'An error occurred while sending OTP. Please try again.'); // showToast('error', 'An error occurred while sending OTP. Please try again.');
} finally { // } finally {
setLoading(false); // setLoading(false);
// Re-enable resend after 60 seconds // // Re-enable resend after 60 seconds
const timer = setTimeout(() => { // const timer = setTimeout(() => {
setResendDisabled(false); // setResendDisabled(false);
}, 60000); // }, 60000);
// Cleanup timer on component unmount // // Cleanup timer on component unmount
return () => clearTimeout(timer); // return () => clearTimeout(timer);
} // }
}; // };
// Start countdown on component mount // // Start countdown on component mount
useEffect(() => { // useEffect(() => {
// Always use 10 minutes for the countdown // // Always use 10 minutes for the countdown
const tenMinutesInMs = 10 * 60 * 1000; // const tenMinutesInMs = 10 * 60 * 1000;
const expiryTime = Date.now() + tenMinutesInMs; // const expiryTime = Date.now() + tenMinutesInMs;
const updateCountdown = () => { // const updateCountdown = () => {
const now = Date.now(); // const now = Date.now();
const remainingSeconds = Math.max(0, Math.floor((expiryTime - now) / 1000)); // const remainingSeconds = Math.max(0, Math.floor((expiryTime - now) / 1000));
setCountdown(remainingSeconds); // setCountdown(remainingSeconds);
if (remainingSeconds <= 0) { // if (remainingSeconds <= 0) {
clearInterval(countdownRef.current); // clearInterval(countdownRef.current);
setResendDisabled(false); // setResendDisabled(false);
} // }
}; // };
// Initial update // // Initial update
updateCountdown(); // updateCountdown();
// Set up interval for countdown updates // // Set up interval for countdown updates
countdownRef.current = setInterval(updateCountdown, 1000); // countdownRef.current = setInterval(updateCountdown, 1000);
return () => { // return () => {
if (countdownRef.current) clearInterval(countdownRef.current); // if (countdownRef.current) clearInterval(countdownRef.current);
}; // };
}, []); // }, []);
// Cleanup on unmount // // Cleanup on unmount
useEffect(() => { // useEffect(() => {
return () => { // return () => {
if (toastTimeoutRef.current) clearTimeout(toastTimeoutRef.current); // if (toastTimeoutRef.current) clearTimeout(toastTimeoutRef.current);
if (countdownRef.current) clearInterval(countdownRef.current); // if (countdownRef.current) clearInterval(countdownRef.current);
}; // };
}, []); // }, []);
const minutes = Math.floor(countdown / 60); // const minutes = Math.floor(countdown / 60);
const seconds = countdown % 60; // const seconds = countdown % 60;
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">
{/* Toast Notification */} // {/* Toast Notification */}
{toast && ( // {toast && (
<div className="fixed top-6 inset-x-0 z-50 flex justify-center px-4"> // <div className="fixed top-6 inset-x-0 z-50 flex justify-center px-4">
<div // <div
className={`text-sm rounded-lg px-4 py-3 border flex items-center justify-between gap-3 shadow-lg max-w-md ${ // className={`text-sm rounded-lg px-4 py-3 border flex items-center justify-between gap-3 shadow-lg max-w-md ${
toast.type === 'success' // toast.type === 'success'
? 'bg-green-100 border-green-400 text-green-800' // ? 'bg-green-100 border-green-400 text-green-800'
: toast.type === 'error' // : toast.type === 'error'
? 'bg-[#FEF2F2] border-[#FECACA] text-[#B91C1C]' // ? 'bg-[#FEF2F2] border-[#FECACA] text-[#B91C1C]'
: 'bg-blue-100 border-blue-200 text-blue-800' // : 'bg-blue-100 border-blue-200 text-blue-800'
}`} // }`}
> // >
<div className="flex items-center gap-2"> // <div className="flex items-center gap-2">
{toast.type === 'error' ? ( // {toast.type === 'error' ? (
<AlertCircle className="h-5 w-5 flex-shrink-0" /> // <AlertCircle className="h-5 w-5 flex-shrink-0" />
) : toast.type === 'success' ? ( // ) : toast.type === 'success' ? (
<CheckCircle className="h-5 w-5 flex-shrink-0 text-green-600" /> // <CheckCircle className="h-5 w-5 flex-shrink-0 text-green-600" />
) : null} // ) : null}
<span>{toast.message}</span> // <span>{toast.message}</span>
</div> // </div>
<button // <button
type="button" // type="button"
className="flex h-6 w-6 items-center justify-center rounded hover:bg-black/5" // className="flex h-6 w-6 items-center justify-center rounded hover:bg-black/5"
onClick={closeToast} // onClick={closeToast}
> // >
//
</button> // </button>
</div> // </div>
</div> // </div>
)} // )}
<div className="w-full max-w-md"> // <div className="w-full max-w-md">
<div className="bg-white rounded-lg shadow-sm ring-1 ring-gray-200 overflow-hidden"> // <div className="bg-white rounded-lg shadow-sm ring-1 ring-gray-200 overflow-hidden">
{/* Logo Section */} // {/* Logo Section */}
<div className="px-7 py-6 border-b border-gray-200 bg-[#F2ECCF] text-center"> // <div className="px-7 py-6 border-b border-gray-200 bg-[#F2ECCF] text-center">
<div className="mb-2"> // <div className="mb-2">
<img src={logoSrc} alt="FCSC Logo" className="mx-auto h-16 w-auto" /> // <img src={logoSrc} alt="FCSC Logo" className="mx-auto h-16 w-auto" />
</div> // </div>
<h1 className="text-[17px] font-semibold text-[#1F2937] mt-0"> // <h1 className="text-[17px] font-semibold text-[#1F2937] mt-0">
IIP (Index of Industrial Production) // IIP (Index of Industrial Production)
</h1> // </h1>
</div> // </div>
<div className="p-7"> // <div className="p-7">
{/* <div className="text-center mb-6"> // {/* <div className="text-center mb-6">
<h2 className="text-xl font-bold text-gray-800">Email OTP</h2> // <h2 className="text-xl font-bold text-gray-800">Email OTP</h2>
<p className="text-gray-600 mt-1">Step 2 of 2</p> // <p className="text-gray-600 mt-1">Step 2 of 2</p>
</div> */} // </div> */}
<form onSubmit={handleVerify}> // <form onSubmit={handleVerify}>
<div className="space-y-6"> // <div className="space-y-6">
<div className="text-center"> // <div className="text-center">
<h3 className="text-lg font-medium text-gray-800">Verify your sign-in</h3> // <h3 className="text-lg font-medium text-gray-800">Verify your sign-in</h3>
<p className="text-gray-600 mt-1"> // <p className="text-gray-600 mt-1">
We sent a 6-digit authentication code to <span className="font-medium">{maskedEmail}</span> // We sent a 6-digit authentication code to <span className="font-medium">{maskedEmail}</span>
</p> // </p>
</div> // </div>
{/* OTP Inputs */} // {/* OTP Inputs */}
<div className="space-y-4"> // <div className="space-y-4">
<div className="flex justify-center space-x-3"> // <div className="flex justify-center space-x-3">
{otp.map((digit, index) => ( // {otp.map((digit, index) => (
<input // <input
key={index} // key={index}
ref={(el) => { inputRefs.current[index] = { current: el } }} // ref={(el) => { inputRefs.current[index] = { current: el } }}
type="text" // type="text"
inputMode="numeric" // inputMode="numeric"
pattern="[0-9]*" // pattern="[0-9]*"
maxLength={1} // maxLength={1}
value={digit} // value={digit}
onChange={(e) => handleOtpChange(e, index)} // onChange={(e) => handleOtpChange(e, index)}
onKeyDown={(e) => handleKeyDown(e, index)} // onKeyDown={(e) => handleKeyDown(e, index)}
onPaste={handlePaste} // onPaste={handlePaste}
className={`w-12 h-12 text-center text-xl bg-transparent border-2 rounded focus:outline-none focus:ring-0 ${ // className={`w-12 h-12 text-center text-xl bg-transparent border-2 rounded focus:outline-none focus:ring-0 ${
error ? 'border-red-500' : // error ? 'border-red-500' :
digit ? 'border-[#92722A]' : // digit ? 'border-[#92722A]' :
'border-gray-300 hover:border-gray-400 focus:border-[#92722A] focus:border-b-2 focus:border-b-[#92722A]' // 'border-gray-300 hover:border-gray-400 focus:border-[#92722A] focus:border-b-2 focus:border-b-[#92722A]'
}`} // }`}
autoFocus={index === 0} // autoFocus={index === 0}
disabled={loading || attemptsRemaining <= 0} // disabled={loading || attemptsRemaining <= 0}
/> // />
))} // ))}
</div> // </div>
{error && ( // {error && (
<div className="text-red-600 text-sm flex items-center justify-center"> // <div className="text-red-600 text-sm flex items-center justify-center">
<AlertCircle className="h-4 w-4 mr-1" /> // <AlertCircle className="h-4 w-4 mr-1" />
{error} // {error}
</div> // </div>
)} // )}
<div className="flex justify-between items-center text-sm text-gray-600"> // <div className="flex justify-between items-center text-sm text-gray-600">
<div className="flex items-center space-x-2 leading-none"> // <div className="flex items-center space-x-2 leading-none">
<Clock className="h-4 w-4 flex-shrink-0 -mt-0.5" /> // <Clock className="h-4 w-4 flex-shrink-0 -mt-0.5" />
<span className="text-sm -ml-1"> // <span className="text-sm -ml-1">
Code expires in {minutes}:{seconds < 10 ? `0${seconds}` : seconds} // Code expires in {minutes}:{seconds < 10 ? `0${seconds}` : seconds}
</span> // </span>
</div> // </div>
<span>Attempts: {attemptsRemaining}/3</span> // <span>Attempts: {attemptsRemaining}/3</span>
</div> // </div>
</div> // </div>
<div className="space-y-4"> // <div className="space-y-4">
<button // <button
type="submit" // type="submit"
disabled={loading || otp.join('').length !== 6 || attemptsRemaining <= 0} // disabled={loading || otp.join('').length !== 6 || attemptsRemaining <= 0}
className={`w-full py-3 px-4 rounded-md font-medium bg-[#92722A] text-white ${ // className={`w-full py-3 px-4 rounded-md font-medium bg-[#92722A] text-white ${
loading || attemptsRemaining <= 0 || otp.join('').length !== 6 // loading || attemptsRemaining <= 0 || otp.join('').length !== 6
? 'opacity-50 cursor-not-allowed' // ? 'opacity-50 cursor-not-allowed'
: 'hover:bg-[#7a5e23] focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-[#92722A]' // : 'hover:bg-[#7a5e23] focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-[#92722A]'
}`} // }`}
> // >
{loading ? 'Verifying...' : 'Verify'} // {loading ? 'Verifying...' : 'Verify'}
</button> // </button>
<button // <button
type="button" // type="button"
onClick={() => navigate('/login')} // onClick={() => navigate('/login')}
className="w-full py-3 px-4 rounded-md font-medium border border-[#92722A] bg-white text-[#92722A] hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-[#92722A]" // className="w-full py-3 px-4 rounded-md font-medium border border-[#92722A] bg-white text-[#92722A] hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-[#92722A]"
> // >
Cancel // Cancel
</button> // </button>
<div className="text-center"> // <div className="text-center">
<button // <button
type="button" // type="button"
onClick={handleResend} // onClick={handleResend}
disabled={resendDisabled} // disabled={resendDisabled}
className={`text-sm font-medium ${ // className={`text-sm font-medium ${
resendDisabled ? 'text-gray-400' : 'text-[#92722A] hover:text-[#7a5e23]' // resendDisabled ? 'text-gray-400' : 'text-[#92722A] hover:text-[#7a5e23]'
}`} // }`}
> // >
{resendDisabled ? `Resend code (${Math.ceil(countdown/60)}:${(countdown%60).toString().padStart(2, '0')})` : 'Resend code'} // {resendDisabled ? `Resend code (${Math.ceil(countdown/60)}:${(countdown%60).toString().padStart(2, '0')})` : 'Resend code'}
</button> // </button>
</div> // </div>
</div> // </div>
</div> // </div>
</form> // </form>
<div className="mt-6 pt-6 border-t border-gray-200 text-center"> // <div className="mt-6 pt-6 border-t border-gray-200 text-center">
<p className="mt-2 text-sm text-gray-600"> // <p className="mt-2 text-sm text-gray-600">
Having trouble?{' '} // Having trouble?{' '}
<button // <button
onClick={() => navigate('/')} // onClick={() => navigate('/')}
className="text-[#92722A] hover:text-[#7a5e23] font-medium focus:outline-none" // className="text-[#92722A] hover:text-[#7a5e23] font-medium focus:outline-none"
> // >
Contact support // Contact support
</button> // </button>
</p> // </p>
</div> // </div>
</div> // </div>
</div> // </div>
</div> // </div>
</div> // </div>
); // );
}; // };
export default OTPVerification; // export default OTPVerification;