(function () { const { useState, useEffect, useCallback, useRef } = React; /* HELPERS */ function fmtDate(iso) { // parseUTC: backend Z'siz naive UTC string'leri yerel saate doğru çevirir. const d = parseUTC(iso); if (!d) return '—'; const pad = n => String(n).padStart(2, '0'); return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}`; } function scoreColor(score) { if (score == null) return 'var(--text-dim)'; if (score >= 70) return 'var(--green)'; if (score >= 40) return 'var(--amber)'; return 'var(--red)'; } const SENTIMENT_CFG = { positive: { label: 'Pozitif', color: 'var(--green)', emoji: '' }, neutral: { label: 'Nötr', color: 'var(--amber)', emoji: '' }, negative: { label: 'Negatif', color: 'var(--red)', emoji: '' }, }; const CATEGORIES = ['Satış', 'Şikayet', 'Teknik', 'Genel']; const PAGE_SIZE = 20; /* STAT CARD */ function StatCard({ icon, label, value, sub, color, bar, barMax }) { return (
{icon}
{label}
{value}
{sub &&
{sub}
} {bar != null && barMax != null && barMax > 0 && (
)}
); } /* SENTIMENT BAR (inline mini breakdown) */ function SentimentBar({ by_sentiment, total }) { if (!by_sentiment || !total) return null; const segments = [ { key: 'positive', color: 'var(--green)' }, { key: 'neutral', color: 'var(--amber)' }, { key: 'negative', color: 'var(--red)' }, ]; return (
{segments.map(({ key, color }) => { const count = by_sentiment[key] || 0; const pct = total > 0 ? (count / total) * 100 : 0; if (pct < 0.5) return null; return (
); })}
); } /* ANALYSIS DETAIL PANEL (expanded row) */ function AnalysisDetail({ item }) { const sc = SENTIMENT_CFG[item.sentiment] || SENTIMENT_CFG.neutral; const perf = item.agent_performance || {}; const perfKeys = ['empathy', 'clarity', 'resolution', 'professionalism']; const perfLabels = { empathy: 'Empati', clarity: 'Netlik', resolution: 'Çözüm', professionalism: 'Profesyonellik' }; return (
{/* Sol: Özet + intent + resolution */}
{item.summary && (
Özet
{item.summary}
)} {item.customer_intent && (
Müşteri Niyeti
{item.customer_intent}
)} {item.resolution && (
Çözüm
{item.resolution}
)} {item.key_topics && item.key_topics.length > 0 && (
Konular
{item.key_topics.map((t, i) => ( {t} ))}
)}
{/* Sağ: Temsilci performansı + duygu skoru */}
Duygu Skoru
{item.score ?? '—'}
{sc.emoji} {sc.label}
0–100 arası puan
{Object.keys(perf).length > 0 && (
Temsilci Performansı
{perfKeys.filter(k => perf[k] != null).map(k => { const val = Math.min(100, Math.max(0, perf[k])); return (
{perfLabels[k] || k} {val}
); })}
)} {item.flags && item.flags.length > 0 && (
Bayraklar
{item.flags.map((f, i) => ( {f} ))}
)}
); } /* KEYWORD ROW */ function KeywordRow({ kw, onDelete }) { const [deleting, setDeleting] = useState(false); const handleDelete = async () => { if (!window.confirm(`"${kw.keyword}" anahtar kelimesini silmek istiyor musunuz?`)) return; setDeleting(true); try { const r = await authFetch(`/api/analysis/keywords/${kw.id}`, { method: 'DELETE' }); if (r.ok) onDelete(kw.id); } finally { setDeleting(false); } }; return (
{kw.keyword} {kw.category || 'Genel'}
); } /* MAIN COMPONENT */ function SpeechAnalyticsTab({ toast }) { /* State */ const [stats, setStats] = useState(null); const [statsLoading, setStatsLoading] = useState(true); const [keywords, setKeywords] = useState([]); const [kwLoading, setKwLoading] = useState(true); // Keyword form const [newKw, setNewKw] = useState(''); const [newCat, setNewCat] = useState('Genel'); const [newCol, setNewCol] = useState('#3b6df0'); const [kwSaving, setKwSaving] = useState(false); // Batch const [daysBack, setDaysBack] = useState(7); const [batchLoading, setBatchLoading] = useState(false); // Table const [page, setPage] = useState(0); const [expandedId, setExpandedId] = useState(null); const timerRef = useRef(null); /* Fetch helpers */ const loadStats = useCallback(async () => { setStatsLoading(true); try { const r = await authFetch('/api/analysis/stats'); if (r.ok) setStats(await r.json()); } finally { setStatsLoading(false); } }, []); const loadKeywords = useCallback(async () => { setKwLoading(true); try { const r = await authFetch('/api/analysis/keywords'); if (r.ok) setKeywords(await r.json()); } finally { setKwLoading(false); } }, []); /* Auto-refresh every 30s */ useEffect(() => { loadStats(); loadKeywords(); timerRef.current = setInterval(() => { loadStats(); }, 30000); return () => clearInterval(timerRef.current); }, [loadStats, loadKeywords]); /* Keyword add */ const handleAddKeyword = async () => { if (!newKw.trim()) return; setKwSaving(true); try { const r = await authFetch('/api/analysis/keywords', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ keyword: newKw.trim(), category: newCat, color: newCol }), }); if (r.ok) { const created = await r.json(); setKeywords(k => [...k, created]); setNewKw(''); toast && toast('Anahtar kelime eklendi', 'success'); } else { toast && toast('Eklenemedi', 'error'); } } finally { setKwSaving(false); } }; /* Keyword delete */ const handleDeleteKeyword = (id) => { setKeywords(k => k.filter(x => x.id !== id)); toast && toast('Anahtar kelime silindi', 'success'); }; /* Batch analyze */ const handleBatchAnalyze = async () => { setBatchLoading(true); try { const r = await authFetch('/api/analysis/analyze-batch', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ days_back: daysBack }), }); if (r.ok) { const result = await r.json(); toast && toast(`Analiz tamamlandı: ${result.analyzed_count ?? result.count ?? '?'} çağrı`, 'success'); await loadStats(); } else { toast && toast('Analiz başlatılamadı', 'error'); } } finally { setBatchLoading(false); } }; /* Derived stats */ const total = stats?.total_analyzed || 0; const byS = stats?.by_sentiment || {}; const posCount = byS.positive || 0; const neuCount = byS.neutral || 0; const negCount = byS.negative || 0; const posPct = total > 0 ? ((posCount / total) * 100).toFixed(1) : '0'; const neuPct = total > 0 ? ((neuCount / total) * 100).toFixed(1) : '0'; const negPct = total > 0 ? ((negCount / total) * 100).toFixed(1) : '0'; const avgScore = stats?.avg_score != null ? Math.round(stats.avg_score) : null; const topKw = stats?.top_keywords || []; const recentAll = stats?.recent_analyses || []; const flaggedCount = stats?.flagged_count || 0; /* Pagination */ const totalPages = Math.ceil(recentAll.length / PAGE_SIZE); const pageItems = recentAll.slice(page * PAGE_SIZE, page * PAGE_SIZE + PAGE_SIZE); /* Row click */ const handleRowClick = (id) => { setExpandedId(prev => prev === id ? null : id); }; /* RENDER */ return (
{/* HEADER */}

Konuşma Analizi

Çağrıların duygu ve içerik analizini görüntüleyin, anahtar kelimeleri yönetin.

{statsLoading ? 'Yükleniyor...' : 'Otomatik güncelleme: 30sn'}
{/* STATS OVERVIEW */}
{/* SENTIMENT BAR */} {!statsLoading && total > 0 && (
Duygu Dağılımı
{[ { key: 'positive', color: 'var(--green)', label: 'Pozitif', pct: posPct }, { key: 'neutral', color: 'var(--amber)', label: 'Nötr', pct: neuPct }, { key: 'negative', color: 'var(--red)', label: 'Negatif', pct: negPct }, ].map(({ key, color, label, pct }) => (
{label} {pct}%
))}
)} {/* MAIN BODY: Table + Sidebar */}
{/* LEFT: Batch + Recent Analyses */}
{/* Batch Analysis */}
Toplu Analiz
Seçili gün aralığındaki tüm çağrıları analiz edin
Son setDaysBack(Math.max(1, Math.min(90, parseInt(e.target.value) || 7)))} style={{ width: 60, padding: '7px 10px', borderRadius: 8, background: 'var(--surface2)', border: '1px solid var(--border)', color: 'var(--text)', fontSize: 14, fontWeight: 700, fontFamily: 'var(--mono)', textAlign: 'center', outline: 'none', }} /> gün
{/* Recent Analyses Table */}
{/* Panel header */}
Son Analizler {recentAll.length} kayıt
{/* Table */}
{['Tarih', 'Telefon', 'Temsilci', 'Duygu', 'Puan', 'Bayraklar', 'Özet'].map(h => ( ))} {statsLoading ? ( ) : pageItems.length === 0 ? ( ) : ( pageItems.map((item, idx) => { const sc = SENTIMENT_CFG[item.sentiment] || SENTIMENT_CFG.neutral; const sc2 = scoreColor(item.score); const isExpanded = expandedId === (item.call_id || item.call_uuid || idx); const rowId = item.call_id || item.call_uuid || idx; return ( handleRowClick(rowId)} style={{ borderBottom: isExpanded ? 'none' : '1px solid var(--border)', cursor: 'pointer', background: isExpanded ? 'rgba(59,109,240,.04)' : (idx % 2 === 0 ? 'transparent' : 'rgba(255,255,255,.01)'), transition: 'background .15s', }} onMouseEnter={e => { if (!isExpanded) e.currentTarget.style.background = 'rgba(255,255,255,.03)'; }} onMouseLeave={e => { if (!isExpanded) e.currentTarget.style.background = idx % 2 === 0 ? 'transparent' : 'rgba(255,255,255,.01)'; }} > {/* Tarih */} {/* Telefon */} {/* Temsilci */} {/* Duygu */} {/* Puan */} {/* Bayraklar */} {/* Özet */} {/* Expanded detail row */} {isExpanded && ( )} ); }) )}
{h}
Yükleniyor...
Henüz analiz yapılmamış
{fmtDate(item.analyzed_at)} {item.phone_number || '—'} {item.agent_name || '—'} {sc.emoji} {sc.label} {item.score != null ? ( {item.score} ) : } {item.flags && item.flags.length > 0 ? ( {item.flags.length} ) : ( )} {item.summary ? item.summary.substring(0, 80) + (item.summary.length > 80 ? '…' : '') : '—'} {isExpanded ? '' : ''}
{/* Pagination */} {totalPages > 1 && (
{page * PAGE_SIZE + 1}–{Math.min((page + 1) * PAGE_SIZE, recentAll.length)} / {recentAll.length}
{Array.from({ length: Math.min(totalPages, 5) }, (_, i) => { let p; if (totalPages <= 5) { p = i; } else if (page < 3) { p = i; } else if (page > totalPages - 4) { p = totalPages - 5 + i; } else { p = page - 2 + i; } return ( ); })}
)}
{/* RIGHT SIDEBAR */}
{/* Top Keywords */} {topKw.length > 0 && (
En Çok Geçen Kelimeler
{topKw.slice(0, 10).map((kw, i) => { const maxCount = topKw[0]?.count || 1; const pct = (kw.count / maxCount) * 100; return (
{kw.keyword} {kw.count}
); })}
)} {/* Keyword Management */}
Anahtar Kelimeler
{/* Add form */}
setNewKw(e.target.value)} onKeyDown={e => { if (e.key === 'Enter') handleAddKeyword(); }} style={{ fontSize: 13, padding: '8px 12px' }} />
setNewCol(e.target.value)} style={{ width: 32, height: 32, padding: 2, borderRadius: 6, border: '1px solid var(--border)', cursor: 'pointer', background: 'var(--surface2)' }} title="Renk seç" />
{/* Keyword list */}
{kwLoading ? (
Yükleniyor...
) : keywords.length === 0 ? (
Henüz kelime eklenmemiş
) : ( keywords.map(kw => ( )) )}
{keywords.length > 0 && (
{keywords.length} kelime aktif
)}
{/* Spinner keyframe — injected once */}
); } window.SpeechAnalyticsTab = SpeechAnalyticsTab; })();