import React, { useState, useCallback } from 'react'; import { useApi } from '../hooks/useApi'; import { verifyAuditChain, generateComplianceReport, getExportEventsUrl, type ComplianceReportSummary, type ChainVerification, } from '../api/compliance'; // ─── Helpers ──────────────────────────────────────────────── function formatDate(d: Date): string { return d.toISOString().slice(0, 10); } function defaultFrom(): string { const d = new Date(); d.setDate(d.getDate() - 30); return formatDate(d); } function defaultTo(): string { return formatDate(new Date()); } // ─── Main Page ────────────────────────────────────────────── export default function Compliance() { const chainQuery = useApi(() => verifyAuditChain(), []); const [from, setFrom] = useState(defaultFrom); const [to, setTo] = useState(defaultTo); const [generating, setGenerating] = useState(false); const [report, setReport] = useState(null); const [reportError, setReportError] = useState(''); const [history, setHistory] = useState>([]); const chain: ChainVerification | null = chainQuery.data ?? null; const handleGenerate = useCallback(async () => { setGenerating(true); setReportError(''); setReport(null); try { const fromDate = new Date(from); const toDate = new Date(to); const diffDays = (toDate.getTime() - fromDate.getTime()) / (1000 * 60 * 60 * 24); if (diffDays > 365) { setReportError('Date range must not exceed 365 days.'); return; } if (diffDays < 0) { setReportError('"From" date must be before "To" date.'); return; } const result = await generateComplianceReport(from, to); setReport(result); setHistory(prev => [{ generatedAt: new Date().toISOString(), from, to }, ...prev]); } catch (err: unknown) { setReportError(err instanceof Error ? err.message : String(err)); } finally { setGenerating(false); } }, [from, to]); const handleExport = useCallback((format: 'json' | 'csv') => { const url = getExportEventsUrl(from, to, format); window.open(url, '_blank'); }, [from, to]); return (

📋 EU AI Act Compliance

{/* Chain Integrity Status */}

Audit Chain Integrity

{chainQuery.loading &&

Verifying chain integrity...

} {chainQuery.error &&

Error: {String(chainQuery.error)}

} {chain && (
{chain.verified ? 'Verified' : 'Broken'} ({chain.sessionsVerified} sessions checked) {!chain.verified && chain.brokenChains.length > 0 && ( Broken: {chain.brokenChains.map((c) => c.reason).join('; ')} )}
)}
{/* Report Generation */}

Generate Compliance Report

{reportError &&

{reportError}

}
{/* Report Summary */} {report && (

📄 Report Summary

Period: {report.range.from} → {report.range.to}
Events: {report.totalEvents.toLocaleString()}
Agents: {report.agentCount}
Sessions: {report.sessionCount}
Guardrail Triggers: {report.guardrailTriggers}
Chain:{' '} {report.chainVerification.verified ? '✓ Verified' : '✗ Broken'}
{report.signature && (
Signature: {report.signature.slice(0, 16)}...
)}
{/* Export Buttons */}
)} {/* Export History */} {history.length > 0 && (

Report History

{history.map((h, i) => ( ))}
Generated From To
{new Date(h.generatedAt).toLocaleString()} {h.from} {h.to}
)}
); } export { Compliance }; // ─── Styles ───────────────────────────────────────────────── const cardStyle: React.CSSProperties = { padding: '16px', border: '1px solid #e2e8f0', borderRadius: '8px', marginBottom: '16px', }; const btnStyle: React.CSSProperties = { padding: '8px 16px', background: '#3b82f6', color: 'white', border: 'none', borderRadius: '6px', cursor: 'pointer', fontSize: '14px', }; const smallBtnStyle: React.CSSProperties = { padding: '6px 12px', background: 'transparent', border: '1px solid #d1d5db', borderRadius: '4px', cursor: 'pointer', fontSize: '13px', }; const inputStyle: React.CSSProperties = { padding: '6px 10px', border: '1px solid #d1d5db', borderRadius: '4px', fontSize: '14px', marginTop: '4px', }; const thStyle: React.CSSProperties = { padding: '8px 12px', fontSize: '13px', color: '#64748b' }; const tdStyle: React.CSSProperties = { padding: '8px 12px', fontSize: '13px' };