feat(chat): vincula timer automaticamente com inicio/fim do chat ao vivo
- Ao iniciar chat: inicia timer EXTERNAL automaticamente se nao houver sessao ativa - Ao encerrar chat: pausa timer automaticamente se houver sessao ativa - Adiciona razao de pausa END_LIVE_CHAT para identificar pausas automaticas 🤖 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
47ccdc51a7
commit
14480df9f3
2 changed files with 89 additions and 2 deletions
|
|
@ -168,7 +168,40 @@ export const startSession = mutation({
|
|||
createdAt: now,
|
||||
})
|
||||
|
||||
return { sessionId, isNew: true }
|
||||
// Iniciar timer automaticamente se nao houver sessao de trabalho ativa
|
||||
// O chat ao vivo eh considerado trabalho EXTERNAL (interacao com cliente)
|
||||
let workSessionId: Id<"ticketWorkSessions"> | null = null
|
||||
if (!ticket.activeSessionId && ticket.assigneeId) {
|
||||
workSessionId = await ctx.db.insert("ticketWorkSessions", {
|
||||
ticketId,
|
||||
agentId: ticket.assigneeId,
|
||||
workType: "EXTERNAL",
|
||||
startedAt: now,
|
||||
})
|
||||
|
||||
await ctx.db.patch(ticketId, {
|
||||
working: true,
|
||||
activeSessionId: workSessionId,
|
||||
status: "AWAITING_ATTENDANCE",
|
||||
updatedAt: now,
|
||||
})
|
||||
|
||||
await ctx.db.insert("ticketEvents", {
|
||||
ticketId,
|
||||
type: "WORK_STARTED",
|
||||
payload: {
|
||||
actorId,
|
||||
actorName: agent.name,
|
||||
actorAvatar: agent.avatarUrl,
|
||||
sessionId: workSessionId,
|
||||
workType: "EXTERNAL",
|
||||
source: "live_chat_auto",
|
||||
},
|
||||
createdAt: now,
|
||||
})
|
||||
}
|
||||
|
||||
return { sessionId, isNew: true, workSessionStarted: workSessionId !== null }
|
||||
},
|
||||
})
|
||||
|
||||
|
|
@ -225,7 +258,60 @@ export const endSession = mutation({
|
|||
createdAt: now,
|
||||
})
|
||||
|
||||
return { ok: true }
|
||||
// Pausar timer automaticamente se houver sessao de trabalho ativa
|
||||
let workSessionPaused = false
|
||||
const ticket = await ctx.db.get(session.ticketId)
|
||||
if (ticket?.activeSessionId) {
|
||||
const workSession = await ctx.db.get(ticket.activeSessionId)
|
||||
if (workSession && !workSession.stoppedAt) {
|
||||
const workDurationMs = now - workSession.startedAt
|
||||
const sessionType = (workSession.workType ?? "INTERNAL").toUpperCase()
|
||||
const deltaInternal = sessionType === "INTERNAL" ? workDurationMs : 0
|
||||
const deltaExternal = sessionType === "EXTERNAL" ? workDurationMs : 0
|
||||
|
||||
// Encerrar sessao de trabalho
|
||||
await ctx.db.patch(ticket.activeSessionId, {
|
||||
stoppedAt: now,
|
||||
durationMs: workDurationMs,
|
||||
pauseReason: "END_LIVE_CHAT",
|
||||
pauseNote: "Pausa automática ao encerrar chat ao vivo",
|
||||
})
|
||||
|
||||
// Atualizar ticket
|
||||
await ctx.db.patch(session.ticketId, {
|
||||
working: false,
|
||||
activeSessionId: undefined,
|
||||
status: "PAUSED",
|
||||
totalWorkedMs: (ticket.totalWorkedMs ?? 0) + workDurationMs,
|
||||
internalWorkedMs: (ticket.internalWorkedMs ?? 0) + deltaInternal,
|
||||
externalWorkedMs: (ticket.externalWorkedMs ?? 0) + deltaExternal,
|
||||
updatedAt: now,
|
||||
})
|
||||
|
||||
// Registrar evento de pausa
|
||||
await ctx.db.insert("ticketEvents", {
|
||||
ticketId: session.ticketId,
|
||||
type: "WORK_PAUSED",
|
||||
payload: {
|
||||
actorId,
|
||||
actorName: actor.name,
|
||||
actorAvatar: actor.avatarUrl,
|
||||
sessionId: workSession._id,
|
||||
sessionDurationMs: workDurationMs,
|
||||
workType: sessionType,
|
||||
pauseReason: "END_LIVE_CHAT",
|
||||
pauseReasonLabel: "Chat ao vivo encerrado",
|
||||
pauseNote: "Pausa automática ao encerrar chat ao vivo",
|
||||
source: "live_chat_auto",
|
||||
},
|
||||
createdAt: now,
|
||||
})
|
||||
|
||||
workSessionPaused = true
|
||||
}
|
||||
}
|
||||
|
||||
return { ok: true, workSessionPaused }
|
||||
},
|
||||
})
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue