feat(export,tickets,forms,emails):\n- Corrige scroll de Dialogs e melhora UI de seleção de colunas (ícones e separador)\n- Ajusta rota/params da exportação em massa e adiciona modal de exportação individual\n- Renomeia 'Chamado padrão' para 'Chamado' e garante visibilidade total para admin/agente\n- Adiciona toggles por empresa/usuário para habilitar Admissão/Desligamento\n- Exibe badge do tipo de solicitação na listagem e no cabeçalho do ticket\n- Prepara notificações por e-mail (comentário público e encerramento) via SMTP\n

This commit is contained in:
codex-bot 2025-11-04 13:41:32 -03:00
parent a8333c010f
commit 06deb99bcd
12 changed files with 543 additions and 17 deletions

View file

@ -1,5 +1,6 @@
// CI touch: enable server-side assignee filtering and trigger redeploy
import { mutation, query } from "./_generated/server";
import { api } from "./_generated/api";
import type { MutationCtx, QueryCtx } from "./_generated/server";
import { ConvexError, v } from "convex/values";
import { Id, type Doc, type DataModel } from "./_generated/dataModel";
@ -1235,6 +1236,7 @@ export const list = query({
priority: t.priority,
channel: t.channel,
queue: queueName,
formTemplate: t.formTemplate ?? null,
company: company
? { id: company._id, name: company.name, isAvulso: company.isAvulso ?? false }
: t.companyId || t.companySnapshot
@ -1887,6 +1889,20 @@ export const addComment = mutation({
});
// bump ticket updatedAt
await ctx.db.patch(args.ticketId, { updatedAt: now });
// Notificação por e-mail: comentário público para o solicitante
try {
const snapshotEmail = (ticketDoc.requesterSnapshot as { email?: string } | undefined)?.email
if (requestedVisibility === "PUBLIC" && snapshotEmail && String(ticketDoc.requesterId) !== String(args.authorId)) {
await ctx.scheduler.runAfter(0, api.ticketNotifications.sendPublicCommentEmail, {
to: snapshotEmail,
ticketId: String(ticketDoc._id),
reference: ticketDoc.reference ?? 0,
subject: ticketDoc.subject ?? "",
})
}
} catch (e) {
console.warn("[tickets] Falha ao agendar e-mail de comentário", e)
}
return id;
},
});
@ -2122,6 +2138,22 @@ export async function resolveTicketHandler(
createdAt: now,
})
// Notificação por e-mail: encerramento do chamado
try {
const requesterDoc = await ctx.db.get(ticketDoc.requesterId)
const email = (requesterDoc as Doc<"users"> | null)?.email || (ticketDoc.requesterSnapshot as { email?: string } | undefined)?.email || null
if (email) {
await ctx.scheduler.runAfter(0, api.ticketNotifications.sendResolvedEmail, {
to: email,
ticketId: String(ticketId),
reference: ticketDoc.reference ?? 0,
subject: ticketDoc.subject ?? "",
})
}
} catch (e) {
console.warn("[tickets] Falha ao agendar e-mail de encerramento", e)
}
for (const rel of linkedTickets) {
const existing = new Set<string>((rel.relatedTicketIds ?? []).map((value) => String(value)))
existing.add(String(ticketId))
@ -2433,10 +2465,14 @@ export const listTicketForms = query({
}>
for (const template of TICKET_FORM_CONFIG) {
const enabled = resolveFormEnabled(template.key, template.defaultEnabled, settings as Doc<"ticketFormSettings">[], {
let enabled = resolveFormEnabled(template.key, template.defaultEnabled, settings as Doc<"ticketFormSettings">[], {
companyId: viewerCompanyId,
userId: viewer.user._id,
})
const viewerRole = (viewer.role ?? "").toUpperCase()
if (viewerRole === "ADMIN" || viewerRole === "AGENT") {
enabled = true
}
if (!enabled) {
continue
}