// Auth.jsx — Login, Register, ForgotPassword
const { useState: useAuthState } = React;
// Shared layout: a centered card on a soft tinted background.
const AuthShell = ({ children, side }) => (
);
// Reused side panel — brand block with bullets
const AuthSidePanel = ({ title, bullets }) => (
{title}
{bullets.map((b, i) => (
))}
);
// Form primitives
const Field = ({ label, hint, error, children }) => (
{label}
{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 }) => (
e.currentTarget.style.background = "var(--primary-hover)"}
onMouseLeave={e => e.currentTarget.style.background = "var(--primary)"}
>{children}
);
// ----- 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 => (
{ setMode(t.id); setError(null); setOtpSent(false); }}
style={{
padding: "10px 16px", borderRadius: "9999px",
background: mode === t.id ? "var(--bg-elev-1)" : "transparent",
color: mode === t.id ? "var(--fg-strong)" : "var(--fg-muted)",
border: 0, font: mode === t.id ? "700 13px var(--font-sans)" : "500 13px var(--font-sans)",
cursor: "pointer", display: "flex", alignItems: "center", justifyContent: "center", gap: 6,
boxShadow: mode === t.id ? "var(--elev-1)" : "none",
transition: "all var(--dur-fast) var(--ease-out-quart)",
}}>
{t.label}
))}
{mode === "email" && (
)}
{mode === "phone" && !otpSent && (
)}
{mode === "phone" && otpSent && (
Código enviado a +52 {phone}
{ setOtpSent(false); setOtp(["", "", "", "", "", ""]); setError(null); }}
style={{ background: "transparent", border: 0, color: "var(--primary)", font: "700 12px var(--font-sans)", cursor: "pointer", padding: 0, marginLeft: "auto" }}>
Cambiar número
Código de 6 dígitos
{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
);
};
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.
);
};
// ----- 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 ? (
) : (
Revisa tu correo
Te enviamos un enlace a {email} . Expira en 30 minutos.
onNav("login")} style={{ alignSelf: "flex-start", background: "transparent", border: 0, color: "var(--primary)", font: "700 13px var(--font-sans)", cursor: "pointer" }}>Volver a iniciar sesión →
)}
);
};
window.LoginPage = LoginPage;
window.RegisterPage = RegisterPage;
window.ForgotPage = ForgotPage;
// Form primitives reused by Account and Seller — must be on window because
// each