feat: enhance call and point registration features with sensor data integration

- Updated the CallWindow component to include connection quality states and reconnection attempts, improving user experience during calls.
- Enhanced the ChatWindow to allow starting audio and video calls in a new window, providing users with more flexibility.
- Integrated accelerometer and gyroscope data collection in the RegistroPonto component, enabling validation of point registration authenticity.
- Improved error handling and user feedback for sensor permissions and data validation, ensuring a smoother registration process.
- Updated backend logic to validate sensor data and adjust confidence scores for point registration, enhancing security against spoofing.
This commit is contained in:
2025-11-22 20:49:52 -03:00
parent fc4b5c5ba5
commit f818756efc
15 changed files with 2100 additions and 275 deletions

View File

@@ -108,9 +108,14 @@
}
// Verificar permissões de localização e webcam
async function verificarPermissoes(): Promise<{ localizacao: boolean; webcam: boolean }> {
async function verificarPermissoes(): Promise<{
localizacao: boolean;
webcam: boolean;
permissoesNecessarias: string[];
}> {
let localizacaoAutorizada = false;
let webcamAutorizada = false;
const permissoesNecessarias: string[] = [];
// Verificar permissão de geolocalização
if (navigator.geolocation) {
@@ -126,8 +131,11 @@
localizacaoAutorizada = true;
resolve();
},
() => {
(error) => {
clearTimeout(timeoutId);
if (error.code === error.PERMISSION_DENIED) {
permissoesNecessarias.push('localização');
}
reject(new Error('Permissão de localização negada'));
},
{ timeout: 5000, maximumAge: 0, enableHighAccuracy: false }
@@ -147,10 +155,11 @@
stream.getTracks().forEach(track => track.stop());
} catch (error) {
console.warn('Permissão de webcam não concedida:', error);
permissoesNecessarias.push('câmera');
}
}
return { localizacao: localizacaoAutorizada, webcam: webcamAutorizada };
return { localizacao: localizacaoAutorizada, webcam: webcamAutorizada, permissoesNecessarias };
}
async function registrarPonto() {
@@ -176,7 +185,8 @@
const permissoes = await verificarPermissoes();
if (!permissoes.localizacao || !permissoes.webcam) {
mensagemErroModal = 'Permissões necessárias';
detalhesErroModal = 'Para registrar o ponto, é necessário autorizar o compartilhamento de localização e a captura de foto.';
const permissoesLista = permissoes.permissoesNecessarias.join(', ');
detalhesErroModal = `Para registrar o ponto, é necessário autorizar o compartilhamento de localização e a captura de foto.\n\nPermissões negadas: ${permissoesLista || 'localização e/ou câmera'}`;
mostrarModalErro = true;
return;
}
@@ -188,6 +198,8 @@
try {
// Coletar informações do dispositivo
const informacoesDispositivo = await obterInformacoesDispositivo();
// Nota: A permissão de sensor não é impeditiva - apenas câmera e localização são obrigatórias
coletandoInfo = false;
// Obter tempo sincronizado e aplicar GMT offset (igual ao relógio)
@@ -278,30 +290,80 @@
}, 1000);
} catch (error) {
console.error('Erro ao registrar ponto:', error);
const mensagemErro = error instanceof Error ? error.message : 'Erro ao registrar ponto';
let mensagemErro = 'Erro desconhecido ao registrar ponto';
let detalhesErro = 'Tente novamente em alguns instantes.';
// Verificar se é erro de registro duplicado
if (
mensagemErro.includes('Já existe um registro neste minuto') ||
mensagemErro.includes('já existe um registro')
) {
mensagemErroModal = 'Registro de ponto duplicado';
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
mensagemErroModal = 'Erro ao registrar ponto';
detalhesErroModal = mensagemErro;
mostrarModalErro = true;
if (error instanceof Error) {
const erroMessage = error.message || '';
// Erro de registro duplicado
if (
erroMessage.includes('Já existe um registro neste minuto') ||
erroMessage.includes('já existe um registro')
) {
mensagemErro = 'Registro de ponto duplicado';
const tipoLabelErro = config
? getTipoRegistroLabel(proximoTipo, {
nomeEntrada: config.nomeEntrada,
nomeSaidaAlmoco: config.nomeSaidaAlmoco,
nomeRetornoAlmoco: config.nomeRetornoAlmoco,
nomeSaida: config.nomeSaida,
})
: getTipoRegistroLabel(proximoTipo);
detalhesErro = `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.`;
}
// Erro de validação de argumentos
else if (
erroMessage.includes('ArgumentValidationError') ||
erroMessage.includes('Object contains extra field') ||
erroMessage.includes('validation')
) {
mensagemErro = 'Erro na validação dos dados';
detalhesErro = 'Ocorreu um erro ao validar as informações do dispositivo.\n\nPor favor, tente novamente ou recarregue a página.';
}
// Erro de autenticação
else if (
erroMessage.includes('não autenticado') ||
erroMessage.includes('autenticado') ||
erroMessage.includes('auth')
) {
mensagemErro = 'Erro de autenticação';
detalhesErro = 'Sua sessão pode ter expirado. Por favor, faça login novamente.';
}
// Erro de permissão/validação de localização
else if (
erroMessage.includes('localização') ||
erroMessage.includes('Localização') ||
erroMessage.includes('location')
) {
mensagemErro = 'Erro na validação de localização';
detalhesErro = 'Não foi possível validar sua localização.\n\nPor favor, verifique se você autorizou o compartilhamento de localização e tente novamente.';
}
// Erro genérico do servidor
else if (erroMessage.includes('Server Error') || erroMessage.includes('Server')) {
mensagemErro = 'Erro no servidor';
detalhesErro = 'Ocorreu um erro no servidor ao processar seu registro.\n\nPor favor, tente novamente em alguns instantes.';
}
// Outros erros - mostrar mensagem simplificada
else {
mensagemErro = 'Erro ao registrar ponto';
// Se a mensagem de erro for muito técnica, mostrar mensagem genérica
if (
erroMessage.includes('Error:') ||
erroMessage.includes('TypeError') ||
erroMessage.includes('ReferenceError') ||
erroMessage.length > 200
) {
detalhesErro = 'Ocorreu um erro ao processar o registro.\n\nPor favor, tente novamente ou recarregue a página.';
} else {
detalhesErro = erroMessage;
}
}
}
mensagemErroModal = mensagemErro;
detalhesErroModal = detalhesErro;
mostrarModalErro = true;
} finally {
registrando = false;
coletandoInfo = false;