feat: enhance absence management with new filters and reporting options, including PDF and Excel generation capabilities

This commit is contained in:
2025-12-06 01:11:33 -03:00
parent 1000b5a030
commit 72450d1f28
15 changed files with 1774 additions and 217 deletions

View File

@@ -24,9 +24,11 @@
let timestampOriginal = $state<number | null>(null);
let timestampUTC = $state<number | null>(null);
let intervaloRelogio: ReturnType<typeof setInterval> | null = null;
let dataLoaded = $state(false);
// Carregar dados apenas uma vez quando a query retornar
$effect(() => {
if (configQuery?.data) {
if (configQuery?.data && !dataLoaded) {
servidorNTP = configQuery.data.servidorNTP || 'pool.ntp.org';
portaNTP = configQuery.data.portaNTP || 123;
usarServidorExterno = configQuery.data.usarServidorExterno || false;
@@ -40,6 +42,8 @@
offsetSegundos: configQuery.data.offsetSegundos ?? null,
usandoServidorExterno: configQuery.data.usarServidorExterno || false
};
dataLoaded = true; // Marcar como carregado para evitar sobrescrever mudanças do usuário
}
});
@@ -53,20 +57,50 @@
timestampUTC = resultado.timestamp; // Timestamp UTC da fonte
// Calcular o timestamp original (antes do ajuste GMT)
timestampOriginal = timestampUTC;
// Atualizar status de sincronização
statusSincronizacao = {
ultimaSincronizacao: Date.now(),
offsetSegundos: resultado.offsetSegundos ?? null,
usandoServidorExterno: resultado.usandoServidorExterno || false
};
}
} else {
// Se não usar servidor externo, usar tempo local do PC (igual ao relógio de ponto)
// Se não usar servidor externo, usar tempo do PC como base UTC
// Date.now() retorna timestamp UTC (milissegundos desde epoch)
timestampUTC = Date.now();
timestampOriginal = timestampUTC;
// Atualizar status para indicar que está usando relógio do PC
statusSincronizacao = {
ultimaSincronizacao: Date.now(),
offsetSegundos: null,
usandoServidorExterno: false
};
}
} catch (error) {
console.error('Erro ao obter tempo sincronizado:', error);
// Fallback: usar tempo local
// Fallback: usar tempo do PC
timestampUTC = Date.now();
timestampOriginal = timestampUTC;
// Atualizar status para indicar fallback
statusSincronizacao = {
ultimaSincronizacao: Date.now(),
offsetSegundos: null,
usandoServidorExterno: false
};
}
}
// Atualizar relógio quando usarServidorExterno mudar (após carregamento inicial)
$effect(() => {
// Quando a opção de usar servidor externo mudar, atualizar o relógio
// Só atualizar após os dados serem carregados para evitar atualizações desnecessárias
if (dataLoaded) {
// Usar usarServidorExterno como dependência reativa
const _ = usarServidorExterno; // Forçar reatividade
obterTempoSincronizado();
}
});
// Inicializar e atualizar relógios periodicamente
$effect(() => {
// Obter tempo inicial quando configuração mudar
@@ -97,46 +131,51 @@
// Funções para formatar os relógios
function formatarRelogio(timestamp: number | null, ajusteGMT: number = 0): string {
if (!timestamp) return '--:--:--';
// Aplicar GMT offset ao timestamp
// Quando GMT é 0, usar timestamp UTC puro e deixar toLocaleTimeString() fazer a conversão automática
// Quando GMT ≠ 0, aplicar offset configurado ao timestamp
let timestampAjustado: number;
if (ajusteGMT !== 0) {
// Aplicar offset configurado
timestampAjustado = timestamp + ajusteGMT * 60 * 60 * 1000;
} else {
// Quando GMT = 0, manter timestamp UTC puro
// O toLocaleTimeString() converterá automaticamente para o timezone local do navegador
timestampAjustado = timestamp;
// Quando ajusteGMT = 0, usar UTC explícito (sem GMT)
if (ajusteGMT === 0) {
return new Date(timestamp).toLocaleTimeString('pt-BR', {
hour: '2-digit',
minute: '2-digit',
second: '2-digit',
hour12: false,
timeZone: 'UTC' // Forçar UTC puro
});
}
const data = new Date(timestampAjustado);
return data.toLocaleTimeString('pt-BR', {
// Quando ajusteGMT ≠ 0, aplicar offset configurado ao timestamp UTC
// O offset é aplicado manualmente, então usamos UTC como base para evitar conversão dupla
const timestampAjustado = timestamp + ajusteGMT * 60 * 60 * 1000;
return new Date(timestampAjustado).toLocaleTimeString('pt-BR', {
hour: '2-digit',
minute: '2-digit',
second: '2-digit',
hour12: false
hour12: false,
timeZone: 'UTC' // Usar UTC como base pois já aplicamos o offset manualmente
});
}
function formatarDataRelogio(timestamp: number | null, ajusteGMT: number = 0): string {
if (!timestamp) return '--/--/----';
// Aplicar GMT offset ao timestamp
// Quando GMT é 0, usar timestamp UTC puro e deixar toLocaleDateString() fazer a conversão automática
// Quando GMT ≠ 0, aplicar offset configurado ao timestamp
let timestampAjustado: number;
if (ajusteGMT !== 0) {
// Aplicar offset configurado
timestampAjustado = timestamp + ajusteGMT * 60 * 60 * 1000;
} else {
// Quando GMT = 0, manter timestamp UTC puro
// O toLocaleDateString() converterá automaticamente para o timezone local do navegador
timestampAjustado = timestamp;
// Quando ajusteGMT = 0, usar UTC explícito (sem GMT)
if (ajusteGMT === 0) {
return new Date(timestamp).toLocaleDateString('pt-BR', {
day: '2-digit',
month: '2-digit',
year: 'numeric',
timeZone: 'UTC' // Forçar UTC puro
});
}
const data = new Date(timestampAjustado);
return data.toLocaleDateString('pt-BR', {
// Quando ajusteGMT ≠ 0, aplicar offset configurado ao timestamp UTC
// O offset é aplicado manualmente, então usamos UTC como base para evitar conversão dupla
const timestampAjustado = timestamp + ajusteGMT * 60 * 60 * 1000;
return new Date(timestampAjustado).toLocaleDateString('pt-BR', {
day: '2-digit',
month: '2-digit',
year: 'numeric'
year: 'numeric',
timeZone: 'UTC' // Usar UTC como base pois já aplicamos o offset manualmente
});
}
@@ -668,3 +707,4 @@
</div>
</div>
</div>

View File

@@ -427,11 +427,11 @@
onclick={() => toggleRecurso(roleId, item.recurso)}
disabled={salvando}
>
<span class="text-lg font-semibold">{item.recurso}</span>
<ChevronDown
<span class="text-lg font-semibold">{item.recurso}</span>
<ChevronDown
class="h-5 w-5 transition-transform {recursoExpandido ? 'rotate-180' : ''}"
strokeWidth={2}
/>
strokeWidth={2}
/>
</button>
<!-- Lista de ações (visível quando expandido) -->