"use client"; import { appTitle } from "@/lib/public-env"; import { motion } from "framer-motion"; import { signIn } from "next-auth/react"; import Link from "next/link"; import { useRouter } from "next/navigation"; import { useState } from "react"; export function LoginForm({ callbackUrl }: { callbackUrl: string }) { const router = useRouter(); const registerHref = `/register?callbackUrl=${encodeURIComponent(callbackUrl)}`; const [emailOrUsername, setEmailOrUsername] = useState(""); const [password, setPassword] = useState(""); const [error, setError] = useState(null); const [busy, setBusy] = useState(false); const submit = async (e: React.FormEvent) => { e.preventDefault(); setBusy(true); setError(null); const res = await signIn("credentials", { email: emailOrUsername.trim(), password, redirect: false, callbackUrl, }); setBusy(false); if (res?.error) { setError("Invalid email/username or password."); return; } router.push(callbackUrl); router.refresh(); }; return (
{/* Background glow */}
{/* Inner border shimmer */}

Secure portal access

Sign in

Supporter login for{" "} {appTitle()}

Access is restricted to credentials issued by the committee. Unauthorized use may violate law or committee policy.

setEmailOrUsername(e.target.value)} className="mt-2 w-full rounded-xl border border-white/10 bg-black/40 px-4 py-3 text-base text-white placeholder-slate-600 outline-none ring-sky-500/40 transition focus:border-sky-500/40 focus:ring" placeholder="you@example.com or supporter_name" />
Forgot password?
setPassword(e.target.value)} className="mt-2 w-full rounded-xl border border-white/10 bg-black/40 px-4 py-3 text-base text-white placeholder-slate-600 outline-none ring-sky-500/40 transition focus:border-sky-500/40 focus:ring" placeholder="••••••••" />
{error ? ( {error} ) : null} {busy ? "Signing in…" : "Continue →"}

Need credentials?{" "} Create an account

); }