import { useMemo } from "react" import { format } from "date-fns" import { ptBR } from "date-fns/locale" import type { TicketWithDetails } from "@/lib/schemas/ticket" import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card" import { getTicketStatusLabel, getTicketStatusSummaryTone } from "@/lib/ticket-status-style" import { Badge } from "@/components/ui/badge" import { cn } from "@/lib/utils" interface TicketDetailsPanelProps { ticket: TicketWithDetails } type SummaryTone = "default" | "info" | "warning" | "success" | "muted" | "danger" | "primary" const priorityLabel: Record = { LOW: "Baixa", MEDIUM: "Média", HIGH: "Alta", URGENT: "Urgente", } const priorityTone: Record = { LOW: "muted", MEDIUM: "info", HIGH: "warning", URGENT: "danger", } function formatDuration(ms?: number | null) { if (!ms || ms <= 0) return "0s" const totalSeconds = Math.floor(ms / 1000) const hours = Math.floor(totalSeconds / 3600) const minutes = Math.floor((totalSeconds % 3600) / 60) const seconds = totalSeconds % 60 if (hours > 0) { return `${hours}h ${minutes.toString().padStart(2, "0")}m` } if (minutes > 0) { return `${minutes}m ${seconds.toString().padStart(2, "0")}s` } return `${seconds}s` } function formatMinutes(value?: number | null) { if (value === null || value === undefined) return "—" return `${value} min` } type SummaryChipConfig = { key: string label: string value: string tone: SummaryTone labelClassName?: string } export function TicketDetailsPanel({ ticket }: TicketDetailsPanelProps) { const isAvulso = Boolean(ticket.company?.isAvulso) const companyLabel = ticket.company?.name ?? (isAvulso ? "Cliente avulso" : "Sem empresa vinculada") const summaryChips = useMemo(() => { const chips: SummaryChipConfig[] = [ { key: "queue", label: "Fila", value: ticket.queue ?? "Sem fila", tone: ticket.queue ? "default" : "muted", }, { key: "company", label: "Empresa", value: companyLabel, tone: isAvulso ? "warning" : "default", }, { key: "status", label: "Status", value: getTicketStatusLabel(ticket.status) ?? ticket.status, tone: getTicketStatusSummaryTone(ticket.status) as SummaryTone, }, { key: "priority", label: "Prioridade", value: priorityLabel[ticket.priority] ?? ticket.priority, tone: priorityTone[ticket.priority] ?? "default", }, { key: "assignee", label: "Responsável", value: ticket.assignee?.name ?? "Não atribuído", tone: ticket.assignee ? "default" : "muted", }, ] if (ticket.formTemplateLabel) { chips.splice(Math.min(chips.length, 5), 0, { key: "formTemplate", label: "Fluxo", value: ticket.formTemplateLabel, tone: "primary", labelClassName: "text-white", }) } return chips }, [companyLabel, isAvulso, ticket.assignee, ticket.formTemplateLabel, ticket.priority, ticket.queue, ticket.status]) const agentTotals = useMemo(() => { const totals = ticket.workSummary?.perAgentTotals ?? [] return totals .map((item) => ({ agentId: String(item.agentId), agentName: item.agentName ?? null, totalWorkedMs: item.totalWorkedMs ?? 0, internalWorkedMs: item.internalWorkedMs ?? 0, externalWorkedMs: item.externalWorkedMs ?? 0, })) .sort((a, b) => b.totalWorkedMs - a.totalWorkedMs) }, [ticket.workSummary?.perAgentTotals]) return (
Detalhes Resumo do ticket, métricas de SLA, tempo dedicado e campos personalizados.
{isAvulso ? ( Cliente avulso ) : null}

Resumo

{summaryChips.map(({ key, label, value, tone, labelClassName }) => ( ))}

SLA & métricas

{ticket.slaPolicy ? ( {ticket.slaPolicy.name} ) : null}

Política de SLA

{ticket.slaPolicy ? (
Resposta inicial {formatMinutes(ticket.slaPolicy.targetMinutesToFirstResponse)}
Resolução {formatMinutes(ticket.slaPolicy.targetMinutesToResolution)}
) : (

Sem política de SLA atribuída.

)}

Métricas

{ticket.metrics ? (
Tempo aguardando {formatMinutes(ticket.metrics.timeWaitingMinutes)}
Tempo aberto {formatMinutes(ticket.metrics.timeOpenedMinutes)}
) : (

Sem dados registrados ainda.

)}

Tempo por agente

{agentTotals.length > 0 ? (
{agentTotals.map((agent) => (
{agent.agentName ?? "Agente removido"} {formatDuration(agent.totalWorkedMs)}
Interno:{" "} {formatDuration(agent.internalWorkedMs)} Externo:{" "} {formatDuration(agent.externalWorkedMs)}
))}
) : (

Nenhum tempo registrado neste ticket ainda.

)}

Histórico

Criado em {format(ticket.createdAt, "dd/MM/yyyy HH:mm", { locale: ptBR })}
Atualizado em {format(ticket.updatedAt, "dd/MM/yyyy HH:mm", { locale: ptBR })}
Resolvido em {ticket.resolvedAt ? format(ticket.resolvedAt, "dd/MM/yyyy HH:mm", { locale: ptBR }) : "—"}
) } function SummaryChip({ label, value, tone = "default", labelClassName, }: { label: string value: string tone?: SummaryTone labelClassName?: string }) { const toneClasses: Record = { default: "border-slate-200 bg-white text-neutral-900", info: "border-sky-200 bg-sky-50 text-sky-900", warning: "border-amber-200 bg-amber-50 text-amber-800", success: "border-emerald-200 bg-emerald-50 text-emerald-800", muted: "border-slate-200 bg-slate-50 text-neutral-600", danger: "border-rose-200 bg-rose-50 text-rose-700", primary: "border-black bg-black text-white", } return (

{label}

{value}

) }