// Auth.jsx — Login, Register, ForgotPassword const { useState: useAuthState } = React; // Shared layout: a centered card on a soft tinted background. const AuthShell = ({ children, side }) => (
{children}
{side}
); // Reused side panel — brand block with bullets const AuthSidePanel = ({ title, bullets }) => (
Allen

{title}

); // Form primitives const Field = ({ label, hint, error, children }) => (
{children} {hint && !error && {hint}} {error && {error}}
); const inputStyle = { padding: "12px 14px", border: "1.5px solid var(--border-strong)", borderRadius: "var(--radius-input)", font: "400 14px var(--font-sans)", background: "var(--bg-elev-1)", color: "var(--fg-strong)", outline: "none", transition: "border-color var(--dur-fast)", }; const TextInput = (props) => ( { e.target.style.borderColor = "var(--primary)"; e.target.style.outline = "2px solid var(--primary-soft)"; e.target.style.outlineOffset = "0"; }} onBlur={e => { e.target.style.borderColor = "var(--border-strong)"; e.target.style.outline = "none"; }} /> ); const PrimaryButton = ({ children, ...p }) => ( ); // ----- Login page ----- const LoginPage = ({ onNav, onLogin }) => { const [mode, setMode] = useAuthState("email"); // 'email' | 'phone' const [email, setEmail] = useAuthState(""); const [pass, setPass] = useAuthState(""); const [show, setShow] = useAuthState(false); const [phone, setPhone] = useAuthState(""); const [otpSent, setOtpSent] = useAuthState(false); const [otp, setOtp] = useAuthState(["", "", "", "", "", ""]); const [resendIn, setResendIn] = useAuthState(0); const [error, setError] = useAuthState(null); const toast = useToast(); // OTP resend countdown React.useEffect(() => { if (resendIn <= 0) return; const t = setTimeout(() => setResendIn(n => n - 1), 1000); return () => clearTimeout(t); }, [resendIn]); const sendOtp = () => { // Demo mode: accept any phone, including empty setError(null); setOtpSent(true); setResendIn(45); toast(`Código enviado al ${phone || "+52 322 000 0000"}`); }; const verifyOtp = (codeArr) => { // Demo mode: accept any (or no) code setError(null); const last4 = phone.replace(/\D/g, "").slice(-4) || "0000"; onLogin({ name: `Usuario ${last4}`, phone: phone || "+52 322 000 0000" }); toast(`¡Hola de nuevo!`); onNav("home"); }; const onOtpChange = (i, val) => { const v = val.replace(/\D/g, "").slice(0, 1); const next = [...otp]; next[i] = v; setOtp(next); // auto-advance if (v && i < 5) { const el = document.getElementById(`otp-${i + 1}`); el?.focus(); } if (next.every(d => d) && i === 5) verifyOtp(next); }; const onOtpKey = (i, e) => { if (e.key === "Backspace" && !otp[i] && i > 0) { document.getElementById(`otp-${i - 1}`)?.focus(); } }; const submit = (e) => { e.preventDefault(); // Demo mode: accept any input setError(null); const name = email ? email.split("@")[0] : "Invitado"; onLogin({ name, email: email || "demo@allenmx.com" }); toast(`¡Hola de nuevo!`); onNav("home"); }; return ( }>
Mi cuenta

Inicia sesión

Usa tu correo o tu teléfono para entrar.

{/* Mode tabs: Email | Phone */}
{[ { id: "email", label: "Correo", icon: "person" }, { id: "phone", label: "Teléfono", icon: "support_agent" }, ].map(t => ( ))}
{mode === "email" && (
setEmail(e.target.value)} autoComplete="email" />
setPass(e.target.value)} style={{ width: "100%", paddingRight: 44 }} autoComplete="current-password" />
onNav("forgot")} style={{ font: "600 13px var(--font-sans)", color: "var(--primary)", cursor: "pointer" }}>¿Olvidaste tu contraseña?
{error && (
{error}
)} Entrar
)} {mode === "phone" && !otpSent && (
{ e.preventDefault(); sendOtp(); }} style={{ display: "flex", flexDirection: "column", gap: 14 }}>
🇲🇽 +52
setPhone(e.target.value.replace(/[^\d ]/g, ""))} maxLength={13} style={{ flex: 1, fontFamily: "var(--font-mono)", letterSpacing: ".05em" }} />
{error && (
{error}
)} Enviar código
Tu número está seguro. Solo lo usamos para verificación y entregas.
)} {mode === "phone" && otpSent && (
Código enviado a +52 {phone}
{otp.map((d, i) => ( onOtpChange(i, e.target.value)} onKeyDown={e => onOtpKey(i, e)} inputMode="numeric" autoComplete="one-time-code" maxLength={1} style={{ ...inputStyle, padding: "16px 0", textAlign: "center", font: "700 22px var(--font-mono)", letterSpacing: 0, borderColor: d ? "var(--primary)" : "var(--border-strong)", }} /> ))}
{error && (
{error}
)} verifyOtp()}>Verificar y entrar
{resendIn > 0 ? ( <>Reenvía el código en {resendIn}s ) : ( Reenviar código )}
)}
o continúa con
¿Aún no tienes cuenta? onNav("register")} style={{ color: "var(--primary)", fontWeight: 700, cursor: "pointer" }}>Crea una en 1 minuto
); }; const socialBtn = { background: "var(--bg-elev-1)", color: "var(--fg-strong)", border: "1.5px solid var(--border-strong)", padding: "12px 16px", borderRadius: "9999px", font: "700 14px var(--font-sans)", display: "flex", alignItems: "center", justifyContent: "center", gap: 10, cursor: "pointer", transition: "background var(--dur-fast)", }; // ----- Register page ----- const RegisterPage = ({ onNav, onLogin }) => { const [step, setStep] = useAuthState(1); const [form, setForm] = useAuthState({ name: "", email: "", phone: "", pass: "", accept: false, marketing: true }); const [error, setError] = useAuthState(null); const toast = useToast(); const set = (k, v) => setForm(f => ({ ...f, [k]: v })); const submit = (e) => { e.preventDefault(); // Demo mode: accept any input setError(null); onLogin({ name: form.name || "Invitado", email: form.email || "demo@allenmx.com" }); toast(`¡Bienvenido, ${(form.name || "Invitado").split(" ")[0]}!`); onNav("home"); }; return ( }>
Mi cuenta

Crea tu cuenta

Solo necesitamos lo básico. Te toma menos de 1 minuto.

set("name", e.target.value)} />
set("email", e.target.value)} /> set("phone", e.target.value)} />
set("pass", e.target.value)} /> {error && (
{error}
)} Crear cuenta
¿Ya tienes cuenta? onNav("login")} style={{ color: "var(--primary)", fontWeight: 700, cursor: "pointer" }}>Inicia sesión
); }; // ----- Forgot password ----- const ForgotPage = ({ onNav }) => { const [email, setEmail] = useAuthState(""); const [sent, setSent] = useAuthState(false); return ( }> onNav("login")} style={{ font: "600 13px var(--font-sans)", color: "var(--primary)", cursor: "pointer", display: "inline-flex", alignItems: "center", gap: 4 }}> Volver a iniciar sesión

Recupera tu contraseña

Ingresa tu correo. Te enviamos un enlace para crear una nueva contraseña.

{!sent ? (
{ e.preventDefault(); setSent(true); }} style={{ display: "flex", flexDirection: "column", gap: 14 }}> setEmail(e.target.value)} /> Enviar enlace
) : (
Revisa tu correo
Te enviamos un enlace a {email}. Expira en 30 minutos.
)}
); }; window.LoginPage = LoginPage; window.RegisterPage = RegisterPage; window.ForgotPage = ForgotPage; // Form primitives reused by Account and Seller — must be on window because // each