feat: add 'Cancelado_RH' status to vacation management
- Introduced a new status 'Cancelado_RH' for vacation requests, allowing for better tracking of cancellations by HR. - Updated the UI components to reflect the new status, including badge colors and alert messages. - Enhanced backend schema and mutation to support the new status, ensuring consistency across the application. - Improved logging and state management for better performance and user experience.
This commit is contained in:
@@ -45,17 +45,13 @@
|
||||
// Estados de loading e error
|
||||
const isLoading = $derived(todasSolicitacoesQuery?.isLoading ?? true);
|
||||
|
||||
// Debug: Log dos dados carregados
|
||||
// Debug: Log dos dados carregados (apenas uma vez quando dados mudam)
|
||||
let ultimoTotalDados = $state<number>(-1);
|
||||
$effect(() => {
|
||||
if (todasSolicitacoesQuery?.data) {
|
||||
console.log('📦 [Backend] Dados carregados:', {
|
||||
total: todasSolicitacoesQuery.data.length,
|
||||
isLoading: todasSolicitacoesQuery.isLoading,
|
||||
dados: todasSolicitacoesQuery.data
|
||||
});
|
||||
}
|
||||
if (todasSolicitacoesQuery?.isLoading !== undefined) {
|
||||
console.log('🔄 [Backend] Estado de loading:', todasSolicitacoesQuery.isLoading);
|
||||
const total = todasSolicitacoesQuery?.data?.length ?? 0;
|
||||
if (total !== ultimoTotalDados && total > 0) {
|
||||
ultimoTotalDados = total;
|
||||
console.log('📦 [Backend] Dados carregados:', { total });
|
||||
}
|
||||
});
|
||||
|
||||
@@ -83,11 +79,18 @@
|
||||
|
||||
// Manter último valor válido para evitar dados desaparecendo
|
||||
let ultimasSolicitacoesValidas = $state<TodasSolicitacoes>([]);
|
||||
let ultimoDataHash = $state<string>('');
|
||||
|
||||
// Atualizar apenas quando temos dados válidos
|
||||
// Atualizar apenas quando temos dados válidos e quando realmente mudou
|
||||
$effect(() => {
|
||||
if (todasSolicitacoesQuery?.data && !hasError) {
|
||||
ultimasSolicitacoesValidas = todasSolicitacoesQuery.data;
|
||||
const dataAtual = todasSolicitacoesQuery?.data;
|
||||
if (dataAtual && !hasError) {
|
||||
// Criar hash simples para comparar se os dados realmente mudaram
|
||||
const dataHash = JSON.stringify(dataAtual.map(d => d._id));
|
||||
if (dataHash !== ultimoDataHash) {
|
||||
ultimoDataHash = dataHash;
|
||||
ultimasSolicitacoesValidas = dataAtual;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -259,31 +262,69 @@
|
||||
|
||||
let rangeInicioIndice = $state(0);
|
||||
let rangeFimIndice = $state(0);
|
||||
let ultimoTamanhoPeriodos = $state(0);
|
||||
let atualizandoRanges = $state(false);
|
||||
|
||||
$effect(() => {
|
||||
if (periodosPorMes.length === 0) {
|
||||
rangeInicioIndice = 0;
|
||||
rangeFimIndice = 0;
|
||||
// Prevenir loops infinitos
|
||||
if (atualizandoRanges) {
|
||||
return;
|
||||
}
|
||||
|
||||
const ultimoIndice = periodosPorMes.length - 1;
|
||||
|
||||
if (rangeFimIndice === 0 && rangeInicioIndice === 0) {
|
||||
rangeFimIndice = ultimoIndice;
|
||||
const tamanhoAtual = periodosPorMes.length;
|
||||
|
||||
// Só atualizar se o tamanho mudou
|
||||
if (tamanhoAtual === 0) {
|
||||
if (rangeInicioIndice !== 0 || rangeFimIndice !== 0) {
|
||||
atualizandoRanges = true;
|
||||
rangeInicioIndice = 0;
|
||||
rangeFimIndice = 0;
|
||||
ultimoTamanhoPeriodos = 0;
|
||||
atualizandoRanges = false;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (rangeInicioIndice > ultimoIndice) {
|
||||
rangeInicioIndice = ultimoIndice;
|
||||
// Se o tamanho mudou, resetar os ranges
|
||||
if (tamanhoAtual !== ultimoTamanhoPeriodos) {
|
||||
atualizandoRanges = true;
|
||||
ultimoTamanhoPeriodos = tamanhoAtual;
|
||||
const ultimoIndice = tamanhoAtual - 1;
|
||||
|
||||
if (rangeFimIndice === 0 && rangeInicioIndice === 0) {
|
||||
rangeFimIndice = ultimoIndice;
|
||||
atualizandoRanges = false;
|
||||
return;
|
||||
}
|
||||
atualizandoRanges = false;
|
||||
}
|
||||
|
||||
if (rangeFimIndice > ultimoIndice) {
|
||||
rangeFimIndice = ultimoIndice;
|
||||
const ultimoIndice = tamanhoAtual - 1;
|
||||
let precisaAtualizar = false;
|
||||
let novoInicio = rangeInicioIndice;
|
||||
let novoFim = rangeFimIndice;
|
||||
|
||||
if (novoInicio > ultimoIndice) {
|
||||
novoInicio = ultimoIndice;
|
||||
precisaAtualizar = true;
|
||||
}
|
||||
|
||||
if (rangeInicioIndice > rangeFimIndice) {
|
||||
rangeInicioIndice = rangeFimIndice;
|
||||
if (novoFim > ultimoIndice) {
|
||||
novoFim = ultimoIndice;
|
||||
precisaAtualizar = true;
|
||||
}
|
||||
|
||||
if (novoInicio > novoFim) {
|
||||
novoInicio = novoFim;
|
||||
precisaAtualizar = true;
|
||||
}
|
||||
|
||||
// Só atualizar se realmente precisar e se os valores mudaram
|
||||
if (precisaAtualizar && (novoInicio !== rangeInicioIndice || novoFim !== rangeFimIndice)) {
|
||||
atualizandoRanges = true;
|
||||
rangeInicioIndice = novoInicio;
|
||||
rangeFimIndice = novoFim;
|
||||
atualizandoRanges = false;
|
||||
}
|
||||
});
|
||||
|
||||
@@ -305,19 +346,16 @@
|
||||
(() => {
|
||||
const agregados = new SvelteMap<number, SolicitacoesPorAnoResumo>();
|
||||
|
||||
for (const solicitacao of solicitacoesAprovadas) {
|
||||
const totalDias = solicitacao.periodos.reduce(
|
||||
(acc, periodo) => acc + periodo.diasFerias,
|
||||
0
|
||||
);
|
||||
const existente = agregados.get(solicitacao.anoReferencia) ?? {
|
||||
ano: solicitacao.anoReferencia,
|
||||
for (const periodo of solicitacoesAprovadas) {
|
||||
// solicitacoesAprovadas são períodos individuais, não agrupados
|
||||
const existente = agregados.get(periodo.anoReferencia) ?? {
|
||||
ano: periodo.anoReferencia,
|
||||
solicitacoes: 0,
|
||||
diasTotais: 0
|
||||
};
|
||||
existente.solicitacoes += 1;
|
||||
existente.diasTotais += totalDias;
|
||||
agregados.set(solicitacao.anoReferencia, existente);
|
||||
existente.diasTotais += periodo.diasFerias;
|
||||
agregados.set(periodo.anoReferencia, existente);
|
||||
}
|
||||
|
||||
return Array.from(agregados.values()).sort((a, b) => a.ano - b.ano);
|
||||
@@ -456,13 +494,13 @@
|
||||
})
|
||||
);
|
||||
|
||||
// Debug: Log dos eventos gerados
|
||||
// Debug: Log dos eventos gerados (apenas quando mudar significativamente)
|
||||
let ultimoTotalEventos = $state<number>(-1);
|
||||
$effect(() => {
|
||||
console.log('📅 [Eventos] Total de eventos:', eventosFerias.length);
|
||||
console.log('📋 [Periodos] Total de períodos:', periodosDetalhados.length);
|
||||
console.log('✅ [Aprovadas] Total de solicitações aprovadas:', solicitacoesAprovadas.length);
|
||||
if (eventosFerias.length > 0) {
|
||||
console.log('📅 [Eventos] Primeiro evento:', eventosFerias[0]);
|
||||
const totalEventos = eventosFerias.length;
|
||||
if (totalEventos !== ultimoTotalEventos && import.meta.env.DEV) {
|
||||
ultimoTotalEventos = totalEventos;
|
||||
console.log('📅 [Eventos] Total:', totalEventos);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -484,16 +522,9 @@
|
||||
}
|
||||
|
||||
try {
|
||||
console.log('🔄 [Calendário] Iniciando inicialização...');
|
||||
console.log('📊 [Calendário] Estado:', {
|
||||
container: !!calendarioContainer,
|
||||
loading: isLoading,
|
||||
error: hasError,
|
||||
eventos: eventosFerias.length,
|
||||
solicitacoes: solicitacoes.length,
|
||||
solicitacoesAprovadas: solicitacoesAprovadas.length,
|
||||
periodosDetalhados: periodosDetalhados.length
|
||||
});
|
||||
if (import.meta.env.DEV) {
|
||||
console.log('🔄 [Calendário] Iniciando inicialização...');
|
||||
}
|
||||
|
||||
const [coreModule, dayGridModule, interactionModule, localeModule] = await Promise.all([
|
||||
import('@fullcalendar/core'),
|
||||
@@ -510,7 +541,6 @@
|
||||
calendarioInstance ||
|
||||
calendarioInicializado
|
||||
) {
|
||||
console.log('⚠️ [Calendário] Condições alteradas após imports');
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -519,11 +549,6 @@
|
||||
const interactionPlugin = interactionModule.default;
|
||||
const ptBrLocale = localeModule.default;
|
||||
|
||||
console.log('✅ [Calendário] Criando instância com', eventosFerias.length, 'eventos');
|
||||
if (eventosFerias.length > 0) {
|
||||
console.log('📅 [Calendário] Primeiro evento:', eventosFerias[0]);
|
||||
}
|
||||
|
||||
calendarioInstance = new CalendarClass(calendarioContainer, {
|
||||
plugins: [dayGridPlugin, interactionPlugin],
|
||||
initialView: 'dayGridMonth',
|
||||
@@ -547,7 +572,9 @@
|
||||
|
||||
calendarioInstance.render();
|
||||
calendarioInicializado = true;
|
||||
console.log('✅ [Calendário] Inicializado com sucesso!');
|
||||
if (import.meta.env.DEV) {
|
||||
console.log('✅ [Calendário] Inicializado com sucesso!');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('❌ [Calendário] Erro ao inicializar:', error);
|
||||
if (error instanceof Error) {
|
||||
@@ -613,68 +640,87 @@
|
||||
};
|
||||
});
|
||||
|
||||
// Sincronizar eventos do calendário quando mudarem
|
||||
// Sincronizar eventos do calendário quando mudarem (com debounce)
|
||||
let ultimosEventosSerializados = $state<string>('');
|
||||
let timeoutAtualizacaoCalendario = $state<ReturnType<typeof setTimeout> | null>(null);
|
||||
|
||||
$effect(() => {
|
||||
// Não atualizar se o calendário não estiver inicializado ou estiver carregando
|
||||
if (!calendarioInstance || !calendarioInicializado || isLoading || hasError) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
console.log('🔄 Atualizando eventos do calendário:', eventosFerias.length, 'eventos');
|
||||
// Serializar eventos para comparar mudanças
|
||||
const eventosSerializados = JSON.stringify(eventosFerias.map(e => ({
|
||||
id: e.id,
|
||||
start: e.start,
|
||||
end: e.end,
|
||||
title: e.title
|
||||
})));
|
||||
|
||||
// Remover todos os eventos existentes
|
||||
calendarioInstance.removeAllEvents();
|
||||
|
||||
// Adicionar novos eventos
|
||||
const eventosClonados = eventosFerias.map((evento) => ({
|
||||
...evento,
|
||||
extendedProps: { ...evento.extendedProps }
|
||||
}));
|
||||
|
||||
// Adicionar eventos em lote
|
||||
for (const evento of eventosClonados) {
|
||||
calendarioInstance.addEvent(evento);
|
||||
}
|
||||
|
||||
console.log('✅ Eventos atualizados com sucesso!');
|
||||
} catch (error) {
|
||||
// Log do erro, mas não interromper o fluxo
|
||||
if (error instanceof Error) {
|
||||
console.error('❌ Erro ao atualizar eventos do calendário:', error.message);
|
||||
} else {
|
||||
console.error('❌ Erro ao atualizar eventos do calendário:', error);
|
||||
}
|
||||
// Se os eventos não mudaram, não fazer nada
|
||||
if (eventosSerializados === ultimosEventosSerializados) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Limpar timeout anterior se existir
|
||||
if (timeoutAtualizacaoCalendario) {
|
||||
clearTimeout(timeoutAtualizacaoCalendario);
|
||||
}
|
||||
|
||||
// Atualizar referência serializada imediatamente para evitar re-execuções
|
||||
ultimosEventosSerializados = eventosSerializados;
|
||||
|
||||
// Debounce para evitar atualizações muito frequentes
|
||||
timeoutAtualizacaoCalendario = setTimeout(() => {
|
||||
try {
|
||||
// Verificar novamente se o calendário ainda está válido
|
||||
if (!calendarioInstance || !calendarioInicializado) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Remover todos os eventos existentes
|
||||
calendarioInstance.removeAllEvents();
|
||||
|
||||
// Adicionar novos eventos
|
||||
const eventosClonados = eventosFerias.map((evento) => ({
|
||||
...evento,
|
||||
extendedProps: { ...evento.extendedProps }
|
||||
}));
|
||||
|
||||
// Adicionar eventos em lote
|
||||
for (const evento of eventosClonados) {
|
||||
calendarioInstance.addEvent(evento);
|
||||
}
|
||||
} catch (error) {
|
||||
// Log do erro, mas não interromper o fluxo
|
||||
if (error instanceof Error) {
|
||||
console.error('❌ Erro ao atualizar eventos do calendário:', error.message);
|
||||
} else {
|
||||
console.error('❌ Erro ao atualizar eventos do calendário:', error);
|
||||
}
|
||||
}
|
||||
}, 300); // Debounce de 300ms
|
||||
|
||||
// Cleanup
|
||||
return () => {
|
||||
if (timeoutAtualizacaoCalendario) {
|
||||
clearTimeout(timeoutAtualizacaoCalendario);
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
if (typeof window !== 'undefined') {
|
||||
$effect(() => {
|
||||
(
|
||||
window as Window & typeof globalThis & { __feriasSolicitacoes: TodasSolicitacoes }
|
||||
).__feriasSolicitacoes = solicitacoes;
|
||||
(
|
||||
window as Window & typeof globalThis & { __feriasFiltradas: TodasSolicitacoes }
|
||||
).__feriasFiltradas = solicitacoesFiltradas;
|
||||
(
|
||||
window as Window & typeof globalThis & { __feriasAprovadas: TodasSolicitacoes }
|
||||
).__feriasAprovadas = solicitacoesAprovadas;
|
||||
(
|
||||
window as Window & typeof globalThis & { __feriasPeriodos: PeriodoDetalhado[] }
|
||||
).__feriasPeriodos = periodosDetalhados;
|
||||
(window as Window & typeof globalThis & { __feriasPorMes: PeriodoPorMes[] }).__feriasPorMes =
|
||||
periodosPorMes;
|
||||
(
|
||||
window as Window & typeof globalThis & { __feriasPorAno: SolicitacoesPorAnoResumo[] }
|
||||
).__feriasPorAno = solicitacoesPorAno;
|
||||
});
|
||||
}
|
||||
// Atualizar variáveis no window apenas em desenvolvimento (desabilitado temporariamente para evitar loops)
|
||||
// if (typeof window !== 'undefined' && import.meta.env.DEV) {
|
||||
// // Código comentado para evitar loops infinitos
|
||||
// }
|
||||
function getStatusBadge(status: string) {
|
||||
const badges: Record<string, string> = {
|
||||
aguardando_aprovacao: 'badge-warning',
|
||||
aprovado: 'badge-success',
|
||||
reprovado: 'badge-error',
|
||||
data_ajustada_aprovada: 'badge-info'
|
||||
data_ajustada_aprovada: 'badge-info',
|
||||
Cancelado_RH: 'badge-error'
|
||||
};
|
||||
return badges[status] || 'badge-neutral';
|
||||
}
|
||||
@@ -684,7 +730,8 @@
|
||||
aguardando_aprovacao: 'Aguardando',
|
||||
aprovado: 'Aprovado',
|
||||
reprovado: 'Reprovado',
|
||||
data_ajustada_aprovada: 'Ajustado'
|
||||
data_ajustada_aprovada: 'Ajustado',
|
||||
Cancelado_RH: 'Cancelado RH'
|
||||
};
|
||||
return textos[status] || status;
|
||||
}
|
||||
@@ -1320,6 +1367,7 @@
|
||||
<option value="aguardando_aprovacao">Aguardando Aprovação</option>
|
||||
<option value="aprovado">Aprovado</option>
|
||||
<option value="reprovado">Reprovado</option>
|
||||
<option value="data_ajustada_aprovada">Data Ajustada</option>
|
||||
<option value="Cancelado_RH">Cancelado RH</option>
|
||||
</select>
|
||||
<p class="text-base-content/60 text-xs leading-relaxed">
|
||||
|
||||
Reference in New Issue
Block a user