// CursoLogin.jsx — acceso al curso. window.CursoLogin // Modo NUBE (si ECCCloud.isEnabled): email + contraseña, sincroniza con Supabase. // Modo LOCAL (sin claves): nombre + PIN, guardado en este dispositivo. const { useState: useStateL } = React; function traducirError(msg) { const m = (msg || '').toLowerCase(); if (m.includes('invalid login')) return 'Email o contraseña incorrectos.'; if (m.includes('already registered') || m.includes('already been registered') || m.includes('user already')) return 'Ese email ya tiene cuenta. Pulsa “Entrar”.'; if (m.includes('password') && (m.includes('6') || m.includes('short') || m.includes('least') || m.includes('weak'))) return 'La contraseña debe tener al menos 6 caracteres.'; if (m.includes('signup') && m.includes('disabled')) return 'El registro está desactivado en Supabase (Authentication → Sign In/Providers → activa Email).'; if (m.includes('signups not allowed') || m.includes('not allowed')) return 'El registro está desactivado en Supabase. Actívalo en Authentication → Providers → Email.'; if (m.includes('email') && m.includes('confirm')) return 'Confirma tu email desde el correo que te hemos enviado y vuelve a entrar.'; if (m.includes('rate') || m.includes('security purposes') || m.includes('after')) return 'Demasiados intentos seguidos. Espera unos segundos y reintenta.'; if (m.includes('unable to validate') || (m.includes('email') && m.includes('invalid'))) return 'Ese email no se acepta. Escríbelo bien y usa un correo real (gmail, etc.).'; // Si no lo reconocemos, mostramos el mensaje real (sin ocultarlo) para poder diagnosticar. return 'Aviso de Supabase: ' + (msg || 'error desconocido'); } function dobAge(dobStr) { if (!dobStr) return null; const d = new Date(dobStr); if (isNaN(d)) return null; const t = new Date(); let a = t.getFullYear() - d.getFullYear(); const m = t.getMonth() - d.getMonth(); if (m < 0 || (m === 0 && t.getDate() < d.getDate())) a--; return a; } function CursoLogin({ onLogin }) { const S = window.CourseStore; const cloud = !!(window.ECCCloud && window.ECCCloud.isEnabled()); const [name, setName] = useStateL(''); const [pin, setPin] = useStateL(''); const [email, setEmail] = useStateL(''); const [fullName, setFullName] = useStateL(''); const [dob, setDob] = useStateL(''); const [guardianEmail, setGuardianEmail] = useStateL(''); const [guardianConsent, setGuardianConsent] = useStateL(false); const [privacyAccepted, setPrivacyAccepted] = useStateL(false); const [pwd, setPwd] = useStateL(''); const [mode, setMode] = useStateL('signin'); // signin | signup const [err, setErr] = useStateL(''); const [info, setInfo] = useStateL(''); const [busy, setBusy] = useStateL(false); const submitLocal = (e) => { e.preventDefault(); const r = S.login(name, pin); if (!r.ok) { setErr(r.error); return; } onLogin(); }; const submitCloud = async (e) => { e.preventDefault(); setErr(''); setInfo(''); setBusy(true); try { const C = window.ECCCloud; const mail = email.trim().toLowerCase(); let meta; if (mode === 'signup') { if (!privacyAccepted) { setErr('Debes aceptar la política de privacidad.'); setBusy(false); return; } if (!dob) { setErr('Indica tu fecha de nacimiento.'); setBusy(false); return; } const age = dobAge(dob); const minor = age !== null && age < 14; if (minor) { if (!guardianEmail.trim()) { setErr('Indica el email de tu madre/padre/tutor.'); setBusy(false); return; } if (!guardianConsent) { setErr('Falta la autorización de tu madre/padre/tutor.'); setBusy(false); return; } } meta = { full_name: fullName.trim(), dob: dob, is_minor: minor, guardian_email: minor ? guardianEmail.trim().toLowerCase() : '', guardian_consent: minor ? true : false, privacy_accepted: true, consent_ts: new Date().toISOString(), }; } const r = mode === 'signup' ? await C.signUp(mail, pwd, meta) : await C.signIn(mail, pwd); if (!r.ok) { setErr(traducirError(r.error)); setBusy(false); return; } if (r.needsConfirm) { setInfo('Te hemos enviado un correo para confirmar tu cuenta. Confírmalo y pulsa “Entrar”.'); setBusy(false); return; } let display = fullName.trim(); if (!display) { try { const u = await C.currentUser(); display = (u && u.user_metadata && u.user_metadata.full_name) || ''; } catch (e) {} } S.loginExternal(mail, display); try { const data = await C.pull(); if (data) S.importUser(data); } catch (e) {} setBusy(false); onLogin(); } catch (e) { setErr('Error de conexión. Revisa tu internet e inténtalo de nuevo.'); setBusy(false); } }; const inputStyle = { width: '100%', padding: '11px 14px', borderRadius: 'var(--r-md)', border: '1px solid var(--line)', font: 'var(--type-body)', color: 'var(--ink)', background: 'var(--bg-surface)', marginBottom: 14 }; return (
estudia con carol
Curso · Matemáticas 4º ESO
{cloud ? (

{mode === 'signup' ? 'Crear cuenta' : 'Entrar al curso'}

Entra con tu email para que tu progreso te siga en el móvil, la tablet y el ordenador.

{mode === 'signup' && ( setFullName(e.target.value)} placeholder="Como quieres que te llamemos" style={inputStyle} /> { setDob(e.target.value); setErr(''); }} style={inputStyle} required /> )} { setEmail(e.target.value); setErr(''); }} placeholder="tucorreo@ejemplo.com" style={inputStyle} required /> { setPwd(e.target.value); setErr(''); }} placeholder="Mínimo 6 caracteres" style={inputStyle} required /> {err &&
{err}
} {info &&
{info}
} {mode === 'signup' && dobAge(dob) !== null && dobAge(dob) < 14 && (
Eres menor de 14 años: necesitamos el permiso de tu madre, padre o tutor.
{ setGuardianEmail(e.target.value); setErr(''); }} placeholder="correo del adulto responsable" style={Object.assign({}, inputStyle, { marginBottom: 10 })} />
)} {mode === 'signup' && ( )}
{mode === 'signup' ? ¿Ya tienes cuenta? { e.preventDefault(); setMode('signin'); setErr(''); setInfo(''); }}>Entrar : ¿Primera vez? { e.preventDefault(); setMode('signup'); setErr(''); setInfo(''); }}>Crear cuenta}
) : (

Entrar al curso

Escribe tu nombre para empezar o continuar. Si es la primera vez, se crea tu acceso.

{ setName(e.target.value); setErr(''); }} placeholder="p. ej. Carol" style={inputStyle} /> { setPin(e.target.value); setErr(''); }} placeholder="Un número que recuerdes" type="password" inputMode="numeric" style={inputStyle} /> {err &&
{err}
}
)}
← Volver a la biblioteca abierta {cloud ? 'Tu progreso se sincroniza en todos tus dispositivos.' : 'Prototipo: tu progreso se guarda en este dispositivo.'}
); } window.CursoLogin = CursoLogin;