← back to Cncp Failures Mockups
public/v3-heatmap.html
102 lines
<!doctype html><html><head>
<meta charset="utf-8"><title>v3 — Heatmap dashboard</title>
<style>
:root { --bg:#0b1220; --ink:#e6eef8; --muted:#6b7a91; --grid:#1c2740; --hi:#ef4444; --med:#f59e0b; --lo:#facc15; --ok:#22c55e; --accent:#60a5fa; }
body { margin:0; background:radial-gradient(ellipse at top left,#142543,var(--bg) 60%); color:var(--ink); font-family: -apple-system,BlinkMacSystemFont,'SF Pro Text','Inter',system-ui,sans-serif; padding:24px; min-height:100vh; }
h1 { font-size:18px; font-weight:600; letter-spacing:-0.01em; margin:0 0 4px; }
.sub { color:var(--muted); font-size:12px; margin-bottom:18px; }
.grid-frame { background:rgba(28,39,64,0.4); border:1px solid var(--grid); border-radius:10px; padding:16px; backdrop-filter: blur(10px); }
.stats { display:grid; grid-template-columns: repeat(6, 1fr); gap:10px; margin-bottom:18px; }
.stat { background:rgba(28,39,64,0.4); border:1px solid var(--grid); border-radius:8px; padding:10px 12px; }
.stat .label { color:var(--muted); font-size:10px; text-transform:uppercase; letter-spacing:1px; margin-bottom:3px; }
.stat .val { font-size:22px; font-weight:600; }
.stat.high .val { color:var(--hi); }
.stat.med .val { color:var(--med); }
.stat.low .val { color:var(--lo); }
.heatmap { display:grid; grid-template-columns: 1fr 280px; gap:18px; }
.hm-grid { display:grid; gap:6px; grid-template-columns:repeat(24,1fr); }
.cell { aspect-ratio:1; border-radius:3px; background:#1a2540; position:relative; transition:transform 0.15s; }
.cell:hover { transform:scale(1.4); z-index:5; box-shadow:0 4px 14px rgba(0,0,0,0.4); }
.cell.h { background:var(--hi); }
.cell.m { background:var(--med); }
.cell.l { background:var(--lo); }
.cell.h, .cell.m, .cell.l { box-shadow: 0 0 8px currentColor; }
.failure-list { display:flex; flex-direction:column; gap:6px; }
.failure-row { display:grid; grid-template-columns:60px 1fr 50px; gap:10px; align-items:center; padding:8px 10px; background:rgba(28,39,64,0.3); border:1px solid var(--grid); border-radius:6px; font-size:12px; }
.failure-row .name { font-family:ui-monospace,Menlo,monospace; color:var(--ink); }
.failure-row .ev { font-variant-numeric: tabular-nums; color:var(--muted); text-align:right; font-size:11px; }
.sev-dot { width:8px; height:8px; border-radius:50%; display:inline-block; }
.sev-dot.h{ background:var(--hi); }
.sev-dot.m{ background:var(--med); }
.sev-dot.l{ background:var(--lo); }
.sev-dot.u{ background:#475569; }
.spark { display:flex; gap:1px; align-items:end; height:18px; }
.spark .bar { width:3px; background:var(--accent); min-height:1px; }
.hermes-strip { margin-top:18px; display:grid; grid-template-columns: 220px 1fr; gap:18px; padding:14px 16px; background:rgba(28,39,64,0.4); border:1px solid var(--grid); border-radius:8px; }
.pulse { width:6px; height:6px; border-radius:50%; background:var(--ok); display:inline-block; box-shadow:0 0 8px var(--ok); animation: pulse 2s infinite; }
@keyframes pulse { 50% { opacity:0.3; transform:scale(0.85); } }
</style>
</head><body>
<h1>Failure Heatmap</h1>
<div class="sub">Last 24 hours · density-first view · brighter = more severe</div>
<div class="grid-frame">
<div class="stats" id="stats"></div>
<div class="heatmap">
<div>
<div class="sub" style="margin-bottom:6px;">Severity over 24h (each cell = ~1 hour, brightest = highest severity event)</div>
<div class="hm-grid" id="hm"></div>
</div>
<div class="failure-list" id="rows"></div>
</div>
<div class="hermes-strip">
<div>
<div class="sub" style="font-size:10px;">HERMES-3 · 8B</div>
<div><span class="pulse"></span> <span style="font-weight:600;">online</span> · in mem · 4.3GB</div>
<div class="sub" style="font-size:10px; margin-top:4px;" id="h-summary"></div>
</div>
<div>
<div class="sub" style="font-size:10px; margin-bottom:6px;">RECENT CALLS</div>
<div id="recent" style="display:flex; gap:6px; overflow-x:auto;"></div>
</div>
</div>
</div>
<script>
fetch('/data' + location.search).then(r=>r.json()).then(d => {
// build a fake 24-cell heatmap weighted by events (illustrative)
const cells = Array.from({length: 24}, (_, i) => {
const r = Math.random();
if (r < 0.05) return 'h';
if (r < 0.18) return 'm';
if (r < 0.4) return 'l';
return '';
});
// tweak a few to known points
cells[14]='h'; cells[15]='m'; cells[18]='m'; cells[20]='m'; cells[21]='m';
document.getElementById('hm').innerHTML = cells.map(c => `<div class="cell ${c}"></div>`).join('');
const sev = d.hermes.last24h.bySev;
document.getElementById('stats').innerHTML = `
<div class="stat high"><div class="label">High</div><div class="val">${sev.high}</div></div>
<div class="stat med"><div class="label">Medium</div><div class="val">${sev.medium}</div></div>
<div class="stat low"><div class="label">Low</div><div class="val">${sev.low}</div></div>
<div class="stat"><div class="label">Open</div><div class="val">${d.failures.filter(f=>f.status==='triage'||f.status==='investigating').length}</div></div>
<div class="stat"><div class="label">Events 24h</div><div class="val">${d.failures.reduce((s,f)=>s+(f.eventCount||0),0)}</div></div>
<div class="stat"><div class="label">Hermes p50</div><div class="val">${(d.hermes.last24h.avgLatencyMs/1000).toFixed(1)}<span style="font-size:11px;color:var(--muted);">s</span></div></div>`;
const sevClass = s => s==='high'?'h':s==='medium'?'m':s==='low'?'l':'u';
document.getElementById('rows').innerHTML = d.failures.map(f => `
<div class="failure-row">
<div><span class="sev-dot ${sevClass(f.severity)}"></span> <span style="color:var(--muted); font-size:10px; text-transform:uppercase;">${f.source}</span></div>
<div class="name">${f.processName}</div>
<div class="ev">×${f.eventCount}</div>
</div>`).join('');
const h = d.hermes.last24h;
document.getElementById('h-summary').textContent = `${h.total} calls · ${h.byType.classify} cls · ${h.byType.chat} chat · ${h.byType['idea-chat']} idea · ${h.fail} failed`;
document.getElementById('recent').innerHTML = d.hermes.recent.slice(0,8).map(c => `
<div style="flex:0 0 auto; padding:8px 10px; background:rgba(255,255,255,0.04); border:1px solid var(--grid); border-radius:6px; font-size:11px; min-width:140px;">
<div style="color:var(--muted); font-size:9px; text-transform:uppercase; letter-spacing:1px;">${c.type}</div>
<div style="font-weight:500; margin-top:2px;">${c.target.slice(0,18)}</div>
<div style="color:${c.ok?'var(--ok)':'var(--hi)'}; font-size:10px; margin-top:2px;">${c.ok?'✓':'✗'} ${(c.latencyMs/1000).toFixed(1)}s</div>
</div>`).join('');
});
</script>
</body></html>