// vita-notify.jsx — Notification engine + push banner. Vita as a daily presence, not an alert. // ── Vita's voice: warm, persona-aware copy ────────────────────── function vitaSays(personaId, kind, ctx = {}) { const n = (ctx.nombre || '').split(' ')[0] || ''; const C = { // Cálida saludo: `Buenos días, ${n} ☀️ ¿Cómo amaneciste? Aquí estoy contigo.`, medicamento: `${n}, es horita de tu ${ctx.med || 'medicamento'} 💊 ¿Ya desayunaste?`, medNoche: `Antes de dormir, ${n}: tu ${ctx.med || 'medicamento'} de la noche. Yo te acompaño.`, agua: `Un vasito de agua, ${n} 💧 Tu cuerpo te lo agradece.`, caminar: `¿Salimos a caminar 15 min, ${n}? 🚶 Te va a sentar increíble.`, animo: `Antes de dormir, cuéntame: ¿cómo te sentiste hoy? Estoy para escucharte 💚`, cita: `${n}, mañana tienes tu ${ctx.titulo || 'cita'} 🗓️ Yo te recuerdo todo, tú tranquila.`, logro: `¡${n}, llevas ${ctx.racha || 12} días sin fallar con tu ${ctx.med || 'tratamiento'}! 🔥 Estoy orgullosa de ti.`, concern: `Vi que hoy no marcaste tu caminata. No pasa nada, ${n} 💚 Mañana es otro día y yo sigo aquí.`, resultado: `¡Llegaron tus resultados, ${n}! Los revisé por ti. Toca para verlos juntas.`, }; const S = { // Serena saludo: `Buenos días, ${n}. Con calma: hoy es un buen día para cuidarte. Aquí estoy.`, medicamento: `${n}, cuando puedas, tu ${ctx.med || 'medicamento'}. Sin afán.`, medNoche: `Para cerrar el día tranquila: tu ${ctx.med || 'medicamento'} de la noche.`, agua: `Una pausa y un poco de agua, ${n}. Respira.`, caminar: `Si te provoca, una caminata suave de 15 min. A tu ritmo, ${n}.`, animo: `¿Cómo estuvo tu día por dentro? Tómate un momento para contarme.`, cita: `${n}, recuerda tu ${ctx.titulo || 'cita'} mañana. Yo me encargo de lo demás.`, logro: `${ctx.racha || 12} días constante con tu ${ctx.med || 'tratamiento'}, ${n}. Vas muy bien, paso a paso.`, concern: `Hoy no alcanzaste tu caminata, y está bien. Descansa, ${n}. Mañana seguimos.`, resultado: `Tus resultados ya están, ${n}. Cuando estés lista, los vemos con calma.`, }; const K = { // Coach saludo: `¡Arriba, ${n}! ☀️ Hoy nos cuidamos con toda. ¿Lista para el día?`, medicamento: `¡Vamos ${n}! 💪 Tu ${ctx.med || 'medicamento'} y a comernos el día.`, medNoche: `Cierre del día: tu ${ctx.med || 'medicamento'} de la noche. ¡Bien hecho hoy!`, agua: `¡Hidrátate, ${n}! 💧 Un vaso más para la meta.`, caminar: `¡Esos 15 min de caminata nos esperan, ${n}! 🚶 Tu corazón lo va a sentir.`, animo: `Cierra el día registrando tu ánimo, ${n}. Cada día cuenta para tu progreso.`, cita: `${n}, ¡mañana tu ${ctx.titulo || 'cita'}! Un paso más por tu salud. Ahí estaré.`, logro: `🔥 ¡${ctx.racha || 12} DÍAS seguidos, ${n}! Eres imparable. Sigamos así.`, concern: `Hoy faltó la caminata, ${n}. ¡Sin culpas! Mañana lo retomamos con fuerza 💪`, resultado: `¡Resultados listos, ${n}! Vamos a revisarlos y seguir mejorando.`, }; const map = { calida: C, serena: S, coach: K }[personaId] || C; return map[kind] || C[kind] || ''; } // ── Build a day's notifications from patient state ─────────────── function buildNotifications({ profile, personaId, careTasks, seguimiento, careDetails }) { const ctx = { nombre: profile.nombre }; const meds = Array.isArray(profile.medicamentos) ? profile.medicamentos : (profile.medicamentos ? [profile.medicamentos] : []); const list = []; const push = (o) => list.push({ read: false, done: false, ...o }); // Morning greeting (proactive presence) push({ id: 'n-saludo', tipo: 'proactivo', hora: '06:45', bloque: 'mañana', icon: 'sunrise', color: '#E6A93C', mensaje: vitaSays(personaId, 'saludo', ctx), actions: ['reply'] }); // Streak celebration const st = (careDetails && careDetails.streaks && careDetails.streaks[0]); if (st) push({ id: 'n-logro', tipo: 'logro', hora: '07:00', bloque: 'mañana', icon: 'flame', color: '#E6A93C', mensaje: vitaSays(personaId, 'logro', { ...ctx, racha: st.racha, med: st.nombre }), actions: ['reply'] }); // Morning medication if (meds[0]) push({ id: 'n-med-am', tipo: 'medicamento', hora: '08:00', bloque: 'mañana', icon: 'pill', color: '#3DD9D6', taskId: 'med-am-0', mensaje: vitaSays(personaId, 'medicamento', { ...ctx, med: meds[0] }), actions: ['done', 'snooze'] }); // Water midmorning push({ id: 'n-agua', tipo: 'habito', hora: '10:30', bloque: 'mañana', icon: 'droplet', color: '#5BC0EB', taskId: 'agua', mensaje: vitaSays(personaId, 'agua', ctx), actions: ['done', 'snooze'] }); // Appointment reminder (if any) const cita = (seguimiento || []).find(s => s.tipo === 'cita' && ['agendado', 'pendiente'].includes(s.estado)); if (cita) push({ id: 'n-cita', tipo: 'cita', hora: '12:00', bloque: 'tarde', icon: 'calendar', color: '#3DD9D6', mensaje: vitaSays(personaId, 'cita', { ...ctx, titulo: cita.titulo }), actions: ['view', 'reply'] }); // Walk afternoon push({ id: 'n-caminar', tipo: 'actividad', hora: '17:00', bloque: 'tarde', icon: 'walk', color: '#73C58E', taskId: 'caminar', mensaje: vitaSays(personaId, 'caminar', ctx), actions: ['done', 'snooze'] }); // Night medication if (meds[0]) push({ id: 'n-med-pm', tipo: 'medicamento', hora: '20:00', bloque: 'noche', icon: 'pill', color: '#3DD9D6', taskId: 'med-pm-0', mensaje: vitaSays(personaId, 'medNoche', { ...ctx, med: meds[0] }), actions: ['done', 'snooze'] }); // Mood check-in night push({ id: 'n-animo', tipo: 'terapia', hora: '21:00', bloque: 'noche', icon: 'brain', color: '#A98CD9', taskId: 'animo', mensaje: vitaSays(personaId, 'animo', ctx), actions: ['view', 'reply'] }); return list; } const ACTION_META = { done: { label: 'Ya lo hice', icon: 'check', primary: true }, snooze: { label: 'En 10 min', icon: 'clock' }, reply: { label: 'Responder', icon: 'mic' }, view: { label: 'Ver', icon: 'chevron' }, }; // per-type action label overrides function actionLabel(k, tipo) { if (k === 'done') { if (tipo === 'medicamento') return 'Ya lo tomé'; if (tipo === 'habito') return 'Listo'; if (tipo === 'actividad') return 'Ya caminé'; } return ACTION_META[k].label; } // ── Push banner (drops from top, inside the device) ───────────── function PushBanner({ t, persona, notif, onAction, onDismiss }) { const [leaving, setLeaving] = React.useState(false); const startX = React.useRef(null); const timerRef = React.useRef(0); const close = React.useCallback(() => { setLeaving(true); setTimeout(onDismiss, 280); }, [onDismiss]); React.useEffect(() => { timerRef.current = setTimeout(close, 7000); return () => clearTimeout(timerRef.current); }, [notif.id]); if (!notif) return null; const color = persona.accent; return (