// // LiveTranscriptPanel.jsx — Canlı Transkript + Alarm Bileşenleri // // Dışa aktarılan bileşenler: // window.AlarmBanner — Aktif alarmları üst panelde gösterir // window.LiveTranscriptPanel — Bir çağrının anlık transkriptini listeler // const { useState, useEffect, useRef, useCallback } = React; // Alarm tipi Türkçe etiket ve ikon eşlemesi const ALARM_META = { angry_detected: { label: 'Sinirli Müşteri', icon: '', color: '#ef4444' }, score_threshold: { label: 'Yüksek Olumsuzluk', icon: '', color: '#f97316' }, sustained_negative: { label: 'Sürekli Olumsuz', icon: '', color: '#f59e0b' }, rapid_drop: { label: 'Ani Duygu Düşüşü', icon: '', color: '#ef4444' }, }; // Duygu renk ve stil eşlemesi const EMOTION_STYLE = { positive: { bg: 'rgba(34,197,94,.15)', border: 'rgba(34,197,94,.4)', color: '#22c55e' }, neutral: { bg: 'rgba(139,148,158,.1)', border: 'var(--border)', color: 'var(--text-dim)' }, negative: { bg: 'rgba(245,158,11,.15)', border: 'rgba(245,158,11,.4)', color: '#f59e0b' }, angry: { bg: 'rgba(239,68,68,.15)', border: 'rgba(239,68,68,.4)', color: '#ef4444' }, }; // Web Audio ile alarm sesi (harici dosya gerektirmez) function playAlarmSound() { try { const ctx = new (window.AudioContext || window.webkitAudioContext)(); [0, 0.18, 0.36].forEach(offset => { const osc = ctx.createOscillator(); const gain = ctx.createGain(); osc.connect(gain); gain.connect(ctx.destination); osc.frequency.value = 880; osc.type = 'square'; gain.gain.setValueAtTime(0.12, ctx.currentTime + offset); gain.gain.exponentialRampToValueAtTime(0.001, ctx.currentTime + offset + 0.15); osc.start(ctx.currentTime + offset); osc.stop(ctx.currentTime + offset + 0.15); }); } catch (_) {} } // // AlarmBanner — Çözümlenmemiş alarmları üstte kırmızı/turuncu banner olarak gösterir // function AlarmBanner({ alarms, onResolve }) { if (!alarms || alarms.length === 0) return null; return (
{alarms.map(alarm => { const meta = ALARM_META[alarm.alarm_type] || { label: alarm.alarm_type, icon: '', color: '#ef4444' }; const emotion = alarm.emotion || {}; const preview = alarm.trigger_text ? `"${alarm.trigger_text.slice(0, 70)}${alarm.trigger_text.length > 70 ? '…' : ''}"` : ''; const callShort = alarm.call_uuid ? alarm.call_uuid.slice(0, 8) : '—'; return (
{/* Sol: ikon + açıklama */}
{meta.icon}
{meta.label} {emotion.emoji} {emotion.tr} {emotion.score != null ? `%${emotion.score}` : ''} #{callShort} {alarm.call_type === 'human' && ( Canlı Temsilci )}
{preview && (
{preview}
)}
{/* Sağ: zaman + kapat butonu */}
{alarm.triggered_at ? fmtTimeSec(alarm.triggered_at) : ''}
); })}
); } // // LiveTranscriptPanel — Tek bir çağrının anlık transkriptini gösterir // function LiveTranscriptPanel({ callUuid, entries = [], callType = 'ai' }) { const bottomRef = useRef(null); // Yeni mesaj gelince otomatik aşağı kaydır useEffect(() => { if (bottomRef.current) { bottomRef.current.scrollIntoView({ behavior: 'smooth' }); } }, [entries.length]); if (entries.length === 0) { return (
Transkript bekleniyor…
); } return (
{entries.map((entry, i) => { const isCustomer = entry.speaker === 'customer'; const isBot = entry.speaker === 'bot'; const isAgent = entry.speaker === 'agent'; const emotion = entry.emotion; const emoStyle = emotion ? (EMOTION_STYLE[emotion.label] || EMOTION_STYLE.neutral) : null; return (
{/* Konuşmacı etiketi */}
{isCustomer ? ' Müşteri' : isBot ? ' Asistan' : ' Temsilci'}
{/* Balon */}
{entry.text}
{/* Duygu badge + zaman */}
{isCustomer && emotion && emotion.label !== 'neutral' && ( {emotion.emoji} {emotion.tr} %{emotion.score} )} {entry.timestamp && ( {fmtTimeSec(entry.timestamp)} )}
); })}
); } // Dışa aktar window.AlarmBanner = AlarmBanner; window.LiveTranscriptPanel = LiveTranscriptPanel; window.playAlarmSound = playAlarmSound;