Readiness Dashboard: System status before external pilots

- Shows all systems: API, Database, Storage, Upload, Mission, Map,
  Replay, AI Processing, Decision Pipeline
- Color-coded status: Green/Ready, Yellow/Partial, Red/Not Ready
- Version info: Environment, Version, Commit, Build time
- Exit criteria checklist for external pilots

Next: Deploy pilot environment
This commit is contained in:
Bernt
2026-07-02 16:57:20 +00:00
parent 8a8fb0a4f4
commit c5a42506c1
2 changed files with 149 additions and 0 deletions
+3
View File
@@ -5,6 +5,7 @@ import MissionUpload from './pages/MissionUpload';
import FieldConsole from './pages/FieldConsole';
import HealthDashboard from './pages/HealthDashboard';
import PilotChecklist from './pages/PilotChecklist';
import ReadinessDashboard from './pages/ReadinessDashboard';
function App() {
return (
@@ -17,6 +18,7 @@ function App() {
<Link to="/console">Field Console</Link>
<Link to="/health">Health</Link>
<Link to="/pilot">Pilot 001</Link>
<Link to="/readiness">Readiness</Link>
</nav>
</header>
@@ -27,6 +29,7 @@ function App() {
<Route path="/console" element={<FieldConsole />} />
<Route path="/health" element={<HealthDashboard />} />
<Route path="/pilot" element={<PilotChecklist />} />
<Route path="/readiness" element={<ReadinessDashboard />} />
</Routes>
</div>
);
@@ -0,0 +1,146 @@
/**
* Readiness Dashboard
*
* Shows system status before opening for external pilots.
* Green = ready, Yellow = partial, Red = not ready
*/
import { useEffect, useState } from 'react';
interface SystemStatus {
name: string;
status: 'ready' | 'partial' | 'not-ready';
details: string;
}
function ReadinessDashboard() {
const [systems] = useState<SystemStatus[]>([
{ name: 'API', status: 'ready', details: 'Running on port 3002' },
{ name: 'Database', status: 'ready', details: 'In-memory (PostgreSQL in PR-003B)' },
{ name: 'Object Storage', status: 'partial', details: 'File system (MinIO in PR-003B)' },
{ name: 'Upload', status: 'ready', details: 'Multipart upload working' },
{ name: 'Mission Service', status: 'ready', details: 'Create, list, view missions' },
{ name: 'Map Service', status: 'not-ready', details: 'Not implemented yet' },
{ name: 'Replay', status: 'not-ready', details: 'Not implemented yet' },
{ name: 'AI Processing', status: 'not-ready', details: 'Not implemented yet' },
{ name: 'Decision Pipeline', status: 'not-ready', details: 'Not implemented yet' },
]);
const [version, setVersion] = useState({
environment: 'PILOT',
version: '0.1.0-pilot.1',
commit: 'unknown',
build: new Date().toISOString(),
});
useEffect(() => {
fetch('/health')
.then(r => r.json())
.then(data => {
setVersion(prev => ({
...prev,
version: data.version || prev.version,
}));
})
.catch(() => {});
}, []);
const readyCount = systems.filter(s => s.status === 'ready').length;
const totalCount = systems.length;
const progress = Math.round((readyCount / totalCount) * 100);
return (
<div>
<h2>Readiness Dashboard</h2>
{/* Version Info */}
<div style={{ marginBottom: 20, padding: 16, background: '#f0f0f0', borderRadius: 8 }}>
<p><strong>Environment:</strong> {version.environment}</p>
<p><strong>Version:</strong> {version.version}</p>
<p><strong>Commit:</strong> {version.commit}</p>
<p><strong>Build:</strong> {version.build}</p>
</div>
{/* Progress */}
<div style={{ marginBottom: 20 }}>
<div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: 8 }}>
<span>System Readiness</span>
<span>{readyCount}/{totalCount} ({progress}%)</span>
</div>
<div style={{ height: 8, background: '#f0f0f0', borderRadius: 4, overflow: 'hidden' }}>
<div style={{
width: `${progress}%`,
height: '100%',
background: progress === 100 ? '#32cd32' : progress > 50 ? '#ffd700' : '#dc143c',
transition: 'width 0.3s',
}} />
</div>
</div>
{/* Systems */}
<div style={{ display: 'grid', gap: 12 }}>
{systems.map(system => (
<div key={system.name} style={{
border: '1px solid #ccc',
padding: 16,
borderRadius: 8,
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
}}>
<div>
<h4 style={{ margin: 0 }}>{system.name}</h4>
<p style={{ margin: '4px 0 0', color: '#666', fontSize: 12 }}>{system.details}</p>
</div>
<StatusBadge status={system.status} />
</div>
))}
</div>
{/* Exit Criteria */}
<div style={{ marginTop: 20, padding: 16, background: '#f0f0f0', borderRadius: 8 }}>
<h3>Exit Criteria for External Pilots</h3>
<ul style={{ margin: 0, paddingLeft: 20 }}>
<li> All functional checks pass</li>
<li> No blocking bugs for multiple test missions</li>
<li> Stable upload</li>
<li> Logging works</li>
<li> Backup works</li>
<li> Version update without data loss</li>
<li> New tester can install app independently</li>
<li> New tester can complete mission without help</li>
<li> Can debug mission from start to finish</li>
</ul>
</div>
</div>
);
}
function StatusBadge({ status }: { status: 'ready' | 'partial' | 'not-ready' }) {
const colors = {
ready: '#32cd32',
partial: '#ffd700',
'not-ready': '#dc143c',
};
const labels = {
ready: '🟢 Ready',
partial: '🟡 Partial',
'not-ready': '🔴 Not Ready',
};
return (
<span style={{
padding: '8px 16px',
borderRadius: 16,
background: colors[status],
color: 'white',
fontWeight: 'bold',
fontSize: 14,
}}>
{labels[status]}
</span>
);
}
export default ReadinessDashboard;