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:
103
apps/web/src/lib/utils/ponto.ts
Normal file
103
apps/web/src/lib/utils/ponto.ts
Normal 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';
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user