// History — chronological timeline of signal events the user has logged. // Source: GET /api/v1/master-equation/me/events (falls back to // /api/v1/health-sync/status if events are not yet exposed). const History = ({ openQuickLog }) => { const [filter, setFilter] = React.useState('all'); const [events, setEvents] = React.useState(null); // null = loading const [error, setError] = React.useState(null); React.useEffect(() => { let dead = false; (async () => { try { const r = await fetch(window.CHVault.apiRoot + '/api/v1/master-equation/me/events?limit=200', { credentials: 'include', headers: { 'Accept': 'application/json' }, }); if (r.status === 404) { if (!dead) setEvents([]); return; } if (!r.ok) throw new Error('events-' + r.status); const j = await r.json(); if (dead) return; setEvents(Array.isArray(j) ? j : (j.events || [])); } catch (e) { if (dead) return; setError(e.message || String(e)); setEvents([]); } })(); return () => { dead = true; }; }, []); const AXES = []; const tintFor = (code) => (window.AXIS_META && window.AXIS_META[code] && window.AXIS_META[code].tint) || 'var(--accent-clinical)'; const filtered = events ? (filter === 'all' ? events : events.filter(e => axisOfSignal(e.signal_id) === filter)) : []; // Group by day label. const grouped = filtered.reduce((acc, e) => { const day = dayLabel(e.recorded_at || e.timestamp_iso || e.created_at); (acc[day] = acc[day] || []).push(e); return acc; }, {}); return (
History

Every axis-lift, settled to chain.

An append-only timeline of signal events your devices and actions produced — readable, filterable by axis, and the exact record researchers reference in any opt-in cohort.
{AXES.map(a => ( ))}
{events === null && (
Loading your signal-event log…
)} {events && events.length === 0 && (
No signal events recorded yet.
The first event will appear here the moment you log a meal, complete a focus block, sync your wearable, or post any action that your iOS, Android, or web client sends to POST /api/v1/master-equation/signal.
)} {Object.entries(grouped).map(([day, items], gi) => (
{day}
{items.map((e, i) => { const code = axisOfSignal(e.signal_id); const tint = tintFor(code); return (
{timeLabel(e.recorded_at || e.timestamp_iso || e.created_at)}
{code}
{e.signal_id}
{e.mode} · {Number(e.value).toLocaleString(undefined, { maximumFractionDigits: 2 })}{e.action_id ? ` · ${e.action_id}` : ''}{e.source ? ` · ${e.source}` : ''}
{Number(e.value).toLocaleString(undefined, { maximumFractionDigits: 2 })}
); })}
))}
); }; function axisOfSignal(signal_id) { if (!signal_id) return '??'; const prefix = String(signal_id).split('_')[0].toUpperCase(); return ['PO','NM','ER','SC','RS','ES','TA','PV'].includes(prefix) ? prefix : '??'; } function dayLabel(iso) { if (!iso) return 'Earlier'; const d = new Date(iso); const today = new Date(); today.setHours(0,0,0,0); const yesterday = new Date(today); yesterday.setDate(yesterday.getDate() - 1); const startOfDay = new Date(d); startOfDay.setHours(0,0,0,0); if (startOfDay.getTime() === today.getTime()) return 'Today'; if (startOfDay.getTime() === yesterday.getTime()) return 'Yesterday'; const days = Math.round((today - startOfDay) / 86400000); if (days < 7) return `${days} days ago`; return d.toLocaleDateString(); } function timeLabel(iso) { if (!iso) return ''; try { return new Date(iso).toLocaleTimeString(undefined, { hour: '2-digit', minute: '2-digit' }); } catch { return ''; } } window.History = History;