added otp configration

This commit is contained in:
Malini 2026-01-07 09:56:24 +05:30
parent 1946e6a817
commit 8ea1b44363
4 changed files with 405 additions and 363 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

@ -3686,6 +3686,9 @@ const handleImport = async () => {
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<span></span><span>Factory Name</span> <span></span><span>Factory Name</span>
</div> </div>
<div className="flex items-center gap-2">
<span></span><span>City/Town</span>
</div>
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<span></span><span>User Name</span> <span></span><span>User Name</span>
</div> </div>
@ -3701,6 +3704,9 @@ const handleImport = async () => {
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<span></span><span>HS Code</span> <span></span><span>HS Code</span>
</div> </div>
<div className="flex items-center gap-2">
<span></span><span>Industry Code (Current Production)</span>
</div>
</div> </div>
</div> </div>

View File

@ -206,20 +206,56 @@ 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', 'Login successful!'); showToast('success', 'OTP sent successfully to your registered email');
// 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(() => {
try { // Calculate OTP expiration time (3 minutes from now)
// Redirect based on user role const otpExpiryTime = Date.now() + (3 * 60 * 1000);
const redirectPath = userData.role === 'Admin' ? '/admin/dashboard' : '/dashboard';
navigate(location.state?.from || redirectPath); // Navigate to OTP verification with email and expiry time
} catch (err) { navigate('/verify-otp', {
console.error('Navigation failed:', err); state: {
showToast('error', 'Failed to redirect. Please try again.'); email,
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;