feat: implement customizable point registration labels and GMT offset adjustment

- Added functionality to customize labels for point registration types (Entrada, Saída, etc.) in the configuration settings.
- Introduced a GMT offset adjustment feature to account for time zone differences during point registration.
- Updated the backend to ensure default values for custom labels and GMT offset are set correctly.
- Enhanced the UI to allow users to input and save personalized names for each type of point registration.
- Improved the point registration process to utilize the new configuration settings for displaying labels consistently across the application.
This commit is contained in:
2025-11-19 06:22:07 -03:00
parent f465bd973e
commit 7cdc726781
10 changed files with 311 additions and 93 deletions

View File

@@ -37,7 +37,6 @@
// Estados
let mostrandoWebcam = $state(false);
let registrando = $state(false);
let erro = $state<string | null>(null);
let sucesso = $state<string | null>(null);
let registroId = $state<Id<'registrosPonto'> | null>(null);
let mostrandoComprovante = $state(false);
@@ -63,6 +62,14 @@
});
const tipoLabel = $derived.by(() => {
if (config) {
return getTipoRegistroLabel(proximoTipo, {
nomeEntrada: config.nomeEntrada,
nomeSaidaAlmoco: config.nomeSaidaAlmoco,
nomeRetornoAlmoco: config.nomeRetornoAlmoco,
nomeSaida: config.nomeSaida,
});
}
return getTipoRegistroLabel(proximoTipo);
});
@@ -153,7 +160,6 @@
}
registrando = true;
erro = null;
sucesso = null;
coletandoInfo = true;
@@ -189,7 +195,15 @@
});
registroId = resultado.registroId;
sucesso = `Ponto registrado com sucesso! Tipo: ${getTipoRegistroLabel(resultado.tipo)}`;
const tipoLabelSucesso = config
? getTipoRegistroLabel(resultado.tipo, {
nomeEntrada: config.nomeEntrada,
nomeSaidaAlmoco: config.nomeSaidaAlmoco,
nomeRetornoAlmoco: config.nomeRetornoAlmoco,
nomeSaida: config.nomeSaida,
})
: getTipoRegistroLabel(resultado.tipo);
sucesso = `Ponto registrado com sucesso! Tipo: ${tipoLabelSucesso}`;
imagemCapturada = null;
justificativa = ''; // Limpar justificativa após registro
mostrandoModalConfirmacao = false;
@@ -208,7 +222,15 @@
mensagemErro.includes('já existe um registro')
) {
mensagemErroModal = 'Registro de ponto duplicado';
detalhesErroModal = `Não é possível registrar o ponto no mesmo minuto.\n\nVocê já possui um registro de ${getTipoRegistroLabel(proximoTipo)} para este minuto.\n\nPor favor, aguarde pelo menos 1 minuto antes de tentar registrar novamente.`;
const tipoLabelErro = config
? getTipoRegistroLabel(proximoTipo, {
nomeEntrada: config.nomeEntrada,
nomeSaidaAlmoco: config.nomeSaidaAlmoco,
nomeRetornoAlmoco: config.nomeRetornoAlmoco,
nomeSaida: config.nomeSaida,
})
: getTipoRegistroLabel(proximoTipo);
detalhesErroModal = `Não é possível registrar o ponto no mesmo minuto.\n\nVocê já possui um registro de ${tipoLabelErro} para este minuto.\n\nPor favor, aguarde pelo menos 1 minuto antes de tentar registrar novamente.`;
mostrarModalErro = true;
} else {
// Outros erros também mostram no modal
@@ -216,8 +238,6 @@
detalhesErroModal = mensagemErro;
mostrarModalErro = true;
}
erro = mensagemErro;
} finally {
registrando = false;
coletandoInfo = false;
@@ -309,7 +329,6 @@
mostrarModalErro = false;
mensagemErroModal = '';
detalhesErroModal = '';
erro = null;
}
async function imprimirComprovante(registroId: Id<'registrosPonto'>) {
@@ -322,6 +341,9 @@
return;
}
// Buscar configuração para usar nomes personalizados
const configComprovante = await client.query(api.configuracaoPonto.obterConfiguracao, {});
const doc = new jsPDF();
// Logo
@@ -385,7 +407,15 @@
yPosition += 8;
doc.setFontSize(10);
doc.text(`Tipo: ${getTipoRegistroLabel(registro.tipo)}`, 15, yPosition);
const tipoLabelComprovante = configComprovante
? getTipoRegistroLabel(registro.tipo, {
nomeEntrada: configComprovante.nomeEntrada,
nomeSaidaAlmoco: configComprovante.nomeSaidaAlmoco,
nomeRetornoAlmoco: configComprovante.nomeRetornoAlmoco,
nomeSaida: configComprovante.nomeSaida,
})
: getTipoRegistroLabel(registro.tipo);
doc.text(`Tipo: ${tipoLabelComprovante}`, 15, yPosition);
yPosition += 6;
const dataHora = formatarDataHoraCompleta(registro.data, registro.hora, registro.minuto, registro.segundo);
@@ -578,10 +608,10 @@
if (!config) return [];
const horarios = [
{ tipo: 'entrada', horario: config.horarioEntrada, label: 'Entrada' },
{ tipo: 'saida_almoco', horario: config.horarioSaidaAlmoco, label: 'Saída para Almoço' },
{ tipo: 'retorno_almoco', horario: config.horarioRetornoAlmoco, label: 'Retorno do Almoço' },
{ tipo: 'saida', horario: config.horarioSaida, label: 'Saída' }
{ tipo: 'entrada', horario: config.horarioEntrada, label: config.nomeEntrada || 'Entrada 1' },
{ tipo: 'saida_almoco', horario: config.horarioSaidaAlmoco, label: config.nomeSaidaAlmoco || 'Saída 1' },
{ tipo: 'retorno_almoco', horario: config.horarioRetornoAlmoco, label: config.nomeRetornoAlmoco || 'Entrada 2' },
{ tipo: 'saida', horario: config.horarioSaida, label: config.nomeSaida || 'Saída 2' }
];
return horarios.map((h) => {
@@ -761,7 +791,16 @@
<div class="flex items-start justify-between gap-4">
<div class="flex-1">
<div class="mb-1 flex items-center gap-2">
<span class="font-semibold">{getTipoRegistroLabel(registro.tipo)}</span>
<span class="font-semibold">
{config
? getTipoRegistroLabel(registro.tipo, {
nomeEntrada: config.nomeEntrada,
nomeSaidaAlmoco: config.nomeSaidaAlmoco,
nomeRetornoAlmoco: config.nomeRetornoAlmoco,
nomeSaida: config.nomeSaida,
})
: getTipoRegistroLabel(registro.tipo)}
</span>
{#if registro.dentroDoPrazo}
<CheckCircle2 class="h-4 w-4 text-success" />
{:else}