Corrige comportamentos do chat e melhora UX
- Corrige contador de mensagens resetando sozinho (web) - Adiciona verificacao de visibilidade antes de marcar como lido - Verifica se aba esta ativa antes de marcar como lido - Adiciona sincronizacao de estado do chat entre abas (web) - Usa BroadcastChannel para sincronizar aberto/fechado/minimizado - Persiste estado no localStorage - Corrige chat minimizando sozinho no desktop (Rust) - Verifica se chat esta expandido antes de minimizar - Mantem chat aberto quando usuario esta usando - Melhora encerramento automatico de sessoes de chat - Muda criterio de inatividade de chat para maquina offline - Sessao permanece ativa enquanto Raven estiver aberto - Encerra apenas quando maquina fica offline por 5+ min - Corrige tabela de tickets em /devices - Adiciona acentuacao correta (Ultima atualizacao, Responsavel) - Torna linha inteira clicavel para abrir ticket - Ajusta sidebar - Menu Tickets agora expande ao clicar (igual Cadastros) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
2682b6e8ac
commit
f4880f32d2
7 changed files with 219 additions and 37 deletions
|
|
@ -753,43 +753,78 @@ export const getTicketChatHistory = query({
|
|||
// ENCERRAMENTO AUTOMATICO POR INATIVIDADE
|
||||
// ============================================
|
||||
|
||||
// Timeout de inatividade: 5 minutos
|
||||
const INACTIVITY_TIMEOUT_MS = 5 * 60 * 1000
|
||||
// Timeout de maquina offline: 5 minutos sem heartbeat
|
||||
const MACHINE_OFFLINE_TIMEOUT_MS = 5 * 60 * 1000
|
||||
|
||||
// Mutation interna para encerrar sessões inativas (chamada pelo cron)
|
||||
// Otimizada com paginação para evitar timeout
|
||||
// Mutation interna para encerrar sessões de máquinas offline (chamada pelo cron)
|
||||
// Nova lógica: só encerra se a MÁQUINA estiver offline, não por inatividade de chat
|
||||
// Isso permite que usuários mantenham o chat aberto sem precisar enviar mensagens
|
||||
export const autoEndInactiveSessions = mutation({
|
||||
args: {},
|
||||
handler: async (ctx) => {
|
||||
// Log obrigatorio para evitar shape_inference errors com logLines vazios
|
||||
console.log("cron: autoEndInactiveSessions iniciado")
|
||||
console.log("cron: autoEndInactiveSessions iniciado (verificando maquinas offline)")
|
||||
const now = Date.now()
|
||||
const cutoffTime = now - INACTIVITY_TIMEOUT_MS
|
||||
const offlineCutoff = now - MACHINE_OFFLINE_TIMEOUT_MS
|
||||
|
||||
// Limitar a 50 sessões por execução para evitar timeout do cron (30s)
|
||||
const maxSessionsPerRun = 50
|
||||
|
||||
// Buscar sessões ativas com inatividade > 5 minutos (usando índice otimizado)
|
||||
const inactiveSessions = await ctx.db
|
||||
// Buscar todas as sessões ativas
|
||||
const activeSessions = await ctx.db
|
||||
.query("liveChatSessions")
|
||||
.withIndex("by_status_lastActivity", (q) =>
|
||||
q.eq("status", "ACTIVE").lt("lastActivityAt", cutoffTime)
|
||||
)
|
||||
.withIndex("by_status_lastActivity", (q) => q.eq("status", "ACTIVE"))
|
||||
.take(maxSessionsPerRun)
|
||||
|
||||
let endedCount = 0
|
||||
let checkedCount = 0
|
||||
|
||||
for (const session of inactiveSessions) {
|
||||
// Encerrar a sessão
|
||||
for (const session of activeSessions) {
|
||||
checkedCount++
|
||||
|
||||
// Buscar o ticket para obter a máquina
|
||||
const ticket = await ctx.db.get(session.ticketId)
|
||||
if (!ticket || !ticket.machineId) {
|
||||
// Ticket sem máquina - encerrar sessão órfã
|
||||
await ctx.db.patch(session._id, {
|
||||
status: "ENDED",
|
||||
endedAt: now,
|
||||
})
|
||||
await ctx.db.insert("ticketEvents", {
|
||||
ticketId: session.ticketId,
|
||||
type: "LIVE_CHAT_ENDED",
|
||||
payload: {
|
||||
sessionId: session._id,
|
||||
agentId: session.agentId,
|
||||
agentName: session.agentSnapshot?.name ?? "Sistema",
|
||||
durationMs: now - session.startedAt,
|
||||
startedAt: session.startedAt,
|
||||
endedAt: now,
|
||||
autoEnded: true,
|
||||
reason: "ticket_sem_maquina",
|
||||
},
|
||||
createdAt: now,
|
||||
})
|
||||
endedCount++
|
||||
continue
|
||||
}
|
||||
|
||||
// Verificar heartbeat da máquina
|
||||
const lastHeartbeatAt = await getLastHeartbeatAt(ctx, ticket.machineId)
|
||||
const machineIsOnline = lastHeartbeatAt !== null && lastHeartbeatAt > offlineCutoff
|
||||
|
||||
// Se máquina está online, manter sessão ativa
|
||||
if (machineIsOnline) {
|
||||
continue
|
||||
}
|
||||
|
||||
// Máquina está offline - encerrar sessão
|
||||
await ctx.db.patch(session._id, {
|
||||
status: "ENDED",
|
||||
endedAt: now,
|
||||
})
|
||||
|
||||
// Calcular duração da sessão
|
||||
const durationMs = now - session.startedAt
|
||||
|
||||
// Registrar evento na timeline
|
||||
await ctx.db.insert("ticketEvents", {
|
||||
ticketId: session.ticketId,
|
||||
type: "LIVE_CHAT_ENDED",
|
||||
|
|
@ -800,8 +835,8 @@ export const autoEndInactiveSessions = mutation({
|
|||
durationMs,
|
||||
startedAt: session.startedAt,
|
||||
endedAt: now,
|
||||
autoEnded: true, // Flag para indicar encerramento automático
|
||||
reason: "inatividade",
|
||||
autoEnded: true,
|
||||
reason: "maquina_offline",
|
||||
},
|
||||
createdAt: now,
|
||||
})
|
||||
|
|
@ -809,7 +844,8 @@ export const autoEndInactiveSessions = mutation({
|
|||
endedCount++
|
||||
}
|
||||
|
||||
return { endedCount, hasMore: inactiveSessions.length === maxSessionsPerRun }
|
||||
console.log(`cron: verificadas ${checkedCount} sessoes, encerradas ${endedCount} (maquinas offline)`)
|
||||
return { endedCount, checkedCount, hasMore: activeSessions.length === maxSessionsPerRun }
|
||||
},
|
||||
})
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue