const { useState, useEffect, useRef } = React; const { BarChart, Bar, XAxis, YAxis, Tooltip, ResponsiveContainer, Cell, LineChart, Line, CartesianGrid } = Recharts; /* QC SESSION DETAIL — Oturum detay sayfası */ function QCSessionDetail({ session, onBack, onStatusChange }) { const [data, setData] = useState(null); const audioRef = useRef(null); useEffect(() => { authFetch(`/api/qc/sessions/${session.id}`).then(r => r.json()).then(setData); }, [session.id]); if (!data) return (
Detaylar yükleniyor...
); const passedCount = data.criteria_scores.filter(c => c.passed).length; const totalCount = data.criteria_scores.length; const passRate = totalCount > 0 ? Math.round(passedCount / totalCount * 100) : 0; const scoreColor = passRate >= 80 ? 'var(--green)' : passRate >= 60 ? 'var(--amber)' : 'var(--red)'; return (
{/* HEADER */}
Oturum Detayı
{data.call_info.phone_number}
{passedCount}/{totalCount} başarılı
{/* SOL: Meta bilgiler */}
Müşteri {data.call_info.phone_number}
Asistan {data.call_info.agent_name}
Tarih {fmtDateTime(data.call_info.start_time, { day: '2-digit', month: '2-digit', year: 'numeric', hour: '2-digit', minute: '2-digit', second: '2-digit' })}
Durum {(() => { const sc = { completed: { label: 'Tamamlandı', color: 'var(--green)' }, failed: { label: 'Başarısız', color: 'var(--red)' }, abandoned: { label: 'Başarısız', color: 'var(--red)' }, in_progress: { label: 'Devam Ediyor', color: 'var(--amber)' } }; const cfg = sc[data.status] || sc.in_progress; return {cfg.label}; })()}
{data.call_info.recording_url && (
SES KAYDI
)} {/* Başarı çubuğu */}
BAŞARI ORANI %{passRate}
{/* SAĞ: Adım adım sonuçlar */}
Adım Sonuçları
{data.criteria_scores.map((c, i) => (
{c.passed ? '' : ''} {c.name}
{c.score}/{c.weight}
{c.reasoning && (
{c.reasoning}
)}
))}
{data.feedback && (
AI Geri Bildirimi
{data.feedback}
)}
); } /* QC SCRIPT MODAL — Senaryo düzenleme */ function QCScriptModal({ script, onClose, onSave }) { const empty = { name: '', description: '', is_active: false, welcome_message: '', retry_message: '', voice: 'en-US-NovaMultilingualNeural', enable_background: true, background_volume: 0.15, speech_rate: 1.0, steps: [], variables: {}, phone_mismatch_enabled: false, phone_mismatch_variable: 'crm_phone', phone_mismatch_message: 'Lütfen kayıt yaptırdığınız telefon ile aramanızı rica ederiz. İyi günler', phone_mismatch_announcement_id: null, end_webhook_service_id: null, end_webhook_only_on_success: true, }; const [form, setForm] = useState(script ? { ...empty, ...script } : empty); const [steps, setSteps] = useState(script?.steps ? [...script.steps] : []); const [vars, setVars] = useState(script?.variables ? Object.entries(script.variables).map(([k, v]) => ({ key: k, value: v })) : []); const [announcements, setAnnouncements] = useState([]); const [wsServices, setWsServices] = useState([]); const [saving, setSaving] = useState(false); useEffect(() => { authFetch('/api/announcements').then(r => r.json()).then(d => setAnnouncements(d.items || d || [])).catch(() => {}); authFetch('/api/ws/services').then(r => r.json()).then(d => setWsServices(d.items || d || [])).catch(() => {}); }, []); const set = (k, v) => setForm(f => ({ ...f, [k]: v })); const addStep = () => setSteps(s => [...s, { order: s.length + 1, title: '', text: '', detail_text: '', step_type: 'info', policy_years: null, max_retries: 2 }]); const updateS = (i, field, val) => setSteps(s => s.map((item, idx) => idx === i ? { ...item, [field]: val } : item)); const removeS = (i) => setSteps(s => s.filter((_, idx) => idx !== i)); const moveS = (i, dir) => { const arr = [...steps]; const j = i + dir; if (j < 0 || j >= arr.length) return; [arr[i], arr[j]] = [arr[j], arr[i]]; setSteps(arr); }; const handleSave = async () => { setSaving(true); try { const varObj = {}; vars.forEach(v => { if (v.key.trim()) varObj[v.key.trim()] = v.value; }); const payload = { ...form, steps: steps.map((s, i) => ({ ...s, order: i + 1 })), variables: varObj, }; const url = script ? `/api/qc/scripts/${script.id}` : '/api/qc/scripts'; const method = script ? 'PUT' : 'POST'; const r = await authFetch(url, { method, headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(payload) }); if (r.ok) onSave(); } finally { setSaving(false); } }; const stepTypeConfig = { info: { label: 'Bilgi', color: 'var(--blue)', desc: 'Cevap beklemez' }, yesno_end: { label: 'Onay', color: 'var(--red)', desc: 'Hayır → bitiş' }, detail_if_yes: { label: 'Detay (Evet)', color: 'var(--green)', desc: 'Evet → detay oku' }, detail_if_no: { label: 'Detay (Hayır)', color: 'var(--amber)', desc: 'Hayır → detay oku' }, qna_handoff: { label: 'Soru-Cevap', color: 'var(--purple)', desc: 'AI\'a devret' }, }; return (
e.stopPropagation()} style={{ maxWidth: 820, maxHeight: '90vh' }}>
{script ? 'Senaryo Düzenle' : 'Yeni Senaryo'}
{/* GENEL BİLGİLER */}
set('name', e.target.value)} placeholder="Örn: Kalite Kontrol Scripti" />
set('description', e.target.value)} />