fix: improve closure templates and hover styling
This commit is contained in:
parent
3012ad4348
commit
81657e52d8
5 changed files with 69 additions and 79 deletions
|
|
@ -1,58 +1,66 @@
|
|||
"use client"
|
||||
|
||||
import { useEffect, useMemo, useState } from "react"
|
||||
import { useCallback, 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 { RichTextEditor, sanitizeEditorHtml, stripLeadingEmptyParagraphs } 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_COMPANY_NAME = "Rever Tecnologia"
|
||||
|
||||
const sanitizeTemplate = (html: string) => stripLeadingEmptyParagraphs(sanitizeEditorHtml(html.trim()))
|
||||
|
||||
const DEFAULT_CLOSING_TEMPLATES: ClosingTemplate[] = [
|
||||
{
|
||||
id: "default-standard",
|
||||
title: "Encerramento padrão",
|
||||
body: sanitizeEditorHtml(`
|
||||
body: sanitizeTemplate(`
|
||||
<p>Olá {{cliente}},</p>
|
||||
<p>A equipe da Raven agradece o contato. Este ticket está sendo encerrado.</p>
|
||||
<p>A equipe da ${DEFAULT_COMPANY_NAME} agradece o contato. Este ticket está sendo encerrado.</p>
|
||||
<p>Se surgirem novas questões, você pode reabrir o ticket em até 7 dias ou nos contatar pelo número <strong>${DEFAULT_PHONE_NUMBER}</strong>. Obrigado.</p>
|
||||
<p>👍 👀 🙌<br />Gabriel Henrique · Raven</p>
|
||||
<p>{{agente}} · ${DEFAULT_COMPANY_NAME}</p>
|
||||
`),
|
||||
},
|
||||
{
|
||||
id: "default-no-contact",
|
||||
title: "Tentativa de contato sem sucesso",
|
||||
body: sanitizeEditorHtml(`
|
||||
body: sanitizeTemplate(`
|
||||
<p>Prezado(a) {{cliente}},</p>
|
||||
<p>Realizamos uma tentativa de contato, mas não obtivemos sucesso.</p>
|
||||
<p>Por favor, retorne assim que possível para seguirmos com as verificações necessárias.</p>
|
||||
<p>Este ticket será encerrado após 3 tentativas realizadas sem sucesso.</p>
|
||||
<p>Telefone para contato: <strong>${DEFAULT_PHONE_NUMBER}</strong>.</p>
|
||||
<p>👍 👀 🙌<br />Gabriel Henrique · Raven</p>
|
||||
<p>{{agente}} · ${DEFAULT_COMPANY_NAME}</p>
|
||||
`),
|
||||
},
|
||||
{
|
||||
id: "default-closed-after-attempts",
|
||||
title: "Encerramento após 3 tentativas",
|
||||
body: sanitizeEditorHtml(`
|
||||
body: sanitizeTemplate(`
|
||||
<p>Prezado(a) {{cliente}},</p>
|
||||
<p>Esse ticket está sendo encerrado pois realizamos 3 tentativas sem retorno.</p>
|
||||
<p>Você pode reabrir este ticket em até 7 dias ou entrar em contato pelo telefone <strong>${DEFAULT_PHONE_NUMBER}</strong> quando preferir.</p>
|
||||
<p>👍 👀 🙌<br />Gabriel Henrique · Raven</p>
|
||||
<p>{{agente}} · ${DEFAULT_COMPANY_NAME}</p>
|
||||
`),
|
||||
},
|
||||
]
|
||||
|
||||
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)
|
||||
function applyTemplatePlaceholders(html: string, customerName?: string | null, agentName?: string | null) {
|
||||
const normalizedCustomer = customerName?.trim()
|
||||
const customerFallback = normalizedCustomer && normalizedCustomer.length > 0 ? normalizedCustomer : "cliente"
|
||||
const normalizedAgent = agentName?.trim()
|
||||
const agentFallback = normalizedAgent && normalizedAgent.length > 0 ? normalizedAgent : "Equipe Rever"
|
||||
return html
|
||||
.replace(/{{\s*(cliente|customer|customername|nome|nomecliente)\s*}}/gi, customerFallback)
|
||||
.replace(/{{\s*(agente|agent|atendente|responsavel|usu[aá]rio|usuario)\s*}}/gi, agentFallback)
|
||||
.replace(/{{\s*(empresa|company|companhia)\s*}}/gi, DEFAULT_COMPANY_NAME)
|
||||
}
|
||||
|
||||
export function CloseTicketDialog({
|
||||
|
|
@ -62,6 +70,7 @@ export function CloseTicketDialog({
|
|||
tenantId,
|
||||
actorId,
|
||||
requesterName,
|
||||
agentName,
|
||||
onSuccess,
|
||||
}: {
|
||||
open: boolean
|
||||
|
|
@ -70,6 +79,7 @@ export function CloseTicketDialog({
|
|||
tenantId: string
|
||||
actorId: Id<"users"> | null
|
||||
requesterName?: string | null
|
||||
agentName?: string | null
|
||||
onSuccess: () => void
|
||||
}) {
|
||||
const updateStatus = useMutation(api.tickets.updateStatus)
|
||||
|
|
@ -92,6 +102,11 @@ export function CloseTicketDialog({
|
|||
const [message, setMessage] = useState<string>("")
|
||||
const [isSubmitting, setIsSubmitting] = useState(false)
|
||||
|
||||
const hydrateTemplateBody = useCallback((templateHtml: string) => {
|
||||
const withPlaceholders = applyTemplatePlaceholders(templateHtml, requesterName, agentName)
|
||||
return stripLeadingEmptyParagraphs(sanitizeEditorHtml(withPlaceholders))
|
||||
}, [requesterName, agentName])
|
||||
|
||||
useEffect(() => {
|
||||
if (!open) {
|
||||
setSelectedTemplateId(null)
|
||||
|
|
@ -101,16 +116,15 @@ export function CloseTicketDialog({
|
|||
}
|
||||
if (templates.length > 0 && !selectedTemplateId && !message) {
|
||||
const first = templates[0]
|
||||
const hydrated = sanitizeEditorHtml(applyTemplatePlaceholders(first.body, requesterName))
|
||||
const hydrated = hydrateTemplateBody(first.body)
|
||||
setSelectedTemplateId(first.id)
|
||||
setMessage(hydrated)
|
||||
}
|
||||
}, [open, templates, requesterName, selectedTemplateId, message])
|
||||
}, [open, templates, selectedTemplateId, message, hydrateTemplateBody])
|
||||
|
||||
const handleTemplateSelect = (template: ClosingTemplate) => {
|
||||
setSelectedTemplateId(template.id)
|
||||
const filled = sanitizeEditorHtml(applyTemplatePlaceholders(template.body, requesterName))
|
||||
setMessage(filled)
|
||||
setMessage(hydrateTemplateBody(template.body))
|
||||
}
|
||||
|
||||
const handleSubmit = async () => {
|
||||
|
|
@ -122,8 +136,8 @@ export function CloseTicketDialog({
|
|||
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 withPlaceholders = applyTemplatePlaceholders(message, requesterName, agentName)
|
||||
const sanitized = stripLeadingEmptyParagraphs(sanitizeEditorHtml(withPlaceholders))
|
||||
const hasContent = sanitized.replace(/<[^>]*>/g, "").trim().length > 0
|
||||
if (hasContent) {
|
||||
await addComment({
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue