import { useEffect, useMemo, useState } from 'react'; import { useNavigate } from 'react-router-dom'; import { createAgentNote, deleteAgentNote, listAgentNotes } from '../services/agentNotesService'; import { getCurrentUser } from '../../auth/services/sessionService'; const WORKSPACE_HEIGHT = 660; function ChannelBadge({ channel }) { const colors = { WhatsApp: '#2bb741', Email: '#e5a22a', SMS: '#00a4b7', }; return ( {channel} ); } function UnreadBadge({ count }) { if (!count) return null; return ( {count > 99 ? '99+' : count} ); } function buildSuggestedReplies(conversation) { const lastMessage = conversation?.lastMessage || conversation?.messages?.at(-1)?.text || ''; const firstName = conversation?.name?.split(' ')?.[0] || 'você'; const lowerContext = lastMessage.toLowerCase(); if ( lowerContext.includes('fatura') || lowerContext.includes('cobranca') || lowerContext.includes('pagamento') ) { return [ `${firstName}, vou conferir os dados financeiros e já te retorno com a posição correta.`, 'Recebi sua mensagem sobre cobrança. Vou validar o histórico antes de seguir com a orientação.', 'Consigo te ajudar com isso. Pode me confirmar o CPF/CNPJ ou protocolo vinculado ao atendimento?', ]; } if ( lowerContext.includes('endereco') || lowerContext.includes('cadastro') || lowerContext.includes('atualizar') ) { return [ `${firstName}, vou validar seu cadastro e confirmar se a alteração já foi registrada.`, 'Para seguir com a atualização, me confirme por favor os dados que precisam ser ajustados.', 'Entendi. Vou verificar o cadastro atual e te retorno com o próximo passo.', ]; } if ( lowerContext.includes('ligar') || lowerContext.includes('telefone') || lowerContext.includes('retorno') ) { return [ `${firstName}, consigo organizar esse retorno. Qual o melhor horário para contato?`, 'Vou registrar sua solicitação e direcionar o retorno para o time responsável.', 'Obrigado pelo aviso. Vou confirmar disponibilidade e te retorno por aqui.', ]; } return [ `${firstName}, recebi sua mensagem e vou verificar o contexto para te orientar corretamente.`, 'Entendi. Vou analisar as informações do atendimento e retorno com o melhor encaminhamento.', 'Posso acionar o time responsável e te atualizar por aqui assim que tiver uma posição.', ]; } function parseMessageText(text) { const rawText = String(text || ''); const match = rawText.match(/^\*(Atendente(?: virtual)?:\s*[^*]+)\*\s*\n+/i); if (!match) { return { senderLabel: null, body: rawText }; } return { senderLabel: match[1], body: rawText.slice(match[0].length), }; } function formatMessageTime(timestamp) { if (!timestamp) return ''; const numericTimestamp = Number(timestamp); const date = new Date(numericTimestamp > 1000000000000 ? numericTimestamp : numericTimestamp * 1000); return date.toLocaleTimeString('pt-BR', { hour: '2-digit', minute: '2-digit' }); } function getUserId(user) { const value = user?.databaseId || user?.id; const numeric = Number(value); return Number.isFinite(numeric) ? numeric : null; } export function MessagesWorkspace({ conversations, activeConversationId, onSelectConversation, onSendSuggestedReply, isWideDesktop = false, isDesktop = false, isTablet = false, isMobile = false, }) { const navigate = useNavigate(); const currentUser = getCurrentUser(); const currentUserId = getUserId(currentUser); const recentConversations = conversations.slice(0, 3); const activeConversation = recentConversations.find((conversation) => conversation.id === activeConversationId) || recentConversations[0] || conversations[0]; const safeActiveConversation = activeConversation || { id: 'empty', name: 'Nenhuma conversa', status: 'offline', messages: [], }; const suggestedReplies = useMemo( () => buildSuggestedReplies(safeActiveConversation), [safeActiveConversation], ); const [selectedReplyIndex, setSelectedReplyIndex] = useState(0); const [noteDraft, setNoteDraft] = useState(''); const [notes, setNotes] = useState([]); const [notesError, setNotesError] = useState(''); const selectedReply = suggestedReplies[selectedReplyIndex] || suggestedReplies[0]; const managerMessages = [ { id: 'sla', title: 'Comunicado do supervisor', text: 'Priorizar atendimentos com SLA abaixo de 15 minutos antes de abrir novos casos.', }, { id: 'script', title: 'Atualização de script', text: 'Use o novo roteiro de confirmação de dados em atendimentos financeiros.', }, ]; useEffect(() => { setSelectedReplyIndex(0); }, [safeActiveConversation.id]); useEffect(() => { let isMounted = true; async function loadNotes() { try { const data = await listAgentNotes(currentUserId); if (isMounted) { setNotes(Array.isArray(data) ? data : []); setNotesError(''); } } catch (error) { if (isMounted) setNotesError(error.message); } } loadNotes(); return () => { isMounted = false; }; }, [currentUserId]); function selectPreviousReply() { setSelectedReplyIndex((current) => current === 0 ? suggestedReplies.length - 1 : current - 1, ); } function selectNextReply() { setSelectedReplyIndex((current) => (current + 1) % suggestedReplies.length); } async function saveNote() { const text = noteDraft.trim(); if (!text || !currentUserId) return; try { const note = await createAgentNote(currentUserId, text); setNotes((current) => [note, ...current]); setNoteDraft(''); setNotesError(''); } catch (error) { setNotesError(error.message); } } async function removeNote(noteId) { if (!currentUserId) return; try { await deleteAgentNote(currentUserId, noteId); setNotes((current) => current.filter((note) => note.id !== noteId)); setNotesError(''); } catch (error) { setNotesError(error.message); } } async function sendSuggestedReply() { if (!safeActiveConversation.id || safeActiveConversation.id === 'empty') return; await onSendSuggestedReply?.(safeActiveConversation.id, selectedReply); navigate(`/chat?chatId=${encodeURIComponent(safeActiveConversation.id)}`); } const gridTemplateColumns = isMobile ? '1fr' : isWideDesktop ? 'minmax(240px, 0.95fr) minmax(360px, 1.8fr) minmax(220px, 0.8fr)' : isDesktop || isTablet ? 'minmax(260px, 320px) minmax(0, 1fr)' : '1fr'; const panelHeight = isMobile ? 'auto' : WORKSPACE_HEIGHT; return (
Últimos 3 atendimentos em tempo real.