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
71
convex/ticketChecklist.ts
Normal file
71
convex/ticketChecklist.ts
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
import type { Id } from "./_generated/dataModel"
|
||||
|
||||
export type TicketChecklistItem = {
|
||||
id: string
|
||||
text: string
|
||||
done: boolean
|
||||
required?: boolean
|
||||
templateId?: Id<"ticketChecklistTemplates">
|
||||
templateItemId?: string
|
||||
createdAt?: number
|
||||
createdBy?: Id<"users">
|
||||
doneAt?: number
|
||||
doneBy?: Id<"users">
|
||||
}
|
||||
|
||||
export type TicketChecklistTemplateLike = {
|
||||
_id: Id<"ticketChecklistTemplates">
|
||||
items: Array<{ id: string; text: string; required?: boolean }>
|
||||
}
|
||||
|
||||
export function normalizeChecklistText(input: string) {
|
||||
return input.replace(/\r\n/g, "\n").trim()
|
||||
}
|
||||
|
||||
export function checklistBlocksResolution(checklist: TicketChecklistItem[] | null | undefined) {
|
||||
return (checklist ?? []).some((item) => (item.required ?? true) && item.done !== true)
|
||||
}
|
||||
|
||||
export function applyChecklistTemplateToItems(
|
||||
existing: TicketChecklistItem[],
|
||||
template: TicketChecklistTemplateLike,
|
||||
options: {
|
||||
now: number
|
||||
actorId?: Id<"users">
|
||||
generateId?: () => string
|
||||
}
|
||||
) {
|
||||
const generateId = options.generateId ?? (() => crypto.randomUUID())
|
||||
const now = options.now
|
||||
|
||||
const next = Array.isArray(existing) ? [...existing] : []
|
||||
const existingKeys = new Set<string>()
|
||||
for (const item of next) {
|
||||
if (!item.templateId || !item.templateItemId) continue
|
||||
existingKeys.add(`${String(item.templateId)}:${item.templateItemId}`)
|
||||
}
|
||||
|
||||
let added = 0
|
||||
for (const tplItem of template.items ?? []) {
|
||||
const templateItemId = String(tplItem.id ?? "").trim()
|
||||
const text = normalizeChecklistText(String(tplItem.text ?? ""))
|
||||
if (!templateItemId || !text) continue
|
||||
const key = `${String(template._id)}:${templateItemId}`
|
||||
if (existingKeys.has(key)) continue
|
||||
existingKeys.add(key)
|
||||
next.push({
|
||||
id: generateId(),
|
||||
text,
|
||||
done: false,
|
||||
required: typeof tplItem.required === "boolean" ? tplItem.required : true,
|
||||
templateId: template._id,
|
||||
templateItemId,
|
||||
createdAt: now,
|
||||
createdBy: options.actorId,
|
||||
})
|
||||
added += 1
|
||||
}
|
||||
|
||||
return { checklist: next, added }
|
||||
}
|
||||
|
||||
Loading…
Add table
Add a link
Reference in a new issue