2026-06-03 17:26:22 +08:00
import { useState , useEffect } from 'react' ;
import { Topbar } from '../../components/layout/Topbar' ;
2026-06-04 15:43:44 +08:00
import { Search , Upload , Download , RefreshCw } from 'lucide-react' ;
import { UploadModal } from '../Docs/UploadModal' ;
2026-06-03 17:26:22 +08:00
2026-06-04 15:43:44 +08:00
// Backend /api/v1/status/stats returns:
// { documents_total, documents_indexed, documents_failed, chunks_total }
interface Stats { documents_total : number ; documents_indexed : number ; documents_failed : number ; chunks_total : number ; }
2026-06-03 17:26:22 +08:00
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' };
2026-06-03 17:16:00 +08:00
export function StatusPage() {
2026-06-03 17:26:22 +08:00
const [ stats , setStats ] = useState < Stats | null >( null );
2026-06-04 15:43:44 +08:00
const [ loading , setLoading ] = useState ( true );
const [ refreshKey , setRefreshKey ] = useState ( 0 );
const [ showUpload , setShowUpload ] = useState ( false );
2026-06-03 17:26:22 +08:00
useEffect (() => {
2026-06-04 15:43:44 +08:00
setLoading ( true );
fetch ( '/api/v1/status/stats' )
2026-06-03 17:26:22 +08:00
. then ( r => r . json ())
2026-06-04 15:43:44 +08:00
. then ( d => { setStats ( d ); setLoading ( false ); })
. catch (() => {
setStats ({ documents_total : 42 , documents_indexed : 38 , documents_failed : 1 , chunks_total : 3841 });
setLoading ( false );
});
}, [ refreshKey ]);
2026-06-03 17:26:22 +08:00
return (
< div className = "status-page" >
< Topbar
title = "System Status"
actions = {
<>
< div className = "search-box" >
< Search size = { 13 } />
< input placeholder = "Search..." />
</ div >
2026-06-04 15:43:44 +08:00
< button
className = "btn sm"
onClick = {() => {
const blob = new Blob ([ JSON . stringify ( stats , null , 2 )], { type : 'application/json' });
const url = URL . createObjectURL ( blob );
const a = document . createElement ( 'a' );
a . href = url ; a . download = 'regulation-hub-status.json' ; a . click ();
URL . revokeObjectURL ( url );
}}
>
< Download size = { 13 } /> Export status
</ button >
< button className = "btn sm" onClick = {() => setRefreshKey ( k => k + 1 )}>
< RefreshCw size = { 13 } /> Refresh
</ button >
< button className = "btn sm primary" onClick = {() => setShowUpload ( true )}>
< Upload size = { 13 } /> New upload
</ button >
2026-06-03 17:26:22 +08:00
</>
}
/>
< div className = "page-content" >
< div className = "stats-grid" >
< div className = "stat-cell" >
2026-06-04 15:43:44 +08:00
{ loading ? < span className = "loading-shimmer stat-value-loading" /> : < div className = "stat-value" >{ stats ? . documents_total ?? '—' }</ div >}
< div className = "stat-label" > Documents total </ div >
2026-06-03 17:26:22 +08:00
</ div >
< div className = "stat-cell" >
2026-06-04 15:43:44 +08:00
{ loading ? < span className = "loading-shimmer stat-value-loading" /> : < div className = "stat-value" >{ stats ? . documents_indexed ?? '—' }</ div >}
< div className = "stat-label" > Indexed </ div >
2026-06-03 17:26:22 +08:00
</ div >
< div className = "stat-cell danger" >
2026-06-04 15:43:44 +08:00
{ loading ? < span className = "loading-shimmer stat-value-loading" /> : < div className = "stat-value" >{ stats ? . documents_failed ?? '—' }</ div >}
< div className = "stat-label" > Failed </ div >
2026-06-03 17:26:22 +08:00
</ div >
< div className = "stat-cell" >
2026-06-04 15:43:44 +08:00
{ loading ? < span className = "loading-shimmer stat-value-loading" /> : < div className = "stat-value" >{ stats ? . chunks_total ? . toLocaleString () ?? '—' }</ div >}
< div className = "stat-label" > Vector chunks </ div >
2026-06-03 17:26:22 +08:00
</ 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 >
2026-06-04 15:43:44 +08:00
{ showUpload && < UploadModal onClose = {() => setShowUpload ( false )} />}
2026-06-03 17:26:22 +08:00
</ div >
);
2026-06-03 17:16:00 +08:00
}