diff --git a/admin/DashboardController.php b/admin/DashboardController.php index 061aa77..dc0fc4a 100644 --- a/admin/DashboardController.php +++ b/admin/DashboardController.php @@ -3747,7 +3747,7 @@ HTML; Requiere bot_type = hybrid y proveedor de IA configurado. -
+ @@ -3842,6 +3842,41 @@ function savePerType() { }); } +function saveAiConfig() { + const cid = document.querySelector('input[name="company_id"]').value; + const fd = new FormData(); + fd.set('company_id', cid); + const qs = sel => document.querySelector(sel); + fd.set('ai_provider', qs('select[name="ai_provider"]')?.value || ''); + fd.set('ai_model', qs('select[name="ai_model"]')?.value || 'gpt-4o-mini'); + fd.set('ai_temperature', qs('input[name="ai_temperature"]')?.value || '0.7'); + fd.set('ai_prompt', qs('textarea[name="ai_prompt"]')?.value || ''); + fd.set('gemini_model', qs('select[name="gemini_model"]')?.value || ''); + fd.set('claude_model', qs('select[name="claude_model"]')?.value || ''); + const oKey = qs('input[name="openai_api_key"]')?.value || ''; + const gKey = qs('input[name="gemini_api_key"]')?.value || ''; + const cKey = qs('input[name="claude_api_key"]')?.value || ''; + if (oKey) fd.set('openai_api_key', oKey); + if (gKey) fd.set('gemini_api_key', gKey); + if (cKey) fd.set('claude_api_key', cKey); + if (qs('input[name="ai_for_media"]')?.checked) fd.set('ai_for_media', '1'); + if (qs('input[name="nlu"]')?.checked) fd.set('nlu', '1'); + + const btn = document.getElementById('saveAiBtn'); + if (btn) { btn.textContent = 'Guardando...'; btn.disabled = true; } + fetch('/admin/bot-config/save-ai', { method: 'POST', body: fd }) + .then(r => r.json()) + .then(d => { + if (btn) { btn.textContent = 'πΎ Guardar IA'; btn.disabled = false; } + if (d.ok) { btn.textContent = 'β Guardado'; setTimeout(() => { btn.textContent = 'πΎ Guardar IA'; }, 2000); } + else alert('β Error al guardar: ' + JSON.stringify(d)); + }) + .catch(e => { + if (btn) { btn.textContent = 'πΎ Guardar IA'; btn.disabled = false; } + alert('β Error de red: ' + e.message); + }); +} + function switchTab(tab, btn) { document.querySelectorAll('.tab-content').forEach(t => t.classList.remove('active')); document.querySelectorAll('.tab-btn').forEach(t => t.classList.remove('active')); diff --git a/public/index.php b/public/index.php index b2e10d4..ca13f77 100644 --- a/public/index.php +++ b/public/index.php @@ -587,6 +587,53 @@ $routes = [ exit; })()], + // βββ Admin: guardar solo campos IA (endpoint separado) βββββββββββββββββββ + ['POST', '/admin/bot-config/save-ai', fn() => (function () { + SessionAuth::require(); + header('Content-Type: application/json; charset=utf-8'); + $companyId = (int)($_POST['company_id'] ?? 0); + if ($companyId === 0) { echo json_encode(['ok' => false, 'error' => 'missing company_id']); exit; } + + $company = CompanyRepository::findById($companyId); + if (!$company) { echo json_encode(['ok' => false, 'error' => 'not found']); exit; } + + $config = json_decode($company['config_json'] ?? '{}', true) ?: []; + + $aiProvider = trim($_POST['ai_provider'] ?? ''); + if ($aiProvider !== '') $config['ai_provider'] = $aiProvider; else unset($config['ai_provider']); + + $aiModel = trim($_POST['ai_model'] ?? ''); + if ($aiModel !== '') $config['ai_model'] = $aiModel; + + $aiTemp = trim($_POST['ai_temperature'] ?? ''); + if ($aiTemp !== '') $config['ai_temperature'] = (float)$aiTemp; + + $aiPrompt = trim($_POST['ai_prompt'] ?? ''); + $config['ai_prompt'] = $aiPrompt; + + $config['ai_for_media'] = isset($_POST['ai_for_media']); + $config['nlu'] = isset($_POST['nlu']); + + $openaiKey = trim($_POST['openai_api_key'] ?? ''); + if ($openaiKey !== '') $config['openai_api_key'] = $openaiKey; + + $geminiKey = trim($_POST['gemini_api_key'] ?? ''); + if ($geminiKey !== '') $config['gemini_api_key'] = $geminiKey; + + $geminiModel = trim($_POST['gemini_model'] ?? ''); + if ($geminiModel !== '') $config['gemini_model'] = $geminiModel; + + $claudeKey = trim($_POST['claude_api_key'] ?? ''); + if ($claudeKey !== '') $config['claude_api_key'] = $claudeKey; + + $claudeModel = trim($_POST['claude_model'] ?? ''); + if ($claudeModel !== '') $config['claude_model'] = $claudeModel; + + CompanyRepository::save(['id' => $companyId, 'config_json' => json_encode($config, JSON_UNESCAPED_UNICODE)]); + echo json_encode(['ok' => true]); + exit; + })()], + // βββ Admin: guardar per_type por categorΓa (endpoint separado) βββββββββββ ['POST', '/admin/bot-config/save-per-type', fn() => (function () { SessionAuth::require();