import axios from "axios"; import { FormEvent, useEffect, useState } from "react"; import { checkEmailStatus, registerRequest, requestPasswordResetRequest, resetPasswordRequest } from "../api/auth"; import { useAuthStore } from "../store/authStore"; type Step = "email" | "password" | "register" | "otp" | "forgot" | "reset"; const AUTH_NOTICE_KEY = "bm_auth_notice"; interface AuthPanelProps { initialResetToken?: string | null; onResetTokenConsumed?: () => void; } export function AuthPanel({ initialResetToken = null, onResetTokenConsumed }: AuthPanelProps) { const login = useAuthStore((s) => s.login); const loading = useAuthStore((s) => s.loading); const [step, setStep] = useState("email"); const [email, setEmail] = useState(""); const [name, setName] = useState(""); const [username, setUsername] = useState(""); const [password, setPassword] = useState(""); const [otpCode, setOtpCode] = useState(""); const [recoveryCode, setRecoveryCode] = useState(""); const [resetToken, setResetToken] = useState(initialResetToken || ""); const [newPassword, setNewPassword] = useState(""); const [useRecoveryCode, setUseRecoveryCode] = useState(false); const [checkingEmail, setCheckingEmail] = useState(false); const [error, setError] = useState(null); const [success, setSuccess] = useState(null); useEffect(() => { const notice = window.localStorage.getItem(AUTH_NOTICE_KEY); if (!notice) { return; } setSuccess(notice); window.localStorage.removeItem(AUTH_NOTICE_KEY); }, []); useEffect(() => { const prepared = initialResetToken?.trim() || ""; if (!prepared) { return; } setResetToken(prepared); setStep("reset"); setSuccess("Enter a new password to finish reset."); onResetTokenConsumed?.(); }, [initialResetToken, onResetTokenConsumed]); async function onSubmit(event: FormEvent) { event.preventDefault(); setError(null); setSuccess(null); try { if (step === "email") { const normalizedEmail = email.trim().toLowerCase(); if (!normalizedEmail) { setError("Enter email."); return; } setCheckingEmail(true); try { const status = await checkEmailStatus(normalizedEmail); setEmail(normalizedEmail); setStep(status.registered ? "password" : "register"); } finally { setCheckingEmail(false); } return; } if (step === "register") { await registerRequest(email, name, username, password); setSuccess("Account created. Verify email and continue login."); setStep("password"); return; } if (step === "password") { await login(email, password); return; } if (step === "forgot") { const normalizedEmail = email.trim().toLowerCase(); if (!normalizedEmail) { setError("Enter email."); return; } await requestPasswordResetRequest(normalizedEmail); setSuccess("If account exists, reset email was sent."); setStep("password"); return; } if (step === "reset") { const preparedToken = resetToken.trim(); if (!preparedToken) { setError("Reset token is required."); return; } if (newPassword.trim().length < 8) { setError("Password must be at least 8 characters."); return; } await resetPasswordRequest(preparedToken, newPassword.trim()); setSuccess("Password reset successful. Sign in with new password."); setStep("password"); setNewPassword(""); return; } await login( email, password, useRecoveryCode ? undefined : (otpCode.trim() || undefined), useRecoveryCode ? (recoveryCode.trim() || undefined) : undefined ); } catch (err) { const message = getErrorMessage(err); if (step === "password" && message.toLowerCase().includes("2fa code required")) { setStep("otp"); setError(null); setSuccess("Enter 2FA code or use a recovery code."); return; } setError(message); setSuccess(null); } } function resetToEmail() { setStep("email"); setPassword(""); setOtpCode(""); setRecoveryCode(""); setUseRecoveryCode(false); setName(""); setUsername(""); setNewPassword(""); setError(null); setSuccess(null); } const submitLabel = step === "email" ? "Continue" : step === "register" ? "Create account" : step === "forgot" ? "Send reset email" : step === "reset" ? "Reset password" : step === "password" ? "Next" : "Sign in"; const isBusy = loading || checkingEmail; return (

{step === "email" ? "Sign in to BenyaMessenger" : step === "register" ? "Create account" : "Enter credentials"}

{step === "email" ? "Enter your email to continue" : step === "register" ? "This email is not registered yet. Complete registration." : step === "password" ? "Enter your password" : "Two-factor authentication is enabled"}

setEmail(e.target.value)} /> {step !== "email" ? ( ) : null}
{step === "register" ? ( <> setName(e.target.value)} /> setUsername(e.target.value.replace("@", ""))} /> ) : null} {step === "forgot" ? (

We'll send a password reset link if this account exists.

) : null} {step === "password" || step === "register" || step === "otp" ? ( setPassword(e.target.value)} /> ) : null} {step === "reset" ? ( <> setResetToken(e.target.value)} /> setNewPassword(e.target.value)} /> ) : null} {step === "otp" ? ( <> {useRecoveryCode ? ( setRecoveryCode(e.target.value.toUpperCase().replace(/[^A-Z0-9-]/g, "").slice(0, 24))} /> ) : ( setOtpCode(e.target.value.replace(/\D/g, "").slice(0, 8))} /> )} ) : null} {step === "password" ? ( ) : null} {(step === "email" || step === "forgot" || step === "password") ? ( ) : null}
{error ?

{error}

: null} {success ?

{success}

: null}
); } function getErrorMessage(err: unknown): string { if (axios.isAxiosError(err)) { const detail = err.response?.data?.detail; if (typeof detail === "string" && detail.trim()) { return detail; } if (typeof err.message === "string" && err.message.trim()) { return err.message; } } if (err instanceof Error && err.message) { return err.message; } return "Auth request failed."; }