From a8a746981201f2ca2cc2b0b710961626c96bf3d1 Mon Sep 17 00:00:00 2001 From: deyvisonwanderley Date: Mon, 22 Dec 2025 16:04:03 -0300 Subject: [PATCH] feat: enhance chat widget and point registration components with improved styling and synchronization handling, including border-radius adjustments and synchronization status messaging --- .../src/lib/components/chat/ChatWidget.svelte | 24 ++--- .../lib/components/ponto/RegistroPonto.svelte | 97 ++++++++++++------- .../ponto/RelogioSincronizado.svelte | 10 ++ 3 files changed, 79 insertions(+), 52 deletions(-) diff --git a/apps/web/src/lib/components/chat/ChatWidget.svelte b/apps/web/src/lib/components/chat/ChatWidget.svelte index 970b0ba..8a3edcd 100644 --- a/apps/web/src/lib/components/chat/ChatWidget.svelte +++ b/apps/web/src/lib/components/chat/ChatWidget.svelte @@ -1315,10 +1315,9 @@ tabindex="0" aria-label="Redimensionar janela pela borda superior" class="absolute top-0 right-0 left-0 z-50 h-2 cursor-ns-resize transition-colors" - style="--hover-bg: {obterPrimariaRgba(0.2)}" + style="--hover-bg: {obterPrimariaRgba(0.2)}; border-radius: 24px 24px 0 0;" onmousedown={(e) => handleResizeStart(e, 'n')} onkeydown={(e) => e.key === 'Enter' && handleResizeStart(e, 'n')} - style="border-radius: 24px 24px 0 0;" >
handleResizeStart(e, 's')} onkeydown={(e) => e.key === 'Enter' && handleResizeStart(e, 's')} - style="border-radius: 0 0 24px 24px;" >
handleResizeStart(e, 'w')} onkeydown={(e) => e.key === 'Enter' && handleResizeStart(e, 'w')} - style="border-radius: 24px 0 0 24px;" >
handleResizeStart(e, 'e')} onkeydown={(e) => e.key === 'Enter' && handleResizeStart(e, 'e')} - style="border-radius: 0 24px 24px 0;" >
handleResizeStart(e, 'nw')} onkeydown={(e) => e.key === 'Enter' && handleResizeStart(e, 'nw')} - style="border-radius: 24px 0 0 0;" >
handleResizeStart(e, 'ne')} onkeydown={(e) => e.key === 'Enter' && handleResizeStart(e, 'ne')} - style="border-radius: 0 24px 0 0;" >
handleResizeStart(e, 'sw')} onkeydown={(e) => e.key === 'Enter' && handleResizeStart(e, 'sw')} - style="border-radius: 0 0 0 24px;" >
handleResizeStart(e, 'se')} onkeydown={(e) => e.key === 'Enter' && handleResizeStart(e, 'se')} - style="border-radius: 0 0 24px 0;" >
diff --git a/apps/web/src/lib/components/ponto/RegistroPonto.svelte b/apps/web/src/lib/components/ponto/RegistroPonto.svelte index 3a76e19..f714c8e 100644 --- a/apps/web/src/lib/components/ponto/RegistroPonto.svelte +++ b/apps/web/src/lib/components/ponto/RegistroPonto.svelte @@ -35,6 +35,9 @@ const client = useConvexClient(); + // Estado de sincronização do relógio + let sincronizacaoConcluida = $state(false); + // Chave de refresh para forçar atualização das queries após registro let refreshKey = $state(0); @@ -60,17 +63,12 @@ funcionarioId && dataHoje ? { funcionarioId, data: dataHoje } : 'skip' ); - const registrosHojeQuery = $derived.by(() => - useQuery(api.pontos.listarRegistrosDia, registrosHojeParams) - ); + // Queries de ponto - usando useQuery com parâmetros derivados reativos + const registrosHojeQuery = useQuery(api.pontos.listarRegistrosDia, registrosHojeParams); - const historicoSaldoQuery = $derived.by(() => - useQuery(api.pontos.obterHistoricoESaldoDia, historicoSaldoParams) - ); + const historicoSaldoQuery = useQuery(api.pontos.obterHistoricoESaldoDia, historicoSaldoParams); - const dispensaQuery = $derived.by(() => - useQuery(api.pontos.verificarDispensaAtiva, dispensaParams) - ); + const dispensaQuery = useQuery(api.pontos.verificarDispensaAtiva, dispensaParams); // Query para obter status atual do funcionário (férias/licença) const funcionarioStatusQuery = useQuery( @@ -355,6 +353,9 @@ justificativa = ''; // Limpar justificativa após registro mostrandoModalConfirmacao = false; + // Aguardar um pouco para garantir que o backend processou o registro + await new Promise((resolve) => setTimeout(resolve, 800)); + // Forçar atualização das queries para mostrar o novo registro refreshKey++; @@ -362,11 +363,13 @@ console.log('[RegistroPonto] Registro bem-sucedido, refreshKey incrementado:', refreshKey); } - // Aguardar um pouco para garantir que o backend processou o registro - await new Promise((resolve) => setTimeout(resolve, 500)); - - // Forçar mais uma atualização após o delay para garantir sincronização - refreshKey++; + // Aguardar mais um pouco e forçar outra atualização para garantir sincronização completa + setTimeout(() => { + refreshKey++; + if (import.meta.env.DEV) { + console.log('[RegistroPonto] Segunda atualização, refreshKey incrementado:', refreshKey); + } + }, 1500); // Mostrar comprovante após 1 segundo setTimeout(() => { @@ -500,25 +503,27 @@ timestampBase = Date.now(); } - // 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 + // Aplicar GMT offset ao timestamp (o horário já vem corrigido do servidor) + // Apenas aplicar o offset configurado, sem ajustes adicionais de timezone let timestamp: number; if (gmtOffset !== 0) { // Aplicar offset configurado timestamp = timestampBase + gmtOffset * 60 * 60 * 1000; } else { - // Quando GMT = 0, manter timestamp UTC puro - // O toLocaleTimeString() converterá automaticamente para o timezone local do navegador + // Quando GMT = 0, usar timestamp base diretamente (já vem corrigido) timestamp = timestampBase; } + // Usar métodos UTC diretamente para evitar conversão automática do navegador + // O timestamp já está ajustado, então formatamos como UTC para manter o valor correto const dataObj = new Date(timestamp); - const data = dataObj.toLocaleDateString('pt-BR'); - const hora = dataObj.toLocaleTimeString('pt-BR', { - hour: '2-digit', - minute: '2-digit', - second: '2-digit' - }); + const dia = String(dataObj.getUTCDate()).padStart(2, '0'); + const mes = String(dataObj.getUTCMonth() + 1).padStart(2, '0'); + const ano = dataObj.getUTCFullYear(); + const data = `${dia}/${mes}/${ano}`; + const horaStr = String(dataObj.getUTCHours()).padStart(2, '0'); + const minutoStr = String(dataObj.getUTCMinutes()).padStart(2, '0'); + const segundoStr = String(dataObj.getUTCSeconds()).padStart(2, '0'); + const hora = `${horaStr}:${minutoStr}:${segundoStr}`; dataHoraAtual = { data, hora }; } catch (error) { console.warn('Erro ao obter tempo do servidor, usando tempo local:', error); @@ -866,7 +871,8 @@ !estaDispensado && !emFerias && !emLicenca && - temFuncionarioAssociado + temFuncionarioAssociado && + sincronizacaoConcluida // Só permitir registro após sincronização concluída ); }); @@ -1131,24 +1137,40 @@ id="relogio-sincronizado-ref" class="card from-primary/10 to-primary/5 border-primary/20 w-full max-w-sm rounded-2xl border-2 bg-linear-to-br p-5 shadow-lg" > - + + + {#if !sincronizacaoConcluida} +
+ +
+

Aguarde a sincronização

+
+ O sistema está sincronizando o horário com o servidor. O botão de registro será habilitado + após a conclusão da sincronização. +
+
+
+ {/if} +