import { useState, useEffect, useRef } from 'react'; import { Link, useNavigate } from 'react-router-dom'; import { useAuth } from '../contexts/AuthContext'; import { useLanguage } from '../contexts/LanguageContext'; import { useBranding } from '../contexts/BrandingContext'; import { Mail, Lock, ArrowRight, Loader2, AlertTriangle, RefreshCw, LogIn, ShieldCheck } from 'lucide-react'; import BrandLogo from '../components/BrandLogo'; import api from '../services/api'; import toast from 'react-hot-toast'; export default function Login() { const [email, setEmail] = useState(''); const [password, setPassword] = useState(''); const [loading, setLoading] = useState(false); const [needsVerification, setNeedsVerification] = useState(false); const [resendCooldown, setResendCooldown] = useState(0); const [resending, setResending] = useState(false); // 2FA state const [needs2FA, setNeeds2FA] = useState(false); const [tempToken, setTempToken] = useState(''); const [totpCode, setTotpCode] = useState(''); const [verifying2FA, setVerifying2FA] = useState(false); const totpInputRef = useRef(null); const { login, verify2FA } = useAuth(); const { t } = useLanguage(); const { registrationMode, oauthEnabled, oauthDisplayName } = useBranding(); const navigate = useNavigate(); useEffect(() => { if (resendCooldown <= 0) return; const timer = setTimeout(() => setResendCooldown(c => c - 1), 1000); return () => clearTimeout(timer); }, [resendCooldown]); // Auto-focus TOTP input when 2FA screen appears useEffect(() => { if (needs2FA && totpInputRef.current) { totpInputRef.current.focus(); } }, [needs2FA]); const handleResend = async () => { if (resendCooldown > 0 || resending) return; setResending(true); try { await api.post('/auth/resend-verification', { email }); toast.success(t('auth.emailVerificationResendSuccess')); setResendCooldown(60); } catch (err) { const wait = err.response?.data?.waitSeconds; if (wait) { setResendCooldown(wait); } toast.error(err.response?.data?.error || t('auth.emailVerificationResendFailed')); } finally { setResending(false); } }; const handleSubmit = async (e) => { e.preventDefault(); setLoading(true); try { const result = await login(email, password); if (result?.requires2FA) { setTempToken(result.tempToken); setNeeds2FA(true); setLoading(false); return; } toast.success(t('auth.loginSuccess')); navigate('/dashboard'); } catch (err) { if (err.response?.data?.needsVerification) { setNeedsVerification(true); } else { toast.error(err.response?.data?.error || t('auth.loginFailed')); } } finally { setLoading(false); } }; const handle2FASubmit = async (e) => { e.preventDefault(); setVerifying2FA(true); try { await verify2FA(tempToken, totpCode); toast.success(t('auth.loginSuccess')); navigate('/dashboard'); } catch (err) { toast.error(err.response?.data?.error || t('auth.2fa.verifyFailed')); setTotpCode(''); } finally { setVerifying2FA(false); } }; const handleBack = () => { setNeeds2FA(false); setTempToken(''); setTotpCode(''); }; return (
{t('auth.2fa.prompt')}
{t('auth.loginSubtitle')}
{t('auth.emailVerificationBanner')}
{t('auth.noAccount')}{' '} {t('auth.signUpNow')}
)} {t('auth.backToHome')} > )}