'use client' import { useEffect, useState, useCallback } from 'react' import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card' import { Button } from '@/components/ui/button' import { Badge } from '@/components/ui/badge' import { ChartContainer, ChartTooltip, ChartTooltipContent, type ChartConfig, } from '@/components/ui/chart' import { Bar, BarChart, XAxis, YAxis, Cell } from 'recharts' import { Sparkles, Loader2, AlertCircle } from 'lucide-react' import { AnalyzeConfirmDialog } from './analyze-confirm-dialog' import { formatCost } from '@/lib/format' import type { MessageClassification } from '@/lib/openai' const CHART_COLORS = [ 'var(--chart-1)', 'var(--chart-2)', 'var(--chart-3)', 'var(--chart-4)', 'var(--chart-5)', 'var(--chart-6)', 'var(--chart-7)', 'var(--chart-8)', 'var(--chart-9)', 'var(--chart-10)', ] function DistributionChart({ title, description, data, }: { title: string description: string data: Record }) { const chartData = Object.entries(data) .sort((a, b) => b[1] - a[1]) .map(([name, count]) => ({ name, count })) const config: ChartConfig = { count: { label: 'Count', color: 'var(--chart-1)' }, } const total = chartData.reduce((sum, d) => sum + d.count, 0) return ( {title} {description} { const n = Number(value) return `${n} (${((n / total) * 100).toFixed(1)}%)` }} /> } /> {chartData.map((_, i) => ( ))} ) } interface AnalysisData { classifications: MessageClassification[] model: string totalMessages: number inputTokens: number outputTokens: number costUSD: number analyzedAt: string } export function SessionAIAnalysis({ sessionId }: { sessionId: string }) { const [analysis, setAnalysis] = useState(null) const [loading, setLoading] = useState(true) const [analyzing, setAnalyzing] = useState(false) const [error, setError] = useState(null) const [confirmOpen, setConfirmOpen] = useState(false) const [estimate, setEstimate] = useState<{ sessionCount: number messageCount: number estimatedCostUSD: number } | null>(null) const loadAnalysis = useCallback(async () => { try { const res = await fetch(`/api/analyze?sessionId=${sessionId}`) const data = await res.json() if (data.analysis) setAnalysis(data.analysis) } catch { // No analysis yet — that's fine } finally { setLoading(false) } }, [sessionId]) useEffect(() => { loadAnalysis() }, [loadAnalysis]) async function handleAnalyzeClick() { // Fetch estimate setEstimate(null) setConfirmOpen(true) try { const res = await fetch('/api/analyze/estimate', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ sessionId }), }) const data = await res.json() setEstimate({ sessionCount: 1, messageCount: data.messageCount, estimatedCostUSD: data.estimatedCostUSD, }) } catch { setError('Failed to estimate cost') setConfirmOpen(false) } } async function handleConfirm() { setAnalyzing(true) setError(null) setConfirmOpen(false) try { const res = await fetch('/api/analyze', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ sessionId }), }) if (!res.ok) { const data = await res.json() throw new Error(data.error || 'Analysis failed') } const data = await res.json() setAnalysis(data.analysis) } catch (e) { setError((e as Error).message) } finally { setAnalyzing(false) } } if (loading) { return (
Loading...
) } if (!analysis) { return (

No AI analysis yet

Classify user messages by type, role, skill level, and sentiment

{error && (
{error}
)}
) } // Build distributions from classifications const messageTypes: Record = {} const roles: Record = {} const skillLevels: Record = {} const sentiments: Record = {} for (const cls of analysis.classifications) { messageTypes[cls.messageType] = (messageTypes[cls.messageType] || 0) + 1 roles[cls.role] = (roles[cls.role] || 0) + 1 skillLevels[cls.skillLevel] = (skillLevels[cls.skillLevel] || 0) + 1 sentiments[cls.sentiment] = (sentiments[cls.sentiment] || 0) + 1 } return (
{analysis.totalMessages} messages classified {analysis.model} Cost: {formatCost(analysis.costUSD)}
Message Classifications Per-message breakdown
{analysis.classifications.map((cls, i) => (
{i + 1}. {cls.messagePreview}
{cls.messageType} {cls.role} {cls.skillLevel} {cls.sentiment}
))}
) }