/* 02 Signup — 3 pasos + verificación (Fase 1b: conectado a la API real) */ function Signup({ go }) { const [step, setStep] = useState(1); const [mode, setMode] = useState('signup'); // signup | login const [busy, setBusy] = useState(false); const [error, setError] = useState(''); const [data, setData] = useState({ name: '', email: '', pass: '', dob: '', city: 'Madrid', gender: '', looking: '', intent: '', partnerCode: '', consentIA: false, consentTC: false, consent18: false, }); const [loginData, setLoginData] = useState({ email: '', pass: '' }); const [partnerCodeLocked, setPartnerCodeLocked] = useState(false); // Código de partner vía URL (#signup?ref=PART-XXX) — precargado Y BLOQUEADO (ver SISTEMA_PARTNERS.md). // OJO: el router es por hash, la query va DENTRO del hash, no en location.search. useState(() => { try { const ref = new URLSearchParams((location.hash.split('?')[1] || '')).get('ref'); if (ref) { setData(d => ({ ...d, partnerCode: String(ref).trim().toUpperCase() })); setPartnerCodeLocked(true); } } catch (e) {} }); const set = (k, v) => { setData(d => ({ ...d, [k]: v })); setError(''); }; const ageFromDob = (dob) => { if (!dob) return null; const d = new Date(dob), n = new Date(); let a = n.getFullYear() - d.getFullYear(); if (n.getMonth() < d.getMonth() || (n.getMonth() === d.getMonth() && n.getDate() < d.getDate())) a--; return a; }; const genderCode = { 'Mujer': 'F', 'Hombre': 'M', 'No binario': 'NB' }; // "Busco conocer" -> código que guarda la API (F|M|NB|ALL). Opcional: se puede quitar volviendo a pulsar. const lookingCode = { 'Mujeres': 'F', 'Hombres': 'M', 'Personas no binarias': 'NB', 'Todas las anteriores': 'ALL' }; // Tipo de vínculo que busca: OPCIONAL. Si ambos lo rellenan y coincide, se ordena antes en el muro. const INTENT_OPTS = ['Pareja estable', 'Algo serio (familia a la vista)', 'Conocer sin prisa', 'Lo que surja']; // Solo capitales de provincia (decisión Stokmarket 2026-09-26) const PROVINCE_CAPITALS = ['A Coruña','Albacete','Alicante','Almería','Ávila','Badajoz','Barcelona','Bilbao','Burgos','Cáceres','Cádiz','Castellón de la Plana','Ceuta','Ciudad Real','Córdoba','Cuenca','Girona','Granada','Guadalajara','Huelva','Huesca','Jaén','Las Palmas de Gran Canaria','León','Lleida','Logroño','Lugo','Madrid','Málaga','Melilla','Murcia','Ourense','Oviedo','Palencia','Palma de Mallorca','Pamplona','Pontevedra','Salamanca','San Sebastián','Santander','Santa Cruz de Tenerife','Segovia','Sevilla','Soria','Tarragona','Teruel','Toledo','Valencia','Valladolid','Vitoria-Gasteiz','Zamora','Zaragoza']; const canStep1 = data.name.trim().length >= 2 && /.+@.+\..+/.test(data.email) && data.pass.length >= 8 && data.dob; async function submitRegister() { setBusy(true); setError(''); try { const r = await window.CruceAPI.register({ email: data.email.trim(), password: data.pass, name: data.name.trim(), age: ageFromDob(data.dob), city: data.city, gender: genderCode[data.gender] || null, looking: lookingCode[data.looking] || null, // opcional -> filtra el muro (no se ven si no encaja) intent: data.intent || null, // opcional -> si coincide con otro perfil, ordena antes partner_code: data.partnerCode.trim() || undefined, // reservado (Fase partners) consents: { terms: true, privacy: true, age18: true, ai_matching: true }, }); window.CruceAPI.Session.set(r.token, r.user); if (data.partnerCode.trim()) localStorage.setItem('cruce.partner_code', data.partnerCode.trim()); go('affinity'); } catch (e) { setError(e.message || 'Error al crear la cuenta.'); } finally { setBusy(false); } } async function submitLogin() { setBusy(true); setError(''); try { const r = await window.CruceAPI.login(loginData.email.trim(), loginData.pass); window.CruceAPI.Session.set(r.token, r.user); go('wall'); } catch (e) { setError(e.message || 'Error al entrar.'); } finally { setBusy(false); } } if (mode === 'login') { return (
Mira a cámara, gira la cabeza lentamente cuando te lo pidamos. Tardas 8 segundos. Este selfie no se publica ni se comparte con otros usuarios.
Comprobamos edad y nacionalidad con lectura automática (OCR). No guardamos tu DNI: solo un hash cifrado + confirmación >18.
Necesario para el test de afinidad y las recomendaciones de matches.
Autorizo a Cruce a procesar mis respuestas del test, mis intereses y mis interacciones dentro de la plataforma mediante sistemas automatizados (IA) para calcular mi porcentaje de afinidad con otros usuarios. Comprendo que: