import { useState } from "react"; import { auth } from "../api"; import type { UserInfo } from "../types"; import "./LoginModal.css"; interface Props { onClose: () => void; onLogin: (user: UserInfo) => void; } type Tab = "login" | "register"; export default function LoginModal({ onClose, onLogin }: Props) { const [tab, setTab] = useState("login"); const [username, setUsername] = useState(""); const [password, setPassword] = useState(""); const [confirm, setConfirm] = useState(""); const [error, setError] = useState(null); const [loading, setLoading] = useState(false); async function handleSubmit(e: React.FormEvent) { e.preventDefault(); setError(null); if (tab === "register" && password !== confirm) { setError("Passwords do not match"); return; } setLoading(true); try { if (tab === "login") { await auth.loginLocal(username, password); } else { await auth.register(username, password); } // Cookie is now set server-side; fetch user info to update parent const user = await auth.me(); if (user) { onLogin(user); onClose(); } else { setError("Login succeeded but could not load user info."); } } catch (err: unknown) { const msg = err instanceof Error ? err.message : String(err); // Extract the human-readable part (strip leading status code) setError( msg .replace(/^\d+:\s*/, "") .replace(/\{.*\}/s, "") .trim() || "Authentication failed", ); } finally { setLoading(false); } } async function handleDiscordLogin() { try { await auth.loginDiscord(window.location.origin + "/map"); } catch (err) { console.error("Discord login failed:", err); } } return (
e.stopPropagation()}> {/* Tab switcher */}
{/* Username / password form */}
{tab === "register" && ( )} {error &&

{error}

}
or
{/* Discord OAuth */}
); }