/* Login / Register — flujo de auth con email + password. * * Modos: * - "login": pide email + password, hace POST /auth/login. * - "register": pide email + password (+ nombre opcional), hace POST /auth/register * y, si 201, hace login automático y redirige a onboarding. * - "demo": no toca el backend, setea api_fit_demo=1 y entra a la app con MOCKs. * * Onboarding/instalación nueva: si el backend responde GET /auth/has_users { has_users: false } * el modo arranca en "register" para forzar la creación del primer usuario. */ const Login = ({ onAuthenticated, onRegisteredFirstTime }) => { const [mode, setMode] = React.useState("login"); // login | register const [email, setEmail] = React.useState(""); const [password, setPassword] = React.useState(""); const [name, setName] = React.useState(""); const [base, setBase] = React.useState(API.getBase()); const [showHelp, setShowHelp] = React.useState(false); const [status, setStatus] = React.useState({ kind: "idle" }); const [bootChecked, setBootChecked] = React.useState(false); // Boot: chequear si hay usuarios en el sistema. Si no, forzar registro. React.useEffect(() => { let alive = true; (async () => { try { const res = await API.authHasUsers(); if (!alive) return; if (res && res.has_users === false) { setMode("register"); } } catch (_) { // backend caído o endpoint no implementado todavía: dejamos modo login. } finally { if (alive) setBootChecked(true); } })(); return () => { alive = false; }; }, []); const validate = () => { if (!email.includes("@")) return "Ingresá un email válido."; if (mode === "register" && password.length < 8) return "La contraseña debe tener al menos 8 caracteres."; if (!password) return "Ingresá una contraseña."; return null; }; const submit = async () => { const err = validate(); if (err) { setStatus({ kind: "bad", msg: err }); return; } if (base) API.setBase(base); setStatus({ kind: "loading" }); try { if (mode === "register") { await API.authRegister({ email: email.trim(), password, name: name.trim() || undefined }); // Auto-login después de registrar. await API.authLogin({ email: email.trim(), password }); // Limpiar flag de demo si quedaba. API.setDemoMode(false); setStatus({ kind: "ok", msg: "Cuenta creada. Entrando…" }); // Primera ejecución: ir al onboarding. setTimeout(() => (onRegisteredFirstTime || onAuthenticated)(), 250); } else { await API.authLogin({ email: email.trim(), password }); API.setDemoMode(false); setStatus({ kind: "ok", msg: "Sesión iniciada." }); setTimeout(onAuthenticated, 200); } } catch (e) { let msg; if (e.network) { msg = `El servidor no responde (${API.getBase() || "mismo origen"}). ¿Está corriendo el backend?`; } else if (e.status === 401) { msg = "Email o contraseña incorrectos."; } else if (e.status === 409) { msg = "Ese email ya está registrado. Probá iniciar sesión."; } else if (e.status === 400) { const detail = e.detail?.detail || ""; if (/password/i.test(detail)) msg = "La contraseña debe tener al menos 8 caracteres."; else if (/email/i.test(detail)) msg = "Email inválido."; else msg = detail || e.message; } else if (e.status >= 500) { msg = "El servidor tuvo un error. Probá de nuevo en un momento."; } else { msg = e.message || "Algo no anduvo."; } setStatus({ kind: "bad", msg }); } }; const skipToDemo = () => { API.setDemoMode(true); API.clearToken(); API.clearUser(); onAuthenticated(); }; const onKeyDown = (e) => { if (e.key === "Enter") submit(); }; return (