// Accept-invite page — rendered when pathname = /accept-invite function InviteOverlay({ token, onLogin }) { const [inviteInfo, setInviteInfo] = React.useState(null); const [tokenError, setTokenError] = React.useState(null); const [status, setStatus] = React.useState(null); // null | 'loading' | 'error' | 'success' const [errMsg, setErrMsg] = React.useState(''); // Email/password Firebase form state const [emailPw, setEmailPw] = React.useState(''); const [confirmPw, setConfirmPw] = React.useState(''); const [showPwForm, setShowPwForm] = React.useState(false); React.useEffect(() => { if (!token) { setTokenError('Enlace inválido.'); return; } fetch(`/auth/invite-info?token=${encodeURIComponent(token)}`) .then(r => r.ok ? r.json() : r.json().then(e => Promise.reject(e.detail || 'Enlace inválido o expirado.'))) .then(info => setInviteInfo(info)) .catch(e => setTokenError(typeof e === 'string' ? e : 'Enlace inválido o expirado.')); }, [token]); async function _finishLogin(idToken) { const res = await fetch('/auth/firebase-login', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ id_token: idToken, invite_token: token }), }); if (!res.ok) { const data = await res.json().catch(() => ({})); throw new Error(data.detail || 'Error al crear la cuenta.'); } const meRes = await fetch('/auth/me'); if (!meRes.ok) throw new Error('Cuenta creada pero error al obtener sesión.'); return await meRes.json(); } async function signInWithGoogle() { setStatus('loading'); setErrMsg(''); try { const provider = new firebase.auth.GoogleAuthProvider(); provider.setCustomParameters({ login_hint: inviteInfo.email }); const result = await window.fbAuth.signInWithPopup(provider); const idToken = await result.user.getIdToken(); const user = await _finishLogin(idToken); setStatus('success'); setTimeout(() => { window.history.replaceState({}, '', '/'); onLogin(user); }, 600); } catch (err) { setStatus('error'); setErrMsg(err.message || 'Error al iniciar sesión.'); } } async function signInWithEmail(e) { e?.preventDefault?.(); if (!emailPw || !confirmPw) { setStatus('error'); setErrMsg('Complete todos los campos.'); return; } if (emailPw !== confirmPw) { setStatus('error'); setErrMsg('Las contraseñas no coinciden.'); return; } if (emailPw.length < 8) { setStatus('error'); setErrMsg('Mín. 8 caracteres.'); return; } setStatus('loading'); setErrMsg(''); try { // Try create account, fall back to sign in if already exists let result; try { result = await window.fbAuth.createUserWithEmailAndPassword(inviteInfo.email, emailPw); } catch (createErr) { if (createErr.code === 'auth/email-already-in-use') { result = await window.fbAuth.signInWithEmailAndPassword(inviteInfo.email, emailPw); } else { throw createErr; } } const idToken = await result.user.getIdToken(); const user = await _finishLogin(idToken); setStatus('success'); setTimeout(() => { window.history.replaceState({}, '', '/'); onLogin(user); }, 600); } catch (err) { setStatus('error'); const msg = err.code === 'auth/wrong-password' ? 'Contraseña incorrecta para ese correo en Firebase.' : err.code === 'auth/weak-password' ? 'Contraseña muy débil (mín. 8 caracteres).' : err.message || 'Error al crear cuenta.'; setErrMsg(msg); } } const wrapStyle = { position:'fixed', inset:0, display:'flex', alignItems:'center', justifyContent:'center', background:'linear-gradient(135deg,#0F1524 0%, #142036 50%, #0A0E1A 100%)', zIndex:9999 }; const cardStyle = { width:440, background:BC.surface, borderRadius:12, boxShadow:'0 30px 80px rgba(0,0,0,.5)', overflow:'hidden', position:'relative', zIndex:1 }; const bgGlow = { position:'absolute', inset:0, backgroundImage:'radial-gradient(circle at 20% 30%, rgba(34,144,206,.18) 0%, transparent 40%)', pointerEvents:'none' }; const brandStrip = (
BControl
Crear cuenta
Invitación de BControl
); const footer = (
CARLOS INCENDIO SPASANTIAGO · CHILE · 2026
); if (tokenError) { return (
{brandStrip}
Enlace inválido
{tokenError}
Contacte al administrador para solicitar una nueva invitación.
Ir al inicio
{footer}
); } if (!inviteInfo) { return (
); } return (
{brandStrip}
{/* Invite info banner */}
Invitado como {inviteInfo.role} · {inviteInfo.email}
{/* Google sign-in */} {/* Divider */}
o con correo y contraseña
{/* Email/password Firebase form */} {!showPwForm ? ( ) : (
{inviteInfo.email}
setEmailPw(e.target.value)} placeholder="Contraseña (mín. 8 caracteres)" style={{ width:'100%', padding:'9px 12px', border:'1px solid '+BC.border, borderRadius:5, background:BC.surfaceAlt, fontSize:13, color:BC.ink, marginBottom:8 }}/> setConfirmPw(e.target.value)} placeholder="Confirmar contraseña" style={{ width:'100%', padding:'9px 12px', border:'1px solid '+BC.border, borderRadius:5, background:BC.surfaceAlt, fontSize:13, color:BC.ink, marginBottom:10 }}/>
)} {/* Status messages */} {status === 'error' && (
{errMsg}
)} {status === 'success' && (
Cuenta creada. Iniciando sesión…
)} {status === 'loading' && (
Verificando…
)}
{footer}
); } window.InviteOverlay = InviteOverlay;