> ## Documentation Index
> Fetch the complete documentation index at: https://docs.winnerr.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Sentiment Analysis

> AI-powered sentiment analysis for calls, communications, and coaching insights in real estate

Winnerr's sentiment analysis system provides real-time insights into client interactions, helping real estate agents understand client emotions, improve communication effectiveness, and receive personalized coaching recommendations.

## Sentiment Analysis Capabilities

<CardGroup cols={2}>
  <Card title="Real-time Call Analysis" icon="phone">
    Live sentiment tracking during phone calls with instant feedback
  </Card>

  <Card title="Communication Scoring" icon="message-square">
    Sentiment analysis for emails, SMS, and other text communications
  </Card>

  <Card title="Coaching Insights" icon="graduation-cap">
    Personalized recommendations to improve client interactions
  </Card>

  <Card title="Trend Analysis" icon="trending-up">
    Historical sentiment patterns and relationship health metrics
  </Card>
</CardGroup>

## Sentiment Analysis Architecture

### AI Processing Pipeline

```mermaid theme={null}
graph TB
    A[Communication Input] --> B{Input Type}
    
    B -->|Voice Call| C[Spectropic Transcription]
    B -->|Text Message| D[Text Processing]
    B -->|Email| E[Email Content Extraction]
    
    C --> F[Transcript Segmentation]
    D --> G[Text Normalization]
    E --> G
    
    F --> H[Speaker Diarization]
    G --> I[Sentiment Classification]
    H --> I
    
    I --> J[Emotion Detection]
    J --> K[Intent Recognition]
    K --> L[Relationship Analysis]
    
    L --> M[Coaching Recommendations]
    L --> N[Trend Analysis]
    L --> O[Alert Generation]
    
    subgraph "AI Models"
        P[BERT Sentiment Model]
        Q[Emotion Classification]
        R[Real Estate NLP]
    end
    
    I --> P
    J --> Q
    K --> R
```

### Sentiment Processing Engine

```typescript theme={null}
// lib/ai/sentiment-analysis-service.ts
export class SentimentAnalysisService {
  private sentimentModel: SentimentModel;
  private emotionClassifier: EmotionClassifier;
  private realEstateNLP: RealEstateNLP;
  
  constructor() {
    this.sentimentModel = new SentimentModel();
    this.emotionClassifier = new EmotionClassifier();
    this.realEstateNLP = new RealEstateNLP();
  }
  
  async analyzeCallRecording(
    callId: string,
    transcriptSegments: TranscriptSegment[]
  ): Promise<CallSentimentAnalysis> {
    try {
      const analysis: CallSentimentAnalysis = {
        callId,
        overallSentiment: 0,
        agentSentiment: 0,
        clientSentiment: 0,
        emotionalJourney: [],
        keyMoments: [],
        coachingInsights: [],
        riskFactors: [],
        conversationMetrics: {
          duration: 0,
          agentTalkTime: 0,
          clientTalkTime: 0,
          interruptionCount: 0,
          silenceRatio: 0
        }
      };
      
      // Process each transcript segment
      for (const segment of transcriptSegments) {
        const segmentAnalysis = await this.analyzeTranscriptSegment(segment);
        
        // Update overall metrics
        if (segment.speaker === 'agent') {
          analysis.conversationMetrics.agentTalkTime += 
            (segment.endTime - segment.startTime);
        } else if (segment.speaker === 'customer') {
          analysis.conversationMetrics.clientTalkTime += 
            (segment.endTime - segment.startTime);
        }
        
        // Track emotional journey
        analysis.emotionalJourney.push({
          timestamp: segment.startTime,
          speaker: segment.speaker,
          sentiment: segmentAnalysis.sentiment,
          emotions: segmentAnalysis.emotions,
          confidence: segmentAnalysis.confidence
        });
        
        // Identify key moments
        if (this.isKeyMoment(segmentAnalysis)) {
          analysis.keyMoments.push({
            timestamp: segment.startTime,
            type: this.classifyMomentType(segmentAnalysis),
            description: segmentAnalysis.keyPhrase,
            sentiment: segmentAnalysis.sentiment,
            impact: segmentAnalysis.impact
          });
        }
        
        // Detect risk factors
        const riskFactors = this.detectRiskFactors(segmentAnalysis);
        analysis.riskFactors.push(...riskFactors);
      }
      
      // Calculate overall sentiment scores
      analysis.overallSentiment = this.calculateOverallSentiment(analysis.emotionalJourney);
      analysis.agentSentiment = this.calculateSpeakerSentiment(analysis.emotionalJourney, 'agent');
      analysis.clientSentiment = this.calculateSpeakerSentiment(analysis.emotionalJourney, 'customer');
      
      // Generate coaching insights
      analysis.coachingInsights = await this.generateCoachingInsights(analysis);
      
      // Store analysis results
      await this.storeAnalysisResults(analysis);
      
      return analysis;
      
    } catch (error) {
      log.error('Call sentiment analysis failed', { 
        callId, 
        error: parseError(error) 
      });
      throw error;
    }
  }
  
  private async analyzeTranscriptSegment(
    segment: TranscriptSegment
  ): Promise<SegmentSentimentAnalysis> {
    const text = segment.text;
    
    // Basic sentiment analysis
    const sentimentResult = await this.sentimentModel.analyze(text);
    
    // Emotion classification
    const emotions = await this.emotionClassifier.classify(text);
    
    // Real estate specific analysis
    const realEstateContext = await this.realEstateNLP.analyze(text);
    
    // Extract key phrases and intent
    const keyPhrases = this.extractKeyPhrases(text);
    const intent = this.classifyIntent(text, realEstateContext);
    
    return {
      segmentId: segment.id,
      text,
      speaker: segment.speaker,
      sentiment: sentimentResult.score, // -1 to 1
      confidence: sentimentResult.confidence,
      emotions: emotions.map(e => ({
        emotion: e.label,
        intensity: e.score
      })),
      keyPhrase: keyPhrases[0]?.phrase || '',
      intent: intent.category,
      topics: realEstateContext.topics,
      impact: this.calculateImpact(sentimentResult, emotions, realEstateContext)
    };
  }
  
  private generateCoachingInsights(
    analysis: CallSentimentAnalysis
  ): CoachingInsight[] {
    const insights: CoachingInsight[] = [];
    
    // Analyze talk time ratio
    const totalTalkTime = analysis.conversationMetrics.agentTalkTime + 
                         analysis.conversationMetrics.clientTalkTime;
    const agentRatio = analysis.conversationMetrics.agentTalkTime / totalTalkTime;
    
    if (agentRatio > 0.7) {
      insights.push({
        type: 'TALK_TIME',
        severity: 'MEDIUM',
        title: 'Consider Listening More',
        description: 'You spoke 70% of the time. Try asking more open-ended questions to encourage client engagement.',
        recommendation: 'Use phrases like "Tell me more about..." or "How do you feel about..." to encourage client participation.',
        impact: 'CONVERSATION_QUALITY'
      });
    }
    
    // Analyze sentiment trajectory
    const sentimentTrend = this.analyzeSentimentTrend(analysis.emotionalJourney);
    
    if (sentimentTrend === 'DECLINING') {
      insights.push({
        type: 'SENTIMENT_DECLINE',
        severity: 'HIGH',
        title: 'Client Sentiment Declined',
        description: 'The client\'s sentiment became more negative during the call.',
        recommendation: 'Consider reaching out with a follow-up to address any concerns that may have arisen.',
        impact: 'RELATIONSHIP_HEALTH'
      });
    }
    
    // Analyze emotional moments
    const negativeKeyMoments = analysis.keyMoments.filter(m => m.sentiment < -0.3);
    
    if (negativeKeyMoments.length > 0) {
      insights.push({
        type: 'NEGATIVE_MOMENTS',
        severity: 'MEDIUM',
        title: 'Address Negative Reactions',
        description: `Found ${negativeKeyMoments.length} moments with negative client reactions.`,
        recommendation: 'Review these moments and consider how to better handle similar situations in the future.',
        impact: 'DEAL_PROGRESSION',
        details: negativeKeyMoments.map(m => ({
          timestamp: m.timestamp,
          description: m.description
        }))
      });
    }
    
    // Real estate specific insights
    const propertyDiscussions = analysis.keyMoments.filter(m => 
      m.description.includes('property') || m.description.includes('house')
    );
    
    if (propertyDiscussions.length === 0 && analysis.conversationMetrics.duration > 300) {
      insights.push({
        type: 'MISSING_PROPERTY_FOCUS',
        severity: 'LOW',
        title: 'Limited Property Discussion',
        description: 'The conversation didn\'t focus much on specific properties.',
        recommendation: 'Consider steering future conversations toward specific property interests and preferences.',
        impact: 'LEAD_QUALIFICATION'
      });
    }
    
    return insights;
  }
}

interface CallSentimentAnalysis {
  callId: string;
  overallSentiment: number;
  agentSentiment: number;
  clientSentiment: number;
  emotionalJourney: EmotionalDataPoint[];
  keyMoments: KeyMoment[];
  coachingInsights: CoachingInsight[];
  riskFactors: RiskFactor[];
  conversationMetrics: ConversationMetrics;
}

interface EmotionalDataPoint {
  timestamp: number;
  speaker: string;
  sentiment: number;
  emotions: { emotion: string; intensity: number }[];
  confidence: number;
}

interface KeyMoment {
  timestamp: number;
  type: 'POSITIVE_REACTION' | 'NEGATIVE_REACTION' | 'OBJECTION' | 'COMMITMENT' | 'QUESTION';
  description: string;
  sentiment: number;
  impact: 'HIGH' | 'MEDIUM' | 'LOW';
}

interface CoachingInsight {
  type: string;
  severity: 'HIGH' | 'MEDIUM' | 'LOW';
  title: string;
  description: string;
  recommendation: string;
  impact: 'RELATIONSHIP_HEALTH' | 'DEAL_PROGRESSION' | 'CONVERSATION_QUALITY' | 'LEAD_QUALIFICATION';
  details?: unknown[];
}
```

## Real-time Sentiment Monitoring

### Live Call Analysis Dashboard

```typescript theme={null}
// components/live-sentiment-monitor.tsx
export function LiveSentimentMonitor({ callId }: { callId: string }) {
  const [sentimentData, setSentimentData] = useState<LiveSentimentData>({
    currentSentiment: 0,
    agentSentiment: 0,
    clientSentiment: 0,
    emotionalJourney: [],
    activeEmotions: [],
    alerts: []
  });
  
  const [isMonitoring, setIsMonitoring] = useState(false);
  const socket = useSocket();
  
  useEffect(() => {
    if (!socket || !callId) return;
    
    // Join call monitoring room
    socket.emit('join-call-monitoring', { callId });
    
    // Listen for real-time sentiment updates
    socket.on('sentiment-update', (data: SentimentUpdate) => {
      setSentimentData(prev => ({
        ...prev,
        currentSentiment: data.sentiment,
        emotionalJourney: [...prev.emotionalJourney, {
          timestamp: Date.now(),
          sentiment: data.sentiment,
          speaker: data.speaker,
          emotions: data.emotions
        }].slice(-50), // Keep last 50 data points
        activeEmotions: data.emotions,
        agentSentiment: data.speaker === 'agent' ? data.sentiment : prev.agentSentiment,
        clientSentiment: data.speaker === 'customer' ? data.sentiment : prev.clientSentiment
      }));
    });
    
    // Listen for sentiment alerts
    socket.on('sentiment-alert', (alert: SentimentAlert) => {
      setSentimentData(prev => ({
        ...prev,
        alerts: [alert, ...prev.alerts.slice(0, 4)] // Keep last 5 alerts
      }));
      
      // Show toast notification for important alerts
      if (alert.severity === 'HIGH') {
        toast.warning(alert.message, {
          duration: 5000,
          action: {
            label: 'View Details',
            onClick: () => openAlertDetails(alert)
          }
        });
      }
    });
    
    setIsMonitoring(true);
    
    return () => {
      socket.off('sentiment-update');
      socket.off('sentiment-alert');
      socket.emit('leave-call-monitoring', { callId });
      setIsMonitoring(false);
    };
  }, [socket, callId]);
  
  const getSentimentColor = (sentiment: number): string => {
    if (sentiment > 0.3) return 'text-green-600';
    if (sentiment > -0.3) return 'text-yellow-600';
    return 'text-red-600';
  };
  
  const getSentimentIcon = (sentiment: number) => {
    if (sentiment > 0.3) return <SmilePlus className="h-5 w-5" />;
    if (sentiment > -0.3) return <Meh className="h-5 w-5" />;
    return <Frown className="h-5 w-5" />;
  };
  
  return (
    <div className="space-y-4">
      {/* Monitoring Status */}
      <div className="flex items-center justify-between">
        <div className="flex items-center space-x-2">
          <div className={`
            w-3 h-3 rounded-full
            ${isMonitoring ? 'bg-green-500 animate-pulse' : 'bg-gray-400'}
          `} />
          <span className="text-sm font-medium">
            {isMonitoring ? 'Live Monitoring' : 'Not Monitoring'}
          </span>
        </div>
        
        <div className="text-xs text-gray-500">
          Call ID: {callId}
        </div>
      </div>
      
      {/* Current Sentiment */}
      <div className="grid grid-cols-3 gap-4">
        <Card>
          <CardContent className="pt-4">
            <div className="flex items-center justify-between">
              <div>
                <p className="text-sm text-gray-600">Overall</p>
                <p className={`text-lg font-bold ${getSentimentColor(sentimentData.currentSentiment)}`}>
                  {(sentimentData.currentSentiment * 100).toFixed(0)}%
                </p>
              </div>
              <div className={getSentimentColor(sentimentData.currentSentiment)}>
                {getSentimentIcon(sentimentData.currentSentiment)}
              </div>
            </div>
          </CardContent>
        </Card>
        
        <Card>
          <CardContent className="pt-4">
            <div className="flex items-center justify-between">
              <div>
                <p className="text-sm text-gray-600">Agent</p>
                <p className={`text-lg font-bold ${getSentimentColor(sentimentData.agentSentiment)}`}>
                  {(sentimentData.agentSentiment * 100).toFixed(0)}%
                </p>
              </div>
              <User className="h-5 w-5 text-blue-500" />
            </div>
          </CardContent>
        </Card>
        
        <Card>
          <CardContent className="pt-4">
            <div className="flex items-center justify-between">
              <div>
                <p className="text-sm text-gray-600">Client</p>
                <p className={`text-lg font-bold ${getSentimentColor(sentimentData.clientSentiment)}`}>
                  {(sentimentData.clientSentiment * 100).toFixed(0)}%
                </p>
              </div>
              <UserCheck className="h-5 w-5 text-purple-500" />
            </div>
          </CardContent>
        </Card>
      </div>
      
      {/* Emotional Journey Chart */}
      <Card>
        <CardHeader>
          <CardTitle className="text-sm">Emotional Journey</CardTitle>
        </CardHeader>
        <CardContent>
          <ResponsiveContainer width="100%" height={200}>
            <LineChart data={sentimentData.emotionalJourney}>
              <CartesianGrid strokeDasharray="3 3" />
              <XAxis 
                dataKey="timestamp" 
                type="number"
                scale="time"
                domain={['dataMin', 'dataMax']}
                tickFormatter={(value) => new Date(value).toLocaleTimeString()}
              />
              <YAxis domain={[-1, 1]} />
              <Tooltip 
                labelFormatter={(value) => new Date(value).toLocaleTimeString()}
                formatter={(value: number, name: string) => [
                  `${(value * 100).toFixed(0)}%`,
                  name === 'sentiment' ? 'Sentiment' : name
                ]}
              />
              <Line 
                type="monotone" 
                dataKey="sentiment" 
                stroke="#3b82f6" 
                strokeWidth={2}
                dot={{ r: 3 }}
              />
            </LineChart>
          </ResponsiveContainer>
        </CardContent>
      </Card>
      
      {/* Active Emotions */}
      {sentimentData.activeEmotions.length > 0 && (
        <Card>
          <CardHeader>
            <CardTitle className="text-sm">Current Emotions</CardTitle>
          </CardHeader>
          <CardContent>
            <div className="flex flex-wrap gap-2">
              {sentimentData.activeEmotions.map((emotion, index) => (
                <Badge 
                  key={index}
                  variant="secondary"
                  className="capitalize"
                >
                  {emotion.emotion} ({(emotion.intensity * 100).toFixed(0)}%)
                </Badge>
              ))}
            </div>
          </CardContent>
        </Card>
      )}
      
      {/* Recent Alerts */}
      {sentimentData.alerts.length > 0 && (
        <Card>
          <CardHeader>
            <CardTitle className="text-sm">Recent Alerts</CardTitle>
          </CardHeader>
          <CardContent>
            <div className="space-y-2">
              {sentimentData.alerts.map((alert, index) => (
                <div 
                  key={index}
                  className={`
                    p-2 rounded border-l-4 text-sm
                    ${alert.severity === 'HIGH' 
                      ? 'border-red-500 bg-red-50' 
                      : alert.severity === 'MEDIUM'
                        ? 'border-yellow-500 bg-yellow-50'
                        : 'border-blue-500 bg-blue-50'
                    }
                  `}
                >
                  <div className="flex items-center justify-between">
                    <span className="font-medium">{alert.type}</span>
                    <time className="text-xs text-gray-500">
                      {formatDistanceToNow(new Date(alert.timestamp))} ago
                    </time>
                  </div>
                  <p className="text-gray-600 mt-1">{alert.message}</p>
                </div>
              ))}
            </div>
          </CardContent>
        </Card>
      )}
      
      {/* Coaching Tips */}
      <Card>
        <CardHeader>
          <CardTitle className="text-sm">Real-time Coaching</CardTitle>
        </CardHeader>
        <CardContent>
          <div className="space-y-2 text-sm">
            {sentimentData.clientSentiment < -0.3 && (
              <div className="p-2 bg-yellow-50 border border-yellow-200 rounded">
                <p className="font-medium text-yellow-800">💡 Client seems concerned</p>
                <p className="text-yellow-700">Try acknowledging their feelings and asking open-ended questions.</p>
              </div>
            )}
            
            {sentimentData.agentSentiment < -0.2 && (
              <div className="p-2 bg-blue-50 border border-blue-200 rounded">
                <p className="font-medium text-blue-800">🎯 Stay positive</p>
                <p className="text-blue-700">Your tone affects the client. Take a breath and focus on solutions.</p>
              </div>
            )}
            
            {sentimentData.currentSentiment > 0.4 && (
              <div className="p-2 bg-green-50 border border-green-200 rounded">
                <p className="font-medium text-green-800">✅ Great momentum!</p>
                <p className="text-green-700">The conversation is going well. Consider moving toward next steps.</p>
              </div>
            )}
          </div>
        </CardContent>
      </Card>
    </div>
  );
}
```

## Sentiment Analytics Dashboard

### Historical Sentiment Analysis

```typescript theme={null}
// components/sentiment-analytics-dashboard.tsx
export function SentimentAnalyticsDashboard() {
  const { data: analytics } = useQuery({
    queryKey: ['sentiment-analytics'],
    queryFn: () => fetch('/api/analytics/sentiment').then(res => res.json())
  });
  
  const [timeRange, setTimeRange] = useState<'7d' | '30d' | '90d'>('30d');
  const [selectedMetric, setSelectedMetric] = useState<'overall' | 'calls' | 'emails'>('overall');
  
  return (
    <div className="space-y-6">
      {/* Summary Cards */}
      <div className="grid grid-cols-1 md:grid-cols-4 gap-4">
        <Card>
          <CardHeader className="pb-2">
            <CardTitle className="text-sm font-medium">Avg Sentiment</CardTitle>
          </CardHeader>
          <CardContent>
            <div className="text-2xl font-bold text-green-600">
              {(analytics?.averageSentiment * 100 || 0).toFixed(0)}%
            </div>
            <p className="text-xs text-gray-500 mt-1">
              +{analytics?.sentimentImprovement || 0}% vs last period
            </p>
          </CardContent>
        </Card>
        
        <Card>
          <CardHeader className="pb-2">
            <CardTitle className="text-sm font-medium">Positive Interactions</CardTitle>
          </CardHeader>
          <CardContent>
            <div className="text-2xl font-bold text-blue-600">
              {analytics?.positiveInteractions || 0}
            </div>
            <p className="text-xs text-gray-500 mt-1">
              {((analytics?.positiveInteractions / analytics?.totalInteractions) * 100 || 0).toFixed(0)}% of total
            </p>
          </CardContent>
        </Card>
        
        <Card>
          <CardHeader className="pb-2">
            <CardTitle className="text-sm font-medium">Risk Alerts</CardTitle>
          </CardHeader>
          <CardContent>
            <div className="text-2xl font-bold text-red-600">
              {analytics?.riskAlerts || 0}
            </div>
            <p className="text-xs text-gray-500 mt-1">
              {analytics?.resolvedAlerts || 0} resolved
            </p>
          </CardContent>
        </Card>
        
        <Card>
          <CardHeader className="pb-2">
            <CardTitle className="text-sm font-medium">Coaching Score</CardTitle>
          </CardHeader>
          <CardContent>
            <div className="text-2xl font-bold text-purple-600">
              {analytics?.coachingScore || 0}/100
            </div>
            <p className="text-xs text-gray-500 mt-1">
              Based on improvements
            </p>
          </CardContent>
        </Card>
      </div>
      
      {/* Sentiment Trend Chart */}
      <Card>
        <CardHeader>
          <div className="flex items-center justify-between">
            <CardTitle>Sentiment Trends</CardTitle>
            <div className="flex space-x-2">
              <Select value={timeRange} onValueChange={(value: unknown) => setTimeRange(value)}>
                <SelectTrigger className="w-24">
                  <SelectValue />
                </SelectTrigger>
                <SelectContent>
                  <SelectItem value="7d">7 days</SelectItem>
                  <SelectItem value="30d">30 days</SelectItem>
                  <SelectItem value="90d">90 days</SelectItem>
                </SelectContent>
              </Select>
            </div>
          </div>
        </CardHeader>
        <CardContent>
          <ResponsiveContainer width="100%" height={300}>
            <LineChart data={analytics?.sentimentTrend || []}>
              <CartesianGrid strokeDasharray="3 3" />
              <XAxis dataKey="date" />
              <YAxis domain={[-1, 1]} />
              <Tooltip 
                formatter={(value: number) => [`${(value * 100).toFixed(0)}%`, 'Sentiment']}
              />
              <Line 
                type="monotone" 
                dataKey="averageSentiment" 
                stroke="#3b82f6" 
                strokeWidth={2}
                name="Average Sentiment"
              />
              <Line 
                type="monotone" 
                dataKey="clientSentiment" 
                stroke="#10b981" 
                strokeWidth={2}
                name="Client Sentiment"
              />
              <Line 
                type="monotone" 
                dataKey="agentSentiment" 
                stroke="#f59e0b" 
                strokeWidth={2}
                name="Agent Sentiment"
              />
            </LineChart>
          </ResponsiveContainer>
        </CardContent>
      </Card>
      
      {/* Emotion Distribution */}
      <div className="grid grid-cols-1 md:grid-cols-2 gap-6">
        <Card>
          <CardHeader>
            <CardTitle>Top Emotions Detected</CardTitle>
          </CardHeader>
          <CardContent>
            <div className="space-y-3">
              {analytics?.topEmotions?.map((emotion: unknown) => (
                <div key={emotion.name} className="flex items-center justify-between">
                  <div className="flex items-center space-x-2">
                    <div className="text-lg">{emotion.emoji}</div>
                    <span className="text-sm font-medium capitalize">{emotion.name}</span>
                  </div>
                  <div className="flex items-center space-x-2">
                    <span className="text-sm text-gray-600">{emotion.count}</span>
                    <div className="w-20 h-2 bg-gray-200 rounded">
                      <div 
                        className="h-2 bg-blue-500 rounded transition-all"
                        style={{ width: `${emotion.percentage}%` }}
                      />
                    </div>
                  </div>
                </div>
              ))}
            </div>
          </CardContent>
        </Card>
        
        <Card>
          <CardHeader>
            <CardTitle>Coaching Insights</CardTitle>
          </CardHeader>
          <CardContent>
            <div className="space-y-3">
              {analytics?.recentInsights?.map((insight: unknown, index: number) => (
                <div key={index} className="p-3 bg-gray-50 rounded border">
                  <div className="flex items-center justify-between mb-2">
                    <span className="text-sm font-medium">{insight.title}</span>
                    <Badge variant={insight.severity === 'HIGH' ? 'destructive' : 'secondary'}>
                      {insight.severity}
                    </Badge>
                  </div>
                  <p className="text-xs text-gray-600">{insight.description}</p>
                  <p className="text-xs text-blue-600 mt-1 font-medium">
                    💡 {insight.recommendation}
                  </p>
                </div>
              ))}
            </div>
          </CardContent>
        </Card>
      </div>
      
      {/* Client Relationship Health */}
      <Card>
        <CardHeader>
          <CardTitle>Client Relationship Health</CardTitle>
        </CardHeader>
        <CardContent>
          <div className="grid grid-cols-1 md:grid-cols-3 gap-4">
            {analytics?.clientHealth?.map((client: unknown) => (
              <div key={client.id} className="p-3 border rounded">
                <div className="flex items-center justify-between mb-2">
                  <span className="font-medium">{client.name}</span>
                  <div className={`
                    w-3 h-3 rounded-full
                    ${client.healthScore > 0.5 
                      ? 'bg-green-500' 
                      : client.healthScore > 0 
                        ? 'bg-yellow-500' 
                        : 'bg-red-500'
                    }
                  `} />
                </div>
                <div className="text-sm text-gray-600 space-y-1">
                  <p>Health Score: {(client.healthScore * 100).toFixed(0)}%</p>
                  <p>Last Contact: {client.lastContact}</p>
                  <p>Sentiment Trend: {client.sentimentTrend}</p>
                </div>
                {client.alerts.length > 0 && (
                  <div className="mt-2">
                    <Badge variant="destructive" className="text-xs">
                      {client.alerts.length} alert{client.alerts.length > 1 ? 's' : ''}
                    </Badge>
                  </div>
                )}
              </div>
            ))}
          </div>
        </CardContent>
      </Card>
    </div>
  );
}
```

## Next Steps

<CardGroup cols={3}>
  <Card title="Lead Scoring" icon="target" href="/ai/lead-scoring">
    Explore AI-driven lead qualification and scoring
  </Card>

  <Card title="AI Assistant" icon="bot" href="/ai/ai-assistant">
    Learn about the AI-powered assistant features
  </Card>

  <Card title="Communication Hub" icon="phone" href="/features/communication-hub">
    Integrate sentiment analysis with communications
  </Card>
</CardGroup>

***

<Note>
  Sentiment analysis provides invaluable insights into client relationships, helping agents build stronger connections and close more deals through improved communication.
</Note>
