// src/components/FusionPanel.jsx import React from 'react' import { RadarChart, PolarGrid, PolarAngleAxis, Radar, ResponsiveContainer } from 'recharts' import { EMOTION_COLORS } from '../lib/api' const EMOTION_ORDER = ['Happy', 'Angry', 'Fear', 'Sad', 'Neutral', 'Surprised', 'Disgust'] const RISK_CONFIG = { low: { label: 'LOW RISK', color: '#30d988', bg: 'rgba(48,217,136,0.08)', bar: '#30d988' }, medium: { label: 'MEDIUM RISK', color: '#f5a623', bg: 'rgba(245,166,35,0.08)', bar: '#f5a623' }, high: { label: 'HIGH RISK', color: '#ff4d6a', bg: 'rgba(255,77,106,0.08)', bar: '#ff4d6a' }, } export default function FusionPanel({ result }) { const emotion = result?.fused_emotion || '—' const probs = result?.fused_probs || {} const conf = result?.confidence ?? null const valence = result?.valence ?? null const arousal = result?.arousal ?? null const risk = result?.risk_level || 'low' const weights = result?.modal_weights || { visual: 0.5, text: 0.5 } const rec = result?.recommendation || '' const ms = result?.inference_ms ?? null const riskCfg = RISK_CONFIG[risk] const radarData = EMOTION_ORDER.map(name => ({ name, value: Math.round((probs[name] ?? 0) * 100), })) const emoColor = EMOTION_COLORS[emotion] || '#5e82aa' return (
Cross-Modal Fusion Output Attention Gate
{/* Primary emotion + confidence */}
Fused Emotion
{emotion}
{conf !== null && (
{(conf * 100).toFixed(1)}% confidence
)}
{/* Radar chart */}
{/* Valence / Arousal */}
`${v >= 0 ? '+' : ''}${v.toFixed(2)}`} colorFn={v => v > 0.2 ? '#30d988' : v < -0.2 ? '#ff4d6a' : '#5e82aa'} /> v.toFixed(2)} colorFn={v => v > 0.6 ? '#ff4d6a' : v < 0.3 ? '#9b74f7' : '#f5a623'} />
{/* Modal weights */}
Attention Gate Weights
Vision {(weights.visual * 100).toFixed(0)}% Text {(weights.text * 100).toFixed(0)}%
{/* Risk level */}
● {riskCfg.label} {ms !== null && ( {ms.toFixed(0)}ms )}
{rec && (

{rec}

)}
) } function ScalarMetric({ label, value, min, max, format, colorFn }) { const pct = value !== null ? ((value - min) / (max - min)) * 100 : 50 const color = value !== null ? colorFn(value) : '#5e82aa' return (
{label}
{value !== null ? format(value) : '—'}
) }