dashboard: substituir 'Abrir ticket' por botão 'Novo ticket' com modal (mesmo layout e funcionalidade da tela de tickets)

This commit is contained in:
Esdras Renan 2025-10-07 16:53:24 -03:00
parent 384d4411b6
commit d2c1913221
3 changed files with 110 additions and 13 deletions

View file

@ -738,6 +738,93 @@ export const addComment = mutation({
});
// bump ticket updatedAt
await ctx.db.patch(args.ticketId, { updatedAt: now });
// If public comment, attempt to notify requester by e-mail (best-effort)
if (args.visibility === "PUBLIC") {
try {
const smtp = {
host: process.env.SMTP_ADDRESS!,
port: Number(process.env.SMTP_PORT ?? 465),
username: process.env.SMTP_USERNAME!,
password: process.env.SMTP_PASSWORD!,
from: process.env.MAILER_SENDER_EMAIL || "no-reply@example.com",
}
if (smtp.host && smtp.username && smtp.password) {
const requester = await ctx.db.get(ticketDoc.requesterId)
const to = (requester as any)?.email as string | undefined
if (to) {
// Minimal SMTP sender (AUTH LOGIN over implicit TLS)
const tls = await import("tls")
const b64 = (input: string) => Buffer.from(input, "utf8").toString("base64")
const sendSmtpMail = async (cfg: { host: string; port: number; username: string; password: string; from: string }, toAddr: string, subject: string, html: string) => {
return new Promise<void>((resolve, reject) => {
const socket = tls.connect(cfg.port, cfg.host, { rejectUnauthorized: false }, () => {
let buffer = ""
const send = (line: string) => socket.write(line + "\r\n")
const wait = (expected: string | RegExp) =>
new Promise<void>((res) => {
const onData = (data: Buffer) => {
buffer += data.toString()
const lines = buffer.split(/\r?\n/)
const last = lines.filter(Boolean).slice(-1)[0] ?? ""
if (typeof expected === "string" ? last.startsWith(expected) : expected.test(last)) {
socket.removeListener("data", onData)
res()
}
}
socket.on("data", onData)
socket.on("error", reject)
})
;(async () => {
await wait(/^220 /)
send(`EHLO ${cfg.host}`)
await wait(/^250-/)
await wait(/^250 /)
send("AUTH LOGIN")
await wait(/^334 /)
send(b64(cfg.username))
await wait(/^334 /)
send(b64(cfg.password))
await wait(/^235 /)
const fromAddr = (cfg.from.match(/<\s*([^>\s]+)\s*>/)?.[1] ?? cfg.from)
send(`MAIL FROM:<${fromAddr}>`)
await wait(/^250 /)
send(`RCPT TO:<${toAddr}>`)
await wait(/^250 /)
send("DATA")
await wait(/^354 /)
const headers = [
`From: ${cfg.from}`,
`To: ${toAddr}`,
`Subject: ${subject}`,
"MIME-Version: 1.0",
"Content-Type: text/html; charset=UTF-8",
].join("\r\n")
send(headers + "\r\n\r\n" + html + "\r\n.")
await wait(/^250 /)
send("QUIT")
socket.end()
resolve()
})().catch(reject)
})
socket.on("error", reject)
})
}
const ticketRef = (ticketDoc as any).reference ? `#${(ticketDoc as any).reference}` : "Chamado"
const subject = `${ticketRef} atualizado: novo comentário público`
const body = `
<p>Olá,</p>
<p>Seu chamado <strong>${ticketRef}</strong> recebeu um novo comentário:</p>
<blockquote style="border-left: 3px solid #e5e7eb; margin: 8px 0; padding: 8px 12px; color: #374151;">${args.body}</blockquote>
<p>Atenciosamente,<br/>Equipe de suporte</p>
`
await sendSmtpMail(smtp, to, subject, body)
}
}
} catch (error) {
console.error("Failed to send public comment notification", error)
}
}
return id;
},
});