const { useState, useEffect } = React;
/*
DIALPLAN MODAL — Adım adım wizard tarzı
*/
function DialplanModal({ dp, onClose, onSave }) {
const [form, setForm] = useState(dp || {
name: '', description: '', is_active: true,
gw_name: 'netgsm', gw_username: '', gw_password: '', gw_proxy: 'sip.netgsm.com.tr',
gw_register: true, gw_caller_id_in_from: true, gw_ping: 25,
ext_name: 'ai_incoming', dest_number: '', dest_regex_prefix: '^(90)?',
recording_enabled: true, recording_stereo: false,
recording_path: '/usr/local/freeswitch/recordings/${strftime(%Y-%m-%d-%H-%M-%S)}_${caller_id_number}.wav',
mode: 'qc_script', agent_id: null, qc_script_id: null, survey_id: null,
ivr_id: null, // EKLENDİ
policy_years: null,
ws_host: '127.0.0.1', ws_port: 5000, silence_ms: 500,
xml_filename: '00_ai_agent.xml', fs_dialplan_dir: '/etc/freeswitch/dialplan/public'
});
const [agents, setAgents] = useState([]);
const [qcScripts, setQcScripts] = useState([]);
const [surveys, setSurveys] = useState([]);
const [ivrs, setIvrs] = useState([]); // BU SATIR EKSİKTİ, EKLENDİ
const [saving, setSaving] = useState(false);
const [step, setStep] = useState(0);
const [xmlPreview, setXmlPreview] = useState('');
const set = (k, v) => setForm(f => ({ ...f, [k]: v }));
useEffect(() => {
authFetch('/api/dialplan/options/agents').then(r => r.json()).then(setAgents).catch(() => {});
authFetch('/api/dialplan/options/qc-scripts').then(r => r.json()).then(setQcScripts).catch(() => {});
authFetch('/api/dialplan/options/surveys').then(r => r.json()).then(setSurveys).catch(() => {});
authFetch('/api/dialplan/options/ivrs').then(r => r.json()).then(setIvrs).catch(() => {}); // EKLENDİ
}, []);
async function save() {
setSaving(true);
try {
const url = dp ? `/api/dialplan/${dp.id}` : '/api/dialplan';
const method = dp ? 'PUT' : 'POST';
const r = await authFetch(url, { method, headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(form) });
if (r.ok) onSave();
} finally { setSaving(false); }
}
async function loadPreview() {
if (dp) {
const r = await authFetch(`/api/dialplan/${dp.id}/preview`);
const d = await r.json();
setXmlPreview(d.xml);
} else {
setXmlPreview('Önce kaydedin, sonra XML önizleme yapabilirsiniz.');
}
}
const steps = [
{ key: 'general', label: 'Genel', icon: '' },
{ key: 'gateway', label: 'Gateway', icon: '' },
{ key: 'mode', label: 'Çalışma Modu', icon: '' },
{ key: 'advanced', label: 'Gelişmiş', icon: '' },
];
const canNext = () => {
if (step === 0) return form.name && form.dest_number;
return true;
};
const Toggle = ({ checked, onChange, label }) => (
);
function renderStep() {
switch (steps[step].key) {
case 'general':
return (
);
case 'gateway':
return (
);
case 'mode':
const modes = [
{ id: 'agent', icon: '', title: 'AI Asistan', desc: 'Serbest konuşma — GPT cevap verir' },
{ id: 'qc_script', icon: '', title: 'Kalite Kontrol', desc: 'Script okunur, cevap beklenir' },
{ id: 'survey', icon: '', title: 'Anket', desc: 'Soru-cevap anketi yapılır' },
{ id: 'ivr', icon: '', title: 'IVR Menü', desc: 'Tuşlamalı yönlendirme sistemi' }, // EKLENDİ
];
return (
{modes.map(m => (
set('mode', m.id)} style={{
padding: '20px 10px', borderRadius: 14, cursor: 'pointer', textAlign: 'center', transition: 'all .2s',
border: form.mode === m.id ? '2px solid var(--blue)' : '2px solid var(--border)',
background: form.mode === m.id ? 'rgba(59,130,246,0.06)' : 'transparent',
}}>
{m.icon}
{m.title}
))}
{form.mode === 'agent' && (
)}
{form.mode === 'qc_script' && (
)}
{form.mode === 'survey' && (
)}
{/* IVR SEÇİMİ EKLENDİ */}
{form.mode === 'ivr' && (
)}
);
case 'advanced':
return (
WebSocket Bağlantısı
FreeSWITCH Dosya
{form.recording_enabled && (
set('recording_path', e.target.value)} style={{ fontSize: 12 }} />
)}
{dp && (
{xmlPreview && (
{xmlPreview}
)}
)}
);
}
}
return (
e.stopPropagation()} style={{ maxWidth: 680, maxHeight: '90vh', display: 'flex', flexDirection: 'column' }}>
{dp ? 'Dialplan Düzenle' : 'Yeni Dialplan'}
{steps.map((s, i) => (
))}
{renderStep()}
{step < steps.length - 1 ? (
) : (
)}
);
}
function SetupTab({ toast }) {
const [dialplans, setDialplans] = useState([]);
const [editing, setEditing] = useState(null);
const [deploying, setDeploying] = useState(null);
const load = () => authFetch('/api/dialplan').then(r => r.json()).then(setDialplans).catch(() => setDialplans([]));
useEffect(() => { load(); }, []);
async function handleDelete(id) {
if (!confirm('Bu dialplanı silmek istediğinize emin misiniz?')) return;
await authFetch(`/api/dialplan/${id}`, { method: 'DELETE' });
toast('Dialplan silindi', 'success');
load();
}
async function handleDeploy(id) {
setDeploying(id);
try {
const r = await authFetch(`/api/dialplan/${id}/deploy`, { method: 'POST' });
const d = await r.json();
toast(d.reload ? 'FreeSWITCH güncellendi' : 'XML yazıldı, reload başarısız', d.reload ? 'success' : 'warning');
} finally { setDeploying(null); }
}
const modeConfig = {
agent: { label: 'AI Asistan', color: 'var(--cyan)', icon: '' },
qc_script: { label: 'Kalite Kontrol', color: 'var(--blue)', icon: '' },
survey: { label: 'Anket', color: 'var(--green)', icon: '' },
ivr: { label: 'IVR Menü', color: 'var(--purple)', icon: '' }, // EKLENDİ
};
return (
Dialplan Yönetimi
FreeSWITCH yapılandırmalarını yönetin. Değişiklikler otomatik deploy edilir.
{dialplans.length === 0 ? (
Henüz dialplan eklenmemiş
) : (
{dialplans.map(dp => {
const mc = modeConfig[dp.mode] || modeConfig.agent;
return (
{mc.icon} {mc.label}
{dp.dest_number}
{dp.xml_filename}
);
})}
)}
{editing !== null && (
setEditing(null)} onSave={() => { setEditing(null); load(); toast('Dialplan kaydedildi', 'success'); }} />
)}
);
}
window.SetupTab = SetupTab;