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:
2025-11-18 11:44:12 -03:00
parent 52123a33b3
commit f0c6e4468f
22 changed files with 3604 additions and 128 deletions

View File

@@ -0,0 +1,103 @@
/**
* Formata hora no formato HH:mm
*/
export function formatarHoraPonto(hora: number, minuto: number): string {
return `${hora.toString().padStart(2, '0')}:${minuto.toString().padStart(2, '0')}`;
}
/**
* Formata data e hora completa
*/
export function formatarDataHoraCompleta(
data: string,
hora: number,
minuto: number,
segundo: number
): string {
const dataObj = new Date(`${data}T${formatarHoraPonto(hora, minuto)}:${segundo.toString().padStart(2, '0')}`);
return dataObj.toLocaleString('pt-BR', {
day: '2-digit',
month: '2-digit',
year: 'numeric',
hour: '2-digit',
minute: '2-digit',
second: '2-digit',
});
}
/**
* Calcula tempo trabalhado entre dois registros
*/
export function calcularTempoTrabalhado(
horaInicio: number,
minutoInicio: number,
horaFim: number,
minutoFim: number
): { horas: number; minutos: number } {
const minutosInicio = horaInicio * 60 + minutoInicio;
const minutosFim = horaFim * 60 + minutoFim;
const diferencaMinutos = minutosFim - minutosInicio;
if (diferencaMinutos < 0) {
return { horas: 0, minutos: 0 };
}
const horas = Math.floor(diferencaMinutos / 60);
const minutos = diferencaMinutos % 60;
return { horas, minutos };
}
/**
* Verifica se está dentro do prazo baseado na configuração
*/
export function verificarDentroDoPrazo(
hora: number,
minuto: number,
horarioConfigurado: string,
toleranciaMinutos: number
): boolean {
const [horaConfig, minutoConfig] = horarioConfigurado.split(':').map(Number);
const totalMinutosRegistro = hora * 60 + minuto;
const totalMinutosConfigurado = horaConfig * 60 + minutoConfig;
const diferenca = totalMinutosRegistro - totalMinutosConfigurado;
return diferenca <= toleranciaMinutos && diferenca >= -toleranciaMinutos;
}
/**
* Obtém label do tipo de registro
*/
export function getTipoRegistroLabel(tipo: 'entrada' | 'saida_almoco' | 'retorno_almoco' | 'saida'): string {
const labels: Record<string, string> = {
entrada: 'Entrada',
saida_almoco: 'Saída para Almoço',
retorno_almoco: 'Retorno do Almoço',
saida: 'Saída',
};
return labels[tipo] || tipo;
}
/**
* Obtém próximo tipo de registro esperado
*/
export function getProximoTipoRegistro(
ultimoTipo: 'entrada' | 'saida_almoco' | 'retorno_almoco' | 'saida' | null
): 'entrada' | 'saida_almoco' | 'retorno_almoco' | 'saida' {
if (!ultimoTipo) {
return 'entrada';
}
switch (ultimoTipo) {
case 'entrada':
return 'saida_almoco';
case 'saida_almoco':
return 'retorno_almoco';
case 'retorno_almoco':
return 'saida';
case 'saida':
return 'entrada'; // Novo dia
default:
return 'entrada';
}
}