From 695a44781a49de53d4a8afcd08ece7eef793dfe1 Mon Sep 17 00:00:00 2001 From: esdrasrenan Date: Wed, 10 Dec 2025 22:48:18 -0300 Subject: [PATCH] Corrige badge de mensagens nao lidas no chat web e desktop MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Web: markChatRead agora zera unreadByAgent na sessao ativa - Desktop: usa unreadCount do backend ao inves de calcular localmente - Backend: listMachineMessages retorna unreadCount da sessao - Centraliza colunas da tabela de tickets do dispositivo 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- apps/desktop/src/chat/ChatWidget.tsx | 31 +++++++------ convex/liveChat.ts | 4 +- convex/tickets.ts | 12 +++++ .../devices/device-tickets-history.client.tsx | 45 +++++++++++-------- 4 files changed, 55 insertions(+), 37 deletions(-) diff --git a/apps/desktop/src/chat/ChatWidget.tsx b/apps/desktop/src/chat/ChatWidget.tsx index aaa5c9a..01bf472 100644 --- a/apps/desktop/src/chat/ChatWidget.tsx +++ b/apps/desktop/src/chat/ChatWidget.tsx @@ -94,8 +94,9 @@ export function ChatWidget({ ticketId, ticketRef }: ChatWidgetProps) { setIsLoading(false) setHasSession(payload.hasSession) hadSessionRef.current = hadSessionRef.current || payload.hasSession - const unreadMessages = payload.messages.filter(m => !m.isFromMachine) - setUnreadCount(unreadMessages.length) + // Usa o unreadCount do backend (baseado em unreadByMachine da sessao) + const backendUnreadCount = (payload as { unreadCount?: number }).unreadCount ?? 0 + setUnreadCount(backendUnreadCount) setMessages(prev => { const existingIds = new Set(prev.map(m => m.id)) const combined = [...prev, ...payload.messages.filter(m => !existingIds.has(m.id))] @@ -106,11 +107,12 @@ export function ChatWidget({ ticketId, ticketRef }: ChatWidgetProps) { const first = payload.messages[0] setTicketInfo((prevInfo) => prevInfo ?? { ref: 0, subject: "", agentName: first.authorName ?? "Suporte" }) } - // Só marca como lidas se a janela estiver expandida (evita perder badge ao minimizar) - const unreadIds = unreadMessages.map(m => m.id as string) - if (unreadIds.length > 0 && !isMinimized) { - markMachineMessagesRead(ticketId, unreadIds).catch(err => console.error("mark read falhou", err)) - setUnreadCount(0) + // Marca como lidas se a janela estiver expandida e houver nao lidas + if (backendUnreadCount > 0 && !isMinimized) { + const unreadIds = payload.messages.filter(m => !m.isFromMachine).map(m => m.id as string) + if (unreadIds.length > 0) { + markMachineMessagesRead(ticketId, unreadIds).catch(err => console.error("mark read falhou", err)) + } } }, (err) => { @@ -139,15 +141,16 @@ export function ChatWidget({ ticketId, ticketRef }: ChatWidgetProps) { return () => window.removeEventListener("resize", handler) }, []) - // Quando expandir, marcar mensagens como lidas e limpar badge + // Quando expandir, marcar mensagens como lidas (o backend zera unreadByMachine e a reatividade atualiza) useEffect(() => { if (isMinimized) return + if (unreadCount === 0) return const unreadIds = messages.filter(m => !m.isFromMachine).map(m => m.id as string) if (unreadIds.length > 0) { markMachineMessagesRead(ticketId, unreadIds).catch(err => console.error("mark read falhou", err)) } - setUnreadCount(0) - }, [isMinimized, messages, ticketId]) + // Nao setamos unreadCount aqui - o backend vai zerar unreadByMachine e a subscription vai atualizar + }, [isMinimized, messages, ticketId, unreadCount]) // Selecionar arquivo para anexar const handleAttach = async () => { @@ -249,12 +252,8 @@ export function ChatWidget({ ticketId, ticketRef }: ChatWidgetProps) { } catch (err) { console.error("Erro ao expandir janela:", err) } - // Marca mensagens como lidas ao expandir - const unreadIds = messages.filter(m => !m.isFromMachine).map(m => m.id as string) - if (unreadIds.length > 0) { - markMachineMessagesRead(ticketId, unreadIds).catch(err => console.error("mark read falhou", err)) - } - setUnreadCount(0) + // Marca mensagens como lidas ao expandir (o useEffect ja cuida disso quando isMinimized muda) + // O backend zera unreadByMachine e a subscription atualiza automaticamente } const handleClose = () => { diff --git a/convex/liveChat.ts b/convex/liveChat.ts index 8009d20..e781be7 100644 --- a/convex/liveChat.ts +++ b/convex/liveChat.ts @@ -467,7 +467,7 @@ export const listMachineMessages = query({ .first() if (!session) { - return { messages: [], hasSession: false } + return { messages: [], hasSession: false, unreadCount: 0 } } // Aplicar limite (máximo 100 mensagens por chamada) @@ -505,7 +505,7 @@ export const listMachineMessages = query({ } }) - return { messages: result, hasSession: true } + return { messages: result, hasSession: true, unreadCount: session.unreadByMachine ?? 0 } }, }) diff --git a/convex/tickets.ts b/convex/tickets.ts index 1107660..a76ceed 100644 --- a/convex/tickets.ts +++ b/convex/tickets.ts @@ -3438,6 +3438,18 @@ export const markChatRead = mutation({ updatedAt: now, }) } + + // Zerar contador de nao lidas pelo agente na sessao ativa + const session = await ctx.db + .query("liveChatSessions") + .withIndex("by_ticket", (q) => q.eq("ticketId", ticketId)) + .filter((q) => q.eq(q.field("status"), "ACTIVE")) + .first() + + if (session) { + await ctx.db.patch(session._id, { unreadByAgent: 0 }) + } + return { ok: true } }, }) diff --git a/src/components/admin/devices/device-tickets-history.client.tsx b/src/components/admin/devices/device-tickets-history.client.tsx index ce07751..13aaa60 100644 --- a/src/components/admin/devices/device-tickets-history.client.tsx +++ b/src/components/admin/devices/device-tickets-history.client.tsx @@ -353,23 +353,30 @@ export function DeviceTicketsHistoryClient({ tenantId: _tenantId, deviceId }: { ) : ( <>
- - - - +
+ + + + + + + + + + Ticket - + Status - + Prioridade - - Última atualização + + Ultima atualizacao - - Responsável + + Responsavel @@ -381,15 +388,15 @@ export function DeviceTicketsHistoryClient({ tenantId: _tenantId, deviceId }: { const updatedAbsolute = formatAbsoluteTime(ticket.updatedAt) return ( - -
+ +
#{ticket.reference} · {ticket.subject} -
+
{requesterLabel} {ticket.queue ? ( @@ -400,22 +407,22 @@ export function DeviceTicketsHistoryClient({ tenantId: _tenantId, deviceId }: {
- + - + {priorityMeta.label} - -
+ +
{updatedLabel} {updatedAbsolute}
- -
+ +
{ticket.assignee ? ( <> {ticket.assignee.name ?? ticket.assignee.email ?? "—"}