import { useState } from 'react' import { Alert, Box, Button, Chip, CircularProgress, Container, Divider, FormControl, FormHelperText, Grid, InputLabel, MenuItem, Paper, Select, Snackbar, TextField, Toolbar, Typography, } from '@mui/material' import DescriptionIcon from '@mui/icons-material/Description' import PictureAsPdfIcon from '@mui/icons-material/PictureAsPdf' import VerifiedUserIcon from '@mui/icons-material/VerifiedUser' import { reportsApi, settingsApi } from '../api/client' import type { ReportType, ReportFormat, AuditIntegrityResult } from '../types' // ── Report metadata ─────────────────────────────────────────────────────────── const REPORT_INFO: Record = { compliance: { title: 'Compliance Report', description: 'Shows patch compliance percentage per host and group. Includes total packages, pending patches, and last patch timestamp.', columns: [ 'Host', 'FQDN', 'Groups', 'Total Packages', 'Pending Patches', 'Compliance %', 'Last Patched', 'Health Status', ], }, 'patch-history': { title: 'Patch History', description: 'Full history of patch job operations across all hosts. Filter by date range to narrow results.', columns: [ 'Job ID', 'Kind', 'Status', 'Host', 'FQDN', 'Package Count', 'Started At', 'Completed At', 'Duration', 'Operator', ], }, vulnerability: { title: 'Vulnerability Exposure', description: 'Lists all known CVEs affecting managed hosts based on cached patch data from agents.', columns: ['Host', 'FQDN', 'CVE ID', 'Package', 'Severity', 'Available Version', 'Last Seen'], }, audit: { title: 'Audit Trail', description: 'Complete tamper-evident audit log of all system actions. Limited to 10,000 most recent events.', columns: [ 'ID', 'Timestamp', 'Action', 'Actor', 'Target Type', 'Target ID', 'IP Address', 'Request ID', ], }, } // ── Default date helpers ────────────────────────────────────────────────────── const defaultFromDate = () => new Date(Date.now() - 30 * 24 * 60 * 60 * 1000).toISOString().split('T')[0] const defaultToDate = () => new Date().toISOString().split('T')[0] // ── Component ───────────────────────────────────────────────────────────────── export default function ReportsPage() { const [reportType, setReportType] = useState('compliance') const [fromDate, setFromDate] = useState(defaultFromDate()) const [toDate, setToDate] = useState(defaultToDate()) const [groupId, setGroupId] = useState('') const [downloading, setDownloading] = useState(false) const [error, setError] = useState(null) const [verifyingIntegrity, setVerifyingIntegrity] = useState(false) const [integrityResult, setIntegrityResult] = useState(null) const info = REPORT_INFO[reportType] const handleDownload = async (format: ReportFormat) => { setDownloading(true) setError(null) try { const params: Record = {} if (fromDate) params.from = new Date(fromDate).toISOString() if (toDate) params.to = new Date(toDate + 'T23:59:59Z').toISOString() if (reportType === 'compliance' && groupId.trim()) params.group_id = groupId.trim() const res = await reportsApi.download(reportType, format, params) // Trigger browser download const url = window.URL.createObjectURL(new Blob([res.data])) const link = document.createElement('a') link.href = url const ext = format === 'pdf' ? 'pdf' : 'csv' const dateStr = new Date().toISOString().split('T')[0] link.setAttribute('download', `${reportType}-report-${dateStr}.${ext}`) document.body.appendChild(link) link.click() link.remove() window.URL.revokeObjectURL(url) } catch { setError('Failed to generate report. Please try again.') } finally { setDownloading(false) } } const handleVerifyIntegrity = async () => { setVerifyingIntegrity(true) setIntegrityResult(null) try { const { data } = await settingsApi.auditIntegrity() setIntegrityResult(data) } catch (err: unknown) { const msg = err instanceof Error ? err.message : 'Verification failed' setIntegrityResult({ intact: false, rows_checked: 0, errors: [{ row_id: 0, expected_hash: '', actual_hash: msg }] }) } finally { setVerifyingIntegrity(false) } } return ( {/* ── Page header ── */} Reports {/* ── Controls card ── */} Report Options {/* Report Type */} Report Type {/* Date Range */} setFromDate(e.target.value)} InputLabelProps={{ shrink: true }} fullWidth /> setToDate(e.target.value)} InputLabelProps={{ shrink: true }} fullWidth /> {/* Group Filter — compliance only */} {reportType === 'compliance' && ( setGroupId(e.target.value)} placeholder="e.g. 550e8400-e29b-41d4-a716-446655440000" /> Filter compliance report by a specific group UUID )} {/* Download buttons */} {/* ── Audit Integrity card ── */} Audit Integrity Verification Verify the audit log hash chain has not been tampered with. Each entry is cryptographically linked to the previous one. {integrityResult && ( {integrityResult.intact ? `✓ Chain intact — ${integrityResult.rows_checked} rows verified` : `✗ Chain compromised! ${integrityResult.errors.length} error(s) in ${integrityResult.rows_checked} rows`} {integrityResult.errors.length > 0 && ( {integrityResult.errors.slice(0, 5).map((e, i) => ( Row {e.row_id}: expected {e.expected_hash.substring(0, 16)}… got {e.actual_hash.substring(0, 16)}… ))} {integrityResult.errors.length > 5 && ( …and {integrityResult.errors.length - 5} more )} )} )} {/* ── Info card ── */} {info.title} {info.description} Columns in this report: {info.columns.map((col) => ( ))} 📊 PDF includes bar charts for compliance and patch history reports. 📁 CSV is suitable for import into Excel or Google Sheets. {/* ── Error snackbar ── */} setError(null)} anchorOrigin={{ vertical: 'bottom', horizontal: 'center' }} > setError(null)} sx={{ width: '100%' }}> {error} ) }