const { useState, useEffect } = React;
function SurveyModal({ survey, onClose, onSave }) {
const empty = {
name: '', description: '', mode: 'survey_only',
welcome_message: 'Merhaba, size birkaç kısa soru sormak istiyorum.',
completion_message: 'Teşekkür ederiz, iyi günler!',
abandon_message: 'Görüşmek üzere, iyi günler.',
escalation_threshold: '', escalation_dest: '9999',
questions: []
};
const [form, setForm] = useState(survey ? { ...survey, escalation_threshold: survey.escalation_threshold ?? '', questions: [] } : empty);
const [questions, setQuestions] = useState([]);
const [loading, setLoading] = useState(!!survey);
useEffect(() => {
if (survey) {
authFetch(`/api/surveys/${survey.id}`).then(r => r.json()).then(d => {
setForm({ ...d, escalation_threshold: d.escalation_threshold ?? '' });
setQuestions(d.questions.map(q => ({ ...q, branching_rules: JSON.stringify(q.branching_rules || []), options: (q.options || []).join(', ') })));
setLoading(false);
});
}
}, []);
const addQuestion = () => setQuestions(qs => [...qs, {
order: qs.length + 1, text: '', question_type: 'open',
options: '', scale_min: 1, scale_max: 10,
retry_text: 'Üzgünüm, anlayamadım. Lütfen tekrar söyleyin.',
max_retries: 2, branching_rules: '[]'
}]);
const updateQ = (i, field, val) => setQuestions(qs => qs.map((q, idx) => idx === i ? { ...q, [field]: val } : q));
const removeQ = (i) => setQuestions(qs => qs.filter((_, idx) => idx !== i).map((q, idx) => ({ ...q, order: idx + 1 })));
const handleSave = async () => {
const payload = {
...form,
escalation_threshold: form.escalation_threshold !== '' ? parseFloat(form.escalation_threshold) : null,
questions: questions.map((q, i) => ({
...q,
order: i + 1,
options: q.options ? q.options.split(',').map(s => s.trim()).filter(Boolean) : [],
branching_rules: (() => { try { return JSON.parse(q.branching_rules || '[]'); } catch { return []; } })()
}))
};
const url = survey ? `/api/surveys/${survey.id}` : '/api/surveys';
const method = survey ? 'PUT' : 'POST';
const r = await authFetch(url, { method, headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(payload) });
if (r.ok) onSave();
else { const e = await r.json(); alert(JSON.stringify(e)); }
};
const qTypeLabel = { open: 'Açık Uçlu', yesno: 'Evet/Hayır', scale: 'Puanlama', choice: 'Çoktan Seçmeli' };
return (
e.stopPropagation()} style={{ width: '850px', maxWidth: '95vw', padding: 0, margin: 'auto' }}>
{survey ? 'Anketi Düzenle' : 'Yeni Anket Oluştur'}
{loading ?
Yükleniyor...
:
{/* Temel Bilgiler */}
{/* Sorular */}
Sorular ({questions.length})
{questions.map((q, i) => (
Soru {i + 1}
updateQ(i, 'max_retries', parseInt(e.target.value))} />
{q.question_type === 'scale' && <>
updateQ(i, 'scale_min', parseInt(e.target.value))} />
updateQ(i, 'scale_max', parseInt(e.target.value))} />
>}
{q.question_type === 'choice' &&
updateQ(i, 'options', e.target.value)} placeholder="Çok memnun, Memnun, Memnun değilim" />
}
updateQ(i, 'retry_text', e.target.value)} />
))}
}
);
}
function SurveyStatsModal({ survey, onClose }) {
const [stats, setStats] = useState(null);
const [responses, setResponses] = useState([]);
const [view, setView] = useState('stats');
useEffect(() => {
authFetch(`/api/surveys/${survey.id}/stats`).then(r => r.json()).then(setStats);
authFetch(`/api/surveys/${survey.id}/responses`).then(r => r.json()).then(setResponses);
}, []);
const npsColor = (s) => s >= 9 ? 'var(--green)' : s >= 7 ? 'var(--amber)' : 'var(--red)';
const sentIcon = { positive: '', neutral: '', negative: '' };
const statusBadge = { completed: '#10b981', abandoned: '#f59e0b', transferred: '#8b5cf6', in_progress: '#0ea5e9' };
return (
e.stopPropagation()} style={{ width: '900px', maxWidth: '95vw', padding: 0, margin: 'auto' }}>
{survey.name}
Anket Raporu
{/* Tab switcher */}
{['stats', 'responses'].map(v => (
))}
{!stats ?
Yükleniyor...
: view === 'stats' ? (
<>
{/* Özet kartlar */}
{[
{ label: 'Toplam', value: stats.total, color: 'var(--blue)' },
{ label: 'Tamamlanan', value: `${stats.completed} (${stats.completion_rate}%)`, color: 'var(--green)' },
{ label: 'Terk', value: stats.abandoned, color: 'var(--amber)' },
{ label: 'Transfer', value: stats.transferred, color: 'var(--purple)' },
].map(c => (
))}
{/* NPS */}
{stats.avg_nps !== null &&
NPS Analizi
= 0 ? 'var(--green)' : 'var(--red)' }}>{stats.nps_index}
NPS Skoru
{stats.avg_nps}
Ort. Puan
{stats.nps_breakdown.promoters}
Promoter (9-10)
{stats.nps_breakdown.detractors}
Detractor (0-6)
}
{/* Duygu analizi */}
{Object.keys(stats.sentiments).length > 0 &&
Duygu Analizi
{Object.entries(stats.sentiments).map(([s, c]) => (
{sentIcon[s] || ''}
{c}
{s}
))}
}
{/* Soru bazlı istatistikler */}
Soru Bazlı Analiz
{stats.question_stats.map(q => (
S{q.order}. {q.text}
{q.answer_count}
yanıt
{q.avg_score !== null &&
Ort: {q.avg_score}
}
{Object.keys(q.distribution).length > 0 &&
{Object.entries(q.distribution).map(([k, v]) => (
{k}: {v}
))}
}
))}
>
) : (
/* Yanıtlar listesi */
{responses.length === 0 ?
Henüz yanıt yok.
: responses.map(r => (
{r.phone}
{r.status}
{r.nps_score !== null && NPS: {r.nps_score}}
{r.sentiment && {sentIcon[r.sentiment]}}
{fmtDateTime(r.started_at, { day: '2-digit', month: '2-digit', year: 'numeric', hour: '2-digit', minute: '2-digit', second: '2-digit' })}
{r.answers.map((a, i) => (
{a.question_text}
{a.raw_text} {a.score !== null && {a.score} Puan}
))}
))}
)}
);
}
function SurveysTab({ toast }) {
const [surveys, setSurveys] = useState([]);
const [showModal, setShowModal] = useState(false);
const [editSurvey, setEditSurvey] = useState(null);
const [statsSurvey, setStatsSurvey] = useState(null);
const load = () => authFetch('/api/surveys').then(r => r.json()).then(setSurveys);
useEffect(() => { load(); }, []);
const deleteSurvey = async (id) => {
if (!confirm('Bu anketi silmek istediğinizden emin misiniz?')) return;
await authFetch(`/api/surveys/${id}`, { method: 'DELETE' });
toast('Anket silindi.', 'info');
load();
};
const modeBadge = { survey_only: 'Sadece Anket', survey_then_agent: 'Anket + Sohbet' };
const modeColor = { survey_only: 'var(--blue)', survey_then_agent: 'var(--purple)' };
return (
Anket Yönetimi
Müşteri memnuniyeti ve NPS anketleri oluşturun, düzenleyin, sonuçları takip edin.
{surveys.length === 0 ? (
Henüz anket yok
İlk anketinizi oluşturun ve FreeSWITCH dialplan'a ekleyin.
) : surveys.map(s => (
{s.name}
{modeBadge[s.mode]}
{!s.is_active && PASİF}
{s.description &&
{s.description}
}
{s.question_count} Soru
{s.response_count} Yanıt
{s.escalation_threshold && NPS ≤ {s.escalation_threshold} → Transfer}
{/* Dialplan snippet */}
{`?uuid=\${uuid}&agent_id=1&phone=\${caller_id_number}&survey_id=${s.id}`}
))}
{showModal &&
setShowModal(false)} onSave={() => { setShowModal(false); load(); toast('Anket kaydedildi!', 'success'); }} />}
{statsSurvey && setStatsSurvey(null)} />}
);
}
window.SurveysTab = SurveysTab;