mirror of
https://github.com/zvx-echo6/meshai.git
synced 2026-06-10 17:04:45 +02:00
fix: dashboard — coverage pillar, active alerts env fallback
- Add coverage pillar to /api/health response and _serialize_health_score - Add coverage to MeshHealth.pillars TypeScript interface - Add Coverage PillarBar between Utilization and Behavior - Active Alerts panel: show high-severity env events (immediate/priority) as fallback when mesh alerts are empty, with ENV badge Issues 3 (Live Event Feed) and 4 (RF Propagation): diagnosed as env feed configuration — SWPC adapter disabled, only ducting feed loaded, /api/env/active returns 0 events. Not a code bug. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
dcb53ae30c
commit
15f2b6c89a
6 changed files with 108 additions and 68 deletions
|
|
@ -19,6 +19,7 @@ export interface MeshHealth {
|
||||||
pillars: {
|
pillars: {
|
||||||
infrastructure: number
|
infrastructure: number
|
||||||
utilization: number
|
utilization: number
|
||||||
|
coverage: number
|
||||||
behavior: number
|
behavior: number
|
||||||
power: number
|
power: number
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -696,6 +696,7 @@ export default function Dashboard() {
|
||||||
<div className="mt-6 space-y-3">
|
<div className="mt-6 space-y-3">
|
||||||
<PillarBar label="Infrastructure" value={health.pillars?.infrastructure ?? 0} />
|
<PillarBar label="Infrastructure" value={health.pillars?.infrastructure ?? 0} />
|
||||||
<PillarBar label="Utilization" value={health.pillars?.utilization ?? 0} />
|
<PillarBar label="Utilization" value={health.pillars?.utilization ?? 0} />
|
||||||
|
<PillarBar label="Coverage" value={health.pillars?.coverage ?? 0} />
|
||||||
<PillarBar label="Behavior" value={health.pillars?.behavior ?? 0} />
|
<PillarBar label="Behavior" value={health.pillars?.behavior ?? 0} />
|
||||||
<PillarBar label="Power" value={health.pillars?.power ?? 0} />
|
<PillarBar label="Power" value={health.pillars?.power ?? 0} />
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -714,12 +715,48 @@ export default function Dashboard() {
|
||||||
<AlertItem key={i} alert={alert} />
|
<AlertItem key={i} alert={alert} />
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (() => {
|
||||||
<div className="flex items-center gap-2 text-slate-500 py-4">
|
const highSeverityEnv = envEvents
|
||||||
<CheckCircle size={16} className="text-green-500" />
|
.filter(e => e.severity === 'immediate' || e.severity === 'priority')
|
||||||
<span>No active alerts</span>
|
.sort((a, b) => {
|
||||||
</div>
|
const ord: Record<string, number> = { immediate: 0, priority: 1 }
|
||||||
)}
|
const diff = (ord[a.severity] ?? 2) - (ord[b.severity] ?? 2)
|
||||||
|
if (diff !== 0) return diff
|
||||||
|
return (b.fetched_at || 0) - (a.fetched_at || 0)
|
||||||
|
})
|
||||||
|
.slice(0, 5)
|
||||||
|
if (highSeverityEnv.length > 0) {
|
||||||
|
return (
|
||||||
|
<div className="space-y-3 max-h-48 overflow-y-auto">
|
||||||
|
{highSeverityEnv.map((ev, i) => {
|
||||||
|
const sevStyle = ev.severity === 'immediate'
|
||||||
|
? { bg: 'bg-red-500/10', border: 'border-red-500', icon: AlertCircle, iconColor: 'text-red-500' }
|
||||||
|
: { bg: 'bg-amber-500/10', border: 'border-amber-500', icon: AlertTriangle, iconColor: 'text-amber-500' }
|
||||||
|
const Icon = sevStyle.icon
|
||||||
|
return (
|
||||||
|
<div key={ev.event_id || i} className={`p-3 rounded-lg ${sevStyle.bg} border-l-2 ${sevStyle.border} flex items-start gap-3`}>
|
||||||
|
<Icon size={16} className={sevStyle.iconColor} />
|
||||||
|
<div className="flex-1 min-w-0">
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<span className="px-1.5 py-0.5 rounded text-xs bg-slate-500/20 text-slate-400 border border-slate-500/30 font-mono">ENV</span>
|
||||||
|
<span className="text-xs text-slate-500">{ev.severity}</span>
|
||||||
|
</div>
|
||||||
|
<div className="text-sm text-slate-200 mt-1">{ev.headline}</div>
|
||||||
|
<div className="text-xs text-slate-500 mt-1">{ev.source} · {new Date(ev.fetched_at * 1000).toLocaleTimeString()}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<div className="flex items-center gap-2 text-slate-500 py-4">
|
||||||
|
<CheckCircle size={16} className="text-green-500" />
|
||||||
|
<span>No active alerts</span>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
})()}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Quick Stats */}
|
{/* Quick Stats */}
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@ def _serialize_health_score(score) -> dict:
|
||||||
"tier": score.tier,
|
"tier": score.tier,
|
||||||
"infrastructure": round(score.infrastructure, 1),
|
"infrastructure": round(score.infrastructure, 1),
|
||||||
"utilization": round(score.utilization, 1),
|
"utilization": round(score.utilization, 1),
|
||||||
|
"coverage": round(score.coverage, 1),
|
||||||
"behavior": round(score.behavior, 1),
|
"behavior": round(score.behavior, 1),
|
||||||
"power": round(score.power, 1),
|
"power": round(score.power, 1),
|
||||||
"infra_online": score.infra_online,
|
"infra_online": score.infra_online,
|
||||||
|
|
@ -73,6 +74,7 @@ async def get_health(request: Request):
|
||||||
"pillars": {
|
"pillars": {
|
||||||
"infrastructure": round(score.infrastructure, 1),
|
"infrastructure": round(score.infrastructure, 1),
|
||||||
"utilization": round(score.utilization, 1),
|
"utilization": round(score.utilization, 1),
|
||||||
|
"coverage": round(score.coverage, 1),
|
||||||
"behavior": round(score.behavior, 1),
|
"behavior": round(score.behavior, 1),
|
||||||
"power": round(score.power, 1),
|
"power": round(score.power, 1),
|
||||||
},
|
},
|
||||||
|
|
|
||||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
|
@ -8,8 +8,8 @@
|
||||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||||
<link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;600;700&display=swap" rel="stylesheet">
|
<link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;600;700&display=swap" rel="stylesheet">
|
||||||
<script type="module" crossorigin src="/assets/index-CM6OazXs.js"></script>
|
<script type="module" crossorigin src="/assets/index-BCcZxs_h.js"></script>
|
||||||
<link rel="stylesheet" crossorigin href="/assets/index-Dp9XCfH-.css">
|
<link rel="stylesheet" crossorigin href="/assets/index-BEgceSNC.css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="root"></div>
|
<div id="root"></div>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue