Files
boc/packages/ui/src/pages/HealthDashboard.tsx
T
Bernt e669155bc9 MVP-0: Field Console + Health Dashboard
- Field Console: 4 tabs (Upload, Queue, Artifact Viewer, Timeline)
- Health Dashboard: system pulse for 6 engines
- No more ADR documents until MVP-0 proven with real mission

Stoppregel: Ingen ny arkitektur förrän första verkliga uppdraget.

Next: Pilot 001 — First real field upload
2026-07-02 16:22:21 +00:00

107 lines
2.9 KiB
TypeScript

/**
* Health Dashboard
*
* System pulse. Not BI. Not statistics.
* Just: which engines are running?
*/
import { useEffect, useState } from 'react';
interface EngineStatus {
name: string;
status: 'running' | 'stopped' | 'error';
lastHeartbeat: string;
}
function HealthDashboard() {
const [engines, setEngines] = useState<EngineStatus[]>([
{ name: 'Reality', status: 'running', lastHeartbeat: new Date().toISOString() },
{ name: 'Knowledge', status: 'running', lastHeartbeat: new Date().toISOString() },
{ name: 'Decision', status: 'stopped', lastHeartbeat: '-' },
{ name: 'Mission', status: 'stopped', lastHeartbeat: '-' },
{ name: 'Economic', status: 'running', lastHeartbeat: new Date().toISOString() },
{ name: 'Learning', status: 'stopped', lastHeartbeat: '-' },
]);
useEffect(() => {
// Poll health status
const interval = setInterval(() => {
fetch('/health')
.then(r => r.json())
.then(data => {
// Update engine status based on health response
setEngines(prev => prev.map(engine => ({
...engine,
status: data.status === 'ok' ? 'running' : 'error',
lastHeartbeat: new Date().toISOString(),
})));
})
.catch(() => {
setEngines(prev => prev.map(engine => ({
...engine,
status: 'error',
})));
});
}, 30000); // Every 30 seconds
return () => clearInterval(interval);
}, []);
return (
<div>
<h2>Health Dashboard</h2>
<p style={{ color: '#666', marginBottom: 20 }}>System pulse not statistics</p>
<div style={{ display: 'grid', gap: 16 }}>
{engines.map(engine => (
<div key={engine.name} style={{
border: '1px solid #ccc',
padding: 16,
borderRadius: 8,
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
}}>
<div>
<h3 style={{ margin: 0 }}>{engine.name} Engine</h3>
<p style={{ margin: '4px 0 0', color: '#666', fontSize: 12 }}>
Last heartbeat: {engine.lastHeartbeat === '-' ? '-' : new Date(engine.lastHeartbeat).toLocaleTimeString()}
</p>
</div>
<StatusIndicator status={engine.status} />
</div>
))}
</div>
</div>
);
}
function StatusIndicator({ status }: { status: 'running' | 'stopped' | 'error' }) {
const colors = {
running: '#32cd32',
stopped: '#ccc',
error: '#dc143c',
};
const labels = {
running: '✓ Running',
stopped: '○ Stopped',
error: '✗ Error',
};
return (
<span style={{
padding: '8px 16px',
borderRadius: 16,
background: colors[status],
color: 'white',
fontWeight: 'bold',
fontSize: 14,
}}>
{labels[status]}
</span>
);
}
export default HealthDashboard;