"use client" import { useMemo, useState } from "react" import { useRouter } from "next/navigation" import { useMutation, useQuery } from "convex/react" import { toast } from "sonner" import type { Id } from "@/convex/_generated/dataModel" import type { TicketPriority, TicketQueueSummary } from "@/lib/schemas/ticket" import { DEFAULT_TENANT_ID } from "@/lib/constants" import { useAuth } from "@/lib/auth-client" // @ts-expect-error Convex runtime API lacks TypeScript declarations import { api } from "@/convex/_generated/api" import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card" import { Button } from "@/components/ui/button" import { Input } from "@/components/ui/input" import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select" import { RichTextEditor } from "@/components/ui/rich-text-editor" import { Spinner } from "@/components/ui/spinner" import { Badge } from "@/components/ui/badge" import { cn } from "@/lib/utils" import { PriorityIcon, priorityBadgeClass, priorityItemClass, priorityStyles, priorityTriggerClass, } from "@/components/tickets/priority-select" import { CategorySelectFields } from "@/components/tickets/category-select" export default function NewTicketPage() { const router = useRouter() const { convexUserId } = useAuth() const queueArgs = convexUserId ? { tenantId: DEFAULT_TENANT_ID, viewerId: convexUserId as Id<"users"> } : "skip" const queuesRaw = useQuery( convexUserId ? api.queues.summary : "skip", queueArgs ) as TicketQueueSummary[] | undefined const queues = useMemo(() => queuesRaw ?? [], [queuesRaw]) const create = useMutation(api.tickets.create) const addComment = useMutation(api.tickets.addComment) const [subject, setSubject] = useState("") const [summary, setSummary] = useState("") const [priority, setPriority] = useState("MEDIUM") const [channel, setChannel] = useState("MANUAL") const [queueName, setQueueName] = useState(null) const [description, setDescription] = useState("") const [loading, setLoading] = useState(false) const [subjectError, setSubjectError] = useState(null) const [categoryId, setCategoryId] = useState(null) const [subcategoryId, setSubcategoryId] = useState(null) const [categoryError, setCategoryError] = useState(null) const [subcategoryError, setSubcategoryError] = useState(null) const queueOptions = useMemo(() => queues.map((q) => q.name), [queues]) async function submit(event: React.FormEvent) { event.preventDefault() if (!convexUserId || loading) return const trimmedSubject = subject.trim() if (trimmedSubject.length < 3) { setSubjectError("Informe um assunto com pelo menos 3 caracteres.") return } setSubjectError(null) if (!categoryId) { setCategoryError("Selecione uma categoria.") return } if (!subcategoryId) { setSubcategoryError("Selecione uma categoria secundária.") return } setLoading(true) toast.loading("Criando ticket...", { id: "create-ticket" }) try { const selQueue = queues.find((q) => q.name === queueName) const queueId = selQueue ? (selQueue.id as Id<"queues">) : undefined const id = await create({ actorId: convexUserId as Id<"users">, tenantId: DEFAULT_TENANT_ID, subject: trimmedSubject, summary: summary.trim() || undefined, priority, channel, queueId, requesterId: convexUserId as Id<"users">, categoryId: categoryId as Id<"ticketCategories">, subcategoryId: subcategoryId as Id<"ticketSubcategories">, }) const plainDescription = description.replace(/<[^>]*>/g, "").trim() if (plainDescription.length > 0) { await addComment({ ticketId: id as Id<"tickets">, authorId: convexUserId as Id<"users">, visibility: "PUBLIC", body: description, attachments: [], }) } toast.success("Ticket criado!", { id: "create-ticket" }) router.replace(`/tickets/${id}`) } catch (error) { console.error(error) toast.error("Não foi possível criar o ticket.", { id: "create-ticket" }) } finally { setLoading(false) } } const selectTriggerClass = "flex h-8 w-full items-center justify-between rounded-full border border-slate-300 bg-white px-3 text-sm font-medium text-neutral-800 shadow-sm focus:ring-0 data-[state=open]:border-[#00d6eb]" const selectItemClass = "flex items-center gap-2 rounded-md px-2 py-2 text-sm text-neutral-800 transition data-[state=checked]:bg-[#00e8ff]/15 data-[state=checked]:text-neutral-900 focus:bg-[#00e8ff]/10" return (
Novo ticket Preencha as informações básicas para abrir um chamado.
{ setSubject(event.target.value) if (subjectError) setSubjectError(null) }} placeholder="Ex.: Erro 500 no portal" aria-invalid={subjectError ? "true" : undefined} /> {subjectError ?

{subjectError}

: null}