import type { ContentAnalysisResponse } from "../types/ApiTypes" import StatsStyling from "../styles/stats_styling"; const styles = StatsStyling; type EmotionalStatsProps = { contentData: ContentAnalysisResponse; } const EmotionalStats = ({contentData}: EmotionalStatsProps) => { const rows = contentData.average_emotion_by_topic ?? []; const overallEmotionAverage = contentData.overall_emotion_average ?? []; const dominantEmotionDistribution = contentData.dominant_emotion_distribution ?? []; const emotionBySource = contentData.emotion_by_source ?? []; const lowSampleThreshold = 20; const stableSampleThreshold = 50; const emotionKeys = rows.length ? Object.keys(rows[0]).filter((key) => key.startsWith("emotion_")) : []; const strongestPerTopic = rows.map((row) => { let maxKey = ""; let maxValue = Number.NEGATIVE_INFINITY; emotionKeys.forEach((key) => { const value = Number(row[key] ?? 0); if (value > maxValue) { maxValue = value; maxKey = key; } }); return { topic: String(row.topic), count: Number(row.n ?? 0), emotion: maxKey.replace("emotion_", "") || "unknown", value: maxValue > Number.NEGATIVE_INFINITY ? maxValue : 0 }; }); const formatEmotion = (value: string) => { if (!value) return "Unknown"; return value.charAt(0).toUpperCase() + value.slice(1); }; const sampleSizes = strongestPerTopic .map((topic) => topic.count) .filter((count) => Number.isFinite(count) && count > 0) .sort((a, b) => a - b); const lowSampleTopics = strongestPerTopic.filter((topic) => topic.count < lowSampleThreshold).length; const stableSampleTopics = strongestPerTopic.filter((topic) => topic.count >= stableSampleThreshold).length; const medianSampleSize = sampleSizes.length ? sampleSizes[Math.floor(sampleSizes.length / 2)] : 0; if (!rows.length) { return (

Emotion by Topic

No topic emotion data available.

); } return (

Topic Mood Overview

Use the strength score together with post count. Topics with fewer than {lowSampleThreshold} events are often noisy.

Topics: {strongestPerTopic.length} Median Posts: {medianSampleSize} Small Topics (<{lowSampleThreshold}): {lowSampleTopics} Stable Topics ({stableSampleThreshold}+): {stableSampleTopics}

Strength means how far the top emotion is ahead in that topic. It does not mean model accuracy.

Mood Averages

Average score for each emotion.

{!overallEmotionAverage.length ? (
No overall emotion averages available.
) : (
{[...overallEmotionAverage] .sort((a, b) => b.score - a.score) .map((row) => (
{formatEmotion(row.emotion)}
{row.score.toFixed(3)}
))}
)}

Mood Split

How often each emotion is dominant.

{!dominantEmotionDistribution.length ? (
No dominant-emotion split available.
) : (
{[...dominantEmotionDistribution] .sort((a, b) => b.ratio - a.ratio) .map((row) => (
{formatEmotion(row.emotion)}
{(row.ratio * 100).toFixed(1)}% • {row.count.toLocaleString()} events
))}
)}

Mood by Source

Leading emotion in each source.

{!emotionBySource.length ? (
No source emotion profile available.
) : (
{[...emotionBySource] .sort((a, b) => b.event_count - a.event_count) .map((row) => (
{row.source}
{formatEmotion(row.dominant_emotion)} • {row.dominant_score.toFixed(3)} • {row.event_count.toLocaleString()} events
))}
)}

Topic Snapshots

Per-topic mood with strength and post count.

{strongestPerTopic.map((topic) => (

{topic.topic}

Likely Mood
{formatEmotion(topic.emotion)}
Strength {topic.value.toFixed(3)}
Posts in Topic {topic.count}
))}
); } export default EmotionalStats;