import React, { useMemo } from 'react'; import type { AudioResult, AudioReason } from '../../services/audioService'; import { Shield, AlertTriangle, CheckCircle, Info, Download, Trash2 } from 'lucide-react'; interface AudioReportPanelProps { result: AudioResult; onReset: () => void; } function generateCaseId(result: AudioResult): string { // Deterministic case ID derived from result data — stable across re-renders const seed = `${result.ai_probability}-${result.verdict}-${result.audio_metadata.duration_sec}`; let hash = 0; for (let i = 0; i < seed.length; i++) { hash = ((hash << 5) - hash + seed.charCodeAt(i)) & 0xffffffff; } return Math.abs(hash).toString(36).toUpperCase().padStart(9, '0').slice(0, 9); } const AudioReportPanel: React.FC = ({ result, onReset }) => { // Stable case ID — won't change on re-renders const caseId = useMemo(() => generateCaseId(result), [result]); const getSeverityColor = (severity: string) => { switch (severity.toLowerCase()) { case 'critical': return 'var(--accent-red)'; case 'high': return 'var(--accent-orange)'; case 'medium': return 'var(--accent-yellow)'; default: return '#00E5CC'; } }; const handleDownload = () => { const report = { case_id: caseId, generated_at: new Date().toISOString(), verdict: result.verdict, ai_probability: result.ai_probability, threat_level: result.threat_level, confidence: result.confidence, agreement: result.agreement, fusion_rule: result.fusion_rule, forensic_summary: result.forensic_summary, recommended_action: result.recommended_action, audio_metadata: result.audio_metadata, signal_scores: result.signal_scores, stability_score: result.stability_score, primary_reasons: result.primary_reasons, supporting_reasons: result.supporting_reasons, exonerating_factors: result.exonerating_factors, }; const blob = new Blob([JSON.stringify(report, null, 2)], { type: 'application/json' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = `fakeshield-audio-report-${caseId}.json`; a.click(); URL.revokeObjectURL(url); }; return (

Forensic Report

Case ID: {caseId}

{/* Summary Card */}
{result.threat_level === 'CRITICAL' || result.threat_level === 'HIGH' ? ( ) : ( )}

{result.verdict.replace(/_/g, ' ')}

"{result.forensic_summary}"

{/* Fusion rule badge */} {result.fusion_rule && (
Decision Rule: {result.fusion_rule.replace(/_/g, ' ').toLowerCase()}
)} {/* Action Recommendation */}
Counter-Measures

{result.recommended_action}

{/* Audio metadata strip */}
{[ { label: 'Duration', value: `${result.audio_metadata.duration_sec.toFixed(1)}s` }, { label: 'Segments', value: `${result.audio_metadata.num_chunks}` }, { label: 'Format', value: result.audio_metadata.format_hint.toUpperCase() }, { label: 'Stability', value: `${((result.stability_score ?? 1) * 100).toFixed(0)}%` }, ].map(({ label, value }) => (
{label} {value}
))}
{/* Primary Reasons */} {result.primary_reasons.length > 0 && (
Primary Evidence Indicators
{result.primary_reasons.map((reason: AudioReason, i: number) => (
{reason.signal} Analysis {Math.round(reason.score * 100)}% match

{reason.message}

{reason.evidence}

))}
)} {result.primary_reasons.length === 0 && (

No primary synthesis indicators detected.

)} {/* Supporting Reasons */} {result.supporting_reasons.length > 0 && (
Supporting Forensic Signals
{result.supporting_reasons.map((reason: AudioReason, i: number) => (

{reason.message}

{reason.evidence}

))}
)} {/* Exonerating Factors */} {result.exonerating_factors.length > 0 && (
Human Authenticity Markers
    {result.exonerating_factors.map((factor: string, i: number) => (
  • {factor}
  • ))}
)}
); }; export default AudioReportPanel;