/** * WhatsApp Bot Manager - JavaScript * Fecha: 13 de noviembre de 2025 */ class WhatsAppBotManager { constructor() { this.apiBaseUrl = './api/'; this.currentTab = 'dashboard'; this.currentMenuId = null; this.charts = {}; this.init(); } init() { this.setupEventListeners(); this.loadDashboard(); this.setupCharts(); // Auto-refresh cada 30 segundos setInterval(() => { if (this.currentTab === 'dashboard') { this.refreshStats(); } }, 30000); // Verificar sesión cada 5 minutos setInterval(() => { this.checkSession(); }, 300000); // 5 minutos } setupEventListeners() { // Navegación de tabs - solo enlaces que tengan data-tab document.querySelectorAll('.nav-link[data-tab]').forEach(link => { link.addEventListener('click', (e) => { e.preventDefault(); const tabName = link.getAttribute('data-tab'); if (tabName) { this.showTab(tabName); } }); }); // Formularios this.setupFormListeners(); // Búsquedas this.setupSearchListeners(); } setupFormListeners() { // Formulario de configuración const system_configForm = document.getElementById('system_config-form'); if (system_configForm) { system_configForm.addEventListener('submit', (e) => { e.preventDefault(); this.savesystem_config(); }); } // Formulario de menú const menuForm = document.getElementById('menu-form'); if (menuForm) { menuForm.addEventListener('submit', (e) => { e.preventDefault(); this.saveMenu(); }); } // Formulario de envío de mensaje const sendMessageForm = document.getElementById('send-message-form'); if (sendMessageForm) { sendMessageForm.addEventListener('submit', (e) => { e.preventDefault(); this.sendMessage(); }); } // Formulario de mensaje masivo const broadcastForm = document.getElementById('broadcast-form'); if (broadcastForm) { broadcastForm.addEventListener('submit', (e) => { e.preventDefault(); this.sendBroadcast(); }); } // Contador de caracteres para broadcast const broadcastMessage = document.getElementById('broadcast-message'); const charCount = document.getElementById('char-count'); if (broadcastMessage && charCount) { broadcastMessage.addEventListener('input', (e) => { charCount.textContent = e.target.value.length; }); } // Cambio de tipo de mensaje const messageType = document.getElementById('message-type'); if (messageType) { messageType.addEventListener('change', () => { this.toggleMessageFields(); }); } } setupSearchListeners() { // Búsqueda de conversaciones const searchConversations = document.getElementById('search-conversations'); if (searchConversations) { searchConversations.addEventListener('input', (e) => { this.searchConversations(e.target.value); }); } } showTab(tabName) { // Ocultar todas las pestañas document.querySelectorAll('.tab-content').forEach(tab => { tab.classList.remove('active'); }); // Remover clase active de navegación document.querySelectorAll('.nav-link').forEach(link => { link.classList.remove('active'); }); // Mostrar pestaña seleccionada const targetTab = document.getElementById(tabName); if (targetTab) { targetTab.classList.add('active'); } // Activar enlace de navegación const activeLink = document.querySelector(`[data-tab="${tabName}"]`); if (activeLink) { activeLink.classList.add('active'); } this.currentTab = tabName; // Cargar datos específicos del tab this.loadTabData(tabName); } loadTabData(tabName) { switch (tabName) { case 'dashboard': this.loadDashboard(); break; case 'conversations': this.loadConversations(); break; case 'users': this.loadUsers(); break; case 'menus': this.loadMenus(); break; case 'send_message': // Cargar lista de usuarios y plantillas cuando se muestra el tab de Enviar Mensaje this.loadMessageUsers(); break; case 'templates': this.loadTemplates(); break; case 'autoresponses': this.loadAutoResponses(); break; case 'system_config': this.loadsystem_config(); break; case 'logs': this.loadLogs(); break; } } async loadDashboard() { try { const stats = await this.apiCall('get_stats.php'); this.updateStats(stats); const recentconversationsResponse = await this.apiCall('get_recent_messages.php'); if (recentconversationsResponse && recentconversationsResponse.success && Array.isArray(recentconversationsResponse.data)) { this.updateRecentconversations(recentconversationsResponse.data); } else if (recentconversationsResponse && Array.isArray(recentconversationsResponse)) { // Retrocompatibilidad por si la API devuelve directamente el array this.updateRecentconversations(recentconversationsResponse); } this.updateChart(); } catch (error) { this.showError('Error cargando dashboard: ' + error.message); } } updateStats(stats) { if (stats) { document.getElementById('total-users').textContent = stats.total_users || 0; document.getElementById('conversations-today').textContent = stats.conversations_today || 0; document.getElementById('active-users').textContent = stats.active_users || 0; document.getElementById('total-conversations').textContent = stats.total_conversations || 0; // Actualizar badge de conversaciones en el menú lateral (usar mensajes pendientes si existe) const convBadge = document.getElementById('conversations-count'); if (convBadge) { const val = (typeof stats.unread_messages !== 'undefined') ? stats.unread_messages : (stats.total_conversations || 0); convBadge.textContent = val; convBadge.style.display = val > 0 ? 'inline-block' : 'none'; } } } updateRecentconversations(conversations) { const container = document.getElementById('recent-conversations'); if (!container) return; container.innerHTML = ''; if (conversations && conversations.length > 0) { conversations.forEach(message => { const messageElement = this.createRecentMessageElement(message); container.appendChild(messageElement); }); } else { container.innerHTML = '
No hay mensajes recientes
'; } } createRecentMessageElement(message) { const div = document.createElement('div'); div.className = 'recent-message'; const direction = message.direction === 'incoming' ? '📥' : '📤'; const time = new Date(message.created_at).toLocaleString(); div.innerHTML = `
${direction} ${message.phone_number} ${time}
${this.truncateText(message.content, 50)}
`; return div; } setupCharts() { const ctx = document.getElementById('conversationsChart'); if (ctx) { this.charts.conversations = new Chart(ctx, { type: 'line', data: { labels: [], datasets: [{ label: 'Mensajes', data: [], borderColor: '#25d366', backgroundColor: 'rgba(37, 211, 102, 0.1)', borderWidth: 3, fill: true, tension: 0.4 }] }, options: { responsive: true, maintainAspectRatio: false, scales: { y: { beginAtZero: true, grid: { color: 'rgba(0,0,0,0.1)' } }, x: { grid: { color: 'rgba(0,0,0,0.1)' } } }, plugins: { legend: { display: false } } } }); } } async updateChart() { try { const chartData = await this.apiCall('get_chart_data.php'); if (chartData && this.charts.conversations) { this.charts.conversations.data.labels = chartData.labels; this.charts.conversations.data.datasets[0].data = chartData.data; this.charts.conversations.update(); } } catch (error) { console.error('Error updating chart:', error); } } async loadConversations() { try { const response = await this.apiCall('get_conversations.php'); console.log('Respuesta get_conversations:', response); // Debug if (response && response.success && Array.isArray(response.data)) { this.updateConversationsTable(response.data); } else if (response && Array.isArray(response)) { // Retrocompatibilidad por si la API devuelve directamente el array this.updateConversationsTable(response); } else { console.error('Formato de respuesta inesperado:', response); this.showError('Error: formato de datos inválido para conversaciones'); } } catch (error) { console.error('Error en loadConversations:', error); this.showError('Error cargando conversaciones: ' + error.message); } } updateConversationsTable(conversations) { const tbody = document.getElementById('conversations-table'); if (!tbody) return; tbody.innerHTML = ''; console.log('Datos para tabla de conversaciones:', conversations); // Debug if (!Array.isArray(conversations)) { console.error('conversations no es un array:', typeof conversations, conversations); tbody.innerHTML = 'Error: datos inválidos'; return; } if (conversations.length === 0) { tbody.innerHTML = 'No hay conversaciones'; return; } conversations.forEach(conv => { const row = this.createConversationRow(conv); tbody.appendChild(row); }); } createConversationRow(conv) { const tr = document.createElement('tr'); const attentionClass = conv.advisor_requested ? 'table-attention' : ''; tr.className = attentionClass; const attentionBadge = conv.advisor_requested ? `¡Atención!` : ''; tr.innerHTML = `
${conv.phone_number} ${attentionBadge}
${conv.name || 'Sin nombre'} ${this.truncateText(conv.last_message, 50)} ${conv.message_type} ${conv.status} ${new Date(conv.last_activity).toLocaleString()} `; return tr; } async loadUsers() { try { const response = await this.apiCall('get_users.php'); if (response && response.success && Array.isArray(response.data)) { this.updateUsersTable(response.data); } else if (response && Array.isArray(response)) { // Retrocompatibilidad por si la API devuelve directamente el array this.updateUsersTable(response); } else { this.showError('Error: formato de datos inválido para usuarios'); } } catch (error) { this.showError('Error cargando usuarios: ' + error.message); } } updateUsersTable(users) { const tbody = document.getElementById('users-table'); if (!tbody) return; tbody.innerHTML = ''; users.forEach(user => { const row = this.createUserRow(user); tbody.appendChild(row); }); } createUserRow(user) { const tr = document.createElement('tr'); tr.innerHTML = ` ${user.id} ${user.phone_number} ${user.name || 'Sin nombre'} ${user.status} ${user.current_menu || 'Ninguno'} ${new Date(user.created_at).toLocaleString()} `; return tr; } async loadMenus() { try { const response = await this.apiCall('get_menus.php'); let menus = response; if (response && response.success && Array.isArray(response.data)) { menus = response.data; } else if (!Array.isArray(response)) { this.showError('Error: formato de datos inválido para menús'); return; } this.updateMenusTree(menus); this.loadMenuParents(menus); } catch (error) { this.showError('Error cargando menús: ' + error.message); } } updateMenusTree(menus) { const container = document.getElementById('menus-tree'); if (!container) return; container.innerHTML = ''; // Organizar menús por jerarquía const rootMenus = menus.filter(menu => menu.is_root); rootMenus.forEach(menu => { const menuElement = this.createMenuElement(menu, menus); container.appendChild(menuElement); }); } createMenuElement(menu, allMenus) { const div = document.createElement('div'); div.className = 'menu-item ' + (menu.is_root ? 'root' : 'child'); div.innerHTML = `
${menu.title}
${menu.description || 'Sin descripción'}
${menu.is_active ? 'Activo' : 'Inactivo'}
`; // Agregar submenús const childMenus = allMenus.filter(child => child.parent_id === menu.id); if (childMenus.length > 0) { const childContainer = document.createElement('div'); childContainer.style.marginLeft = '20px'; childContainer.style.marginTop = '10px'; childMenus.forEach(child => { const childElement = this.createMenuElement(child, allMenus); childContainer.appendChild(childElement); }); div.appendChild(childContainer); } return div; } loadMenuParents(menus) { const select = document.getElementById('menu-parent'); if (!select) return; select.innerHTML = ''; menus.forEach(menu => { const option = document.createElement('option'); option.value = menu.id; option.textContent = menu.title; select.appendChild(option); }); } async loadMessageUsers() { const userSelect = document.getElementById('message-recipient'); const templateSelect = document.getElementById('message-template'); // Mostrar estados de carga if (userSelect) userSelect.innerHTML = ''; if (templateSelect) templateSelect.innerHTML = ''; try { const usersResponse = await this.apiCall('get_users.php'); const templatesResponse = await this.apiCall('get_templates.php'); let users = []; if (usersResponse && usersResponse.success && Array.isArray(usersResponse.data)) { users = usersResponse.data; } else if (Array.isArray(usersResponse)) { users = usersResponse; } let templates = []; if (templatesResponse && templatesResponse.success && Array.isArray(templatesResponse.data)) { templates = templatesResponse.data; } else if (Array.isArray(templatesResponse)) { templates = templatesResponse; } console.log('loadMessageUsers - users:', users.length, 'templates:', templates.length); // Actualizar selects this.updateMessageUserSelect(users); this.updateTemplateSelect(templates); } catch (error) { console.error('Error loadMessageUsers:', error); if (userSelect) userSelect.innerHTML = ''; if (templateSelect) templateSelect.innerHTML = ''; this.showError('Error cargando usuarios o plantillas: ' + (error.message || error)); } } updateMessageUserSelect(users) { const select = document.getElementById('message-recipient'); if (!select) return; select.innerHTML = ''; users.forEach(user => { const option = document.createElement('option'); option.value = user.phone_number; option.textContent = `${user.phone_number} ${user.name ? '(' + user.name + ')' : ''}`; select.appendChild(option); }); } updateTemplateSelect(templates) { const select = document.getElementById('message-template'); if (!select) return; select.innerHTML = ''; templates.forEach(template => { if (template.status === 'approved') { const option = document.createElement('option'); option.value = template.template_name; option.textContent = template.name; select.appendChild(option); } }); } toggleMessageFields() { const messageType = document.getElementById('message-type').value; const textGroup = document.getElementById('message-text-group'); const templateGroup = document.getElementById('template-group'); if (messageType === 'template') { textGroup.style.display = 'none'; templateGroup.style.display = 'block'; } else { textGroup.style.display = 'block'; templateGroup.style.display = 'none'; } } async sendMessage() { const recipientType = document.getElementById('recipient-type').value; const messageType = document.getElementById('message-type').value; let recipient; if (recipientType === 'existing') { recipient = document.getElementById('message-recipient').value; } else { recipient = document.getElementById('manual-recipient').value.trim(); // Para números manuales, solo permitir plantillas if (messageType !== 'template') { this.showError('Para números manuales solo se pueden enviar plantillas aprobadas'); return; } } const messageText = document.getElementById('message-text').value; const template = document.getElementById('message-template').value; if (!recipient) { this.showError('Por favor ingresa un destinatario'); return; } // Validar formato de número manual if (recipientType === 'manual') { const phoneRegex = /^\+\d{10,15}$/; if (!phoneRegex.test(recipient)) { this.showError('El número debe tener el formato: +57XXXXXXXXX (10-15 dígitos)'); return; } } const data = { recipient: recipient, type: messageType }; if (messageType === 'text') { if (!messageText) { this.showError('Por favor escribe un mensaje'); return; } data.message = messageText; } else if (messageType === 'template') { if (!template) { this.showError('Por favor selecciona una plantilla'); return; } data.template = template; } try { this.showLoading('Enviando mensaje...'); const result = await this.apiCall('send_message.php', 'POST', data); if (result.success) { this.showSuccess('Mensaje enviado correctamente'); document.getElementById('send-message-form').reset(); // Resetear la interfaz this.resetMessageForm(); // Actualizar conversaciones si es necesario if (this.currentTab === 'conversations') { this.loadConversations(); } } else { this.showError(result.error || 'Error enviando mensaje'); } } catch (error) { this.showError('Error enviando mensaje: ' + error.message); } finally { this.hideLoading(); } } resetMessageForm() { document.getElementById('recipient-type').value = 'existing'; document.getElementById('message-type').value = 'text'; toggleRecipientType(); toggleMessageType(); } } else { this.showError(result.error || 'Error enviando mensaje'); } } catch (error) { this.showError('Error enviando mensaje: ' + error.message); } finally { this.hideLoading(); } } async sendBroadcast() { const selectionType = document.getElementById('broadcast-selection-type').value; const messageType = document.getElementById('broadcast-message-type').value; let payload = { selection_type: selectionType, message_type: messageType }; // Validar según tipo de selección if (selectionType === 'filter') { payload.filter = document.getElementById('broadcast-filter').value; } else { const selectedUsers = $('#broadcast-users').val(); if (!selectedUsers || selectedUsers.length === 0) { this.showError('Debes seleccionar al menos un usuario'); return; } payload.user_ids = selectedUsers; } // Validar según tipo de mensaje if (messageType === 'text') { const message = document.getElementById('broadcast-message').value; if (!message.trim()) { this.showError('El mensaje no puede estar vacío'); return; } payload.message = message; } else { const templateId = document.getElementById('broadcast-template').value; if (!templateId) { this.showError('Debes seleccionar una plantilla'); return; } payload.template_id = templateId; // Recoger variables const variables = {}; document.querySelectorAll('.broadcast-template-var').forEach(input => { variables[input.dataset.var] = input.value; }); payload.template_variables = variables; } // Confirmar envío const targetText = selectionType === 'filter' ? `usuarios filtrados por "${payload.filter}"` : `${payload.user_ids.length} usuario(s) seleccionado(s)`; const messageText = messageType === 'text' ? 'mensaje de texto' : 'plantilla'; if (!confirm(`¿Enviar este ${messageText} a ${targetText}?`)) { return; } try { this.showLoading('Enviando mensajes masivos...'); const result = await this.apiCall('send_broadcast.php', 'POST', payload); if (result.success) { this.showSuccess(`✅ Mensajes enviados exitosamente!\nEnviados: ${result.sent_count}\nErrores: ${result.error_count}\nTotal usuarios: ${result.total_users}`); document.getElementById('broadcast-form').reset(); $('#broadcast-users').val(null).trigger('change'); document.getElementById('broadcast-template-variables').style.display = 'none'; document.getElementById('broadcast-template-preview').style.display = 'none'; } else { this.showError(result.error || 'Error enviando broadcast'); } } catch (error) { this.showError('Error enviando broadcast: ' + error.message); } finally { this.hideLoading(); } } async saveMenu() { const formData = { name: document.getElementById('menu-name').value, title: document.getElementById('menu-title').value, description: document.getElementById('menu-description').value, parent_id: document.getElementById('menu-parent').value || null, is_active: document.getElementById('menu-active').checked ? 1 : 0, menu_type: document.getElementById('menu-type')?.value || 'main' }; try { this.showLoading('Guardando menú...'); const result = await this.apiCall('save_menu.php', 'POST', formData); if (result.success) { this.showSuccess('Menú guardado correctamente'); document.getElementById('menu-form').reset(); this.loadMenus(); } else { this.showError(result.error || 'Error guardando menú'); } } catch (error) { this.showError('Error guardando menú: ' + error.message); } finally { this.hideLoading(); } } async savesystem_config() { const formData = { whatsapp_token: document.getElementById('whatsapp-token').value, phone_number_id: document.getElementById('phone-number-id').value, webhook_token: document.getElementById('webhook-token').value, business_name: document.getElementById('business-name').value, welcome_message: document.getElementById('welcome-message').value }; try { this.showLoading('Guardando configuración...'); const result = await this.apiCall('save_system_config.php', 'POST', formData); if (result.success) { this.showSuccess('Configuración guardada correctamente'); } else { this.showError(result.error || 'Error guardando configuración'); } } catch (error) { this.showError('Error guardando configuración: ' + error.message); } finally { this.hideLoading(); } } async loadsystem_config() { try { const system_config = await this.apiCall('get_system_config.php'); if (system_config) { document.getElementById('whatsapp-token').value = system_config.whatsapp_token || ''; document.getElementById('phone-number-id').value = system_config.phone_number_id || ''; document.getElementById('webhook-token').value = system_config.webhook_token || ''; document.getElementById('business-name').value = system_config.business_name || ''; document.getElementById('welcome-message').value = system_config.welcome_message || ''; } } catch (error) { this.showError('Error cargando configuración: ' + error.message); } } async loadLogs() { try { const response = await this.apiCall('get_logs.php'); if (response && response.success && Array.isArray(response.data)) { this.updateLogsTable(response.data); } else if (response && Array.isArray(response)) { // Retrocompatibilidad por si la API devuelve directamente el array this.updateLogsTable(response); } else { this.showError('Error: formato de datos inválido para logs'); } } catch (error) { this.showError('Error cargando logs: ' + error.message); } } updateLogsTable(logs) { const tbody = document.getElementById('logs-table'); if (!tbody) return; tbody.innerHTML = ''; logs.forEach(log => { const row = this.createLogRow(log); tbody.appendChild(row); }); } createLogRow(log) { const tr = document.createElement('tr'); tr.innerHTML = ` ${new Date(log.created_at).toLocaleString()} ${log.ip_address} ${log.status_code} `; return tr; } // Utility methods async apiCall(endpoint, method = 'GET', data = null) { const options = { method: method, headers: { 'Content-Type': 'application/json', 'X-Requested-With': 'XMLHttpRequest' // Para identificar peticiones AJAX } }; if (data && method !== 'GET') { options.body = JSON.stringify(data); } try { const response = await fetch(this.apiBaseUrl + endpoint, options); // Verificar si es error de autenticación (401) if (response.status === 401) { try { const errorData = await response.json(); if (errorData.redirect) { this.showError(errorData.error || 'Sesión expirada. Redirigiendo al login...'); setTimeout(() => { window.location.href = errorData.redirect; }, 2000); return null; } } catch (e) { // Si no puede parsear JSON, redirigir al login this.showError('Sesión expirada. Redirigiendo al login...'); setTimeout(() => { window.location.href = 'login.php'; }, 2000); return null; } } if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } const result = await response.json(); // Verificar si la respuesta indica problemas de autenticación if (result.success === false && result.redirect) { this.showError(result.error || 'Sesión expirada. Redirigiendo al login...'); setTimeout(() => { window.location.href = result.redirect; }, 2000); return null; } return result; } catch (error) { console.error('Error en apiCall:', error); throw error; } } showError(message) { this.showNotification(message, 'error'); } showSuccess(message) { this.showNotification(message, 'success'); } showWarning(message) { this.showNotification(message, 'warning'); } showInfo(message) { this.showNotification(message, 'info'); } showNotification(message, type = 'info') { // Crear notificación toast const toast = document.createElement('div'); toast.className = `alert alert-${type} position-fixed`; toast.style.top = '20px'; toast.style.right = '20px'; toast.style.zIndex = '9999'; toast.style.minWidth = '300px'; toast.innerHTML = ` ${message} `; document.body.appendChild(toast); // Auto-remove después de 5 segundos setTimeout(() => { if (toast.parentElement) { toast.remove(); } }, 5000); } getNotificationIcon(type) { const icons = { success: 'check-circle', error: 'exclamation-circle', warning: 'exclamation-triangle', info: 'info-circle' }; return icons[type] || 'info-circle'; } showLoading(message = 'Cargando...') { const loading = document.createElement('div'); loading.id = 'loading-overlay'; loading.className = 'position-fixed w-100 h-100 d-flex align-items-center justify-content-center'; loading.style.top = '0'; loading.style.left = '0'; loading.style.backgroundColor = 'rgba(0,0,0,0.5)'; loading.style.zIndex = '9999'; loading.innerHTML = `
${message}
`; document.body.appendChild(loading); } hideLoading() { const loading = document.getElementById('loading-overlay'); if (loading) { loading.remove(); } } truncateText(text, length) { if (!text) return ''; return text.length > length ? text.substring(0, length) + '...' : text; } getStatusColor(status) { const colors = { 'active': 'success', 'inactive': 'secondary', 'blocked': 'danger', 'sent': 'info', 'delivered': 'success', 'read': 'success', 'failed': 'danger' }; return colors[status] || 'secondary'; } getMessageTypeColor(type) { const colors = { 'text': 'primary', 'image': 'info', 'audio': 'warning', 'video': 'danger', 'document': 'secondary', 'template': 'success' }; return colors[type] || 'primary'; } // Funciones específicas searchConversations(query) { // Función de búsqueda de conversaciones const tbody = document.getElementById('conversations-table'); if (!tbody) return; const rows = tbody.getElementsByTagName('tr'); const searchTerm = query.toLowerCase(); for (let row of rows) { const phoneNumber = row.cells[0]?.textContent?.toLowerCase() || ''; const lastMessage = row.cells[1]?.textContent?.toLowerCase() || ''; if (phoneNumber.includes(searchTerm) || lastMessage.includes(searchTerm)) { row.style.display = ''; } else { row.style.display = 'none'; } } } refreshData() { this.loadTabData(this.currentTab); this.showSuccess('Datos actualizados'); } refreshStats() { this.loadDashboard(); } exportUsers() { window.open(this.apiBaseUrl + 'export_users.php', '_blank'); } viewConversation(userId) { // Implementar modal o página de conversación console.log('Ver conversación del usuario:', userId); } replyToUser(phoneNumber) { document.getElementById('message-recipient').value = phoneNumber; this.showTab('conversations'); } replyToMessage(messageId, userPhone) { // Poner en el input del chat y almacenar reply_to en data attribute document.getElementById('message-input').focus(); document.getElementById('message-input').dataset.replyTo = messageId; this.showSuccess('Preparado para responder al mensaje ' + messageId); } async reactToMessage(messageId, userPhone) { const emoji = prompt('Escribe el emoji de reacción (ej: ❤️, 👍):'); if (!emoji) return; try { const resp = await fetch('api/send_reaction.php', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ to: userPhone, message_id: messageId, emoji: emoji }) }); const data = await resp.json(); if (data.success) { this.showSuccess('Reacción enviada'); // Refrescar mensajes await this.loadconversations(this.currentUserId, false); } else { this.showError('Error enviando reacción: ' + (data.error || 'desconocido')); } } catch (err) { console.error('reactToMessage error', err); this.showError('Error comunicándose con el servidor'); } } editUser(userId) { // Implementar modal de edición de usuario console.log('Editar usuario:', userId); } blockUser(userId) { if (confirm('¿Estás seguro de bloquear este usuario?')) { // Implementar bloqueo de usuario console.log('Bloquear usuario:', userId); } } editMenu(menuId) { this.currentMenuId = menuId; // Cargar datos del menú en el formulario console.log('Editar menú:', menuId); } deleteMenu(menuId) { if (confirm('¿Estás seguro de eliminar este menú?')) { // Implementar eliminación console.log('Eliminar menú:', menuId); } } manageMenuOptions(menuId) { // Mostrar modal de opciones de menú console.log('Gestionar opciones del menú:', menuId); } viewLogDetails(logId, type) { // Mostrar detalles del log en modal console.log('Ver detalles del log:', logId, type); } clearLogs() { if (confirm('¿Estás seguro de eliminar todos los logs?')) { // Implementar limpieza de logs console.log('Limpiar logs'); } } refreshLogs() { this.loadLogs(); } async loadTemplates() { try { const response = await this.apiCall('get_templates.php'); let templates = []; if (response && response.success && Array.isArray(response.data)) { templates = response.data; } else if (response && Array.isArray(response)) { // Retrocompatibilidad por si la API devuelve directamente el array templates = response; } else { this.showError('Error: formato de datos inválido para plantillas'); return; } // Guardar en memoria para usos posteriores (ej. confirmaciones más amigables) this.templates = templates; this.updateTemplatesTable(templates); } catch (error) { this.showError('Error cargando plantillas: ' + error.message); } } updateTemplatesTable(templates) { const tbody = document.getElementById('templates-table'); if (!tbody) return; tbody.innerHTML = ''; if (templates && templates.length > 0) { templates.forEach(template => { const row = this.createTemplateRow(template); tbody.appendChild(row); }); } else { tbody.innerHTML = 'No hay plantillas registradas'; } } createTemplateRow(template) { const tr = document.createElement('tr'); const statusBadge = this.getStatusBadge(template.status); tr.innerHTML = ` ${template.name} ${template.template_name} ${template.language_code.toUpperCase()} ${template.category} ${statusBadge}
`; return tr; } getStatusBadge(status) { const badges = { 'approved': 'Aprobada', 'pending': 'Pendiente', 'rejected': 'Rechazada' }; return badges[status] || 'Desconocido'; } async saveTemplate() { const formData = { name: document.getElementById('template-name').value.trim(), template_name: document.getElementById('template-whatsapp-name').value.trim(), language_code: document.getElementById('template-language').value, category: document.getElementById('template-category').value }; if (!formData.name || !formData.template_name) { this.showError('Por favor complete todos los campos requeridos'); return; } try { this.showLoading('Guardando plantilla...'); const result = await this.apiCall('save_template.php', 'POST', formData); if (result.success) { this.showSuccess(result.message); document.getElementById('template-form').reset(); // Cerrar modal const modal = bootstrap.Modal.getInstance(document.getElementById('createTemplateModal')); modal.hide(); // Recargar plantillas this.loadTemplates(); } else { this.showError(result.error || 'Error guardando plantilla'); } } catch (error) { this.showError('Error guardando plantilla: ' + error.message); } finally { this.hideLoading(); } } async editTemplate(templateId) { // Por ahora, solo permitir cambiar el estado const status = prompt('Cambiar estado de la plantilla:\n- pending (Pendiente)\n- approved (Aprobada)\n- rejected (Rechazada)', 'approved'); if (!status || !['pending', 'approved', 'rejected'].includes(status)) { return; } try { this.showLoading('Actualizando plantilla...'); const result = await this.apiCall('update_template_status.php', 'POST', { id: templateId, status: status }); if (result.success) { this.showSuccess(result.message); this.loadTemplates(); } else { this.showError(result.error || 'Error actualizando plantilla'); } } catch (error) { this.showError('Error actualizando plantilla: ' + error.message); } finally { this.hideLoading(); } } async deleteTemplate(templateId) { const tmpl = (this.templates || []).find(t => t.id === templateId) || {}; const name = tmpl.name || tmpl.template_name || `ID ${templateId}`; if (!confirm(`¿Estás seguro de que deseas eliminar la plantilla "${name}"? Esta acción no se puede deshacer.`)) { return; } try { this.showLoading('Eliminando plantilla...'); const result = await this.apiCall('delete_template.php', 'POST', { id: templateId }); if (result && result.success) { this.showSuccess('Plantilla eliminada'); this.loadTemplates(); } else { this.showError(result.error || 'Error eliminando plantilla'); } } catch (error) { this.showError('Error eliminando plantilla: ' + error.message); } finally { this.hideLoading(); } } // Método para verificar el estado de la sesión async checkSession() { try { const result = await this.apiCall('get_stats.php'); // Si la llamada es exitosa, la sesión está activa if (!result) { // La función apiCall ya manejó la redirección al login return false; } return true; } catch (error) { console.warn('Error verificando sesión:', error); // No mostrar error, solo en caso de problemas de red return false; } } } // Inicializar la aplicación const app = new WhatsAppBotManager(); // Funciones globales - usando function declarations para evitar problemas con SES window.refreshData = function() { if (typeof app !== 'undefined') app.refreshData(); }; window.exportUsers = function() { if (typeof app !== 'undefined') app.exportUsers(); }; window.refreshLogs = function() { if (typeof app !== 'undefined') app.refreshLogs(); }; window.clearLogs = function() { if (typeof app !== 'undefined') app.clearLogs(); }; // Las funciones de modales se definen ahora en el HTML para evitar problemas con SES // window.showCreateTemplateModal y window.showCreateMenuModal están en index.php window.toggleRecipientType = () => { const recipientType = document.getElementById('recipient-type').value; const existingGroup = document.getElementById('existing-recipient-group'); const manualGroup = document.getElementById('manual-recipient-group'); const messageTypeSelect = document.getElementById('message-type'); if (recipientType === 'existing') { existingGroup.style.display = 'block'; manualGroup.style.display = 'none'; // Habilitar todas las opciones messageTypeSelect.innerHTML = ` `; } else { existingGroup.style.display = 'none'; manualGroup.style.display = 'block'; // Solo permitir plantillas para números manuales messageTypeSelect.innerHTML = ` `; messageTypeSelect.value = 'template'; toggleMessageType(); // Actualizar la interfaz } }; // Funciones movidas al HTML inline para evitar problemas con SES // window.showCreateMenuModal y window.saveNewMenu están ahora en index.php window.toggleMessageType = function() { const messageType = document.getElementById('message-type').value; const textGroup = document.getElementById('message-text-group'); const templateGroup = document.getElementById('template-group'); if (messageType === 'text') { textGroup.style.display = 'block'; templateGroup.style.display = 'none'; } else { textGroup.style.display = 'none'; templateGroup.style.display = 'block'; } };