feat(notifs): centro de notificaciones con contador interactivo de mensajes pendientes y alertas de servicio en app movil

This commit is contained in:
AI Bot 2026-10-13 20:31:55 -05:00
parent b92a7ba6c3
commit 20b48cfd5a
3 changed files with 318 additions and 0 deletions

View File

@ -1302,4 +1302,174 @@ h1, h2, h3, h4, .brand-title {
transform: scale(1.15);
}
/* NOTIFICATION CENTER & UNREAD COUNTER BADGE */
.notification-wrapper {
position: relative;
display: inline-flex;
align-items: center;
}
.btn-notif-bell {
position: relative;
background: rgba(15, 23, 42, 0.85);
border: 1px solid var(--border-glass);
color: #cbd5e1;
width: 40px;
height: 40px;
border-radius: 10px;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
transition: all 0.2s ease;
}
.btn-notif-bell:hover {
background: rgba(30, 41, 59, 0.9);
color: #fff;
border-color: var(--accent-cyan);
box-shadow: 0 0 12px var(--accent-cyan-glow);
}
.notif-badge {
position: absolute;
top: -6px;
right: -6px;
background: #ef4444;
color: #fff;
font-size: 0.72rem;
font-weight: 800;
min-width: 19px;
height: 19px;
border-radius: 10px;
display: flex;
align-items: center;
justify-content: center;
padding: 0 4px;
box-shadow: 0 0 10px rgba(239, 68, 68, 0.8);
border: 2px solid #080c14;
animation: notifPulse 2.5s infinite;
}
@keyframes notifPulse {
0% { transform: scale(1); }
50% { transform: scale(1.15); box-shadow: 0 0 14px rgba(239, 68, 68, 1); }
100% { transform: scale(1); }
}
.notif-dropdown {
position: absolute;
top: calc(100% + 12px);
right: 0;
width: 360px;
max-width: 90vw;
background: #0f172a;
border: 1px solid var(--border-glass);
border-radius: 16px;
box-shadow: 0 16px 40px rgba(0, 0, 0, 0.85);
z-index: 1000;
display: none;
overflow: hidden;
animation: fadeIn 0.2s ease;
}
.notif-dropdown.active {
display: block;
}
.notif-header {
padding: 0.85rem 1.15rem;
background: rgba(15, 23, 42, 0.95);
border-bottom: 1px solid rgba(255, 255, 255, 0.08);
display: flex;
justify-content: space-between;
align-items: center;
}
.notif-header strong {
color: #fff;
font-size: 0.95rem;
}
.notif-header-badge {
background: rgba(239, 68, 68, 0.2);
color: #f87171;
font-size: 0.72rem;
font-weight: 700;
padding: 2px 7px;
border-radius: 10px;
}
.btn-mark-all-read {
background: transparent;
border: none;
color: #38bdf8;
font-size: 0.78rem;
cursor: pointer;
}
.btn-mark-all-read:hover {
text-decoration: underline;
}
.notif-list {
max-height: 380px;
overflow-y: auto;
}
.notif-item {
padding: 0.9rem 1.15rem;
border-bottom: 1px solid rgba(255, 255, 255, 0.05);
display: flex;
gap: 0.85rem;
cursor: pointer;
transition: background 0.15s ease;
}
.notif-item:hover {
background: rgba(30, 41, 59, 0.5);
}
.notif-item.unread {
background: rgba(6, 182, 212, 0.07);
border-left: 3px solid var(--accent-cyan);
}
.notif-item-icon {
font-size: 1.35rem;
flex-shrink: 0;
margin-top: 2px;
}
.notif-item-content {
flex: 1;
}
.notif-item-title {
font-size: 0.88rem;
font-weight: 700;
color: #fff;
margin-bottom: 0.2rem;
}
.notif-item-desc {
font-size: 0.8rem;
color: #94a3b8;
line-height: 1.35;
margin-bottom: 0.35rem;
}
.notif-item-time {
font-size: 0.72rem;
color: #64748b;
}
.notif-footer {
padding: 0.75rem;
background: rgba(11, 17, 32, 0.95);
border-top: 1px solid rgba(255, 255, 255, 0.08);
text-align: center;
}

View File

@ -69,6 +69,38 @@
</div>
</div>
<!-- Centro de Notificaciones & Contador de Mensajes Pendientes -->
<div class="notification-wrapper" id="notif-wrapper">
<button type="button" class="btn-notif-bell" id="btn-toggle-notifs" title="Notificaciones y Mensajes Pendientes">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M18 8A6 6 0 0 0 6 8c0 7-3 9-3 9h18s-3-2-3-9"></path>
<path d="M13.73 21a2 2 0 0 1-3.46 0"></path>
</svg>
<span class="notif-badge" id="notif-unread-count">3</span>
</button>
<!-- Dropdown de Notificaciones -->
<div class="notif-dropdown" id="notif-dropdown-panel">
<div class="notif-header">
<div style="display: flex; align-items: center; gap: 0.5rem;">
<strong>🔔 Notificaciones</strong>
<span class="notif-header-badge" id="notif-header-unread">3 nuevas</span>
</div>
<button type="button" class="btn-mark-all-read" id="btn-mark-all-read">Marcar leídas</button>
</div>
<div class="notif-list" id="notif-items-list">
<!-- Renderizado dinámicamente por app.js -->
</div>
<div class="notif-footer">
<a href="https://wa.me/573167455877" target="_blank" id="notif-wa-link" style="color: var(--accent-cyan); text-decoration: none; font-size: 0.8rem; display: flex; align-items: center; justify-content: center; gap: 0.35rem;">
<span>💬 Contactar a Central de Soporte WhatsApp</span>
</a>
</div>
</div>
</div>
<a href="/PacifictransGo.apk" class="btn-apk-header" id="btn-download-header">
<svg width="18" height="18" viewBox="0 0 24 24" fill="currentColor">
<path d="M17.523 15.3414c-.5511 0-.9993-.4486-.9993-1.0001s.4482-1.0001.9993-1.0001c.552 0 1.0001.4486 1.0001 1.0001s-.4481 1.0001-1.0001 1.0001m-11.046 0c-.5511 0-.9993-.4486-.9993-1.0001s.4482-1.0001.9993-1.0001c.552 0 1.0001.4486 1.0001 1.0001s-.4481 1.0001-1.0001 1.0001m11.4045-6.02l1.997-3.4589c.1408-.2439.0573-.5552-.1866-.696-.2438-.1408-.5551-.0573-.696.1866l-2.0238 3.5054c-1.5037-.6873-3.1979-1.0772-4.9691-1.0772s-3.4654.3899-4.9691 1.0772l-2.0238-3.5054c-.1409-.2439-.4522-.3274-.696-.1866-.2439.1408-.3274.4521-.1866.696l1.997 3.4589c-3.7997 2.0623-6.375 5.9863-6.446 10.5369h24.646c-.071-4.5506-2.6463-8.4746-6.446-10.5369"/>

View File

@ -28,6 +28,7 @@ document.addEventListener('DOMContentLoaded', () => {
initPWA();
initTabs();
initAuthSystem();
initNotificationsSystem();
initPassengerCalculator();
initFreightCalculator();
initLeafletRadarMap();
@ -1040,3 +1041,118 @@ function initFreightCalculator() {
if (declaredValueInput) declaredValueInput.addEventListener('input', window.recalculateFreight);
window.recalculateFreight();
}
/* ==========================================================================
9. CENTRO DE NOTIFICACIONES & CONTADOR DE MENSAJES PENDIENTES
========================================================================== */
function initNotificationsSystem() {
const btnBell = document.getElementById('btn-toggle-notifs');
const dropdown = document.getElementById('notif-dropdown-panel');
const badge = document.getElementById('notif-unread-count');
const headerUnread = document.getElementById('notif-header-unread');
const listEl = document.getElementById('notif-items-list');
const btnMarkAll = document.getElementById('btn-mark-all-read');
const waLink = document.getElementById('notif-wa-link');
const defaultNotifs = [
{
id: '1',
icon: '🚗',
title: '¡Cupo 4/4 Completado!',
desc: 'Tu viaje Cali ➔ Buenaventura tiene conductor asignado (Mario Fernando G.). Recogida escalonada y ruta Waze TSP activa.',
time: 'Hace 5 min',
unread: true
},
{
id: '2',
icon: '📦',
title: 'Encomienda en Ruta 40',
desc: 'Guía #PT-8921 despachada desde Cali con destino a Buenaventura. Seguimiento satelital en vivo.',
time: 'Hace 25 min',
unread: true
},
{
id: '3',
icon: '🛡️',
title: 'Identidad Verificada 2FA',
desc: 'Tu cuenta ha sido validada con WhatsApp OTP. Viajes y envíos 100% seguros y respaldados.',
time: 'Hace 1 hora',
unread: true
}
];
let stored = localStorage.getItem('pt_notifications');
let notifications = stored ? JSON.parse(stored) : defaultNotifs;
function saveAndRender() {
localStorage.setItem('pt_notifications', JSON.stringify(notifications));
const unreadCount = notifications.filter(n => n.unread).length;
if (badge) {
badge.textContent = unreadCount;
badge.style.display = unreadCount > 0 ? 'flex' : 'none';
}
if (headerUnread) {
headerUnread.textContent = `${unreadCount} nuevas`;
headerUnread.style.display = unreadCount > 0 ? 'inline-block' : 'none';
}
if (listEl) {
if (notifications.length === 0) {
listEl.innerHTML = '<div style="padding: 1.5rem; text-align: center; color: #64748b; font-size: 0.88rem;">No tienes notificaciones pendientes.</div>';
} else {
listEl.innerHTML = notifications.map(n => `
<div class="notif-item ${n.unread ? 'unread' : ''}" data-id="${n.id}">
<div class="notif-item-icon">${n.icon}</div>
<div class="notif-item-content">
<div class="notif-item-title">${n.title}</div>
<div class="notif-item-desc">${n.desc}</div>
<div class="notif-item-time">${n.time}</div>
</div>
</div>
`).join('');
listEl.querySelectorAll('.notif-item').forEach(item => {
item.addEventListener('click', () => {
const id = item.getAttribute('data-id');
const target = notifications.find(n => n.id === id);
if (target) {
target.unread = false;
saveAndRender();
}
});
});
}
}
}
if (btnBell && dropdown) {
btnBell.addEventListener('click', (e) => {
e.stopPropagation();
dropdown.classList.toggle('active');
});
document.addEventListener('click', (e) => {
if (!dropdown.contains(e.target) && !btnBell.contains(e.target)) {
dropdown.classList.remove('active');
}
});
}
if (btnMarkAll) {
btnMarkAll.addEventListener('click', () => {
notifications.forEach(n => { n.unread = false; });
saveAndRender();
});
}
// Sincronizar enlace de WhatsApp con el número configurado dinámicamente
if (waLink && currentTariffs.numeroAtencionWhatsApp) {
const clean = currentTariffs.numeroAtencionWhatsApp.replace(/\D/g, '');
waLink.href = `https://wa.me/${clean}?text=Hola%20Central%20de%20Despacho%20Pacifictrans%20Go`;
}
saveAndRender();
}