/** * 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); } setupEventListeners() { // Navegación de tabs document.querySelectorAll('.nav-link').forEach(link => { link.addEventListener('click', (e) => { e.preventDefault(); const tabName = link.getAttribute('data-tab'); this.showTab(tabName); }); }); // Formularios this.setupFormListeners(); // Búsquedas this.setupSearchListeners(); } setupFormListeners() { // Formulario de configuración const settingsForm = document.getElementById('settings-form'); if (settingsForm) { settingsForm.addEventListener('submit', (e) => { e.preventDefault(); this.saveSettings(); }); } // 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(); }); } // 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 'messages': this.loadMessageUsers(); break; case 'templates': this.loadTemplates(); break; case 'autoresponses': this.loadAutoResponses(); break; case 'settings': this.loadSettings(); break; case 'logs': this.loadLogs(); break; } } async loadDashboard() { try { const stats = await this.apiCall('get_stats.php'); this.updateStats(stats); const recentMessages = await this.apiCall('get_recent_messages.php'); this.updateRecentMessages(recentMessages); 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('messages-today').textContent = stats.messages_today || 0; document.getElementById('active-users').textContent = stats.active_users || 0; document.getElementById('total-messages').textContent = stats.total_messages || 0; } } updateRecentMessages(messages) { const container = document.getElementById('recent-messages'); if (!container) return; container.innerHTML = ''; if (messages && messages.length > 0) { messages.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('messagesChart'); if (ctx) { this.charts.messages = 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.messages) { this.charts.messages.data.labels = chartData.labels; this.charts.messages.data.datasets[0].data = chartData.data; this.charts.messages.update(); } } catch (error) { console.error('Error updating chart:', error); } } async loadConversations() { try { const conversations = await this.apiCall('get_conversations.php'); this.updateConversationsTable(conversations); } catch (error) { this.showError('Error cargando conversaciones: ' + error.message); } } updateConversationsTable(conversations) { const tbody = document.getElementById('conversations-table'); if (!tbody) return; tbody.innerHTML = ''; conversations.forEach(conv => { const row = this.createConversationRow(conv); tbody.appendChild(row); }); } createConversationRow(conv) { const tr = document.createElement('tr'); tr.innerHTML = `
${conv.phone_number}
${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 users = await this.apiCall('get_users.php'); this.updateUsersTable(users); } 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 menus = await this.apiCall('get_menus.php'); 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() { try { const users = await this.apiCall('get_users.php'); const templates = await this.apiCall('get_templates.php'); this.updateMessageUserSelect(users); this.updateTemplateSelect(templates); } catch (error) { this.showError('Error cargando datos de mensaje: ' + error.message); } } 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 recipient = document.getElementById('message-recipient').value; const messageType = document.getElementById('message-type').value; const messageText = document.getElementById('message-text').value; const template = document.getElementById('message-template').value; if (!recipient) { this.showError('Por favor selecciona un destinatario'); 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(); } else { this.showError(result.error || 'Error enviando mensaje'); } } catch (error) { this.showError('Error enviando mensaje: ' + error.message); } finally { this.hideLoading(); } } async sendBroadcast() { const filter = document.getElementById('broadcast-filter').value; const message = document.getElementById('broadcast-message').value; if (!message) { this.showError('Por favor escribe un mensaje'); return; } if (!confirm('¿Estás seguro de enviar este mensaje a múltiples usuarios?')) { return; } try { this.showLoading('Enviando mensajes...'); const result = await this.apiCall('send_broadcast.php', 'POST', { filter: filter, message: message }); if (result.success) { this.showSuccess(`Mensajes enviados a ${result.sent_count} usuarios`); document.getElementById('broadcast-form').reset(); } else { this.showError(result.error || 'Error en envío masivo'); } } catch (error) { this.showError('Error en envío masivo: ' + 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 }; 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 saveSettings() { 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_settings.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 loadSettings() { try { const settings = await this.apiCall('get_settings.php'); if (settings) { document.getElementById('whatsapp-token').value = settings.whatsapp_token || ''; document.getElementById('phone-number-id').value = settings.phone_number_id || ''; document.getElementById('webhook-token').value = settings.webhook_token || ''; document.getElementById('business-name').value = settings.business_name || ''; document.getElementById('welcome-message').value = settings.welcome_message || ''; } } catch (error) { this.showError('Error cargando configuración: ' + error.message); } } async loadLogs() { try { const logs = await this.apiCall('get_logs.php'); this.updateLogsTable(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', } }; if (data && method !== 'GET') { options.body = JSON.stringify(data); } const response = await fetch(this.apiBaseUrl + endpoint, options); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } return await response.json(); } 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 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('messages'); } 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(); } } // Inicializar la aplicación const app = new WhatsAppBotManager(); // Funciones globales window.refreshData = () => app.refreshData(); window.exportUsers = () => app.exportUsers(); window.refreshLogs = () => app.refreshLogs(); window.clearLogs = () => app.clearLogs();