// AUTH — login, registro, recuperar, confirmar email window.AuthPages = {}; window.AuthPages.Login = function Login({ navigate, store }) { const [email, setEmail] = React.useState('maria.lopez@ejemplo.mx'); const [pass, setPass] = React.useState('demo1234'); const [err, setErr] = React.useState(''); const [loading, setLoading] = React.useState(false); const submit = async (e) => { e.preventDefault(); setErr(''); if (!email.includes('@') || pass.length < 4) { setErr('Verifica tu correo y contraseña.'); return; } setLoading(true); try { await store.login(email, pass); navigate('/dashboard'); } catch (ex) { if (ex.message && ex.message.toLowerCase().includes('fetch')) { store.loginDemo(email); navigate('/dashboard'); // API no disponible → demo } else { setErr(ex.message || 'Credenciales incorrectas'); } } finally { setLoading(false); } }; return (

Inicia sesión

Accede a tu panel y simuladores.

setEmail(e.target.value)} />
setPass(e.target.value)} />
{err &&
{err}
}
{e.preventDefault();navigate('/recuperar');}} className="muted">¿Olvidaste tu contraseña? {e.preventDefault();navigate('/registro');}}>Crear cuenta
Demo: usa cualquier correo y contraseña ≥ 4 caracteres.
); }; window.AuthPages.Registro = function Registro({ navigate, store }) { const BASE = window.API_BASE || '/simuladordocente/api'; const [step, setStep] = React.useState(1); const [data, setData] = React.useState({ nombre: '', email: '', pass: '' }); const [err, setErr] = React.useState(''); const submit = async (e) => { e.preventDefault(); setErr(''); try { const r = await fetch(`${BASE}/auth/registro`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ nombre: data.nombre, email: data.email, password: data.pass }), }); const d = await r.json(); if (!r.ok) { setErr(d.error || 'Error al registrarse'); return; } } catch {} // Si API falla, igual avanzamos (demo mode) setStep(2); }; if (step === 2) { return (
📬

Revisa tu correo

Enviamos un enlace de confirmación a {data.email}. Confirma tu correo para poder realizar pagos y desbloquear simuladores completos.

); } if (step === 3) { return (

¡Cuenta confirmada!

Ya puedes acceder a tu panel y probar las muestras gratuitas.

); } return (

Crea tu cuenta

Empieza con muestras gratis. Pago solo cuando estés listo.

setData({...data, nombre: e.target.value})} />
setData({...data, email: e.target.value})} />
setData({...data, pass: e.target.value})} />
Mínimo 8 caracteres recomendado.
{err &&
{err}
}
¿Ya tienes cuenta? {e.preventDefault();navigate('/login');}} style={{color:'var(--ink)', fontWeight: 600}}>Inicia sesión
); }; window.AuthPages.Recuperar = function Recuperar({ navigate }) { const BASE = window.API_BASE || '/simuladordocente/api'; const [sent, setSent] = React.useState(false); const [email, setEmail] = React.useState(''); const [loading, setLoading] = React.useState(false); const submit = async (e) => { e.preventDefault(); setLoading(true); try { await fetch(`${BASE}/auth/recuperar`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ email }), }); } catch {} setLoading(false); setSent(true); }; if (sent) { return (
📨

Enlace enviado

Si {email} existe en nuestro sistema, recibirás un correo con el enlace para restablecer tu contraseña.

Revisa también tu carpeta de spam.

); } return (

Recupera tu contraseña

Te enviaremos un enlace para crear una nueva contraseña.

setEmail(e.target.value)} />
{e.preventDefault();navigate('/login');}} style={{color:'var(--ink)', fontWeight: 600}}>← Volver
); }; // Página: restablecer contraseña (llegó desde el link del email) window.AuthPages.Reset = function Reset({ navigate, token }) { const BASE = window.API_BASE || '/simuladordocente/api'; const [pass, setPass] = React.useState(''); const [pass2, setPass2] = React.useState(''); const [msg, setMsg] = React.useState(''); const [ok, setOk] = React.useState(false); const [loading, setLoading] = React.useState(false); const submit = async (e) => { e.preventDefault(); if (pass !== pass2) { setMsg('Las contraseñas no coinciden.'); return; } if (pass.length < 4) { setMsg('Mínimo 4 caracteres.'); return; } setLoading(true); setMsg(''); try { const r = await fetch(`${BASE}/auth/reset`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ token, password: pass }), }); const d = await r.json(); if (!r.ok) { setMsg(d.error || 'Error al restablecer.'); } else { setOk(true); } } catch { setMsg('Error de conexión.'); } setLoading(false); }; if (ok) { return (

Contraseña actualizada

Ya puedes iniciar sesión con tu nueva contraseña.

); } return (

Nueva contraseña

Elige una contraseña segura para tu cuenta.

setPass(e.target.value)} />
setPass2(e.target.value)} />
{msg &&
{msg}
}
); }; // Página: cuenta confirmada (redirige aquí el backend) window.AuthPages.Confirmado = function Confirmado({ navigate, error }) { if (error) { return (

Enlace inválido

El enlace de confirmación es inválido o ya expiró. Intenta registrarte de nuevo.

); } return (

¡Cuenta confirmada!

Tu correo ha sido verificado. Ya puedes acceder a tu panel.

); }; function AuthShell({ children, navigate }) { return (
{e.preventDefault();navigate('/');}}>

Practica como en el examen real.

Simuladores construidos con la estructura, dificultad y bloques temáticos del examen USICAMM. Pago único por materia, acceso permanente.

13 simuladores
$149 por materia
intentos
{children}
); }