import { useState, useEffect } from 'react' import { useNavigate, useLocation } from 'react-router-dom' import { useAuth } from '@/contexts/AuthContext' import { AuthLayout } from '@/components/layout/AuthLayout' import { Button } from '@/components/ui/button' import { Input } from '@/components/ui/input' import { Label } from '@/components/ui/label' import { Alert, AlertDescription } from '@/components/ui/alert' import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs' import { Loader2, Mail, Smartphone, AlertCircle, RefreshCw, Shield } from 'lucide-react' import axios from 'axios' const API_URL = import.meta.env.VITE_API_URL || 'http://localhost:3001' export function OtpVerification() { const navigate = useNavigate() const location = useLocation() const { verifyOtp } = useAuth() // Get params from either location.state (from login) or URL query params (from Google OAuth) const searchParams = new URLSearchParams(location.search) const urlToken = searchParams.get('token') const urlMethods = searchParams.get('methods') const tempToken = location.state?.tempToken || urlToken const availableMethods = location.state?.availableMethods || (urlMethods ? JSON.parse(decodeURIComponent(urlMethods)) : null) const [code, setCode] = useState('') const [method, setMethod] = useState<'email' | 'whatsapp' | 'totp'>( availableMethods?.totp ? 'totp' : availableMethods?.whatsapp ? 'whatsapp' : 'email' ) const [loading, setLoading] = useState(false) const [error, setError] = useState('') const [resendLoading, setResendLoading] = useState(false) const [resendTimer, setResendTimer] = useState(30) const [canResend, setCanResend] = useState(false) // Countdown timer for resend button useEffect(() => { if (resendTimer > 0) { const timer = setTimeout(() => setResendTimer(resendTimer - 1), 1000) return () => clearTimeout(timer) } else { setCanResend(true) } }, [resendTimer]) if (!tempToken) { navigate('/auth/login') return null } const handleSubmit = async (e: React.FormEvent) => { e.preventDefault() setError('') setLoading(true) try { const result = await verifyOtp(tempToken, code, method) // Verification successful, redirect based on role if (result.user?.role === 'admin') { navigate('/admin') } else { navigate('/') } } catch (err) { const error = err as { response?: { data?: { message?: string } } } setError(error.response?.data?.message || 'Invalid OTP code. Please try again.') } finally { setLoading(false) } } const handleResendEmail = async () => { setResendLoading(true) setError('') try { // Call backend to resend OTP with temp token await axios.post(`${API_URL}/api/otp/email/resend`, { tempToken }) // Reset timer setResendTimer(30) setCanResend(false) setError('') } catch { setError('Failed to resend code. Please try again.') } finally { setResendLoading(false) } } const handleResendWhatsApp = async () => { setResendLoading(true) setError('') try { // Call backend to resend WhatsApp OTP with temp token await axios.post(`${API_URL}/api/otp/whatsapp/resend`, { tempToken }) // Reset timer setResendTimer(30) setCanResend(false) setError('') } catch { setError('Failed to resend code. Please try again.') setResendLoading(false) } } return (
{error && ( {error} )}

Two-factor authentication is enabled

setMethod(v as 'email' | 'whatsapp' | 'totp')}> {availableMethods?.email && ( Email )} {availableMethods?.whatsapp && ( WhatsApp )} {availableMethods?.totp && ( Authenticator )}

A 6-digit code has been sent to your email address. Please check your inbox.

setCode(e.target.value.replace(/\D/g, '').slice(0, 6))} maxLength={6} required disabled={loading} className="text-center text-2xl tracking-widest" />

A 6-digit code has been sent to your WhatsApp number. Please check your WhatsApp messages.

setCode(e.target.value.replace(/\D/g, '').slice(0, 6))} maxLength={6} required disabled={loading} className="text-center text-2xl tracking-widest" />

Open your authenticator app and enter the 6-digit code.

setCode(e.target.value.replace(/\D/g, '').slice(0, 6))} maxLength={6} required disabled={loading} className="text-center text-2xl tracking-widest" />
) }