From 8f51659f1ea9830e7b964e8fd309c854a30db40d Mon Sep 17 00:00:00 2001 From: Lizandro Guarnizo <77708265+lizandrogd@users.noreply.github.com> Date: Tue, 30 Jun 2026 19:06:13 -0500 Subject: [PATCH] fix: save per-category menus via dedicated endpoint, not the main form MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The main form exceeded max_input_vars (27 flows × 37 fields each) so PHP silently dropped per_type_menu before reaching the save handler. - Add POST /admin/bot-config/save-per-type — reads existing DB config, merges only the per_type_menu values, saves back - Per-type selects fire savePerType() onchange → AJAX → toast on success - Add public/.user.ini with max_input_vars=5000 as belt+suspenders - Remove array_is_list() (PHP 8.1) — replaced with PHP 7.4 compatible check Co-Authored-By: Claude Sonnet 4.6 --- admin/DashboardController.php | 23 ++++++++++++++++++++++- public/.user.ini | 1 + public/index.php | 34 ++++++++++++++++++++++++++++++++++ 3 files changed, 57 insertions(+), 1 deletion(-) create mode 100644 public/.user.ini diff --git a/admin/DashboardController.php b/admin/DashboardController.php index b7dfca2..efdf6b3 100644 --- a/admin/DashboardController.php +++ b/admin/DashboardController.php @@ -2917,7 +2917,7 @@ HTML;
{$label}
- {$opts}
Menú que se muestra cuando el usuario escribe por primera vez o sin contexto activo
@@ -3491,6 +3491,27 @@ HTML; 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 + ']"]'); + if (sel) fd.append('per_type_menu[' + cat + ']', sel.value); + }); + 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); + } + }); +} + 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/.user.ini b/public/.user.ini new file mode 100644 index 0000000..cc0cce8 --- /dev/null +++ b/public/.user.ini @@ -0,0 +1 @@ +max_input_vars = 5000 diff --git a/public/index.php b/public/index.php index dbb0087..813aa75 100644 --- a/public/index.php +++ b/public/index.php @@ -570,6 +570,40 @@ $routes = [ exit; })()], + // ─── Admin: guardar per_type por categoría (endpoint separado) ─────────── + ['POST', '/admin/bot-config/save-per-type', 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]); exit; } + + $company = CompanyRepository::findById($companyId); + if (!$company) { echo json_encode(['ok' => false]); exit; } + + $config = json_decode($company['config_json'] ?? '{}', true) ?: []; + + $rawPt = $config['per_type'] ?? []; + $perType = (is_array($rawPt) && count(array_filter(array_keys($rawPt), 'is_string')) > 0) + ? $rawPt : []; + + $perTypeMenus = $_POST['per_type_menu'] ?? []; + foreach ([1, 2, 3] as $cat) { + $mk = trim($perTypeMenus[$cat] ?? ''); + $key = (string)$cat; + if ($mk !== '') { + $perType[$key] = array_merge($perType[$key] ?? [], ['greeting_menu' => $mk]); + } else { + unset($perType[$key]['greeting_menu']); + if (empty($perType[$key])) unset($perType[$key]); + } + } + $config['per_type'] = empty($perType) ? new stdClass() : $perType; + + CompanyRepository::save(['id' => $companyId, 'config_json' => json_encode($config, JSON_UNESCAPED_UNICODE)]); + echo json_encode(['ok' => true]); + exit; + })()], + // ─── Admin: configuración general ────────────────────────────────────── ['GET', '/admin/settings', fn() => DashboardController::settings()],