diff --git a/conversations.php b/conversations.php index 0f30cf3..0ee1bb0 100644 --- a/conversations.php +++ b/conversations.php @@ -564,6 +564,22 @@ .notification-toast.urgent { border-left: 4px solid #e74c3c; } + /* Visual destacado para notificaciones de tipo "attention" (ej. usuario subió documentos) */ + .notification-toast.attention { + background: linear-gradient(90deg, #fff7e6, #fff3e0); + border: 1px solid rgba(255,165,0,0.18); + color: #333; + box-shadow: 0 8px 24px rgba(255,165,0,0.06); + min-width: 240px; + } + .notification-toast.attention .nt-icon { + font-size: 18px; + margin-right: 8px; + transform: translateY(-1px); + color: #ff9900; + } + .notification-toast.attention .nt-actions .btn { min-width: 70px; } + .message-template { background: #fffacd; border: 1px solid #f0e68c; @@ -572,6 +588,31 @@ margin-bottom: 10px; font-size: 13px; } + .message-template.system { + background: linear-gradient(90deg,#fff4e6,#fffaf0); + border: 1px solid rgba(255,165,0,0.18); + color: #5c3a00; + font-weight: 600; + display: flex; + gap: 8px; + align-items: center; + animation: systemPop 320ms ease; + } + .message-template.system .mt-icon { font-size:16px; margin-right:6px; } + @keyframes systemPop { from { transform: translateY(6px); opacity:0 } to { transform: translateY(0); opacity:1 } } + + /* Highlight animation for newly inserted system messages */ + .message-template.system.system-highlight { + box-shadow: 0 10px 30px rgba(255,165,0,0.12); + border-color: rgba(255,165,0,0.28); + transform-origin: center left; + animation: pulseHighlight 1s ease both; + } + @keyframes pulseHighlight { + 0% { transform: translateY(6px) scale(.995); opacity: 0.95; } + 50% { transform: translateY(0) scale(1.01); opacity: 1; } + 100% { transform: translateY(0) scale(1); opacity: 1; } + } .unread-count { background: var(--whatsapp-green); @@ -1066,12 +1107,20 @@ const toast = document.createElement('div'); // compact by default toast.className = 'notification-toast small'; + // Detect special "attention" notifications (e.g. user sent documents or similar system events) + const isAttention = ( + // explicit attention flags + notification.type === 'attention' || notification.system === 'attention' || notification.level === 'attention' || notification.attention || + // known event types/tags for user-sent documents + notification.type === 'usersentdocuments' || notification.tag === 'usersentdocuments' || notification.system === 'usersentdocuments' + ); if (notification.cool || notification.type === 'cool') toast.classList.add('cool'); if (notification.level === 'urgent' || notification.urgent) toast.classList.add('urgent'); + if (isAttention) toast.classList.add('attention'); const iconSpan = document.createElement('span'); iconSpan.className = 'nt-icon'; - iconSpan.textContent = notification.icon || (notification.cool ? '✨' : (notification.level === 'urgent' ? '⚠️' : '🔔')); + iconSpan.textContent = notification.icon || (isAttention ? '📎' : (notification.cool ? '✨' : (notification.level === 'urgent' ? '⚠️' : '🔔'))); toast.appendChild(iconSpan); const msg = document.createElement('div'); @@ -1090,8 +1139,8 @@ }; const openBtn = document.createElement('button'); - openBtn.className = notification.cool ? 'btn btn-sm btn-light' : 'btn btn-sm btn-primary'; - openBtn.textContent = 'Abrir'; + openBtn.className = notification.cool ? 'btn btn-sm btn-light' : (isAttention ? 'btn btn-sm btn-warning' : 'btn btn-sm btn-primary'); + openBtn.textContent = (notification.open_label && notification.open_label !== 'Ver') ? notification.open_label : 'Abrir'; openBtn.onclick = async () => { // Try ack on server; if fails, schedule retry. Always mark as dismissed locally so it won't reappear. try { @@ -1104,11 +1153,14 @@ } } catch (e) { this._pendingAck.set(nid, notification); } removeToastLocal(); - // navigate to conv if present + // navigate to conv or open media if present let data = {}; try { data = notification.data ? JSON.parse(notification.data) : {}; } catch(e) {} const userId = data.user_id || notification.user_id; - if (userId) { + const fileUrl = data.file_url || data.url || null; + if (fileUrl && this.currentUserId && String(this.currentUserId) === String(userId)) { + try { openMediaLightbox(fileUrl, data.file_name || ''); } catch (e) { console.warn('openMediaLightbox failed', e); } + } else if (userId) { const conv = this.conversations.find(c => c.user_id == userId); if (conv) { this.openConversation(conv.user_id, conv.name, conv.phone_number); @@ -1116,7 +1168,7 @@ this.openConversation(userId, 'Usuario', ''); } } - }; + }; const dismiss = document.createElement('button'); dismiss.className = 'btn btn-sm btn-outline-secondary'; @@ -1141,6 +1193,15 @@ container.appendChild(toast); + // If attention notification and related to current open conversation, insert a system message into the chat view + try { + const dataObj = notification.data ? (typeof notification.data === 'string' ? JSON.parse(notification.data) : notification.data) : {}; + const userId = dataObj.user_id || notification.user_id; + if (isAttention && userId && this.currentUserId && String(this.currentUserId) === String(userId) && typeof this.showSystemNotificationInChat === 'function') { + this.showSystemNotificationInChat(notification); + } + } catch (e) { console.warn('showNotificationToast -> showSystemNotificationInChat failed', e); } + // Auto remove (shorter for compact notifications) with minimum enforced const _minToastDuration = 3000; let timeout = notification.duration ? Number(notification.duration) : (notification.cool ? 15000 : 8000); @@ -1163,6 +1224,82 @@ }, timeout); } + showSystemNotificationInChat(notification) { + try { + const data = notification.data ? (typeof notification.data === 'string' ? JSON.parse(notification.data) : notification.data) : {}; + const userId = data.user_id || notification.user_id; + // Only show in currently open conversation + if (!userId || String(this.currentUserId) !== String(userId)) return; + + const container = document.getElementById('chat-conversations'); + if (!container) return; + + const el = document.createElement('div'); + el.className = 'message incoming'; + el.dataset.notificationId = String(notification.id || ''); + + const bubble = document.createElement('div'); + bubble.className = 'message-bubble message-template system'; + + // Friendly title and message (prefer explicit fields if present) + const title = notification.title || data.title || 'Documentos recibidos'; + const msg = notification.message || data.text || data.message || (data.summary || 'El usuario envió documentos.'); + + // File info (if available) + const fileName = data.file_name || data.filename || data.file || null; + const fileUrl = data.file_url || data.url || null; + const fileSize = data.file_size || data.size || null; + + // Build inner HTML + const parts = []; + parts.push(``); + parts.push(`