sistema-de-chamados/src/components/tickets/tickets-view.tsx
2025-10-13 00:08:18 -03:00

113 lines
4 KiB
TypeScript

"use client"
import { useEffect, useMemo, useState } from "react"
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, TicketQueueSummary } from "@/lib/schemas/ticket"
import { TicketsFilters, TicketFiltersState, defaultTicketFilters } from "@/components/tickets/tickets-filters"
import { TicketsTable } from "@/components/tickets/tickets-table"
import { useAuth } from "@/lib/auth-client"
import { useDefaultQueues } from "@/hooks/use-default-queues"
type TicketsViewProps = {
initialFilters?: Partial<TicketFiltersState>
}
export function TicketsView({ initialFilters }: TicketsViewProps = {}) {
const mergedInitialFilters = useMemo(
() => ({
...defaultTicketFilters,
...(initialFilters ?? {}),
}),
[initialFilters]
)
const [filters, setFilters] = useState<TicketFiltersState>(mergedInitialFilters)
useEffect(() => {
setFilters(mergedInitialFilters)
}, [mergedInitialFilters])
const { session, convexUserId, isStaff } = useAuth()
const tenantId = session?.user.tenantId ?? DEFAULT_TENANT_ID
useDefaultQueues(tenantId)
const queuesEnabled = Boolean(isStaff && convexUserId)
const queues = useQuery(
queuesEnabled ? api.queues.summary : "skip",
queuesEnabled ? { tenantId, viewerId: convexUserId as Id<"users"> } : "skip"
) as TicketQueueSummary[] | undefined
const ticketsRaw = useQuery(
api.tickets.list,
convexUserId
? {
tenantId,
viewerId: convexUserId as Id<"users">,
status: filters.status ?? undefined,
priority: filters.priority ?? undefined,
channel: filters.channel ?? undefined,
queueId: undefined, // simplified: filter by queue name on client
search: filters.search || undefined,
}
: "skip"
)
const tickets = useMemo(() => mapTicketsFromServerList((ticketsRaw ?? []) as unknown[]), [ticketsRaw])
const companies = useMemo(() => {
const set = new Set<string>()
for (const t of tickets) {
const name = ((t as unknown as { company?: { name?: string } })?.company?.name) as string | undefined
if (name) set.add(name)
}
return Array.from(set).sort((a, b) => a.localeCompare(b, "pt-BR"))
}, [tickets])
const filteredTickets = useMemo(() => {
const completedStatuses = new Set<Ticket["status"]>(["RESOLVED"])
let working = tickets
if (!filters.status) {
if (filters.view === "active") {
working = working.filter((t) => !completedStatuses.has(t.status))
} else if (filters.view === "completed") {
working = working.filter((t) => completedStatuses.has(t.status))
}
}
if (filters.queue) {
working = working.filter((t) => t.queue === filters.queue)
}
if (filters.company) {
working = working.filter((t) => (((t as unknown as { company?: { name?: string } })?.company?.name) ?? null) === filters.company)
}
return working
}, [tickets, filters.queue, filters.status, filters.view, filters.company])
return (
<div className="flex flex-col gap-6 px-4 lg:px-6">
<TicketsFilters
onChange={setFilters}
queues={(queues ?? []).map((q) => q.name)}
companies={companies}
initialState={mergedInitialFilters}
/>
{ticketsRaw === undefined ? (
<div className="rounded-2xl border border-slate-200 bg-white p-4 shadow-sm">
<div className="grid gap-3">
{Array.from({ length: 6 }).map((_, i) => (
<div key={i} className="flex items-center justify-between gap-3">
<div className="h-4 w-48 animate-pulse rounded bg-slate-100" />
<div className="h-4 w-24 animate-pulse rounded bg-slate-100" />
</div>
))}
</div>
</div>
) : (
<TicketsTable tickets={filteredTickets} />
)}
</div>
)
}