import { useState } from "react"; import { useNavigate } from "react-router-dom"; import { login, loginTotp, getMe } from "@/api/auth"; import { useAuthStore } from "@/store/authStore"; import { DollarSign, Eye, EyeOff, Loader2, ShieldCheck } from "lucide-react"; export default function LoginPage() { const navigate = useNavigate(); const { setToken, setTotpEnabled } = useAuthStore(); const [email, setEmail] = useState(""); const [password, setPassword] = useState(""); const [showPassword, setShowPassword] = useState(false); const [totpCode, setTotpCode] = useState(""); const [challengeToken, setChallengeToken] = useState(null); const [error, setError] = useState(null); const [loading, setLoading] = useState(false); async function handleLogin(e: React.FormEvent) { e.preventDefault(); if (!email || !password) { setError("Please enter your email and password."); return; } setError(null); setLoading(true); try { const res = await login(email, password); if (res.totp_required && res.challenge_token) { setChallengeToken(res.challenge_token); return; } if (res.access_token) { // Set token first so getMe() has the Authorization header setToken(res.access_token, "", ""); const me = await getMe(); setToken(res.access_token, me.id, me.display_name ?? me.email); setTotpEnabled(me.totp_enabled); navigate("/"); } } catch (e: unknown) { const detail = (e as { response?: { data?: { detail?: unknown } } }).response?.data?.detail; if (typeof detail === "string") { setError(detail); } else if (Array.isArray(detail)) { setError((detail[0] as { msg?: string })?.msg ?? "Login failed"); } else { setError("Login failed. Check your credentials and try again."); } } finally { setLoading(false); } } async function handleTotp(e: React.FormEvent) { e.preventDefault(); if (!challengeToken) return; setError(null); setLoading(true); try { const res = await loginTotp(challengeToken, totpCode); if (res.access_token) { setToken(res.access_token, "", ""); const me = await getMe(); setToken(res.access_token, me.id, me.display_name ?? me.email); setTotpEnabled(me.totp_enabled); navigate("/"); } } catch { setError("Invalid TOTP code. Try again."); } finally { setLoading(false); } } return (
Finance Tracker
{!challengeToken ? ( <>

Sign in

setEmail(e.target.value)} autoComplete="email" autoFocus className="w-full rounded-md border border-input bg-background px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-ring" placeholder="you@example.com" />
setPassword(e.target.value)} autoComplete="current-password" className="w-full rounded-md border border-input bg-background px-3 py-2 pr-10 text-sm focus:outline-none focus:ring-2 focus:ring-ring" />
{error && (

{error}

)}
) : ( <>

Two-factor authentication

Enter the 6-digit code from your authenticator app.

setTotpCode(e.target.value)} autoComplete="one-time-code" autoFocus className="w-full rounded-md border border-input bg-background px-3 py-2 text-sm text-center tracking-widest text-lg font-mono focus:outline-none focus:ring-2 focus:ring-ring" placeholder="000000" /> {error && (

{error}

)}
)}
); }