From cec91ff45a098953e83643c488307b8ad3939e84 Mon Sep 17 00:00:00 2001 From: lizandrogd <77708265+lizandrogd@users.noreply.github.com> Date: Wed, 21 Jan 2026 02:49:42 -0500 Subject: [PATCH] up --- api/get_notifications.php | 19 ++++++ api/mark_notification_read.php | 22 +++++++ api/webhook.php | 14 +++++ conversations.php | 94 +++++++++++++++++++++++++++++ scripts/add_notifications_table.php | 25 ++++++++ scripts/test_notifications_flow.php | 35 +++++++++++ 6 files changed, 209 insertions(+) create mode 100644 api/get_notifications.php create mode 100644 api/mark_notification_read.php create mode 100644 scripts/add_notifications_table.php create mode 100644 scripts/test_notifications_flow.php diff --git a/api/get_notifications.php b/api/get_notifications.php new file mode 100644 index 0000000..729243f --- /dev/null +++ b/api/get_notifications.php @@ -0,0 +1,19 @@ +fetchAll( + "SELECT id, user_id, type, message, data, is_read, created_at FROM notifications WHERE is_read = 0 ORDER BY created_at DESC LIMIT ?", + [$limit] + ); + + echo json_encode(['success' => true, 'data' => $rows]); +} catch (Exception $e) { + http_response_code(500); + echo json_encode(['success' => false, 'error' => $e->getMessage()]); +} diff --git a/api/mark_notification_read.php b/api/mark_notification_read.php new file mode 100644 index 0000000..de59c87 --- /dev/null +++ b/api/mark_notification_read.php @@ -0,0 +1,22 @@ + false, 'error' => 'id required']); + exit; + } + + $db = Database::getInstance(); + $db->update('notifications', ['is_read' => 1], 'id = :id', ['id' => $id]); + + echo json_encode(['success' => true]); +} catch (Exception $e) { + http_response_code(500); + echo json_encode(['success' => false, 'error' => $e->getMessage()]); +} diff --git a/api/webhook.php b/api/webhook.php index 5a3406f..e5d1a96 100644 --- a/api/webhook.php +++ b/api/webhook.php @@ -184,6 +184,20 @@ class WhatsAppWebhook { } $this->saveMessage($saveData); + + // Crear notificación para UI (nuevo mensaje entrante) + try { + $this->db->insert('notifications', [ + 'user_id' => $user['id'], + 'type' => 'incoming_message', + 'message' => substr($messageText, 0, 250), + 'data' => json_encode(['message_id' => $messageId]), + 'is_read' => 0, + 'created_at' => date('Y-m-d H:i:s') + ]); + } catch (Exception $e) { + error_log('Failed to create notification: ' . $e->getMessage()); + } // Procesar con bot (protección contra excepciones externas) try { diff --git a/conversations.php b/conversations.php index 4286425..e0d0aaa 100644 --- a/conversations.php +++ b/conversations.php @@ -539,6 +539,100 @@ this.loadConversations(); this.setupEventListeners(); this.setupAutoRefresh(); + this.setupNotificationPolling(); + } + + setupNotificationPolling() { + // Poll every 7 seconds for unread notifications + this.loadNotifications(); + setInterval(() => this.loadNotifications(), 7000); + } + + async loadNotifications() { + try { + const resp = await fetch('api/get_notifications.php'); + const json = await resp.json(); + if (json && json.success && Array.isArray(json.data)) { + json.data.forEach(n => this.showNotificationToast(n)); + } + } catch (e) { + console.error('Error loading notifications', e); + } + } + + async showNotificationToast(notification) { + // Create toast element + const containerId = 'notification-toasts'; + let container = document.getElementById(containerId); + if (!container) { + container = document.createElement('div'); + container.id = containerId; + container.style.position = 'fixed'; + container.style.top = '12px'; + container.style.right = '12px'; + container.style.zIndex = 9999; + document.body.appendChild(container); + } + + const toast = document.createElement('div'); + toast.className = 'notification-toast'; + toast.style.background = '#fff'; + toast.style.padding = '10px 14px'; + toast.style.border = '1px solid #ddd'; + toast.style.boxShadow = '0 2px 6px rgba(0,0,0,0.12)'; + toast.style.marginTop = '8px'; + toast.style.borderRadius = '6px'; + toast.style.minWidth = '220px'; + + const msg = document.createElement('div'); + msg.textContent = notification.message || 'Nuevo evento'; + toast.appendChild(msg); + + const actions = document.createElement('div'); + actions.style.marginTop = '8px'; + actions.style.display = 'flex'; + actions.style.gap = '8px'; + + const openBtn = document.createElement('button'); + openBtn.className = 'btn btn-sm btn-primary'; + openBtn.textContent = 'Abrir'; + openBtn.onclick = async () => { + // Mark notification read, then open conversation + await fetch('api/mark_notification_read.php', {method:'POST', headers:{'Content-Type':'application/json'}, body: JSON.stringify({id: notification.id})}); + // Parse data to get user_id + let data = {}; + try { data = notification.data ? JSON.parse(notification.data) : {}; } catch(e) {} + const userId = data.user_id || notification.user_id; + if (userId) { + // find conversation item and open it + const conv = this.conversations.find(c => c.user_id == userId); + if (conv) { + this.openConversation(conv.user_id, conv.name, conv.phone_number); + } else { + // fallback: open by fetching user info + this.openConversation(userId, 'Usuario', ''); + } + } + // remove toast + toast.remove(); + }; + + const dismiss = document.createElement('button'); + dismiss.className = 'btn btn-sm btn-secondary'; + dismiss.textContent = 'Descartar'; + dismiss.onclick = async () => { + await fetch('api/mark_notification_read.php', {method:'POST', headers:{'Content-Type':'application/json'}, body: JSON.stringify({id: notification.id})}); + toast.remove(); + }; + + actions.appendChild(openBtn); + actions.appendChild(dismiss); + toast.appendChild(actions); + + container.appendChild(toast); + + // Auto remove after 18s + setTimeout(() => { if (toast.parentNode) toast.remove(); }, 18000); } setupEventListeners() { diff --git a/scripts/add_notifications_table.php b/scripts/add_notifications_table.php new file mode 100644 index 0000000..0e4aec3 --- /dev/null +++ b/scripts/add_notifications_table.php @@ -0,0 +1,25 @@ +fetch("SHOW TABLES LIKE 'notifications'"); +if ($col) { + echo "Table notifications already exists\n"; + exit(0); +} +try { + $db->query("CREATE TABLE notifications ( + id INT AUTO_INCREMENT PRIMARY KEY, + user_id INT NULL, + type VARCHAR(64) NOT NULL, + message TEXT, + data JSON NULL, + is_read TINYINT(1) DEFAULT 0, + created_at DATETIME DEFAULT NOW(), + INDEX idx_user_id (user_id), + INDEX idx_is_read (is_read) + ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;"); + echo "Created notifications table\n"; +} catch (Exception $e) { + echo "Failed to create notifications table: " . $e->getMessage() . "\n"; + exit(1); +} diff --git a/scripts/test_notifications_flow.php b/scripts/test_notifications_flow.php new file mode 100644 index 0000000..f33607e --- /dev/null +++ b/scripts/test_notifications_flow.php @@ -0,0 +1,35 @@ + 'whatsapp_business_account', + 'entry' => [ + [ + 'changes' => [ + [ + 'field' => 'messages', + 'value' => [ + 'messages' => [ + [ + 'from' => '573019999900', + 'id' => 'NTFTEST1', + 'timestamp' => time(), + 'type' => 'text', + 'text' => ['body' => 'Notificacion test'] + ] + ] + ] + ] + ] + ] + ] +]; + +$w = new WhatsAppWebhook(); +$w->processPayload($payload); + +$db = Database::getInstance(); +$rows = $db->fetchAll('SELECT * FROM notifications ORDER BY id DESC LIMIT 5'); +foreach ($rows as $r) echo json_encode($r) . "\n";