feat: implement scheduling for notifications with enhanced validation

- Added functionality to schedule notifications for future delivery, including date and time selection in the UI.
- Implemented validation to ensure scheduled times are in the future and correctly formatted.
- Updated backend email handling to support scheduled sending, with appropriate checks for agendamentos.
- Enhanced user feedback for both immediate and scheduled notifications, improving overall user experience.
This commit is contained in:
2025-11-03 23:11:27 -03:00
parent e59d96735a
commit 3d8f907fa5
2 changed files with 227 additions and 39 deletions

View File

@@ -21,6 +21,7 @@ export const enfileirarEmail = mutation({
corpo: v.string(),
templateId: v.optional(v.id("templatesMensagens")),
enviadoPorId: v.id("usuarios"),
agendadaPara: v.optional(v.number()), // timestamp opcional para agendamento
},
returns: v.object({
sucesso: v.boolean(),
@@ -33,6 +34,13 @@ export const enfileirarEmail = mutation({
return { sucesso: false };
}
// Validar agendamento se fornecido
if (args.agendadaPara !== undefined) {
if (args.agendadaPara <= Date.now()) {
return { sucesso: false };
}
}
// Adicionar à fila
const emailId = await ctx.db.insert("notificacoesEmail", {
destinatario: args.destinatario,
@@ -44,12 +52,22 @@ export const enfileirarEmail = mutation({
tentativas: 0,
enviadoPor: args.enviadoPorId,
criadoEm: Date.now(),
agendadaPara: args.agendadaPara,
});
// Agendar envio imediato via action
await ctx.scheduler.runAfter(0, api.actions.email.enviar, {
emailId,
});
// Agendar envio
if (args.agendadaPara !== undefined) {
// Agendar para o momento especificado
const delayMs = args.agendadaPara - Date.now();
await ctx.scheduler.runAfter(delayMs, api.actions.email.enviar, {
emailId,
});
} else {
// Envio imediato
await ctx.scheduler.runAfter(0, api.actions.email.enviar, {
emailId,
});
}
return { sucesso: true, emailId };
},
@@ -65,6 +83,7 @@ export const enviarEmailComTemplate = mutation({
templateCodigo: v.string(),
variaveis: v.record(v.string(), v.string()),
enviadoPorId: v.id("usuarios"),
agendadaPara: v.optional(v.number()), // timestamp opcional para agendamento
},
returns: v.object({
sucesso: v.boolean(),
@@ -82,6 +101,13 @@ export const enviarEmailComTemplate = mutation({
return { sucesso: false };
}
// Validar agendamento se fornecido
if (args.agendadaPara !== undefined) {
if (args.agendadaPara <= Date.now()) {
return { sucesso: false };
}
}
// Renderizar template
const assunto = renderizarTemplate(template.titulo, args.variaveis);
const corpo = renderizarTemplate(template.corpo, args.variaveis);
@@ -97,12 +123,22 @@ export const enviarEmailComTemplate = mutation({
tentativas: 0,
enviadoPor: args.enviadoPorId,
criadoEm: Date.now(),
agendadaPara: args.agendadaPara,
});
// Agendar envio imediato via action
await ctx.scheduler.runAfter(0, api.actions.email.enviar, {
emailId,
});
// Agendar envio
if (args.agendadaPara !== undefined) {
// Agendar para o momento especificado
const delayMs = args.agendadaPara - Date.now();
await ctx.scheduler.runAfter(delayMs, api.actions.email.enviar, {
emailId,
});
} else {
// Envio imediato
await ctx.scheduler.runAfter(0, api.actions.email.enviar, {
emailId,
});
}
return { sucesso: true, emailId };
},