Notificación intentos fallidos

This commit is contained in:
2025-05-27 16:02:13 -06:00
parent 303bf955c3
commit aa793813e8
2 changed files with 655 additions and 54 deletions

View File

@@ -145,9 +145,7 @@ function validar()
header('Location: /IMPORTADORES/login');
}
/**
* Función auxiliar para redirección por rol
*/
/** Función auxiliar para redirección por rol **/
function redirectByRole($tipoUsuario)
{
switch ($tipoUsuario) {
@@ -498,7 +496,6 @@ function verificarCodigoVista()
function verificarCodigo()
{
// ❌ REMOVER session_start() - ya se maneja en session.php
header('Content-Type: application/json');
$conn = getConnection();
@@ -507,8 +504,24 @@ function verificarCodigo()
$codigo = isset($_POST['codigo']) ? trim($_POST['codigo']) : '';
$email = $_SESSION['email_recuperacion'] ?? '';
if (!$conn || empty($codigo) || empty($email)) {
echo json_encode(['success' => false, 'message' => '❌ Código o correo faltante.']);
if (!$conn) {
echo json_encode(['success' => false, 'message' => '❌ Error de conexión a la base de datos.']);
exit;
}
if (empty($codigo)) {
echo json_encode(['success' => false, 'message' => '❌ Código requerido.']);
exit;
}
if (empty($email)) {
echo json_encode(['success' => false, 'message' => '❌ Sesión expirada. Solicita un nuevo código.']);
exit;
}
// Validar formato del código
if (!preg_match('/^\d{6}$/', $codigo)) {
echo json_encode(['success' => false, 'message' => '❌ Formato de código inválido. Debe ser de 6 dígitos.']);
exit;
}
@@ -521,11 +534,14 @@ function verificarCodigo()
$stmt = sqlsrv_query($conn, $sql, [$emailEncrypted]);
if ($stmt === false) {
$errors = sqlsrv_errors();
error_log("Error SQL en verificarCodigo: " . print_r($errors, true));
echo json_encode(['success' => false, 'message' => '❌ Error al consultar el código.']);
exit;
}
$row = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC);
sqlsrv_free_stmt($stmt);
if (!$row) {
echo json_encode(['success' => false, 'message' => '❌ Código no encontrado o ya usado.']);
@@ -539,18 +555,43 @@ function verificarCodigo()
$_SESSION['intentos_codigo']++;
if ($_SESSION['intentos_codigo'] > 5) {
// Bloquea el código en la base y resetea intentos
sqlsrv_query($conn, "UPDATE recuperacion_password SET estatus = 1 WHERE id = ?", [$row['id']]);
unset($_SESSION['intentos_codigo']);
// Verificar si se han excedido los intentos
if ($_SESSION['intentos_codigo'] >= 5) {
// Bloquear el código en la base de datos
$sqlUpdate = "UPDATE recuperacion_password SET estatus = 1 WHERE id = ?";
$stmtUpdate = sqlsrv_query($conn, $sqlUpdate, [$row['id']]);
echo json_encode(['success' => false, 'message' => '🚫 Has excedido el número de intentos. Solicita un nuevo código.']);
if ($stmtUpdate) {
sqlsrv_free_stmt($stmtUpdate);
}
// 🚨 ENVIAR NOTIFICACIÓN DE SEGURIDAD
try {
enviarNotificacionIntentosExcedidos($email);
} catch (Exception $e) {
error_log("❌ Error al enviar notificación de intentos excedidos: " . $e->getMessage());
}
// Limpiar sesión
unset($_SESSION['intentos_codigo']);
unset($_SESSION['email_recuperacion']);
echo json_encode([
'success' => false,
'message' => '🚫 Has excedido el número de intentos permitidos. Por seguridad, se ha bloqueado el código. Solicita uno nuevo.',
'blocked' => true
]);
exit;
}
// Compara códigos - asegurando que no haya problemas con mayúsculas o espacios
if (strcasecmp($codigo, $row['codigo']) !== 0) {
echo json_encode(['success' => false, 'message' => "❌ Código incorrecto. Intento {$_SESSION['intentos_codigo']} de 5."]);
$intentosRestantes = 5 - $_SESSION['intentos_codigo'];
echo json_encode([
'success' => false,
'message' => "❌ Código incorrecto. Te quedan {$intentosRestantes} intentos.",
'intentos_restantes' => $intentosRestantes
]);
exit;
}
@@ -566,21 +607,218 @@ function verificarCodigo()
if ($expiracionTimestamp === false || $expiracionTimestamp < time()) {
// Código expirado: marcar como usado y enviar mensaje
sqlsrv_query($conn, "UPDATE recuperacion_password SET estatus = 1 WHERE id = ?", [$row['id']]);
echo json_encode(['success' => false, 'message' => '⏰ El código ha expirado.']);
$sqlExpired = "UPDATE recuperacion_password SET estatus = 1 WHERE id = ?";
$stmtExpired = sqlsrv_query($conn, $sqlExpired, [$row['id']]);
if ($stmtExpired) {
sqlsrv_free_stmt($stmtExpired);
}
echo json_encode(['success' => false, 'message' => '⏰ El código ha expirado. Solicita uno nuevo.']);
exit;
}
// Código correcto: actualizar estado y permitir cambio de contraseña
sqlsrv_query($conn, "UPDATE recuperacion_password SET estatus = 1 WHERE id = ?", [$row['id']]);
$sqlSuccess = "UPDATE recuperacion_password SET estatus = 1 WHERE id = ?";
$stmtSuccess = sqlsrv_query($conn, $sqlSuccess, [$row['id']]);
if ($stmtSuccess) {
sqlsrv_free_stmt($stmtSuccess);
}
$_SESSION['recuperacion_autorizada'] = true;
unset($_SESSION['intentos_codigo']);
echo json_encode(['success' => true, 'message' => '✅ Código verificado. Redirigiendo...']);
echo json_encode(['success' => true, 'message' => '✅ Código verificado correctamente. Redirigiendo...']);
exit;
}
/** Envía notificación cuando se exceden los intentos de verificación **/
function enviarNotificacionIntentosExcedidos($email)
{
$conn = getConnection();
if (!$conn) {
throw new Exception("No se pudo conectar a la base de datos");
}
// Buscar información del usuario por email
$emailEncrypted = encrypt($email);
$sqlNotif = "
SELECT
u.nombre,
u.email,
u.notificaciones,
u.notificaciones_extra,
COALESCE(p.intentos_fallidos, 0) as intentos_fallidos,
ce.correo as correo_extra
FROM usuarios_sistema u
LEFT JOIN preferencias_notificaciones_usuario p ON u.id_usuario = p.id_usuario
LEFT JOIN correo_extra ce ON u.id_usuario = ce.id_usuario
WHERE u.email = ?";
$stmtUsuario = sqlsrv_query($conn, $sqlNotif, [$emailEncrypted]);
if ($stmtUsuario === false) {
throw new Exception("Error al consultar información del usuario");
}
$usuario = sqlsrv_fetch_array($stmtUsuario, SQLSRV_FETCH_ASSOC);
sqlsrv_free_stmt($stmtUsuario);
if (!$usuario) {
error_log("⚠️ No se encontró usuario para email: $email");
return false;
}
// 🔐 Desencriptar datos sensibles
$usuario['email'] = decrypt($usuario['email']);
$usuario['nombre'] = decrypt($usuario['nombre']);
// Verificar si el usuario tiene habilitadas las notificaciones de seguridad
if ($usuario['notificaciones'] != 1 || $usuario['intentos_fallidos'] != 1) {
error_log(" Usuario {$usuario['email']} no tiene habilitadas las notificaciones de intentos fallidos");
return false;
}
// Preparar datos para la notificación
$datosNotificacion = [
'email' => $usuario['email'],
'nombre' => $usuario['nombre'],
'fecha_hora' => date('Y-m-d H:i:s'),
'ip' => $_SERVER['REMOTE_ADDR'] ?? 'Desconocida',
'user_agent' => $_SERVER['HTTP_USER_AGENT'] ?? 'Desconocido'
];
$resultadoEnvio = false;
// Enviar al correo principal
$resultadoPrincipal = enviarNotificacionSeguridadIntentos(
$usuario['email'],
$usuario['nombre'],
$datosNotificacion
);
if ($resultadoPrincipal) {
$resultadoEnvio = true;
error_log("✅ Notificación de seguridad enviada a: {$usuario['email']}");
}
// Enviar al correo adicional si está configurado
if ($usuario['notificaciones_extra'] == 1 && !empty($usuario['correo_extra'])) {
$resultadoExtra = enviarNotificacionSeguridadIntentos(
$usuario['correo_extra'],
$usuario['nombre'],
$datosNotificacion,
true // Indicar que es correo adicional
);
if ($resultadoExtra) {
$resultadoEnvio = true;
error_log("✅ Notificación de seguridad enviada a correo adicional: {$usuario['correo_extra']}");
}
}
// Registrar el evento de seguridad en logs del sistema
registrarEventoSeguridad($usuario['id_usuario'], 'INTENTOS_CODIGO_EXCEDIDOS', $datosNotificacion);
return $resultadoEnvio;
}
/** Función para enviar el email de notificación de seguridad **/
function enviarNotificacionSeguridadIntentos($emailDestino, $nombreUsuario, $datos, $esCorreoAdicional = false)
{
try {
$mail = new PHPMailer(true);
// Configuración de servidor SMTP
$mail->isSMTP();
$mail->Host = 'secure.emailsrvr.com';
$mail->SMTPAuth = true;
$mail->Username = 'noreply@aduanasoft.com.mx';
$mail->Password = $_ENV['SMTP_PASS'];
$mail->SMTPSecure = PHPMailer::ENCRYPTION_STARTTLS;
$mail->Port = 587;
// Configuración del correo
$mail->setFrom('noreply@aduanasoft.com.mx', 'SIIH | AduanaSoft');
$mail->addAddress($emailDestino);
$mail->CharSet = 'UTF-8';
$mail->isHTML(true);
$asunto = "🚨 Alerta de Seguridad - Intentos de Verificación Excedidos";
$mail->Subject = $asunto;
$tipoCorreo = $esCorreoAdicional ? "(Correo Adicional)" : "";
$mail->Body = "
<html>
<head>
<meta charset='UTF-8'>
<title>Alerta de Seguridad</title>
</head>
<body style='font-family: Arial, sans-serif; line-height: 1.6; color: #333;'>
<div style='max-width: 600px; margin: 0 auto; padding: 20px; border: 1px solid #ddd; border-radius: 10px;'>
<div style='text-align: center; margin-bottom: 30px;'>
<h1 style='color: #dc3545; margin: 0;'>🚨 Alerta de Seguridad</h1>
<p style='color: #666; margin: 5px 0;'>$tipoCorreo</p>
</div>
<div style='background-color: #f8f9fa; padding: 20px; border-radius: 8px; margin-bottom: 20px;'>
<h2 style='color: #dc3545; margin-top: 0;'>Intentos de Verificación Excedidos</h2>
<p>Hola <strong>" . htmlspecialchars($nombreUsuario) . "</strong>,</p>
<p>Se han detectado <strong>múltiples intentos fallidos</strong> para verificar el código de recuperación de contraseña en tu cuenta.</p>
</div>
<div style='background-color: #fff3cd; padding: 15px; border-radius: 8px; margin-bottom: 20px; border-left: 4px solid #ffc107;'>
<h3 style='color: #856404; margin-top: 0;'>📋 Detalles del Evento:</h3>
<ul style='margin: 10px 0; padding-left: 20px;'>
<li><strong>Fecha y Hora:</strong> " . htmlspecialchars($datos['fecha_hora']) . "</li>
<li><strong>Dirección IP:</strong> " . htmlspecialchars($datos['ip']) . "</li>
<li><strong>Navegador:</strong> " . htmlspecialchars(substr($datos['user_agent'], 0, 100)) . "...</li>
</ul>
</div>
<div style='background-color: #d1ecf1; padding: 15px; border-radius: 8px; margin-bottom: 20px; border-left: 4px solid #bee5eb;'>
<h3 style='color: #0c5460; margin-top: 0;'>🔒 Medidas de Seguridad Aplicadas:</h3>
<ul style='margin: 10px 0; padding-left: 20px;'>
<li>El código de verificación ha sido <strong>bloqueado automáticamente</strong></li>
<li>Será necesario solicitar un <strong>nuevo código de recuperación</strong></li>
<li>Este evento ha sido <strong>registrado en nuestros logs de seguridad</strong></li>
</ul>
</div>
<div style='background-color: #f8d7da; padding: 15px; border-radius: 8px; margin-bottom: 20px; border-left: 4px solid #f5c6cb;'>
<h3 style='color: #721c24; margin-top: 0;'>⚠️ ¿No fuiste tú?</h3>
<p>Si <strong>no intentaste recuperar tu contraseña</strong>, esto podría indicar que alguien está tratando de acceder a tu cuenta.</p>
<p><strong>Te recomendamos:</strong></p>
<ul style='margin: 10px 0; padding-left: 20px;'>
<li>Verificar la seguridad de tu cuenta</li>
<li>Cambiar tu contraseña inmediatamente</li>
<li>Revisar tus configuraciones de seguridad</li>
<li>Contactar a soporte si sospechas actividad no autorizada</li>
</ul>
</div>
<div style='text-align: center; margin-top: 30px; padding-top: 20px; border-top: 1px solid #eee;'>
<p style='color: #666; font-size: 12px; margin: 0;'>
Esta es una notificación automática de seguridad.<br>
Si tienes preguntas, contacta a nuestro equipo de soporte.
</p>
</div>
</div>
</body>
</html>";
// Enviar el correo
$mail->send();
return true;
} catch (Exception $e) {
error_log("❌ Error al enviar notificación de seguridad: " . $e->getMessage());
return false;
}
}
function cambiarPasswordVista()
{
if (!isset($_SESSION['recuperacion_autorizada'])) {

View File

@@ -54,6 +54,18 @@ $mensajeMantenimiento = $config['mensaje_mantenimiento'] ?? 'El sistema se encue
.form-control {
border-radius: 6px;
transition: all 0.3s ease;
}
.form-control.error {
border-color: #dc3545;
background-color: #fff5f5;
animation: shake 0.5s ease-in-out;
}
.form-control.success {
border-color: #28a745;
background-color: #f8fff9;
}
footer {
@@ -75,14 +87,111 @@ $mensajeMantenimiento = $config['mensaje_mantenimiento'] ?? 'El sistema se encue
background: rgba(0, 0, 0, 0.5);
z-index: 5;
}
#mensaje {
transition: opacity 0.3s ease;
transition: all 0.3s ease;
min-height: 24px;
}
#btnReenviarRespaldo {
background: none;
border: none;
color: <?= $color1 ?>;
text-decoration: underline;
cursor: pointer;
font-size: 14px;
padding: 0;
transition: color 0.3s ease;
}
#btnReenviarRespaldo:hover {
color: <?= $color2 ?>;
}
#btnReenviarRespaldo:disabled {
color: #ccc;
cursor: not-allowed;
text-decoration: none;
}
.btn:disabled {
opacity: 0.6;
cursor: not-allowed;
}
.loading {
position: relative;
pointer-events: none;
}
.loading::after {
content: '';
position: absolute;
top: 50%;
left: 50%;
width: 20px;
height: 20px;
border: 2px solid transparent;
border-top: 2px solid #fff;
border-radius: 50%;
animation: spin 1s linear infinite;
transform: translate(-50%, -50%);
}
@keyframes shake {
0%, 100% { transform: translateX(0); }
25% { transform: translateX(-5px); }
75% { transform: translateX(5px); }
}
@keyframes spin {
0% { transform: translate(-50%, -50%) rotate(0deg); }
100% { transform: translate(-50%, -50%) rotate(360deg); }
}
@keyframes fadeIn {
from { opacity: 0; transform: translateY(-10px); }
to { opacity: 1; transform: translateY(0); }
}
.fade-in {
animation: fadeIn 0.3s ease-out;
}
@media (max-height: 600px) {
footer {
position: static !important;
}
}
/* Estilos para input de código */
#codigo {
font-size: 18px;
letter-spacing: 3px;
text-align: center;
font-weight: bold;
}
.input-group {
position: relative;
}
.clear-input {
position: absolute;
right: 10px;
top: 50%;
transform: translateY(-50%);
background: none;
border: none;
color: #ccc;
cursor: pointer;
z-index: 5;
display: none;
}
.clear-input:hover {
color: #666;
}
</style>
</head>
<body>
@@ -128,6 +237,10 @@ $mensajeMantenimiento = $config['mensaje_mantenimiento'] ?? 'El sistema se encue
<!-- Formulario de validación de código -->
<div class="login-container">
<h4 class="mb-4 text-center text-dark">Verifica tu código</h4>
<p class="text-muted text-center mb-4">
<i class="fas fa-envelope me-2"></i>
Hemos enviado un código de 6 dígitos a tu correo electrónico
</p>
<?php
if (!isset($_SESSION['email_recuperacion'])) {
@@ -136,30 +249,68 @@ $mensajeMantenimiento = $config['mensaje_mantenimiento'] ?? 'El sistema se encue
exit;
}
?>
<!-- Formulario para verificar el código -->
<form id="formCodigo" action="/IMPORTADORES/login/verificarCodigo" method="POST">
<div class="mb-3">
<label class="form-label" for="codigo">Ingresa el código recibido por correo</label>
<input type="text" id="codigo" name="codigo" class="form-control" required pattern="\d{6}" maxlength="6" placeholder="Ej. 123456">
<label class="form-label" for="codigo">
<i class="fas fa-key me-2"></i>Código de verificación
</label>
<div class="input-group">
<input type="text"
id="codigo"
name="codigo"
class="form-control"
required
pattern="\d{6}"
maxlength="6"
placeholder="000000"
autocomplete="off"
inputmode="numeric">
<button type="button" class="clear-input" id="clearCode" title="Limpiar código">
<i class="fas fa-times"></i>
</button>
</div>
<div class="form-text">
<i class="fas fa-info-circle me-1"></i>
Ingresa el código de 6 dígitos que recibiste por correo
</div>
</div>
<button type="submit" class="btn btn-primary w-100">Verificar código</button>
<!-- Botón para enviar el código al corrreo de respaldo-->
<button type="submit" id="btnVerificar" class="btn btn-success w-100">
<i class="fas fa-check me-2"></i>Verificar código
</button>
<!-- Botón para enviar el código al correo de respaldo-->
<div class="text-center mt-3">
<button type="button" id="btnReenviarRespaldo">¿No tienes acceso al correo? Reenviar al correo de respaldo</button>
<button type="button" id="btnReenviarRespaldo" title="Enviar código al correo de respaldo">
<i class="fas fa-paper-plane me-1"></i>
¿No tienes acceso al correo? Reenviar al correo de respaldo
</button>
</div>
</form>
<!-- Mostrar mensajes -->
<div id="mensaje" class="mt-3 text-center fw-bold">
<?php if (isset($_SESSION['codigo_error'])): ?>
<div class="text-danger"><?= $_SESSION['codigo_error'] ?></div>
<div class="text-danger fade-in">
<i class="fas fa-exclamation-triangle me-2"></i>
<?= $_SESSION['codigo_error'] ?>
</div>
<?php unset($_SESSION['codigo_error']); ?>
<?php elseif (isset($_SESSION['codigo_exito'])): ?>
<div class="text-success"><?= $_SESSION['codigo_exito'] ?></div>
<div class="text-success fade-in">
<i class="fas fa-check-circle me-2"></i>
<?= $_SESSION['codigo_exito'] ?>
</div>
<?php unset($_SESSION['codigo_exito']); ?>
<?php endif; ?>
</div>
<!-- Contador de intentos -->
<div id="intentosInfo" class="mt-2 text-center text-muted" style="font-size: 12px;">
<!-- Se llenará dinámicamente -->
</div>
</div>
<!-- FOOTER -->
@@ -171,64 +322,276 @@ $mensajeMantenimiento = $config['mensaje_mantenimiento'] ?? 'El sistema se encue
document.addEventListener('DOMContentLoaded', function () {
const form = document.getElementById('formCodigo');
const mensaje = document.getElementById('mensaje');
const inputCodigo = form.querySelector('input[name="codigo"]');
const inputCodigo = document.getElementById('codigo');
const btnVerificar = document.getElementById('btnVerificar');
const btnReenviar = document.getElementById('btnReenviarRespaldo');
const clearBtn = document.getElementById('clearCode');
const intentosInfo = document.getElementById('intentosInfo');
inputCodigo.addEventListener('input', () => {
mensaje.innerHTML = '';
mensaje.classList.remove("text-danger", "text-success");
let intentosRealizados = 0;
const maxIntentos = 5;
// Auto-focus en el input al cargar
inputCodigo.focus();
// Mostrar/ocultar botón de limpiar
inputCodigo.addEventListener('input', function() {
clearBtn.style.display = this.value.length > 0 ? 'block' : 'none';
// Limpiar mensajes previos
limpiarMensajes();
// Remover clases de error/éxito
inputCodigo.classList.remove('error', 'success');
// Solo permitir números
this.value = this.value.replace(/[^0-9]/g, '');
// Actualizar contador visual
actualizarContadorVisual();
});
// Limpiar input
clearBtn.addEventListener('click', function() {
inputCodigo.value = '';
inputCodigo.focus();
clearBtn.style.display = 'none';
limpiarMensajes();
inputCodigo.classList.remove('error', 'success');
});
// Permitir solo números en tiempo real
inputCodigo.addEventListener('keypress', function(e) {
if (!/[0-9]/.test(e.key) && !['Backspace', 'Delete', 'Tab', 'Enter'].includes(e.key)) {
e.preventDefault();
}
});
// Auto-envío cuando se completen 6 dígitos (opcional)
inputCodigo.addEventListener('input', function() {
if (this.value.length === 6) {
// Opcional: enviar automáticamente después de un breve delay
// setTimeout(() => form.dispatchEvent(new Event('submit')), 500);
}
});
// Manejo del formulario
form.addEventListener('submit', function(e) {
e.preventDefault();
const codigo = inputCodigo.value.trim();
if (!codigo || codigo.length !== 6) {
mensaje.textContent = "❌ Ingresa un código de 6 dígitos.";
mensaje.classList.add("text-danger");
return;
if (!validarCodigo(codigo)) return;
enviarCodigo(codigo);
});
// Validación del código
function validarCodigo(codigo) {
if (!codigo) {
mostrarError("❌ Por favor, ingresa el código.");
return false;
}
if (codigo.length !== 6) {
mostrarError("❌ El código debe tener exactamente 6 dígitos.");
inputCodigo.classList.add('error');
return false;
}
if (!/^\d{6}$/.test(codigo)) {
mostrarError("❌ El código solo debe contener números.");
inputCodigo.classList.add('error');
return false;
}
return true;
}
// Enviar código para verificación
function enviarCodigo(codigo) {
// Mostrar estado de carga
btnVerificar.disabled = true;
btnVerificar.classList.add('loading');
btnVerificar.innerHTML = '<span style="opacity: 0;">Verificando...</span>';
const formData = new FormData();
formData.append('codigo', codigo);
fetch('/IMPORTADORES/login/verificarCodigo', {
method: 'POST',
body: formData,
credentials: 'same-origin' // ✅ Importante para mantener sesión
credentials: 'same-origin'
})
.then(response => response.json())
.then(data => {
if (data.success) {
mensaje.textContent = data.message;
mensaje.classList.remove("text-danger");
mensaje.classList.add("text-success");
// Código correcto
mostrarExito(data.message);
inputCodigo.classList.add('success');
inputCodigo.disabled = true;
// Redirigir al formulario de cambio de contraseña
setTimeout(() => {
window.location.href = '/IMPORTADORES/login/cambiarPasswordVista';
}, 1500);
} else {
mensaje.textContent = "❌ " + data.message;
mensaje.classList.remove("text-success");
mensaje.classList.add("text-danger");
// Código incorrecto
manejarCodigoIncorrecto(data);
}
})
.catch(() => {
mensaje.textContent = "❌ Error al verificar el código.";
mensaje.classList.remove("text-success");
mensaje.classList.add("text-danger");
});
});
.catch(error => {
console.error('Error:', error);
mostrarError("❌ Error de conexión. Intenta nuevamente.");
inputCodigo.classList.add('error');
limpiarYEnfocarInput();
})
.finally(() => {
// Restaurar botón
btnVerificar.disabled = false;
btnVerificar.classList.remove('loading');
btnVerificar.innerHTML = '<i class="fas fa-check me-2"></i>Verificar código';
});
}
// Manejar código incorrecto
function manejarCodigoIncorrecto(data) {
intentosRealizados++;
mostrarError(data.message);
inputCodigo.classList.add('error');
// Limpiar input y enfocar para nuevo intento
limpiarYEnfocarInput();
// Actualizar contador de intentos
actualizarContadorIntentos();
// Si se bloqueó el código
if (data.blocked) {
bloquearFormulario();
}
}
// Limpiar input y enfocar
function limpiarYEnfocarInput() {
setTimeout(() => {
inputCodigo.value = '';
inputCodigo.classList.remove('error');
inputCodigo.focus();
clearBtn.style.display = 'none';
}, 1500); // Esperar 1.5 segundos antes de limpiar
}
// Actualizar contador visual
function actualizarContadorVisual() {
const longitud = inputCodigo.value.length;
if (longitud > 0) {
inputCodigo.setAttribute('placeholder', '0'.repeat(6 - longitud) + inputCodigo.value);
} else {
inputCodigo.setAttribute('placeholder', '000000');
}
}
// Actualizar contador de intentos
function actualizarContadorIntentos() {
if (intentosRealizados > 0) {
const restantes = maxIntentos - intentosRealizados;
intentosInfo.innerHTML = `
<i class="fas fa-exclamation-triangle text-warning me-1"></i>
Intentos restantes: <strong>${restantes}</strong> de ${maxIntentos}
`;
intentosInfo.classList.add('fade-in');
}
}
// Bloquear formulario cuando se exceden intentos
function bloquearFormulario() {
inputCodigo.disabled = true;
btnVerificar.disabled = true;
btnReenviar.disabled = true;
intentosInfo.innerHTML = `
<i class="fas fa-ban text-danger me-1"></i>
<span class="text-danger">Código bloqueado por seguridad</span>
`;
// Redirigir a solicitar nuevo código después de 5 segundos
setTimeout(() => {
window.location.href = '/IMPORTADORES/login/recuperar';
}, 5000);
}
// Funciones de utilidad para mensajes
function mostrarMensaje(texto, tipo) {
mensaje.innerHTML = `<div class="${tipo} fade-in">${texto}</div>`;
mensaje.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
}
function mostrarError(texto) {
mostrarMensaje(`<i class="fas fa-exclamation-triangle me-2"></i>${texto}`, 'text-danger');
}
function mostrarExito(texto) {
mostrarMensaje(`<i class="fas fa-check-circle me-2"></i>${texto}`, 'text-success');
}
function mostrarInfo(texto) {
mostrarMensaje(`<i class="fas fa-info-circle me-2"></i>${texto}`, 'text-info');
}
function limpiarMensajes() {
mensaje.innerHTML = '';
}
// Manejo del botón de reenvío
btnReenviar.addEventListener("click", function() {
const btnOriginalText = this.innerHTML;
// Deshabilitar botón temporalmente
this.disabled = true;
this.innerHTML = '<i class="fas fa-spinner fa-spin me-1"></i>Enviando...';
document.getElementById("btnReenviarRespaldo").addEventListener("click", () => {
fetch("/IMPORTADORES/login/reenviarCodigo", {
method: "POST",
headers: {'Content-Type': 'application/x-www-form-urlencoded'},
body: "accion=reenviarCodigo"
})
.then(response => response.json())
.then(data => alert(data.message));
.then(data => {
mostrarInfo(data.message);
// Habilitar botón después de 30 segundos
setTimeout(() => {
this.disabled = false;
this.innerHTML = btnOriginalText;
}, 30000);
})
.catch(error => {
console.error('Error:', error);
mostrarError("❌ Error al reenviar código.");
// Restaurar botón en caso de error
this.disabled = false;
this.innerHTML = btnOriginalText;
});
});
// Manejar tecla Enter en cualquier parte del formulario
document.addEventListener('keypress', function(e) {
if (e.key === 'Enter' && !btnVerificar.disabled) {
form.dispatchEvent(new Event('submit'));
}
});
// Prevenir paste de contenido no numérico
inputCodigo.addEventListener('paste', function(e) {
e.preventDefault();
const paste = (e.clipboardData || window.clipboardData).getData('text');
const numericPaste = paste.replace(/[^0-9]/g, '').substring(0, 6);
this.value = numericPaste;
this.dispatchEvent(new Event('input'));
});
});
</script>