/* Centralized mock data for the api_fit prototype */ const MOCK = (() => { // Generate 90d of daily metrics with realistic variation + trend const days = 90; const seed = (i) => Math.sin(i * 12.9898) * 43758.5453; const rand = (i, o = 0) => (seed(i + o) - Math.floor(seed(i + o))); const today = new Date("2026-05-02"); const dateStr = (d) => d.toISOString().slice(0, 10); const fmtDate = (d) => `${String(d.getDate()).padStart(2, "0")}/${String(d.getMonth()+1).padStart(2,"0")}`; const daily = []; for (let i = days - 1; i >= 0; i--) { const d = new Date(today); d.setDate(d.getDate() - i); const day = days - i; // gentle improving trend (positive narrative) const trend = day / days; daily.push({ date: dateStr(d), label: fmtDate(d), sleepScore: Math.round(68 + trend * 12 + rand(i) * 14 - 5), sleepHours: 6.2 + trend * 1.0 + rand(i, 7) * 1.4, hrv: Math.round(48 + trend * 14 + rand(i, 1) * 10 - 4), rhr: Math.round(58 - trend * 5 + rand(i, 2) * 4), weight: 78.5 - trend * 2.4 + rand(i, 3) * 0.6 - 0.3, bodyFat: 18.2 - trend * 1.5 + rand(i, 8) * 0.4, energy: Math.round(60 + trend * 18 + rand(i, 4) * 18 - 6), load: Math.round(rand(i, 5) * 100 + (i % 7 === 0 ? 0 : 30)), stress: Math.round(40 + rand(i, 6) * 30 - trend * 8), }); } const todayD = daily[daily.length - 1]; const activities = [ { id: "a1", date: "2026-05-01", time: "07:12", type: "Carrera", subtype: "Z2 endurance", dur: "1:08:24", durMin: 68, dist: 12.4, hr: 142, hrMax: 158, pace: "5:31", load: 84, te: 3.2, recovery: "18h", calories: 712, elevation: 142, source: "Garmin" }, { id: "a2", date: "2026-04-30", time: "18:45", type: "Fuerza", subtype: "Tren superior", dur: "0:52:10", durMin: 52, dist: 0, hr: 118, hrMax: 152, pace: "—", load: 48, te: 2.4, recovery: "10h", calories: 286, elevation: 0, source: "Garmin" }, { id: "a3", date: "2026-04-29", time: "06:55", type: "Carrera", subtype: "Intervalos 6×800", dur: "0:48:30", durMin: 48, dist: 8.1, hr: 158, hrMax: 178, pace: "5:58", load: 112, te: 4.1, recovery: "32h", calories: 624, elevation: 88, source: "Garmin" }, { id: "a4", date: "2026-04-28", time: "07:40", type: "Bici", subtype: "Recovery ride", dur: "1:24:00", durMin: 84, dist: 32.1, hr: 124, hrMax: 142, pace: "—", load: 52, te: 2.0, recovery: "12h", calories: 488, elevation: 312, source: "Garmin" }, { id: "a5", date: "2026-04-26", time: "08:00", type: "Carrera", subtype: "Largo Z2", dur: "1:42:18", durMin: 102, dist: 18.6, hr: 145, hrMax: 162, pace: "5:30", load: 138, te: 3.6, recovery: "28h", calories: 1080, elevation: 224, source: "Garmin" }, { id: "a6", date: "2026-04-25", time: "19:10", type: "Fuerza", subtype: "Tren inferior", dur: "1:02:00", durMin: 62, dist: 0, hr: 122, hrMax: 162, pace: "—", load: 64, te: 2.8, recovery: "14h", calories: 360, elevation: 0, source: "Garmin" }, { id: "a7", date: "2026-04-24", time: "07:25", type: "Carrera", subtype: "Tempo 30'", dur: "0:54:42", durMin: 54, dist: 10.2, hr: 152, hrMax: 168, pace: "5:22", load: 92, te: 3.4, recovery: "22h", calories: 668, elevation: 102, source: "Garmin" }, ]; // Build a stream for activity a1 (Z2 endurance run, 12.4km / 68min) const streamLen = 90; const stream = { hr: Array.from({length: streamLen}, (_, i) => { const warmup = i < 8 ? 100 + i * 5 : 0; const main = i >= 8 ? 138 + Math.sin(i * 0.3) * 6 + (i > 60 ? (i-60) * 0.8 : 0) : 0; return Math.round(warmup + main + (Math.random() - 0.5) * 4); }), pace: Array.from({length: streamLen}, (_, i) => { return 5.4 + Math.sin(i * 0.4) * 0.3 + (i < 5 ? 1.5 - i * 0.3 : 0) + (Math.random() - 0.5) * 0.15; }), elevation: Array.from({length: streamLen}, (_, i) => { return 120 + Math.sin(i * 0.15) * 60 + Math.cos(i * 0.08) * 30; }), cadence: Array.from({length: streamLen}, (_, i) => { return 168 + Math.sin(i * 0.5) * 4 + (Math.random() - 0.5) * 3; }), }; // Plan: 4-week training block toward a half-marathon const planWeek = [ [ { day: "Lun", type: "rest", title: "Descanso activo", desc: "Caminata 30'", tone: "rest" }, { day: "Mar", type: "run", title: "Z2 endurance", desc: "60' · zona 2", load: 70, tone: "easy" }, { day: "Mié", type: "strength", title: "Fuerza tren inf.", desc: "60' · zonas 3–4", load: 55, tone: "strength" }, { day: "Jue", type: "run", title: "Tempo 30'", desc: "10' E + 30' tempo + 10' E", load: 95, tone: "hard" }, { day: "Vie", type: "rest", title: "Descanso", desc: "Movilidad 20'", tone: "rest" }, { day: "Sáb", type: "run", title: "Largo Z2", desc: "1h45' · zona 2", load: 140, tone: "long", today: true }, { day: "Dom", type: "bike", title: "Bici recovery", desc: "1h · zona 1", load: 45, tone: "easy" }, ], [ { day: "Lun", type: "rest", title: "Descanso", desc: "—", tone: "rest" }, { day: "Mar", type: "run", title: "Intervalos 5×1000", desc: "Z4 · 90s rec.", load: 110, tone: "hard" }, { day: "Mié", type: "strength", title: "Tren superior", desc: "Pull/push", load: 50, tone: "strength" }, { day: "Jue", type: "run", title: "Z2 endurance", desc: "70' suave", load: 80, tone: "easy" }, { day: "Vie", type: "rest", title: "Descanso", desc: "—", tone: "rest" }, { day: "Sáb", type: "run", title: "Progresivo", desc: "1h30' · Z2→Z3", load: 130, tone: "long" }, { day: "Dom", type: "bike", title: "Bici larga", desc: "2h · Z1–Z2", load: 75, tone: "easy" }, ], [ { day: "Lun", type: "rest", title: "Descanso", desc: "—", tone: "rest" }, { day: "Mar", type: "run", title: "Cuestas 8×60s", desc: "rec. caminando", load: 100, tone: "hard" }, { day: "Mié", type: "strength", title: "Tren inferior", desc: "Sentadillas/peso muerto", load: 60, tone: "strength" }, { day: "Jue", type: "run", title: "Z2 endurance", desc: "75'", load: 90, tone: "easy" }, { day: "Vie", type: "rest", title: "Descanso", desc: "Movilidad", tone: "rest" }, { day: "Sáb", type: "run", title: "Largo + tempo", desc: "1h45' (30' tempo)", load: 145, tone: "long" }, { day: "Dom", type: "bike", title: "Recovery", desc: "1h Z1", load: 40, tone: "easy" }, ], [ { day: "Lun", type: "rest", title: "Descanso", desc: "—", tone: "rest" }, { day: "Mar", type: "run", title: "Activación 30'", desc: "4×100m progresivo", load: 50, tone: "easy" }, { day: "Mié", type: "rest", title: "Descanso pre-test", desc: "—", tone: "rest" }, { day: "Jue", type: "run", title: "Test 5K", desc: "Pre-objetivo", load: 95, tone: "hard" }, { day: "Vie", type: "rest", title: "Descanso", desc: "—", tone: "rest" }, { day: "Sáb", type: "run", title: "Suave", desc: "30' Z1–Z2", load: 35, tone: "easy" }, { day: "Dom", type: "rest", title: "Descanso", desc: "—", tone: "rest" }, ], ]; // Chat messages const chat = [ { role: "user", text: "Resume mi semana", time: "10:42" }, { role: "assistant", agent: "coach", time: "10:42", sources: ["actividades últ. 7d", "sueño últ. 7d", "HRV últ. 7d"], cost: { tokens: 1240, usd: 0.0021 }, text: "Semana sólida — 4 sesiones, 49 km totales, carga 426 (+8% vs anterior). El **largo del sábado** salió mejor de lo esperado: pulso 145 a 5:30/km, decoupling solo 4%. Indica que tu base aeróbica está respondiendo al bloque.", insights: [ { kind: "good", title: "Recovery al alza", body: "HRV +12% en 7 días" }, { kind: "improve", title: "Sueño bajo el sáb.", body: "5h42 — siguiente largo después de un viernes en casa" }, ], }, { role: "user", text: "¿Por qué bajó mi HRV el martes?", time: "10:44" }, { role: "assistant", agent: "coach", time: "10:44", sources: ["HRV 14d", "actividades", "sueño martes"], cost: { tokens: 980, usd: 0.0017 }, text: "Tu HRV cayó de 64 a 51 entre lunes y martes (-20%). Tres factores se alinean:", bullets: [ "Lunes hiciste **intervalos** (carga 112, TE 4.1) — la carga aguda alta tras una semana baja activa la adaptación.", "Dormiste **6h05** el lunes, por debajo de tu media de 7h12.", "RHR del martes: 61 vs media 56 — coherente con la caída de HRV.", ], followup: "No es alarmante: la HRV se recuperó el jueves (63). Si vuelves a ver una caída así sin entreno duro, vale la pena chequear estrés/hidratación.", }, ]; return { daily, today: todayD, activities, stream, planWeek, chat, user: { name: "Daniel", goal: "Media maratón · sub 1h45", weeks: 4 }, providers: [ { name: "Garmin", logo: "G", mode: "POLL", state: "ok", lastSync: "hace 12 min", caps: ["sleep","hrv","rhr","activities","streams","load","recovery"] }, { name: "Eufy", logo: "E", mode: "EVENT", state: "ok", lastSync: "ayer 21:14", caps: ["weight","body_fat","muscle","bone","water"] }, { name: "Whoop", logo: "W", mode: "POLL", state: "off", lastSync: "—", caps: ["recovery","strain","sleep"] }, { name: "Oura", logo: "O", mode: "POLL", state: "off", lastSync: "—", caps: ["sleep","hrv","temperature"] }, ], llm: { provider: "OpenAI", model: "gpt-4o-mini", today: { tokens: 24180, usd: 0.043 }, month: { tokens: 482300, usd: 0.91 }, limit: 5.00 }, }; })(); window.MOCK = MOCK;