// // AlarmsTab.jsx — Alarm Geçmişi + Otomatik Transfer Ayarları // const { useState, useEffect, useCallback } = React; const ALARM_META_TAB = { 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' }, }; // Küçük Toggle bileşeni function Toggle({ value, onChange }) { return ( ); } // // AlarmsTab // function AlarmsTab({ toast }) { // Alarm listesi const [alarms, setAlarms] = useState([]); const [total, setTotal] = useState(0); const [loading, setLoading] = useState(false); const [filter, setFilter] = useState({ alarm_type: '', offset: 0, limit: 20 }); // İstatistikler const [stats, setStats] = useState({ total: 0, active: 0, resolved: 0, by_type: {} }); // Otomatik transfer konfigürasyonu const [config, setConfig] = useState({ enabled: false, threshold: 3, destination: '9999' }); const [configDirty, setDirty] = useState(false); const [configSaving, setSaving] = useState(false); // Yükleme fonksiyonları const loadAlarms = useCallback(async () => { setLoading(true); try { const p = new URLSearchParams({ limit: filter.limit, offset: filter.offset, ...(filter.alarm_type && { alarm_type: filter.alarm_type }), }); const res = await authFetch(`/api/alarms/history?${p}`); const data = await res.json(); setAlarms(data.items || []); setTotal(data.total || 0); } catch (_) { toast?.('Alarmlar yüklenemedi', 'error'); } finally { setLoading(false); } }, [filter]); const loadStats = useCallback(async () => { try { const res = await authFetch('/api/alarms/stats'); const data = await res.json(); setStats(data); } catch (_) {} }, []); const loadConfig = useCallback(async () => { try { const res = await authFetch('/api/alarms/config'); const data = await res.json(); setConfig(data); setDirty(false); } catch (_) {} }, []); useEffect(() => { loadAlarms(); }, [filter]); useEffect(() => { loadStats(); loadConfig(); }, []); // Alarm çözümleme const resolveAlarm = async (id) => { try { await authFetch(`/api/alarms/${id}/resolve?resolved_by=Admin`, { method: 'POST' }); setAlarms(prev => prev.map(a => a.id === id ? { ...a, is_active: false } : a)); setStats(prev => ({ ...prev, active: Math.max(0, prev.active - 1), resolved: prev.resolved + 1, })); toast?.('Alarm çözümlendi ', 'success'); } catch (_) { toast?.('Alarm çözümlenemedi', 'error'); } }; // Konfigürasyon kaydetme const saveConfig = async () => { setSaving(true); try { const p = new URLSearchParams({ enabled: config.enabled, threshold: config.threshold, destination: config.destination, }); await authFetch(`/api/alarms/config?${p}`, { method: 'POST' }); setDirty(false); toast?.('Ayarlar kaydedildi ', 'success'); } catch (_) { toast?.('Kaydetme başarısız', 'error'); } finally { setSaving(false); } }; const updateCfg = (patch) => { setConfig(c => ({ ...c, ...patch })); setDirty(true); }; // Sayfalama const totalPages = Math.max(1, Math.ceil(total / filter.limit)); const currentPage = Math.floor(filter.offset / filter.limit) + 1; // En sık alarm tipi const topType = Object.entries(stats.by_type || {}).sort((a, b) => b[1] - a[1])[0]; const topMeta = topType ? (ALARM_META_TAB[topType[0]] || { label: topType[0], icon: '' }) : null; // return (
{/* İstatistik Kartları */}
{[ { label: 'Toplam Alarm', value: stats.total, color: '#6366f1', icon: '' }, { label: 'Aktif', value: stats.active, color: '#ef4444', icon: '' }, { label: 'Çözümlendi', value: stats.resolved, color: '#22c55e', icon: '' }, { label: 'En Sık Tip', value: topMeta ? `${topMeta.icon} ${topMeta.label}` : '—', sub: topType ? `${topType[1]} kez` : '', color: topMeta ? (ALARM_META_TAB[topType[0]]?.color || '#f97316') : '#f97316', small: true, }, ].map(s => (
{s.label}
{s.value}
{s.sub && (
{s.sub}
)}
))}
{/* Otomatik Transfer Ayarları */}
Otomatik Transfer
Alarm eşiği aşılınca çağrıyı otomatik olarak canlı temsilciye bağlar
updateCfg({ enabled: v })} />
{/* Eşik */}
Alarm Eşiği
updateCfg({ threshold: parseInt(e.target.value) || 1 })} disabled={!config.enabled} style={{ width: 72, padding: '8px 12px', borderRadius: 8, border: '1px solid var(--border)', background: config.enabled ? 'var(--surface2)' : 'var(--surface)', color: 'var(--text)', fontSize: 16, fontWeight: 700, textAlign: 'center', opacity: config.enabled ? 1 : .5, }} /> alarm sonrası
{/* Hedef */}
Transfer Hedefi
updateCfg({ destination: e.target.value })} disabled={!config.enabled} style={{ width: 130, padding: '8px 12px', borderRadius: 8, border: '1px solid var(--border)', background: config.enabled ? 'var(--surface2)' : 'var(--surface)', color: 'var(--text)', fontSize: 14, opacity: config.enabled ? 1 : .5, }} />
{/* Kaydet butonu */} {configDirty && ( )}
{/* Durum mesajı */} {config.enabled ? (
Aktif: Bir çağrıda {config.threshold} alarm tetiklenince →{' '} {config.destination} numarasına transfer
) : (
Pasif: Otomatik transfer kapalı. Açmak için toggle'ı kullanın.
)}
{/* Alarm Geçmişi Tablosu */}
{/* Başlık + Filtreler */}
Alarm Geçmişi ({total} kayıt) {/* Tip filtresi */} {/* Yenile */}
{/* Tablo */}
{loading ? (
Yükleniyor...
) : alarms.length === 0 ? (
Alarm bulunamadı
) : ( {['Zaman', 'Çağrı', 'Alarm Tipi', 'Duygu', 'Tetikleyen Metin', 'Durum', ''].map(h => ( ))} {alarms.map(alarm => { const meta = ALARM_META_TAB[alarm.alarm_type] || { label: alarm.alarm_type, icon: '', color: '#ef4444' }; const emo = alarm.emotion || {}; const ts = fmtDateTime(alarm.triggered_at, { day: '2-digit', month: '2-digit', year: 'numeric', hour: '2-digit', minute: '2-digit', second: '2-digit' }); return ( e.currentTarget.style.background = 'var(--surface2)'} onMouseLeave={e => e.currentTarget.style.background = ''} > {/* Zaman */} {/* Çağrı UUID */} {/* Alarm Tipi */} {/* Duygu */} {/* Tetikleyen Metin */} {/* Durum */} {/* Aksiyon */} ); })}
{h}
{ts} #{alarm.call_uuid ? alarm.call_uuid.slice(0, 8) : '—'} {meta.icon} {meta.label} {emo.emoji || ''} {emo.tr || '—'} {emo.score != null && ( %{emo.score} )} {alarm.trigger_text ? ( "{alarm.trigger_text}" ) : ( )} {alarm.is_active ? ( Aktif ) : ( Çözümlendi )} {alarm.is_active && ( )}
)}
{/* Sayfalama */} {totalPages > 1 && (
{currentPage} / {totalPages}
)}
); } window.AlarmsTab = AlarmsTab;