// CustomersTab.jsx — Müşteri Kartları (Customer CRM) UI // Exported as window.CustomersTab (function () { const { useState, useEffect, useCallback, useRef } = React; /* ── Format helpers ─────────────────────────────────────────────────────── */ // Backend naive UTC döndüğü için parseUTC ile yerel saate çevirilir. function formatTR(iso) { return fmtDateTime(iso, { day: '2-digit', month: '2-digit', year: 'numeric', hour: '2-digit', minute: '2-digit', }); } function formatDuration(seconds) { if (!seconds && seconds !== 0) return '—'; const m = Math.floor(seconds / 60); const s = Math.floor(seconds % 60); if (m === 0) return `${s}sn`; return `${m}dk ${s}sn`; } function formatDate(iso) { return fmtDateOnly(iso); } /* ── Tag badges ─────────────────────────────────────────────────────────── */ const TAG_COLORS = [ { bg: '#1e3a5f', border: '#3b82f6', text: '#93c5fd' }, { bg: '#064e3b', border: '#10b981', text: '#6ee7b7' }, { bg: '#4a1942', border: '#a855f7', text: '#d8b4fe' }, { bg: '#451a03', border: '#f59e0b', text: '#fcd34d' }, { bg: '#3b0764', border: '#8b5cf6', text: '#c4b5fd' }, { bg: '#0f172a', border: '#64748b', text: '#94a3b8' }, ]; function tagColor(tag) { let h = 0; for (let i = 0; i < tag.length; i++) h = (h * 31 + tag.charCodeAt(i)) & 0xffffffff; return TAG_COLORS[Math.abs(h) % TAG_COLORS.length]; } function TagBadge({ tag }) { const c = tagColor(tag); return ( {tag} ); } function BlacklistBadge() { return ( Kara Liste ); } function StatusBadge({ status }) { const map = { completed: { bg: '#064e3b20', border: '#10b981', text: '#34d399', label: 'Tamamlandı' }, missed: { bg: '#7f1d1d20', border: '#ef4444', text: '#f87171', label: 'Cevapsız' }, failed: { bg: '#7f1d1d20', border: '#ef4444', text: '#f87171', label: 'Başarısız' }, answered: { bg: '#064e3b20', border: '#10b981', text: '#34d399', label: 'Yanıtlandı' }, busy: { bg: '#451a0320', border: '#f59e0b', text: '#fcd34d', label: 'Meşgul' }, voicemail: { bg: '#1e3a5f20', border: '#3b82f6', text: '#93c5fd', label: 'Sesli Mesaj' }, }; const s = map[status] || { bg: '#1f293720', border: '#6b7280', text: '#9ca3af', label: status || '—' }; return ( {s.label} ); } /* ── Stat Card ──────────────────────────────────────────────────────────── */ function StatCard({ label, value, color, icon }) { return (
{icon &&
{icon}
}
{value ?? '—'}
{label}
); } /* ── Confirm Delete Modal ───────────────────────────────────────────────── */ function ConfirmDeleteModal({ customer, onConfirm, onCancel }) { return (
e.stopPropagation()} style={{ maxWidth: 420, width: '90vw', padding: 0 }}>
Müşteriyi Sil

{customer.name || customer.phone}adlı müşteriyi kalıcı olarak silmek istediğinizden emin misiniz? Bu işlem geri alınamaz.

); } /* ── New Customer Modal ─────────────────────────────────────────────────── */ function NewCustomerModal({ onClose, onSaved, toast }) { const [form, setForm] = useState({ phone: '', name: '', company: '', email: '', tags: '' }); const [saving, setSaving] = useState(false); const set = (k, v) =>setForm(f => ({ ...f, [k]: v })); async function handleSave() { if (!form.phone.trim()) { toast('Hata', 'Telefon numarası zorunludur.', 'error'); return; } setSaving(true); try { const payload = { phone: form.phone.trim(), name: form.name.trim(), company: form.company.trim(), email: form.email.trim(), tags: form.tags ? form.tags.split(',').map(t =>t.trim()).filter(Boolean) : [], }; const r = await authFetch('/api/customers', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(payload), }); if (r.ok) { toast('Başarılı', 'Müşteri oluşturuldu.', 'success'); onSaved(); } else { const e = await r.json().catch(() => ({})); toast('Hata', e.error || e.message || 'Müşteri oluşturulamadı.', 'error'); } } finally { setSaving(false); } } return (
e.stopPropagation()} style={{ maxWidth: 500, width: '92vw', padding: 0 }}>
Yeni Müşteri Ekle
set('phone', e.target.value)} placeholder="+90 5XX XXX XX XX" autoFocus />
set('name', e.target.value)} placeholder="Müşteri adı" />
set('company', e.target.value)} placeholder="Şirket adı" />
set('email', e.target.value)} placeholder="ornek@sirket.com" />
set('tags', e.target.value)} placeholder="vip, kurumsal, aktif" />
); } /* ── Customer Detail/Edit Modal ─────────────────────────────────────────── */ function CustomerDetailModal({ customerId, initialTab, onClose, onSaved, toast }) { const [activeTab, setActiveTab] = useState(initialTab || 'info'); const [data, setData] = useState(null); const [loading, setLoading] = useState(true); const [calls, setCalls] = useState([]); const [callsLoading, setCallsLoading] = useState(false); const [saving, setSaving] = useState(false); const [noteText, setNoteText] = useState(''); const [addingNote, setAddingNote] = useState(false); // Edit form state const [form, setForm] = useState(null); const setField = (k, v) =>setForm(f => ({ ...f, [k]: v })); useEffect(() => { loadCustomer(); }, [customerId]); useEffect(() => { if (activeTab === 'calls' && calls.length === 0 && data) { loadCalls(); } }, [activeTab, data]); async function loadCustomer() { setLoading(true); try { const r = await authFetch(`/api/customers/lookup?phone=${encodeURIComponent('')}&id=${customerId}`); // fallback: try direct GET if lookup by id not supported const r2 = await authFetch(`/api/customers/${customerId}`); if (r2.ok) { const d = await r2.json(); setData(d); setForm({ name: d.name || '', company: d.company || '', email: d.email || '', tags: (d.tags || []).join(', '), notes: d.notes || '', is_blacklist: !!d.is_blacklist, }); } else if (r.ok) { const d = await r.json(); const c = d.customer || d; setData(c); setForm({ name: c.name || '', company: c.company || '', email: c.email || '', tags: (c.tags || []).join(', '), notes: c.notes || '', is_blacklist: !!c.is_blacklist, }); } } finally { setLoading(false); } } async function loadCalls() { setCallsLoading(true); try { const r = await authFetch(`/api/customers/${customerId}/calls`); if (r.ok) { const d = await r.json(); setCalls(Array.isArray(d) ? d : (d.items || d.calls || [])); } } finally { setCallsLoading(false); } } async function handleSave() { setSaving(true); try { const payload = { name: form.name.trim(), company: form.company.trim(), email: form.email.trim(), tags: form.tags ? form.tags.split(',').map(t =>t.trim()).filter(Boolean) : [], notes: form.notes, is_blacklist: form.is_blacklist, }; const r = await authFetch(`/api/customers/${customerId}`, { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(payload), }); if (r.ok) { toast('Başarılı', 'Müşteri bilgileri güncellendi.', 'success'); onSaved(); loadCustomer(); } else { const e = await r.json().catch(() => ({})); toast('Hata', e.error || e.message || 'Güncelleme başarısız.', 'error'); } } finally { setSaving(false); } } async function handleAddNote() { if (!noteText.trim()) return; setAddingNote(true); try { const r = await authFetch(`/api/customers/${customerId}/notes`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ note: noteText.trim() }), }); if (r.ok) { toast('Başarılı', 'Not eklendi.', 'success'); setNoteText(''); loadCustomer(); } else { const e = await r.json().catch(() => ({})); toast('Hata', e.error || e.message || 'Not eklenemedi.', 'error'); } } finally { setAddingNote(false); } } const TABS = [ { key: 'info', label: 'Bilgiler' }, { key: 'calls', label: 'Çağrı Geçmişi' }, { key: 'notes', label: 'Notlar' }, ]; const tabStyle = (key) => ({ padding: '9px 18px', fontSize: 13, fontWeight: 600, border: 'none', borderBottom: activeTab === key ? '2px solid var(--blue)' : '2px solid transparent', background: 'transparent', color: activeTab === key ? 'var(--blue)' : 'var(--text-dim)', cursor: 'pointer', transition: 'color .15s, border-color .15s', }); return (
e.stopPropagation()} style={{ maxWidth: 720, width: '94vw', padding: 0, margin: 'auto' }}> {/* Header */}
{loading || !data ? (
Yükleniyor...
) : (
{data.name || '(İsimsiz Müşteri)'} {data.is_blacklist && }
{data.phone}
)}
{/* Tab bar */}
{TABS.map(t => ( ))}
{/* Tab content */}
{loading ? (
Yükleniyor...
) : ( <> {/* ── INFO TAB ── */} {activeTab === 'info' && form && (
setField('name', e.target.value)} placeholder="Müşteri adı" />
setField('company', e.target.value)} placeholder="Şirket adı" />
setField('email', e.target.value)} placeholder="ornek@sirket.com" />
setField('tags', e.target.value)} placeholder="vip, kurumsal, aktif" /> {form.tags && (
{form.tags.split(',').map(t =>t.trim()).filter(Boolean).map(t => ( ))}
)}
{form.is_blacklist && ( Bu müşteri kara listede — gelen/giden aramalar engellenir. )}
)} {/* ── CALLS TAB ── */} {activeTab === 'calls' && (
{callsLoading ? (
Çağrı geçmişi yükleniyor...
) : calls.length === 0 ? (
Henüz çağrı kaydı yok
) : (
{['Tarih', 'Süre', 'Durum', 'Asistan', 'Disposisyon', 'Özet'].map(h => ( ))} {calls.map((call, i) => ( e.currentTarget.style.background = 'var(--surface2)'} onMouseLeave={e =>e.currentTarget.style.background = ''} > ))}
{h}
{formatTR(call.start_time)} {formatDuration(call.duration)} {call.agent_name || '—'} {call.disposition_code ? ( {call.disposition_code} ) : '—'} {call.summary ? ( {call.summary} ) : '—'}
)}
)} {/* ── NOTES TAB ── */} {activeTab === 'notes' && (
{/* Existing notes */}
Mevcut Notlar
{data.notes ? (
{data.notes}
) : (
Henüz not eklenmemiş.
)}
{/* Add new note */}
Yeni Not Ekle