feat: núcleo de tickets com Convex (CRUD, play, comentários com anexos) + auth placeholder; docs em AGENTS.md; toasts e updates otimistas; mapeadores Zod; refinos PT-BR e layout do painel de detalhes

This commit is contained in:
esdrasrenan 2025-10-04 00:31:44 -03:00
parent 2230590e57
commit 27b103cb46
97 changed files with 15117 additions and 15715 deletions

View file

@ -1,53 +1,53 @@
import { format } from "date-fns"
import { ptBR } from "date-fns/locale"
import { IconAlertTriangle, IconClockHour4, IconTags } from "@tabler/icons-react"
import type { TicketWithDetails } from "@/lib/schemas/ticket"
import { Badge } from "@/components/ui/badge"
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"
import { Separator } from "@/components/ui/separator"
interface TicketDetailsPanelProps {
ticket: TicketWithDetails
}
import { format } from "date-fns"
import { ptBR } from "date-fns/locale"
import { IconAlertTriangle, IconClockHour4, IconTags } from "@tabler/icons-react"
import type { TicketWithDetails } from "@/lib/schemas/ticket"
import { Badge } from "@/components/ui/badge"
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"
import { Separator } from "@/components/ui/separator"
interface TicketDetailsPanelProps {
ticket: TicketWithDetails
}
export function TicketDetailsPanel({ ticket }: TicketDetailsPanelProps) {
return (
<Card className="border-none shadow-none">
<CardHeader className="px-0">
<CardTitle className="text-lg font-semibold">Detalhes</CardTitle>
</CardHeader>
<CardContent className="flex flex-col gap-4 px-0 text-sm text-muted-foreground">
<div className="space-y-1">
<p className="text-xs font-semibold uppercase">Fila</p>
<Badge variant="outline">{ticket.queue ?? "Sem fila"}</Badge>
<CardContent className="flex flex-col gap-5 px-0 text-sm text-muted-foreground">
<div className="space-y-1 break-words">
<p className="text-xs font-semibold uppercase tracking-wide">Fila</p>
<Badge variant="outline" className="max-w-full truncate">{ticket.queue ?? "Sem fila"}</Badge>
</div>
<Separator />
<div className="space-y-2">
<p className="text-xs font-semibold uppercase">SLA</p>
<div className="space-y-2 break-words">
<p className="text-xs font-semibold uppercase tracking-wide">SLA</p>
{ticket.slaPolicy ? (
<div className="flex flex-col gap-2 rounded-lg border border-dashed bg-card px-3 py-2">
<span className="text-foreground">{ticket.slaPolicy.name}</span>
<span className="text-foreground text-sm font-medium leading-tight">{ticket.slaPolicy.name}</span>
<div className="flex flex-col gap-1 text-xs text-muted-foreground">
{ticket.slaPolicy.targetMinutesToFirstResponse ? (
<span>
<span className="leading-normal">
Resposta inicial: {ticket.slaPolicy.targetMinutesToFirstResponse} min
</span>
) : null}
{ticket.slaPolicy.targetMinutesToResolution ? (
<span>
Resolucao: {ticket.slaPolicy.targetMinutesToResolution} min
<span className="leading-normal">
Resolução: {ticket.slaPolicy.targetMinutesToResolution} min
</span>
) : null}
</div>
</div>
) : (
<span>Sem politica atribuida.</span>
<span>Sem política atribuída.</span>
)}
</div>
<Separator />
<div className="space-y-2">
<p className="text-xs font-semibold uppercase">Metricas</p>
<p className="text-xs font-semibold uppercase tracking-wide">tricas</p>
{ticket.metrics ? (
<div className="flex flex-col gap-2 text-xs text-muted-foreground">
<span className="flex items-center gap-2">
@ -62,8 +62,8 @@ export function TicketDetailsPanel({ ticket }: TicketDetailsPanelProps) {
)}
</div>
<Separator />
<div className="space-y-2">
<p className="text-xs font-semibold uppercase">Tags</p>
<div className="space-y-2 break-words">
<p className="text-xs font-semibold uppercase tracking-wide">Tags</p>
<div className="flex flex-wrap gap-2">
{ticket.tags?.length ? (
ticket.tags.map((tag) => (
@ -78,7 +78,7 @@ export function TicketDetailsPanel({ ticket }: TicketDetailsPanelProps) {
</div>
<Separator />
<div className="space-y-1">
<p className="text-xs font-semibold uppercase">Historico</p>
<p className="text-xs font-semibold uppercase tracking-wide">Histórico</p>
<div className="flex flex-col gap-1 text-xs text-muted-foreground">
<span>Criado: {format(ticket.createdAt, "dd/MM/yyyy HH:mm", { locale: ptBR })}</span>
<span>Atualizado: {format(ticket.updatedAt, "dd/MM/yyyy HH:mm", { locale: ptBR })}</span>