const { useState, useEffect, useMemo } = React; const { ResponsiveContainer, AreaChart, Area, BarChart, Bar, XAxis, YAxis, Tooltip, CartesianGrid, Cell } = Recharts; /* ═══════════════════════════════════════════════════════════════ STATS TAB — Tam Yeniden Tasarım ═══════════════════════════════════════════════════════════════ */ /* ── Mini KPI Kartı ── */ function KpiCard({ icon, label, value, sub, color, trend }) { const trendUp = trend > 0; const trendColor = trendUp ? '#22c55e' : trend < 0 ? '#ef4444' : 'var(--text-dim)'; return (
{icon}
{label}
{value}
{trend !== undefined && trend !== null && (
{trendUp ? '↑' : trend < 0 ? '↓' : '→'} {Math.abs(trend)}%
)} {sub && !trend && trend !== 0 &&
{sub}
}
); } /* ── Saatlik Isı Haritası ── */ function HourlyHeatmap({ data }) { const maxCount = Math.max(...data.map(d =>d.count), 1); return (
{data.map(d => { const intensity = d.count / maxCount; const bg = d.count === 0 ? 'var(--surface2)' : `rgba(59, 109, 240, ${0.12 + intensity * 0.78})`; const textColor = intensity > 0.5 ? '#fff' : 'var(--text-dim)'; return (
{String(d.hour).padStart(2, '0')}
{d.count}
); })}
); } /* ── Durum Çubuğu ── */ function StatusBar({ data, total }) { const statusConfig = { ended: { label: 'Tamamlanan', color: '#22c55e' }, transferred: { label: 'Transfer', color: '#06b6d4' }, active: { label: 'Aktif', color: '#f59e0b' }, abandoned: { label: 'Terk Edilen', color: '#ef4444' }, failed: { label: 'Başarısız', color: '#ef4444' }, }; if (!data || data.length === 0 || total === 0) return null; return (
{data.map(d => { const cfg = statusConfig[d.status] || { label: d.status, color: '#94a3b8' }; const pct = (d.count / total * 100); if (pct < 1) return null; return (
3 ? 'auto' : 4, display: 'flex', alignItems: 'center', justifyContent: 'center', fontSize: 11, fontWeight:600, color: '#fff', transition: 'width .5s ease', }}> {pct > 10 && `${Math.round(pct)}%`}
); })}
{data.map(d => { const cfg = statusConfig[d.status] || { label: d.status, color: '#94a3b8' }; return (
{cfg.label} {d.count}
); })}
); } /* ── Özel Tooltip ── */ function ChartTooltip({ active, payload, label, suffix }) { if (!active || !payload?.length) return null; return (
{label}
{payload.map((p, i) => (
{p.value} {suffix || 'çağrı'}
))}
); } /* ═══ ANA COMPONENT ═══ */ function StatsTab() { const { data: stats, loading } = window.useApi('/api/stats', []); const { data: qcSessionsData } = window.useApi('/api/qc/sessions?limit=100', []); const [period, setPeriod] = useState('14d'); const daily = useMemo(() => { if (!stats?.daily_calls) return []; const days = period === '7d' ? 7 : 14; return stats.daily_calls.slice(-days).map(d => ({ ...d, label: d.date.slice(5), })); }, [stats, period]); const qcStats = useMemo(() => { // API artık {total, items, ...} formatında döndürüyor const qcSessions = Array.isArray(qcSessionsData) ? qcSessionsData : (qcSessionsData?.items || []); if (!qcSessions || qcSessions.length === 0) return null; const total = qcSessions.length; const completed = qcSessions.filter(s => s.status === 'completed').length; const failed = qcSessions.filter(s => s.status === 'failed').length; const abandoned = qcSessions.filter(s => s.status === 'abandoned').length; return { total, completed, failed, abandoned, rate: total > 0 ? Math.round(completed / total * 100) : 0 }; }, [qcSessionsData]); if (loading || !stats) { return (
Raporlar yükleniyor...
); } const durBucketColors = ['#e2e8f0', '#bfdbfe', '#93c5fd', '#60a5fa', '#3b82f6', '#1d4ed8']; return (
{/* ═══ BAŞLIK ═══ */}

Raporlar & Analiz

Çağrı merkezi performansınızın detaylı görünümü

{[{ k: '7d', l: '7 Gün' }, { k: '14d', l: '14 Gün' }].map(p => ( ))}
{/* ═══ KPI KARTLARI ═══ */}
} label="Toplam Çağrı" value={stats.total_calls} color="#3b6df0" trend={stats.week_change} /> } label="Bugün" value={stats.today_calls} color="#06b6d4" sub={`Dün: ${stats.yesterday_calls}`} /> } label="Ort. Süre" value={window.fmtDuration(stats.avg_duration)} color="#f59e0b" sub={`Max: ${window.fmtDuration(stats.max_duration)}`} /> } label="Transfer Oranı" value={stats.total_calls > 0 ? `%${Math.round(stats.transferred_calls / stats.total_calls * 100)}` : '%0'} color="#8b5cf6" sub={`${stats.transferred_calls} çağrı`} /> {qcStats && ( } label="QC Başarı" value={`%${qcStats.rate}`} color="#22c55e" sub={`${qcStats.completed}/${qcStats.total} tamamlandı`} /> )}
{/* ═══ ANA GRAFİKLER ═══ */}
{/* Çağrı Trendi */}
Çağrı Trendi {stats.this_week_calls} bu hafta
{daily.length > 0 ? ( } /> ) : (
Veri yok
)}
{/* Durum Dağılımı + Süre */}
Çağrı Durumları
Süre Dağılımı
} /> {stats.duration_distribution.map((_, i) => ( ))}
{/* ═══ İKİNCİ SATIR ═══ */}
{/* Saatlik Dağılım */}
Saatlik Yoğunluk Son 7 gün
{stats.hourly_distribution ? ( ) : (
Veri yok
)}
{/* QC Sonuçları */}
Kalite Kontrol Özeti {qcStats && {qcStats.total} değerlendirme}
{qcStats ? (
{[ { label: 'Başarılı', value: qcStats.completed, color: '#22c55e' }, { label: 'Başarısız', value: qcStats.failed, color: '#ef4444' }, { label: 'Terk Edilen', value: qcStats.abandoned, color: '#f59e0b' }, ].map(s => (
{s.value}
{s.label}
))}
{/* Başarı çubuğu */}
Başarı oranı: %{qcStats.rate}
) : (
Henüz QC verisi yok
)}
{/* ═══ AGENT PERFORMANSI ═══ */} {stats.agent_performance?.length > 0 && (
Asistan Performansı
{stats.agent_performance.map(a => { const maxTotal = stats.agent_performance[0]?.total || 1; const barPct = (a.total / maxTotal * 100); return ( ); })}
Asistan Toplam Çağrı Tamamlanan Transfer Ort. Süre Dağılım
{a.name} {a.total} {a.ended} {a.transferred} {window.fmtDuration(a.avg_duration)}
)} {/* ═══ DEPARTMAN DAĞILIMI ═══ */} {stats.department_distribution?.length > 0 && (
Departman Dağılımı
{stats.department_distribution.map((d, i) => { const colors = { support: '#3b6df0', sales: '#22c55e', appointment: '#8b5cf6' }; const labels = { support: 'Destek', sales: 'Satış', appointment: 'Randevu' }; const color = colors[d.dept] || '#94a3b8'; const pct = stats.total_calls > 0 ? Math.round(d.count / stats.total_calls * 100) : 0; return (
{labels[d.dept] || d.dept}
{d.count} %{pct}
); })}
)}
); } window.StatsTab = StatsTab;