"use client"
import { useEffect, useMemo, useState } from "react"
import { useMutation, useQuery } from "convex/react"
import { api } from "@/convex/_generated/api"
import type { Id } from "@/convex/_generated/dataModel"
import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from "@/components/ui/dialog"
import { Button } from "@/components/ui/button"
import { Spinner } from "@/components/ui/spinner"
import { RichTextEditor, sanitizeEditorHtml } from "@/components/ui/rich-text-editor"
import { toast } from "sonner"
type ClosingTemplate = { id: string; title: string; body: string }
const DEFAULT_PHONE_NUMBER = "(11) 4173-5368"
const DEFAULT_CLOSING_TEMPLATES: ClosingTemplate[] = [
{
id: "default-standard",
title: "Encerramento padrão",
body: sanitizeEditorHtml(`
Olá {{cliente}},
A equipe da Raven agradece o contato. Este ticket está sendo encerrado.
Se surgirem novas questões, você pode reabrir o ticket em até 7 dias ou nos contatar pelo número ${DEFAULT_PHONE_NUMBER}. Obrigado.
👍 👀 🙌
Gabriel Henrique · Raven
`),
},
{
id: "default-no-contact",
title: "Tentativa de contato sem sucesso",
body: sanitizeEditorHtml(`
Prezado(a) {{cliente}},
Realizamos uma tentativa de contato, mas não obtivemos sucesso.
Por favor, retorne assim que possível para seguirmos com as verificações necessárias.
Este ticket será encerrado após 3 tentativas realizadas sem sucesso.
Telefone para contato: ${DEFAULT_PHONE_NUMBER}.
👍 👀 🙌
Gabriel Henrique · Raven
`),
},
{
id: "default-closed-after-attempts",
title: "Encerramento após 3 tentativas",
body: sanitizeEditorHtml(`
Prezado(a) {{cliente}},
Esse ticket está sendo encerrado pois realizamos 3 tentativas sem retorno.
Você pode reabrir este ticket em até 7 dias ou entrar em contato pelo telefone ${DEFAULT_PHONE_NUMBER} quando preferir.
👍 👀 🙌
Gabriel Henrique · Raven
`),
},
]
function applyTemplatePlaceholders(html: string, customerName?: string | null) {
const normalizedName = customerName?.trim()
const fallback = normalizedName && normalizedName.length > 0 ? normalizedName : "cliente"
return html.replace(/{{\s*(cliente|customer|customername|nome|nomecliente)\s*}}/gi, fallback)
}
export function CloseTicketDialog({
open,
onOpenChange,
ticketId,
tenantId,
actorId,
requesterName,
onSuccess,
}: {
open: boolean
onOpenChange: (open: boolean) => void
ticketId: string
tenantId: string
actorId: Id<"users"> | null
requesterName?: string | null
onSuccess: () => void
}) {
const updateStatus = useMutation(api.tickets.updateStatus)
const addComment = useMutation(api.tickets.addComment)
const closingTemplates = useQuery(
actorId && open ? api.commentTemplates.list : "skip",
actorId && open ? { tenantId, viewerId: actorId, kind: "closing" as const } : "skip"
) as { id: string; title: string; body: string }[] | undefined
const templatesLoading = Boolean(actorId && open && closingTemplates === undefined)
const templates = useMemo(() => {
if (closingTemplates && closingTemplates.length > 0) {
return closingTemplates.map((t) => ({ id: t.id, title: t.title, body: t.body }))
}
return DEFAULT_CLOSING_TEMPLATES
}, [closingTemplates])
const [selectedTemplateId, setSelectedTemplateId] = useState(null)
const [message, setMessage] = useState("")
const [isSubmitting, setIsSubmitting] = useState(false)
useEffect(() => {
if (!open) {
setSelectedTemplateId(null)
setMessage("")
setIsSubmitting(false)
return
}
if (templates.length > 0 && !selectedTemplateId && !message) {
const first = templates[0]
const hydrated = sanitizeEditorHtml(applyTemplatePlaceholders(first.body, requesterName))
setSelectedTemplateId(first.id)
setMessage(hydrated)
}
}, [open, templates, requesterName, selectedTemplateId, message])
const handleTemplateSelect = (template: ClosingTemplate) => {
setSelectedTemplateId(template.id)
const filled = sanitizeEditorHtml(applyTemplatePlaceholders(template.body, requesterName))
setMessage(filled)
}
const handleSubmit = async () => {
if (!actorId) {
toast.error("É necessário estar autenticado para encerrar o ticket.")
return
}
setIsSubmitting(true)
toast.loading("Encerrando ticket...", { id: "close-ticket" })
try {
await updateStatus({ ticketId: ticketId as unknown as Id<"tickets">, status: "RESOLVED", actorId })
const withPlaceholders = applyTemplatePlaceholders(message, requesterName)
const sanitized = sanitizeEditorHtml(withPlaceholders)
const hasContent = sanitized.replace(/<[^>]*>/g, "").trim().length > 0
if (hasContent) {
await addComment({
ticketId: ticketId as unknown as Id<"tickets">,
authorId: actorId,
visibility: "PUBLIC",
body: sanitized,
attachments: [],
})
}
toast.success("Ticket encerrado com sucesso!", { id: "close-ticket" })
onOpenChange(false)
onSuccess()
} catch (error) {
console.error(error)
toast.error("Não foi possível encerrar o ticket.", { id: "close-ticket" })
} finally {
setIsSubmitting(false)
}
}
return (
)
}