//
// 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 */}
{/* 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 ? (
) : (
{['Zaman', 'Çağrı', 'Alarm Tipi', 'Duygu', 'Tetikleyen Metin', 'Durum', ''].map(h => (
|
{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 */}
|
{ts}
|
{/* Çağrı UUID */}
#{alarm.call_uuid ? alarm.call_uuid.slice(0, 8) : '—'}
|
{/* Alarm Tipi */}
{meta.icon} {meta.label}
|
{/* Duygu */}
{emo.emoji || ''} {emo.tr || '—'}
{emo.score != null && (
%{emo.score}
)}
|
{/* Tetikleyen Metin */}
{alarm.trigger_text ? (
"{alarm.trigger_text}"
) : (
—
)}
|
{/* Durum */}
{alarm.is_active ? (
Aktif
) : (
Çözümlendi
)}
|
{/* Aksiyon */}
{alarm.is_active && (
)}
|
);
})}
)}
{/* Sayfalama */}
{totalPages > 1 && (
{currentPage} / {totalPages}
)}
);
}
window.AlarmsTab = AlarmsTab;