function LoginForm({ tokenInput, setTokenInput, setToken, setAccount, address, setAddress, password, setPassword, loginMode, setLoginMode, loginError, setLoginError, resetToken, setResetToken, setResetMode }) { const [forgotSent, setForgotSent] = React.useState(false); const [remember, setRemember] = React.useState(() => !!getRememberedCreds()); React.useEffect(() => { const saved = getRememberedCreds(); if (saved) { setAddress(saved.address); setPassword(saved.password); } }, [setAddress, setPassword]); const onLogin = (e) => { e.preventDefault(); if (!address.trim() || !password.trim()) return; setLoginError(""); fetch("/api/login", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ address: address.trim(), password: password.trim() }), }) .then(async (r) => { const j = await r.json().catch(() => ({})); if (!j.ok) throw new Error(j.error?.message || j.error || "login failed"); return j.data; }) .then((data) => { setToken(data.token); if (data.address) { setAccount({ address: data.address, name: data.name || "" }); } if (remember) { setRememberedCreds({ address: address.trim(), password: password.trim() }); } else { clearRememberedCreds(); } }) .catch((e) => setLoginError(String(e.message || e))); }; const onToken = (e) => { e.preventDefault(); if (tokenInput.trim()) setToken(tokenInput.trim()); }; const onForgot = (e) => { e.preventDefault(); if (!address.trim()) return; setLoginError(""); setForgotSent(false); fetch("/api/forgot-password", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ address: address.trim() }), }) .then(async (r) => { const j = await r.json().catch(() => ({})); if (!j.ok) throw new Error(j.error?.message || j.error || "request failed"); setForgotSent(true); }) .catch((e) => setLoginError(String(e.message || e))); }; const onResetPassword = (e) => { e.preventDefault(); if (!resetToken.trim() || !password.trim()) return; setLoginError(""); fetch("/api/reset-password", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ token: resetToken.trim(), new_password: password.trim() }), }) .then(async (r) => { const j = await r.json().catch(() => ({})); if (!j.ok) throw new Error(j.error?.message || j.error || "reset failed"); setResetMode(false); setResetToken(""); setLoginMode("password"); setLoginError(""); alert("Password reset. Please log in with your new password."); }) .catch((e) => setLoginError(String(e.message || e))); }; // ─── reset password screen ────────────────────────────────────────── if (loginMode === "reset") { return (

Set new password

Enter the reset token and your new password.

{loginError && (
{loginError}
)}
setResetToken(e.target.value)} className="w-full bg-paper border border-paper-line rounded px-2 py-1.5 text-sm font-mono" placeholder="reset token" autoFocus /> setPassword(e.target.value)} className="w-full bg-paper border border-paper-line rounded px-2 py-1.5 text-sm" placeholder="new password" />
); } // ─── admin token screen ───────────────────────────────────────────── if (loginMode === "token") { return (

Admin token

Paste the Bearer token (WEBMAIL_TOKEN or ADMIN_TOKEN).

setTokenInput(e.target.value)} className="flex-1 bg-paper border border-paper-line rounded px-2 py-1.5 text-sm" placeholder="token" />
); } // ─── default: password login ──────────────────────────────────────── return (

Webmail login

Sign in with your email address and password.

{loginError && (
{loginError}
)} {forgotSent && (
If the address exists, a reset link was sent to the recovery email.
)}
setAddress(e.target.value)} className="w-full bg-paper border border-paper-line rounded px-2 py-1.5 text-sm" placeholder="email address" autoFocus /> setPassword(e.target.value)} className="w-full bg-paper border border-paper-line rounded px-2 py-1.5 text-sm" placeholder="password" />
); }