Implementa sistema de chat em tempo real entre agente e cliente

- Adiciona tabela liveChatSessions no schema Convex
- Cria convex/liveChat.ts com mutations e queries para chat
- Adiciona API routes para maquinas (sessions, messages, poll)
- Cria modulo chat.rs no Tauri com ChatRuntime e polling
- Adiciona comandos de chat no lib.rs (start/stop polling, open/close window)
- Cria componentes React do chat widget (ChatWidget, types)
- Adiciona botao "Iniciar Chat" no dashboard (ticket-chat-panel)
- Implementa menu de chat no system tray
- Polling de 2 segundos para maior responsividade
- Janela de chat flutuante, frameless, always-on-top

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
esdrasrenan 2025-12-07 01:00:27 -03:00
parent 0c8d53c0b6
commit ba91c1e0f5
15 changed files with 2004 additions and 15 deletions

View file

@ -2956,12 +2956,59 @@ export const listChatMessages = query({
.withIndex("by_ticket_created", (q) => q.eq("ticketId", ticketId))
.collect()
// Verificar maquina e sessao de chat ao vivo
let liveChat: {
hasMachine: boolean
machineOnline: boolean
machineHostname: string | null
activeSession: {
sessionId: Id<"liveChatSessions">
agentId: Id<"users">
agentName: string | null
startedAt: number
unreadByAgent: number
} | null
} = {
hasMachine: false,
machineOnline: false,
machineHostname: null,
activeSession: null,
}
if (ticketDoc.machineId) {
const machine = await ctx.db.get(ticketDoc.machineId)
if (machine) {
const fiveMinutesAgo = now - 5 * 60 * 1000
liveChat.hasMachine = true
liveChat.machineOnline = Boolean(machine.lastHeartbeatAt && machine.lastHeartbeatAt > fiveMinutesAgo)
liveChat.machineHostname = machine.hostname
// Verificar sessao ativa
const activeSession = await ctx.db
.query("liveChatSessions")
.withIndex("by_ticket", (q) => q.eq("ticketId", ticketId))
.filter((q) => q.eq(q.field("status"), "ACTIVE"))
.first()
if (activeSession) {
liveChat.activeSession = {
sessionId: activeSession._id,
agentId: activeSession.agentId,
agentName: activeSession.agentSnapshot?.name ?? null,
startedAt: activeSession.startedAt,
unreadByAgent: activeSession.unreadByAgent ?? 0,
}
}
}
}
return {
ticketId: String(ticketId),
chatEnabled,
status,
canPost,
reopenDeadline: ticketDoc.reopenDeadline ?? null,
liveChat,
messages: messages
.sort((a, b) => a.createdAt - b.createdAt)
.map((message) => ({