"use client" import { useEffect, useMemo, useRef, useState } from "react" import Link from "next/link" import { formatDistanceToNow } from "date-fns" import { ptBR } from "date-fns/locale" import { useQuery } from "convex/react" import { api } from "@/convex/_generated/api" import type { Id } from "@/convex/_generated/dataModel" import { DEFAULT_TENANT_ID } from "@/lib/constants" import { mapTicketsFromServerList } from "@/lib/mappers/ticket" import type { Ticket } from "@/lib/schemas/ticket" import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card" import { Skeleton } from "@/components/ui/skeleton" import { TicketPriorityPill } from "@/components/tickets/priority-pill" import { TicketStatusBadge } from "@/components/tickets/status-badge" import { useAuth } from "@/lib/auth-client" import { cn } from "@/lib/utils" function TicketRow({ ticket, entering }: { ticket: Ticket; entering: boolean }) { const queueLabel = ticket.queue ?? "Sem fila" const requesterName = ticket.requester.name ?? ticket.requester.email ?? "Solicitante" const categoryBadges = [ticket.category?.name, ticket.subcategory?.name].filter((value): value is string => Boolean(value)) const badgeClass = "rounded-lg border border-slate-300 px-3.5 py-1.5 text-sm font-medium text-slate-600 transition-colors" return (
#{ticket.reference} {queueLabel}
{ticket.subject}

{ticket.summary ?? "Sem descrição informada."}

{requesterName} {formatDistanceToNow(ticket.updatedAt, { addSuffix: true, locale: ptBR })}
{categoryBadges.length > 0 ? ( categoryBadges.map((label) => ( {label} )) ) : ( Sem categoria )}
) } export function RecentTicketsPanel() { const { convexUserId } = useAuth() const ticketsArgs = convexUserId ? { tenantId: DEFAULT_TENANT_ID, viewerId: convexUserId as Id<"users">, limit: 12 } : undefined const ticketsResult = useQuery(convexUserId ? api.tickets.list : "skip", ticketsArgs) const [enteringId, setEnteringId] = useState(null) const previousIdsRef = useRef([]) const tickets = useMemo(() => { if (!Array.isArray(ticketsResult)) return [] const all = mapTicketsFromServerList(ticketsResult as unknown[]).filter((t) => t.status !== "RESOLVED") // Unassigned first (no assignee), oldest first among unassigned; then the rest by updatedAt desc const unassigned = all .filter((t) => !t.assignee) .sort((a, b) => a.createdAt.getTime() - b.createdAt.getTime()) const assigned = all .filter((t) => !!t.assignee) .sort((a, b) => b.updatedAt.getTime() - a.updatedAt.getTime()) return [...unassigned, ...assigned].slice(0, 6) }, [ticketsResult]) useEffect(() => { if (!Array.isArray(ticketsResult)) { previousIdsRef.current = [] return } const ids = tickets.map((ticket) => ticket.id) const previous = previousIdsRef.current if (!ids.length) { previousIdsRef.current = ids return } if (!previous.length) { previousIdsRef.current = ids return } const topId = ids[0] if (!previous.includes(topId)) { setEnteringId(topId) } previousIdsRef.current = ids }, [tickets, ticketsResult]) useEffect(() => { if (!enteringId) return const timer = window.setTimeout(() => setEnteringId(null), 600) return () => window.clearTimeout(timer) }, [enteringId]) if (convexUserId && !Array.isArray(ticketsResult)) { return ( Últimos chamados {Array.from({ length: 4 }).map((_, index) => (
))}
) } return ( Últimos chamados Ver todos {tickets.length === 0 ? (
Nenhum ticket recente encontrado.
) : ( tickets.map((ticket) => ( )) )}
) }