chore: reorganize project structure and ensure default queues

This commit is contained in:
Esdras Renan 2025-10-06 22:59:35 -03:00
parent 854887f499
commit 1cccb852a5
201 changed files with 417 additions and 838 deletions

View file

@ -0,0 +1,89 @@
"use client"
import { useMemo } from "react"
import { useQuery } from "convex/react"
// @ts-expect-error Convex runtime API lacks TypeScript definitions
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 { useAuth } from "@/lib/auth-client"
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"
import { Empty, EmptyDescription, EmptyHeader, EmptyMedia, EmptyTitle } from "@/components/ui/empty"
import { Skeleton } from "@/components/ui/skeleton"
import { PortalTicketCard } from "@/components/portal/portal-ticket-card"
export function PortalTicketList() {
const { convexUserId, session } = useAuth()
const ticketsRaw = useQuery(
api.tickets.list,
convexUserId
? {
tenantId: session?.user.tenantId ?? DEFAULT_TENANT_ID,
viewerId: convexUserId as Id<"users">,
limit: 100,
}
: "skip"
)
const tickets = useMemo(() => {
if (!ticketsRaw) return []
return mapTicketsFromServerList((ticketsRaw as unknown[]) ?? [])
}, [ticketsRaw])
if (ticketsRaw === undefined) {
return (
<Card className="rounded-2xl border border-slate-200 bg-white shadow-sm">
<CardHeader className="px-5 py-5">
<CardTitle className="text-lg font-semibold text-neutral-900">Carregando chamados...</CardTitle>
</CardHeader>
<CardContent className="grid gap-4 px-5 pb-6">
{Array.from({ length: 4 }).map((_, index) => (
<Skeleton key={index} className="h-[132px] w-full rounded-xl" />
))}
</CardContent>
</Card>
)
}
if (!tickets.length) {
return (
<Card className="rounded-2xl border border-slate-200 bg-white shadow-sm">
<CardHeader className="px-5 py-5">
<CardTitle className="text-lg font-semibold text-neutral-900">Meus chamados</CardTitle>
</CardHeader>
<CardContent className="px-5 pb-6">
<Empty>
<EmptyHeader>
<EmptyMedia variant="icon">
<span className="text-2xl">📭</span>
</EmptyMedia>
<EmptyTitle className="text-neutral-900">Nenhum chamado aberto</EmptyTitle>
<EmptyDescription className="text-neutral-600">
Quando você registrar um chamado, ele aparecerá aqui. Clique em Abrir chamado para iniciar um novo atendimento.
</EmptyDescription>
</EmptyHeader>
</Empty>
</CardContent>
</Card>
)
}
return (
<div className="space-y-4">
<div className="flex items-center justify-between">
<div>
<h2 className="text-lg font-semibold text-neutral-900">Meus chamados</h2>
<p className="text-sm text-neutral-600">Acompanhe seus tickets e veja as últimas atualizações.</p>
</div>
</div>
<div className="grid gap-4">
{(tickets as Ticket[]).map((ticket) => (
<PortalTicketCard key={ticket.id} ticket={ticket} />
))}
</div>
</div>
)
}