feat: integrate point management features into the dashboard
- Added a new "Meu Ponto" section for users to register their work hours, breaks, and attendance. - Introduced a "Controle de Ponto" category in the Recursos Humanos section for managing employee time records. - Enhanced the backend schema to support point registration and configuration settings. - Updated various components to improve UI consistency and user experience across the dashboard.
This commit is contained in:
118
apps/web/src/lib/components/ponto/RelogioSincronizado.svelte
Normal file
118
apps/web/src/lib/components/ponto/RelogioSincronizado.svelte
Normal file
@@ -0,0 +1,118 @@
|
||||
<script lang="ts">
|
||||
import { onMount, onDestroy } from 'svelte';
|
||||
import { useConvexClient } from 'convex-svelte';
|
||||
import { api } from '@sgse-app/backend/convex/_generated/api';
|
||||
import { obterTempoServidor, obterTempoPC } from '$lib/utils/sincronizacaoTempo';
|
||||
import { CheckCircle2, AlertCircle, Clock } from 'lucide-svelte';
|
||||
|
||||
const client = useConvexClient();
|
||||
|
||||
let tempoAtual = $state<Date>(new Date());
|
||||
let sincronizado = $state(false);
|
||||
let usandoServidorExterno = $state(false);
|
||||
let offsetSegundos = $state(0);
|
||||
let erro = $state<string | null>(null);
|
||||
let intervalId: ReturnType<typeof setInterval> | null = null;
|
||||
|
||||
async function atualizarTempo() {
|
||||
try {
|
||||
const config = await client.query(api.configuracaoRelogio.obterConfiguracao, {});
|
||||
|
||||
if (config.usarServidorExterno) {
|
||||
try {
|
||||
const resultado = await client.action(api.configuracaoRelogio.sincronizarTempo, {});
|
||||
if (resultado.sucesso && resultado.timestamp) {
|
||||
tempoAtual = new Date(resultado.timestamp);
|
||||
sincronizado = true;
|
||||
usandoServidorExterno = resultado.usandoServidorExterno || false;
|
||||
offsetSegundos = resultado.offsetSegundos || 0;
|
||||
erro = null;
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn('Erro ao sincronizar:', error);
|
||||
if (config.fallbackParaPC) {
|
||||
tempoAtual = new Date(obterTempoPC());
|
||||
sincronizado = false;
|
||||
usandoServidorExterno = false;
|
||||
erro = 'Usando relógio do PC (falha na sincronização)';
|
||||
} else {
|
||||
erro = 'Falha ao sincronizar tempo';
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Usar tempo do servidor Convex
|
||||
const tempoServidor = await obterTempoServidor(client);
|
||||
tempoAtual = new Date(tempoServidor);
|
||||
sincronizado = true;
|
||||
usandoServidorExterno = false;
|
||||
erro = null;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Erro ao obter tempo:', error);
|
||||
tempoAtual = new Date(obterTempoPC());
|
||||
sincronizado = false;
|
||||
erro = 'Erro ao obter tempo do servidor';
|
||||
}
|
||||
}
|
||||
|
||||
function atualizarRelogio() {
|
||||
// Atualizar segundo a segundo
|
||||
const agora = new Date(tempoAtual.getTime() + 1000);
|
||||
tempoAtual = agora;
|
||||
}
|
||||
|
||||
onMount(async () => {
|
||||
await atualizarTempo();
|
||||
// Sincronizar a cada 30 segundos
|
||||
setInterval(atualizarTempo, 30000);
|
||||
// Atualizar display a cada segundo
|
||||
intervalId = setInterval(atualizarRelogio, 1000);
|
||||
});
|
||||
|
||||
onDestroy(() => {
|
||||
if (intervalId) {
|
||||
clearInterval(intervalId);
|
||||
}
|
||||
});
|
||||
|
||||
const horaFormatada = $derived.by(() => {
|
||||
return tempoAtual.toLocaleTimeString('pt-BR', {
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
second: '2-digit',
|
||||
});
|
||||
});
|
||||
|
||||
const dataFormatada = $derived.by(() => {
|
||||
return tempoAtual.toLocaleDateString('pt-BR', {
|
||||
weekday: 'long',
|
||||
day: '2-digit',
|
||||
month: 'long',
|
||||
year: 'numeric',
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
<div class="flex flex-col items-center gap-2">
|
||||
<div class="text-4xl font-bold font-mono text-primary">{horaFormatada}</div>
|
||||
<div class="text-sm text-base-content/70 capitalize">{dataFormatada}</div>
|
||||
<div class="flex items-center gap-2 text-xs">
|
||||
{#if sincronizado}
|
||||
<CheckCircle2 class="h-4 w-4 text-success" />
|
||||
<span class="text-success">
|
||||
{#if usandoServidorExterno}
|
||||
Sincronizado com servidor NTP
|
||||
{:else}
|
||||
Sincronizado com servidor
|
||||
{/if}
|
||||
</span>
|
||||
{:else if erro}
|
||||
<AlertCircle class="h-4 w-4 text-warning" />
|
||||
<span class="text-warning">{erro}</span>
|
||||
{:else}
|
||||
<Clock class="h-4 w-4 text-base-content/50" />
|
||||
<span class="text-base-content/50">Sincronizando...</span>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user