// ============================================================================== // SHIFT ERP - MODULO INDEPENDIENTE BANDEJA & CHAT WHATSAPP WEB // ============================================================================== const formatWhatsAppChatTime = (timestamp) => { if (!timestamp) return ''; const date = new Date(Number(timestamp)); if (isNaN(date.getTime())) return ''; const now = new Date(); const diffMs = now.getTime() - date.getTime(); if (diffMs < 0) return 'Ahora'; const diffSec = Math.floor(diffMs / 1000); const diffMin = Math.floor(diffSec / 60); const diffHours = Math.floor(diffMin / 60); const diffDays = Math.floor(diffHours / 24); // Menos de 1 minuto if (diffMin < 1) return 'Ahora'; // Menos de 60 minutos if (diffMin < 60) return `Hace ${diffMin} min`; // Mismo día calendario const isToday = now.toDateString() === date.toDateString(); if (isToday) { if (diffHours < 12) return `Hace ${diffHours} h`; return date.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }); } // Ayer const yesterday = new Date(now); yesterday.setDate(yesterday.getDate() - 1); if (yesterday.toDateString() === date.toDateString()) { return 'Ayer'; } // De 2 a 30 días atrás if (diffDays >= 2 && diffDays <= 30) { return `Hace ${diffDays} días`; } // Más de 30 días: fecha corta DD/MM/YY const d = String(date.getDate()).padStart(2, '0'); const m = String(date.getMonth() + 1).padStart(2, '0'); const y = String(date.getFullYear()).slice(-2); return `${d}/${m}/${y}`; }; window.AdminWhatsAppModal = function({ isOpen, onClose, activeClientId, adminAlerts = { unread_count: 0, alerts: [] }, fetchAdminAlerts = () => {}, showToast = () => {}, initialSelectedUser = null }) { if (!isOpen) return null; const [conversations, setConversations] = React.useState([]); const [chatSearchQuery, setChatSearchQuery] = React.useState(''); const [selectedChatUser, setSelectedChatUser] = React.useState(initialSelectedUser); const [chatHistory, setChatHistory] = React.useState([]); const [adminReplyText, setAdminReplyText] = React.useState(''); const [isSendingReply, setIsSendingReply] = React.useState(false); const chatEndRef = React.useRef(null); const fetchConversations = React.useCallback(async () => { try { const res = await window.apiCall('get_chat_conversations', {}, activeClientId); if (res && res.success) { setConversations(res.conversations || []); } } catch (e) {} }, [activeClientId]); const handleLoadChatHistory = React.useCallback(async (userObj) => { if (!userObj) return; setSelectedChatUser(userObj); try { const res = await window.apiCall('get_chat_history', { user_id: userObj.user_id || userObj.id || 0, phone: userObj.phone || userObj.sender_phone || '' }, activeClientId); if (res && res.success) { setChatHistory(res.messages || []); if (typeof fetchAdminAlerts === 'function') fetchAdminAlerts(); setConversations(prev => prev.map(c => { const match = (userObj.user_id && c.user_id === userObj.user_id) || (userObj.phone && c.phone === userObj.phone); return match ? { ...c, unread_count: 0 } : c; })); } } catch (e) {} }, [activeClientId, fetchAdminAlerts]); React.useEffect(() => { if (isOpen) { fetchConversations(); fetchAdminAlerts(); if (initialSelectedUser) { handleLoadChatHistory(initialSelectedUser); } } }, [isOpen, fetchConversations, fetchAdminAlerts, initialSelectedUser, handleLoadChatHistory]); // Polling en vivo mientras el modal está abierto React.useEffect(() => { if (!isOpen) return; const interval = setInterval(() => { fetchConversations(); fetchAdminAlerts(); if (selectedChatUser) { handleLoadChatHistory(selectedChatUser); } }, 3500); return () => clearInterval(interval); }, [isOpen, fetchConversations, fetchAdminAlerts, selectedChatUser, handleLoadChatHistory]); // Auto-scroll al fondo al actualizar historial React.useEffect(() => { if (chatEndRef.current) { chatEndRef.current.scrollIntoView({ behavior: 'smooth' }); } }, [chatHistory]); const handleMarkAlertRead = async (alertId = 0) => { try { await window.apiCall('mark_alert_read', { alert_id: alertId }, activeClientId); await fetchAdminAlerts(); await fetchConversations(); } catch (e) {} }; const handleSendAdminReply = async () => { if (!selectedChatUser || !adminReplyText.trim()) return; setIsSendingReply(true); try { const res = await window.apiCall('send_admin_chat_reply', { user_id: selectedChatUser.user_id || selectedChatUser.id || 0, phone: selectedChatUser.phone || selectedChatUser.sender_phone || '', message: adminReplyText.trim() }, activeClientId); if (res && res.success) { if (typeof showToast === 'function') showToast("✅ Respuesta enviada vía WhatsApp."); setAdminReplyText(''); await handleLoadChatHistory(selectedChatUser); await fetchConversations(); } else { if (typeof showToast === 'function') showToast("⚠️ Error: " + (res?.error || "Fallo de envío")); } } catch (e) { if (typeof showToast === 'function') showToast("Error de conexión al responder."); } finally { setIsSendingReply(false); } }; return (
e.stopPropagation()}> {/* MAIN LAYOUT 2 COLUMNAS WHATSAPP WEB */}
{/* COLUMNA IZQUIERDA: BUZÓN DE CONVERSACIONES / CONTACTOS */}
{/* HEADER DE BARRA IZQUIERDA */}

Buzón WhatsApp

Conversaciones & Alertas

{adminAlerts.unread_count > 0 && (
{adminAlerts.unread_count} no leídas
)}
{/* BUSCADOR DE CONTACTOS */}
setChatSearchQuery(e.target.value)} placeholder="Buscar colaborador o mensaje..." className="bg-transparent border-none text-white text-xs placeholder-[#8696a0] focus:outline-none w-full font-medium" /> {chatSearchQuery && ( )}
{/* LISTA DE BUZÓN DE CHATS POR USUARIO */}
{conversations.length === 0 ? (
Cargando conversaciones de la empresa...
) : ( conversations .filter(c => { if (!chatSearchQuery.trim()) return true; const q = chatSearchQuery.toLowerCase(); return (c.name || '').toLowerCase().includes(q) || (c.phone || '').includes(q) || (c.last_message || '').toLowerCase().includes(q); }) .map(conv => { const isSelected = selectedChatUser && ( (conv.user_id > 0 && (selectedChatUser.user_id === conv.user_id || selectedChatUser.id === conv.user_id)) || (conv.user_id === 0 && conv.phone && conv.phone.length >= 8 && (selectedChatUser.phone === conv.phone || selectedChatUser.sender_phone === conv.phone)) ); return (
0 ? `u_${conv.user_id}` : `p_${conv.phone}`} onClick={() => { if (conv.unread_count > 0) handleMarkAlertRead(0); handleLoadChatHistory(conv); }} className={`p-3 flex items-center gap-3 transition-colors cursor-pointer border-l-4 ${isSelected ? 'bg-[#2a3942] border-[#00a884]' : conv.unread_count > 0 ? 'bg-[#182229] border-[#00a884] hover:bg-[#202c33]' : 'border-transparent hover:bg-[#202c33]'}`} > {/* AVATAR */}
{(conv.name || 'U')[0].toUpperCase()} {conv.unread_count > 0 && ( {conv.unread_count} )}
{/* DETALLES CONVERSACIÓN */}
{conv.name} {conv.last_time && ( {formatWhatsAppChatTime(conv.last_time)} )}

{conv.direction === 'outgoing' && ✓✓} {conv.last_message}

{conv.role} {conv.phone ? `• +${conv.phone}` : ''}
); }) )}
{/* COLUMNA DERECHA: VENTANA DE CHAT WHATSAPP WEB */}
{selectedChatUser ? (
{/* HEADER DEL CHAT ACTIVO */}
{(selectedChatUser.name || 'U')[0].toUpperCase()}

{selectedChatUser.name}

{selectedChatUser.role || 'Colaborador'} {selectedChatUser.phone ? `• +${selectedChatUser.phone}` : ''}
{/* MENSAJES (ESTILO BUBBLE WHATSAPP WEB, RECIENTES ABAJO) */}
{/* Fondo de patrón WhatsApp sutil */}
{chatHistory.length === 0 ? (
No hay mensajes registrados con este usuario.
) : ( chatHistory.map(m => { const isIncoming = m.direction === 'incoming'; return (
{m.sender_name || (isIncoming ? selectedChatUser.name : 'Shift ERP')} {window.formatNICTimeFull ? window.formatNICTimeFull(m.created_at) : new Date(m.created_at).toLocaleTimeString()}

{m.message_text || m.message_body || m.message || ''}

{!isIncoming && (
✓✓
)}
); }) )}
{/* CAJA DE ENVIAR MENSAJE (WHATSAPP WEB BAR) */}
setAdminReplyText(e.target.value)} onKeyDown={e => { if (e.key === 'Enter') handleSendAdminReply(); }} placeholder={`Escribe un mensaje a ${selectedChatUser.name} por WhatsApp...`} className="flex-1 px-4 py-2.5 bg-[#2a3942] border border-[#374248] rounded-xl text-xs text-white placeholder-[#8696a0] focus:outline-none focus:border-[#00a884] focus:bg-[#202c33] font-medium transition-all" />
) : (

Shift ERP • WhatsApp Web

Selecciona un colaborador del buzón de la izquierda para abrir su chat en vivo, revisar el historial de mensajes y enviar notificaciones o respuestas por WhatsApp.

)}
); };