diff --git a/apps/desktop/src/chat/ChatHubWidget.tsx b/apps/desktop/src/chat/ChatHubWidget.tsx
index 8d3da45..cabe10c 100644
--- a/apps/desktop/src/chat/ChatHubWidget.tsx
+++ b/apps/desktop/src/chat/ChatHubWidget.tsx
@@ -118,11 +118,13 @@ export function ChatHubWidget() {
}
const handleExpand = async () => {
- setIsMinimized(false)
try {
await invoke("set_hub_minimized", { minimized: false })
+ // Aguarda a janela redimensionar antes de atualizar o estado
+ setTimeout(() => setIsMinimized(false), 100)
} catch (err) {
console.error("Erro ao expandir hub:", err)
+ setIsMinimized(false) // Fallback
}
}
@@ -201,7 +203,7 @@ export function ChatHubWidget() {
// Expandido - mostrar lista
return (
-
+
{
+ const now = Date.now()
+
+ // Buscar sessoes ativas que podem ter campos faltando
+ const activeSessions = await ctx.db
+ .query("liveChatSessions")
+ .withIndex("by_status_lastActivity", (q) => q.eq("status", "ACTIVE"))
+ .take(100)
+
+ let fixed = 0
+ let ended = 0
+
+ for (const session of activeSessions) {
+ // Se sessao nao tem lastAgentMessageAt, adiciona o valor de startedAt
+ if (session.lastAgentMessageAt === undefined) {
+ // Sessao muito antiga (mais de 24h) - encerrar
+ if (now - session.startedAt > 24 * 60 * 60 * 1000) {
+ await ctx.db.patch(session._id, {
+ status: "ENDED",
+ endedAt: now,
+ lastAgentMessageAt: session.lastActivityAt ?? session.startedAt,
+ })
+ ended++
+ } else {
+ // Sessao recente - apenas corrigir o campo
+ await ctx.db.patch(session._id, {
+ lastAgentMessageAt: session.lastActivityAt ?? session.startedAt,
+ })
+ fixed++
+ }
+ }
+ }
+
+ console.log(`fixLegacySessions: fixed=${fixed}, ended=${ended}`)
+ return { fixed, ended, total: activeSessions.length }
+ },
+})
+
// ============================================
// UPLOAD DE ARQUIVOS (Maquina/Cliente)
// ============================================
diff --git a/src/components/tickets/ticket-summary-header.tsx b/src/components/tickets/ticket-summary-header.tsx
index 8242bf3..95b8418 100644
--- a/src/components/tickets/ticket-summary-header.tsx
+++ b/src/components/tickets/ticket-summary-header.tsx
@@ -343,13 +343,38 @@ export function TicketSummaryHeader({ ticket }: TicketHeaderProps) {
const machineOnline = liveChatSession?.machineOnline ?? false
const hasActiveSession = Boolean(liveChatSession?.sessionId)
const ticketHasAssignee = Boolean(ticket.assignee)
- const canStartChat = hasMachine && !hasActiveSession && isStaff && ticketHasAssignee
+ // Permitir iniciar chat mesmo sem responsavel - vai atribuir automaticamente
+ const canStartChat = hasMachine && !hasActiveSession && isStaff
const handleStartLiveChat = async () => {
if (!convexUserId || !ticket.id || isStartingChat) return
setIsStartingChat(true)
toast.dismiss("live-chat")
+
try {
+ // Se nao ha responsavel, atribuir o usuario logado automaticamente
+ if (!ticketHasAssignee) {
+ toast.loading("Atribuindo responsavel...", { id: "live-chat" })
+ await changeAssignee({
+ ticketId: ticket.id as Id<"tickets">,
+ assigneeId: convexUserId as Id<"users">,
+ actorId: convexUserId as Id<"users">,
+ reason: "Atribuido automaticamente ao iniciar chat ao vivo",
+ })
+ // Atualizar estado local
+ const agent = agents.find((a) => String(a._id) === convexUserId)
+ if (agent) {
+ setAssigneeState({
+ id: String(agent._id),
+ name: agent.name,
+ email: agent.email,
+ avatarUrl: agent.avatarUrl ?? undefined,
+ teams: Array.isArray(agent.teams) ? agent.teams.filter((t): t is string => typeof t === "string") : [],
+ })
+ }
+ }
+
+ toast.loading("Iniciando chat ao vivo...", { id: "live-chat" })
const result = await startLiveChat({
ticketId: ticket.id as Id<"tickets">,
actorId: convexUserId as Id<"users">,
@@ -357,18 +382,18 @@ export function TicketSummaryHeader({ ticket }: TicketHeaderProps) {
if (result.isNew) {
toast.success("Chat ao vivo iniciado", { id: "live-chat" })
} else {
- toast.info("Já existe uma sessão de chat ativa", { id: "live-chat" })
+ toast.info("Ja existe uma sessao de chat ativa", { id: "live-chat" })
}
} catch (error: unknown) {
console.error("[LiveChat] Erro ao iniciar chat:", error)
// Extrair mensagem amigável do erro do Convex
- let message = "Não foi possível iniciar o chat"
+ let message = "Nao foi possivel iniciar o chat"
if (error instanceof Error) {
const errorMsg = error.message.toLowerCase()
if (errorMsg.includes("offline")) {
- message = "Máquina offline. Aguarde a máquina ficar online para iniciar o chat."
- } else if (errorMsg.includes("não encontrad") || errorMsg.includes("not found")) {
- message = "Máquina não encontrada"
+ message = "Maquina offline. Aguarde a maquina ficar online para iniciar o chat."
+ } else if (errorMsg.includes("nao encontrad") || errorMsg.includes("not found")) {
+ message = "Maquina nao encontrada"
}
}
toast.error(message, { id: "live-chat" })
@@ -597,7 +622,8 @@ export function TicketSummaryHeader({ ticket }: TicketHeaderProps) {
const hasAssignee = Boolean(currentAssigneeId)
const isCurrentResponsible = hasAssignee && convexUserId ? currentAssigneeId === convexUserId : false
const isResolved = status === "RESOLVED"
- const canControlWork = !isResolved && isStaff && hasAssignee
+ // Permitir Play mesmo sem responsavel - vai atribuir automaticamente
+ const canControlWork = !isResolved && isStaff
const canPauseWork = !isResolved && (isAdmin || isCurrentResponsible)
const pauseDisabled = !canPauseWork
const startDisabled = !canControlWork
@@ -605,14 +631,11 @@ export function TicketSummaryHeader({ ticket }: TicketHeaderProps) {
if (isResolved) {
return "Este chamado está encerrado. Reabra o ticket para iniciar um novo atendimento."
}
- if (!hasAssignee) {
- return "Defina um responsável antes de iniciar o atendimento."
- }
if (!isStaff) {
return "Apenas a equipe interna pode iniciar este atendimento."
}
return "Não é possível iniciar o atendimento neste momento."
- }, [isResolved, hasAssignee, isStaff])
+ }, [isResolved, isStaff])
useEffect(() => {
if (!customersInitialized) {
@@ -1097,10 +1120,34 @@ export function TicketSummaryHeader({ ticket }: TicketHeaderProps) {
const handleStartWork = async (workType: "INTERNAL" | "EXTERNAL") => {
if (!convexUserId) return
+
+ // Se nao ha responsavel, atribuir o usuario logado automaticamente
if (!assigneeState?.id) {
- toast.error("Defina um responsável antes de iniciar o atendimento.")
- return
+ toast.loading("Atribuindo responsavel e iniciando...", { id: "work" })
+ try {
+ await changeAssignee({
+ ticketId: ticket.id as Id<"tickets">,
+ assigneeId: convexUserId as Id<"users">,
+ actorId: convexUserId as Id<"users">,
+ reason: "Atribuido automaticamente ao iniciar atendimento",
+ })
+ // Atualizar estado local
+ const agent = agents.find((a) => String(a._id) === convexUserId)
+ if (agent) {
+ setAssigneeState({
+ id: String(agent._id),
+ name: agent.name,
+ email: agent.email,
+ avatarUrl: agent.avatarUrl ?? undefined,
+ teams: Array.isArray(agent.teams) ? agent.teams.filter((t): t is string => typeof t === "string") : [],
+ })
+ }
+ } catch (err) {
+ toast.error("Nao foi possivel atribuir responsavel.", { id: "work" })
+ return
+ }
}
+
toast.dismiss("work")
toast.loading("Iniciando atendimento...", { id: "work" })
try {