"use client"; import { signIn } from "next-auth/react"; import { type FormEvent, useState } from "react"; import { precheckLogin } from "@/actions/auth-precheck"; import { type CaptchaPublicConfig, CaptchaWidget, readCaptchaToken, } from "@/components/auth/captcha-widget"; import { signInWithTransientRetry } from "@/lib/auth/sign-in-retry"; export function HomeLoginForm({ captcha = { provider: "none" }, nonce, }: { captcha?: CaptchaPublicConfig; nonce?: string; } = {}) { const [username, setUsername] = useState(""); const [password, setPassword] = useState(""); const [showPassword, setShowPassword] = useState(false); const [code, setCode] = useState(""); const [needs2fa, setNeeds2fa] = useState(false); const [error, setError] = useState(null); const [pending, setPending] = useState(false); async function onSubmit(e: FormEvent) { e.preventDefault(); setError(null); setPending(true); try { if (!needs2fa) { const captchaToken = readCaptchaToken(e.currentTarget, captcha); const pre = await precheckLogin(username, password, captchaToken); if (pre === "invalid") { setError("Invalid username or password"); return; } if (pre === "captcha") { setError("Captcha verification failed. Please try again."); return; } if (pre === "unverified") { setError("Please verify your email before signing in."); return; } if (pre === "twofactor") { setNeeds2fa(true); return; } } const res = await signInWithTransientRetry(() => signIn("credentials", { username, password, code, redirect: false, }), ); if (!res || res.error) { setError( needs2fa ? "Invalid 2FA code" : "Invalid username or password", ); return; } window.location.href = "/"; } catch { setError(needs2fa ? "Invalid 2FA code" : "Invalid username or password"); } finally { setPending(false); } } return (
setUsername(e.target.value)} placeholder="Your username" autoComplete="username" disabled={needs2fa} className="input-glow w-full rounded-xl border-2 px-4 py-3 text-sm font-medium transition-all focus:outline-none disabled:opacity-50" style={{ backgroundColor: "var(--color-background)", color: "var(--color-text-readable)", borderColor: needs2fa ? "color-mix(in srgb, var(--color-text-muted) 15%, transparent)" : "color-mix(in srgb, var(--color-text-muted) 25%, transparent)", }} required />
setPassword(e.target.value)} placeholder="Your password" autoComplete="current-password" disabled={needs2fa} className="input-glow w-full rounded-xl border-2 px-4 py-3 text-sm font-medium transition-all focus:outline-none disabled:opacity-50" style={{ backgroundColor: "var(--color-background)", color: "var(--color-text-readable)", borderColor: needs2fa ? "color-mix(in srgb, var(--color-text-muted) 15%, transparent)" : "color-mix(in srgb, var(--color-text-muted) 25%, transparent)", }} required />
{needs2fa ? (
setCode(e.target.value)} placeholder="Enter your 2FA code" inputMode="numeric" autoComplete="one-time-code" className="w-full rounded-xl border-2 px-4 py-3 text-sm font-medium transition-all focus:outline-none" style={{ backgroundColor: "var(--color-background)", color: "var(--color-text-readable)", borderColor: "color-mix(in srgb, var(--color-primary) 30%, transparent)", }} />
) : ( )} {error ? (

{error}

) : null} ); }