import { useState, useEffect } from "react"; import type { FormEvent } from "react"; import { useNavigate, useLocation } from "react-router-dom"; import { useAuthStore } from "../../stores/authStore"; import { Loader2, Mail, Lock } from "lucide-react"; interface LocationState { from?: { pathname: string }; } const DOTS = [ { size: 12, color: "#f97316", top: "8%", left: "6%", delay: "0s" }, { size: 7, color: "#a855f7", top: "22%", left: "3%", delay: "1.2s" }, { size: 9, color: "#22c55e", top: "65%", left: "5%", delay: "0.6s" }, { size: 8, color: "#f43f5e", top: "80%", left: "8%", delay: "2.1s" }, { size: 12, color: "#3b82f6", top: "10%", right: "6%", delay: "1.8s" }, { size: 7, color: "#eab308", top: "40%", right: "3%", delay: "0.9s" }, { size: 10, color: "#a855f7", top: "72%", right: "5%", delay: "0.4s" }, { size: 8, color: "#f97316", top: "55%", right: "8%", delay: "1.5s" }, ]; const STYLES = ` @import url('https://fonts.googleapis.com/css2?family=Nunito:wght@400;700;800;900&family=Nunito+Sans:wght@400;600;700&display=swap'); .lg-screen { min-height: 100vh; background: #fffbf4; font-family: 'Nunito', sans-serif; position: relative; overflow: hidden; display: flex; align-items: center; justify-content: center; padding: 2rem 1.25rem; } /* Blobs */ .lg-blob { position:fixed;pointer-events:none;z-index:0; } .lg-blob-1 { width:280px;height:280px;background:#fde68a;top:-100px;left:-100px;border-radius:60% 40% 70% 30%/50% 60% 40% 50%;animation:lgWobble1 14s ease-in-out infinite; } .lg-blob-2 { width:220px;height:220px;background:#a5f3c0;bottom:-60px;left:4%;border-radius:40% 60% 30% 70%/60% 40% 60% 40%;animation:lgWobble2 16s ease-in-out infinite; } .lg-blob-3 { width:250px;height:250px;background:#fbcfe8;top:10%;right:-70px;border-radius:70% 30% 50% 50%/40% 60% 40% 60%;animation:lgWobble1 18s ease-in-out infinite reverse; } .lg-blob-4 { width:180px;height:180px;background:#bfdbfe;bottom:8%;right:0;border-radius:50% 50% 30% 70%/60% 40% 60% 40%;animation:lgWobble2 12s ease-in-out infinite; } @keyframes lgWobble1 { 0%,100%{border-radius:60% 40% 70% 30%/50% 60% 40% 50%;transform:translate(0,0) rotate(0deg);} 50%{border-radius:40% 60% 30% 70%/60% 40% 60% 40%;transform:translate(14px,18px) rotate(8deg);} } @keyframes lgWobble2 { 0%,100%{border-radius:40% 60% 30% 70%/60% 40% 60% 40%;transform:translate(0,0) rotate(0deg);} 50%{border-radius:60% 40% 70% 30%/40% 60% 40% 60%;transform:translate(-12px,14px) rotate(-6deg);} } .lg-dot { position:fixed;border-radius:50%;pointer-events:none;z-index:0;opacity:0.28;animation:lgFloat 7s ease-in-out infinite; } @keyframes lgFloat { 0%,100%{transform:translateY(0) rotate(0deg);} 50%{transform:translateY(-14px) rotate(180deg);} } /* Card */ .lg-card { position: relative; z-index: 1; width: 100%; max-width: 400px; background: white; border: 2.5px solid #f3f4f6; border-radius: 28px; box-shadow: 0 12px 40px rgba(0,0,0,0.08), 0 4px 12px rgba(0,0,0,0.04); padding: 2.25rem 2rem 2rem; display: flex; flex-direction: column; gap: 1.75rem; animation: lgPopIn 0.5s cubic-bezier(0.34,1.56,0.64,1) both; } @keyframes lgPopIn { from { opacity:0; transform:scale(0.9) translateY(20px); } to { opacity:1; transform:scale(1) translateY(0); } } /* Logo area */ .lg-logo-wrap { display: flex; flex-direction: column; align-items: center; gap: 0.85rem; } .lg-logo-badge { width: 64px; height: 64px; border-radius: 20px; background: linear-gradient(135deg, #a855f7, #7c3aed); display: flex; align-items: center; justify-content: center; box-shadow: 0 6px 0 #5b21b655, 0 10px 24px rgba(124,58,237,0.25); font-size: 1.75rem; animation: lgPopIn 0.5s cubic-bezier(0.34,1.56,0.64,1) 0.1s both; } .lg-title { font-size: 1.5rem; font-weight: 900; color: #1e1b4b; letter-spacing: -0.02em; text-align: center; } .lg-sub { font-family: 'Nunito Sans', sans-serif; font-size: 0.82rem; font-weight: 600; color: #9ca3af; text-align: center; margin-top: -0.25rem; } /* Form fields */ .lg-fields { display: flex; flex-direction: column; gap: 1rem; } .lg-field { display: flex; flex-direction: column; gap: 0.4rem; } .lg-label { font-size: 0.72rem; font-weight: 800; letter-spacing: 0.1em; text-transform: uppercase; color: #6b7280; padding-left: 0.25rem; } .lg-input-wrap { position: relative; } .lg-input-icon { position: absolute; left: 0.85rem; top: 50%; transform: translateY(-50%); pointer-events: none; color: #9ca3af; transition: color 0.2s ease; } .lg-input { width: 100%; padding: 0.8rem 1rem 0.8rem 2.6rem; background: #f9fafb; border: 2.5px solid #f3f4f6; border-radius: 14px; font-family: 'Nunito Sans', sans-serif; font-size: 0.88rem; font-weight: 600; color: #1e1b4b; outline: none; transition: all 0.2s ease; box-sizing: border-box; } .lg-input:focus { background: white; border-color: #c4b5fd; box-shadow: 0 0 0 3px rgba(168,85,247,0.1); } .lg-input:focus ~ .lg-input-icon { color: #a855f7; } .lg-input:disabled { opacity: 0.5; cursor: not-allowed; } .lg-input::placeholder { color: #d1d5db; } /* Remember me */ .lg-remember { display: flex; align-items: center; gap: 0.5rem; padding: 0 0.1rem; } .lg-checkbox { width: 18px; height: 18px; border-radius: 6px; accent-color: #a855f7; cursor: pointer; flex-shrink: 0; } .lg-remember-label { font-family: 'Nunito Sans', sans-serif; font-size: 0.8rem; font-weight: 600; color: #6b7280; cursor: pointer; } /* Error */ .lg-error { background: #fff1f2; border: 2px solid #fecdd3; border-radius: 14px; padding: 0.75rem 1rem; font-family: 'Nunito Sans', sans-serif; font-size: 0.82rem; font-weight: 700; color: #e11d48; display: flex; align-items: center; gap: 0.5rem; } /* Submit button */ .lg-btn { width: 100%; padding: 0.95rem; background: #f97316; color: white; border: none; border-radius: 100px; cursor: pointer; font-family: 'Nunito', sans-serif; font-size: 0.95rem; font-weight: 900; display: flex; align-items: center; justify-content: center; gap: 0.5rem; box-shadow: 0 6px 0 #c2560e, 0 8px 20px rgba(249,115,22,0.25); transition: transform 0.1s ease, box-shadow 0.1s ease; } .lg-btn:hover { transform:translateY(-2px); box-shadow:0 8px 0 #c2560e,0 12px 24px rgba(249,115,22,0.3); } .lg-btn:active { transform:translateY(3px); box-shadow:0 3px 0 #c2560e; } .lg-btn:disabled { background: #e5e7eb; color: #9ca3af; cursor: not-allowed; box-shadow: 0 4px 0 #d1d5db; } .lg-btn:disabled:hover { transform: none; box-shadow: 0 4px 0 #d1d5db; } .lg-spinner { animation: lgSpin 0.8s linear infinite; } @keyframes lgSpin { to { transform: rotate(360deg); } } /* Footer hint */ .lg-footer { text-align: center; font-family: 'Nunito Sans', sans-serif; font-size: 0.75rem; font-weight: 600; color: #9ca3af; } .rg-footer { text-align: center; font-family: 'Nunito Sans', sans-serif; font-size: 0.78rem; font-weight: 600; color: #9ca3af; } .rg-link { color: #a855f7; font-weight: 800; text-decoration: none; transition: color 0.2s ease; } .rg-link:hover { color: #7c3aed; } `; export const Login = () => { const [email, setEmail] = useState(""); const [password, setPassword] = useState(""); const navigate = useNavigate(); const location = useLocation(); const { login, isAuthenticated, isLoading, error, clearError } = useAuthStore(); const from = (location.state as LocationState)?.from?.pathname || "/student"; useEffect(() => { if (isAuthenticated) navigate("/student/home", { replace: true }); }, [isAuthenticated, navigate]); useEffect(() => { return () => clearError(); }, [clearError]); const handleSubmit = async (e: FormEvent) => { e.preventDefault(); clearError(); const success = await login({ email, password }); if (success) navigate(from, { replace: true }); }; if (isAuthenticated) return null; return (
{/* Blobs */}
{/* Dots */} {DOTS.map((d, i) => (
))}
{/* Logo + heading */}
EdBridge { (e.target as HTMLImageElement).style.display = "none"; }} />

Welcome back 👋

Sign in to continue your SAT prep

{/* Fields */}
{/* Email */}
setEmail(e.target.value)} disabled={isLoading} />
{/* Password */}
setPassword(e.target.value)} disabled={isLoading} />
{/* Remember me */}
{/* Error */} {error && (
⚠️ {error}
)} {/* Submit */}

By signing in you agree to Edbridge's Terms & Privacy Policy.

Don't have an account?{" "} Sign up

); };