feat: enhance vacation management system with new employee association functionality, improved email notification handling, and comprehensive documentation; update dependencies and UI components for better user experience

This commit is contained in:
2025-10-30 09:27:10 -03:00
parent 21b41121db
commit fd445e8246
43 changed files with 6097 additions and 515 deletions

View File

@@ -98,6 +98,7 @@ export const listarFilaEmails = query({
)),
limite: v.optional(v.number()),
},
returns: v.array(v.any()),
handler: async (ctx, args) => {
let query = ctx.db.query("notificacoesEmail");
@@ -140,9 +141,7 @@ export const reenviarEmail = mutation({
});
/**
* Action para enviar email (será implementado com nodemailer)
*
* NOTA: Este é um placeholder. Implementação real requer nodemailer.
* Action para enviar email REAL usando nodemailer
*/
export const enviarEmailAction = action({
args: {
@@ -150,7 +149,9 @@ export const enviarEmailAction = action({
},
returns: v.object({ sucesso: v.boolean(), erro: v.optional(v.string()) }),
handler: async (ctx, args) => {
// TODO: Implementar com nodemailer quando instalado
"use node";
const nodemailer = require("nodemailer");
try {
// Buscar email da fila
@@ -171,7 +172,11 @@ export const enviarEmailAction = action({
});
if (!config) {
return { sucesso: false, erro: "Configuração de email não encontrada" };
return { sucesso: false, erro: "Configuração de email não encontrada ou inativa" };
}
if (!config.testado) {
return { sucesso: false, erro: "Configuração SMTP não foi testada. Teste a conexão primeiro!" };
}
// Marcar como enviando
@@ -183,13 +188,33 @@ export const enviarEmailAction = action({
});
});
// TODO: Enviar email real com nodemailer aqui
console.log("⚠️ AVISO: Envio de email simulado (nodemailer não instalado)");
// Criar transporter do nodemailer
const transporter = nodemailer.createTransport({
host: config.smtpHost,
port: config.smtpPort,
secure: config.smtpSecure, // true para 465, false para outros
auth: {
user: config.smtpUser,
pass: config.smtpPassword,
},
tls: {
// Não rejeitar certificados não autorizados (útil para testes)
rejectUnauthorized: false
}
});
// Enviar email REAL
const info = await transporter.sendMail({
from: `"${config.remetenteNome}" <${config.remetenteEmail}>`,
to: email.destinatario,
subject: email.assunto,
html: email.corpo,
});
console.log("✅ Email enviado com sucesso!");
console.log(" Para:", email.destinatario);
console.log(" Assunto:", email.assunto);
// Simular delay de envio
await new Promise((resolve) => setTimeout(resolve, 500));
console.log(" Message ID:", info.messageId);
// Marcar como enviado
await ctx.runMutation(async (ctx) => {
@@ -201,6 +226,8 @@ export const enviarEmailAction = action({
return { sucesso: true };
} catch (error: any) {
console.error("❌ Erro ao enviar email:", error.message);
// Marcar como falha
await ctx.runMutation(async (ctx) => {
const email = await ctx.db.get(args.emailId);
@@ -221,6 +248,7 @@ export const enviarEmailAction = action({
*/
export const processarFilaEmails = internalMutation({
args: {},
returns: v.object({ processados: v.number() }),
handler: async (ctx) => {
// Buscar emails pendentes (max 10 por execução)
const emailsPendentes = await ctx.db
@@ -240,17 +268,17 @@ export const processarFilaEmails = internalMutation({
continue;
}
// Agendar envio (será feito por uma action separada)
// Por enquanto, apenas marca como enviado para não bloquear
await ctx.db.patch(email._id, {
status: "enviado",
enviadoEm: Date.now(),
// Agendar envio via action
// IMPORTANTE: Não podemos chamar action diretamente de mutation
// Por isso, usaremos o scheduler
await ctx.scheduler.runAfter(0, "email:enviarEmailAction" as any, {
emailId: email._id,
});
processados++;
}
console.log(`📧 Fila de emails processada: ${processados} emails`);
console.log(`📧 Fila de emails processada: ${processados} emails agendados para envio`);
return { processados };
},