feat: improve point registration processing feedback with step-by-step messages and update modal positioning across components.

This commit is contained in:
2025-11-28 16:50:45 -03:00
parent 330d376930
commit 501751c22f
4 changed files with 70 additions and 34 deletions

View File

@@ -13,20 +13,20 @@
let modalPosition = $state<{ top: number; left: number } | null>(null); let modalPosition = $state<{ top: number; left: number } | null>(null);
// Função para calcular a posição baseada no relógio sincronizado // Função para calcular a posição baseada no card de registro de ponto
function calcularPosicaoModal() { function calcularPosicaoModal() {
// Procurar pelo elemento do relógio sincronizado // Procurar pelo elemento do card de registro de ponto
const relogioRef = document.getElementById('relogio-sincronizado-ref'); const cardRef = document.getElementById('card-registro-ponto-ref');
if (relogioRef) { if (cardRef) {
const rect = relogioRef.getBoundingClientRect(); const rect = cardRef.getBoundingClientRect();
const viewportWidth = window.innerWidth; const viewportWidth = window.innerWidth;
const viewportHeight = window.innerHeight; const viewportHeight = window.innerHeight;
// Posicionar o modal na mesma posição do relógio sincronizado // Posicionar o modal na mesma posição do card de registro
// Centralizado horizontalmente no card do relógio // Centralizado horizontalmente no card
const left = rect.left + (rect.width / 2); const left = rect.left + (rect.width / 2);
// Posicionar abaixo do card do relógio com um pequeno espaçamento // Posicionar abaixo do card com um pequeno espaçamento
const top = rect.bottom + 20; const top = rect.bottom + 20;
return { return {

View File

@@ -21,20 +21,20 @@
let gerando = $state(false); let gerando = $state(false);
let modalPosition = $state<{ top: number; left: number } | null>(null); let modalPosition = $state<{ top: number; left: number } | null>(null);
// Função para calcular a posição baseada no relógio sincronizado // Função para calcular a posição baseada no card de registro de ponto
function calcularPosicaoModal() { function calcularPosicaoModal() {
// Procurar pelo elemento do relógio sincronizado // Procurar pelo elemento do card de registro de ponto
const relogioRef = document.getElementById('relogio-sincronizado-ref'); const cardRef = document.getElementById('card-registro-ponto-ref');
if (relogioRef) { if (cardRef) {
const rect = relogioRef.getBoundingClientRect(); const rect = cardRef.getBoundingClientRect();
const viewportWidth = window.innerWidth; const viewportWidth = window.innerWidth;
const viewportHeight = window.innerHeight; const viewportHeight = window.innerHeight;
// Posicionar o modal na mesma posição do relógio sincronizado // Posicionar o modal na mesma posição do card de registro
// Centralizado horizontalmente no card do relógio // Centralizado horizontalmente no card
const left = rect.left + (rect.width / 2); const left = rect.left + (rect.width / 2);
// Posicionar abaixo do card do relógio com um pequeno espaçamento // Posicionar abaixo do card com um pequeno espaçamento
const top = rect.bottom + 20; const top = rect.bottom + 20;
return { return {

View File

@@ -65,6 +65,7 @@
let mostrandoModalConfirmacao = $state(false); let mostrandoModalConfirmacao = $state(false);
let dataHoraAtual = $state<{ data: string; hora: string } | null>(null); let dataHoraAtual = $state<{ data: string; hora: string } | null>(null);
let aguardandoProcessamento = $state(false); let aguardandoProcessamento = $state(false);
let etapaProcessamento = $state<'coletando' | 'sincronizando' | 'upload' | 'registrando' | null>(null);
const registrosHoje = $derived(registrosHojeQuery?.data || []); const registrosHoje = $derived(registrosHojeQuery?.data || []);
const config = $derived(configQuery?.data); const config = $derived(configQuery?.data);
@@ -204,15 +205,19 @@
registrando = true; registrando = true;
sucesso = null; sucesso = null;
coletandoInfo = true; coletandoInfo = true;
aguardandoProcessamento = true;
etapaProcessamento = 'coletando';
try { try {
// Coletar informações do dispositivo // Coletar informações do dispositivo
etapaProcessamento = 'coletando';
const informacoesDispositivo = await obterInformacoesDispositivo(); const informacoesDispositivo = await obterInformacoesDispositivo();
// Nota: A permissão de sensor não é impeditiva - apenas câmera e localização são obrigatórias // Nota: A permissão de sensor não é impeditiva - apenas câmera e localização são obrigatórias
coletandoInfo = false; coletandoInfo = false;
// Obter tempo sincronizado e aplicar GMT offset (igual ao relógio) // Obter tempo sincronizado e aplicar GMT offset (igual ao relógio)
etapaProcessamento = 'sincronizando';
const configRelogio = await client.query(api.configuracaoRelogio.obterConfiguracao, {}); const configRelogio = await client.query(api.configuracaoRelogio.obterConfiguracao, {});
// Usar gmtOffset da configuração, sem valor padrão, pois 0 é um valor válido // Usar gmtOffset da configuração, sem valor padrão, pois 0 é um valor válido
const gmtOffset = configRelogio.gmtOffset ?? 0; const gmtOffset = configRelogio.gmtOffset ?? 0;
@@ -262,6 +267,7 @@
let imagemId: Id<'_storage'> | undefined = undefined; let imagemId: Id<'_storage'> | undefined = undefined;
if (imagemCapturada) { if (imagemCapturada) {
try { try {
etapaProcessamento = 'upload';
imagemId = await uploadImagem(imagemCapturada); imagemId = await uploadImagem(imagemCapturada);
} catch (error) { } catch (error) {
console.error('Erro ao fazer upload da imagem:', error); console.error('Erro ao fazer upload da imagem:', error);
@@ -272,6 +278,7 @@
} }
// Registrar ponto // Registrar ponto
etapaProcessamento = 'registrando';
const resultado = await client.mutation(api.pontos.registrarPonto, { const resultado = await client.mutation(api.pontos.registrarPonto, {
imagemId, imagemId,
informacoesDispositivo, informacoesDispositivo,
@@ -314,6 +321,7 @@
} catch (error) { } catch (error) {
console.error('Erro ao registrar ponto:', error); console.error('Erro ao registrar ponto:', error);
aguardandoProcessamento = false; aguardandoProcessamento = false;
etapaProcessamento = null;
let mensagemErro = 'Erro desconhecido ao registrar ponto'; let mensagemErro = 'Erro desconhecido ao registrar ponto';
let detalhesErro = 'Tente novamente em alguns instantes.'; let detalhesErro = 'Tente novamente em alguns instantes.';
@@ -392,6 +400,7 @@
registrando = false; registrando = false;
coletandoInfo = false; coletandoInfo = false;
aguardandoProcessamento = false; aguardandoProcessamento = false;
etapaProcessamento = null;
} }
} }
@@ -518,7 +527,11 @@
function confirmarRegistro() { function confirmarRegistro() {
mostrandoModalConfirmacao = false; mostrandoModalConfirmacao = false;
aguardandoProcessamento = true; aguardandoProcessamento = true;
registrarPonto(); etapaProcessamento = 'coletando';
// Usar setTimeout para garantir que o modal de processamento apareça antes de iniciar o registro
setTimeout(() => {
registrarPonto();
}, 100);
} }
function cancelarRegistro() { function cancelarRegistro() {
@@ -855,20 +868,20 @@
// Posicionamento dos modais // Posicionamento dos modais
let modalPosition = $state<{ top: number; left: number } | null>(null); let modalPosition = $state<{ top: number; left: number } | null>(null);
// Função para calcular a posição baseada no relógio sincronizado // Função para calcular a posição baseada no card de registro de ponto
function calcularPosicaoModal() { function calcularPosicaoModal() {
// Procurar pelo elemento do relógio sincronizado // Procurar pelo elemento do card de registro de ponto
const relogioRef = document.getElementById('relogio-sincronizado-ref'); const cardRef = document.getElementById('card-registro-ponto-ref');
if (relogioRef) { if (cardRef) {
const rect = relogioRef.getBoundingClientRect(); const rect = cardRef.getBoundingClientRect();
const viewportWidth = window.innerWidth; const viewportWidth = window.innerWidth;
const viewportHeight = window.innerHeight; const viewportHeight = window.innerHeight;
// Posicionar o modal na mesma posição do relógio sincronizado // Posicionar o modal na mesma posição do card de registro
// Centralizado horizontalmente no card do relógio // Centralizado horizontalmente no card
const left = rect.left + (rect.width / 2); const left = rect.left + (rect.width / 2);
// Posicionar abaixo do card do relógio com um pequeno espaçamento // Posicionar abaixo do card com um pequeno espaçamento
const top = rect.bottom + 20; const top = rect.bottom + 20;
return { return {
@@ -1009,7 +1022,7 @@
<!-- Card de Registro de Ponto Modernizado --> <!-- Card de Registro de Ponto Modernizado -->
<div class="card bg-gradient-to-br from-base-100 via-base-100 to-primary/5 border border-base-300 shadow-2xl max-w-2xl mx-auto"> <div class="card bg-gradient-to-br from-base-100 via-base-100 to-primary/5 border border-base-300 shadow-2xl max-w-2xl mx-auto">
<div class="card-body p-6"> <div id="card-registro-ponto-ref" class="card-body p-6">
<!-- Cabeçalho --> <!-- Cabeçalho -->
<div class="flex items-center justify-center gap-3 mb-6"> <div class="flex items-center justify-center gap-3 mb-6">
<div class="p-2.5 bg-primary/10 rounded-xl"> <div class="p-2.5 bg-primary/10 rounded-xl">
@@ -1498,8 +1511,32 @@
> >
<div class="flex flex-col items-center gap-4 text-center"> <div class="flex flex-col items-center gap-4 text-center">
<span class="loading loading-spinner loading-lg text-primary"></span> <span class="loading loading-spinner loading-lg text-primary"></span>
<h3 id="modal-aguardando-title" class="text-xl font-bold text-base-content">Processando Registro</h3> <h3 id="modal-aguardando-title" class="text-xl font-bold text-base-content">
<p class="text-base-content/70">Por favor, aguarde enquanto processamos seu registro de ponto...</p> {#if etapaProcessamento === 'coletando'}
Coletando Informações
{:else if etapaProcessamento === 'sincronizando'}
Sincronizando Horário
{:else if etapaProcessamento === 'upload'}
Enviando Foto
{:else if etapaProcessamento === 'registrando'}
Registrando Ponto
{:else}
Processando Registro
{/if}
</h3>
<p class="text-base-content/70">
{#if etapaProcessamento === 'coletando'}
Coletando informações do dispositivo e localização...
{:else if etapaProcessamento === 'sincronizando'}
Sincronizando o horário com o servidor...
{:else if etapaProcessamento === 'upload'}
Enviando a foto capturada para o servidor...
{:else if etapaProcessamento === 'registrando'}
Finalizando o registro de ponto no sistema...
{:else}
Por favor, aguarde enquanto processamos seu registro de ponto...
{/if}
</p>
</div> </div>
</div> </div>
</div> </div>
@@ -1540,7 +1577,7 @@
<button <button
class="btn btn-sm btn-circle btn-ghost hover:bg-base-300" class="btn btn-sm btn-circle btn-ghost hover:bg-base-300"
onclick={cancelarRegistro} onclick={cancelarRegistro}
disabled={registrando} disabled={registrando || aguardandoProcessamento}
> >
<XCircle class="h-5 w-5" /> <XCircle class="h-5 w-5" />
</button> </button>
@@ -1658,7 +1695,7 @@
<button <button
class="btn btn-outline" class="btn btn-outline"
onclick={cancelarRegistro} onclick={cancelarRegistro}
disabled={registrando} disabled={registrando || aguardandoProcessamento}
> >
<XCircle class="h-5 w-5" /> <XCircle class="h-5 w-5" />
Cancelar Cancelar
@@ -1666,11 +1703,11 @@
<button <button
class="btn btn-primary gap-2" class="btn btn-primary gap-2"
onclick={confirmarRegistro} onclick={confirmarRegistro}
disabled={registrando} disabled={registrando || aguardandoProcessamento}
> >
{#if registrando} {#if registrando || aguardandoProcessamento}
<span class="loading loading-spinner loading-sm"></span> <span class="loading loading-spinner loading-sm"></span>
Registrando... Processando...
{:else} {:else}
<CheckCircle2 class="h-5 w-5" /> <CheckCircle2 class="h-5 w-5" />
Confirmar Registro Confirmar Registro

View File

@@ -1,6 +1,5 @@
{ {
"lockfileVersion": 1, "lockfileVersion": 1,
"configVersion": 0,
"workspaces": { "workspaces": {
"": { "": {
"name": "sgse-app", "name": "sgse-app",