// vita-profile.jsx — Profile tab: family members, patient switch (face), voice-guided add, privacy const PARENTESCOS = ['Hijo / Hija', 'Esposo / Esposa', 'Mamá', 'Papá', 'Hermano / Hermana', 'Abuelo / Abuela', 'Otro']; function avatarFor(nombre) { return (nombre || '?').trim().charAt(0).toUpperCase(); } function Avatar({ t, persona, nombre, size = 48, active, menor }) { return (
{avatarFor(nombre)}
{menor && ( {Icon.child({ s: size * 0.26, c: persona.accent })} )}
); } // ── Profile screen ────────────────────────────────────────────── function ProfileScreen({ t, persona, profile, usuarioSesion, family, activeId, onSwitch, onAdd, onOpenData, onShowSecurity, onTalk, onToggleTheme, onLogout, onOpenHome, onOpenHistory, onOpenProfile, onOpenServices }) { const nombreCuenta = usuarioSesion ? [usuarioSesion.nombre, usuarioSesion.apellido].filter(Boolean).join(' ') : null; const titular = { id: 'titular', nombre: nombreCuenta || profile.nombre || 'Tú', sub: 'Titular de la cuenta', edad: profile.edad, eps: profile.eps }; return (
Perfil
{t.dark ? Icon.sun({ s: 17 }) : Icon.moon({ s: 17 })}
{/* account holder */} {/* cuenta real — a propósito distinta de "tu historia con Vita": esto viene del backend (sesión autenticada), no de lo que se contó en el onboarding */} {usuarioSesion && (
{Icon.shield({ s: 14, c: t.muted })} Tu cuenta
Correo {usuarioSesion.correo}
Rol {usuarioSesion.rol}
)} {/* family */}
{Icon.users({ s: 16, c: t.muted })} Mi familia
{family.length === 0 && (
{Icon.users({ s: 26, c: t.muted })}
Aún no has agregado familiares.
Vita puede cuidar a toda tu familia.
)} {family.map(m => { const isActive = activeId === m.id; return (
{m.nombre}
{m.parentesco}{m.edad ? ' · ' + m.edad + ' años' : ''} {m.menor && Menor · gestiona {m.guardianNombre}}
{isActive ? Activo : }
); })}
{/* add member */} {/* privacy & data */}
Privacidad y datos
{[ { icon: 'user', label: 'Mis datos personales', desc: 'Nombre, contacto, cobertura', onClick: onOpenData }, { icon: 'faceid', label: 'Seguridad y biometría', desc: 'Tu rostro y acceso', onClick: onShowSecurity }, { icon: 'consent', label: 'Consentimientos y privacidad', desc: 'Habeas Data · quién ve tus datos', onClick: onShowSecurity }, { icon: 'logout', label: 'Cerrar sesión', desc: 'Saldrás de tu cuenta', onClick: onLogout, danger: true }, ].map((r, i) => ( ))}
{/* Vita helper */}
); } // ── Member home (when managing a family member) ───────────────── function MemberHome({ t, persona, member, titularName, onBackToTitular, onStartTriage, onToggleTheme, onOpenProfile, onTalk }) { return (
{/* managing banner */}
{Icon.swap({ s: 17, c: persona.accent })} Estás gestionando a {member.nombre.split(' ')[0]}
{member.nombre}
{member.parentesco}{member.edad ? ' · ' + member.edad + ' años' : ''}{member.menor ? ' · Acudiente: ' + member.guardianNombre : ''}
{t.dark ? Icon.sun({ s: 17 }) : Icon.moon({ s: 17 })}
{/* empty state — Vita getting to know them */}
Vita aún no conoce a {member.nombre.split(' ')[0]}

{member.menor ? `Como ${member.nombre.split(' ')[0]} es menor, tú harás su primer triage por él. Hablado, sencillo, igual que el tuyo.` : `Hagamos su primer triage hablado para empezar a cuidar su salud y construir su historia.`}

{Icon.shield({ s: 14, c: t.muted })} Su historia se guarda en tu cuenta, protegida y privada
{}} onOpenProfile={onOpenProfile} onOpenHistory={onOpenProfile} onShowSecurity={onOpenProfile} />
); } // ── Voice-guided add member ───────────────────────────────────── function AddMemberFlow({ t, persona, titularName, voice, tts, onDone, onClose }) { const accent = persona.accent; const [step, setStep] = React.useState(0); const [nombre, setNombre] = React.useState(''); const [parentesco, setParentesco] = React.useState(''); const [edad, setEdad] = React.useState(''); const [text, setText] = React.useState(''); const menor = edad !== '' && Number(edad) < 18; const steps = [ { q: '¿A quién quieres agregar? Dime su nombre.', hint: 'Nombre completo' }, { q: '¿Qué parentesco tiene contigo?', hint: 'Elige uno' }, { q: '¿Cuántos años tiene?', hint: 'Su edad' }, { q: 'confirm' }, ]; const spokenRef = React.useRef(-1); React.useEffect(() => { if (!tts || step === spokenRef.current || step >= 3) return; spokenRef.current = step; voice && voice.speak(steps[step].q, persona); }, [step]); const next = () => setStep(s => Math.min(3, s + 1)); const submitName = (v) => { const val = (v != null ? v : text).trim(); if (!val) return; setNombre(val); setText(''); next(); }; const micName = () => { const ok = voice && voice.startListening((f) => submitName(f)); if (!ok) document.getElementById('addm-input') && document.getElementById('addm-input').focus(); }; const finish = () => { onDone({ id: 'm' + Date.now(), nombre: nombre.trim(), parentesco, edad: edad ? Number(edad) : null, menor, guardianNombre: titularName, hasHistoria: false }); }; return ( {/* progress dots */}
{[0, 1, 2, 3].map(i => )}
{/* Vita asks */} {step < 3 && (
{steps[step].q}
)} {/* Step 0 — name */} {step === 0 && (
setText(e.target.value)} onKeyDown={e => { if (e.key === 'Enter') submitName(); }} placeholder={voice && voice.listening ? 'Escuchando…' : 'Nombre completo'} style={{ flex: 1, border: 'none', outline: 'none', background: 'transparent', fontSize: 16, color: t.text, fontFamily: 'Hanken Grotesk, sans-serif', minWidth: 0 }} /> {text.trim() && !(voice && voice.listening) && }
)} {/* Step 1 — parentesco */} {step === 1 && (
{PARENTESCOS.map(p => ( ))}
)} {/* Step 2 — edad */} {step === 2 && (
setEdad(e.target.value.replace(/\D/g, '').slice(0, 3))} onKeyDown={e => { if (e.key === 'Enter' && edad) next(); }} placeholder="Edad" style={{ flex: 1, height: 52, padding: '0 16px', borderRadius: 15, border: `1px solid ${t.stroke}`, background: t.dark ? 'rgba(255,255,255,0.06)' : '#fff', color: t.text, fontSize: 18, fontWeight: 600, fontFamily: 'Hanken Grotesk, sans-serif', outline: 'none' }} />
{menor &&
{Icon.child({ s: 17, c: '#E6A93C' })}Es menor de edad. Tú ({titularName}) serás su acudiente y gestionas su salud.
}
)} {/* Step 3 — confirm */} {step === 3 && (
{nombre}
{parentesco}{edad ? ' · ' + edad + ' años' : ''}
{Icon.faceid({ s: 18, c: accent })} {menor ? <>Como es menor, se gestiona con tu rostro. No necesita acceso propio. : <>{nombre.split(' ')[0]} tendrá su propio acceso. Registramos su rostro cuando esté presente.}
)}
); } // ── Personal data sheet ───────────────────────────────────────── function PersonalDataSheet({ t, persona, profile, onClose }) { const rows = [ ['Nombre', profile.nombre], ['Edad', profile.edad ? profile.edad + ' años' : null], ['Sexo', profile.sexo], ['EPS', profile.eps], ['Prepagada / Plan', profile.prepagada], ['Objetivo de salud', profile.objetivo], ].filter(r => r[1]); return (
Mis datos personales

Puedes pedirle a Vita que cambie cualquiera de estos por voz.

{rows.map(([k, v], i) => (
{k}
{Array.isArray(v) ? v.join(', ') : v}
))}
); } Object.assign(window, { ProfileScreen, MemberHome, AddMemberFlow, PersonalDataSheet });