fix: dedicated save button for categories + preserve all extra DB keys

- Replace onchange+AJAX with explicit button '💾 Guardar categorías'
  that shows alert on success/error for clear feedback
- Get company_id from DOM (not COMPANY_ID JS const from another script block)
- Main form save now preserves welcome_menu and per_type from DB even if
  POST is truncated by max_input_vars
- Restore welcome_menu='welcome_msg' in DB (was wiped by a previous save)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Lizandro Guarnizo
2026-06-30 19:16:36 -05:00
co-authored by Claude Sonnet 4.6
parent ae5b7266d4
commit 601169525e
2 changed files with 32 additions and 19 deletions
+17 -15
View File
@@ -2917,7 +2917,7 @@ HTML;
<div style="font-weight:700;font-size:13px;color:#0b3d91;margin-bottom:12px">{$label}</div>
<div class="form-group" style="margin-bottom:0">
<label>Menú de bienvenida (greeting menu)</label>
<select name="per_type_menu[{$cat}]" onchange="savePerType()" style="width:100%;padding:9px 12px;border:1px solid #e2e5ea;border-radius:8px;font-size:13px">
<select name="per_type_menu[{$cat}]" style="width:100%;padding:9px 12px;border:1px solid #e2e5ea;border-radius:8px;font-size:13px">
{$opts}
</select>
<div style="font-size:11px;color:#7a8291;margin-top:4px">Menú que se muestra cuando el usuario escribe por primera vez o sin contexto activo</div>
@@ -3347,6 +3347,7 @@ HTML;
<div class="card-h">👥 Configuración por Categoría</div>
<p style="font-size:12px;color:#7a8291;margin-bottom:16px">Define qué menú de bienvenida ve cada categoría de usuario cuando escribe por primera vez o sin contexto activo.</p>
{$categoryTabHtml}
<button type="button" id="savePerTypeBtn" onclick="savePerType()" class="btn-primary" style="margin-top:8px">💾 Guardar categorías</button>
</div>
<!-- ─── AI ───────────────────────────────────────────────────────────── -->
@@ -3492,23 +3493,24 @@ const CONFIG = {$botConfigJson};
const ENDPOINTS = {$botEndpointsJson};
function savePerType() {
const fd = new FormData();
fd.set('company_id', COMPANY_ID);
[1, 2, 3].forEach(cat => {
const sel = document.querySelector('select[name="per_type_menu[' + cat + ']"]');
const cid = document.querySelector('input[name="company_id"]').value;
const fd = new FormData();
fd.set('company_id', cid);
[1, 2, 3].forEach(function(cat) {
var sel = document.querySelector('[name="per_type_menu[' + cat + ']"]');
if (sel) fd.append('per_type_menu[' + cat + ']', sel.value);
});
var btn = document.getElementById('savePerTypeBtn');
if (btn) { btn.textContent = 'Guardando...'; btn.disabled = true; }
fetch('/admin/bot-config/save-per-type', { method: 'POST', body: fd })
.then(r => r.json())
.then(d => {
if (d.ok) {
const t = document.createElement('div');
t.className = 'toast toast-success';
t.textContent = '✅ Categorías guardadas';
t.style.cssText = 'position:fixed;bottom:24px;right:24px;z-index:9999;padding:10px 18px;border-radius:8px;background:#166534;color:#fff;font-size:13px;font-weight:600';
document.body.appendChild(t);
setTimeout(() => t.remove(), 2500);
}
.then(function(r) { return r.json(); })
.then(function(d) {
if (btn) { btn.textContent = '💾 Guardar categorías'; btn.disabled = false; }
alert(d.ok ? '✅ Categorías guardadas correctamente.' : '❌ Error al guardar: ' + JSON.stringify(d));
})
.catch(function(e) {
if (btn) { btn.textContent = '💾 Guardar categorías'; btn.disabled = false; }
alert('❌ Error de red: ' + e.message);
});
}
+15 -4
View File
@@ -557,10 +557,21 @@ $routes = [
$approvalWebhook = trim($_POST['approval_webhook'] ?? '');
if ($approvalWebhook !== '') $config['approval_webhook'] = $approvalWebhook;
$welcomeMenu = trim($_POST['welcome_menu'] ?? '');
if ($welcomeMenu !== '') {
$config['welcome_menu'] = $welcomeMenu;
} elseif (!empty($existingConfig['welcome_menu'])) {
$config['welcome_menu'] = $existingConfig['welcome_menu'];
if ($welcomeMenu !== '') $config['welcome_menu'] = $welcomeMenu;
// Preserve any DB keys the form doesn't explicitly manage (welcome_menu, per_type, etc.)
$formManagedKeys = ['commands', 'menus', 'flows', 'ai_prompt', 'ai_model', 'ai_temperature',
'ai_provider', 'openai_api_key', 'gemini_api_key', 'gemini_model',
'ai_for_media', 'greeting', 'fallback', 'approval_webhook',
'ignore_prefixes', 'welcome_menu', 'per_type'];
foreach ($existingConfig as $k => $v) {
if (!in_array($k, $formManagedKeys, true) && !isset($config[$k])) {
$config[$k] = $v;
}
// Also preserve managed keys that POST didn't provide (e.g. truncated by max_input_vars)
if (in_array($k, ['welcome_menu', 'per_type'], true) && !isset($config[$k])) {
$config[$k] = $v;
}
}
CompanyRepository::save(['id' => $companyId, 'config_json' => json_encode($config, JSON_UNESCAPED_UNICODE)]);