import { faSpinner } from "@fortawesome/free-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { type FormEvent, useEffect, useState } from "react"; import { useLocation } from "wouter"; import { ApiClientError, useAuth } from "../stores"; export function LoginPage() { const [, navigate] = useLocation(); const { login, isAuthenticated } = useAuth(); const [username, setUsername] = useState(""); const [password, setPassword] = useState(""); const [error, setError] = useState(null); const [isSubmitting, setIsSubmitting] = useState(false); // Redirect if already authenticated useEffect(() => { if (isAuthenticated) { navigate("/", { replace: true }); } }, [isAuthenticated, navigate]); const handleSubmit = async (e: FormEvent) => { e.preventDefault(); setError(null); setIsSubmitting(true); try { await login(username, password); navigate("/", { replace: true }); } catch (err) { if (err instanceof ApiClientError) { setError(err.message); } else { setError("Login failed. Please try again."); } } finally { setIsSubmitting(false); } }; return (
{/* Logo/Brand */}

Kioku

Your memory, amplified

{/* Login Card */}

Welcome back

{error && (
{error}
)}
setUsername(e.target.value)} required autoComplete="username" disabled={isSubmitting} className="w-full px-4 py-2.5 bg-ivory border border-border rounded-lg text-slate placeholder-muted transition-all duration-200 hover:border-muted focus:border-primary focus:ring-2 focus:ring-primary/10 disabled:opacity-50 disabled:cursor-not-allowed" placeholder="Enter your username" />
setPassword(e.target.value)} required autoComplete="current-password" disabled={isSubmitting} className="w-full px-4 py-2.5 bg-ivory border border-border rounded-lg text-slate placeholder-muted transition-all duration-200 hover:border-muted focus:border-primary focus:ring-2 focus:ring-primary/10 disabled:opacity-50 disabled:cursor-not-allowed" placeholder="Enter your password" />
{/* Footer note */}

Spaced repetition learning

); }