Correcciones - registro

This commit is contained in:
2025-07-07 15:53:01 -06:00
parent 9925a0bf55
commit 1a5ed8b65d
7 changed files with 597 additions and 119 deletions

View File

@@ -85,9 +85,9 @@ function aprobar_usuario()
} }
// Preparar datos con validación // Preparar datos con validación
$nombre = !empty($solicitud['company_name']) ? decrypt($solicitud['company_name']) : null; $nombre = !empty($solicitud['company_name']) ? decrypt($solicitud['company_name']) : null;
$email = $solicitud['email'] ?? null; $email = $solicitud['email'] ?? null;
$rfc = !empty($solicitud['rfc']) ? decrypt($solicitud['rfc']) : null; $rfc = !empty($solicitud['rfc']) ? decrypt($solicitud['rfc']) : null;
$telefono = $solicitud['phone'] ?? null; $telefono = $solicitud['phone'] ?? null;
// Validar datos requeridos // Validar datos requeridos
@@ -106,9 +106,9 @@ function aprobar_usuario()
$email_encrypt = encrypt($email); $email_encrypt = encrypt($email);
// Insertar en usuarios_sistema y obtener ID en la misma consulta // Insertar en usuarios_sistema y obtener ID en la misma consulta
$sqlInsert = "INSERT INTO usuarios_sistema (nombre, email, password_hash, tipo_usuario, activo, creado_en, dos_factores, creado_por, preferencias_catalogos) $sqlInsert = "INSERT INTO usuarios_sistema (nombre, email, password_hash, tipo_usuario, activo, creado_en, dos_factores, creado_por, preferencias_catalogos)
OUTPUT INSERTED.id_usuario OUTPUT INSERTED.id_usuario
VALUES (?, ?, ?, ?, 1, GETDATE(), 0, ?, 1)"; VALUES (?, ?, ?, ?, 1, GETDATE(), 0, ?, 1)";
$stmtInsert = sqlsrv_query($conn, $sqlInsert, [ $stmtInsert = sqlsrv_query($conn, $sqlInsert, [
$nombre_encrypt, $email_encrypt, $password_hash, $tipo, $_SESSION['usuario_id'] $nombre_encrypt, $email_encrypt, $password_hash, $tipo, $_SESSION['usuario_id']
@@ -120,7 +120,7 @@ function aprobar_usuario()
// Obtener el ID directamente del OUTPUT // Obtener el ID directamente del OUTPUT
$idUsuarioRow = sqlsrv_fetch_array($stmtInsert, SQLSRV_FETCH_ASSOC); $idUsuarioRow = sqlsrv_fetch_array($stmtInsert, SQLSRV_FETCH_ASSOC);
$id_usuario = $idUsuarioRow['id_usuario'] ?? null; $id_usuario = $idUsuarioRow['id_usuario'] ?? null;
if (!$id_usuario) { if (!$id_usuario) {
throw new Exception("No se pudo obtener el ID del usuario creado. ID obtenido: " . var_export($idUsuarioRow, true)); throw new Exception("No se pudo obtener el ID del usuario creado. ID obtenido: " . var_export($idUsuarioRow, true));

View File

@@ -265,7 +265,7 @@ function enviarSoliAgencia()
// Sanitizar y obtener datos // Sanitizar y obtener datos
$agencia = trim($_POST['nombre_agencia'] ?? ''); $agencia = trim($_POST['nombre_agencia'] ?? '');
$rfc = trim($_POST['rfc'] ?? ''); $rfc = trim($_POST['rfc_agencia'] ?? '');
$correo = trim($_POST['correo'] ?? ''); $correo = trim($_POST['correo'] ?? '');
$telefono = trim($_POST['telefono'] ?? ''); $telefono = trim($_POST['telefono'] ?? '');
$direccion = trim($_POST['direccion'] ?? ''); $direccion = trim($_POST['direccion'] ?? '');

View File

@@ -134,54 +134,58 @@
<h5 class="mb-3"> Nueva Agencia Aduanal</h5> <h5 class="mb-3"> Nueva Agencia Aduanal</h5>
<div class="row g-3"> <div class="row g-3">
<!-- Usando floating labels animados -->
<div class="col-md-6 form-group-animated"> <div class="col-md-6 form-group-animated">
<div class="form-floating-custom"></div> <div class="form-floating-custom">
<input type="text" class="form-control input-pulse input-gradient" name="nombre_agencia" id="nombre_agencia" placeholder=" " required>
<label for="nombre_agencia" class="form-label">Nombre de la agencia *</label> <label for="nombre_agencia" class="form-label">Nombre de la agencia *</label>
<input type="text" class="form-control input-pulse input-gradient" name="nombre_agencia" id="nombre_agencia" required>
</div> </div>
</div> </div>
<div class="col-md-6 form-group-animated"> <div class="col-md-6 form-group-animated">
<div class="form-floating-custom"> <div class="form-floating-custom">
<input type="text" class="form-control input-pulse input-ripple" name="rfc_agencia" maxlength="13" id="rfc_agencia" placeholder=" " required>
<label for="rfc_agencia" class="form-label">RFC *</label> <label for="rfc_agencia" class="form-label">RFC *</label>
<input type="text" class="form-control input-pulse input-ripple" name="rfc_agencia" maxlength="13" id="rfc_agencia" required>
</div> </div>
</div> </div>
<div class="col-md-6 form-group-animated"> <div class="col-md-6 form-group-animated">
<div class="form-floating-custom"> <div class="form-floating-custom">
<input type="email" class="form-control input-pulse input-typing" name="correo" id="correo" placeholder=" " required>
<label for="correo" class="form-label">Correo electrónico *</label> <label for="correo" class="form-label">Correo electrónico *</label>
<input type="email" class="form-control input-pulse input-typing" name="correo" id="correo" required>
</div> </div>
</div> </div>
<div class="col-md-6 form-group-animated"> <div class="col-md-6 form-group-animated">
<div class="form-floating-custom"> <div class="form-floating-custom">
<input type="text" class="form-control input-pulse input-gradient" name="telefono" maxlength="11" id="telefono" placeholder=" " required>
<label for="telefono" class="form-label">Teléfono *</label> <label for="telefono" class="form-label">Teléfono *</label>
<input type="text" class="form-control input-pulse input-gradient" name="telefono" maxlength="11" id="telefono" required>
</div> </div>
</div> </div>
<div class="col-md-12 form-group-animated"> <div class="col-md-12 form-group-animated">
<div class="form-floating-custom"> <div class="form-floating-custom">
<input type="text" class="form-control input-pulse input-ripple" name="direccion" id="direccion" placeholder=" " required>
<label for="direccion" class="form-label">Dirección *</label> <label for="direccion" class="form-label">Dirección *</label>
<input type="text" class="form-control input-pulse input-ripple" name="direccion" id="direccion"
placeholder=" " required>
</div> </div>
</div> </div>
<div class="col-md-12 form-group-animated">
<div class="col-12 form-group-animated">
<hr> <hr>
<p><strong>Administrador de la Agencia</strong></p> <p><strong>Administrador de la Agencia</strong></p>
</div> </div>
<div class="col-md-6 form-group-animated"> <div class="col-md-6 form-group-animated">
<div class="form-floating-custom"> <div class="form-floating-custom">
<input type="text" class="form-control input-pulse input-gradient" name="nombre_admin" id="nombre_admin" placeholder=" " required>
<label for="nombre_admin" class="form-label">Nombre *</label> <label for="nombre_admin" class="form-label">Nombre *</label>
<input type="text" class="form-control input-pulse input-gradient" name="nombre_admin" id="nombre_admin" required>
</div> </div>
</div> </div>
<div class="col-md-6 form-group-animated"> <div class="col-md-6 form-group-animated">
<div class="form-floating-custom"> <div class="form-floating-custom">
<input type="email" class="form-control input-pulse input-typing" name="correo_admin" id="correo_admin" placeholder=" " required>
<label for="correo_admin" class="form-label">Correo Administrador *</label> <label for="correo_admin" class="form-label">Correo Administrador *</label>
<input type="email" class="form-control input-pulse input-typing" name="correo_admin" id="correo_admin" required>
</div> </div>
</div> </div>

View File

@@ -62,6 +62,111 @@
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2), transparent); transition: left 0.5s; } background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2), transparent); transition: left 0.5s; }
.btn-animated:hover::before { left: 100%; } .btn-animated:hover::before { left: 100%; }
table.dataTable thead th { background:#343a40; color:#fff; } table.dataTable thead th { background:#343a40; color:#fff; }
/* ========== ANIMACIONES PARA INPUTS ========== */
/* 1. Animación al hacer focus */
.form-control { transition: all 0.3s ease; border: 2px solid #dee2e6; position: relative; }
.form-control:focus { border-color: #0d6efd; box-shadow: 0 0 0 0.2rem rgba(13, 110, 253, 0.25), 0 0 20px rgba(13, 110, 253, 0.3); transform: translateY(-2px); }
/* 2. Animación al escribir */
.form-control:not(:placeholder-shown) { border-color: #198754; background-color: #f8fff9; }
/* 3. Efecto de shake en validación */
.form-control.shake { animation: shake 0.5s ease-in-out; }
@keyframes shake {
0%, 100% { transform: translateX(0); }
25% { transform: translateX(-5px); }
75% { transform: translateX(5px); }
}
/* 4. Floating labels con animación */
.form-floating-custom { position: relative; margin-bottom: 1.5rem; }
.form-floating-custom .form-control { padding: 1rem 0.75rem 0.5rem 0.75rem; height: auto; }
.form-floating-custom .form-label { position: absolute; top: 0.75rem; left: 0.75rem; transition: all 0.3s ease; pointer-events: none; color: #6c757d; z-index: 2; }
.form-floating-custom .form-control:focus ~ .form-label,
.form-floating-custom .form-control:not(:placeholder-shown) ~ .form-label { top: 0.25rem; font-size: 0.75rem; color: #0d6efd; font-weight: 600; }
/* 5. Efecto de pulso en hover */
.input-pulse:hover { animation: inputPulse 0.6s ease-in-out; }
@keyframes inputPulse {
0% { transform: scale(1); }
50% { transform: scale(1.02); }
100% { transform: scale(1); }
}
/* 6. Gradiente animado en el borde */
.input-gradient { position: relative; overflow: hidden; }
.input-gradient::before { content: ''; position: absolute; top: 0; left: -100%; width: 100%; height: 100%;
background: linear-gradient(90deg, transparent, rgba(13, 110, 253, 0.3), transparent); transition: left 0.5s; pointer-events: none; z-index: 1; }
.input-gradient:focus::before { left: 100%; }
/* 7. Efecto de brillo en validación exitosa */
.form-control.valid { border-color: #198754; background: linear-gradient(45deg, #fff, #f8fff9); animation: successGlow 1s ease-in-out; }
@keyframes successGlow {
0% { box-shadow: 0 0 5px rgba(25, 135, 84, 0.5); }
50% { box-shadow: 0 0 20px rgba(25, 135, 84, 0.8); }
100% { box-shadow: 0 0 5px rgba(25, 135, 84, 0.5); }
}
/* 8. Typing effect simulation */
.input-typing { position: relative; }
.input-typing::after { content: '|'; position: absolute; right: 10px; top: 50%; transform: translateY(-50%); opacity: 0; animation: blink 1s infinite; color: #0d6efd; }
.input-typing:focus::after { opacity: 1; }
@keyframes blink {
0%, 50% { opacity: 1; }
51%, 100% { opacity: 0; }
}
/* 9. Efecto de ondas */
.input-ripple { position: relative; overflow: hidden; }
.input-ripple::before { content: ''; position: absolute; top: 50%; left: 50%; width: 0; height: 0; background: rgba(13, 110, 253, 0.2);
border-radius: 50%; transform: translate(-50%, -50%); transition: width 0.3s, height 0.3s; pointer-events: none; }
.input-ripple:focus::before { width: 300px; height: 300px; }
/* 10. Slide-up animation para los campos */
.form-group-animated { opacity: 0; transform: translateY(20px); animation: slideUp 0.6s ease-out forwards; }
.form-group-animated:nth-child(1) { animation-delay: 0.1s; }
.form-group-animated:nth-child(2) { animation-delay: 0.2s; }
.form-group-animated:nth-child(3) { animation-delay: 0.3s; }
.form-group-animated:nth-child(4) { animation-delay: 0.4s; }
.form-group-animated:nth-child(5) { animation-delay: 0.5s; }
.form-group-animated:nth-child(6) { animation-delay: 0.6s; }
.form-group-animated:nth-child(7) { animation-delay: 0.7s; }
.form-group-animated:nth-child(8) { animation-delay: 0.8s; }
@keyframes slideUp { to { opacity: 1; transform: translateY(0); } }
/* ========== ANIMACIONES PARA SELECT ========== */
/* 1. Estilos base para select */
.form-select {transition: all 0.3s ease; border: 2px solid #dee2e6; position: relative; cursor: pointer;
background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23343a40' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m1 6 7 7 7-7'/%3e%3c/svg%3e");
background-repeat: no-repeat; background-position: right 0.75rem center; background-size: 16px 12px; }
/* 2. Animación al hacer focus */
.form-select:focus { border-color: #0d6efd; box-shadow: 0 0 0 0.2rem rgba(13, 110, 253, 0.25), 0 0 20px rgba(13, 110, 253, 0.3); transform: translateY(-2px);
background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%230d6efd' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m1 6 7 7 7-7'/%3e%3c/svg%3e"); }
/* 3. Animación cuando tiene valor seleccionado */
.form-select:not([value=""]):not(:invalid) { border-color: #198754; background-color: #f8fff9;
background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23198754' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m1 6 7 7 7-7'/%3e%3c/svg%3e"); padding: 1rem 2.5rem 0.5rem 0.75rem; height: auto; min-height: 3.5rem; }
.form-floating-custom .form-select:focus ~ .form-label,
.form-floating-custom .form-select:not([value=""]) ~ .form-label { top: 0.25rem; font-size: 0.75rem; color: #0d6efd; font-weight: 600; }
/* 5. Efecto de shake en validación para select */
.form-select.shake { animation: shake 0.5s ease-in-out; }
/* 6. Efecto de pulso en hover para select */
.select-pulse:hover { animation: inputPulse 0.6s ease-in-out; }
/* 7. Gradiente animado para select */
.select-gradient { position: relative; overflow: hidden; }
.select-gradient::before { content: ''; position: absolute; top: 0; left: -100%; width: 100%; height: 100%; background: linear-gradient(90deg, transparent, rgba(13, 110, 253, 0.3), transparent); transition: left 0.5s; pointer-events: none; z-index: 1; }
.select-gradient:focus::before { left: 100%; }
/* 8. Efecto de brillo en validación exitosa para select */
.form-select.valid { border-color: #198754; background-color: #f8fff9; animation: successGlow 1s ease-in-out; }
/* 9. Efecto de rotación de la flecha al abrir */
.select-arrow-rotate { transition: all 0.3s ease; }
.select-arrow-rotate:focus { background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%230d6efd' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m1 10 7-7 7 7'/%3e%3c/svg%3e"); }
/* 10. Efecto de ondas para select */
.select-ripple { position: relative; overflow: hidden; }
.select-ripple::after { content: ''; position: absolute; top: 50%; left: 50%; width: 0; height: 0;
background: rgba(13, 110, 253, 0.2); border-radius: 50%; transform: translate(-50%, -50%); transition: width 0.3s, height 0.3s; pointer-events: none; }
.select-ripple:focus::after { width: 300px; height: 300px; }
/* 11. Efecto de scale suave */
.select-scale { transition: transform 0.2s ease; }
.select-scale:hover { transform: scale(1.02); }
.select-scale:focus { transform: scale(1.02) translateY(-2px); }
/* 12. Personalización de opciones (limitado en algunos navegadores) */
.form-select option { padding: 0.5rem; transition: all 0.2s ease; }
.form-select option:hover { background-color: #f8f9fa; }
/* 13. Indicador de estado */
.select-status { position: relative; }
.select-status::after { content: ''; position: absolute; right: 2.5rem; top: 50%; transform: translateY(-50%);
width: 8px; height: 8px; border-radius: 50%; background-color: #dc3545; opacity: 0; transition: opacity 0.3s ease; } .select-status.valid::after { background-color: #198754; opacity: 1; }
.select-status:invalid::after { background-color: #dc3545; opacity: 1; }
</style> </style>
</head> </head>
<body> <body>
@@ -71,24 +176,36 @@
<h3 class="mb-4 animate__animated animate__fadeInDown title_glow">👥 Alta de Usuarios</h3> <h3 class="mb-4 animate__animated animate__fadeInDown title_glow">👥 Alta de Usuarios</h3>
<!-- FORMULARIO --> <!-- FORMULARIO -->
<div class="card p-4 mb-4 shadow-sm bg-white card-hover position-relative h-auto"> <div class="card p-4 mb-4 shadow-sm bg-white card-hover position-relative h-auto">
<form action="/IMPORTADORES/administrador/guardar_usuario" method="POST"> <form action="/IMPORTADORES/administrador/guardar_usuario" method="POST" id="formAltaUsuarios">
<h5 class="mb-3"> Nuevo Usuario</h5> <h5 class="mb-3"> Nuevo Usuario</h5>
<div class="row g-3"> <div class="row g-3">
<div class="col-md-4"> <div class="col-md-4 form-group-animated">
<input name="nombre" type="text" class="form-control" id="nombre" placeholder="Nombre completo" required> <div class="form-floating-custom">
<input name="nombre" type="text" class="form-control input-pulse input-gradient" id="nombre" placeholder=" " required>
<label for="nombre" class="form-label">Nombre *</label>
</div>
</div> </div>
<div class="col-md-4"> <div class="col-md-4 form-group-animated">
<input name="email" type="email" class="form-control" id="email" placeholder="Correo electrónico" required> <div class="form-floating-custom">
<input name="email" type="email" class="form-control input-pulse input-typing" id="email" placeholder=" " required>
<label for="email" class="form-label">Correo *</label>
</div>
</div> </div>
<div class="col-md-2"> <div class="col-md-2 form-group-animated">
<input name="password" type="password" class="form-control" id="password" placeholder="Contraseña" required> <div class="form-floating-custom">
<input name="password" type="password" class="form-control form-control input-pulse input-ripple" id="password" placeholder=" " required>
<label for="password" class="form-label">Contraseña *</label>
</div>
</div> </div>
<div class="col-md-2"> <div class="col-md-2 form-group-animated">
<select name="tipo_usuario" class="form-select" required> <div class="form-floating-custom">
<option value="">Tipo</option> <select name="tipo_usuario" class="form-select select-pulse select-gradient select-arrow-rotate select-status" required>
<option value="super_admin">Administrador General</option> <option value="">Tipo</option>
<option value="importador">Importador</option> <option value="super_admin">Administrador General</option>
</select> <option value="importador">Importador</option>
</select>
<label for="tipo_usuario" class="form_label"></label>
</div>
</div> </div>
</div> </div>
<div class="text-end mt-4"> <div class="text-end mt-4">
@@ -144,6 +261,55 @@
} }
}); });
}); });
// Script para manejar las animaciones de validación
document.addEventListener('DOMContentLoaded', function() {
const inputs = document.querySelectorAll('.form-control');
inputs.forEach(input => {
// Validación en tiempo real
input.addEventListener('input', function() {
if (this.checkValidity()) {
this.classList.remove('shake');
this.classList.add('valid');
} else {
this.classList.remove('valid');
}
});
// Efecto shake en campos inválidos
input.addEventListener('invalid', function() {
this.classList.add('shake');
setTimeout(() => {
this.classList.remove('shake');
}, 500);
});
});
// Validación del formulario
document.getElementById('formAltaUsuarios').addEventListener('submit', function(e) {
e.preventDefault();
let isValid = true;
inputs.forEach(input => {
if (!input.checkValidity()) {
input.classList.add('shake');
isValid = false;
}
});
if (isValid) {
// Aquí puedes agregar tu lógica de envío
Swal.fire({
icon: 'success',
title: '¡Éxito!',
text: 'Agencia registrada correctamente',
showConfirmButton: false,
timer: 1500
});
}
});
});
</script> </script>
<?php if (isset($_GET['success'])): ?> <?php if (isset($_GET['success'])): ?>

View File

@@ -48,6 +48,7 @@ $color2 = explode(',', $config['colores_primarios'] ?? '#003366,#0055A5')
.form-container.active { display: block; } .form-container.active { display: block; }
.form-title { text-align: center; margin-bottom: 2rem; color: #333; } .form-title { text-align: center; margin-bottom: 2rem; color: #333; }
.transition-fade { transition: opacity 0.3s ease-in-out; } .transition-fade { transition: opacity 0.3s ease-in-out; }
.alert { bottom: 30px; }
</style> </style>
</head> </head>
<body> <body>
@@ -159,7 +160,8 @@ $color2 = explode(',', $config['colores_primarios'] ?? '#003366,#0055A5')
<div class="col-md-12"> <div class="col-md-12">
<label for="opinion_file_agencia" class="form-label">Padrón de importadores o encargo conferido (PDF) *</label> <label for="opinion_file_agencia" class="form-label">Padrón de importadores o encargo conferido (PDF) *</label>
<input type="file" class="form-control" name="opinion_file" id="opinion_file_agencia" accept=".pdf" required> <input type="file" class="form-control" name="opinion_file" id="opinion_file_agencia" accept=".pdf" required>
</div><hr> </div>
<hr>
<p><strong>Administrador de la Agencia</strong></p> <p><strong>Administrador de la Agencia</strong></p>
<div class="col-md-6"> <div class="col-md-6">
<label for="nombre_admin" class="form-label">Nombre *</label> <label for="nombre_admin" class="form-label">Nombre *</label>
@@ -183,7 +185,7 @@ $color2 = explode(',', $config['colores_primarios'] ?? '#003366,#0055A5')
</div> </div>
</div> </div>
<div class="container "> <div class="container mb-4">
<div class="alert alert-warning" role="alert"> <div class="alert alert-warning" role="alert">
Una vez que envíes tu solicitud, será revisada manualmente por la agencia aduanal. Una vez que envíes tu solicitud, será revisada manualmente por la agencia aduanal.
Recibirás una notificación por correo electrónico cuando tu registro sea aprobado, junto con las instrucciones para activar tu cuenta e iniciar sesión en la plataforma. Recibirás una notificación por correo electrónico cuando tu registro sea aprobado, junto con las instrucciones para activar tu cuenta e iniciar sesión en la plataforma.
@@ -191,7 +193,7 @@ $color2 = explode(',', $config['colores_primarios'] ?? '#003366,#0055A5')
</div> </div>
<!-- FOOTER --> <!-- FOOTER -->
<footer class="fixed-bottom"> <footer class="fixed-bottom text-center py-3">
&copy; <?= date('Y') ?> <?= htmlspecialchars($siglas) ?> · <?= htmlspecialchars($nombre) ?> &copy; <?= date('Y') ?> <?= htmlspecialchars($siglas) ?> · <?= htmlspecialchars($nombre) ?>
</footer> </footer>
@@ -336,7 +338,7 @@ $color2 = explode(',', $config['colores_primarios'] ?? '#003366,#0055A5')
const correo = document.getElementById('correo').value.trim(); const correo = document.getElementById('correo').value.trim();
const telefono = document.getElementById('telefono').value.trim(); const telefono = document.getElementById('telefono').value.trim();
const direccion = document.getElementById('direccion').value.trim(); const direccion = document.getElementById('direccion').value.trim();
const archivo = document.getElementById('opinion_file').files[0]; const archivo = document.getElementById('opinion_file_agencia').files[0];
const nombre_admin = document.getElementById('nombre_admin').value.trim(); const nombre_admin = document.getElementById('nombre_admin').value.trim();
const correo_admin = document.getElementById('correo_admin').value.trim(); const correo_admin = document.getElementById('correo_admin').value.trim();
const recaptcha = getActiveRecaptchaResponse(); const recaptcha = getActiveRecaptchaResponse();

View File

@@ -66,6 +66,111 @@
.modal-dialog { z-index: 10000 !important; position: relative; } .modal-dialog { z-index: 10000 !important; position: relative; }
/* Opcional: Mejorar la apariencia del overlay */ /* Opcional: Mejorar la apariencia del overlay */
.modal-backdrop.show { opacity: 0.5; } .modal-backdrop.show { opacity: 0.5; }
/* ========== ANIMACIONES PARA INPUTS ========== */
/* 1. Animación al hacer focus */
.form-control { transition: all 0.3s ease; border: 2px solid #dee2e6; position: relative; }
.form-control:focus { border-color: #0d6efd; box-shadow: 0 0 0 0.2rem rgba(13, 110, 253, 0.25), 0 0 20px rgba(13, 110, 253, 0.3); transform: translateY(-2px); }
/* 2. Animación al escribir */
.form-control:not(:placeholder-shown) { border-color: #198754; background-color: #f8fff9; }
/* 3. Efecto de shake en validación */
.form-control.shake { animation: shake 0.5s ease-in-out; }
@keyframes shake {
0%, 100% { transform: translateX(0); }
25% { transform: translateX(-5px); }
75% { transform: translateX(5px); }
}
/* 4. Floating labels con animación */
.form-floating-custom { position: relative; margin-bottom: 1.5rem; }
.form-floating-custom .form-control { padding: 1rem 0.75rem 0.5rem 0.75rem; height: auto; }
.form-floating-custom .form-label { position: absolute; top: 0.75rem; left: 0.75rem; transition: all 0.3s ease; pointer-events: none; color: #6c757d; z-index: 2; }
.form-floating-custom .form-control:focus ~ .form-label,
.form-floating-custom .form-control:not(:placeholder-shown) ~ .form-label { top: 0.25rem; font-size: 0.75rem; color: #0d6efd; font-weight: 600; }
/* 5. Efecto de pulso en hover */
.input-pulse:hover { animation: inputPulse 0.6s ease-in-out; }
@keyframes inputPulse {
0% { transform: scale(1); }
50% { transform: scale(1.02); }
100% { transform: scale(1); }
}
/* 6. Gradiente animado en el borde */
.input-gradient { position: relative; overflow: hidden; }
.input-gradient::before { content: ''; position: absolute; top: 0; left: -100%; width: 100%; height: 100%;
background: linear-gradient(90deg, transparent, rgba(13, 110, 253, 0.3), transparent); transition: left 0.5s; pointer-events: none; z-index: 1; }
.input-gradient:focus::before { left: 100%; }
/* 7. Efecto de brillo en validación exitosa */
.form-control.valid { border-color: #198754; background: linear-gradient(45deg, #fff, #f8fff9); animation: successGlow 1s ease-in-out; }
@keyframes successGlow {
0% { box-shadow: 0 0 5px rgba(25, 135, 84, 0.5); }
50% { box-shadow: 0 0 20px rgba(25, 135, 84, 0.8); }
100% { box-shadow: 0 0 5px rgba(25, 135, 84, 0.5); }
}
/* 8. Typing effect simulation */
.input-typing { position: relative; }
.input-typing::after { content: '|'; position: absolute; right: 10px; top: 50%; transform: translateY(-50%); opacity: 0; animation: blink 1s infinite; color: #0d6efd; }
.input-typing:focus::after { opacity: 1; }
@keyframes blink {
0%, 50% { opacity: 1; }
51%, 100% { opacity: 0; }
}
/* 9. Efecto de ondas */
.input-ripple { position: relative; overflow: hidden; }
.input-ripple::before { content: ''; position: absolute; top: 50%; left: 50%; width: 0; height: 0; background: rgba(13, 110, 253, 0.2);
border-radius: 50%; transform: translate(-50%, -50%); transition: width 0.3s, height 0.3s; pointer-events: none; }
.input-ripple:focus::before { width: 300px; height: 300px; }
/* 10. Slide-up animation para los campos */
.form-group-animated { opacity: 0; transform: translateY(20px); animation: slideUp 0.6s ease-out forwards; }
.form-group-animated:nth-child(1) { animation-delay: 0.1s; }
.form-group-animated:nth-child(2) { animation-delay: 0.2s; }
.form-group-animated:nth-child(3) { animation-delay: 0.3s; }
.form-group-animated:nth-child(4) { animation-delay: 0.4s; }
.form-group-animated:nth-child(5) { animation-delay: 0.5s; }
.form-group-animated:nth-child(6) { animation-delay: 0.6s; }
.form-group-animated:nth-child(7) { animation-delay: 0.7s; }
.form-group-animated:nth-child(8) { animation-delay: 0.8s; }
@keyframes slideUp { to { opacity: 1; transform: translateY(0); } }
/* ========== ANIMACIONES PARA SELECT ========== */
/* 1. Estilos base para select */
.form-select {transition: all 0.3s ease; border: 2px solid #dee2e6; position: relative; cursor: pointer;
background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23343a40' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m1 6 7 7 7-7'/%3e%3c/svg%3e");
background-repeat: no-repeat; background-position: right 0.75rem center; background-size: 16px 12px; }
/* 2. Animación al hacer focus */
.form-select:focus { border-color: #0d6efd; box-shadow: 0 0 0 0.2rem rgba(13, 110, 253, 0.25), 0 0 20px rgba(13, 110, 253, 0.3); transform: translateY(-2px);
background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%230d6efd' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m1 6 7 7 7-7'/%3e%3c/svg%3e"); }
/* 3. Animación cuando tiene valor seleccionado */
.form-select:not([value=""]):not(:invalid) { border-color: #198754; background-color: #f8fff9;
background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23198754' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m1 6 7 7 7-7'/%3e%3c/svg%3e"); padding: 1rem 2.5rem 0.5rem 0.75rem; height: auto; min-height: 3.5rem; }
.form-floating-custom .form-select:focus ~ .form-label,
.form-floating-custom .form-select:not([value=""]) ~ .form-label { top: 0.25rem; font-size: 0.75rem; color: #0d6efd; font-weight: 600; }
/* 5. Efecto de shake en validación para select */
.form-select.shake { animation: shake 0.5s ease-in-out; }
/* 6. Efecto de pulso en hover para select */
.select-pulse:hover { animation: inputPulse 0.6s ease-in-out; }
/* 7. Gradiente animado para select */
.select-gradient { position: relative; overflow: hidden; }
.select-gradient::before { content: ''; position: absolute; top: 0; left: -100%; width: 100%; height: 100%; background: linear-gradient(90deg, transparent, rgba(13, 110, 253, 0.3), transparent); transition: left 0.5s; pointer-events: none; z-index: 1; }
.select-gradient:focus::before { left: 100%; }
/* 8. Efecto de brillo en validación exitosa para select */
.form-select.valid { border-color: #198754; background-color: #f8fff9; animation: successGlow 1s ease-in-out; }
/* 9. Efecto de rotación de la flecha al abrir */
.select-arrow-rotate { transition: all 0.3s ease; }
.select-arrow-rotate:focus { background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%230d6efd' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m1 10 7-7 7 7'/%3e%3c/svg%3e"); }
/* 10. Efecto de ondas para select */
.select-ripple { position: relative; overflow: hidden; }
.select-ripple::after { content: ''; position: absolute; top: 50%; left: 50%; width: 0; height: 0;
background: rgba(13, 110, 253, 0.2); border-radius: 50%; transform: translate(-50%, -50%); transition: width 0.3s, height 0.3s; pointer-events: none; }
.select-ripple:focus::after { width: 300px; height: 300px; }
/* 11. Efecto de scale suave */
.select-scale { transition: transform 0.2s ease; }
.select-scale:hover { transform: scale(1.02); }
.select-scale:focus { transform: scale(1.02) translateY(-2px); }
/* 12. Personalización de opciones (limitado en algunos navegadores) */
.form-select option { padding: 0.5rem; transition: all 0.2s ease; }
.form-select option:hover { background-color: #f8f9fa; }
/* 13. Indicador de estado */
.select-status { position: relative; }
.select-status::after { content: ''; position: absolute; right: 2.5rem; top: 50%; transform: translateY(-50%);
width: 8px; height: 8px; border-radius: 50%; background-color: #dc3545; opacity: 0; transition: opacity 0.3s ease; } .select-status.valid::after { background-color: #198754; opacity: 1; }
.select-status:invalid::after { background-color: #dc3545; opacity: 1; }
</style> </style>
</head> </head>
<body> <body>
@@ -74,46 +179,57 @@
<h4 class="mb-4 animate__animated animate__fadeInDown title_glow"> Nuevo Transporte</h4> <h4 class="mb-4 animate__animated animate__fadeInDown title_glow"> Nuevo Transporte</h4>
<form id="formTransCrear" action="/IMPORTADORES/transportes/guardar" method="POST" enctype="multipart/form-data" class="card p-4 shadow-sm card-hover position-relative h-auto"> <form id="formTransCrear" action="/IMPORTADORES/transportes/guardar" method="POST" enctype="multipart/form-data" class="card p-4 shadow-sm card-hover position-relative h-auto">
<div class="row g-3"> <div class="row g-3">
<div class="col-md-6">
<label class="form-label">Contenedor *</label> <div class="col-md-6 form-group-animated">
<div class="d-flex align-items-center"> <div class="form-floating-custom">
<input name="vehiculo" id="vehiculo" class="form-control" maxlength="20" required> <div class="d-flex align-items-center">
<button type="button" class="btn btn-outline-secondary ms-2" data-bs-toggle="modal" data-bs-target="#infoContenedor" style="border: none;"></button> <input name="vehiculo" id="vehiculo" class="form-control input-pulse input-gradient" maxlength="20" placeholder=" " required>
<label for="vehiculo" class="form-label">Contenedor *</label>
<button type="button" class="btn btn-white ms-2" data-bs-toggle="modal" data-bs-target="#infoContenedor" style="border: none;"></button>
</div>
</div> </div>
</div> </div>
<div class="col-md-6"> <div class="col-md-6 form-group-animated">
<label class="form-label">Identificación fiscal *</label> <div class="form-floating-custom">
<div class="d-flex align-items-center"> <div class="d-flex align-items-center">
<input name="identificador_fiscal" id="ident_fiscal" class="form-control" maxlength="20" required> <input name="identificador_fiscal" id="ident_fiscal" class="form-control input-pulse input-gradient" maxlength="20" placeholder=" " required>
<button type="button" class="btn btn-outline-secondary ms-2" data-bs-toggle="modal" data-bs-target="#infoIdentificacion" style="border: none;"></button> <label for="ident_fiscal" class="form-label">Identificación fiscal *</label>
<button type="button" class="btn btn-white ms-2" data-bs-toggle="modal" data-bs-target="#infoIdentificacion" style="border: none;"></button>
</div>
</div> </div>
</div> </div>
<div class="col-md-6"> <div class="col-md-6 form-group-animated">
<label class="form-label">Foto (opcional)</label> <label class="form-label">Foto (opcional)</label>
<input type="file" name="foto" id="foto" class="form-control" accept="image/*"> <div class="form-floating-custom">
<input type="file" name="foto" id="foto" class="form-control" accept="image/*">
</div>
</div> </div>
<div class="col-md-6">
<label class="form-label">Transportista *</label> <div class="col-md-6 form-group-animated">
<select name="id_transportista" id="transportista" class="form-select" required> <label for="" class="form-label">Transportista *</label>
<option value="">Selecciona...</option> <div class="form-floating-custom">
<?php foreach($transportistas as $tr): ?> <select name="id_transportista" id="transportista" class="form-select select-pulse select-gradient select-arrow-rotate select-status" required>
<option value="<?= $tr['id_transportista'] ?>"> <option value=""></option>
<?= htmlspecialchars(($tr['clave_identificador'] . ' - ' . $tr['nombre'] . ' - ' . $tr['ciudad_nombre'] . ' - ' . $tr['domicilio'])) ?> <?php foreach($transportistas as $tr): ?>
</option> <option value="<?= $tr['id_transportista'] ?>">
<?php endforeach; ?> <?= htmlspecialchars(($tr['clave_identificador'] . ' - ' . $tr['nombre'] . ' - ' . $tr['ciudad_nombre'] . ' - ' . $tr['domicilio'])) ?>
</select> </option>
<?php endforeach; ?>
</select>
<label for="trasportista" class="form-label">Transportista *</label>
</div>
</div> </div>
</div> </div>
<div class="text-end mt-4"> <div class="text-end mt-4">
<button type="submit" class="btn btn-success">Guardar</button> <button type="submit" class="btn btn-success ms-2 mt-auto w-auto btn-animated">Guardar</button>
<a href="/IMPORTADORES/transportes/lista" class="btn btn-secondary ms-2 mt-auto w-auto btn-animated">Cancelar</a> <a href="/IMPORTADORES/transportes/lista" class="btn btn-secondary ms-2 mt-auto w-auto btn-animated">Cancelar</a>
</div> </div>
</form> </form>
</div> </div>
<!-- Modales movidos FUERA del formulario -->
<!-- Modal de ayuda - Contenedor --> <!-- Modal de ayuda - Contenedor -->
<div class="modal fade" id="infoContenedor" tabindex="-1" aria-labelledby="infoContenedorLabel" aria-hidden="true"> <div class="modal fade" id="infoContenedor" tabindex="-1" aria-labelledby="infoContenedorLabel" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered"> <div class="modal-dialog modal-dialog-centered">
@@ -148,36 +264,53 @@
<script> <script>
document.getElementById('formTransCrear').addEventListener('submit', function(e) { document.getElementById('formTransCrear').addEventListener('submit', function(e) {
e.preventDefault(); // Prevenir envío por defecto e.preventDefault();
// Campos que validamos: const vehiculo = document.getElementById('vehiculo').value.trim();
const vehiculo = document.getElementById('vehiculo').value.trim(); const ident_fiscal = document.getElementById('ident_fiscal').value.trim();
const ident_fiscal = document.getElementById('ident_fiscal').value.trim(); const transportista = document.getElementById('transportista').value;
const transportista = document.getElementById('transportista').value; const fotoF = document.getElementById('foto').files[0];
const fotoF = document.getElementById('foto').files[0];
// Validaciones de campos obligatorios
if (!vehiculo) { if (!vehiculo) {
Swal.fire({ icon: 'warning', title: 'Contenedor requerido', text: 'Por favor ingresa el número económico del vehículo.', confirmButtonColor: '#dc3545'}); Swal.fire({ icon: 'warning', title: 'Contenedor requerido', text: 'Por favor ingresa el número económico del vehículo.', confirmButtonColor: '#dc3545' });
document.getElementById('vehiculo').focus(); return document.getElementById('vehiculo').focus();
return;
} }
if (!ident_fiscal) { if (!ident_fiscal) {
Swal.fire({ icon: 'warning', title: 'Identificación fiscal requerida', text: 'Por favor ingresa la identificación fiscal.', confirmButtonColor: '#dc3545' }); Swal.fire({ icon: 'warning', title: 'Identificación fiscal requerida', text: 'Por favor ingresa la identificación fiscal.', confirmButtonColor: '#dc3545' });
document.getElementById('ident_fiscal').focus(); return document.getElementById('ident_fiscal').focus();
return;
} }
if (!transportista) { if (!transportista) {
Swal.fire({ icon: 'warning', title: 'Transportista no seleccionado', text: 'Debes elegir un transportista.', confirmButtonColor: '#dc3545' }); Swal.fire({ icon: 'warning', title: 'Transportista no seleccionado', text: 'Debes elegir un transportista.', confirmButtonColor: '#dc3545' });
document.getElementById('transportista').focus(); return document.getElementById('transportista').focus();
return;
} }
if (fotoF && fotoF.size > 2 * 1024 * 1024) { // 2 MB if (fotoF && fotoF.size > 2 * 1024 * 1024) {
return Swal.fire({ icon: 'error', title: 'Foto demasiado grande', text: 'La imagen no debe exceder 2 MB.' confirmButtonColor: '#dc3545' }); return Swal.fire({ icon: 'error', title: 'Foto demasiado grande', text: 'La imagen no debe exceder 2 MB.', confirmButtonColor: '#dc3545' });
} }
// Si todas las validaciones pasan, el formulario se envía.
this.submit(); this.submit();
}); });
document.addEventListener('DOMContentLoaded', function() {
const inputs = document.querySelectorAll('.form-control');
inputs.forEach(input => {
input.addEventListener('input', function() {
if (this.checkValidity()) {
this.classList.remove('shake');
this.classList.add('valid');
} else {
this.classList.remove('valid');
}
});
input.addEventListener('invalid', function() {
this.classList.add('shake');
setTimeout(() => {
this.classList.remove('shake');
}, 500);
});
});
});
</script> </script>
</body> </body>

View File

@@ -51,6 +51,111 @@
.btn-animated::before { content: ''; position: absolute; top: 0; left: -100%; width: 100%; height: 100%; .btn-animated::before { content: ''; position: absolute; top: 0; left: -100%; width: 100%; height: 100%;
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2), transparent); transition: left 0.5s; } background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2), transparent); transition: left 0.5s; }
.btn-animated:hover::before { left: 100%; } .btn-animated:hover::before { left: 100%; }
/* ========== ANIMACIONES PARA INPUTS ========== */
/* 1. Animación al hacer focus */
.form-control { transition: all 0.3s ease; border: 2px solid #dee2e6; position: relative; }
.form-control:focus { border-color: #0d6efd; box-shadow: 0 0 0 0.2rem rgba(13, 110, 253, 0.25), 0 0 20px rgba(13, 110, 253, 0.3); transform: translateY(-2px); }
/* 2. Animación al escribir */
.form-control:not(:placeholder-shown) { border-color: #198754; background-color: #f8fff9; }
/* 3. Efecto de shake en validación */
.form-control.shake { animation: shake 0.5s ease-in-out; }
@keyframes shake {
0%, 100% { transform: translateX(0); }
25% { transform: translateX(-5px); }
75% { transform: translateX(5px); }
}
/* 4. Floating labels con animación */
.form-floating-custom { position: relative; margin-bottom: 1.5rem; }
.form-floating-custom .form-control { padding: 1rem 0.75rem 0.5rem 0.75rem; height: auto; }
.form-floating-custom .form-label { position: absolute; top: 0.75rem; left: 0.75rem; transition: all 0.3s ease; pointer-events: none; color: #6c757d; z-index: 2; }
.form-floating-custom .form-control:focus ~ .form-label,
.form-floating-custom .form-control:not(:placeholder-shown) ~ .form-label { top: 0.25rem; font-size: 0.75rem; color: #0d6efd; font-weight: 600; }
/* 5. Efecto de pulso en hover */
.input-pulse:hover { animation: inputPulse 0.6s ease-in-out; }
@keyframes inputPulse {
0% { transform: scale(1); }
50% { transform: scale(1.02); }
100% { transform: scale(1); }
}
/* 6. Gradiente animado en el borde */
.input-gradient { position: relative; overflow: hidden; }
.input-gradient::before { content: ''; position: absolute; top: 0; left: -100%; width: 100%; height: 100%;
background: linear-gradient(90deg, transparent, rgba(13, 110, 253, 0.3), transparent); transition: left 0.5s; pointer-events: none; z-index: 1; }
.input-gradient:focus::before { left: 100%; }
/* 7. Efecto de brillo en validación exitosa */
.form-control.valid { border-color: #198754; background: linear-gradient(45deg, #fff, #f8fff9); animation: successGlow 1s ease-in-out; }
@keyframes successGlow {
0% { box-shadow: 0 0 5px rgba(25, 135, 84, 0.5); }
50% { box-shadow: 0 0 20px rgba(25, 135, 84, 0.8); }
100% { box-shadow: 0 0 5px rgba(25, 135, 84, 0.5); }
}
/* 8. Typing effect simulation */
.input-typing { position: relative; }
.input-typing::after { content: '|'; position: absolute; right: 10px; top: 50%; transform: translateY(-50%); opacity: 0; animation: blink 1s infinite; color: #0d6efd; }
.input-typing:focus::after { opacity: 1; }
@keyframes blink {
0%, 50% { opacity: 1; }
51%, 100% { opacity: 0; }
}
/* 9. Efecto de ondas */
.input-ripple { position: relative; overflow: hidden; }
.input-ripple::before { content: ''; position: absolute; top: 50%; left: 50%; width: 0; height: 0; background: rgba(13, 110, 253, 0.2);
border-radius: 50%; transform: translate(-50%, -50%); transition: width 0.3s, height 0.3s; pointer-events: none; }
.input-ripple:focus::before { width: 300px; height: 300px; }
/* 10. Slide-up animation para los campos */
.form-group-animated { opacity: 0; transform: translateY(20px); animation: slideUp 0.6s ease-out forwards; }
.form-group-animated:nth-child(1) { animation-delay: 0.1s; }
.form-group-animated:nth-child(2) { animation-delay: 0.2s; }
.form-group-animated:nth-child(3) { animation-delay: 0.3s; }
.form-group-animated:nth-child(4) { animation-delay: 0.4s; }
.form-group-animated:nth-child(5) { animation-delay: 0.5s; }
.form-group-animated:nth-child(6) { animation-delay: 0.6s; }
.form-group-animated:nth-child(7) { animation-delay: 0.7s; }
.form-group-animated:nth-child(8) { animation-delay: 0.8s; }
@keyframes slideUp { to { opacity: 1; transform: translateY(0); } }
/* ========== ANIMACIONES PARA SELECT ========== */
/* 1. Estilos base para select */
.form-select {transition: all 0.3s ease; border: 2px solid #dee2e6; position: relative; cursor: pointer;
background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23343a40' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m1 6 7 7 7-7'/%3e%3c/svg%3e");
background-repeat: no-repeat; background-position: right 0.75rem center; background-size: 16px 12px; }
/* 2. Animación al hacer focus */
.form-select:focus { border-color: #0d6efd; box-shadow: 0 0 0 0.2rem rgba(13, 110, 253, 0.25), 0 0 20px rgba(13, 110, 253, 0.3); transform: translateY(-2px);
background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%230d6efd' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m1 6 7 7 7-7'/%3e%3c/svg%3e"); }
/* 3. Animación cuando tiene valor seleccionado */
.form-select:not([value=""]):not(:invalid) { border-color: #198754; background-color: #f8fff9;
background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23198754' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m1 6 7 7 7-7'/%3e%3c/svg%3e"); padding: 1rem 2.5rem 0.5rem 0.75rem; height: auto; min-height: 3.5rem; }
.form-floating-custom .form-select:focus ~ .form-label,
.form-floating-custom .form-select:not([value=""]) ~ .form-label { top: 0.25rem; font-size: 0.75rem; color: #0d6efd; font-weight: 600; }
/* 5. Efecto de shake en validación para select */
.form-select.shake { animation: shake 0.5s ease-in-out; }
/* 6. Efecto de pulso en hover para select */
.select-pulse:hover { animation: inputPulse 0.6s ease-in-out; }
/* 7. Gradiente animado para select */
.select-gradient { position: relative; overflow: hidden; }
.select-gradient::before { content: ''; position: absolute; top: 0; left: -100%; width: 100%; height: 100%; background: linear-gradient(90deg, transparent, rgba(13, 110, 253, 0.3), transparent); transition: left 0.5s; pointer-events: none; z-index: 1; }
.select-gradient:focus::before { left: 100%; }
/* 8. Efecto de brillo en validación exitosa para select */
.form-select.valid { border-color: #198754; background-color: #f8fff9; animation: successGlow 1s ease-in-out; }
/* 9. Efecto de rotación de la flecha al abrir */
.select-arrow-rotate { transition: all 0.3s ease; }
.select-arrow-rotate:focus { background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%230d6efd' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m1 10 7-7 7 7'/%3e%3c/svg%3e"); }
/* 10. Efecto de ondas para select */
.select-ripple { position: relative; overflow: hidden; }
.select-ripple::after { content: ''; position: absolute; top: 50%; left: 50%; width: 0; height: 0;
background: rgba(13, 110, 253, 0.2); border-radius: 50%; transform: translate(-50%, -50%); transition: width 0.3s, height 0.3s; pointer-events: none; }
.select-ripple:focus::after { width: 300px; height: 300px; }
/* 11. Efecto de scale suave */
.select-scale { transition: transform 0.2s ease; }
.select-scale:hover { transform: scale(1.02); }
.select-scale:focus { transform: scale(1.02) translateY(-2px); }
/* 12. Personalización de opciones (limitado en algunos navegadores) */
.form-select option { padding: 0.5rem; transition: all 0.2s ease; }
.form-select option:hover { background-color: #f8f9fa; }
/* 13. Indicador de estado */
.select-status { position: relative; }
.select-status::after { content: ''; position: absolute; right: 2.5rem; top: 50%; transform: translateY(-50%);
width: 8px; height: 8px; border-radius: 50%; background-color: #dc3545; opacity: 0; transition: opacity 0.3s ease; } .select-status.valid::after { background-color: #198754; opacity: 1; }
.select-status:invalid::after { background-color: #dc3545; opacity: 1; }
</style> </style>
</head> </head>
<body> <body>
@@ -72,67 +177,87 @@
</div> </div>
<div class="row g-3"> <div class="row g-3">
<div class="col-md-4"> <div class="col-md-4 form-group-animated">
<label for="clave" class="form-label">Clave *</label> <div class="form-floating-custom">
<input name="clave" id="clave" class="form-control" maxlength="8" required> <input name="clave" id="clave" type="text" class="form-control input-pulse input-gradient" maxlength="8" placeholder=" " required>
<label for="clave" class="form-label">Clave *</label>
</div>
</div> </div>
<div class="col-md-4"> <div class="col-md-4 form-group-animated">
<label for="nombre" class="form-label" id="labelNombre">Nombre *</label> <div class="form-floating-custom">
<input name="nombre" id="nombre" class="form-control" required> <input name="nombre" id="nombre" type="text" class="form-control input-pulse input-gradient" placeholder=" " required>
<label for="nombre" class="form-label" id="labelNombre">Nombre *</label>
</div>
</div> </div>
<div class="col-md-4"> <div class="col-md-4 form-group-animated">
<label for="rfc" class="form-label">RFC *</label> <div class="form-floating-custom">
<input name="rfc" id="rfc" class="form-control" maxlength="13" required> <input name="rfc" id="rfc" type="text" class="form-control input-pulse input-gradient" maxlength="13" placeholder=" " required>
<label for="rfc" class="form-label">RFC *</label>
</div>
</div> </div>
<div class="col-md-4"> <div class="col-md-4 form-group-animated">
<label for="curp" class="form-label">CURP *</label> <div class="form-floating-custom">
<input name="curp" id="curp" class="form-control" maxlength="18"> <input name="curp" id="curp" type="text" class="form-control input-pulse input-gradient" maxlength="18" placeholder=" ">
<label for="curp" class="form-label">CURP *</label>
</div>
</div> </div>
<div class="col-md-4"> <div class="col-md-4 form-group-animated">
<label for="telefono" class="form-label">Teléfono *</label> <div class="form-floating-custom">
<input name="telefono" id="telefono" class="form-control" maxlength="11" required> <input name="telefono" id="telefono" type="text" class="form-control input-pulse input-gradient" maxlength="11" placeholder=" " required>
<label for="telefono" class="form-label">Teléfono *</label>
</div>
</div> </div>
<div class="col-md-4"> <div class="col-md-4 form-group-animated">
<label for="caat" class="form-label">Código caat *</label> <div class="form-floating-custom">
<input name="caat" id="caat" class="form-control" maxlength="20" required> <input name="caat" id="caat" type="text" class="form-control input-pulse input-gradient" maxlength="20" placeholder=" " required>
<label for="caat" class="form-label">Código caat *</label>
</div>
</div> </div>
<div class="col-md-4"> <div class="col-md-4 form-group-animated">
<label for="pais" class="form-label">País *</label> <div class="form-floating-custom">
<select id="pais" name="pais" class="form-select" required> <select id="pais" name="pais" class="form-select select-pulse select-gradient select-arrow-rotate select-status" required>
<option value="">Selecciona país</option> <option value="">Selecciona país</option>
<?php foreach($paises as $p): ?> <?php foreach($paises as $p): ?>
<option value="<?= $p['id_pais'] ?>"><?= htmlspecialchars($p['nombre']) ?></option> <option value="<?= $p['id_pais'] ?>"><?= htmlspecialchars($p['nombre']) ?></option>
<?php endforeach; ?> <?php endforeach; ?>
</select> </select>
<label for="pais" class="form-label">País *</label>
</div>
</div> </div>
<div class="col-md-4"> <div class="col-md-4 form-group-animated">
<label for="entidad" class="form-label">Entidad *</label> <div class="form-floating-custom">
<select id="entidad" name="entidad" class="form-select" required disabled> <select id="entidad" name="entidad" class="form-select select-pulse select-gradient select-arrow-rotate select-status" required disabled>
<option value="">Primero país…</option> <option value="">Primero país…</option>
</select> </select>
<label for="entidad" class="form-label">Entidad *</label>
</div>
</div> </div>
<div class="col-md-4"> <div class="col-md-4 form-group-animated">
<label for="ciudad" class="form-label">Ciudad *</label> <div class="form-floating-custom">
<select id="ciudad" name="ciudad" class="form-select" required disabled> <select id="ciudad" name="ciudad" class="form-select select-pulse select-gradient select-arrow-rotate select-status" required disabled>
<option value="">Primero estado…</option> <option value="">Primero estado…</option>
</select> </select>
<label for="ciudad" class="form-label">Ciudad *</label>
</div>
</div> </div>
<div class="col-md-8"> <div class="col-md-8 form-group-animated">
<label for="domicilio" class="form-label">Domicilio *</label> <div class="form-floating-custom">
<input name="domicilio" id="domicilio" class="form-control" maxlength="100" required> <input name="domicilio" id="domicilio" type="text" class="form-control input-pulse input-gradient" maxlength="100" placeholder=" " required>
<label for="domicilio" class="form-label">Domicilio *</label>
</div>
</div> </div>
</div> </div>
<div class="text-end mt-4"> <div class="text-end mt-4 form-group-animated">
<button class="btn btn-success px-4 mt-auto w-auto btn-animated">Guardar Transportista</button> <button class="btn btn-success px-4 mt-auto w-auto btn-animated">Guardar Transportista</button>
<a href="/IMPORTADORES/transportistas/lista" class="btn btn-secondary ms-2 mt-auto w-auto btn-animated">Cancelar</a> <a href="/IMPORTADORES/transportistas/lista" class="btn btn-secondary ms-2 mt-auto w-auto btn-animated">Cancelar</a>
</div> </div>
@@ -280,9 +405,9 @@
// Switch Persona Moral // Switch Persona Moral
document.getElementById('personaMoralSwitch').addEventListener('change', function() { document.getElementById('personaMoralSwitch').addEventListener('change', function() {
const checked = this.checked; const checked = this.checked;
const label = document.getElementById('labelNombre'); const label = document.getElementById('labelNombre');
const curp = document.getElementById('curp'); const curp = document.getElementById('curp');
const hidden = document.getElementById('es_persona_moral'); const hidden = document.getElementById('es_persona_moral');
if (checked) { if (checked) {
label.textContent = 'Razón Social *'; label.textContent = 'Razón Social *';
curp.value = ''; curp.value = '';
@@ -296,6 +421,54 @@
hidden.value = '0'; hidden.value = '0';
} }
}); });
// Script para manejar las animaciones de validación
document.addEventListener('DOMContentLoaded', function() {
const inputs = document.querySelectorAll('.form-control');
inputs.forEach(input => {
// Validación en tiempo real
input.addEventListener('input', function() {
if (this.checkValidity()) {
this.classList.remove('shake');
this.classList.add('valid');
} else {
this.classList.remove('valid');
}
});
// Efecto shake en campos inválidos
input.addEventListener('invalid', function() {
this.classList.add('shake');
setTimeout(() => {
this.classList.remove('shake');
}, 500);
});
});
// Validación del formulario
document.getElementById('formAltaAgencias').addEventListener('submit', function(e) {
e.preventDefault();
let isValid = true;
inputs.forEach(input => {
if (!input.checkValidity()) {
input.classList.add('shake');
isValid = false;
}
});
if (isValid) {
// Aquí puedes agregar tu lógica de envío
Swal.fire({
icon: 'success',
title: '¡Éxito!',
text: 'Agencia registrada correctamente',
showConfirmButton: false,
timer: 1500
});
}
});
});
</script> </script>
</body> </body>