feat: melhorias no vínculo de tickets e exportação

This commit is contained in:
Esdras Renan 2025-11-06 13:07:01 -03:00
parent 1b32638eb5
commit 9495b54a28
7 changed files with 226 additions and 16 deletions

View file

@ -1,9 +1,10 @@
"use client"
import Link from "next/link"
import { useCallback, useEffect, useMemo, useRef, useState } from "react"
import { format, formatDistanceToNow } from "date-fns"
import { ptBR } from "date-fns/locale"
import { IconClock, IconDownload, IconPlayerPause, IconPlayerPlay, IconPencil } from "@tabler/icons-react"
import { IconClock, IconDownload, IconLink, IconPlayerPause, IconPlayerPlay, IconPencil } from "@tabler/icons-react"
import { useMutation, useQuery } from "convex/react"
import { toast } from "sonner"
import { api } from "@/convex/_generated/api"
@ -283,6 +284,39 @@ export function TicketSummaryHeader({ ticket }: TicketHeaderProps) {
}, [selectedCategoryId, selectedSubcategoryId, currentCategoryId, currentSubcategoryId])
const currentQueueName = ticket.queue ?? ""
const isAvulso = Boolean(((ticket.company ?? null) as { isAvulso?: boolean } | null)?.isAvulso ?? false)
const resolvedWithSummary = useMemo(() => {
if (!ticket.resolvedWithTicketId) return null
const linkedId = String(ticket.resolvedWithTicketId)
let reference: string | null = null
let subject: string | null = null
for (const event of ticket.timeline ?? []) {
if (!event || event.type !== "TICKET_LINKED") continue
const payload = (event.payload ?? {}) as {
linkedTicketId?: unknown
linkedReference?: unknown
linkedSubject?: unknown
}
if (String(payload.linkedTicketId ?? "") !== linkedId) continue
if (reference === null) {
const rawReference = payload.linkedReference
if (typeof rawReference === "number") {
reference = rawReference.toString()
} else if (typeof rawReference === "string" && rawReference.trim().length > 0) {
reference = rawReference.trim()
}
}
if (subject === null) {
const rawSubject = payload.linkedSubject
if (typeof rawSubject === "string" && rawSubject.trim().length > 0) {
subject = rawSubject.trim()
}
}
if (reference !== null && subject !== null) {
break
}
}
return { id: linkedId, reference, subject }
}, [ticket.resolvedWithTicketId, ticket.timeline])
const [queueSelection, setQueueSelection] = useState(currentQueueName)
const queueDirty = useMemo(() => queueSelection !== currentQueueName, [queueSelection, currentQueueName])
const currentRequesterRecord = useMemo(
@ -1152,6 +1186,23 @@ export function TicketSummaryHeader({ ticket }: TicketHeaderProps) {
) : null}
{canReopenTicket && reopenDeadlineLabel ? (
<p className="text-xs text-neutral-500">Prazo para reabrir: {reopenDeadlineLabel}</p>
) : null}
{resolvedWithSummary ? (
<Link
href={`/tickets/${resolvedWithSummary.id}`}
className="flex items-center gap-3 rounded-2xl border border-primary/20 bg-primary/5 px-4 py-3 text-sm font-semibold text-primary-700 transition hover:border-primary hover:bg-primary/10 hover:text-primary-800"
>
<span className="flex size-9 items-center justify-center rounded-full border border-primary/30 bg-white text-primary">
<IconLink className="size-5" strokeWidth={1.7} />
</span>
<span className="flex flex-col">
<span className="text-xs font-semibold uppercase tracking-wide text-primary/70">Chamado vinculado</span>
<span className="text-sm font-semibold text-neutral-900">
{resolvedWithSummary.reference ? `#${resolvedWithSummary.reference}` : "Ver ticket"}
{resolvedWithSummary.subject ? ` · ${resolvedWithSummary.subject}` : ""}
</span>
</span>
</Link>
) : null}
{isPlaying ? (
<Tooltip>