This commit is contained in:
Lizandro Guarnizo
2026-01-22 00:19:20 -05:00
parent ee016d62f5
commit 5e345b951f
7 changed files with 211 additions and 7 deletions
+29 -2
View File
@@ -72,7 +72,14 @@ try {
<div>
<h1><i class="fas fa-robot"></i> Sistema de Gestión WhatsApp Bot</h1>
<small class="text-muted">
<i class="fas fa-user"></i> Conectado como <strong><?= htmlspecialchars(ADMIN_USERNAME) ?></strong>
<i class="fas fa-user"></i> Conectado como <strong><?= htmlspecialchars(
// Preferir nombre completo del admin si existe
(
$_SESSION['admin_user']['full_name'] ?? $_SESSION['admin_user']['username'] ?? ADMIN_USERNAME
)
) ?></strong>
| <i class="fas fa-clock"></i> Sesión desde: <?= date('H:i:s', $_SESSION['login_time'] ?? time()) ?>
</small>
</div>
@@ -936,6 +943,8 @@ try {
// Global Notification Manager: polls server for unread notifications and shows toasts + native notifications
const NotificationManager = {
interval: 7000,
// IDs de notificaciones ya mostradas para evitar re-sonar/repetir
_seenIds: new Set(),
init() {
this.bell = document.getElementById('notification-bell');
this.countEl = document.getElementById('notification-count');
@@ -950,6 +959,8 @@ try {
if ("Notification" in window && Notification.permission === 'default') {
Notification.requestPermission().then(p => console.log('Notification permission:', p));
}
// Al abrir la campana se considera que se atendieron visualmente las notificaciones: limpiar indicador visual
this.bell.classList.remove('has-notifications');
});
}
@@ -971,10 +982,19 @@ try {
if (unreadCount > 0) {
this.countEl.textContent = unreadCount;
this.countEl.style.display = 'inline-block';
this.bell.classList.add('has-notifications');
} else {
this.countEl.style.display = 'none';
this.bell.classList.remove('has-notifications');
}
json.data.forEach(n => this.showToast(n));
// Mostrar sólo las nuevas notificaciones (no repetir sonido/visual)
json.data.forEach(n => {
if (!this._seenIds.has(n.id)) {
this._seenIds.add(n.id);
this.showToast(n);
}
});
} else if (json && json.success === false) {
console.warn('Notification API error:', json.error || json);
}
@@ -1033,6 +1053,9 @@ try {
try { data = notification.data ? JSON.parse(notification.data) : {}; } catch(e) {}
const userId = data.user_id || notification.user_id;
if (userId) {
// marcar conversaciones como leídas también
try { await fetch('api/mark_conversation_read.php', {method:'POST', headers:{'Content-Type':'application/json'}, body: JSON.stringify({user_id: userId})}); } catch(e) { console.warn('mark_conversation_read failed', e); }
const convLink = document.querySelector('.nav-link[data-tab="conversations"]');
if (convLink) convLink.click();
const tryOpen = () => {
@@ -1070,6 +1093,9 @@ try {
window.focus();
const userId = data.user_id || notification.user_id;
if (userId) {
// marcar conversación leída cuando se abre desde la notification nativa
try { fetch('api/mark_conversation_read.php', {method:'POST', headers:{'Content-Type':'application/json'}, body: JSON.stringify({user_id: userId})}); } catch(e) { console.warn('mark_conversation_read failed', e); }
const convLink = document.querySelector('.nav-link[data-tab="conversations"]');
if (convLink) convLink.click();
const tryOpen = () => {
@@ -1088,6 +1114,7 @@ try {
// play sound
try {
// usar Web Audio API para reproducir un sonido breve
const ctx = new (window.AudioContext || window.webkitAudioContext)();
const o = ctx.createOscillator();
const g = ctx.createGain();