/** * WhatsApp Bot Manager - JavaScript Simplificado * Versión sin librerías externas problemáticas */ class SimpleWhatsAppManager { constructor() { this.apiBaseUrl = './api/'; this.currentTab = 'dashboard'; this.init(); } init() { this.log('Iniciando sistema simplificado'); this.setupEventListeners(); this.setupCharts(); this.loadDashboard(); } log(message, type = 'info') { const timestamp = new Date().toLocaleTimeString(); console.log(`[${timestamp}] [${type.toUpperCase()}] ${message}`); } setupEventListeners() { this.log('Configurando event listeners'); // Navegación de tabs - más defensiva const navLinks = document.querySelectorAll('.nav-link[data-tab]'); if (navLinks) { navLinks.forEach(link => { link.addEventListener('click', (e) => { e.preventDefault(); const tabName = link.getAttribute('data-tab'); if (tabName) { this.showTab(tabName); } }); }); } // Formularios this.setupFormListeners(); } setupFormListeners() { // Formulario de configuración const settingsForm = document.getElementById('settings-form'); if (settingsForm) { settingsForm.addEventListener('submit', (e) => { e.preventDefault(); this.saveSettings(); }); } // Formulario de envío de mensaje const sendForm = document.getElementById('send-message-form'); if (sendForm) { sendForm.addEventListener('submit', (e) => { e.preventDefault(); this.sendMessage(); }); } } showTab(tabName) { this.log(`Cambiando a tab: ${tabName}`); // Ocultar todas las pestañas const tabs = document.querySelectorAll('.tab-content'); if (tabs) { tabs.forEach(tab => tab.classList.remove('active')); } // Remover clase active de navegación const navLinks = document.querySelectorAll('.nav-link'); if (navLinks) { navLinks.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; this.loadTabData(tabName); } loadTabData(tabName) { this.log(`Cargando datos para tab: ${tabName}`); switch (tabName) { case 'dashboard': this.loadDashboard(); break; case 'conversations': this.loadConversations(); break; case 'users': this.loadUsers(); break; case 'messages': this.loadMessages(); break; case 'templates': this.loadTemplates(); break; case 'logs': this.loadLogs(); break; case 'settings': this.loadSettings(); break; default: this.log(`Tab no reconocido: ${tabName}`, 'warning'); } } async apiCall(endpoint, options = {}) { const url = `${this.apiBaseUrl}${endpoint}${endpoint.includes('?') ? '&' : '?'}debug=true`; this.log(`API Call: ${url}`, 'info'); try { const response = await fetch(url, { method: options.method || 'GET', headers: { 'Content-Type': 'application/json', ...options.headers }, body: options.body ? JSON.stringify(options.body) : undefined }); if (!response.ok) { throw new Error(`HTTP ${response.status}: ${response.statusText}`); } const data = await response.json(); this.log(`API Response: ${JSON.stringify(data).substring(0, 100)}...`, 'success'); return data; } catch (error) { this.log(`API Error: ${error.message}`, 'error'); this.showError('Error en petición: ' + error.message); throw error; } } async apiCallReal(endpoint, options = {}) { const url = `${this.apiBaseUrl}${endpoint}`; this.log(`API Call (REAL): ${url}`, 'info'); try { const response = await fetch(url, { method: options.method || 'GET', headers: { 'Content-Type': 'application/json', ...options.headers }, body: options.body ? JSON.stringify(options.body) : undefined }); if (!response.ok) { throw new Error(`HTTP ${response.status}: ${response.statusText}`); } const data = await response.json(); this.log(`API Response (REAL): ${JSON.stringify(data).substring(0, 100)}...`, 'success'); return data; } catch (error) { this.log(`API Error (REAL): ${error.message}`, 'error'); this.showError('Error en petición real: ' + error.message); throw error; } } async loadDashboard() { this.log('Cargando dashboard'); try { // Cargar estadísticas básicas const stats = await this.apiCall('get_stats.php'); this.updateStats(stats); // Cargar mensajes recientes const recentMessagesResponse = await this.apiCall('get_recent_messages.php'); if (recentMessagesResponse && recentMessagesResponse.success && Array.isArray(recentMessagesResponse.data)) { this.updateRecentMessages(recentMessagesResponse.data); } else if (recentMessagesResponse && Array.isArray(recentMessagesResponse)) { // Retrocompatibilidad por si la API devuelve directamente el array this.updateRecentMessages(recentMessagesResponse); } // Actualizar gráfico si existe this.updateChart(); } catch (error) { this.showError('Error cargando dashboard: ' + error.message); } } updateStats(stats) { this.log('Actualizando estadísticas del dashboard'); if (stats) { const elements = [ { id: 'total-users', value: stats.total_users || 0 }, { id: 'messages-today', value: stats.messages_today || 0 }, { id: 'active-users', value: stats.active_users || 0 }, { id: 'total-messages', value: stats.total_messages || 0 } ]; elements.forEach(item => { const element = document.getElementById(item.id); if (element) { element.textContent = item.value; this.log(`Estadística actualizada: ${item.id} = ${item.value}`); } }); } } updateRecentMessages(messages) { const container = document.getElementById('recent-messages'); if (!container) { this.log('Contenedor de mensajes recientes no encontrado', 'warning'); return; } container.innerHTML = ''; if (messages && messages.length > 0) { this.log(`Mostrando ${messages.length} mensajes recientes`); 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 mb-3 p-2 border-bottom'; 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; } truncateText(text, length) { if (!text) return ''; return text.length > length ? text.substring(0, length) + '...' : text; } async updateChart() { if (!this.charts || !this.charts.messages) { this.log('Gráfico no inicializado, omitiendo actualización', 'warning'); return; } 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(); this.log('Gráfico actualizado correctamente'); } } catch (error) { this.log('Error actualizando gráfico: ' + error.message, 'warning'); } } setupCharts() { const ctx = document.getElementById('messagesChart'); if (!ctx) { this.log('Canvas de gráfico no encontrado', 'warning'); return; } if (typeof Chart === 'undefined') { this.log('Chart.js no está cargado', 'warning'); return; } try { this.charts = this.charts || {}; 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 } } } }); this.log('Gráfico de mensajes inicializado'); } catch (error) { this.log('Error inicializando gráfico: ' + error.message, 'error'); } } async loadUsers() { this.log('Cargando usuarios'); try { const response = await this.apiCall('get_users.php'); if (response && response.success) { this.updateUsersList(response.data || []); } else { this.showError('No se pudieron cargar los usuarios'); } } catch (error) { this.showError('Error cargando usuarios: ' + error.message); } } updateUsersList(users) { const container = document.getElementById('users-table'); if (!container) return; if (users.length === 0) { container.innerHTML = 'No hay usuarios registrados'; return; } let html = ''; users.forEach(user => { const createdDate = new Date(user.created_at); const formatDate = createdDate.toLocaleDateString() + ' ' + createdDate.toLocaleTimeString('es', { hour: '2-digit', minute: '2-digit' }); html += ` ${user.id} ${user.phone_number} ${user.name || 'Sin nombre'} ${user.status} ${user.current_menu || 'Sin menú'} ${formatDate} `; }); container.innerHTML = html; } async loadConversations() { this.log('Cargando conversaciones'); try { const response = await this.apiCall('get_conversation_list.php'); console.log('Respuesta get_conversation_list:', response); // Debug if (response && response.success && Array.isArray(response.conversations)) { this.updateConversationsList(response.conversations); } else if (response && Array.isArray(response)) { // Retrocompatibilidad por si la API devuelve directamente el array this.updateConversationsList(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); } } updateConversationsList(conversations) { const container = document.getElementById('conversations-container'); if (!container) return; if (conversations.length === 0) { container.innerHTML = `
No hay conversaciones

Los chats aparecerán aquí cuando recibas o envíes mensajes

`; return; } let html = ''; conversations.forEach(conv => { const timeAgo = conv.time_ago || this.formatTimeAgo(conv.last_message_time || conv.created_at); const lastMessage = conv.last_message || conv.content || 'Sin mensajes'; const userName = conv.name || conv.phone_number; const userInitial = userName.charAt(0).toUpperCase(); const isOnline = Math.random() > 0.6; // Simulación de estado online // Unread badge const unreadBadge = (conv.unread_count && conv.unread_count > 0) ? `
${conv.unread_count}
` : ''; // Status icon const statusIcon = conv.last_direction === 'outgoing' ? '' : ''; // Message type indicator const messageTypeIcon = conv.last_message_type === 'template' ? ' ' : conv.last_message_type === 'image' ? ' ' : ''; html += `
${userInitial}
${userName} ${conv.last_message_status === 'read' ? '' : ''}
${timeAgo}
${statusIcon}
${messageTypeIcon}${lastMessage}
${unreadBadge}
`; }); container.innerHTML = html; // Agregar funcionalidad de búsqueda this.setupConversationSearch(conversations); } setupConversationSearch(conversations) { const searchInput = document.getElementById('search-conversations'); if (!searchInput) return; searchInput.addEventListener('input', (e) => { const query = e.target.value.toLowerCase().trim(); if (!query) { this.updateConversationsDisplay(conversations); return; } const filtered = conversations.filter(conv => { const name = (conv.name || '').toLowerCase(); const phone = (conv.phone_number || '').toLowerCase(); const message = (conv.last_message || conv.content || '').toLowerCase(); return name.includes(query) || phone.includes(query) || message.includes(query); }); this.updateConversationsDisplay(filtered); }); } updateConversationsDisplay(conversations) { const container = document.getElementById('conversations-container'); if (!container) return; if (conversations.length === 0) { container.innerHTML = `
No se encontraron conversaciones

Intenta con otros términos de búsqueda

`; return; } let html = ''; conversations.forEach(conv => { const timeAgo = conv.time_ago || this.formatTimeAgo(conv.last_message_time || conv.created_at); const lastMessage = conv.last_message || conv.content || 'Sin mensajes'; const userName = conv.name || conv.phone_number; const userInitial = userName.charAt(0).toUpperCase(); const isOnline = Math.random() > 0.6; const unreadBadge = (conv.unread_count && conv.unread_count > 0) ? `
${conv.unread_count}
` : ''; const statusIcon = conv.last_direction === 'outgoing' ? '' : ''; const messageTypeIcon = conv.last_message_type === 'template' ? ' ' : conv.last_message_type === 'image' ? ' ' : ''; html += `
${userInitial}
${userName} ${conv.last_message_status === 'read' ? '' : ''}
${timeAgo}
${statusIcon}
${messageTypeIcon}${lastMessage}
${unreadBadge}
`; }); container.innerHTML = html; } formatTimeAgo(datetime) { if (!datetime) return ''; const now = new Date(); const date = new Date(datetime); const diff = Math.floor((now - date) / 1000); if (diff < 60) return 'Ahora'; if (diff < 3600) return `${Math.floor(diff/60)}m`; if (diff < 86400) return `${Math.floor(diff/3600)}h`; if (diff < 2592000) return `${Math.floor(diff/86400)}d`; return date.toLocaleDateString(); } async loadSettings() { this.log('Cargando configuraciones'); try { const response = await this.apiCall('manage_config.php?action=whatsapp'); if (response && response.success) { this.updateSettingsForm(response.data); } } catch (error) { this.showError('Error cargando configuraciones: ' + error.message); } } updateSettingsForm(data) { if (!data) return; const fields = [ { id: 'whatsapp-token', value: data.whatsapp_token }, { id: 'phone-number-id', value: data.phone_number_id }, { id: 'webhook-token', value: data.webhook_verify_token }, { id: 'api-url', value: data.api_url }, { id: 'business-name', value: data.business_name }, { id: 'welcome-message', value: data.welcome_message } ]; fields.forEach(field => { const element = document.getElementById(field.id); if (element && field.value) { element.value = field.value; this.log(`Campo ${field.id} actualizado con valor: ${field.value}`); } else { this.log(`Campo ${field.id} no encontrado o sin valor`, 'warning'); } }); } async saveSettings() { this.log('Guardando configuraciones'); const settings = { token: document.getElementById('whatsapp-token').value, phone_number_id: document.getElementById('phone-number-id').value, webhook_verify_token: document.getElementById('webhook-token').value, api_url: document.getElementById('api-url').value, business_name: document.getElementById('business-name').value, welcome_message: document.getElementById('welcome-message').value }; try { const response = await this.apiCall('manage_config.php?action=save_whatsapp', { method: 'POST', body: { whatsapp: settings } }); if (response && response.success) { this.showSuccess('Configuraciones guardadas exitosamente'); } else { this.showError('Error guardando configuraciones'); } } catch (error) { this.showError('Error: ' + error.message); } } showError(message) { this.log(`Error: ${message}`, 'error'); this.showAlert(message, 'danger'); } showSuccess(message) { this.log(`Success: ${message}`, 'success'); this.showAlert(message, 'success'); } showAlert(message, type) { // Crear alerta temporal const alertDiv = document.createElement('div'); alertDiv.className = `alert alert-${type} alert-dismissible fade show position-fixed`; alertDiv.style.cssText = 'top: 20px; right: 20px; z-index: 9999; min-width: 300px;'; alertDiv.innerHTML = ` ${message} `; document.body.appendChild(alertDiv); // Auto-remover después de 5 segundos setTimeout(() => { if (alertDiv.parentNode) { alertDiv.parentNode.removeChild(alertDiv); } }, 5000); } async loadMessages() { this.log('Cargando interfaz de mensajes'); try { // Cargar usuarios para el select const usersResponse = await this.apiCall('get_users.php'); if (usersResponse && usersResponse.success) { this.populateUsersSelect(usersResponse.data || []); } // Cargar plantillas para el select const templatesResponse = await this.apiCall('get_templates.php'); if (templatesResponse && templatesResponse.success) { this.populateTemplatesSelect(templatesResponse.data || []); } } catch (error) { this.showError('Error cargando datos para mensajes: ' + error.message); } } populateUsersSelect(users) { const select = document.getElementById('message-recipient'); if (!select) return; // Limpiar opciones existentes (excepto la primera) select.innerHTML = ''; // Agregar usuarios users.forEach(user => { const option = document.createElement('option'); option.value = user.id; option.textContent = `${user.name || user.phone_number} (${user.phone_number})`; select.appendChild(option); }); } populateTemplatesSelect(templates) { const select = document.getElementById('message-template'); if (!select) return; // Limpiar opciones existentes (excepto la primera) select.innerHTML = ''; // Agregar plantillas templates.forEach(template => { const option = document.createElement('option'); option.value = template.template_name; option.textContent = `${template.name} - ${template.category || 'Sin descripción'}`; select.appendChild(option); }); } async loadTemplates() { this.log('Cargando plantillas'); try { const response = await this.apiCall('get_templates.php'); if (response && response.success) { this.updateTemplatesList(response.data || []); } else { this.showError('Error cargando plantillas'); } } catch (error) { this.showError('Error cargando plantillas: ' + error.message); } } updateTemplatesList(templates) { const container = document.getElementById('templates-table'); if (!container) return; if (templates.length === 0) { container.innerHTML = 'No hay plantillas registradas'; return; } let html = ''; templates.forEach(template => { html += ` ${template.name} ${template.template_name} ${template.language_code || 'es'} ${template.category || 'utility'} ${template.status || 'pending'} `; }); container.innerHTML = html; } async saveTemplate() { this.log('Guardando plantilla'); try { const templateData = { name: document.getElementById('template-name').value, whatsapp_name: document.getElementById('template-whatsapp-name').value, language: document.getElementById('template-language').value, category: document.getElementById('template-category').value }; // Validar campos requeridos if (!templateData.name || !templateData.whatsapp_name) { this.showError('Nombre descriptivo y nombre de WhatsApp son requeridos'); return; } const response = await this.apiCall('save_template.php', { method: 'POST', body: templateData }); if (response && response.success) { this.showSuccess('Plantilla guardada correctamente'); // Cerrar modal const modal = document.getElementById('createTemplateModal'); if (modal) { const bsModal = bootstrap.Modal.getInstance(modal); if (bsModal) bsModal.hide(); } // Limpiar formulario document.getElementById('template-form').reset(); // Recargar plantillas this.loadTemplates(); } else { this.showError(`Error guardando plantilla: ${response?.error || 'Error desconocido'}`); } } catch (error) { this.showError('Error guardando plantilla: ' + error.message); } } async sendMessage() { this.log('Enviando mensaje'); try { const recipientType = document.getElementById('recipient-type').value; const messageType = document.getElementById('message-type').value; let recipient; let messageData = {}; // Obtener destinatario if (recipientType === 'existing') { const userSelect = document.getElementById('message-recipient'); if (!userSelect.value) { this.showError('Por favor selecciona un usuario'); return; } // Obtener datos del usuario seleccionado const selectedOption = userSelect.options[userSelect.selectedIndex]; const userId = userSelect.value; // Necesitamos obtener el número de teléfono del usuario const usersResponse = await this.apiCall('get_users.php'); if (usersResponse && usersResponse.success) { const user = usersResponse.data.find(u => u.id == userId); if (user) { recipient = user.phone_number; } else { this.showError('Usuario no encontrado'); return; } } else { this.showError('Error obteniendo datos del usuario'); return; } } else { recipient = document.getElementById('manual-recipient').value; if (!recipient) { this.showError('Por favor ingresa un número de teléfono'); return; } } // Preparar mensaje según el tipo if (messageType === 'text') { const messageText = document.getElementById('message-text').value; if (!messageText) { this.showError('Por favor escribe un mensaje'); return; } messageData = { recipient: recipient, type: 'text', message: messageText }; } else { const templateSelect = document.getElementById('message-template'); if (!templateSelect.value) { this.showError('Por favor selecciona una plantilla'); return; } messageData = { recipient: recipient, type: 'template', template_name: templateSelect.value }; } this.log(`Enviando mensaje a ${recipient}: ${JSON.stringify(messageData)}`); // Preguntar al usuario si quiere envío real o simulado const sendReal = confirm('¿Enviar mensaje REAL por WhatsApp?\n\nSí = Envío real\nNo = Envío simulado (debug)'); // Enviar mensaje const response = sendReal ? await this.apiCallReal('send_message.php', { method: 'POST', body: messageData }) : await this.apiCall('send_message.php', { method: 'POST', body: messageData }); if (response && response.success) { this.showSuccess(`Mensaje enviado correctamente a ${recipient}`); // Limpiar formulario document.getElementById('send-message-form').reset(); document.getElementById('message-recipient').value = ''; } else { this.showError(`Error enviando mensaje: ${response?.error || 'Error desconocido'}`); } } catch (error) { this.showError('Error enviando mensaje: ' + 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); } } } // Inicializar cuando el DOM esté listo document.addEventListener('DOMContentLoaded', function () { console.log('DOM cargado, inicializando WhatsApp Manager...'); try { window.whatsappManager = new SimpleWhatsAppManager(); console.log('WhatsApp Manager inicializado correctamente'); } catch (error) { console.error('Error inicializando WhatsApp Manager:', error); } }); // Funciones globales para modales (compatibilidad con el HTML existente) window.openCreateTemplateModal = function () { console.log('🔵 Abriendo modal de plantilla...'); const modalElement = document.getElementById('createTemplateModal'); if (!modalElement) { console.error('❌ Modal de plantilla no encontrado'); return; } try { const form = document.getElementById('template-form'); if (form) form.reset(); if (typeof bootstrap !== 'undefined' && bootstrap.Modal) { const modal = new bootstrap.Modal(modalElement); modal.show(); } else { modalElement.style.display = 'block'; modalElement.classList.add('show'); } console.log('✅ Modal de plantilla abierto'); } catch (error) { console.error('❌ Error abriendo modal de plantilla:', error); } }; window.openCreateMenuModal = function () { console.log('🔵 Abriendo modal de menú...'); const modalElement = document.getElementById('createMenuModal'); if (!modalElement) { console.error('❌ Modal de menú no encontrado'); return; } try { const form = document.getElementById('menu-form'); if (form) form.reset(); if (typeof bootstrap !== 'undefined' && bootstrap.Modal) { const modal = new bootstrap.Modal(modalElement); modal.show(); } else { modalElement.style.display = 'block'; modalElement.classList.add('show'); } console.log('✅ Modal de menú abierto'); } catch (error) { console.error('❌ Error abriendo modal de menú:', error); } }; // Función global para debugging window.debugAPI = async function (endpoint) { try { const manager = window.whatsappManager; if (manager) { const result = await manager.apiCall(endpoint); console.log('Debug API Result:', result); return result; } else { console.error('Manager no inicializado'); } } catch (error) { console.error('Debug API Error:', error); } }; // Función para abrir ventana de chat window.openChatWindow = function (userId) { console.log('Abriendo chat del usuario:', userId); // Abrir nueva ventana de chat const chatUrl = `chat_window.php?user_id=${userId}&debug=true`; const windowFeatures = 'width=1000,height=700,resizable=yes,scrollbars=yes,status=yes,toolbar=no,menubar=no,location=no'; const chatWindow = window.open(chatUrl, `chat_${userId}`, windowFeatures); if (chatWindow) { chatWindow.focus(); } else { alert('No se pudo abrir la ventana de chat. Verifica que tu navegador permita ventanas emergentes.'); } }; // Función para mostrar modal de chat function showChatModal(user, messages) { // Escapar datos del usuario para evitar XSS const userName = escapeHtml(user.name || 'Usuario'); const userPhone = escapeHtml(user.phone_number || ''); const modalHtml = ` `; // Remover modal anterior si existe const existingModal = document.getElementById('chatModal'); if (existingModal) { existingModal.remove(); } document.body.insertAdjacentHTML('beforeend', modalHtml); // Event listener para cambio de tipo de mensaje document.getElementById('messageType').addEventListener('change', function() { const templateSelector = document.getElementById('templateSelector'); const messageInput = document.getElementById('messageInput'); if (this.value === 'template') { templateSelector.style.display = 'block'; messageInput.placeholder = 'Parámetros de la plantilla (opcional)'; } else { templateSelector.style.display = 'none'; messageInput.placeholder = 'Escribe tu mensaje...'; } }); // Mostrar modal const modal = new bootstrap.Modal(document.getElementById('chatModal')); modal.show(); // Scroll al final setTimeout(() => { const chatContainer = document.getElementById('chat-messages'); chatContainer.scrollTop = chatContainer.scrollHeight; }, 200); } // Generar HTML de mensajes del chat function generateChatMessages(messages) { if (!messages || messages.length === 0) { return '


No hay mensajes en esta conversación
'; } let html = ''; messages.forEach(msg => { const isOutgoing = msg.direction === 'outgoing'; const messageClass = isOutgoing ? 'outgoing' : 'incoming'; const alignClass = isOutgoing ? 'justify-content-end' : 'justify-content-start'; const bgClass = isOutgoing ? 'bg-success text-white' : 'bg-white'; // Escapar el contenido del mensaje const messageContent = escapeHtml(msg.content) || 'Sin contenido'; const messageTime = escapeHtml(msg.time || ''); html += `
${messageContent}
${messageTime} ${isOutgoing ? '✓✓' : ''}
`; }); return html; } // Función para escapar HTML y prevenir XSS function escapeHtml(text) { if (!text) return ''; const map = { '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' }; return text.toString().replace(/[&<>"']/g, function(m) { return map[m]; }); } // Función para enviar mensaje desde el chat window.sendChatMessage = async function (userId) { const messageType = document.getElementById('messageType').value; const messageInput = document.getElementById('messageInput'); const message = messageInput.value.trim(); if (!message && messageType === 'text') { alert('Por favor escribe un mensaje'); return; } if (messageType === 'template') { const templateSelect = document.getElementById('templateSelect'); const template = templateSelect.value; if (!template) { alert('Por favor selecciona una plantilla'); return; } await sendTemplateMessage(userId, template, message); } else { await sendTextMessage(userId, message); } messageInput.value = ''; }; // Función para enviar mensaje de texto async function sendTextMessage(userId, message) { try { const manager = window.whatsappManager; const usersResponse = await manager.apiCall('get_users.php?debug=true'); // Manejar diferentes formatos de respuesta let users = []; if (Array.isArray(usersResponse)) { users = usersResponse; } else if (usersResponse && usersResponse.data && Array.isArray(usersResponse.data)) { users = usersResponse.data; } else if (usersResponse && usersResponse.users && Array.isArray(usersResponse.users)) { users = usersResponse.users; } else { console.error('Formato de respuesta de usuarios inesperado:', usersResponse); throw new Error('Error obteniendo lista de usuarios'); } const user = users.find(u => u.id == userId); if (!user) { throw new Error('Usuario no encontrado'); } const response = await manager.apiCall('send_message.php', { method: 'POST', body: { recipient: user.phone_number, type: 'text', message: message } }); if (response && response.success) { // Refrescar chat openChatWindow(userId); showAlert('Mensaje enviado correctamente', 'success'); } else { throw new Error(response.error || 'Error enviando mensaje'); } } catch (error) { console.error('Error enviando mensaje:', error); showAlert('Error enviando mensaje: ' + error.message, 'danger'); } } // Función para enviar mensaje de plantilla async function sendTemplateMessage(userId, template, parameters) { try { const manager = window.whatsappManager; const usersResponse = await manager.apiCall('get_users.php?debug=true'); // Manejar diferentes formatos de respuesta let users = []; if (Array.isArray(usersResponse)) { users = usersResponse; } else if (usersResponse && usersResponse.data && Array.isArray(usersResponse.data)) { users = usersResponse.data; } else if (usersResponse && usersResponse.users && Array.isArray(usersResponse.users)) { users = usersResponse.users; } else { console.error('Formato de respuesta de usuarios inesperado:', usersResponse); throw new Error('Error obteniendo lista de usuarios'); } const user = users.find(u => u.id == userId); if (!user) { throw new Error('Usuario no encontrado'); } const response = await manager.apiCall('send_message.php', { method: 'POST', body: { recipient: user.phone_number, type: 'template', template: template, language: 'en_US', parameters: parameters ? parameters.split(',') : [] } }); if (response && response.success) { // Refrescar chat openChatWindow(userId); showAlert('Mensaje de plantilla enviado correctamente', 'success'); } else { throw new Error(response.error || 'Error enviando plantilla'); } } catch (error) { console.error('Error enviando plantilla:', error); showAlert('Error enviando plantilla: ' + error.message, 'danger'); } } // Función para ver detalles de conversación window.viewConversationDetails = function (userId) { console.log('Viendo detalles de conversación del usuario:', userId); openChatWindow(userId); }; // Función para mostrar alertas function showAlert(message, type = 'info') { const alertHtml = ` `; document.body.insertAdjacentHTML('beforeend', alertHtml); // Auto-dismiss después de 5 segundos setTimeout(() => { const alert = document.querySelector('.alert'); if (alert) { alert.remove(); } }, 5000); } // Función para ver conversación específica (mantener compatibilidad) window.viewConversation = function (userId) { openChatWindow(userId); }; // Función para editar usuario window.editUser = function (userId) { console.log('Editando usuario:', userId); alert(`Editar usuario ${userId} - Función en desarrollo`); }; // Función para eliminar usuario window.deleteUser = function (userId) { console.log('Eliminando usuario:', userId); if (confirm(`¿Está seguro que desea eliminar el usuario ${userId}?`)) { alert(`Eliminar usuario ${userId} - Función en desarrollo`); } }; // Funciones para plantillas window.showCreateTemplateModal = function () { const modal = new bootstrap.Modal(document.getElementById('createTemplateModal')); modal.show(); }; window.editTemplate = function (templateId) { console.log('Editando plantilla:', templateId); alert(`Editar plantilla ${templateId} - Función en desarrollo`); }; window.deleteTemplate = function (templateId) { console.log('Eliminando plantilla:', templateId); if (confirm(`¿Está seguro que desea eliminar la plantilla ${templateId}?`)) { alert(`Eliminar plantilla ${templateId} - Función en desarrollo`); } }; // Hacer que app.saveTemplate() funcione window.app = { saveTemplate: function () { if (window.whatsappManager) { window.whatsappManager.saveTemplate(); } } }; // Funciones para el formulario de mensajes window.toggleRecipientType = function () { const recipientType = document.getElementById('recipient-type').value; const existingGroup = document.getElementById('existing-recipient-group'); const manualGroup = document.getElementById('manual-recipient-group'); if (recipientType === 'manual') { existingGroup.style.display = 'none'; manualGroup.style.display = 'block'; } else { existingGroup.style.display = 'block'; manualGroup.style.display = 'none'; } }; 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 === 'template') { textGroup.style.display = 'none'; templateGroup.style.display = 'block'; } else { textGroup.style.display = 'block'; templateGroup.style.display = 'none'; } }; // Función de diagnóstico window.runDiagnostic = function () { const resultsDiv = document.getElementById('diagnostic-results'); // Mostrar spinner de carga resultsDiv.innerHTML = `
Cargando...

Ejecutando diagnóstico...

`; fetch('./api/diagnose_whatsapp.php') .then(response => response.json()) .then(data => { if (data.success) { displayDiagnosticResults(data); } else { resultsDiv.innerHTML = `
Error: ${data.error || 'Error desconocido'}
`; } }) .catch(error => { console.error('Error en diagnóstico:', error); resultsDiv.innerHTML = `
Error de conexión: ${error.message}
`; }); }; function displayDiagnosticResults(data) { const resultsDiv = document.getElementById('diagnostic-results'); let html = `
Última verificación: ${data.timestamp}
Configuración
Phone Number ID ${data.config_status.phone_number_id ? '✓' : '✗'}
Access Token ${data.config_status.access_token ? '✓' : '✗'}
Webhook Token ${data.config_status.webhook_verify_token ? '✓' : '✗'}
Plantillas
${data.template_status.approved || 0}
Aprobadas
${data.template_status.pending || 0}
Pendientes
${data.template_status.rejected || 0}
Rechazadas
API WhatsApp
${data.api_status.reachable ? `✓ Conectado - ${data.api_status.phone_number || 'N/A'}` : `✗ ${data.api_status.error || 'No conectado'}` }
`; // Recomendaciones if (data.recommendations && data.recommendations.length > 0) { html += `
Recomendaciones
`; } html += '
'; resultsDiv.innerHTML = html; } function updateLogsTable(logs) { const tbody = document.getElementById('logs-table'); if (!tbody) { this.log('Tabla de logs no encontrada', 'warning'); return; } tbody.innerHTML = ''; if (!logs || logs.length === 0) { tbody.innerHTML = 'No hay logs registrados'; return; } logs.forEach(log => { const row = this.createLogRow(log); tbody.appendChild(row); }); this.log(`${logs.length} logs cargados en la tabla`); } function createLogRow(log) { const tr = document.createElement('tr'); const date = new Date(log.created_at); const formatDate = date.toLocaleDateString() + ' ' + date.toLocaleTimeString('es', { hour: '2-digit', minute: '2-digit' }); tr.innerHTML = ` ${formatDate} ${log.ip_address || 'N/A'} ${log.status_code || 'N/A'} `; return tr; }