refactor: simplify ticket form and SLA configuration handling
- Removed SLA configuration selection from the TicketForm component to streamline the ticket creation process. - Updated the abrir-chamado route to eliminate unnecessary SLA loading logic and directly pass loading state to the TicketForm. - Enhanced the central-chamados route to support SLA configurations by priority, allowing for better management of SLA settings. - Introduced new mutations for SLA configuration management, including creation, updating, and deletion of SLA settings. - Improved email templates for ticket notifications, ensuring better communication with users regarding ticket status and updates.
This commit is contained in:
@@ -100,14 +100,13 @@ function montarTimeline(base: number, prazos: ReturnType<typeof calcularPrazos>)
|
||||
return timeline;
|
||||
}
|
||||
|
||||
async function selecionarSlaConfig(ctx: Parameters<typeof getCurrentUserFunction>[0], slaConfigId?: Id<"slaConfigs">) {
|
||||
if (slaConfigId) {
|
||||
return await ctx.db.get(slaConfigId);
|
||||
}
|
||||
|
||||
async function selecionarSlaConfig(
|
||||
ctx: Parameters<typeof getCurrentUserFunction>[0],
|
||||
prioridade: "baixa" | "media" | "alta" | "critica"
|
||||
): Promise<Doc<"slaConfigs"> | null> {
|
||||
return await ctx.db
|
||||
.query("slaConfigs")
|
||||
.withIndex("by_ativo", (q) => q.eq("ativo", true))
|
||||
.withIndex("by_prioridade", (q) => q.eq("prioridade", prioridade).eq("ativo", true))
|
||||
.first();
|
||||
}
|
||||
|
||||
@@ -122,6 +121,7 @@ async function registrarNotificacoes(
|
||||
) {
|
||||
const { ticket, titulo, mensagem, usuarioEvento } = params;
|
||||
|
||||
// Notificar solicitante
|
||||
if (ticket.solicitanteEmail) {
|
||||
await ctx.runMutation(api.email.enfileirarEmail, {
|
||||
destinatario: ticket.solicitanteEmail,
|
||||
@@ -142,6 +142,31 @@ async function registrarNotificacoes(
|
||||
lida: false,
|
||||
criadaEm: Date.now(),
|
||||
});
|
||||
|
||||
// Notificar responsável (se houver)
|
||||
if (ticket.responsavelId && ticket.responsavelId !== ticket.solicitanteId) {
|
||||
const responsavel = await ctx.db.get(ticket.responsavelId);
|
||||
if (responsavel?.email) {
|
||||
await ctx.runMutation(api.email.enfileirarEmail, {
|
||||
destinatario: responsavel.email,
|
||||
destinatarioId: ticket.responsavelId,
|
||||
assunto: `${titulo} - Chamado ${ticket.numero}`,
|
||||
corpo: `${mensagem}\n\n---\nCentral de Chamados SGSE`,
|
||||
enviadoPor: usuarioEvento,
|
||||
});
|
||||
}
|
||||
|
||||
await ctx.db.insert("notificacoes", {
|
||||
usuarioId: ticket.responsavelId,
|
||||
tipo: "nova_mensagem",
|
||||
...(ticket.conversaId ? { conversaId: ticket.conversaId } : {}),
|
||||
remetenteId: usuarioEvento,
|
||||
titulo,
|
||||
descricao: mensagem.length > 120 ? `${mensagem.slice(0, 117)}...` : mensagem,
|
||||
lida: false,
|
||||
criadaEm: Date.now(),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async function registrarInteracao(
|
||||
@@ -185,7 +210,6 @@ export const abrirChamado = mutation({
|
||||
categoria: v.optional(v.string()),
|
||||
prioridade: prioridadeValidator,
|
||||
anexos: v.optional(v.array(arquivoValidator)),
|
||||
slaConfigId: v.optional(v.id("slaConfigs")),
|
||||
canalOrigem: v.optional(v.string()),
|
||||
},
|
||||
returns: v.object({
|
||||
@@ -195,7 +219,7 @@ export const abrirChamado = mutation({
|
||||
handler: async (ctx, args) => {
|
||||
const usuario = await assertAuth(ctx);
|
||||
const agora = Date.now();
|
||||
const sla = await selecionarSlaConfig(ctx, args.slaConfigId);
|
||||
const sla = await selecionarSlaConfig(ctx, args.prioridade);
|
||||
const prazos = calcularPrazos(agora, sla);
|
||||
const timeline = montarTimeline(agora, prazos);
|
||||
|
||||
@@ -486,7 +510,7 @@ export const salvarSlaConfig = mutation({
|
||||
slaId: v.optional(v.id("slaConfigs")),
|
||||
nome: v.string(),
|
||||
descricao: v.optional(v.string()),
|
||||
setores: v.optional(v.array(v.string())),
|
||||
prioridade: prioridadeValidator,
|
||||
tempoRespostaHoras: v.number(),
|
||||
tempoConclusaoHoras: v.number(),
|
||||
tempoEncerramentoHoras: v.optional(v.number()),
|
||||
@@ -501,7 +525,7 @@ export const salvarSlaConfig = mutation({
|
||||
await ctx.db.patch(args.slaId, {
|
||||
nome: args.nome,
|
||||
descricao: args.descricao,
|
||||
setores: args.setores,
|
||||
prioridade: args.prioridade,
|
||||
tempoRespostaHoras: args.tempoRespostaHoras,
|
||||
tempoConclusaoHoras: args.tempoConclusaoHoras,
|
||||
tempoEncerramentoHoras: args.tempoEncerramentoHoras,
|
||||
@@ -516,7 +540,7 @@ export const salvarSlaConfig = mutation({
|
||||
return await ctx.db.insert("slaConfigs", {
|
||||
nome: args.nome,
|
||||
descricao: args.descricao,
|
||||
setores: args.setores,
|
||||
prioridade: args.prioridade,
|
||||
tempoRespostaHoras: args.tempoRespostaHoras,
|
||||
tempoConclusaoHoras: args.tempoConclusaoHoras,
|
||||
tempoEncerramentoHoras: args.tempoEncerramentoHoras,
|
||||
@@ -530,6 +554,102 @@ export const salvarSlaConfig = mutation({
|
||||
},
|
||||
});
|
||||
|
||||
export const excluirSlaConfig = mutation({
|
||||
args: {
|
||||
slaId: v.id("slaConfigs"),
|
||||
},
|
||||
handler: async (ctx, args) => {
|
||||
await assertAuth(ctx);
|
||||
const sla = await ctx.db.get(args.slaId);
|
||||
if (!sla) {
|
||||
throw new Error("Configuração de SLA não encontrada");
|
||||
}
|
||||
await ctx.db.delete(args.slaId);
|
||||
return { sucesso: true };
|
||||
},
|
||||
});
|
||||
|
||||
export const prorrogarChamado = mutation({
|
||||
args: {
|
||||
ticketId: v.id("tickets"),
|
||||
horasAdicionais: v.number(),
|
||||
prazo: v.union(v.literal("resposta"), v.literal("conclusao")),
|
||||
motivo: v.string(),
|
||||
},
|
||||
handler: async (ctx, args) => {
|
||||
const usuario = await assertAuth(ctx);
|
||||
const ticket = await ctx.db.get(args.ticketId);
|
||||
if (!ticket) {
|
||||
throw new Error("Chamado não encontrado");
|
||||
}
|
||||
|
||||
const agora = Date.now();
|
||||
const horasMs = args.horasAdicionais * 60 * 60 * 1000;
|
||||
let novoPrazoResposta = ticket.prazoResposta;
|
||||
let novoPrazoConclusao = ticket.prazoConclusao;
|
||||
let prazoExtendido: number;
|
||||
|
||||
if (args.prazo === "resposta") {
|
||||
prazoExtendido = (ticket.prazoResposta || agora) + horasMs;
|
||||
novoPrazoResposta = prazoExtendido;
|
||||
// Se o prazo de conclusão é antes do novo prazo de resposta, ajuste-o também
|
||||
if (ticket.prazoConclusao && ticket.prazoConclusao < prazoExtendido) {
|
||||
novoPrazoConclusao = prazoExtendido + (ticket.prazoConclusao - (ticket.prazoResposta || agora));
|
||||
}
|
||||
} else {
|
||||
prazoExtendido = (ticket.prazoConclusao || agora) + horasMs;
|
||||
novoPrazoConclusao = prazoExtendido;
|
||||
}
|
||||
|
||||
// Atualizar timeline
|
||||
const timelineAtualizada = ticket.timeline?.map((etapa) => {
|
||||
if (args.prazo === "resposta" && etapa.etapa === "resposta_inicial") {
|
||||
return {
|
||||
...etapa,
|
||||
prazo: prazoExtendido,
|
||||
status: prazoExtendido > agora ? "pendente" : etapa.status,
|
||||
};
|
||||
}
|
||||
if (args.prazo === "conclusao" && etapa.etapa === "conclusao") {
|
||||
return {
|
||||
...etapa,
|
||||
prazo: prazoExtendido,
|
||||
status: prazoExtendido > agora ? "pendente" : etapa.status,
|
||||
};
|
||||
}
|
||||
return etapa;
|
||||
}) || ticket.timeline;
|
||||
|
||||
await ctx.db.patch(ticket._id, {
|
||||
prazoResposta: novoPrazoResposta,
|
||||
prazoConclusao: novoPrazoConclusao,
|
||||
timeline: timelineAtualizada,
|
||||
atualizadoEm: agora,
|
||||
ultimaInteracaoEm: agora,
|
||||
});
|
||||
|
||||
await registrarInteracao(ctx, {
|
||||
ticketId: ticket._id,
|
||||
autorId: usuario._id,
|
||||
origem: "ti",
|
||||
tipo: "status",
|
||||
conteudo: `Prazo ${args.prazo === "resposta" ? "de resposta" : "de conclusão"} prorrogado em ${args.horasAdicionais}h. Motivo: ${args.motivo}`,
|
||||
});
|
||||
|
||||
const ticketAtualizado = await ctx.db.get(ticket._id);
|
||||
if (ticketAtualizado) {
|
||||
await registrarNotificacoes(ctx, {
|
||||
ticket: ticketAtualizado,
|
||||
titulo: `Prazo prorrogado - Chamado ${ticketAtualizado.numero}`,
|
||||
mensagem: `O prazo ${args.prazo === "resposta" ? "de resposta" : "de conclusão"} foi prorrogado em ${args.horasAdicionais} horas. Motivo: ${args.motivo}`,
|
||||
usuarioEvento: usuario._id,
|
||||
});
|
||||
}
|
||||
|
||||
return { sucesso: true };
|
||||
},
|
||||
});
|
||||
|
||||
export const emitirAlertaPrazo = mutation({
|
||||
args: {
|
||||
ticketId: v.id("tickets"),
|
||||
|
||||
Reference in New Issue
Block a user