/* SettingsTab.jsx — Tenant Ayarları: API Keys + Kullanım & Limitler */ const { useState, useEffect, useCallback } = React; function SettingsTab() { const user = Auth.getUser(); const isAdmin = user && (user.role === 'admin' || user.role === 'super_admin'); /* ── State ── */ const [keys, setKeys] = useState(null); const [usage, setUsage] = useState(null); const [loading, setLoading] = useState(true); const [saving, setSaving] = useState(false); const [msg, setMsg] = useState(null); // { type, text } const [editKeys, setEditKeys] = useState({}); // Only changed fields const [visibleFields, setVisibleFields] = useState({}); /* ── Load ── */ const load = useCallback(async () => { setLoading(true); try { const [kRes, uRes] = await Promise.all([ authFetch(API + '/api/tenant/api-keys'), authFetch(API + '/api/tenant/usage'), ]); if (kRes.ok) setKeys(await kRes.json()); if (uRes.ok) setUsage(await uRes.json()); } catch (e) { console.error('Settings load error:', e); } setLoading(false); }, []); useEffect(() => { load(); }, []); /* ── Save API Keys ── */ const saveKeys = async () => { if (!Object.keys(editKeys).length) return; setSaving(true); setMsg(null); try { const r = await authFetch(API + '/api/tenant/api-keys', { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(editKeys), }); if (r.ok) { setKeys(await r.json()); setEditKeys({}); setMsg({ type: 'success', text: 'API anahtarları güncellendi.' }); } else { const err = await r.json().catch(() => ({})); setMsg({ type: 'error', text: err.detail || 'Kaydetme başarısız.' }); } } catch (e) { setMsg({ type: 'error', text: 'Bağlantı hatası.' }); } setSaving(false); }; /* ── Helpers ── */ const handleKeyChange = (field, value) => { setEditKeys(prev => ({ ...prev, [field]: value })); }; const toggleVisible = (field) => { setVisibleFields(prev => ({ ...prev, [field]: !prev[field] })); }; const usagePercent = (u) => { if (!u || !u.limit) return 0; return Math.min(100, Math.round((u.current / u.limit) * 100)); }; const usageBarClass = (pct) => { if (pct >= 90) return 'danger'; if (pct >= 70) return 'warn'; return ''; }; /* ── Loading ── */ if (loading) { return (

Ayarlar yükleniyor…

); } /* ── Render ── */ const keyField = (label, field, placeholder) => { const masked = keys ? keys[field] : ''; const editing = field in editKeys; const val = editing ? editKeys[field] : (masked || ''); const isSecret = field.includes('key'); return (
handleKeyChange(field, e.target.value)} readOnly={!isAdmin} /> {isSecret && ( )}
); }; const usageRow = (label, u) => { if (!u) return null; const pct = usagePercent(u); return (
{label} {u.current} / {u.limit}
); }; return (
{/* Message banner */} {msg && (
{msg.text}
)}
{/* ── API Keys ── */}
API Anahtarları
Azure Speech
{keyField('Speech Key', 'azure_speech_key', 'Azure Speech API Key')} {keyField('Speech Region', 'azure_speech_region', 'eastus')}
Azure OpenAI
{keyField('Endpoint', 'azure_openai_endpoint', 'https://xxx.openai.azure.com/')} {keyField('API Key', 'azure_openai_key', 'Azure OpenAI Key')} {keyField('Deployment', 'azure_openai_deploy', 'gpt-4o')} {keyField('Embedding Deployment', 'azure_openai_embed_deploy', 'text-embedding-3-small')} {keyField('API Version', 'azure_openai_api_version', '2025-01-01-preview')}
ElevenLabs / MiniMax TTS
{keyField('ElevenLabs Key', 'elevenlabs_key', 'ElevenLabs API Key')} {keyField('MiniMax Key', 'minimax_key', 'MiniMax API Key')} {keyField('MiniMax Group ID', 'minimax_group_id', 'MiniMax Group ID')}
{isAdmin && (
{Object.keys(editKeys).length > 0 && ( )}
)}
{/* ── Usage & Limits ── */}
Kullanım & Limitler
{usage && ( <>
{usage.plan?.toUpperCase() || 'STARTER'}
{usageRow('AI Agent', usage.agents)} {usageRow('İnsan Agent', usage.human_agents)} {usageRow('Kampanya', usage.campaigns)} {usageRow('Kuyruk', usage.queues)} )} {!usage && (

Kullanım bilgisi yüklenemedi.

)}
{/* ── Departman Yonetimi ── */} {isAdmin && } {/* ── Sonuc Kodlari Yonetimi ── */} {isAdmin && }
); } /* ═══════════════════════════════════════════════════════════════════════════ DepartmentManager — Departman Yönetimi ═══════════════════════════════════════════════════════════════════════════ */ function DepartmentManager() { const [depts, setDepts] = useState([]); const [loading, setLoading] = useState(true); const [showForm, setShowForm] = useState(false); const [editingId, setEditingId] = useState(null); const [form, setForm] = useState({ key: '', label: '', sort_order: 0 }); const [msg, setMsg] = useState(null); const loadDepts = async () => { try { const r = await authFetch(API + '/api/departments'); if (r.ok) setDepts(await r.json()); } catch (e) {} setLoading(false); }; useEffect(() => { loadDepts(); }, []); const seedDefaults = async () => { try { const r = await authFetch(API + '/api/departments/seed', { method: 'POST' }); const data = await r.json(); setMsg({ type: 'success', text: data.message || 'Varsayilan departmanlar yuklendi' }); loadDepts(); } catch (e) { setMsg({ type: 'error', text: 'Hata' }); } }; const handleSave = async () => { if (!form.key || !form.label) return; try { const url = editingId ? API + `/api/departments/${editingId}` : API + '/api/departments'; const method = editingId ? 'PUT' : 'POST'; const r = await authFetch(url, { method, headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(form), }); if (r.ok) { setMsg({ type: 'success', text: editingId ? 'Guncellendi' : 'Eklendi' }); setShowForm(false); setEditingId(null); setForm({ key: '', label: '', sort_order: 0 }); loadDepts(); } else { const err = await r.json().catch(() => ({})); setMsg({ type: 'error', text: err.detail || 'Hata' }); } } catch (e) { setMsg({ type: 'error', text: 'Baglanti hatasi' }); } }; const handleEdit = (d) => { setEditingId(d.id); setForm({ key: d.key, label: d.label, sort_order: d.sort_order || 0 }); setShowForm(true); }; const handleDelete = async (id) => { if (!confirm('Bu departmani silmek istiyor musunuz?')) return; try { const r = await authFetch(API + `/api/departments/${id}`, { method: 'DELETE' }); if (r.ok) { setMsg({ type: 'success', text: 'Silindi' }); loadDepts(); } else { const err = await r.json().catch(() => ({})); setMsg({ type: 'error', text: err.detail || 'Silinemedi' }); } } catch (e) {} }; const handleToggle = async (d) => { try { await authFetch(API + `/api/departments/${d.id}`, { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ is_active: !d.is_active }), }); loadDepts(); } catch (e) {} }; return (
Departmanlar
{depts.length === 0 && ( )}
{msg && (
{msg.text}
)} {showForm && (
{editingId ? 'Departman Duzenle' : 'Yeni Departman'}
setForm({ ...form, key: e.target.value })} placeholder="orn: support" style={{ fontSize: 12, marginTop: 4 }} />
setForm({ ...form, label: e.target.value })} placeholder="orn: Destek" style={{ fontSize: 12, marginTop: 4 }} />
setForm({ ...form, sort_order: parseInt(e.target.value) || 0 })} style={{ fontSize: 12, marginTop: 4 }} />
)} {loading ? (

Yukleniyor...

) : depts.length === 0 ? (

Henuz departman tanimlanmamis.

"Varsayilanlari Yukle" ile standart departmanlari ekleyebilir veya kendiniz tanimlayabilirsiniz.

) : (
{['Kod', 'Etiket', 'Sira', 'Durum', ''].map((h, i) => ( ))} {depts.map(d => ( ))}
{h}
{d.key} {d.label} {d.sort_order}
)}
); } /* ═══════════════════════════════════════════════════════════════════════════ DispositionManager — Sonuc Kodu Yonetim Paneli ═══════════════════════════════════════════════════════════════════════════ */ function DispositionManager() { const [codes, setCodes] = useState([]); const [loading, setLoading] = useState(true); const [showForm, setShowForm] = useState(false); const [editingId, setEditingId] = useState(null); const [form, setForm] = useState({ code: '', label: '', color: '#9ca3af', icon: '', category: 'general', sort_order: 0 }); const [msg, setMsg] = useState(null); const loadCodes = async () => { try { const r = await authFetch(API + '/api/dispositions?active_only=false'); if (r.ok) setCodes(await r.json()); } catch (e) {} setLoading(false); }; useEffect(() => { loadCodes(); }, []); const seedDefaults = async () => { try { const r = await authFetch(API + '/api/dispositions/seed', { method: 'POST' }); const data = await r.json(); setMsg({ type: 'success', text: data.message || 'Varsayilan kodlar yuklendi' }); loadCodes(); } catch (e) { setMsg({ type: 'error', text: 'Hata' }); } }; const handleSave = async () => { if (!form.code || !form.label) return; try { const url = editingId ? API + `/api/dispositions/${editingId}` : API + '/api/dispositions'; const method = editingId ? 'PUT' : 'POST'; const r = await authFetch(url, { method, headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(form), }); if (r.ok) { setMsg({ type: 'success', text: editingId ? 'Guncellendi' : 'Eklendi' }); setShowForm(false); setEditingId(null); setForm({ code: '', label: '', color: '#9ca3af', icon: '', category: 'general', sort_order: 0 }); loadCodes(); } else { const err = await r.json().catch(() => ({})); setMsg({ type: 'error', text: err.detail || 'Hata' }); } } catch (e) { setMsg({ type: 'error', text: 'Baglanti hatasi' }); } }; const handleEdit = (c) => { setEditingId(c.id); setForm({ code: c.code, label: c.label, color: c.color || '#9ca3af', icon: c.icon || '', category: c.category || 'general', sort_order: c.sort_order || 0 }); setShowForm(true); }; const handleDelete = async (id) => { if (!confirm('Bu sonuc kodunu silmek istiyor musunuz?')) return; try { const r = await authFetch(API + `/api/dispositions/${id}`, { method: 'DELETE' }); if (r.ok) { setMsg({ type: 'success', text: 'Silindi' }); loadCodes(); } else { const err = await r.json().catch(() => ({})); setMsg({ type: 'error', text: err.detail || 'Silinemedi' }); } } catch (e) {} }; const handleToggle = async (c) => { try { await authFetch(API + `/api/dispositions/${c.id}`, { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ is_active: !c.is_active }), }); loadCodes(); } catch (e) {} }; const categoryLabel = (cat) => { return { general: 'Genel', dialer: 'Dialer', inbound: 'Inbound' }[cat] || cat; }; const categoryColor = (cat) => { return { general: '#6366f1', dialer: '#f59e0b', inbound: '#3b82f6' }[cat] || '#9ca3af'; }; return (
Sonuc Kodlari
{codes.length === 0 && ( )}
{msg && (
{msg.text}
)} {/* Yeni/Duzenle formu */} {showForm && (
{editingId ? 'Sonuc Kodu Duzenle' : 'Yeni Sonuc Kodu'}
setForm({ ...form, code: e.target.value })} placeholder="orn: sale" style={{ fontSize: 12, marginTop: 4 }} disabled={editingId && codes.find(c =>c.id === editingId)?.is_system} />
setForm({ ...form, label: e.target.value })} placeholder="orn: Satis" style={{ fontSize: 12, marginTop: 4 }} />
setForm({ ...form, icon: e.target.value })} placeholder="orn: " style={{ fontSize: 12, marginTop: 4 }} />
setForm({ ...form, color: e.target.value })} style={{ width: '100%', height: 34, border: '1px solid var(--border)', borderRadius: 6, marginTop: 4, cursor: 'pointer' }} />
setForm({ ...form, sort_order: parseInt(e.target.value) || 0 })} style={{ fontSize: 12, marginTop: 4 }} />
)} {/* Kod listesi */} {loading ? (

Yukleniyor...

) : codes.length === 0 ? (

Henuz sonuc kodu tanimlanmamis.

"Varsayilanlari Yukle" butonuna tiklayarak standart kodlari ekleyebilirsiniz.

) : (
{['', 'Kod', 'Etiket', 'Kategori', 'Durum', ''].map((h, i) => ( ))} {codes.map(c => ( ))}
{h}
{c.icon || ''} {c.code} {c.label} {categoryLabel(c.category)} {!c.is_system && ( )}
)}
); } window.SettingsTab = SettingsTab;