diff --git a/web/src/app/globals.css b/web/src/app/globals.css index 2f2ba1d..400c7d2 100644 --- a/web/src/app/globals.css +++ b/web/src/app/globals.css @@ -137,4 +137,19 @@ .rich-text h3 { @apply text-base font-semibold my-2; } .rich-text code { @apply rounded bg-muted px-1 py-0.5 text-xs; } .rich-text pre { @apply my-3 overflow-x-auto rounded bg-muted p-3 text-xs; } + + @keyframes recent-ticket-enter { + 0% { + opacity: 0; + transform: translateY(-12px); + } + 100% { + opacity: 1; + transform: translateY(0); + } + } + + .recent-ticket-enter { + animation: recent-ticket-enter 0.45s ease-out; + } } diff --git a/web/src/components/tickets/recent-tickets-panel.tsx b/web/src/components/tickets/recent-tickets-panel.tsx index 0b77068..d65850c 100644 --- a/web/src/components/tickets/recent-tickets-panel.tsx +++ b/web/src/components/tickets/recent-tickets-panel.tsx @@ -1,5 +1,6 @@ "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" @@ -28,9 +29,11 @@ const channelLabel: Record = { MANUAL: "Manual", } -function TicketRow({ ticket }: { ticket: Ticket }) { +function TicketRow({ ticket, entering }: { ticket: Ticket; entering: boolean }) { return ( -
+
@@ -78,6 +81,41 @@ function TicketRow({ ticket }: { ticket: Ticket }) { export function RecentTicketsPanel() { const ticketsRaw = useQuery(api.tickets.list, { tenantId: DEFAULT_TENANT_ID, limit: 6 }) + const [enteringId, setEnteringId] = useState(null) + const previousIdsRef = useRef([]) + + const tickets = useMemo( + () => mapTicketsFromServerList((ticketsRaw ?? []) as unknown[]).slice(0, 6), + [ticketsRaw] + ) + + useEffect(() => { + if (ticketsRaw === undefined) { + 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, ticketsRaw]) + + useEffect(() => { + if (!enteringId) return + const timer = window.setTimeout(() => setEnteringId(null), 600) + return () => window.clearTimeout(timer) + }, [enteringId]) if (ticketsRaw === undefined) { return ( @@ -97,8 +135,6 @@ export function RecentTicketsPanel() { ) } - const tickets = mapTicketsFromServerList((ticketsRaw ?? []) as unknown[]).slice(0, 6) - return ( @@ -113,7 +149,9 @@ export function RecentTicketsPanel() { Nenhum ticket recente encontrado.
) : ( - tickets.map((ticket) => ) + tickets.map((ticket) => ( + + )) )}