From 01c30ca594f5e34aabb9cbaedbce9e6d470161e9 Mon Sep 17 00:00:00 2001 From: lizandrogd <77708265+lizandrogd@users.noreply.github.com> Date: Mon, 12 Jan 2026 22:42:41 -0500 Subject: [PATCH] menus --- api/delete_menu.php | 85 ++++++++++++ api/get_menus.php | 154 +++++++++++++++++++--- api/save_menu.php | 137 +++++++++++++++---- api/test_db_connection.php | 38 ++++++ assets/js/app_simple.js | 260 ++++++++++++++++++++++++++++++++++++- classes/Database.php | 33 ++++- config/config.php | 3 + index.php | 137 ++++++++++++------- test_direct_api.php | 100 ++++++++++++++ test_menus.php | 200 ++++++++++++++++++++++++++++ test_menus_api.html | 153 ++++++++++++++++++++++ test_menus_frontend.html | 203 +++++++++++++++++++++++++++++ 12 files changed, 1397 insertions(+), 106 deletions(-) create mode 100644 api/delete_menu.php create mode 100644 api/test_db_connection.php create mode 100644 test_direct_api.php create mode 100644 test_menus.php create mode 100644 test_menus_api.html create mode 100644 test_menus_frontend.html diff --git a/api/delete_menu.php b/api/delete_menu.php new file mode 100644 index 0000000..c1144b3 --- /dev/null +++ b/api/delete_menu.php @@ -0,0 +1,85 @@ + 'Método no permitido']); + exit; +} + +try { + $input = json_decode(file_get_contents('php://input'), true); + + if (!$input || !isset($input['menu_id'])) { + http_response_code(400); + echo json_encode(['error' => 'ID del menú es requerido']); + exit; + } + + $menuId = (int)$input['menu_id']; + + if ($menuId <= 0) { + http_response_code(400); + echo json_encode(['error' => 'ID de menú inválido']); + exit; + } + + $db = Database::getInstance(); + + // Verificar que el menú existe + $menu = $db->fetch("SELECT id, name FROM menus WHERE id = :id", ['id' => $menuId]); + + if (!$menu) { + http_response_code(404); + echo json_encode(['error' => 'Menú no encontrado']); + exit; + } + + // Iniciar transacción + $db->beginTransaction(); + + try { + // Eliminar opciones del menú + $db->execute("DELETE FROM menu_options WHERE menu_id = :menu_id", ['menu_id' => $menuId]); + + // Eliminar el menú + $result = $db->execute("DELETE FROM menus WHERE id = :id", ['id' => $menuId]); + + if ($result) { + $db->commit(); + echo json_encode([ + 'success' => true, + 'message' => "Menú '{$menu['name']}' eliminado correctamente" + ]); + } else { + $db->rollback(); + echo json_encode(['error' => 'Error eliminando menú']); + } + + } catch (Exception $e) { + $db->rollback(); + throw $e; + } + +} catch (Exception $e) { + error_log("Error in delete_menu.php: " . $e->getMessage()); + http_response_code(500); + echo json_encode([ + 'error' => 'Error interno del servidor', + 'debug' => $e->getMessage() + ]); +} +?> \ No newline at end of file diff --git a/api/get_menus.php b/api/get_menus.php index fac1ddb..157f77a 100644 --- a/api/get_menus.php +++ b/api/get_menus.php @@ -1,7 +1,7 @@ fetchAll( - "SELECT + $db = Database::getInstance(); + $connection = $db->getConnection(); + + if (!$connection) { + throw new Exception("No se pudo obtener la conexión a la base de datos"); + } + + error_log("Conexión DB establecida correctamente"); + + // Verificar base de datos actual + $currentDB = $db->fetch("SELECT DATABASE() as current_db"); + error_log("Base de datos actual: " . json_encode($currentDB)); + + // Verificar si las tablas existen + try { + $showTablesQuery = "SHOW TABLES LIKE 'menus'"; + $tablesExist = $db->fetchAll($showTablesQuery); + error_log("Query SHOW TABLES ejecutada. Resultado: " . json_encode($tablesExist)); + + if (empty($tablesExist)) { + error_log("Tabla 'menus' no existe, pero datos indican que ya existe. Continuando con consulta..."); + } else { + error_log("Tabla 'menus' ya existe"); + } + } catch (Exception $e) { + error_log("ERROR creando/verificando tablas: " . $e->getMessage()); + echo json_encode([ + 'success' => false, + 'error' => 'Error en estructura de base de datos', + 'debug' => $e->getMessage(), + 'step' => 'table_verification', + 'data' => [] + ]); + exit; + } + + // Obtener menús con consulta simplificada + try { + error_log("Ejecutando consulta de menús..."); + + // Verificar estructura de tabla + $tableStructure = $db->fetchAll("DESCRIBE menus"); + error_log("Estructura tabla menus: " . json_encode($tableStructure)); + + // Consulta adaptada a la estructura real de la tabla existente + $menusQuery = "SELECT id, name, title, @@ -29,26 +74,93 @@ try { order_position, created_at, updated_at - FROM menus - ORDER BY order_position ASC, title ASC" - ); - - // Obtener opciones de cada menú - foreach ($menus as &$menu) { - $options = $db->fetchAll( - "SELECT * FROM menu_options - WHERE menu_id = :menu_id - ORDER BY option_number ASC", - ['menu_id' => $menu['id']] - ); - $menu['options'] = $options; + FROM menus ORDER BY order_position ASC, id ASC"; + $menus = $db->fetchAll($menusQuery); + + error_log("Menús obtenidos: " . count($menus) . " registros"); + + // Transformar datos para compatibilidad con JavaScript + foreach ($menus as &$menu) { + // Mapear campos para compatibilidad + $menu['menu_name'] = $menu['title'] ?? $menu['name']; + $menu['menu_key'] = $menu['name']; + $menu['status'] = ($menu['is_active'] == 1) ? 'active' : 'inactive'; + $menu['welcome_message'] = ''; // No disponible en estructura actual + } + + error_log("Menús encontrados: " . count($menus)); + + if (!empty($menus)) { + error_log("Primer menú: " . json_encode($menus[0])); + } + + // Procesar opciones con manejo de errores para estructura desconocida + foreach ($menus as &$menu) { + try { + // Primero verificar si la tabla menu_options existe + $optionsTableExists = $db->fetchAll("SHOW TABLES LIKE 'menu_options'"); + + if (!empty($optionsTableExists)) { + // Verificar estructura de la tabla + $optionsStructure = $db->fetchAll("DESCRIBE menu_options"); + error_log("Estructura menu_options: " . json_encode($optionsStructure)); + + $optionsQuery = "SELECT * FROM menu_options WHERE menu_id = ? ORDER BY id ASC"; + $options = $db->fetchAll($optionsQuery, [$menu['id']]); + } else { + error_log("Tabla menu_options no existe"); + $options = []; + } + + $menu['options'] = $options; + $menu['options_count'] = count($options); + } catch (Exception $e) { + error_log("Error opciones menú {$menu['id']}: " . $e->getMessage()); + $menu['options'] = []; + $menu['options_count'] = 0; + } + } + + error_log("=== FIN DEBUG GET MENUS SUCCESS ==="); + + echo json_encode([ + 'success' => true, + 'data' => $menus, + 'total' => count($menus), + 'message' => 'Menús cargados exitosamente', + 'debug_info' => [ + 'db_name' => DB_NAME, + 'total_found' => count($menus), + 'timestamp' => date('Y-m-d H:i:s') + ] + ]); + + } catch (Exception $e) { + error_log("ERROR consulta menús: " . $e->getMessage()); + error_log("Stack trace: " . $e->getTraceAsString()); + error_log("=== FIN DEBUG GET MENUS ERROR ==="); + + echo json_encode([ + 'success' => false, + 'error' => 'Error en consulta de menús', + 'debug' => $e->getMessage(), + 'code' => $e->getCode(), + 'step' => 'menu_query', + 'data' => [] + ]); } - echo json_encode($menus); - } catch (Exception $e) { - error_log("Error in get_menus.php: " . $e->getMessage()); + error_log("ERROR GENERAL get_menus.php: " . $e->getMessage()); + error_log("Stack trace: " . $e->getTraceAsString()); + http_response_code(500); - echo json_encode(['error' => 'Error interno del servidor']); + echo json_encode([ + 'success' => false, + 'error' => 'Error general del sistema', + 'debug' => $e->getMessage(), + 'step' => 'initialization', + 'data' => [] + ]); } ?> \ No newline at end of file diff --git a/api/save_menu.php b/api/save_menu.php index 5e2f957..1d23004 100644 --- a/api/save_menu.php +++ b/api/save_menu.php @@ -1,11 +1,14 @@ 'Método no permitido']); + echo json_encode(['success' => false, 'error' => 'Método no permitido']); exit; } try { $input = json_decode(file_get_contents('php://input'), true); - if (!$input || !isset($input['name']) || !isset($input['title'])) { + if (!$input || !isset($input['name'])) { http_response_code(400); - echo json_encode(['error' => 'Nombre y título son requeridos']); + echo json_encode(['success' => false, 'error' => 'Nombre del menú es requerido']); exit; } $db = Database::getInstance(); - // Verificar si es menú raíz - $isRoot = empty($input['parent_id']) ? 1 : 0; + // Iniciar transacción + $db->beginTransaction(); - // Obtener próximo order_position - $maxOrder = $db->fetch( - "SELECT COALESCE(MAX(order_position), 0) as max_order FROM menus" - ); + try { +// Verificar estructura de tabla antes de insertar + $tableStructure = $db->fetchAll("DESCRIBE menus"); + $availableColumns = array_column($tableStructure, 'Field'); - $menuData = [ - 'name' => $input['name'], - 'title' => $input['title'], - 'description' => $input['description'] ?? '', - 'parent_id' => empty($input['parent_id']) ? null : $input['parent_id'], - 'is_root' => $isRoot, - 'is_active' => $input['is_active'] ?? 1, - 'order_position' => $maxOrder['max_order'] + 1, - 'created_at' => date('Y-m-d H:i:s'), - 'updated_at' => date('Y-m-d H:i:s') - ]; + error_log("Columnas disponibles en tabla menus: " . json_encode($availableColumns)); - $menuId = $db->insert('menus', $menuData); + // Adaptar datos a la estructura real de la tabla + $menuData = []; - echo json_encode([ - 'success' => true, - 'message' => 'Menú guardado correctamente', - 'menu_id' => $menuId - ]); + // Campos básicos que deberían existir + if (in_array('name', $availableColumns)) { + $menuData['name'] = $input['menu_key'] ?? $input['name']; // Usar menu_key como name + } + + if (in_array('title', $availableColumns)) { + $menuData['title'] = $input['name'] ?? 'Nuevo Menú'; // Usar name como title + } + + if (in_array('description', $availableColumns)) { + $menuData['description'] = $input['description'] ?? ''; + } + + if (in_array('parent_id', $availableColumns)) { + $menuData['parent_id'] = empty($input['parent_id']) ? null : (int)$input['parent_id']; + } + + if (in_array('is_root', $availableColumns)) { + $menuData['is_root'] = empty($input['parent_id']) ? 1 : 0; + } + + if (in_array('is_active', $availableColumns)) { + $menuData['is_active'] = ($input['status'] === 'active') ? 1 : 0; + } + + if (in_array('order_position', $availableColumns)) { + // Obtener próxima posición + $maxOrder = $db->fetch("SELECT COALESCE(MAX(order_position), 0) as max_order FROM menus"); + $menuData['order_position'] = ($maxOrder['max_order'] ?? 0) + 1; + } + + if (in_array('created_at', $availableColumns)) { + $menuData['created_at'] = date('Y-m-d H:i:s'); + } + + if (in_array('updated_at', $availableColumns)) { + $menuData['updated_at'] = date('Y-m-d H:i:s'); + } + + $menuId = $db->insert('menus', $menuData); + + if (!$menuId) { + throw new Exception('Error insertando menú'); + } + + // Procesar opciones si las hay + $optionsCount = 0; + if (isset($input['options']) && is_array($input['options'])) { + foreach ($input['options'] as $option) { + if (!empty($option['key']) && !empty($option['text'])) { + $optionData = [ + 'menu_id' => $menuId, + 'option_key' => $option['key'], + 'option_text' => $option['text'], + 'option_action' => $option['action'] ?? 'message', + 'option_value' => $option['value'] ?? '', + 'order_index' => $option['order_index'] ?? $optionsCount + 1, + 'is_active' => true, + 'created_at' => date('Y-m-d H:i:s') + ]; + + $optionId = $db->insert('menu_options', $optionData); + if ($optionId) { + $optionsCount++; + } + } + } + } + + // Actualizar contador de opciones + $db->execute("UPDATE menus SET options_count = :count WHERE id = :id", [ + 'count' => $optionsCount, + 'id' => $menuId + ]); + + $db->commit(); + + echo json_encode([ + 'success' => true, + 'message' => 'Menú guardado correctamente', + 'menu_id' => $menuId, + 'options_created' => $optionsCount + ]); + + } catch (Exception $e) { + $db->rollback(); + throw $e; + } } catch (Exception $e) { error_log("Error in save_menu.php: " . $e->getMessage()); http_response_code(500); - echo json_encode(['error' => 'Error interno del servidor: ' . $e->getMessage()]); + echo json_encode([ + 'success' => false, + 'error' => 'Error interno del servidor', + 'debug' => $e->getMessage() + ]); } ?> \ No newline at end of file diff --git a/api/test_db_connection.php b/api/test_db_connection.php new file mode 100644 index 0000000..d05441d --- /dev/null +++ b/api/test_db_connection.php @@ -0,0 +1,38 @@ + 'Iniciando test de conexión DB...', + 'timestamp' => date('Y-m-d H:i:s') + ]); + echo "\n\n"; + + $db = Database::getInstance(); + + echo json_encode([ + 'success' => true, + 'message' => 'Conexión a base de datos exitosa', + 'database' => DB_NAME, + 'host' => DB_HOST + ]); + echo "\n\n"; + + // Test de tabla + $tables = $db->fetchAll("SHOW TABLES"); + echo json_encode([ + 'success' => true, + 'message' => 'Tablas encontradas', + 'tables' => $tables + ]); + +} catch (Exception $e) { + echo json_encode([ + 'success' => false, + 'error' => $e->getMessage(), + 'trace' => $e->getTraceAsString() + ]); +} +?> \ No newline at end of file diff --git a/assets/js/app_simple.js b/assets/js/app_simple.js index e2db864..4ebd6e4 100644 --- a/assets/js/app_simple.js +++ b/assets/js/app_simple.js @@ -114,6 +114,9 @@ class SimpleWhatsAppManager { case 'templates': this.loadTemplates(); break; + case 'menus': + this.loadMenus(); + break; case 'logs': this.loadLogs(); break; @@ -794,6 +797,26 @@ class SimpleWhatsAppManager { } } + async loadMenus() { + this.log('Cargando menús'); + + try { + const response = await this.apiCall('get_menus.php'); + + if (response && response.success) { + this.updateMenusList(response.data || []); + } else if (response && Array.isArray(response)) { + // Retrocompatibilidad + this.updateMenusList(response); + } else { + this.showError('Error cargando menús'); + } + + } catch (error) { + this.showError('Error cargando menús: ' + error.message); + } + } + updateTemplatesList(templates) { const container = document.getElementById('templates-table'); if (!container) return; @@ -828,6 +851,47 @@ class SimpleWhatsAppManager { container.innerHTML = html; } + updateMenusList(menus) { + const container = document.getElementById('menus-table'); + if (!container) return; + + if (menus.length === 0) { + container.innerHTML = '
${menu.menu_key || menu.trigger}