import { useNavigate } from 'react-router-dom'; import type { HealthScore, PostureSummary, BacklogStats, ConfidenceDistribution, ScannerQuality, HotSink, OwaspBucket, LanguageHealth, SuppressionHygiene, BaselineInfo, WeightedFile, OverviewCount, } from '../../api/types'; import { truncPath } from '../../utils/truncPath'; // ── HealthScoreCard ───────────────────────────────────────────────────────── export function HealthScoreCard({ health, posture, }: { health: HealthScore; posture?: PostureSummary; }) { const gradeClass = `grade-${health.grade.toLowerCase()}`; return (
Health Score
{health.grade}
{health.score} / 100
{posture && (
{posture.message}
)}
{health.components.map((c) => (
{c.score}
{c.label}
))}
); } // ── PostureBanner ────────────────────────────────────────────────────────── export function PostureBanner({ posture }: { posture: PostureSummary }) { return (
{posture.message}
); } // ── BacklogCard ──────────────────────────────────────────────────────────── export function BacklogCard({ backlog }: { backlog: BacklogStats }) { const total = backlog.age_buckets.reduce((s, b) => s + b.count, 0); const noHistory = backlog.oldest_open_days == null && backlog.age_buckets.length === 0; if (noHistory) { return null; } return (
Backlog Age
{backlog.oldest_open_days != null ? `${backlog.oldest_open_days}d` : '–'}
Oldest open
{backlog.median_age_days != null ? `${backlog.median_age_days}d` : '–'}
Median age
{backlog.stale_count}
Older than 30 days
{total > 0 && (
)}
); } function BucketBar({ buckets }: { buckets: OverviewCount[] }) { const total = buckets.reduce((s, b) => s + b.count, 0); if (total === 0) return null; const colors = ['#3498db', '#2ecc71', '#f1c40f', '#e67e22', '#e74c3c']; return (
`${b.name}: ${b.count}`).join(' · ')} > {buckets.map((b, i) => (
))}
); } // ── ConfidenceDistributionChart ──────────────────────────────────────────── export function ConfidenceDistributionChart({ dist, }: { dist: ConfidenceDistribution; }) { const total = dist.high + dist.medium + dist.low + dist.none; if (total === 0) { return (

No data

); } const segments = [ { label: 'High', value: dist.high, color: '#27ae60' }, { label: 'Medium', value: dist.medium, color: '#f39c12' }, { label: 'Low', value: dist.low, color: '#95a5a6' }, { label: 'None', value: dist.none, color: '#bdc3c7' }, ]; return (
{segments.map((s) => s.value > 0 ? (
) : null, )}
{segments.map((s) => (
{s.label} {s.value}
))}
); } // ── ScannerQualityPanel ──────────────────────────────────────────────────── export function ScannerQualityPanel({ quality, crossFileRatio, }: { quality: ScannerQuality; crossFileRatio?: number; }) { const symexAttempted = Object.entries(quality.symex_breakdown || {}) .filter(([k]) => k !== 'not_attempted') .reduce((s, [, v]) => s + v, 0); const symexTotal = Object.values(quality.symex_breakdown || {}).reduce( (s, v) => s + v, 0, ); const totalFiles = quality.files_scanned + quality.files_skipped; const filesValue = totalFiles.toLocaleString(); const filesDetail = quality.files_skipped > 0 ? `${quality.files_scanned.toLocaleString()} fresh · ${quality.files_skipped.toLocaleString()} from cache` : quality.files_scanned > 0 ? `${quality.files_scanned.toLocaleString()} freshly indexed` : undefined; const rows: Array<{ label: string; hint: string; value: string; detail?: string; }> = [ { label: 'Files', hint: 'Files the scanner saw on this run.', value: filesValue, detail: filesDetail, }, { label: 'Functions analyzed', hint: 'Function bodies the call graph saw.', value: quality.functions_analyzed.toLocaleString(), }, { label: 'Call edges resolved', hint: 'Share of call sites that the scanner resolved to a known callee. The remainder are typically external/library calls.', value: `${(quality.call_resolution_rate * 100).toFixed(1)}%`, detail: quality.unresolved_calls > 0 ? `${quality.unresolved_calls.toLocaleString()} unresolved` : undefined, }, { label: 'Cross-file flows', hint: 'Findings whose taint path crosses a file boundary.', value: crossFileRatio != null ? `${(crossFileRatio * 100).toFixed(1)}%` : '0%', detail: 'of findings', }, { label: 'Symbolic verification', hint: 'Taint findings the symbolic engine attempted to verify (confirmed, infeasible, or inconclusive).', value: symexTotal > 0 ? `${(quality.symex_verified_rate * 100).toFixed(1)}%` : 'n/a', detail: symexTotal > 0 ? `${symexAttempted} of ${symexTotal} taint findings` : 'no taint findings', }, ]; return (
{rows.map((r) => (
{r.label}
{r.value}
{r.detail &&
{r.detail}
}
))}
); } // ── HotSinksList ─────────────────────────────────────────────────────────── export function HotSinksList({ sinks }: { sinks: HotSink[] }) { if (!sinks.length) { return (

No data

); } return ( {sinks.map((s) => ( ))}
Sink Findings
{s.callee} {s.count}
); } // ── OwaspChart ───────────────────────────────────────────────────────────── export function OwaspChart({ buckets }: { buckets: OwaspBucket[] }) { if (!buckets.length) { return (

No data

); } const max = Math.max(...buckets.map((b) => b.count), 1); return ( ); } // ── WeightedTopFiles ─────────────────────────────────────────────────────── export function WeightedTopFiles({ files, onRowClick, }: { files: WeightedFile[]; onRowClick?: (name: string) => void; }) { if (!files.length) { return (

No data

); } return ( {files.map((f) => ( onRowClick(f.name) : undefined} > ))}
File Severity Score
{truncPath(f.name, 45)} {f.score}
); } function SeverityStack({ high, medium, low, }: { high: number; medium: number; low: number; }) { const total = high + medium + low; if (total === 0) return null; return (
{high > 0 && (
{high}
)} {medium > 0 && (
{medium}
)} {low > 0 && (
{low}
)}
); } // ── LanguageHealthTable ──────────────────────────────────────────────────── export function LanguageHealthTable({ rows }: { rows: LanguageHealth[] }) { if (!rows.length) { return (

No data

); } return ( {rows.map((r) => ( ))}
Language Findings Severity
{r.language} {r.findings}
); } // ── SuppressionHygieneCard ───────────────────────────────────────────────── export function SuppressionHygieneCard({ hygiene, }: { hygiene: SuppressionHygiene; }) { const total = hygiene.fingerprint_level + hygiene.rule_level + hygiene.file_level + hygiene.rule_in_file_level; const blanket = hygiene.rule_level + hygiene.file_level + hygiene.rule_in_file_level; const blanketDisplay = total > 0 ? `${(hygiene.blanket_rate * 100).toFixed(0)}%` : 'n/a'; const blanketDetail = total > 0 ? `${blanket} of ${total} suppressions are not pinned to a specific finding` : 'No suppressions yet'; return (
Blanket rate Lower is better
{blanketDisplay}
{blanketDetail}
By fingerprint Most specific
{hygiene.fingerprint_level}
By rule in a file
{hygiene.rule_in_file_level}
By rule
{hygiene.rule_level}
By file Least specific
{hygiene.file_level}
); } // ── BaselinePinControl ───────────────────────────────────────────────────── interface BaselinePinControlProps { baseline?: BaselineInfo; latestScanId?: string; onPin: (scanId: string) => void; onUnpin: () => void; isPending: boolean; } export function BaselinePinControl({ baseline, latestScanId, onPin, onUnpin, isPending, }: BaselinePinControlProps) { const navigate = useNavigate(); if (baseline) { const net = baseline.drift_new - baseline.drift_fixed; const driftClass = net > 0 ? 'baseline-drift-bad' : net < 0 ? 'baseline-drift-good' : 'baseline-drift-flat'; return (
Baseline: drift: +{baseline.drift_new} new / -{baseline.drift_fixed} fixed ( {net >= 0 ? '+' : ''} {net})
); } if (!latestScanId) return null; return (
No baseline pinned.
); }