feat: implement System Status page with stats grid, panel grid, KPI strip

This commit is contained in:
2026-06-03 17:26:22 +08:00
parent 07ccf055ab
commit 235de65975

View File

@@ -1,3 +1,155 @@
import { useState, useEffect } from 'react';
import { Topbar } from '../../components/layout/Topbar';
import { Search, Upload, Download } from 'lucide-react';
interface Stats { total_documents: number; vector_chunks: number; high_impact: number; last_90_days: number; }
const TASKS = [
{ name: 'EU AI Act — Article 13 check', status: 'ok', progress: 88, cta: 'View report' },
{ name: 'GB/T 42118 compliance scan', status: 'warn', progress: 54, cta: 'Continue' },
{ name: 'MIIT Draft — automotive AI embedding', status: 'info', progress: 12, cta: 'Start' },
];
const PROGRAMS = [
{ name: 'EU AI Act Readiness', status: 'ok', coverage: 88 },
{ name: 'China MIIT Compliance', status: 'warn', coverage: 54 },
{ name: 'ISO/SAE 21434 Audit', status: 'info', coverage: 32 },
];
const KPIS = [
{ label: 'Retrieval hit rate', value: 94, unit: '%' },
{ label: 'Evidence coverage', value: 78, unit: '%' },
{ label: 'Reviewer SLA', value: 91, unit: '%' },
];
const SERVICES = [
{ name: 'Vector store (Chroma)', status: 'ok' },
{ name: 'LLM gateway (Claude)', status: 'ok' },
{ name: 'Document parser', status: 'ok' },
{ name: 'SSE stream endpoint', status: 'ok' },
{ name: 'Regulation feed sync', status: 'warn' },
];
const EVENTS = [
{ date: '2025-11-18', title: 'EU AI Act — Delegated acts published', summary: 'European Commission releases implementing rules for high-risk AI classification under Annex III.' },
{ date: '2025-10-30', title: 'MIIT Draft — automotive AI', summary: 'New draft regulation covers in-vehicle AI training data provenance and OTA update governance.' },
{ date: '2025-10-05', title: 'ISO/SAE 21434 amendment', summary: 'Amendment 1 clarifies cybersecurity management system scope for software-only updates.' },
];
const STATUS_LABEL: Record<string, string> = { ok: 'Complete', warn: 'In progress', info: 'Pending' };
export function StatusPage() { export function StatusPage() {
return <div className="page-content"><p>Status</p></div>; const [stats, setStats] = useState<Stats | null>(null);
useEffect(() => {
fetch('/api/v1/perception/stats')
.then(r => r.json())
.then(d => setStats(d))
.catch(() => setStats({ total_documents: 42, vector_chunks: 3841, high_impact: 7, last_90_days: 14 }));
}, []);
return (
<div className="status-page">
<Topbar
title="System Status"
actions={
<>
<div className="search-box">
<Search size={13} />
<input placeholder="Search..." />
</div>
<button className="btn sm"><Download size={13} />Export status</button>
<button className="btn sm primary"><Upload size={13} />New upload</button>
</>
}
/>
<div className="page-content">
<div className="stats-grid">
<div className="stat-cell">
<div className="stat-value">{stats?.total_documents ?? '—'}</div>
<div className="stat-label">Documents indexed</div>
</div>
<div className="stat-cell">
<div className="stat-value">{stats?.vector_chunks?.toLocaleString() ?? '—'}</div>
<div className="stat-label">Vector chunks</div>
</div>
<div className="stat-cell danger">
<div className="stat-value">{stats?.high_impact ?? '—'}</div>
<div className="stat-label">High-impact signals</div>
</div>
<div className="stat-cell">
<div className="stat-value">{stats?.last_90_days ?? '—'}</div>
<div className="stat-label">Last 90 days</div>
</div>
</div>
<div className="panel-grid">
<div className="panel-left">
<div className="card">
<div className="card-header">Workflow queue</div>
{TASKS.map(t => (
<div key={t.name} className="task-row">
<div className="task-info">
<div className="task-name">{t.name}</div>
<div className="task-progress-bar">
<div className="task-progress-fill" style={{ width: `${t.progress}%` }} />
</div>
</div>
<span className={`status ${t.status}`}>{STATUS_LABEL[t.status]}</span>
<button className="btn sm">{t.cta}</button>
</div>
))}
</div>
<div className="card">
<div className="card-header">Active compliance programs</div>
{PROGRAMS.map(p => (
<div key={p.name} className="program-row">
<span className={`status ${p.status}`} style={{ marginRight: 'auto' }}>{p.name}</span>
<span className="program-pct">{p.coverage}%</span>
</div>
))}
<div className="kpi-strip">
{KPIS.map(k => (
<div key={k.label} className="kpi-item">
<div className="kpi-label">{k.label}</div>
<div className="kpi-bar"><div className="kpi-fill" style={{ width: `${k.value}%` }} /></div>
<div className="kpi-value">{k.value}{k.unit}</div>
</div>
))}
</div>
</div>
</div>
<div className="panel-right">
<div className="card">
<div className="card-header">System health</div>
{SERVICES.map(s => (
<div key={s.name} className="service-row">
<span className="service-name">{s.name}</span>
<span className={`status ${s.status}`}>{s.status === 'ok' ? 'Online' : 'Degraded'}</span>
</div>
))}
</div>
<div className="card">
<div className="card-header">Regulatory watch</div>
{EVENTS.map(e => (
<div key={e.title} className="event-row">
<div className="event-date">{e.date}</div>
<div className="event-title">{e.title}</div>
<div className="event-summary">{e.summary}</div>
</div>
))}
</div>
</div>
</div>
</div>
<footer className="page-footer">
<div className="live-dot" />
<span>Regulation Hub · T-Systems AI · Online</span>
</footer>
</div>
);
} }