feat: checklists em tickets + automações
- Adiciona checklist no ticket (itens obrigatórios/opcionais) e bloqueia encerramento com pendências\n- Cria templates de checklist (globais/por empresa) + tela em /settings/checklists\n- Nova ação de automação: aplicar template de checklist\n- Corrige crash do Select (value vazio), warnings de Dialog e dimensionamento de charts\n- Ajusta SMTP (STARTTLS) e melhora teste de integração
This commit is contained in:
parent
4306b0504d
commit
88a9ef454e
27 changed files with 2685 additions and 226 deletions
|
|
@ -16,6 +16,7 @@ import { getTemplateByKey, normalizeFormTemplateKey } from "./ticketFormTemplate
|
|||
import { TICKET_FORM_CONFIG } from "./ticketForms.config"
|
||||
import { renderAutomationEmailHtml, type AutomationEmailProps } from "./reactEmail"
|
||||
import { buildBaseUrl } from "./url"
|
||||
import { applyChecklistTemplateToItems, type TicketChecklistItem } from "./ticketChecklist"
|
||||
|
||||
type AutomationEmailTarget = "AUTO" | "PORTAL" | "STAFF"
|
||||
|
||||
|
|
@ -32,6 +33,7 @@ type AutomationAction =
|
|||
| { type: "SET_FORM_TEMPLATE"; formTemplate: string | null }
|
||||
| { type: "SET_CHAT_ENABLED"; enabled: boolean }
|
||||
| { type: "ADD_INTERNAL_COMMENT"; body: string }
|
||||
| { type: "APPLY_CHECKLIST_TEMPLATE"; templateId: Id<"ticketChecklistTemplates"> }
|
||||
| {
|
||||
type: "SEND_EMAIL"
|
||||
recipients: AutomationEmailRecipient[]
|
||||
|
|
@ -141,6 +143,12 @@ function parseAction(value: unknown): AutomationAction | null {
|
|||
return { type: "ADD_INTERNAL_COMMENT", body }
|
||||
}
|
||||
|
||||
if (type === "APPLY_CHECKLIST_TEMPLATE") {
|
||||
const templateId = typeof record.templateId === "string" ? record.templateId : ""
|
||||
if (!templateId) return null
|
||||
return { type: "APPLY_CHECKLIST_TEMPLATE", templateId: templateId as Id<"ticketChecklistTemplates"> }
|
||||
}
|
||||
|
||||
if (type === "SEND_EMAIL") {
|
||||
const subject = typeof record.subject === "string" ? record.subject.trim() : ""
|
||||
const message = typeof record.message === "string" ? record.message : ""
|
||||
|
|
@ -672,6 +680,10 @@ async function applyActions(
|
|||
ctaLabel: string
|
||||
}> = []
|
||||
|
||||
let checklist = (Array.isArray((ticket as unknown as { checklist?: unknown }).checklist)
|
||||
? ((ticket as unknown as { checklist?: unknown }).checklist as TicketChecklistItem[])
|
||||
: []) as TicketChecklistItem[]
|
||||
|
||||
for (const action of actions) {
|
||||
if (action.type === "SET_PRIORITY") {
|
||||
const next = action.priority.trim().toUpperCase()
|
||||
|
|
@ -814,6 +826,32 @@ async function applyActions(
|
|||
continue
|
||||
}
|
||||
|
||||
if (action.type === "APPLY_CHECKLIST_TEMPLATE") {
|
||||
const template = (await ctx.db.get(action.templateId)) as Doc<"ticketChecklistTemplates"> | null
|
||||
if (!template || template.tenantId !== ticket.tenantId || template.isArchived === true) {
|
||||
throw new ConvexError("Template de checklist inválido na automação")
|
||||
}
|
||||
if (template.companyId && (!ticket.companyId || String(template.companyId) !== String(ticket.companyId))) {
|
||||
throw new ConvexError("Template de checklist não pertence à empresa do ticket")
|
||||
}
|
||||
|
||||
const result = applyChecklistTemplateToItems(checklist, template, {
|
||||
now,
|
||||
actorId: automation.createdBy ?? undefined,
|
||||
})
|
||||
|
||||
if (result.added > 0) {
|
||||
checklist = result.checklist
|
||||
patch.checklist = checklist.length > 0 ? checklist : undefined
|
||||
}
|
||||
|
||||
applied.push({
|
||||
type: action.type,
|
||||
details: { templateId: String(template._id), templateName: template.name, added: result.added },
|
||||
})
|
||||
continue
|
||||
}
|
||||
|
||||
if (action.type === "SEND_EMAIL") {
|
||||
const subject = action.subject.trim()
|
||||
const message = action.message.replace(/\r\n/g, "\n").trim()
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue