feat: implement error handling and logging in server hooks to capture and notify on 404 and 500 errors, enhancing server reliability and monitoring

This commit is contained in:
2025-12-08 11:52:27 -03:00
parent e1f1af7530
commit fdfbd8b051
31 changed files with 7305 additions and 932 deletions

View File

@@ -2,6 +2,8 @@ import { v } from 'convex/values';
import { mutation, query, internalMutation } from './_generated/server';
import { internal } from './_generated/api';
import { Id, Doc } from './_generated/dataModel';
import { verificarLicencaAtiva } from './atestadosLicencas';
import { getCurrentUserFunction } from './auth';
// Validador para períodos
const periodoValidator = v.object({
@@ -46,7 +48,7 @@ function agruparPorSolicitacao(registros: Array<Doc<'ferias'>>): Array<{
grupos.get(chave)!.push(registro);
}
return Array.from(grupos.entries()).map(([_, periodos]) => {
return Array.from(grupos.entries()).map(([, periodos]) => {
// Ordenar por data de criação para manter ordem
periodos.sort((a, b) => a._creationTime - b._creationTime);
@@ -819,7 +821,15 @@ export const atualizarStatusTodosFuncionarios = internalMutation({
}
}
const novoStatus = emFerias ? 'em_ferias' : 'ativo';
// Determinar o status: férias tem prioridade sobre licença
let novoStatus: 'ativo' | 'em_ferias' | 'em_licenca';
if (emFerias) {
novoStatus = 'em_ferias';
} else {
// Se não está em férias, verificar se está em licença
const emLicenca = await verificarLicencaAtiva(ctx, func._id, hoje);
novoStatus = emLicenca ? 'em_licenca' : 'ativo';
}
if (func.statusFerias !== novoStatus) {
await ctx.db.patch(func._id, { statusFerias: novoStatus });
@@ -829,3 +839,142 @@ export const atualizarStatusTodosFuncionarios = internalMutation({
return null;
}
});
// Internal Mutation: Atualizar status de um funcionário específico
export const atualizarStatusFuncionario = internalMutation({
args: {
funcionarioId: v.id('funcionarios')
},
returns: v.null(),
handler: async (ctx, args) => {
const func = await ctx.db.get(args.funcionarioId);
if (!func) return null;
const hoje = new Date();
hoje.setHours(0, 0, 0, 0);
// Buscar todos os registros de férias que podem estar em férias
const feriasAprovadas = await ctx.db
.query('ferias')
.withIndex('by_funcionario_and_status', (q) =>
q.eq('funcionarioId', func._id).eq('status', 'aprovado')
)
.collect();
const feriasAjustadas = await ctx.db
.query('ferias')
.withIndex('by_funcionario_and_status', (q) =>
q.eq('funcionarioId', func._id).eq('status', 'data_ajustada_aprovada')
)
.collect();
const feriasEmFerias = await ctx.db
.query('ferias')
.withIndex('by_funcionario_and_status', (q) =>
q.eq('funcionarioId', func._id).eq('status', 'EmFérias')
)
.collect();
const idsAprovados = new Set(feriasAprovadas.map((f) => f._id));
const idsAjustados = new Set(feriasAjustadas.map((f) => f._id));
const statusAnteriorPorId = new Map<Id<'ferias'>, 'aprovado' | 'data_ajustada_aprovada'>();
for (const ferias of feriasEmFerias) {
if (ferias.historicoAlteracoes && ferias.historicoAlteracoes.length > 0) {
const historico = ferias.historicoAlteracoes;
for (let i = historico.length - 1; i >= 0; i--) {
const entrada = historico[i];
if (entrada.acao.includes('Aprovado') || entrada.acao.includes('aprovado')) {
statusAnteriorPorId.set(ferias._id, 'aprovado');
break;
} else if (entrada.acao.includes('Data ajustada') || entrada.acao.includes('ajustada')) {
statusAnteriorPorId.set(ferias._id, 'data_ajustada_aprovada');
break;
}
}
}
if (!statusAnteriorPorId.has(ferias._id)) {
statusAnteriorPorId.set(ferias._id, 'aprovado');
}
}
const todasFerias = [...feriasAprovadas, ...feriasAjustadas, ...feriasEmFerias];
let emFerias = false;
for (const ferias of todasFerias) {
const inicio = new Date(ferias.dataInicio);
const fim = new Date(ferias.dataFim);
inicio.setHours(0, 0, 0, 0);
fim.setHours(23, 59, 59, 999);
if (hoje >= inicio && hoje <= fim) {
emFerias = true;
if (ferias.status !== 'EmFérias') {
await ctx.db.patch(ferias._id, {
status: 'EmFérias'
});
}
} else {
if (ferias.status === 'EmFérias') {
let statusAnterior: 'aprovado' | 'data_ajustada_aprovada';
if (idsAprovados.has(ferias._id)) {
statusAnterior = 'aprovado';
} else if (idsAjustados.has(ferias._id)) {
statusAnterior = 'data_ajustada_aprovada';
} else {
statusAnterior = statusAnteriorPorId.get(ferias._id) || 'aprovado';
}
await ctx.db.patch(ferias._id, {
status: statusAnterior
});
}
}
}
// Determinar o status: férias tem prioridade sobre licença
let novoStatus: 'ativo' | 'em_ferias' | 'em_licenca';
if (emFerias) {
novoStatus = 'em_ferias';
console.log(`[atualizarStatusFuncionario] Funcionário ${func._id} está em férias`);
} else {
// Se não está em férias, verificar se está em licença
const emLicenca = await verificarLicencaAtiva(ctx, func._id, hoje);
novoStatus = emLicenca ? 'em_licenca' : 'ativo';
console.log(
`[atualizarStatusFuncionario] Funcionário ${func._id}: emLicenca=${emLicenca}, novoStatus=${novoStatus}`
);
}
if (func.statusFerias !== novoStatus) {
console.log(
`[atualizarStatusFuncionario] Atualizando status de ${func.statusFerias} para ${novoStatus}`
);
await ctx.db.patch(func._id, { statusFerias: novoStatus });
} else {
console.log(`[atualizarStatusFuncionario] Status já está correto: ${novoStatus}`);
}
return null;
}
});
// Mutation pública para atualizar status do funcionário atual (útil para debug/teste)
export const atualizarMeuStatus = mutation({
args: {},
returns: v.null(),
handler: async (ctx) => {
const usuario = await getCurrentUserFunction(ctx);
if (!usuario || !usuario.funcionarioId) {
throw new Error('Usuário não encontrado ou não possui funcionário associado');
}
await ctx.runMutation(internal.ferias.atualizarStatusFuncionario, {
funcionarioId: usuario.funcionarioId
});
return null;
}
});