'use client'; import { useMemo } from 'react'; import { X, Filter, Smile, Frown, Meh, AlertTriangle } from 'lucide-react'; import type { SentimentDataPoint, Sentiment } from '../types'; import { DOMAIN_LABELS } from '../types'; import { useReviewIQFilters } from '@/contexts/ReviewIQFilterContext'; interface SentimentPieProps { data: SentimentDataPoint[]; // AI-generated insight (optional - shows when available) insight?: string | null; } // User-friendly sentiment config const SENTIMENT_CONFIG: Record = { 'V+': { emoji: '😊', icon: Smile, label: 'Happy', description: 'Positive experiences', color: '#22c55e', bgColor: '#dcfce7', borderColor: '#86efac', }, 'V-': { emoji: '😟', icon: Frown, label: 'Unhappy', description: 'Negative experiences', color: '#ef4444', bgColor: '#fee2e2', borderColor: '#fca5a5', }, 'V0': { emoji: '😐', icon: Meh, label: 'Neutral', description: 'Factual mentions', color: '#eab308', bgColor: '#fef9c3', borderColor: '#fde047', }, 'V±': { emoji: 'šŸ¤”', icon: AlertTriangle, label: 'Mixed', description: 'Both good & bad', color: '#f97316', bgColor: '#ffedd5', borderColor: '#fdba74', }, }; // Map valence codes to sentiment filter values const valenceToSentiment: Record = { 'V+': 'positive', 'V0': 'neutral', 'V-': 'negative', 'V±': 'negative', // Mixed is treated as negative for filtering }; // Display order const SENTIMENT_ORDER = ['V+', 'V-', 'V0', 'V±']; /** * Sentiment Overview - Visual cards showing how customers feel. * User-friendly design with emojis and clear numbers. * Click to filter by sentiment. */ export function SentimentPie({ data, insight }: SentimentPieProps) { const { filters, toggleSentiment } = useReviewIQFilters(); // Process data const processedData = useMemo(() => { const lookup = new Map(); let totalReviews = 0; let totalMentions = 0; data.forEach((d) => { lookup.set(d.valence, d); totalReviews += d.review_count; totalMentions += d.count; }); // Build cards in order const cards = SENTIMENT_ORDER .filter(valence => lookup.has(valence)) .map(valence => { const d = lookup.get(valence)!; const config = SENTIMENT_CONFIG[valence]; return { valence, config, reviewCount: d.review_count, mentionCount: d.count, percentage: d.percentage, }; }); // Calculate overall sentiment score (0-100) const positive = lookup.get('V+'); const negative = lookup.get('V-'); const posCount = positive?.review_count || 0; const negCount = negative?.review_count || 0; const sentimentScore = totalReviews > 0 ? Math.round(((posCount - negCount) / totalReviews + 1) * 50) : 50; return { cards, totalReviews, totalMentions, sentimentScore }; }, [data]); const handleClick = (valence: string) => { const sentiment = valenceToSentiment[valence]; if (sentiment) { toggleSentiment(sentiment); } }; const isFiltering = filters.sentiment.length > 0; const hasDomainFilter = filters.urtDomain !== null; // Determine sentiment indicator color const getScoreColor = () => { if (processedData.sentimentScore >= 60) return 'text-green-600'; if (processedData.sentimentScore >= 40) return 'text-yellow-600'; return 'text-red-600'; }; const getScoreEmoji = () => { if (processedData.sentimentScore >= 70) return 'šŸŽ‰'; if (processedData.sentimentScore >= 55) return 'šŸ‘'; if (processedData.sentimentScore >= 45) return '😐'; if (processedData.sentimentScore >= 30) return 'šŸ˜•'; return '😰'; }; return (

How Customers Feel

{processedData.totalReviews.toLocaleString()} reviews analyzed

{hasDomainFilter && !isFiltering && ( {DOMAIN_LABELS[filters.urtDomain!]} )} {isFiltering && (
{filters.sentiment.join(', ')}
)}
{/* AI Insight (when available) */} {insight && (
✨
AI Insight

{insight}

)} {processedData.cards.length === 0 ? (
No sentiment data available
) : ( <> {/* Overall Sentiment Score */}
{getScoreEmoji()}
{processedData.sentimentScore}%
Sentiment Score
{/* Sentiment Cards Grid */}
{processedData.cards.map((card) => { const sentiment = valenceToSentiment[card.valence]; const isActive = sentiment && filters.sentiment.includes(sentiment); const Icon = card.config.icon; return ( ); })}
{/* Tip */}
Click any card to filter reviews by sentiment
)}
); }