diff --git a/api/delete_menu_option.php b/api/delete_menu_option.php
new file mode 100644
index 0000000..d43cd9c
--- /dev/null
+++ b/api/delete_menu_option.php
@@ -0,0 +1,78 @@
+fetch("SELECT * FROM menu_options WHERE id = :id", ['id' => $optionId]);
+
+ if (!$option) {
+ throw new Exception('Opción no encontrada');
+ }
+
+ // Si se proporcionó menu_id, verificar que coincida
+ if ($menuId !== null && $option['menu_id'] != $menuId) {
+ throw new Exception('La opción no pertenece al menú especificado');
+ }
+
+ // Eliminar la opción
+ $result = $db->execute("DELETE FROM menu_options WHERE id = :id", ['id' => $optionId]);
+
+ if ($result > 0) {
+ // Registrar en log
+ error_log("Opción de menú eliminada: ID={$optionId}, Menú={$option['menu_id']}");
+
+ echo json_encode([
+ 'success' => true,
+ 'message' => 'Opción eliminada correctamente',
+ 'option_id' => $optionId,
+ 'menu_id' => $option['menu_id']
+ ]);
+ } else {
+ throw new Exception('No se pudo eliminar la opción');
+ }
+
+} catch (Exception $e) {
+ error_log("Error in delete_menu_option.php: " . $e->getMessage());
+ http_response_code(400);
+ echo json_encode([
+ 'success' => false,
+ 'error' => $e->getMessage()
+ ]);
+}
+?>
\ No newline at end of file
diff --git a/api/delete_user.php b/api/delete_user.php
index 66757a1..772cca1 100644
--- a/api/delete_user.php
+++ b/api/delete_user.php
@@ -1,80 +1,130 @@
fetch("SELECT id, name, phone_number FROM users WHERE id = :user_id", ['user_id' => $user_id]);
+
+ if (!$user_data) {
+ throw new Exception('Usuario no encontrado');
}
- // Simular un pequeño delay para realismo
- usleep(500000); // 0.5 segundos
+ // Iniciar transacción
+ $conn = $db->getConnection();
+ $conn->beginTransaction();
- // Respuesta exitosa
- $response = [
- 'success' => true,
- 'message' => 'Usuario eliminado correctamente',
- 'data' => [
- 'deleted_user_id' => $user_id,
- 'timestamp' => date('Y-m-d H:i:s')
- ],
- 'debug' => $debug ? [
- 'timestamp' => date('Y-m-d H:i:s'),
- 'simulated' => true,
- 'user_id' => $user_id
- ] : null
- ];
+ try {
+ // Eliminar mensajes relacionados usando PDO
+ $stmt = $conn->prepare("DELETE FROM messages WHERE user_id = ?");
+ $stmt->bindValue(1, $user_id, PDO::PARAM_INT);
+ $stmt->execute();
+ $deleted_messages = $stmt->rowCount();
- echo json_encode($response, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
+ // Eliminar el usuario
+ $stmt = $conn->prepare("DELETE FROM users WHERE id = ?");
+ $stmt->bindValue(1, $user_id, PDO::PARAM_INT);
+ $stmt->execute();
+
+ if ($stmt->rowCount() === 0) {
+ throw new Exception('No se pudo eliminar el usuario');
+ }
+
+ // Confirmar transacción
+ $conn->commit();
+
+ // Respuesta exitosa
+ echo json_encode([
+ 'success' => true,
+ 'message' => 'Usuario eliminado correctamente',
+ 'data' => [
+ 'deleted_user_id' => $user_id,
+ 'deleted_user_name' => $user_data['name'],
+ 'deleted_user_phone' => $user_data['phone_number'],
+ 'deleted_messages_count' => $deleted_messages,
+ 'timestamp' => date('Y-m-d H:i:s')
+ ]
+ ]);
+
+ } catch (Exception $e) {
+ // Revertir transacción
+ $conn->rollback();
+ throw $e;
+ }
} catch (Exception $e) {
- $error_response = [
- 'success' => false,
+ http_response_code(400);
+ echo json_encode([
+ 'success' => false,
'error' => $e->getMessage(),
- 'debug' => $debug ? [
- 'timestamp' => date('Y-m-d H:i:s'),
- 'file' => __FILE__,
+ 'debug_info' => [
+ 'file' => basename(__FILE__),
'line' => $e->getLine(),
'trace' => $e->getTraceAsString()
- ] : null
- ];
-
+ ]
+ ]);
+} catch (Error $e) {
http_response_code(500);
- echo json_encode($error_response, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
+ echo json_encode([
+ 'success' => false,
+ 'error' => 'Error interno del servidor: ' . $e->getMessage(),
+ 'debug_info' => [
+ 'file' => basename(__FILE__),
+ 'line' => $e->getLine()
+ ]
+ ]);
}
?>
\ No newline at end of file
diff --git a/api/save_menu.php b/api/save_menu.php
index 1d23004..b060754 100644
--- a/api/save_menu.php
+++ b/api/save_menu.php
@@ -35,7 +35,19 @@ try {
$db->beginTransaction();
try {
-// Verificar estructura de tabla antes de insertar
+ // Verificar si es actualización o creación
+ $isUpdate = isset($input['id']) && !empty($input['id']);
+ $menuId = $isUpdate ? intval($input['id']) : null;
+
+ if ($isUpdate) {
+ // Verificar que el menú existe
+ $existingMenu = $db->fetch("SELECT id FROM menus WHERE id = ?", [$menuId]);
+ if (!$existingMenu) {
+ throw new Exception('Menú no encontrado para actualizar');
+ }
+ }
+
+ // Verificar estructura de tabla antes de insertar/actualizar
$tableStructure = $db->fetchAll("DESCRIBE menus");
$availableColumns = array_column($tableStructure, 'Field');
@@ -44,13 +56,14 @@ try {
// Adaptar datos a la estructura real de la tabla
$menuData = [];
- // Campos básicos que deberían existir
+ // La tabla usa 'name' para la clave del menú
if (in_array('name', $availableColumns)) {
- $menuData['name'] = $input['menu_key'] ?? $input['name']; // Usar menu_key como name
+ $menuData['name'] = $input['menu_key'] ?? $input['name'] ?? 'menu_' . time();
}
+ // La tabla usa 'title' para el nombre visible del menú
if (in_array('title', $availableColumns)) {
- $menuData['title'] = $input['name'] ?? 'Nuevo Menú'; // Usar name como title
+ $menuData['title'] = $input['name'] ?? $input['title'] ?? 'Nuevo Menú';
}
if (in_array('description', $availableColumns)) {
@@ -66,28 +79,37 @@ try {
}
if (in_array('is_active', $availableColumns)) {
- $menuData['is_active'] = ($input['status'] === 'active') ? 1 : 0;
+ // Mapear status a is_active: 'active' -> 1, cualquier otra cosa -> 0
+ $menuData['is_active'] = (($input['status'] ?? 'active') === 'active') ? 1 : 0;
}
- if (in_array('order_position', $availableColumns)) {
- // Obtener próxima posición
+ if (in_array('order_position', $availableColumns) && !$isUpdate) {
+ // Solo establecer order_position para menús nuevos
$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)) {
+ if (in_array('created_at', $availableColumns) && !$isUpdate) {
$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');
}
-
+
+ // Crear o actualizar menú
+ if ($isUpdate) {
+ $result = $db->update('menus', $menuData, 'id = :menu_id', ['menu_id' => $menuId]);
+ if (!$result || $result->rowCount() === 0) {
+ throw new Exception('No se pudo actualizar el menú');
+ }
+ } else {
$menuId = $db->insert('menus', $menuData);
if (!$menuId) {
throw new Exception('Error insertando menú');
}
+ }
// Procesar opciones si las hay
$optionsCount = 0;
@@ -113,19 +135,22 @@ try {
}
}
- // Actualizar contador de opciones
- $db->execute("UPDATE menus SET options_count = :count WHERE id = :id", [
- 'count' => $optionsCount,
- 'id' => $menuId
- ]);
+ // Actualizar contador de opciones solo si la columna existe
+ if (in_array('options_count', $availableColumns)) {
+ $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',
+ 'message' => $isUpdate ? 'Menú actualizado correctamente' : 'Menú guardado correctamente',
'menu_id' => $menuId,
- 'options_created' => $optionsCount
+ 'options_created' => $optionsCount,
+ 'action' => $isUpdate ? 'updated' : 'created'
]);
} catch (Exception $e) {
diff --git a/api/save_menu_option.php b/api/save_menu_option.php
new file mode 100644
index 0000000..f97caa7
--- /dev/null
+++ b/api/save_menu_option.php
@@ -0,0 +1,126 @@
+fetch("SELECT id FROM menus WHERE id = :id", ['id' => $input['menu_id']]);
+ if (!$menu) {
+ throw new Exception('Menú no encontrado');
+ }
+
+ // Verificar estructura de tabla menu_options
+ $tableStructure = $db->fetchAll("DESCRIBE menu_options");
+ $availableColumns = array_column($tableStructure, 'Field');
+
+ // Preparar datos según estructura de tabla
+ $optionData = [];
+
+ if (in_array('menu_id', $availableColumns)) {
+ $optionData['menu_id'] = (int)$input['menu_id'];
+ }
+
+ if (in_array('option_number', $availableColumns)) {
+ $optionData['option_number'] = (int)$input['option_key'];
+ }
+
+ if (in_array('option_key', $availableColumns)) {
+ $optionData['option_key'] = $input['option_key'];
+ }
+
+ if (in_array('text', $availableColumns)) {
+ $optionData['text'] = $input['option_text'];
+ }
+
+ if (in_array('option_text', $availableColumns)) {
+ $optionData['option_text'] = $input['option_text'];
+ }
+
+ if (in_array('action_type', $availableColumns)) {
+ $optionData['action_type'] = $input['option_action'] ?? 'message';
+ }
+
+ if (in_array('option_action', $availableColumns)) {
+ $optionData['option_action'] = $input['option_action'] ?? 'message';
+ }
+
+ if (in_array('action_value', $availableColumns)) {
+ $optionData['action_value'] = $input['option_value'] ?? '';
+ }
+
+ if (in_array('option_value', $availableColumns)) {
+ $optionData['option_value'] = $input['option_value'] ?? '';
+ }
+
+ if (in_array('is_active', $availableColumns)) {
+ $optionData['is_active'] = ($input['is_active'] ?? true) ? 1 : 0;
+ }
+
+ if (in_array('order_index', $availableColumns)) {
+ // Obtener el siguiente índice de orden
+ $maxOrder = $db->fetch(
+ "SELECT COALESCE(MAX(order_index), 0) as max_order FROM menu_options WHERE menu_id = :menu_id",
+ ['menu_id' => $input['menu_id']]
+ );
+ $optionData['order_index'] = ($maxOrder['max_order'] ?? 0) + 1;
+ }
+
+ if (in_array('created_at', $availableColumns)) {
+ $optionData['created_at'] = date('Y-m-d H:i:s');
+ }
+
+ // Insertar la opción
+ $optionId = $db->insert('menu_options', $optionData);
+
+ if (!$optionId) {
+ throw new Exception('Error insertando opción de menú');
+ }
+
+ echo json_encode([
+ 'success' => true,
+ 'message' => 'Opción de menú guardada correctamente',
+ 'option_id' => $optionId,
+ 'menu_id' => $input['menu_id']
+ ]);
+
+} catch (Exception $e) {
+ error_log("Error in save_menu_option.php: " . $e->getMessage());
+ http_response_code(400);
+ echo json_encode([
+ 'success' => false,
+ 'error' => $e->getMessage()
+ ]);
+}
+?>
\ No newline at end of file
diff --git a/api/update_menu_option.php b/api/update_menu_option.php
new file mode 100644
index 0000000..5f4f9f4
--- /dev/null
+++ b/api/update_menu_option.php
@@ -0,0 +1,118 @@
+fetch("SELECT * FROM menu_options WHERE id = :id", ['id' => $optionId]);
+
+ if (!$option) {
+ throw new Exception('Opción no encontrada');
+ }
+
+ // Si se proporcionó menu_id, verificar que coincida
+ if ($menuId !== null && $option['menu_id'] != $menuId) {
+ throw new Exception('La opción no pertenece al menú especificado');
+ }
+
+ // Verificar estructura de tabla menu_options
+ $tableStructure = $db->fetchAll("DESCRIBE menu_options");
+ $availableColumns = array_column($tableStructure, 'Field');
+
+ // Preparar datos según estructura de tabla
+ $updateData = [];
+
+ if (in_array('option_number', $availableColumns)) {
+ $updateData['option_number'] = (int)$input['option_key'];
+ }
+
+ if (in_array('option_key', $availableColumns)) {
+ $updateData['option_key'] = $input['option_key'];
+ }
+
+ if (in_array('text', $availableColumns)) {
+ $updateData['text'] = $input['option_text'];
+ }
+
+ if (in_array('option_text', $availableColumns)) {
+ $updateData['option_text'] = $input['option_text'];
+ }
+
+ if (in_array('action_type', $availableColumns)) {
+ $updateData['action_type'] = $input['option_action'] ?? 'message';
+ }
+
+ if (in_array('option_action', $availableColumns)) {
+ $updateData['option_action'] = $input['option_action'] ?? 'message';
+ }
+
+ if (in_array('action_value', $availableColumns)) {
+ $updateData['action_value'] = $input['option_value'] ?? '';
+ }
+
+ if (in_array('option_value', $availableColumns)) {
+ $updateData['option_value'] = $input['option_value'] ?? '';
+ }
+
+ if (in_array('updated_at', $availableColumns)) {
+ $updateData['updated_at'] = date('Y-m-d H:i:s');
+ }
+
+ // Actualizar la opción
+ $result = $db->update('menu_options', $updateData, 'id = :option_id', ['option_id' => $optionId]);
+
+ if ($result && $result->rowCount() >= 0) {
+ echo json_encode([
+ 'success' => true,
+ 'message' => 'Opción de menú actualizada correctamente',
+ 'option_id' => $optionId,
+ 'menu_id' => $option['menu_id']
+ ]);
+ } else {
+ throw new Exception('No se pudo actualizar la opción');
+ }
+
+} catch (Exception $e) {
+ error_log("Error in update_menu_option.php: " . $e->getMessage());
+ http_response_code(400);
+ echo json_encode([
+ 'success' => false,
+ 'error' => $e->getMessage()
+ ]);
+}
+?>
\ No newline at end of file
diff --git a/api/update_user.php b/api/update_user.php
new file mode 100644
index 0000000..f01ebb7
--- /dev/null
+++ b/api/update_user.php
@@ -0,0 +1,115 @@
+fetch("SELECT id FROM users WHERE id = :user_id", ['user_id' => $user_id]);
+
+ if (!$checkUser) {
+ throw new Exception('Usuario no encontrado');
+ }
+
+ // Actualizar usuario usando el método de la clase Database
+ $updateData = [
+ 'name' => $name,
+ 'status' => $status
+ ];
+
+ $result = $db->update('users', $updateData, 'id = :user_id', ['user_id' => $user_id]);
+
+ if ($result === false) {
+ throw new Exception('No se pudo actualizar el usuario');
+ }
+
+ // Respuesta exitosa
+ echo json_encode([
+ 'success' => true,
+ 'message' => 'Usuario actualizado correctamente',
+ 'user_id' => $user_id,
+ 'data' => [
+ 'name' => $name,
+ 'status' => $status
+ ]
+ ]);
+
+} catch (Exception $e) {
+ http_response_code(400);
+ echo json_encode([
+ 'success' => false,
+ 'error' => $e->getMessage(),
+ 'debug_info' => [
+ 'file' => basename(__FILE__),
+ 'line' => $e->getLine(),
+ 'trace' => $e->getTraceAsString()
+ ]
+ ]);
+} catch (Error $e) {
+ http_response_code(500);
+ echo json_encode([
+ 'success' => false,
+ 'error' => 'Error interno del servidor: ' . $e->getMessage(),
+ 'debug_info' => [
+ 'file' => basename(__FILE__),
+ 'line' => $e->getLine()
+ ]
+ ]);
+}
+?>
\ No newline at end of file
diff --git a/assets/js/app_simple.js b/assets/js/app_simple.js
index 3c37a57..88c650e 100644
--- a/assets/js/app_simple.js
+++ b/assets/js/app_simple.js
@@ -708,6 +708,16 @@ class SimpleWhatsAppManager {
this.showAlert(message, 'success');
}
+ showInfo(message) {
+ this.log(`Info: ${message}`, 'info');
+ this.showAlert(message, 'info');
+ }
+
+ showWarning(message) {
+ this.log(`Warning: ${message}`, 'warning');
+ this.showAlert(message, 'warning');
+ }
+
showAlert(message, type) {
// Crear alerta temporal
const alertDiv = document.createElement('div');
@@ -2107,10 +2117,28 @@ window.app = {
};
// Funciones globales para gestión de menús
-window.editMenu = function (menuId) {
+window.editMenu = async function (menuId) {
console.log('Editando menú:', menuId);
- // TODO: Implementar edición de menús
- alert(`Editar menú ${menuId} - Función en desarrollo`);
+
+ try {
+ // Obtener datos del menú
+ const response = await window.whatsappManager.apiCall('get_menus.php');
+
+ if (response && response.success) {
+ const menu = response.data.find(m => m.id == menuId);
+
+ if (menu) {
+ showEditMenuModal(menu);
+ } else {
+ window.whatsappManager.showError('Menú no encontrado');
+ }
+ } else {
+ window.whatsappManager.showError('Error cargando datos del menú');
+ }
+ } catch (error) {
+ console.error('Error editando menú:', error);
+ window.whatsappManager.showError('Error editando menú: ' + error.message);
+ }
};
window.deleteMenu = function (menuId) {
@@ -2122,10 +2150,28 @@ window.deleteMenu = function (menuId) {
}
};
-window.viewMenuOptions = function (menuId) {
+window.viewMenuOptions = async function (menuId) {
console.log('Viendo opciones del menú:', menuId);
- // TODO: Implementar visualización de opciones del menú
- alert(`Ver opciones del menú ${menuId} - Función en desarrollo`);
+
+ try {
+ // Obtener datos del menú y sus opciones
+ const response = await window.whatsappManager.apiCall('get_menus.php');
+
+ if (response && response.success) {
+ const menu = response.data.find(m => m.id == menuId);
+
+ if (menu) {
+ showMenuOptionsModal(menu);
+ } else {
+ window.whatsappManager.showError('Menú no encontrado');
+ }
+ } else {
+ window.whatsappManager.showError('Error cargando opciones del menú');
+ }
+ } catch (error) {
+ console.error('Error viendo opciones:', error);
+ window.whatsappManager.showError('Error viendo opciones: ' + error.message);
+ }
};
// Función para agregar nueva opción al menú
@@ -2598,4 +2644,1176 @@ document.addEventListener('DOMContentLoaded', function() {
}
});
}
-});
\ No newline at end of file
+});
+
+// Funciones globales para gestión de usuarios
+
+// Función para editar usuario
+window.editUser = function(userId) {
+ console.log('Editando usuario:', userId);
+
+ // Buscar los datos del usuario actual en la tabla
+ const userRow = document.querySelector(`button[onclick="editUser(${userId})"]`).closest('tr');
+ const cells = userRow.querySelectorAll('td');
+
+ const currentData = {
+ id: userId,
+ phone: cells[1].textContent,
+ name: cells[2].textContent === 'Sin nombre' ? '' : cells[2].textContent,
+ status: cells[3].querySelector('.badge').textContent.toLowerCase()
+ };
+
+ showEditUserModal(currentData);
+};
+
+// Función para eliminar usuario
+window.deleteUser = function(userId) {
+ console.log('Eliminando usuario:', userId);
+
+ const userRow = document.querySelector(`button[onclick="deleteUser(${userId})"]`).closest('tr');
+ const userName = userRow.querySelectorAll('td')[2].textContent;
+ const userPhone = userRow.querySelectorAll('td')[1].textContent;
+
+ if (confirm(`¿Estás seguro de que quieres eliminar este usuario?\n\nUsuario: ${userName}\nTeléfono: ${userPhone}\n\nEsta acción no se puede deshacer y eliminará:\n• Todos sus mensajes\n• Su historial de conversación\n• Sus datos personales`)) {
+ deleteUserAction(userId);
+ }
+};
+
+// Mostrar modal de edición de usuario
+function showEditUserModal(userData) {
+ const modalHtml = `
+
+ `;
+
+ // Remover modal anterior si existe
+ const existingModal = document.getElementById('editUserModal');
+ if (existingModal) {
+ existingModal.remove();
+ }
+
+ document.body.insertAdjacentHTML('beforeend', modalHtml);
+
+ const modal = new bootstrap.Modal(document.getElementById('editUserModal'));
+ modal.show();
+}
+
+// Guardar edición de usuario
+window.saveUserEdit = async function() {
+ const userId = document.getElementById('editUserId').value;
+ const userName = document.getElementById('editUserName').value.trim();
+ const userStatus = document.getElementById('editUserStatus').value;
+
+ console.log('Guardando usuario:', { userId, userName, userStatus });
+
+ const userData = {
+ user_id: userId,
+ name: userName || null, // null si está vacío
+ status: userStatus
+ };
+
+ try {
+ const response = await window.whatsappManager.apiCall('update_user.php', {
+ method: 'POST',
+ body: userData
+ });
+
+ if (response && response.success) {
+ window.whatsappManager.showSuccess('Usuario actualizado correctamente');
+
+ // Cerrar modal
+ const modal = bootstrap.Modal.getInstance(document.getElementById('editUserModal'));
+ if (modal) modal.hide();
+
+ // Recargar lista de usuarios
+ setTimeout(() => {
+ window.whatsappManager.loadUsers();
+ }, 500);
+ } else {
+ window.whatsappManager.showError(`Error actualizando usuario: ${response?.error || 'Error desconocido'}`);
+ }
+ } catch (error) {
+ console.error('Error:', error);
+ window.whatsappManager.showError('Error actualizando usuario: ' + error.message);
+ }
+};
+
+// Acción de eliminar usuario
+async function deleteUserAction(userId) {
+ try {
+ console.log('Eliminando usuario con ID:', userId);
+
+ const response = await window.whatsappManager.apiCall('delete_user.php', {
+ method: 'POST',
+ body: { user_id: userId }
+ });
+
+ if (response && response.success) {
+ window.whatsappManager.showSuccess('Usuario eliminado correctamente');
+
+ // Recargar lista de usuarios
+ setTimeout(() => {
+ window.whatsappManager.loadUsers();
+ }, 500);
+ } else {
+ window.whatsappManager.showError(`Error eliminando usuario: ${response?.error || 'Error desconocido'}`);
+ }
+ } catch (error) {
+ console.error('Error:', error);
+ window.whatsappManager.showError('Error eliminando usuario: ' + error.message);
+ }
+}
+
+// ========================= FUNCIONES MODALES PARA MENÚS =========================
+
+// Modal para editar menú
+function showEditMenuModal(menuData) {
+ const modalHtml = `
+
+ `;
+
+ // Remover modal anterior si existe
+ const existingModal = document.getElementById('editMenuModal');
+ if (existingModal) {
+ existingModal.remove();
+ }
+
+ document.body.insertAdjacentHTML('beforeend', modalHtml);
+
+ const modal = new bootstrap.Modal(document.getElementById('editMenuModal'));
+ modal.show();
+}
+
+// Modal para ver opciones del menú
+function showMenuOptionsModal(menuData) {
+ const modalHtml = `
+
+ `;
+
+ // Remover modal anterior si existe
+ const existingModal = document.getElementById('viewMenuOptionsModal');
+ if (existingModal) {
+ existingModal.remove();
+ }
+
+ document.body.insertAdjacentHTML('beforeend', modalHtml);
+
+ const modal = new bootstrap.Modal(document.getElementById('viewMenuOptionsModal'));
+ modal.show();
+}
+
+// Guardar edición de menú
+window.saveMenuEdit = async function() {
+ const menuId = document.getElementById('editMenuId').value;
+ const menuName = document.getElementById('editMenuName').value.trim();
+ const menuKey = document.getElementById('editMenuKey').value.trim();
+ const menuDescription = document.getElementById('editMenuDescription').value.trim();
+ const menuWelcome = document.getElementById('editMenuWelcome').value.trim();
+ const menuStatus = document.getElementById('editMenuStatus').value;
+
+ if (!menuName || !menuKey) {
+ window.whatsappManager.showError('Nombre y clave del menú son obligatorios');
+ return;
+ }
+
+ console.log('Guardando menú editado:', { menuId, menuName, menuKey, menuStatus });
+
+ const menuData = {
+ id: menuId,
+ name: menuName,
+ menu_key: menuKey,
+ description: menuDescription,
+ welcome_message: menuWelcome,
+ status: menuStatus
+ };
+
+ try {
+ const response = await window.whatsappManager.apiCall('save_menu.php', {
+ method: 'POST',
+ body: menuData
+ });
+
+ if (response && response.success) {
+ window.whatsappManager.showSuccess('Menú actualizado correctamente');
+
+ // Cerrar modal
+ const modal = bootstrap.Modal.getInstance(document.getElementById('editMenuModal'));
+ if (modal) modal.hide();
+
+ // Recargar lista de menús
+ setTimeout(() => {
+ window.whatsappManager.loadMenus();
+ }, 500);
+ } else {
+ window.whatsappManager.showError(`Error actualizando menú: ${response?.error || 'Error desconocido'}`);
+ }
+ } catch (error) {
+ console.error('Error:', error);
+ window.whatsappManager.showError('Error actualizando menú: ' + error.message);
+ }
+};
+
+// Funciones auxiliares para opciones
+window.addMenuOptionInModal = function(menuId) {
+ console.log('Agregando opción al menú:', menuId);
+
+ const modalHtml = `
+
+ `;
+
+ // Remover modal anterior si existe
+ const existingModal = document.getElementById('addMenuOptionModal');
+ if (existingModal) {
+ existingModal.remove();
+ }
+
+ document.body.insertAdjacentHTML('beforeend', modalHtml);
+
+ const modal = new bootstrap.Modal(document.getElementById('addMenuOptionModal'));
+ modal.show();
+};
+
+window.editMenuOption = async function(menuId, optionId) {
+ console.log('Editando opción:', { menuId, optionId });
+
+ try {
+ // Obtener datos del menú y sus opciones
+ const response = await window.whatsappManager.apiCall('get_menus.php');
+
+ if (response && response.success) {
+ const menu = response.data.find(m => m.id == menuId);
+
+ if (menu && menu.options) {
+ const option = menu.options.find(o => o.id == optionId);
+
+ if (option) {
+ showEditMenuOptionModal(menuId, option);
+ } else {
+ window.whatsappManager.showError('Opción no encontrada');
+ }
+ } else {
+ window.whatsappManager.showError('Menú no encontrado');
+ }
+ } else {
+ window.whatsappManager.showError('Error cargando datos de la opción');
+ }
+ } catch (error) {
+ console.error('Error:', error);
+ window.whatsappManager.showError('Error cargando opción: ' + error.message);
+ }
+};
+
+// Modal para editar opción de menú
+function showEditMenuOptionModal(menuId, optionData) {
+ const modalHtml = `
+
+ `;
+
+ // Remover modal anterior si existe
+ const existingModal = document.getElementById('editMenuOptionModal');
+ if (existingModal) {
+ existingModal.remove();
+ }
+
+ document.body.insertAdjacentHTML('beforeend', modalHtml);
+
+ const modal = new bootstrap.Modal(document.getElementById('editMenuOptionModal'));
+ modal.show();
+}
+
+// Función para guardar cambios en opción editada
+window.saveEditedMenuOption = async function() {
+ const optionId = document.getElementById('editOptionId').value;
+ const menuId = document.getElementById('editOptionMenuId').value;
+ const optionKey = document.getElementById('editOptionKey').value.trim();
+ const optionText = document.getElementById('editOptionText').value.trim();
+ const optionAction = document.getElementById('editOptionAction').value;
+ const optionValue = document.getElementById('editOptionValue').value.trim();
+
+ if (!optionKey || !optionText) {
+ window.whatsappManager.showError('Clave y texto de la opción son obligatorios');
+ return;
+ }
+
+ console.log('Actualizando opción:', { optionId, menuId, optionKey, optionText });
+
+ const optionData = {
+ option_id: optionId,
+ menu_id: menuId,
+ option_key: optionKey,
+ option_text: optionText,
+ option_action: optionAction,
+ option_value: optionValue
+ };
+
+ try {
+ const response = await window.whatsappManager.apiCall('update_menu_option.php', {
+ method: 'POST',
+ body: optionData
+ });
+
+ if (response && response.success) {
+ window.whatsappManager.showSuccess('Opción actualizada correctamente');
+
+ // Cerrar modales
+ const editModal = bootstrap.Modal.getInstance(document.getElementById('editMenuOptionModal'));
+ if (editModal) editModal.hide();
+
+ const viewModal = bootstrap.Modal.getInstance(document.getElementById('viewMenuOptionsModal'));
+ if (viewModal) viewModal.hide();
+
+ // Reabrir modal actualizado
+ setTimeout(async () => {
+ const menusResponse = await window.whatsappManager.apiCall('get_menus.php');
+ if (menusResponse && menusResponse.success) {
+ const menu = menusResponse.data.find(m => m.id == menuId);
+ if (menu) {
+ showMenuOptionsModal(menu);
+ }
+ }
+ }, 500);
+ } else {
+ window.whatsappManager.showError(`Error actualizando opción: ${response?.error || 'Error desconocido'}`);
+ }
+ } catch (error) {
+ console.error('Error:', error);
+ window.whatsappManager.showError('Error actualizando opción: ' + error.message);
+ }
+};
+
+window.saveNewMenuOption = async function() {
+ const menuId = document.getElementById('addOptionMenuId').value;
+ const optionKey = document.getElementById('addOptionKey').value.trim();
+ const optionText = document.getElementById('addOptionText').value.trim();
+ const optionAction = document.getElementById('addOptionAction').value;
+ const optionValue = document.getElementById('addOptionValue').value.trim();
+
+ if (!optionKey || !optionText) {
+ window.whatsappManager.showError('Clave y texto de la opción son obligatorios');
+ return;
+ }
+
+ console.log('Guardando nueva opción:', { menuId, optionKey, optionText, optionAction, optionValue });
+
+ const optionData = {
+ menu_id: menuId,
+ option_key: optionKey,
+ option_text: optionText,
+ option_action: optionAction,
+ option_value: optionValue,
+ is_active: true
+ };
+
+ try {
+ const response = await window.whatsappManager.apiCall('save_menu_option.php', {
+ method: 'POST',
+ body: optionData
+ });
+
+ if (response && response.success) {
+ window.whatsappManager.showSuccess('Opción agregada correctamente');
+
+ // Cerrar modal de agregar opción
+ const addModal = bootstrap.Modal.getInstance(document.getElementById('addMenuOptionModal'));
+ if (addModal) addModal.hide();
+
+ // Cerrar modal de ver opciones si está abierto
+ const viewModal = bootstrap.Modal.getInstance(document.getElementById('viewMenuOptionsModal'));
+ if (viewModal) viewModal.hide();
+
+ // Reabrir modal de ver opciones actualizado
+ setTimeout(async () => {
+ const menusResponse = await window.whatsappManager.apiCall('get_menus.php');
+ if (menusResponse && menusResponse.success) {
+ const menu = menusResponse.data.find(m => m.id == menuId);
+ if (menu) {
+ showMenuOptionsModal(menu);
+ }
+ }
+ }, 500);
+ } else {
+ window.whatsappManager.showError(`Error agregando opción: ${response?.error || 'Error desconocido'}`);
+ }
+ } catch (error) {
+ console.error('Error:', error);
+ window.whatsappManager.showError('Error agregando opción: ' + error.message);
+ }
+};
+
+window.deleteMenuOption = async function(menuId, optionId) {
+ if (!confirm('¿Estás seguro de que deseas eliminar esta opción del menú?')) {
+ return;
+ }
+
+ console.log('Eliminando opción:', { menuId, optionId });
+
+ try {
+ const response = await window.whatsappManager.apiCall('delete_menu_option.php', {
+ method: 'POST',
+ body: {
+ menu_id: menuId,
+ option_id: optionId
+ }
+ });
+
+ if (response && response.success) {
+ window.whatsappManager.showSuccess('Opción eliminada correctamente');
+
+ // Cerrar modal actual
+ const viewModal = bootstrap.Modal.getInstance(document.getElementById('viewMenuOptionsModal'));
+ if (viewModal) viewModal.hide();
+
+ // Reabrir modal actualizado
+ setTimeout(async () => {
+ const menusResponse = await window.whatsappManager.apiCall('get_menus.php');
+ if (menusResponse && menusResponse.success) {
+ const menu = menusResponse.data.find(m => m.id == menuId);
+ if (menu) {
+ showMenuOptionsModal(menu);
+ }
+ }
+ }, 500);
+ } else {
+ window.whatsappManager.showError(`Error eliminando opción: ${response?.error || 'Error desconocido'}`);
+ }
+ } catch (error) {
+ console.error('Error:', error);
+ window.whatsappManager.showError('Error eliminando opción: ' + error.message);
+ }
+};
+
+window.testMenuPreview = async function(menuId) {
+ console.log('Mostrando vista previa del menú:', menuId);
+
+ try {
+ // Obtener datos del menú
+ const response = await window.whatsappManager.apiCall('get_menus.php');
+
+ if (response && response.success) {
+ const menu = response.data.find(m => m.id == menuId);
+
+ if (menu) {
+ showMenuPreviewModal(menu);
+ } else {
+ window.whatsappManager.showError('Menú no encontrado');
+ }
+ } else {
+ window.whatsappManager.showError('Error cargando datos del menú');
+ }
+ } catch (error) {
+ console.error('Error:', error);
+ window.whatsappManager.showError('Error mostrando vista previa: ' + error.message);
+ }
+};
+
+// Modal de vista previa del menú estilo WhatsApp
+function showMenuPreviewModal(menuData) {
+ const options = menuData.options || [];
+ const welcomeMsg = menuData.welcome_message || `¡Hola! Bienvenido al ${menuData.title || menuData.name}`;
+
+ // Construir el mensaje del menú como se vería en WhatsApp
+ let menuMessage = `📋 *${menuData.title || menuData.name}*\n\n`;
+
+ if (menuData.description) {
+ menuMessage += `${menuData.description}\n\n`;
+ }
+
+ if (options.length > 0) {
+ menuMessage += '*Opciones disponibles:*\n\n';
+ options.forEach(option => {
+ const key = option.option_key || option.key || '?';
+ const text = option.option_text || option.text || 'Sin texto';
+ menuMessage += `${key}. ${text}\n`;
+ });
+ menuMessage += '\n💬 *Responde con el número de la opción que deseas*';
+ } else {
+ menuMessage += '⚠️ _Este menú no tiene opciones configuradas_';
+ }
+
+ const modalHtml = `
+
+ `;
+
+ // Remover modal anterior si existe
+ const existingModal = document.getElementById('menuPreviewModal');
+ if (existingModal) {
+ existingModal.remove();
+ }
+
+ document.body.insertAdjacentHTML('beforeend', modalHtml);
+
+ const modal = new bootstrap.Modal(document.getElementById('menuPreviewModal'));
+ modal.show();
+}
+
+// Función para copiar el mensaje de vista previa
+window.copyMenuPreview = function(message) {
+ // Limpiar el mensaje de HTML
+ const cleanMessage = message.replace(/<[^>]*>/g, '');
+
+ navigator.clipboard.writeText(cleanMessage).then(() => {
+ window.whatsappManager.showSuccess('Mensaje copiado al portapapeles');
+ }).catch(err => {
+ window.whatsappManager.showError('Error al copiar: ' + err.message);
+ });
+};
+
+// Función para guardar nuevo menú desde el modal de index.php
+window.saveNewMenu = async function() {
+ const menuName = document.getElementById('menu-name')?.value?.trim();
+ const menuDescription = document.getElementById('menu-description')?.value?.trim();
+
+ if (!menuName) {
+ window.whatsappManager.showError('El nombre del menú es obligatorio');
+ return;
+ }
+
+ console.log('Guardando nuevo menú:', { menuName, menuDescription });
+
+ // Generar clave del menú basada en el nombre
+ const menuKey = menuName.toLowerCase()
+ .replace(/[^\w\s]/g, '') // Remover caracteres especiales
+ .replace(/\s+/g, '_') // Reemplazar espacios con guiones bajos
+ .substring(0, 20); // Limitar longitud
+
+ const menuData = {
+ name: menuName,
+ menu_key: menuKey,
+ description: menuDescription,
+ welcome_message: `¡Hola! Bienvenido al ${menuName}. Por favor selecciona una opción:`,
+ status: 'active',
+ options: [] // Sin opciones inicialmente
+ };
+
+ try {
+ const response = await window.whatsappManager.apiCall('save_menu.php', {
+ method: 'POST',
+ body: menuData
+ });
+
+ if (response && response.success) {
+ window.whatsappManager.showSuccess('Menú creado correctamente');
+
+ // Limpiar formulario
+ document.getElementById('menu-name').value = '';
+ document.getElementById('menu-description').value = '';
+
+ // Cerrar modal
+ const modal = bootstrap.Modal.getInstance(document.getElementById('createMenuModal'));
+ if (modal) modal.hide();
+
+ // Recargar lista de menús si existe la función
+ setTimeout(() => {
+ if (window.whatsappManager.loadMenus) {
+ window.whatsappManager.loadMenus();
+ }
+ if (typeof loadMenusTest === 'function') {
+ loadMenusTest();
+ }
+ }, 500);
+ } else {
+ window.whatsappManager.showError(`Error creando menú: ${response?.error || 'Error desconocido'}`);
+ }
+ } catch (error) {
+ console.error('Error:', error);
+ window.whatsappManager.showError('Error creando menú: ' + error.message);
+ }
+};
+
+// ========================= FUNCIONES GLOBALES ADICIONALES =========================
+
+// Función para refrescar datos del dashboard
+window.refreshData = function() {
+ console.log('Refrescando datos del dashboard');
+
+ if (window.whatsappManager) {
+ window.whatsappManager.showInfo('Actualizando datos...');
+
+ // Recargar todas las secciones
+ if (window.whatsappManager.loadUsers) {
+ window.whatsappManager.loadUsers();
+ }
+ if (window.whatsappManager.loadMenus) {
+ window.whatsappManager.loadMenus();
+ }
+ if (window.whatsappManager.loadTemplates) {
+ window.whatsappManager.loadTemplates();
+ }
+ if (window.whatsappManager.loadAutoResponses) {
+ window.whatsappManager.loadAutoResponses();
+ }
+
+ setTimeout(() => {
+ window.whatsappManager.showSuccess('Datos actualizados correctamente');
+ }, 1000);
+ }
+};
+
+// Función para exportar usuarios
+window.exportUsers = function() {
+ console.log('Exportando usuarios...');
+
+ if (window.whatsappManager) {
+ window.whatsappManager.showInfo('Preparando exportación de usuarios...');
+
+ // Realizar llamada a API de exportación
+ window.whatsappManager.apiCall('export_users.php')
+ .then(response => {
+ if (response && response.success) {
+ // Si la API devuelve datos para descargar
+ if (response.download_url) {
+ window.open(response.download_url, '_blank');
+ } else if (response.data) {
+ // Crear descarga directa de datos JSON
+ const dataStr = JSON.stringify(response.data, null, 2);
+ const dataBlob = new Blob([dataStr], { type: 'application/json' });
+ const url = URL.createObjectURL(dataBlob);
+
+ const link = document.createElement('a');
+ link.href = url;
+ link.download = `usuarios_export_${new Date().toISOString().split('T')[0]}.json`;
+ document.body.appendChild(link);
+ link.click();
+ document.body.removeChild(link);
+ URL.revokeObjectURL(url);
+ }
+
+ window.whatsappManager.showSuccess('Usuarios exportados correctamente');
+ } else {
+ window.whatsappManager.showError(`Error exportando usuarios: ${response?.error || 'Error desconocido'}`);
+ }
+ })
+ .catch(error => {
+ console.error('Error exportando usuarios:', error);
+ window.whatsappManager.showError('Error exportando usuarios: ' + error.message);
+ });
+ }
+};
+
+// Función para refrescar logs
+window.refreshLogs = function() {
+ console.log('Refrescando logs...');
+
+ const logsContainer = document.getElementById('logs-container');
+ if (logsContainer) {
+ logsContainer.innerHTML = ' Cargando logs...
';
+
+ // Simular carga de logs (aquí se debería hacer una llamada a la API real)
+ setTimeout(() => {
+ logsContainer.innerHTML = `
+
+ [${new Date().toLocaleString()}]
+ INFO: Logs actualizados correctamente
+
+
+ [${new Date().toLocaleString()}]
+ SUCCESS: Sistema funcionando normalmente
+
+ `;
+
+ if (window.whatsappManager) {
+ window.whatsappManager.showSuccess('Logs actualizados');
+ }
+ }, 1000);
+ }
+};
+
+// Función para limpiar logs
+window.clearLogs = function() {
+ console.log('Limpiando logs...');
+
+ if (confirm('¿Estás seguro de que quieres limpiar todos los logs? Esta acción no se puede deshacer.')) {
+ const logsContainer = document.getElementById('logs-container');
+ if (logsContainer) {
+ logsContainer.innerHTML = ' Logs limpiados
';
+
+ if (window.whatsappManager) {
+ window.whatsappManager.showInfo('Logs limpiados correctamente');
+ }
+ }
+ }
+};
\ No newline at end of file
diff --git a/classes/Database.php b/classes/Database.php
index 02e2f17..40f9ba5 100644
--- a/classes/Database.php
+++ b/classes/Database.php
@@ -110,7 +110,15 @@ class Database {
}
public function rollback() {
- return $this->pdo->rollback();
+ try {
+ return $this->pdo->rollback();
+ } catch (PDOException $e) {
+ // Si no hay transacción activa, simplemente ignorar
+ if ($e->getCode() !== 'HY000' || strpos($e->getMessage(), 'no active transaction') === false) {
+ throw $e;
+ }
+ return false;
+ }
}
public function execute($sql, $params = []) {