Vinculaciones multi-agencia

This commit is contained in:
2025-06-25 12:38:50 -06:00
parent a830e3a84a
commit c5c698dbe4
62 changed files with 5107 additions and 1454 deletions

View File

@@ -1,11 +1,17 @@
<?php include __DIR__ . '/../partials/sidebar_sistemas.php'; ?>
<?php include __DIR__ . '/../partials/sidebar_administrador.php'; ?>
<html>
<!DOCTYPE html>
<html lang="es">
<head>
<title>Dashboard | Agencia Aduanal</title>
<meta charset="UTF-8">
<title>Dashboard | Administrador</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css" rel="stylesheet">
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
<link href="https://cdn.datatables.net/1.13.4/css/jquery.dataTables.min.css" rel="stylesheet">
<script src="https://code.jquery.com/jquery-3.7.0.min.js"></script>
<script src="https://cdn.datatables.net/1.13.4/js/jquery.dataTables.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
<style>
body { font-family: 'Segoe UI', sans-serif; background-color: #f4f6f9; }
@@ -27,12 +33,89 @@
.card { border-radius: 12px; }
.table thead th { background: #343a40; color: #fff; }
</style>
</head>
<body>
<div class="content">
<h4>✅ Agencias Activas</h4>
<h4 class="mb-4">✅ Agencias Activas</h4>
<!-- TABLA DE AGENCIAS -->
<div class="card p-3 shadow-sm">
<div class="table-responsive">
<table class="table table-striped table-hover align-middle" id="tabla-agencias-activas">
<thead>
<tr>
<th>#</th>
<th>Nombre</th>
<th>RFC</th>
<th>Correo</th>
<th>Teléfono</th>
<th>Dirección</th>
<th>Fecha</th>
<th>Administrador</th>
<th>Acciones</th>
</tr>
</thead>
<tbody>
<?php if (!empty($agencias)): ?>
<?php foreach ($agencias as $ag): ?>
<tr>
<td><?= $ag['id_agencia'] ?></td>
<td><?= htmlspecialchars(decrypt($ag['nombre_agencia'])) ?></td>
<td><?= htmlspecialchars($ag['rfc_agencia']) ?></td>
<td><?= htmlspecialchars(decrypt($ag['email'])) ?></td>
<td><?= htmlspecialchars($ag['telefono']) ?></td>
<td><?= htmlspecialchars($ag['direccion']) ?></td>
<td><?= $ag['creado_en'] ? $ag['creado_en']->format('Y-m-d H:i') : '' ?></td>
<td><?= htmlspecialchars($ag['id_administrador']) ?></td>
<td>
<?php if (isset($ag['activo']) && $ag['activo'] == 1): ?>
<a href="/IMPORTADORES/administrador/suspenderAgencia?id=<?= $ag['id_agencia'] ?>&success=1" class="btn btn-sm btn-danger">Suspender</a>
<?php endif; ?>
</td>
</tr>
<?php endforeach; ?>
<?php else: ?>
<tr>
<td colspan="8" class="text-center">No hay agencias registradas aún.</td>
</tr>
<?php endif; ?>
</tbody>
</table>
</div>
</div>
</div>
<script>
$(document).ready(function () {
$('#tabla-agencias-activas').DataTable({
order: [],
language: {
url: 'https://cdn.datatables.net/plug-ins/1.13.4/i18n/es-ES.json'
}
});
});
</script>
<?php if (isset($_GET['success'])): ?>
<script>
<?php if ($_GET['success'] === 'status_deactivated'): ?>
Swal.fire({ icon: 'success', title: 'Usuario desactivado', text: 'El usuario ha sido desactivado correctamente.', confirmButtonColor: '#198754' });
<?php endif; ?>
</script>
<?php endif; ?>
<?php if (isset($_GET['error'])): ?>
<script>
<?php if ($_GET['error'] === 'unauthorized'): ?>
Swal.fire({ icon: 'error', title: 'No autorizado', text: 'No tienes permisos para realizar esta acción.', confirmButtonColor: '#dc3545' });
<?php elseif ($_GET['error'] === 'invalid_id'): ?>
Swal.fire({ icon: 'error', title: 'ID inválido', text: 'El identificador del usuario no es válido.', confirmButtonColor: '#dc3545' });
<?php elseif ($_GET['error'] === 'agencia_not_found'): ?>
Swal.fire({ icon: 'error', title: 'Usuario no encontrado', text: 'No se encontró el usuario especificado.', confirmButtonColor: '#dc3545' });
<?php elseif ($_GET['error'] === 'update_failed'): ?>
Swal.fire({ icon: 'error', title: 'Error al actualizar', text: 'No se pudo actualizar el estado del usuario.', confirmButtonColor: '#dc3545' });
<?php endif; ?>
</script>
<?php endif; ?>
</body>
</html>

View File

@@ -0,0 +1,202 @@
<?php include __DIR__ . '/../partials/sidebar_administrador.php'; ?>
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<title>Dashboard | Administrador</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
<style>
body { font-family: 'Segoe UI', sans-serif; background-color: #f4f6f9; }
.sidebar { width: 220px; height: 100vh; background-color: #343a40; position: fixed; top: 0; left: 0; padding-top: 56px; z-index: 1040; }
.sidebar .nav-link { font-weight: normal; color: white; transition: all 0.3s ease; }
.sidebar .nav-link:hover,
.sidebar .nav-link.active { background-color: #495057; color: #fff; }
.content { margin-top: 56px; padding: 40px 20px; position: relative; z-index: 1; background-color: #f4f6f9; transition: margin-left 0.3s ease; }
.navbar { position: fixed; top: 0; width: 100%; z-index: 1050; }
/* A partir de dispositivos medianos (>=768px), deja espacio lateral */
@media (min-width: 768px) { .content { margin-left: 250px; /* Ancho del sidebar */ } }
/* En móviles, sin margen lateral */
@media (max-width: 767.98px) {
.content { margin-left: 0; }
.sidebar .nav-link { font-weight: normal; color: #343a40; background-color: transparent; }
.sidebar .nav-link:hover,
.sidebar .nav-link.active { background-color: #e9ecef; color: #212529; }
}
.card { border-radius: 12px; }
.table thead th { background: #343a40; color: #fff; }
</style>
</head>
<body>
<!-- 📄 CONTENIDO -->
<div class="content">
<h4 class="mb-4">🏪 Alta de Agencias Aduanales</h4>
<!-- FORMULARIO -->
<div class="card p-4 mb-5 shadow-sm">
<form action="/IMPORTADORES/administrador/guardar_agencia" method="POST" id="formAltaAgencias">
<h5 class="mb-3"> Nueva Agencia Aduanal</h5>
<div class="row g-3">
<div class="col-md-6">
<label for="nombre_agencia" class="form-label">Nombre de la agencia *</label>
<input type="text" class="form-control" name="nombre_agencia" id="nombre_agencia" required>
</div>
<div class="col-md-6">
<label for="rfc_agencia" class="form-label">RFC *</label>
<input type="text" class="form-control" name="rfc_agencia" maxlength="13" id="rfc_agencia" required>
</div>
<div class="col-md-6">
<label for="correo" class="form-label">Correo electrónico *</label>
<input type="email" class="form-control" name="correo" id="correo" required>
</div>
<div class="col-md-6">
<label for="telefono" class="form-label">Teléfono *</label>
<input type="text" class="form-control" name="telefono" maxlength="11" id="telefono" required>
</div>
<div class="col-md-12">
<label for="direccion" class="form-label">Dirección *</label>
<input type="text" class="form-control" name="direccion" id="direccion"
placeholder="Ej: Calle, num.externo, num.interno, colonia, municipio, código postal, estado" required>
</div><hr>
<p><strong>Administrador de la Agencia</strong></p>
<div class="col-md-6">
<label for="nombre_admin" class="form-label">Nombre *</label>
<input type="text" class="form-control" name="nombre_admin" id="nombre_admin" required>
</div>
<div class="col-md-6">
<label for="correo_admin" class="form-label">Correo Administrador *</label>
<input type="email" class="form-control" name="correo_admin" id="correo_admin" required>
</div>
<div class="text-end mt-4">
<button class="btn btn-success px-4">Registrar</button>
</div>
</form>
</div>
</div>
<script>
// Registro de Agencia
document.getElementById('formAltaAgencias').addEventListener('submit', function(e) {
e.preventDefault();
const nombre = document.getElementById('nombre_agencia').value.trim();
const rfc_agencia = document.getElementById('rfc_agencia').value.trim().toUpperCase();
const correo = document.getElementById('correo').value.trim();
const telefono = document.getElementById('telefono').value.trim();
const direccion = document.getElementById('direccion').value.trim();
const nombre_admin = document.getElementById('nombre_admin').value.trim();
const correo_admin = document.getElementById('correo_admin').value.trim();
// Expresiones regulares
const correoRegex = /^[a-zA-Z0-9._+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
const rfcRegex = /^[A-ZÑ&]{3,4}\d{6}[A-Z0-9]{3}$/;
const telefonoRegex = /^\d{3}\s\d{7}$/;
// Validación de nombre
if (!nombre) {
Swal.fire({ icon: 'warning', title: 'Campo requerido', text: 'El nombre es obligatorio.', confirmButtonColor: '#dc3545' });
document.getElementById('nombre_agencia').focus();
return;
}
// Validación de correo
if (!correo) {
Swal.fire({ icon: 'warning', title: 'Campo requerido', text: 'El correo es obligatorio.', confirmButtonColor: '#dc3545' });
document.getElementById('correo').focus();
return;
}
if (!correoRegex.test(correo)) {
Swal.fire({ icon: 'error', title: 'Correo inválido', text: 'No es una dirección de correo válida.', confirmButtonColor: '#dc3545' });
document.getElementById('correo').focus();
return;
}
// Validación de RFC
if (!rfc_agencia) {
Swal.fire({ icon: 'warning', title: 'Campo requerido', text: 'El RFC es obligatorio.', confirmButtonColor: '#dc3545' });
document.getElementById('rfc_agencia').focus();
return;
}
if (!rfcRegex.test(rfc_agencia)) {
Swal.fire({ icon: 'error', title: 'RFC inválido', text: 'El RFC debe tener 12 o 13 caracteres con el formato correcto (ej. ABC123456XYZ).', confirmButtonColor: '#dc3545' });
document.getElementById('rfc_agencia').focus();
return;
}
// Validación de teléfono
if (!telefono) {
Swal.fire({ icon: 'warning', title: 'Campo requerido', text: 'El teléfono es obligatorio.', confirmButtonColor: '#dc3545' });
document.getElementById('telefono').focus();
return;
}
if (!telefonoRegex.test(telefono)) {
Swal.fire({ icon: 'error', title: 'Teléfono inválido', text: 'Debe tener 10 dígitos en el formato: 3 dígitos (LADA), espacio, y 7 dígitos (ej. 123 4567890).', confirmButtonColor: '#dc3545' });
document.getElementById('telefono').focus();
return;
}
// Validación de dirección
if (!direccion) {
Swal.fire({ icon: 'warning', title: 'Campo requerido', text: 'La dirección es obligatoria.', confirmButtonColor: '#dc3545' });
document.getElementById('direccion').focus();
return;
}
// Validación de administrador
if (!nombre_admin) {
Swal.fire({ icon: 'warning', title: 'Campo requerido', text: 'El nombre del administrador es obligatorio.', confirmButtonColor: '#dc3545' });
document.getElementById('nombre_admin').focus();
return;
}
// Validación de correo de administrador
if (!correo_admin) {
Swal.fire({ icon: 'warning', title: 'Campo requerido', text: 'El correo de administrador es obligatorio.', confirmButtonColor: '#dc3545' });
document.getElementById('correo_admin').focus();
return;
}
if (!correoRegex.test(correo_admin)) {
Swal.fire({ icon: 'error', title: 'Correo inválido', text: 'No es una dirección de correo válida.', confirmButtonColor: '#dc3545' });
document.getElementById('correo_admin').focus();
return;
}
// Si todas las validaciones pasan, el formulario se envía.
this.submit();
});
</script>
<?php if (isset($_GET['success'])): ?>
<script>
<?php if ($_GET['success'] === 'created'): ?>
Swal.fire({ icon: 'success', title: 'Agencia registrada',
text: 'La agencia adunal se ha registrada correctamente y se ha enviado un correo con las credenciales de acceso al administrador.',
confirmButtonColor: '#198754' });
<?php endif; ?>
</script>
<?php endif; ?>
<?php if (isset($_GET['error'])): ?>
<script>
<?php if ($_GET['error'] === 'unauthorized'): ?>
Swal.fire({ icon: 'error', title: 'No autorizado', text: 'No tienes permisos para realizar esta acción.', confirmButtonColor: '#dc3545' });
<?php elseif ($_GET['error'] === 'invalid_id_agencia'): ?>
Swal.fire({ icon: 'error', title: 'ID de agencia inválido', text: 'El identificador la agencia no es válido.', confirmButtonColor: '#dc3545' });
<?php elseif ($_GET['error'] === 'invalid_id'): ?>
Swal.fire({ icon: 'error', title: 'ID inválido', text: 'El identificador del usuario no es válido.', confirmButtonColor: '#dc3545' });
<?php elseif ($_GET['error'] === 'save_agencia_failed'): ?>
Swal.fire({ icon: 'error', title: 'Error al guardar agencia', text: 'No se pudo guardar la agencia. Inténtalo nuevamente.', confirmButtonColor: '#dc3545' });
<?php elseif ($_GET['error'] === 'update_failed'): ?>
Swal.fire({ icon: 'error', title: 'Error al actualizar', text: 'No se pudo actualizar el estado del usuario.', confirmButtonColor: '#dc3545' });
<?php elseif ($_GET['error'] === 'invalid_data'): ?>
Swal.fire({ icon: 'error', title: 'Datos inválidos', text: 'Por favor, completa todos los campos requeridos correctamente.', confirmButtonColor: '#dc3545' });
<?php elseif ($_GET['error'] === 'save_failed'): ?>
Swal.fire({ icon: 'error', title: 'Error al guardar', text: 'No se pudo guardar el usuario. Inténtalo nuevamente.', confirmButtonColor: '#dc3545' });
<?php endif; ?>
</script>
<?php endif; ?>
</body>
</html>

View File

@@ -1,128 +1,153 @@
<?php include __DIR__ . '/../partials/sidebar_sistemas.php'; ?>
<?php include __DIR__ . '/../partials/sidebar_administrador.php'; ?>
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<title>Panel de Administración - Usuarios</title>
<title>Dashboard | Administrador</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css" rel="stylesheet">
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
<link href="https://cdn.datatables.net/1.13.4/css/jquery.dataTables.min.css" rel="stylesheet">
<script src="https://code.jquery.com/jquery-3.7.0.min.js"></script>
<script src="https://cdn.datatables.net/1.13.4/js/jquery.dataTables.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
<style>
body { font-family: 'Segoe UI', sans-serif; background-color: #f4f6f9; }
.sidebar { width: 220px; height: 100vh; background-color: #343a40; position: fixed; top: 0; left: 0; padding-top: 56px; z-index: 1040; }
.sidebar .nav-link { font-weight: normal; color: white; transition: all 0.3s ease; }
.sidebar .nav-link:hover,
.sidebar .nav-link.active { background-color: #495057; color: #fff; }
.content { margin-top: 56px; padding: 40px 20px; position: relative; z-index: 1; background-color: #f4f6f9; transition: margin-left 0.3s ease; }
.navbar { position: fixed; top: 0; width: 100%; z-index: 1050; }
/* A partir de dispositivos medianos (>=768px), deja espacio lateral */
@media (min-width: 768px) { .content { margin-left: 250px; /* Ancho del sidebar */ } }
/* En móviles, sin margen lateral */
@media (max-width: 767.98px) {
.content { margin-left: 0; }
.sidebar .nav-link { font-weight: normal; color: #343a40; background-color: transparent; }
.sidebar .nav-link:hover,
.sidebar .nav-link.active { background-color: #e9ecef; color: #212529; }
}
.card { border-radius: 12px; }
table.dataTable thead th { background:#343a40; color:#fff; }
</style>
</head>
<body>
<!-- 📄 CONTENIDO -->
<div class="content px-4">
<h3 class="mb-4">Administración de Usuarios</h3>
<div class="content">
<h4 class="mb-4">👥 Alta de Usuarios</h4>
<!-- FORMULARIO -->
<form action="/IMPORTADORES/sistemas/guardar_usuario" method="POST" class="card p-4 mb-5 shadow-sm">
<h5 class="mb-3"> Nuevo Usuario</h5>
<div class="row g-3">
<div class="col-md-4">
<input name="nombre" class="form-control" placeholder="Nombre completo" required>
<div class="card p-4 mb-4 shadow-sm bg-white">
<form action="/IMPORTADORES/administrador/guardar_usuario" method="POST">
<h5 class="mb-3"> Nuevo Usuario</h5>
<div class="row g-3">
<div class="col-md-4">
<input name="nombre" type="text" class="form-control" id="nombre" placeholder="Nombre completo" required>
</div>
<div class="col-md-4">
<input name="email" type="email" class="form-control" id="email" placeholder="Correo electrónico" required>
</div>
<div class="col-md-2">
<input name="password" type="password" class="form-control" id="password" placeholder="Contraseña" required>
</div>
<div class="col-md-2">
<select name="tipo_usuario" class="form-select" required>
<option value="">Tipo</option>
<option value="super_admin">Administrador General</option>
<option value="importador">Importador</option>
</select>
</div>
</div>
<div class="col-md-4">
<input name="email" type="email" class="form-control" placeholder="Correo electrónico" required>
<div class="text-end mt-4">
<button class="btn btn-success px-4">Registrar</button>
</div>
<div class="col-md-2">
<input name="password" type="password" class="form-control" placeholder="Contraseña" required>
</div>
<div class="col-md-2">
<select name="tipo_usuario" class="form-select" required>
<option value="">Tipo</option>
<option value="agente_aduanal">Agente Aduanal</option>
<option value="importador">Importador</option>
</select>
</div>
</div>
<div class="text-end mt-4">
<button class="btn btn-success px-4">Registrar</button>
</div>
</form>
</form>
</div>
<!-- TABLA DE USUARIOS -->
<div class="card p-4 shadow-sm">
<h5 class="mb-3">👁️ Usuarios Registrados</h5>
<h4 class="mb-4">✅ Usuarios Activos</h4>
<div class="card p-3 shadow-sm">
<div class="table-responsive">
<table class="table table-striped table-hover align-middle">
<thead>
<table class="table table-striped table-hover align-middle" id="tabla-activos">
<thead class="table-dark">
<tr>
<th>#</th>
<th>Nombre</th>
<th>Correo</th>
<th>Tipo</th>
<th>Estado</th>
<th>Creado por</th>
<th>Fecha Registro</th>
<th>Acciones</th>
</tr>
</thead>
<tbody>
<?php if (!empty($usuarios)): ?>
<?php foreach ($usuarios as $u): ?>
<?php foreach ($usuarios as $u): ?>
<tr>
<td><?= $u['id_usuario'] ?></td>
<td><?= decrypt($u['nombre']) ?></td>
<td><?= decrypt($u['email']) ?></td>
<td><?= ucfirst($u['tipo_usuario']) ?></td>
<td><?= htmlspecialchars(decrypt($u['nombre'])) ?></td>
<td><?= htmlspecialchars(decrypt($u['email'])) ?></td>
<td><?= htmlspecialchars($u['tipo_usuario']) ?></td>
<td><?= htmlspecialchars($u['creado_por']) ?></td>
<td><?= isset($u['creado_en']) && $u['creado_en'] instanceof DateTime ? $u['creado_en']->format('Y-m-d H:i') : '' ?></td>
<td>
<span class="badge <?= $u['activo'] ? 'bg-success' : 'bg-danger' ?>">
<?= $u['activo'] ? 'Activo' : 'Inactivo' ?>
</span>
</td>
<td>
<a href="/IMPORTADORES/sistemas/toggle_estado?id=<?= $u['id_usuario'] ?>" class="btn btn-sm btn-warning">Toggle</a>
<a href="/IMPORTADORES/sistemas/reset_password?id=<?= $u['id_usuario'] ?>" class="btn btn-sm btn-secondary">Reset</a>
<?php if (isset($u['activo']) && $u['activo'] == 1): ?>
<a href="/IMPORTADORES/administrador/suspender?id=<?= $u['id_usuario'] ?>&success=1" class="btn btn-sm btn-danger">Suspender</a>
<?php endif; ?>
</td>
</tr>
<?php endforeach; ?>
<?php else: ?>
<tr>
<td colspan="6" class="text-center">No hay usuarios registrados aún.</td>
</tr>
<?php endif; ?>
<?php endforeach; ?>
</tbody>
</table>
</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
<?php if (isset($_GET['reset'], $_GET['pass'], $_GET['email']) && $_GET['reset'] === 'ok'): ?>
<script>
const nuevaPassword = "<?= htmlspecialchars($_GET["pass"]) ?>";
const emailUsuario = "<?= htmlspecialchars($_GET["email"]) ?>";
Swal.fire({
icon: 'success',
title: 'Contraseña restablecida',
html: `
Nueva contraseña generada:<br>
<strong style="font-size:18px;">${nuevaPassword}</strong>
<div class="mt-3">
<button class="btn btn-outline-primary btn-sm" onclick="copiarPassword()">📋 Copiar</button>
<button class="btn btn-outline-success btn-sm" onclick="enviarPorCorreo()">📧 Enviar por correo</button>
</div>
`,
showConfirmButton: false,
<script>
$(document).ready(function () {
$('#tabla-activos').DataTable({
order: [],
language: {
url: 'https://cdn.datatables.net/plug-ins/1.13.4/i18n/es-ES.json'
}
});
});
</script>
function copiarPassword() {
navigator.clipboard.writeText(nuevaPassword).then(() => {
Swal.fire('Copiado', 'La contraseña fue copiada al portapapeles.', 'success');
});
}
<?php if (isset($_GET['success'])): ?>
<script>
<?php if ($_GET['success'] === 'created'): ?>
Swal.fire({ icon: 'success', title: 'Usuario creado',
text: 'El usuario se ha creado correctamente y se ha enviado un correo con las credenciales.',
confirmButtonColor: '#198754' });
<?php elseif ($_GET['success'] === 'status_deactivated'): ?>
Swal.fire({ icon: 'success', title: 'Usuario desactivado', text: 'El usuario ha sido desactivado correctamente.', confirmButtonColor: '#198754' });
<?php endif; ?>
</script>
<?php endif; ?>
function enviarPorCorreo() {
fetch(`/IMPORTADORES/sistemas/enviar_password?email=${encodeURIComponent(emailUsuario)}&pass=${encodeURIComponent(nuevaPassword)}`)
.then(response => response.text())
.then(data => {
Swal.fire('Enviado', 'La contraseña fue enviada al correo del usuario.', 'success');
})
.catch(() => {
Swal.fire('Error', 'No se pudo enviar el correo.', 'error');
});
}
<?php if (isset($_GET['error'])): ?>
<script>
<?php if ($_GET['error'] === 'unauthorized'): ?>
Swal.fire({ icon: 'error', title: 'No autorizado', text: 'No tienes permisos para realizar esta acción.', confirmButtonColor: '#dc3545' });
<?php elseif ($_GET['error'] === 'invalid_id'): ?>
Swal.fire({ icon: 'error', title: 'ID inválido', text: 'El identificador del usuario no es válido.', confirmButtonColor: '#dc3545' });
<?php elseif ($_GET['error'] === 'user_not_found'): ?>
Swal.fire({ icon: 'error', title: 'Usuario no encontrado', text: 'No se encontró el usuario especificado.', confirmButtonColor: '#dc3545' });
<?php elseif ($_GET['error'] === 'update_failed'): ?>
Swal.fire({ icon: 'error', title: 'Error al actualizar', text: 'No se pudo actualizar el estado del usuario.', confirmButtonColor: '#dc3545' });
<?php elseif ($_GET['error'] === 'invalid_data'): ?>
Swal.fire({ icon: 'error', title: 'Datos inválidos', text: 'Por favor, completa todos los campos requeridos correctamente.', confirmButtonColor: '#dc3545' });
<?php elseif ($_GET['error'] === 'invalid_user_type'): ?>
Swal.fire({ icon: 'error', title: 'Tipo de usuario inválido', text: 'El tipo de usuario seleccionado no es válido.', confirmButtonColor: '#dc3545' });
<?php elseif ($_GET['error'] === 'email_exists'): ?>
Swal.fire({ icon: 'error', title: 'Correo ya registrado', text: 'Este correo electrónico ya está registrado en el sistema.', confirmButtonColor: '#dc3545' });
<?php elseif ($_GET['error'] === 'save_failed'): ?>
Swal.fire({ icon: 'error', title: 'Error al guardar', text: 'No se pudo guardar el usuario. Inténtalo nuevamente.', confirmButtonColor: '#dc3545' });
<?php endif; ?>
</script>
<?php endif; ?>

View File

@@ -0,0 +1,91 @@
<?php include __DIR__ . '/../partials/sidebar_administrador.php'; ?>
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<title>Dashboard | Administrador</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
<style>
body { font-family: 'Segoe UI', sans-serif; background-color: #f4f6f9; }
.sidebar { width: 220px; height: 100vh; background-color: #343a40; position: fixed; top: 0; left: 0; padding-top: 56px; z-index: 1040; }
.sidebar .nav-link { font-weight: normal; color: white; transition: all 0.3s ease; }
.sidebar .nav-link:hover,
.sidebar .nav-link.active { background-color: #495057; color: #fff; }
.content { margin-top: 56px; padding: 40px 20px; position: relative; z-index: 1; background-color: #f4f6f9; transition: margin-left 0.3s ease; }
.navbar { position: fixed; top: 0; width: 100%; z-index: 1050; }
/* A partir de dispositivos medianos (>=768px), deja espacio lateral */
@media (min-width: 768px) { .content { margin-left: 250px; /* Ancho del sidebar */ } }
/* En móviles, sin margen lateral */
@media (max-width: 767.98px) {
.content { margin-left: 0; }
.sidebar .nav-link { font-weight: normal; color: #343a40; background-color: transparent; }
.sidebar .nav-link:hover,
.sidebar .nav-link.active { background-color: #e9ecef; color: #212529; }
}
.card { border-radius: 12px; }
.table thead th { background: #343a40; color: #fff; }
</style>
</head>
<body>
<!-- 📄 CONTENIDO -->
<div class="content">
<h4 class="mb-4">✅ Aprobación de Agencias Aduanales</h4>
<!-- TABLA DE AGENCIAS -->
<div class="card p-3 shadow-sm">
<h5 class="mb-3">🏪 Agencias Solicitantes</h5>
<div class="table-responsive">
<table class="table table-striped table-hover align-middle" id="tabla-agencias-pendientes">
<thead>
<tr>
<th>#</th>
<th>Nombre</th>
<th>RFC</th>
<th>Correo</th>
<th>Teléfono</th>
<th>Dirección</th>
<th>Fecha</th>
<th>Archivo SAT</th>
<th>Acciones</th>
</tr>
</thead>
<tbody>
<?php if (!empty($solicitudes)): ?>
<?php foreach ($solicitudes as $s): ?>
<tr>
<td><?= $s['request_id'] ?></td>
<td><?= htmlspecialchars(decrypt($s['agencia_name'])) ?></td>
<td><?= htmlspecialchars(decrypt($s['rfc'])) ?></td>
<td><?= htmlspecialchars($s['email']) ?></td>
<td><?= htmlspecialchars($s['phone']) ?></td>
<td><?= htmlspecialchars($s['direccion']) ?></td>
<td><?= $s['request_date'] ? $s['request_date']->format('Y-m-d H:i') : '' ?></td>
<td>
<?php if (!empty($s['opinion_file'])): ?>
<a href="/IMPORTADORES/ver_opinion.php?file=<?= urlencode($s['opinion_file']) ?>" target="_blank" class="btn btn-sm btn-outline-secondary">Ver PDF</a>
<?php else: ?>
<span class="text-muted">No adjunto</span>
<?php endif; ?>
</td>
<td>
<a href="/IMPORTADORES/administrador/aprobar_agencia?id=<?= $s['request_id'] ?>" class="btn btn-sm btn-success">Aprobar</a>
</td>
</tr>
<?php endforeach; ?>
<?php else: ?>
<tr>
<td colspan="8" class="text-center">No hay agencias registradas aún.</td>
</tr>
<?php endif; ?>
</tbody>
</table>
</div>
</div>
</div>
</body>
</html>

View File

@@ -0,0 +1,119 @@
<?php include __DIR__ . '/../partials/sidebar_administrador.php'; ?>
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<title>Dashboard | Administrador</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
<style>
body { font-family: 'Segoe UI', sans-serif; background-color: #f4f6f9; }
.sidebar { width: 220px; height: 100vh; background-color: #343a40; position: fixed; top: 0; left: 0; padding-top: 56px; z-index: 1040; }
.sidebar .nav-link { font-weight: normal; color: white; transition: all 0.3s ease; }
.sidebar .nav-link:hover,
.sidebar .nav-link.active { background-color: #495057; color: #fff; }
.content { margin-top: 56px; padding: 40px 20px; position: relative; z-index: 1; background-color: #f4f6f9; transition: margin-left 0.3s ease; }
.navbar { position: fixed; top: 0; width: 100%; z-index: 1050; }
/* A partir de dispositivos medianos (>=768px), deja espacio lateral */
@media (min-width: 768px) { .content { margin-left: 250px; /* Ancho del sidebar */ } }
/* En móviles, sin margen lateral */
@media (max-width: 767.98px) {
.content { margin-left: 0; }
.sidebar .nav-link { font-weight: normal; color: #343a40; background-color: transparent; }
.sidebar .nav-link:hover,
.sidebar .nav-link.active { background-color: #e9ecef; color: #212529; }
}
.card { border-radius: 12px; }
.table thead th { background: #343a40; color: #fff; }
</style>
</head>
<body>
<!-- 📄 CONTENIDO -->
<div class="content">
<h4 class="mb-4">✅ Aprobación de Usuarios</h4>
<!-- TABLA DE USUARIOS -->
<div class="card p-3 shadow-sm">
<h5 class="mb-3">👥 Usuarios Solicitantes</h5>
<div class="table-responsive">
<table class="table table-striped table-hover align-middle" id="tabla-usuarios-pendientes">
<thead>
<tr>
<th>#</th>
<th>Empresa</th>
<th>RFC</th>
<th>Correo</th>
<th>Teléfono</th>
<th>Fecha</th>
<th>Archivo SAT</th>
<th>Acciones</th>
</tr>
</thead>
<tbody>
<?php if (!empty($solicitudes)): ?>
<?php foreach ($solicitudes as $s): ?>
<tr>
<td><?= $s['request_id'] ?></td>
<td><?= htmlspecialchars(decrypt($s['company_name'])) ?></td>
<td><?= htmlspecialchars(decrypt($s['rfc'])) ?></td>
<td><?= htmlspecialchars($s['email']) ?></td>
<td><?= htmlspecialchars($s['phone']) ?></td>
<td><?= $s['request_date'] ? $s['request_date']->format('Y-m-d H:i') : '' ?></td>
<td>
<?php if (!empty($s['opinion_file'])): ?>
<a href="/IMPORTADORES/ver_opinion.php?file=<?= urlencode($s['opinion_file']) ?>" target="_blank" class="btn btn-sm btn-outline-secondary">Ver PDF</a>
<?php else: ?>
<span class="text-muted">No adjunto</span>
<?php endif; ?>
</td>
<td>
<a href="/IMPORTADORES/administrador/aprobar_usuario?id=<?= $s['request_id'] ?>" class="btn btn-sm btn-success">Aprobar</a>
</td>
</tr>
<?php endforeach; ?>
<?php else: ?>
<tr>
<td colspan="8" class="text-center">No hay solicitudes registradas aún.</td>
</tr>
<?php endif; ?>
</tbody>
</table>
</div>
</div>
</div>
<?php if (isset($_GET['success'])): ?>
<script>
<?php if ($_GET['success'] === 'approved'): ?>
Swal.fire({ icon: 'success', title: 'Usuario aprobado',
text: 'El usuario ha sido aprobado correctamente y se ha enviado un correo con las credenciales.',
confirmButtonColor: '#198754' });
<?php endif; ?>
</script>
<?php endif; ?>
<?php if (isset($_GET['error'])): ?>
<script>
<?php if ($_GET['error'] === 'invalid_id'): ?>
Swal.fire({ icon: 'error', title: 'ID inválido', text: 'El identificador del usuario no es válido.', confirmButtonColor: '#dc3545' });
<?php elseif ($_GET['error'] === 'already_approved'): ?>
Swal.fire({ icon: 'warning', title: 'Ya aprobado', text: 'Esta solicitud ya fue aprobada anteriormente.', confirmButtonColor: '#ffc107' });
<?php elseif ($_GET['error'] === 'request_not_found'): ?>
Swal.fire({ icon: 'error', title: 'Solicitud no encontrada', text: 'No se encontró la solicitud especificada.', confirmButtonColor: '#dc3545' });
<?php elseif ($_GET['error'] === 'save_failed'): ?>
Swal.fire({ icon: 'error', title: 'Error al guardar', text: 'No se pudo guardar el usuario. Inténtalo nuevamente.', confirmButtonColor: '#dc3545' });
<?php elseif ($_GET['error'] === 'update_failed'): ?>
Swal.fire({ icon: 'error', title: 'Error al actualizar', text: 'No se pudo actualizar el estado del usuario.', confirmButtonColor: '#dc3545' });
<?php elseif ($_GET['error'] === 'failed'): ?>
Swal.fire({ icon: 'error', title: 'Error inesperado', text: 'Ha ocurrido un error inesperado. Por favor, inténtalo nuevamente.', confirmButtonColor: '#dc3545' })
<?php else: ?>
Swal.fire({ icon: 'error', title: 'Error', text: 'Ha ocurrido un error inesperado. Por favor, inténtalo nuevamente.', confirmButtonColor: '#dc3545' });
<?php endif; ?>
</script>
<?php endif; ?>
</body>
</html>

View File

@@ -1,50 +0,0 @@
<?php include __DIR__ . '/../partials/sidebar_sistemas.php'; ?>
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<title>Panel de Administración - Usuarios</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
</head>
<body>
<div class="content px-4 pt-5 mt-4">
<h4>🕵️ Bitácora de Accesos</h4>
<table class="table table-hover mt-3 align-middle">
<thead class="table-dark">
<tr>
<th>#</th>
<th>ID Usuario</th>
<th>Correo</th>
<th>IP</th>
<th>Fecha</th>
<th>Éxito</th>
<th>Detalle</th>
</tr>
</thead>
<tbody>
<?php foreach ($registros as $r): ?>
<tr>
<td><?= $r['id'] ?></td>
<td><?= $r['id_usuario'] ?></td>
<td><?= htmlspecialchars($r['email']) ?></td>
<td><?= htmlspecialchars($r['ip']) ?></td>
<td><?= $r['fecha'] instanceof DateTime ? $r['fecha']->format('Y-m-d H:i') : htmlspecialchars($r['fecha']) ?></td>
<td>
<span class="badge <?= $r['exito'] ? 'bg-success' : 'bg-danger' ?>">
<?= $r['exito'] ? 'Sí' : 'No' ?>
</span>
</td>
<td><?= htmlspecialchars($r['detalle']) ?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
</body>
</html>

View File

@@ -1,42 +0,0 @@
<?php include __DIR__ . '/../partials/sidebar_sistemas.php'; ?>
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<title>Panel de Administración - Usuarios</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
</head>
<body>
<div class="content px-4 pt-5 mt-4">
<h4>🛠️ Bitácora de Cambios de Usuario</h4>
<table class="table table-hover mt-3 align-middle">
<thead class="table-dark">
<tr>
<th>#</th>
<th>ID Usuario</th>
<th>Acción</th>
<th>Descripción</th>
<th>Fecha</th>
</tr>
</thead>
<tbody>
<?php foreach ($registros as $r): ?>
<tr>
<td><?= $r['id_bitacora'] ?></td>
<td><?= $r['usuario_id'] ?></td>
<td><strong><?= htmlspecialchars($r['accion']) ?></strong></td>
<td><?= nl2br(htmlspecialchars($r['descripcion'])) ?></td>
<td><?= $r['fecha'] instanceof DateTime ? $r['fecha']->format('Y-m-d H:i') : htmlspecialchars($r['fecha']) ?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
</body>
</html>

View File

@@ -0,0 +1,113 @@
<?php include __DIR__ . '/../partials/sidebar_administrador.php'; ?>
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<title>Dashboard | Administrador</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
<style>
body { font-family: 'Segoe UI', sans-serif; background-color: #f4f6f9; }
.sidebar { width: 220px; height: 100vh; background-color: #343a40; position: fixed; top: 0; left: 0; padding-top: 56px; z-index: 1040; }
.sidebar .nav-link { font-weight: normal; color: white; transition: all 0.3s ease; }
.sidebar .nav-link:hover,
.sidebar .nav-link.active { background-color: #495057; color: #fff; }
.content { margin-top: 56px; padding: 40px 20px; position: relative; z-index: 1; background-color: #f4f6f9; transition: margin-left 0.3s ease; }
.navbar { position: fixed; top: 0; width: 100%; z-index: 1050; }
.btn-indigo { background-color: #6610f2; color: white; }
.btn-indigo:hover { background-color: #520dc2; color: white; }
.text-indigo { color: #520dc2; }
.btn-orange { background-color: orangeRed; color: white; }
.btn-orange:hover { background-color: #ff3600; color: white; }
.text-orange { color: orangeRed; }
.btn-teal { background-color: #20c997; color: white; }
.btn-teal:hover { background-color: #4dd4ac; color: black; }
.text-teal { color: #20c997; }
/* A partir de dispositivos medianos (>=768px), deja espacio lateral */
@media (min-width: 768px) { .content { margin-left: 250px; /* Ancho del sidebar */ } }
/* En móviles, sin margen lateral */
@media (max-width: 767.98px) {
.content { margin-left: 0; }
.sidebar .nav-link { font-weight: normal; color: #343a40; background-color: transparent; }
.sidebar .nav-link:hover,
.sidebar .nav-link.active { background-color: #e9ecef; color: #212529; }
}
.card { border-radius: 12px; }
</style>
</head>
<body>
<!-- 📄 CONTENIDO -->
<div class="content">
<h4 class="mb-4">📦 Panel de Administrador</h4>
<div class="row g-4">
<div class="col-md-4">
<div class="card shadow-sm p-3">
<h5 class="text-primary">Aprobar agencias</h5>
<p>Aprueba las agencias que solicitaron un registro.</p>
<a href="/IMPORTADORES/sistemas/aprobarAgencias"
class="btn btn-primary btn-sm mt-2 <?= $_SERVER['REQUEST_URI'] === '/IMPORTADORES/sistemas/aprobarAgencias' ? 'active' : '' ?>">
Ver bitácora</a>
</div>
</div>
<div class="col-md-4">
<div class="card shadow-sm p-3">
<h5 class="text-success">Alta de agencias</h5>
<p>Da de alta manualmente una agencia.</p>
<a href="/IMPORTADORES/sistemas/altaAgencias"
class="btn btn-success btn-sm mt-2 <?= $_SERVER['REQUEST_URI'] === '/IMPORTADORES/sistemas/altaAgencias' ? 'active' : '' ?>">
Nuevo agente
</a>
</div>
</div>
<div class="col-md-4">
<div class="card shadow-sm p-3">
<h5 class="text-info">Aprobar de usuarios</h5>
<p>Aprueba los usuarios que solicitaron un registro.</p>
<a href="/IMPORTADORES/sistemas/aprobarUsuarios"
class="btn btn-info btn-sm mt-2 <?= $_SERVER['REQUEST_URI'] === '/IMPORTADORES/sistemas/aprobarUsuarios' ? 'active' : '' ?>">
Ver usuarios
</a>
</div>
</div>
<div class="col-md-4">
<div class="card shadow-sm p-3">
<h5 class="text-indigo">Alta de usuarios</h5>
<p>Da de alta manualmente a un usuario.</p>
<a href="/IMPORTADORES/sistemas/altaUsuarios"
class="btn btn-indigo btn-sm mt-2 <?= $_SERVER['REQUEST_URI'] === '/IMPORTADORES/sistemas/altaUsuarios' ? 'active' : '' ?>">
Nuevo agente
</a>
</div>
</div>
<div class="col-md-4">
<div class="card shadow-sm p-3">
<h5 class="text-warning">Configuración</h5>
<p>Administra los datos de tu agencia.</p>
<a href="/IMPORTADORES/sistemas/configuracion"
class="btn btn-warning btn-sm mt-2 <?= $_SERVER['REQUEST_URI'] === '/IMPORTADORES/sistemas/configuracion' ? 'active' : '' ?>">
Configurar
</a>
</div>
</div>
<div class="col-md-4">
<div class="card shadow-sm p-3">
<h5 class="text-danger">Cerrar sesión</h5>
<p>Salir del sistema de forma segura.</p>
<a href="/IMPORTADORES/sistemas/logout" class="btn btn-danger btn-sm mt-2">Logout</a>
</div>
</div>
</div>
</div>
</body>
</html>

View File

@@ -1,28 +0,0 @@
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<title>Login Sistemas</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body class="bg-light">
<div class="container mt-5" style="max-width: 400px;">
<h4 class="mb-4 text-center">Acceso Administrativo</h4>
<form action="/IMPORTADORES/sistemas/login" method="POST">
<div class="mb-3">
<label class="form-label">Correo</label>
<input type="email" name="email" class="form-control" required>
</div>
<div class="mb-3">
<label class="form-label">Contraseña</label>
<input type="password" name="clave" class="form-control" required>
</div>
<div class="d-grid">
<button class="btn btn-primary">Entrar</button>
</div>
</form>
</div>
</body>
</html>

View File

@@ -0,0 +1,117 @@
<?php include __DIR__ . '/../partials/sidebar_administrador.php'; ?>
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<title>Dashboard | Administrador</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css" rel="stylesheet">
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
<link href="https://cdn.datatables.net/1.13.4/css/jquery.dataTables.min.css" rel="stylesheet">
<script src="https://code.jquery.com/jquery-3.7.0.min.js"></script>
<script src="https://cdn.datatables.net/1.13.4/js/jquery.dataTables.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
<style>
body { font-family: 'Segoe UI', sans-serif; background-color: #f4f6f9; }
.sidebar { width: 220px; height: 100vh; background-color: #343a40; position: fixed; top: 0; left: 0; padding-top: 56px; z-index: 1040; }
.sidebar .nav-link { font-weight: normal; color: white; transition: all 0.3s ease; }
.sidebar .nav-link:hover,
.sidebar .nav-link.active { background-color: #495057; color: #fff; }
.content { margin-top: 56px; padding: 40px 20px; position: relative; z-index: 1; background-color: #f4f6f9; transition: margin-left 0.3s ease; }
.navbar { position: fixed; top: 0; width: 100%; z-index: 1050; }
/* A partir de dispositivos medianos (>=768px), deja espacio lateral */
@media (min-width: 768px) { .content { margin-left: 250px; /* Ancho del sidebar */ } }
/* En móviles, sin margen lateral */
@media (max-width: 767.98px) {
.content { margin-left: 0; }
.sidebar .nav-link { font-weight: normal; color: #343a40; background-color: transparent; }
.sidebar .nav-link:hover,
.sidebar .nav-link.active { background-color: #e9ecef; color: #212529; }
}
.card { border-radius: 12px; }
table.dataTable thead th { background:#343a40; color:#fff; }
</style>
</head>
<body>
<!-- 📄 CONTENIDO -->
<div class="content">
<h4 class="mb-4">🚷 Usuarios Inactivos</h4>
<div class="card p-3 shadow-sm">
<div class="table-responsive">
<table class="table table-striped" id="tabla-inactivos">
<thead class="table-dark">
<tr>
<th>#</th>
<th>Nombre</th>
<th>Correo</th>
<th>Tipo</th>
<th>Creado por</th>
<th>Fecha Registro</th>
<th>Acciones</th>
</tr>
</thead>
<tbody>
<?php foreach ($usuarios as $u): ?>
<tr>
<td><?= $u['id_usuario'] ?></td>
<td><?= htmlspecialchars(decrypt($u['nombre'])) ?></td>
<td><?= htmlspecialchars(decrypt($u['email'])) ?></td>
<td><?= htmlspecialchars($u['tipo_usuario']) ?></td>
<td><?= htmlspecialchars($u['creado_por']) ?></td>
<td><?= isset($u['creado_en']) && $u['creado_en'] instanceof DateTime ? $u['creado_en']->format('Y-m-d H:i') : '' ?></td>
<td>
<?php if (isset($u['activo']) && $u['activo'] == 0): ?>
<a href="/IMPORTADORES/administrador/activar?id=<?= $u['id_usuario'] ?>&success=1" class="btn btn-sm btn-success">Activar</a>
<a href="/IMPORTADORES/administrador/reset_password?id=<?= $u['id_usuario'] ?>" class="btn btn-sm btn-secondary">Nueva Contraseña</a>
<?php endif; ?>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
</div>
</div>
<script>
$(document).ready(function () {
$('#tabla-inactivos').DataTable({
order: [],
language: {
url: 'https://cdn.datatables.net/plug-ins/1.13.4/i18n/es-ES.json'
}
});
});
</script>
<?php if (isset($_GET['success'])): ?>
<script>
<?php if ($_GET['success'] === 'status_activated'): ?>
Swal.fire({ icon: 'success', title: 'Usuario activado', text: 'El usuario ha sido activado correctamente.', confirmButtonColor: '#198754' });
<?php elseif ($_GET['success'] === 'update_success'): ?>
Swal.fire({ icon: 'success', title: 'Contraseña temporal restablecida', text: 'La contraseña de accesso temporal fue restablecida correctamente y se envio al correo del usuario.', confirmButtonColor: '#198754' });
<?php endif; ?>
</script>
<?php endif; ?>
<?php if (isset($_GET['error'])): ?>
<script>
<?php if ($_GET['error'] === 'unauthorized'): ?>
Swal.fire({ icon: 'error', title: 'No autorizado', text: 'No tienes permisos para realizar esta acción.', confirmButtonColor: '#dc3545' });
<?php elseif ($_GET['error'] === 'invalid_id'): ?>
Swal.fire({ icon: 'error', title: 'ID inválido', text: 'El identificador del usuario no es válido.', confirmButtonColor: '#dc3545' });
<?php elseif ($_GET['error'] === 'user_not_found'): ?>
Swal.fire({ icon: 'error', title: 'Usuario no encontrado', text: 'No se encontró el usuario especificado.', confirmButtonColor: '#dc3545' });
<?php elseif ($_GET['error'] === 'update_failed'): ?>
Swal.fire({ icon: 'error', title: 'Error al actualizar', text: 'No se pudo actualizar el estado del usuario.', confirmButtonColor: '#dc3545' });
<?php endif; ?>
</script>
<?php endif; ?>
</body>
</html>

View File

@@ -9,6 +9,7 @@
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
<link href="https://cdn.datatables.net/1.13.4/css/jquery.dataTables.min.css" rel="stylesheet">
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
<script src="https://code.jquery.com/jquery-3.7.0.min.js"></script>
<script src="https://cdn.datatables.net/1.13.4/js/jquery.dataTables.min.js"></script>
<style>
@@ -39,7 +40,7 @@
<h3 class="mb-4">Administración de Agentes</h3>
<!-- FORMULARIO -->
<form action="/IMPORTADORES/agencias/alta_agente" method="POST" class="card p-4 mb-5 shadow-sm">
<form action="/IMPORTADORES/agencias/guardarAgente" method="POST" class="card p-4 mb-5 shadow-sm">
<h5 class="mb-3"> Nuevo Agente</h5>
<div class="row g-3">
<div class="col-md-4">
@@ -55,7 +56,6 @@
<select name="tipo_usuario" class="form-select" required>
<option value="">Tipo</option>
<option value="agente_aduanal">Agente Aduanal</option>
<option value="importador">Importador</option>
</select>
</div>
</div>
@@ -65,5 +65,39 @@
</form>
</div>
<?php if (isset($_GET['success'])): ?>
<script>
<?php if ($_GET['success'] === 'created'): ?>
Swal.fire({ icon: 'success', title: 'Usuario creado',
text: 'El usuario se ha creado correctamente y se ha enviado un correo con las credenciales.',
confirmButtonColor: '#198754' });
<?php elseif ($_GET['success'] === 'status_deactivated'): ?>
Swal.fire({ icon: 'success', title: 'Usuario desactivado', text: 'El usuario ha sido desactivado correctamente.', confirmButtonColor: '#198754' });
<?php endif; ?>
</script>
<?php endif; ?>
<?php if (isset($_GET['error'])): ?>
<script>
<?php if ($_GET['error'] === 'unauthorized'): ?>
Swal.fire({ icon: 'error', title: 'No autorizado', text: 'No tienes permisos para realizar esta acción.', confirmButtonColor: '#dc3545' });
<?php elseif ($_GET['error'] === 'invalid_id'): ?>
Swal.fire({ icon: 'error', title: 'ID inválido', text: 'El identificador del usuario no es válido.', confirmButtonColor: '#dc3545' });
<?php elseif ($_GET['error'] === 'user_not_found'): ?>
Swal.fire({ icon: 'error', title: 'Usuario no encontrado', text: 'No se encontró el usuario especificado.', confirmButtonColor: '#dc3545' });
<?php elseif ($_GET['error'] === 'update_failed'): ?>
Swal.fire({ icon: 'error', title: 'Error al actualizar', text: 'No se pudo actualizar el estado del usuario.', confirmButtonColor: '#dc3545' });
<?php elseif ($_GET['error'] === 'invalid_data'): ?>
Swal.fire({ icon: 'error', title: 'Datos inválidos', text: 'Por favor, completa todos los campos requeridos correctamente.', confirmButtonColor: '#dc3545' });
<?php elseif ($_GET['error'] === 'invalid_user_type'): ?>
Swal.fire({ icon: 'error', title: 'Tipo de usuario inválido', text: 'El tipo de usuario seleccionado no es válido.', confirmButtonColor: '#dc3545' });
<?php elseif ($_GET['error'] === 'email_exists'): ?>
Swal.fire({ icon: 'error', title: 'Correo ya registrado', text: 'Este correo electrónico ya está registrado en el sistema.', confirmButtonColor: '#dc3545' });
<?php elseif ($_GET['error'] === 'save_failed'): ?>
Swal.fire({ icon: 'error', title: 'Error al guardar', text: 'No se pudo guardar el usuario. Inténtalo nuevamente.', confirmButtonColor: '#dc3545' });
<?php endif; ?>
</script>
<?php endif; ?>
</body>
</html>

View File

@@ -35,30 +35,50 @@
<!-- 📄 CONTENIDO -->
<div class="content">
<h4 class="mb-4">📦 Panel del Agencia Aduanal</h4>
<h4 class="mb-4">📦 Panel de Agencia Aduanal</h4>
<div class="row g-4">
<div class="col-md-4">
<div class="card shadow-sm p-3">
<h5 class="text-primary">Usuarios activos</h5>
<h5 class="text-primary">Solicitudes de vinculación</h5>
<p>Aprueba las solicitudes de vinculación de los importadores.</p>
<a href="/IMPORTADORES/agencias/solicitudesVinculacion"
class="btn btn-primary btn-sm mt-2 <?= $_SERVER['REQUEST_URI'] === '/IMPORTADORES/agencias/solicitudesVinculacion' ? 'active' : '' ?>">
Ver solicitudes
</a>
</div>
</div>
<div class="col-md-4">
<div class="card shadow-sm p-3">
<h5 class="text-success">Usuarios vinculados</h5>
<p>Consulta los que ya fueron autorizados.</p>
<a href="/IMPORTADORES/agencias/activos" class="btn btn-primary btn-sm mt-2">Ver usuarios</a>
<a href="/IMPORTADORES/agencias/activos"
class="btn btn-success btn-sm mt-2 <?= $_SERVER['REQUEST_URI'] === '/IMPORTADORES/agencias/activos' ? 'active' : '' ?>">
Ver usuarios
</a>
</div>
</div>
<div class="col-md-4">
<div class="card shadow-sm p-3">
<h5 class="text-success">Alta de agentes</h5>
<p>Da de alta a tus agentes aduanales</p>
<a href="/IMPORTADORES/agencias/alta" class="btn btn-success btn-sm mt-2">Nuevo agente</a>
<h5 class="text-info">Alta de agentes</h5>
<p>Da de alta a tus agentes aduanales.</p>
<a href="/IMPORTADORES/agencias/alta"
class="btn btn-info btn-sm mt-2 <?= $_SERVER['REQUEST_URI'] === '/IMPORTADORES/agencias/alta' ? 'active' : '' ?>">
Nuevo agente
</a>
</div>
</div>
<div class="col-md-4">
<div class="card shadow-sm p-3">
<h5 class="text-warning">Bitácora del sistema</h5>
<p>Revisa los accesos y acciones recientes.</p>
<a href="/IMPORTADORES/agencias/bitacora" class="btn btn-warning btn-sm mt-2">Ver bitácora</a>
<h5 class="text-warning">Configuración</h5>
<p>Administra tus datos y preferencias.</p>
<a href="/IMPORTADORES/configuracion"
class="btn btn-warning btn-sm mt-2 <?= $_SERVER['REQUEST_URI'] === '/IMPORTADORES/agencias/configuracion' ? 'active' : '' ?>">
Configurar
</a>
</div>
</div>

View File

@@ -0,0 +1,119 @@
<?php include __DIR__ . '/../partials/sidebar_agencia.php'; ?>
<html>
<head>
<title>Dashboard | Agencia Aduanal</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
<link href="https://cdn.datatables.net/1.13.4/css/jquery.dataTables.min.css" rel="stylesheet">
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
<script src="https://code.jquery.com/jquery-3.7.0.min.js"></script>
<script src="https://cdn.datatables.net/1.13.4/js/jquery.dataTables.min.js"></script>
<style>
body { font-family: 'Segoe UI', sans-serif; background-color: #f4f6f9; }
.sidebar { width: 220px; height: 100vh; background-color: #343a40; position: fixed; top: 0; left: 0; padding-top: 56px; z-index: 1040; }
.sidebar .nav-link { font-weight: normal; color: white; transition: all 0.3s ease; }
.sidebar .nav-link:hover,
.sidebar .nav-link.active { background-color: #495057; color: #fff; }
.content { margin-top: 56px; padding: 40px 20px; position: relative; z-index: 1; background-color: #f4f6f9; transition: margin-left 0.3s ease; }
.navbar { position: fixed; top: 0; width: 100%; z-index: 1050; }
/* A partir de dispositivos medianos (>=768px), deja espacio lateral */
@media (min-width: 768px) { .content { margin-left: 250px; /* Ancho del sidebar */ } }
/* En móviles, sin margen lateral */
@media (max-width: 767.98px) {
.content { margin-left: 0; }
.sidebar .nav-link { font-weight: normal; color: #343a40; background-color: transparent; }
.sidebar .nav-link:hover,
.sidebar .nav-link.active { background-color: #e9ecef; color: #212529; }
}
.card { border-radius: 12px; }
.table thead th { background: #343a40; color: #fff; }
</style>
</head>
<body>
<div class="content">
<h4 class="mb-4">🔗 Solicitudes de Vinculación</h4>
<!-- TABLA DE SOLICITUDES DE VINCULACIÓN -->
<div class="card p-3 shadow-sm">
<h5 class="mb-3">👥 Usuarios Solicitantes</h5>
<div class="table-responsive">
<table class="table table-striped table-hover align-middle" id="tabla-vinculaciones-pendientes">
<thead>
<tr>
<th>#</th>
<th>Nombre</th>
<th>RFC</th>
<th>Mensaje</th>
<th>Fecha</th>
<th>Archivo SAT</th>
<th>Acciones</th>
</tr>
</thead>
<tbody>
<?php if (!empty($solicitudes)): ?>
<?php foreach ($solicitudes as $s): ?>
<tr>
<td><?= htmlspecialchars($s['id_solicitud'] ?? '') ?></td>
<td><?= htmlspecialchars(decrypt($s['importador_nombre'] ?? '')) ?></td>
<td><?= htmlspecialchars($s['rfc'] ?? '') ?></td>
<td><?= htmlspecialchars($s['mensaje'] ?? '') ?></td>
<td><?= !empty($s['fecha_solicitud']) ? $s['fecha_solicitud']->format('Y-m-d H:i') : '' ?></td>
<td>
<?php if (!empty($s['opinion_file'])): ?>
<a href="/IMPORTADORES/ver_opinion.php?file=<?= urlencode($s['opinion_file']) ?>" target="_blank" class="btn btn-sm btn-outline-secondary">Ver PDF</a>
<?php else: ?>
<span class="text-muted">No adjunto</span>
<?php endif; ?>
</td>
<td>
<a href="/IMPORTADORES/vinculaciones/aprobarVinculacion?id=<?= $s['id_solicitud'] ?>" class="btn btn-sm btn-success">Aprobar</a>
<a href="/IMPORTADORES/vinculaciones/denegarVinculacion?id=<?= $s['id_solicitud'] ?>" class="btn btn-sm btn-danger">Denegar</a>
</td>
</tr>
<?php endforeach; ?>
<?php endif; ?>
</tbody>
</table>
</div>
</div>
</div>
<script>
$(document).ready(function () {
$('#tabla-vinculaciones-pendientes').DataTable({
order: [],
language: {
url: 'https://cdn.datatables.net/plug-ins/1.13.4/i18n/es-ES.json'
}
});
});
</script>
<?php if (isset($_GET['success'])): ?>
<script>
<?php if ($_GET['success'] === 'approved'): ?>
Swal.fire({ icon: 'success', title: 'Usuario vinculado',
text: 'El usuario se ha vinculado correctamente, ahora puede visualizar tus catálogos.',
confirmButtonColor: '#198754' });
<?php elseif ($_GET['success'] === 'denied'): ?>
Swal.fire({ icon: 'success', title: 'Solicitud denegada', text: 'La solicitud fue denegada correctamente.', confirmButtonColor: '#198754' });
<?php endif; ?>
</script>
<?php endif; ?>
<?php if (isset($_GET['error'])): ?>
<script>
<?php if ($_GET['error'] === 'request_id_invalid'): ?>
Swal.fire({ icon: 'error', title: 'ID inválido', text: 'El identificador de la solicitud no es válido.', confirmButtonColor: '#dc3545' });
<?php elseif ($_GET['error'] === 'denied'): ?>
Swal.fire({ icon: 'error', title: 'Error de vinculación', text: 'Error al aprobar la vinculación.', confirmButtonColor: '#dc3545' });
<?php elseif ($_GET['error'] === 'denied_error'): ?>
Swal.fire({ icon: 'error', title: 'Error al denegar', text: 'No se pudo rechazar la solicitud.', confirmButtonColor: '#dc3545' });
<?php endif; ?>
</script>
<?php endif; ?>
</body>
</html>

View File

@@ -0,0 +1,105 @@
<?php include __DIR__ . '/../partials/sidebar_agencia.php'; ?>
<html>
<head>
<title>Dashboard | Agencia Aduanal</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
<link href="https://cdn.datatables.net/1.13.4/css/jquery.dataTables.min.css" rel="stylesheet">
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
<script src="https://code.jquery.com/jquery-3.7.0.min.js"></script>
<script src="https://cdn.datatables.net/1.13.4/js/jquery.dataTables.min.js"></script>
<style>
body { font-family: 'Segoe UI', sans-serif; background-color: #f4f6f9; }
.sidebar { width: 220px; height: 100vh; background-color: #343a40; position: fixed; top: 0; left: 0; padding-top: 56px; z-index: 1040; }
.sidebar .nav-link { font-weight: normal; color: white; transition: all 0.3s ease; }
.sidebar .nav-link:hover,
.sidebar .nav-link.active { background-color: #495057; color: #fff; }
.content { margin-top: 56px; padding: 40px 20px; position: relative; z-index: 1; background-color: #f4f6f9; transition: margin-left 0.3s ease; }
.navbar { position: fixed; top: 0; width: 100%; z-index: 1050; }
/* A partir de dispositivos medianos (>=768px), deja espacio lateral */
@media (min-width: 768px) { .content { margin-left: 250px; /* Ancho del sidebar */ } }
/* En móviles, sin margen lateral */
@media (max-width: 767.98px) {
.content { margin-left: 0; }
.sidebar .nav-link { font-weight: normal; color: #343a40; background-color: transparent; }
.sidebar .nav-link:hover,
.sidebar .nav-link.active { background-color: #e9ecef; color: #212529; }
}
.card { border-radius: 12px; }
.table thead th { background: #343a40; color: #fff; }
</style>
</head>
<body>
<div class="content">
<h4 class="mb-4">👥 Usuarios Vinculados</h4>
<!-- TABLA DE VINCULACIÓN AGENCIA -->
<div class="card p-3 shadow-sm">
<div class="table-responsive">
<table class="table table-striped table-hover align-middle" id="tabla-vinculaciones-agencia">
<thead>
<tr>
<th>#</th>
<th>Nombre</th>
<th>RFC</th>
<th>Teléfono</th>
<th>Fecha de Vinculación</th>
<th>Acciones</th>
</tr>
</thead>
<tbody>
<?php if (!empty($vinculados)): ?>
<?php foreach ($vinculados as $v): ?>
<tr>
<td><?= htmlspecialchars($v['id_relacion'] ?? '') ?></td>
<td><?= htmlspecialchars(decrypt($v['importador_nombre'] ?? '')) ?></td>
<td><?= htmlspecialchars($v['rfc'] ?? '') ?></td>
<td><?= htmlspecialchars($v['telefono'] ?? '') ?></td>
<td><?= !empty($v['fecha_vinculacion']) ? $v['fecha_vinculacion']->format('Y-m-d H:i') : '' ?></td>
<td>
<a href="/IMPORTADORES/vinculaciones/desvincular?id=<?= $v['id_relacion'] ?>" class="btn btn-sm btn-danger">Desvincular</a>
</td>
</tr>
<?php endforeach; ?>
<?php endif; ?>
</tbody>
</table>
</div>
</div>
</div>
<script>
$(document).ready(function () {
$('#tabla-vinculaciones-agencia').DataTable({
order: [],
language: {
url: 'https://cdn.datatables.net/plug-ins/1.13.4/i18n/es-ES.json'
}
});
});
</script>
<?php if (isset($_GET['success'])): ?>
<script>
<?php if ($_GET['success'] === 'unlinked'): ?>
Swal.fire({ icon: 'success', title: 'Usuario desvinculado',
text: 'El usuario se ha desvinculado correctamente.',
confirmButtonColor: '#198754' });
<?php endif; ?>
</script>
<?php endif; ?>
<?php if (isset($_GET['error'])): ?>
<script>
<?php if ($_GET['error'] === 'unlinked_failed'): ?>
Swal.fire({ icon: 'error', title: 'Desvinculación fallida', text: 'No se ha podido desvincular el usuario.', confirmButtonColor: '#dc3545' });
<?php elseif ($_GET['error'] === 'invalid_id'): ?>
Swal.fire({ icon: 'error', title: 'ID inválido', text: 'El identificador de la relación no es válido.', confirmButtonColor: '#dc3545' });
<?php endif; ?>
</script>
<?php endif; ?>
</body>
</html>

View File

@@ -1,86 +0,0 @@
<?php include __DIR__ . '/../partials/sidebar_agente.php'; ?>
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<title>Dashboard | Agente Aduanal</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
<link href="https://cdn.datatables.net/1.13.4/css/jquery.dataTables.min.css" rel="stylesheet">
<script src="https://code.jquery.com/jquery-3.7.0.min.js"></script>
<script src="https://cdn.datatables.net/1.13.4/js/jquery.dataTables.min.js"></script>
<style>
body { font-family: 'Segoe UI', sans-serif; background-color: #f4f6f9; }
.sidebar { width: 220px; height: 100vh; background-color: #343a40; position: fixed; top: 0; left: 0; padding-top: 56px; z-index: 1040; }
.sidebar .nav-link { font-weight: normal; color: white; transition: all 0.3s ease; }
.sidebar .nav-link:hover,
.sidebar .nav-link.active { background-color: #495057; color: #fff; }
.content { margin-top: 56px; padding: 40px 20px; position: relative; z-index: 1; background-color: #f4f6f9; transition: margin-left 0.3s ease; }
.navbar { position: fixed; top: 0; width: 100%; z-index: 1050; }
/* A partir de dispositivos medianos (>=768px), deja espacio lateral */
@media (min-width: 768px) { .content { margin-left: 250px; /* Ancho del sidebar */ } }
/* En móviles, sin margen lateral */
@media (max-width: 767.98px) {
.content { margin-left: 0; }
.sidebar .nav-link { font-weight: normal; color: #343a40; background-color: transparent; }
.sidebar .nav-link:hover,
.sidebar .nav-link.active { background-color: #e9ecef; color: #212529; }
}
.card { border-radius: 12px; }
table.dataTable thead th { background: #343a40; color: #fff; }
</style>
</head>
<body>
<div class="content">
<h4 class="mb-4">👥 Acceso de Usuarios</h4>
<div class="card p-3 shadow-sm">
<div class="table-responsive">
<table class="table table-striped" id="tabla-logins">
<thead>
<tr>
<th>ID</th>
<th>Nombre</th>
<th>Correo</th>
<th>IP</th>
<th>Fecha</th>
<th>Estatus</th>
<th>Detalles</th>
</tr>
</thead>
<tbody>
<?php foreach ($bitacoras as $b): ?>
<tr>
<td><?= htmlspecialchars($b['id_usuario']) ?></td>
<td><?= htmlspecialchars($b['nombre']) ?></td>
<td><?= htmlspecialchars($b['email']) ?></td>
<td><?= htmlspecialchars($b['ip']) ?></td>
<td><?= $b['fecha']->format('Y-m-d H:i:s') ?></td>
<td>
<?= $b['exito'] == 1 ? 'Éxito' : 'Fallido' ?>
</td>
<td><?= htmlspecialchars($b['detalle']) ?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
</div>
</div>
<script>
$(document).ready(function () {
$('#tabla-logins').DataTable({
order: [[4, 'desc']], // Índice de columna 4 = Fecha
language: {
url: '//cdn.datatables.net/plug-ins/1.13.4/i18n/es-ES.json'
}
});
});
</script>
</body>
</html>

View File

@@ -45,7 +45,10 @@ include __DIR__ . '/../partials/sidebar_agente.php';
<div class="card shadow-sm p-3">
<h5 class="text-primary">Importadores activos</h5>
<p>Consulta los que ya fueron autorizados.</p>
<a href="/IMPORTADORES/agentes/activos" class="btn btn-primary btn-sm mt-2">Ver importadores</a>
<a href="/IMPORTADORES/agentes/activos"
class="btn btn-primary btn-sm mt-2 <?= $_SERVER['REQUEST_URI'] === '/IMPORTADORES/agentes/activos' ? 'active' : '' ?>">
Ver importadores
</a>
</div>
</div>
@@ -53,15 +56,21 @@ include __DIR__ . '/../partials/sidebar_agente.php';
<div class="card shadow-sm p-3">
<h5 class="text-success">Solicitudes pendientes</h5>
<p>Valida nuevas solicitudes de registro.</p>
<a href="/IMPORTADORES/agentes/solicitudes_pendientes" class="btn btn-success btn-sm mt-2">Ver solicitudes</a>
<a href="/IMPORTADORES/agentes/solicitudes_pendientes"
class="btn btn-success btn-sm mt-2 <?= $_SERVER['REQUEST_URI'] === '/IMPORTADORES/agentes/solicitudes_pendientes' ? 'active' : '' ?>">
Ver solicitudes
</a>
</div>
</div>
<div class="col-md-4">
<div class="card shadow-sm p-3">
<h5 class="text-warning">Bitácora del sistema</h5>
<h5 class="text-info">Bitácora del sistema</h5>
<p>Revisa los accesos y acciones recientes.</p>
<a href="/IMPORTADORES/agentes/bitacora" class="btn btn-warning btn-sm mt-2">Ver bitácora</a>
<a href="/IMPORTADORES/agentes/bitacora"
class="btn btn-info btn-sm mt-2 <?= $_SERVER['REQUEST_URI'] === '/IMPORTADORES/agentes/bitacora' ? 'active' : '' ?>">
Ver bitácora
</a>
</div>
</div>
@@ -69,7 +78,21 @@ include __DIR__ . '/../partials/sidebar_agente.php';
<div class="card shadow-sm p-3">
<h5 class="text-orange">Locaciones</h5>
<p>Gestiona la locaciones validas para nuevos registros.</p>
<a href="/IMPORTADORES/agentes/lista" class="btn btn-orange btn-sm mt-2">Gestionar locaciones</a>
<a href="/IMPORTADORES/agentes/lista"
class="btn btn-orange btn-sm mt-2 <?= $_SERVER['REQUEST_URI'] === '/IMPORTADORES/agentes/lista' ? 'active' : '' ?>">
Gestionar locaciones
</a>
</div>
</div>
<div class="col-md-4">
<div class="card shadow-sm p-3">
<h5 class="text-warning">Configuración</h5>
<p>Administra tus datos y preferencias.</p>
<a href="/IMPORTADORES/configuracion"
class="btn btn-warning btn-sm mt-2 <?= $_SERVER['REQUEST_URI'] === '/IMPORTADORES/agentes/configuracion' ? 'active' : '' ?>">
Configurar
</a>
</div>
</div>

View File

View File

@@ -1,4 +1,30 @@
<?php include __DIR__ . '/../partials/sidebar_agencia.php'; ?>
<?php
// Aseguramos que el usuario esté autenticado
if (!isset($_SESSION['usuario_id'])) {
header('Location: /IMPORTADORES/login');
exit;
}
// Determinar el tipo de usuario
$tipoUsuario = $_SESSION['tipo_usuario'] ?? 'importador';
// Incluir el sidebar correspondiente
switch ($tipoUsuario) {
case 'super_admin':
include __DIR__ . '/../partials/sidebar_administrador.php';
break;
case 'admin_agencia':
include __DIR__ . '/../partials/sidebar_agencia.php';
break;
case 'agente_aduanal':
include __DIR__ . '/../partials/sidebar_agente.php';
break;
case 'importador':
default:
include __DIR__ . '/../partials/sidebar_importador.php';
break;
}
?>
<!DOCTYPE html>
<html lang="es">

View File

@@ -1,12 +1,13 @@
<?php include __DIR__ . '/../partials/sidebar_agencia.php'; ?>
<?php include __DIR__ . '/../partials/sidebar_administrador.php'; ?>
<html>
<!DOCTYPE html>
<html lang="es">
<head>
<title>Dashboard | Agencia Aduanal</title>
<meta charset="UTF-8">
<title>Dashboard | Administrador</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
<style>
body { font-family: 'Segoe UI', sans-serif; background-color: #f4f6f9; }
.sidebar { width: 220px; height: 100vh; background-color: #343a40; position: fixed; top: 0; left: 0; padding-top: 56px; z-index: 1040; }
@@ -30,8 +31,31 @@
</head>
<body>
<div class="content">
<h4> Usuarios Activos</h4>
<div class="content px-4 pt-5 mt-4">
<h4>🛠️ Bitácora de Cambios de Usuario</h4>
<table class="table table-hover mt-3 align-middle">
<thead class="table-dark">
<tr>
<th>#</th>
<th>ID Usuario</th>
<th>Acción</th>
<th>Descripción</th>
<th>Fecha</th>
</tr>
</thead>
<tbody>
<?php foreach ($registros as $r): ?>
<tr>
<td><?= $r['id_bitacora'] ?></td>
<td><?= $r['usuario_id'] ?></td>
<td><strong><?= htmlspecialchars($r['accion']) ?></strong></td>
<td><?= nl2br(htmlspecialchars($r['descripcion'])) ?></td>
<td><?= $r['fecha'] instanceof DateTime ? $r['fecha']->format('Y-m-d H:i') : htmlspecialchars($r['fecha']) ?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
</body>

View File

@@ -34,17 +34,129 @@
<h4 class="mb-4">📖 Bitácoras</h4>
<div class="row g-4">
<div class="col-md-4">
<div class="card shadow-sm p-3">
<h5 class="text-primary">Registro de Accesos</h5>
<p>Ver actividad de los usuarios.</p>
<a href="/IMPORTADORES/bitacoras/lista"
class="btn btn-primary btn-sm mt-2 <?= $_SERVER['REQUEST_URI'] === '/IMPORTADORES/bitacoras/lista' ? 'active' : '' ?>">
Ver Accesos
</a>
<?php if ($_SESSION['tipo_usuario'] === 'super_admin'): ?>
<div class="col-md-4">
<div class="card shadow-sm p-3">
<h5 class="text-primary">Registro de accesos</h5>
<p>Ver actividad de los usuarios.</p>
<a href="/IMPORTADORES/bitacoras/sistema"
class="btn btn-primary btn-sm mt-2 <?= $_SERVER['REQUEST_URI'] === '/IMPORTADORES/bitacoras/sistema' ? 'active' : '' ?>">
Ver actividad
</a>
</div>
</div>
</div>
<div class="col-md-4">
<div class="card shadow-sm p-3">
<h5 class="text-primary">Registro de usuarios</h5>
<p>Ver registros de los usuarios.</p>
<a href="/IMPORTADORES/bitacoras/usuarios"
class="btn btn-primary btn-sm mt-2 <?= $_SERVER['REQUEST_URI'] === '/IMPORTADORES/bitacoras/usuarios' ? 'active' : '' ?>">
Ver registro de usuarios
</a>
</div>
</div>
<div class="col-md-4">
<div class="card shadow-sm p-3">
<h5 class="text-primary">Registro de agencias</h5>
<p>Ver registros de las agencias.</p>
<a href="/IMPORTADORES/bitacoras/agencias"
class="btn btn-primary btn-sm mt-2 <?= $_SERVER['REQUEST_URI'] === '/IMPORTADORES/bitacoras/agencias' ? 'active' : '' ?>">
Ver registro de agencias
</a>
</div>
</div>
<div class="col-md-4">
<div class="card shadow-sm p-3">
<h5 class="text-primary">Mi actividad</h5>
<p>Ver mi actividad en la plataforma.</p>
<a href="/IMPORTADORES/bitacoras/miAcceso"
class="btn btn-primary btn-sm mt-2 <?= $_SERVER['REQUEST_URI'] === '/IMPORTADORES/bitacoras/miAcceso' ? 'active' : '' ?>">
Ver mis accesos
</a>
</div>
</div>
<?php endif; ?>
<?php if ($_SESSION['tipo_usuario'] === 'admin_agencia'): ?>
<div class="col-md-4">
<div class="card shadow-sm p-3">
<h5 class="text-primary">Registro de accesos</h5>
<p>Ver actividad de los usuarios.</p>
<a href="/IMPORTADORES/bitacoras/sistemaAgencia"
class="btn btn-primary btn-sm mt-2 <?= $_SERVER['REQUEST_URI'] === '/IMPORTADORES/bitacoras/sistemaAgencia' ? 'active' : '' ?>">
Ver actividad
</a>
</div>
</div>
<div class="col-md-4">
<div class="card shadow-sm p-3">
<h5 class="text-primary">Registro de vinculaciones</h5>
<p>Ver las vinculaciones de mi agencia.</p>
<a href="/IMPORTADORES/bitacoras/vinculaciones"
class="btn btn-primary btn-sm mt-2 <?= $_SERVER['REQUEST_URI'] === '/IMPORTADORES/bitacoras/vinculaciones' ? 'active' : '' ?>">
Ver actividad
</a>
</div>
</div>
<div class="col-md-4">
<div class="card shadow-sm p-3">
<h5 class="text-primary">Mi actividad</h5>
<p>Ver mi actividad en la plataforma.</p>
<a href="/IMPORTADORES/bitacoras/miAcceso"
class="btn btn-primary btn-sm mt-2 <?= $_SERVER['REQUEST_URI'] === '/IMPORTADORES/bitacoras/miAcceso' ? 'active' : '' ?>">
Ver mis accesos
</a>
</div>
</div>
<?php endif; ?>
<?php if ($_SESSION['tipo_usuario'] === 'agente_aduanal'): ?>
<div class="col-md-4">
<div class="card shadow-sm p-3">
<h5 class="text-primary">Registro de accesos</h5>
<p>Ver actividad de los usuarios.</p>
<a href="/IMPORTADORES/bitacoras/sistemaAgencia"
class="btn btn-primary btn-sm mt-2 <?= $_SERVER['REQUEST_URI'] === '/IMPORTADORES/bitacoras/sistemaAgencia' ? 'active' : '' ?>">
Ver actividad
</a>
</div>
</div>
<div class="col-md-4">
<div class="card shadow-sm p-3">
<h5 class="text-primary">Mi actividad</h5>
<p>Ver mi actividad en la plataforma.</p>
<a href="/IMPORTADORES/bitacoras/miAcceso"
class="btn btn-primary btn-sm mt-2 <?= $_SERVER['REQUEST_URI'] === '/IMPORTADORES/bitacoras/miAcceso' ? 'active' : '' ?>">
Ver mis accesos
</a>
</div>
</div>
<?php endif; ?>
<?php if ($_SESSION['tipo_usuario'] === 'importador'): ?>
<div class="col-md-4">
<div class="card shadow-sm p-3">
<h5 class="text-primary">Mis vinculaciones</h5>
<p>Ver mi registro de vinculaciones.</p>
<a href="/IMPORTADORES/bitacoras/vinculacionesUsuario"
class="btn btn-primary btn-sm mt-2 <?= $_SERVER['REQUEST_URI'] === '/IMPORTADORES/bitacoras/vinculacionesUsuario' ? 'active' : '' ?>">
Ver mis vinculaciones
</a>
</div>
</div>
<div class="col-md-4">
<div class="card shadow-sm p-3">
<h5 class="text-primary">Mi actividad</h5>
<p>Ver mi actividad en la plataforma.</p>
<a href="/IMPORTADORES/bitacoras/miAcceso"
class="btn btn-primary btn-sm mt-2 <?= $_SERVER['REQUEST_URI'] === '/IMPORTADORES/bitacoras/miAcceso' ? 'active' : '' ?>">
Ver mis accesos
</a>
</div>
</div>
<?php endif; ?>
</div>
</div>
</body>

View File

View File

@@ -103,43 +103,43 @@
// Obtener todos los campos correctamente
const transportista_id = document.getElementById('transportista_id').value;
const nombre = document.getElementById('nombre').value.trim();
const apellido = document.getElementById('apellido').value.trim();
const numero_licencia = document.getElementById('numero_licencia').value.trim();
const numero_gafete = document.getElementById('numero_gafete').value.trim();
const telefono = document.getElementById('telefono').value.trim();
const email = document.getElementById('email').value.trim();
const fecha_ingreso = document.getElementById('fecha_ingreso').value;
const foto = document.getElementById('foto').files[0];
const nombre = document.getElementById('nombre').value.trim();
const apellido = document.getElementById('apellido').value.trim();
const numero_licencia = document.getElementById('numero_licencia').value.trim();
const numero_gafete = document.getElementById('numero_gafete').value.trim();
const telefono = document.getElementById('telefono').value.trim();
const email = document.getElementById('email').value.trim();
const fecha_ingreso = document.getElementById('fecha_ingreso').value;
const foto = document.getElementById('foto').files[0];
// Expresiones regulares
const telefonoRegex = /^\d{3}\s\d{7}$/;
const emailRegex = /^[a-zA-Z0-9._+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
const telefonoRegex = /^\d{3}\s\d{7}$/;
const emailRegex = /^[a-zA-Z0-9._+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
const soloNumerosRegex = /^[0-9]+$/;
// VALIDAR CAMPOS OBLIGATORIOS PRIMERO
if (!transportista_id) {
Swal.fire({ icon: 'error', title: 'Transportista requerido', text: 'Por favor selecciona un transportista.', confirmButtonColor: '#dc3545' });
Swal.fire({ icon: 'warning', title: 'Transportista requerido', text: 'Por favor selecciona un transportista.', confirmButtonColor: '#dc3545' });
document.getElementById('transportista_id').focus();
return;
}
if (!nombre) {
Swal.fire({ icon: 'error', title: 'Nombre requerido', text: 'Por favor ingresa el nombre.', confirmButtonColor: '#dc3545' });
Swal.fire({ icon: 'warning', title: 'Nombre requerido', text: 'Por favor ingresa el nombre.', confirmButtonColor: '#dc3545' });
document.getElementById('nombre').focus();
return;
}
if (!apellido) {
Swal.fire({ icon: 'error', title: 'Apellido requerido', text: 'Por favor ingresa el apellido.', confirmButtonColor: '#dc3545' });
Swal.fire({ icon: 'warning', title: 'Apellido requerido', text: 'Por favor ingresa el apellido.', confirmButtonColor: '#dc3545' });
document.getElementById('apellido').focus();
return;
}
if (!numero_licencia) {
Swal.fire({ icon: 'error', title: 'Número de Licencia requerido', text: 'Por favor ingresa el número de licencia.', confirmButtonColor: '#dc3545' });
Swal.fire({ icon: 'warning', title: 'Número de Licencia requerido', text: 'Por favor ingresa el número de licencia.', confirmButtonColor: '#dc3545' });
document.getElementById('numero_licencia').focus();
return;
}
if (!numero_gafete) {
Swal.fire({ icon: 'error', title: 'Número de Gafete requerido', text: 'Por favor ingresa el número de gafete.', confirmButtonColor: '#dc3545' });
Swal.fire({ icon: 'warning', title: 'Número de Gafete requerido', text: 'Por favor ingresa el número de gafete.', confirmButtonColor: '#dc3545' });
document.getElementById('numero_gafete').focus();
return;
}

View File

@@ -142,43 +142,43 @@
// Obtener todos los campos correctamente
const transportista_id = document.getElementById('transportista_id').value;
const nombre = document.getElementById('nombre').value.trim();
const apellido = document.getElementById('apellido').value.trim();
const numero_licencia = document.getElementById('numero_licencia').value.trim();
const numero_gafete = document.getElementById('numero_gafete').value.trim();
const telefono = document.getElementById('telefono').value.trim();
const email = document.getElementById('email').value.trim();
const fecha_ingreso = document.getElementById('fecha_ingreso').value;
const foto = document.getElementById('foto').files[0];
const nombre = document.getElementById('nombre').value.trim();
const apellido = document.getElementById('apellido').value.trim();
const numero_licencia = document.getElementById('numero_licencia').value.trim();
const numero_gafete = document.getElementById('numero_gafete').value.trim();
const telefono = document.getElementById('telefono').value.trim();
const email = document.getElementById('email').value.trim();
const fecha_ingreso = document.getElementById('fecha_ingreso').value;
const foto = document.getElementById('foto').files[0];
// Expresiones regulares
const telefonoRegex = /^\d{3}\s\d{7}$/;
const emailRegex = /^[a-zA-Z0-9._+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
const telefonoRegex = /^\d{3}\s\d{7}$/;
const emailRegex = /^[a-zA-Z0-9._+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
const soloNumerosRegex = /^[0-9]+$/;
// VALIDAR CAMPOS OBLIGATORIOS PRIMERO
if (!transportista_id) {
Swal.fire({ icon: 'error', title: 'Transportista requerido', text: 'Por favor selecciona un transportista.', confirmButtonColor: '#dc3545' });
Swal.fire({ icon: 'warning', title: 'Transportista requerido', text: 'Por favor selecciona un transportista.', confirmButtonColor: '#dc3545' });
document.getElementById('transportista_id').focus();
return;
}
if (!nombre) {
Swal.fire({ icon: 'error', title: 'Nombre requerido', text: 'Por favor ingresa el nombre.', confirmButtonColor: '#dc3545' });
Swal.fire({ icon: 'warning', title: 'Nombre requerido', text: 'Por favor ingresa el nombre.', confirmButtonColor: '#dc3545' });
document.getElementById('nombre').focus();
return;
}
if (!apellido) {
Swal.fire({ icon: 'error', title: 'Apellido requerido', text: 'Por favor ingresa el apellido.', confirmButtonColor: '#dc3545' });
Swal.fire({ icon: 'warning', title: 'Apellido requerido', text: 'Por favor ingresa el apellido.', confirmButtonColor: '#dc3545' });
document.getElementById('apellido').focus();
return;
}
if (!numero_licencia) {
Swal.fire({ icon: 'error', title: 'Número de Licencia requerido', text: 'Por favor ingresa el número de licencia.', confirmButtonColor: '#dc3545' });
Swal.fire({ icon: 'warning', title: 'Número de Licencia requerido', text: 'Por favor ingresa el número de licencia.', confirmButtonColor: '#dc3545' });
document.getElementById('numero_licencia').focus();
return;
}
if (!numero_gafete) {
Swal.fire({ icon: 'error', title: 'Número de Gafete requerido', text: 'Por favor ingresa el número de gafete.', confirmButtonColor: '#dc3545' });
Swal.fire({ icon: 'warning', title: 'Número de Gafete requerido', text: 'Por favor ingresa el número de gafete.', confirmButtonColor: '#dc3545' });
document.getElementById('numero_gafete').focus();
return;
}

View File

@@ -11,17 +11,20 @@
<style>
body { font-family: 'Segoe UI', sans-serif; background-color: #f4f6f9; }
.sidebar { width: 220px; height: 100vh; background-color: #343a40; position: fixed; top: 0; left: 0; padding-top: 56px; z-index: 1040; }
.sidebar .nav-link { font-weight: bold; color: white; transition: all 0.3s ease; }
.sidebar .nav-link { font-weight: normal; color: white; transition: all 0.3s ease; }
.sidebar .nav-link:hover,
.sidebar .nav-link.active { background-color: #495057; color: #fff; }
.content { margin-top: 56px; padding: 40px 20px; position: relative; z-index: 1; background-color: #f4f6f9; transition: margin-left 0.3s ease; }
.navbar { position: fixed; top: 0; width: 100%; z-index: 1050; }
.btn-indigo { background-color: #6610f2; color: white;}
.btn-indigo:hover { background-color: #520dc2; color: white;}
.text-indigo { color: orangeRed;}
.text-indigo { color: #520dc2;}
.btn-orange { background-color: orangeRed; color: white;}
.btn-orange:hover { background-color: #ff3600; color: white;}
.text-orange { color: orangeRed;}
.btn-teal { background-color: #20c997; color: white; }
.btn-teal:hover { background-color: #4dd4ac; color: black; }
.text-teal { color: #20c997; }
/* A partir de dispositivos medianos (>=768px), deja espacio lateral */
@media (min-width: 768px) { .content { margin-left: 250px; /* Ancho del sidebar */ } }
/* En móviles, sin margen lateral */
@@ -106,6 +109,17 @@
</div>
</div>
<div class="col-md-4">
<div class="card shadow-sm p-3">
<h5 class="text-teal">Vinculaciones</h5>
<p>Visualiza o establece nuevas vinculaciones con agencias aduanales.</p>
<a href="/IMPORTADORES/importadores/vinculaciones"
class="btn btn-teal btn-sm mt-2 <?= str_contains($_SERVER['REQUEST_URI'], '/importadores/vinculaciones') ? 'active' : '' ?>">
Ver vinculaciones
</a>
</div>
</div>
<div class="col-md-4">
<div class="card shadow-sm p-3">
<h5 class="text-danger">Cerrar sesión</h5>

View File

@@ -38,7 +38,7 @@
<div class="content">
<h4 class="mb-4">🌍 Locaciones</h4>
<a href="/IMPORTADORES/agentes/alta" class="btn btn-success mb-3"> Agregar Locación</a>
<a href="/IMPORTADORES/locaciones/alta" class="btn btn-success mb-3"> Agregar Locación</a>
<div class="card p-3 shadow-sm">
<div class="table-responsive">
<table class="table table-striped" id="tabla-locaciones">

View File

@@ -41,30 +41,30 @@
<div class="card p-3 shadow-sm">
<div class="table-responsive">
<table class="table table-striped" id="tabla-locaciones">
<thead class="table-dark">
<tr>
<th>ID País</th>
<th>País</th>
<th>ISO3</th>
<th>ID Estado</th>
<th>Estado</th>
<th>ID Ciudad</th>
<th>Ciudad</th>
</tr>
</thead>
<tbody>
<?php foreach ($locaciones as $loc): ?>
<tr>
<td><?= htmlspecialchars($loc['id_pais']) ?></td>
<td><?= htmlspecialchars($loc['nombre_pais']) ?></td>
<td><?= htmlspecialchars($loc['iso3']) ?></td>
<td><?= htmlspecialchars($loc['id_estado']) ?></td>
<td><?= htmlspecialchars($loc['nombre_estado']) ?></td>
<td><?= htmlspecialchars($loc['id_ciudad']) ?></td>
<td><?= htmlspecialchars($loc['nombre_ciudad']) ?></td>
</tr>
<?php endforeach; ?>
</tbody>
<thead class="table-dark">
<tr>
<th>ID País</th>
<th>País</th>
<th>ISO3</th>
<th>ID Estado</th>
<th>Estado</th>
<th>ID Ciudad</th>
<th>Ciudad</th>
</tr>
</thead>
<tbody>
<?php foreach ($locaciones as $loc): ?>
<tr>
<td><?= htmlspecialchars($loc['id_pais']) ?></td>
<td><?= htmlspecialchars($loc['nombre_pais']) ?></td>
<td><?= htmlspecialchars($loc['iso3']) ?></td>
<td><?= htmlspecialchars($loc['id_estado']) ?></td>
<td><?= htmlspecialchars($loc['nombre_estado']) ?></td>
<td><?= htmlspecialchars($loc['id_ciudad']) ?></td>
<td><?= htmlspecialchars($loc['nombre_ciudad']) ?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
</div>

View File

@@ -0,0 +1,179 @@
<style>
.nav .nav-link { font-weight: normal; color: white; transition: all 0.3s ease; }
.nav .nav-link:hover,
.nav .nav-link.active { background-color: #495057; color: #fff; }
/* Estilo inverso solo para pantallas pequeñas (sidebar tipo offcanvas) */
@media (max-width: 767.98px) {
.nav .nav-link { font-weight: normal; color: #343a40; background-color: transparent; }
.nav .nav-link:hover,
.nav .nav-link.active { background-color: #e9ecef; color: #212529; }
}
</style>
<!-- 🔷 NAVBAR -->
<nav class="navbar navbar-dark bg-dark fixed-top">
<div class="container-fluid">
<!-- Botón de menú para móviles -->
<button class="btn btn-outline-light d-md-none me-2" type="button" data-bs-toggle="offcanvas" data-bs-target="#sidebarMenu">
</button>
<span class="navbar-brand">SIIH | Administrador</span>
<div class="d-flex ms-auto text-white">
<?php
// Verifica si estás en el dashboard principal del administrador
$esDashboard = str_contains($_SERVER['REQUEST_URI'], '/administrador/dashboard');
if ($esDashboard): ?>
Bienvenido, <?= htmlspecialchars($_SESSION['usuario_nombre']) ?>
<?php else: ?>
<a href="/IMPORTADORES/sistemas/logout" class="btn btn-outline-light btn-sm">Cerrar Sesión</a>
<?php endif; ?>
</div>
</div>
</nav>
<!-- SIDEBAR FIJA (escritorio) -->
<div class="d-none d-md-block bg-dark text-white position-fixed h-100 pt-5" style="width: 250px;">
<nav class="nav flex-column">
<!-- INICIO -->
<a href="/IMPORTADORES/administrador/dashboard"
class="nav-link px-3 py-2 <?= str_contains($_SERVER['REQUEST_URI'], '/IMPORTADORES/administrador/dashboard') ? 'active' : '' ?>">
🏠 Inicio
</a>
<!-- AGENCIAS -->
<a class="nav-link px-3 py-2 d-flex justify-content-between align-items-center
<?= str_contains($_SERVER['REQUEST_URI'], '/IMPORTADORES/administrador') ? 'active' : '' ?>"
data-bs-toggle="collapse" href="#submenuAgencias" role="button" aria-expanded="false">
🏪 Agencias
<span class="badge bg-secondary">3</span>
</a>
<div class="collapse <?= str_contains($_SERVER['REQUEST_URI'], '/administrador') ? 'show' : '' ?>" id="submenuAgencias">
<nav class="nav flex-column ms-3">
<a href="/IMPORTADORES/administrador/aprobarAgencias"
class="nav-link px-3 py-1 <?= str_contains($_SERVER['REQUEST_URI'], '/IMPORTADORES/administrador/aprobarAgencias') ? 'active' : '' ?>">
• Aprobar agencias
</a>
<a href="/IMPORTADORES/administrador/altaAgencias"
class="nav-link px-3 py-1 <?= str_contains($_SERVER['REQUEST_URI'], '/IMPORTADORES/administrador/altaAgencias') ? 'active' : '' ?>">
• Alta de agencia
</a>
<a href="/IMPORTADORES/administrador/agenciasActivas"
class="nav-link px-3 py-1 <?= str_contains($_SERVER['REQUEST_URI'], '/IMPORTADORES/administrador/agenciasActivas') ? 'active' : '' ?>">
• Ver agencias
</a>
</nav>
</div>
<!-- USUARIOS -->
<a class="nav-link px-3 py-2 d-flex justify-content-between align-items-center
<?= str_contains($_SERVER['REQUEST_URI'], '/IMPORTADORES/administrador') ? 'active' : '' ?>"
data-bs-toggle="collapse" href="#submenuUsuarios" role="button" aria-expanded="false">
👥 Usuarios
<span class="badge bg-secondary">3</span>
</a>
<div class="collapse <?= str_contains($_SERVER['REQUEST_URI'], '/administrador') ? 'show' : '' ?>" id="submenuUsuarios">
<nav class="nav flex-column ms-3">
<a href="/IMPORTADORES/administrador/aprobarUsuarios"
class="nav-link px-3 py-1 <?= str_contains($_SERVER['REQUEST_URI'], '/IMPORTADORES/administrador/aprobarUsuarios') ? 'active' : '' ?>">
• Aprobar usuarios
</a>
<a href="/IMPORTADORES/administrador/altaUsuarios"
class="nav-link px-3 py-1 <?= str_contains($_SERVER['REQUEST_URI'], '/IMPORTADORES/administrador/altaUsuarios') ? 'active' : '' ?>">
• Ver usuarios
</a>
<a href="/IMPORTADORES/administrador/usuariosInactivos"
class="nav-link px-3 py-1 <?= str_contains($_SERVER['REQUEST_URI'], '/IMPORTADORES/administrador/usuariosInactivos') ? 'active' : '' ?>">
• Usuarios inactivos
</a>
</nav>
</div>
<!-- CONFIGURACIÓN -->
<a href="/IMPORTADORES/administrador/configuracion"
class="nav-link px-3 py-2 <?= str_contains($_SERVER['REQUEST_URI'], '/IMPORTADORES/administrador/configuracion') ? 'active' : '' ?>">
⚙️ Configuración
</a>
</nav>
</div>
<!-- OFFCANVAS PARA MÓVILES -->
<div class="offcanvas offcanvas-start d-md-none" tabindex="-1" id="sidebarMenu" style="top: 75px;">
<div class="offcanvas-header">
<h5 class="offcanvas-title">Menú</h5>
<button type="button" class="btn-close" data-bs-dismiss="offcanvas"></button>
</div>
<div class="offcanvas-body">
<!-- INICIO -->
<a href="/IMPORTADORES/administrador/dashboard"
class="nav-link px-3 py-2 <?= str_contains($_SERVER['REQUEST_URI'], '/IMPORTADORES/administrador/dashboard') ? 'active' : '' ?>">
🏠 Inicio
</a>
<!-- AGENCIAS -->
<a class="nav-link px-3 py-2 d-flex justify-content-between align-items-center
<?= str_contains($_SERVER['REQUEST_URI'], '/IMPORTADORES/administrador') ? 'active' : '' ?>"
data-bs-toggle="collapse" href="#submenuAgencias" role="button"
aria-expanded="<?= str_contains($_SERVER['REQUEST_URI'], '/IMPORTADORES/administrador') ? 'true' : 'false' ?>"
aria-controls="submenuAgencias">
🏪 Agencias
<span class="badge bg-secondary">3</span>
</a>
<div class="collapse <?= str_contains($_SERVER['REQUEST_URI'], '/administrador') ? 'show' : '' ?>" id="submenuAgencias">
<nav class="nav flex-column ms-3">
<a href="/IMPORTADORES/administrador/aprobarAgencias"
class="nav-link px-3 py-1 <?= str_contains($_SERVER['REQUEST_URI'], '/IMPORTADORES/administrador/aprobarAgencias') ? 'active' : '' ?>">
• Aprobar agencias
</a>
<a href="/IMPORTADORES/administrador/altaAgencias"
class="nav-link px-3 py-1 <?= str_contains($_SERVER['REQUEST_URI'], '/IMPORTADORES/administrador/altaAgencias') ? 'active' : '' ?>">
• Alta de agencia
</a>
<a href="/IMPORTADORES/administrador/agenciasActivas"
class="nav-link px-3 py-1 <?= str_contains($_SERVER['REQUEST_URI'], '/IMPORTADORES/administrador/agenciasActivas') ? 'active' : '' ?>">
• Ver agencias
</a>
</nav>
</div>
<!-- USUARIOS -->
<a class="nav-link px-3 py-2 d-flex justify-content-between align-items-center
<?= str_contains($_SERVER['REQUEST_URI'], '/IMPORTADORES/administrador') ? 'active' : '' ?>"
data-bs-toggle="collapse" href="#submenuUsuarios" role="button"
aria-expanded="<?= str_contains($_SERVER['REQUEST_URI'], '/IMPORTADORES/administrador') ? 'true' : 'false' ?>"
aria-controls="submenuUsuarios">
👥 Usuarios
<span class="badge bg-secondary">4</span>
</a>
<div class="collapse <?= str_contains($_SERVER['REQUEST_URI'], '/administrador') ? 'show' : '' ?>" id="submenuUsuarios">
<nav class="nav flex-column ms-3">
<a href="/IMPORTADORES/administrador/aprobarUsuarios"
class="nav-link px-3 py-1 <?= str_contains($_SERVER['REQUEST_URI'], '/IMPORTADORES/administrador/aprobarUsuarios') ? 'active' : '' ?>">
• Aprobar usuarios
</a>
<a href="/IMPORTADORES/administrador/altaUsuarios"
class="nav-link px-3 py-1 <?= str_contains($_SERVER['REQUEST_URI'], '/IMPORTADORES/administrador/altaUsuarios') ? 'active' : '' ?>">
• Alta de usuario
</a>
<a href="/IMPORTADORES/administrador/usuariosActivos"
class="nav-link px-3 py-1 <?= str_contains($_SERVER['REQUEST_URI'], '/IMPORTADORES/administrador/usuariosActivos') ? 'active' : '' ?>">
• Ver usuarios
</a>
<a href="/IMPORTADORES/administrador/usuariosInactivos"
class="nav-link px-3 py-1 <?= str_contains($_SERVER['REQUEST_URI'], '/IMPORTADORES/administrador/usuariosInactivos') ? 'active' : '' ?>">
• Usuarios inactivos
</a>
</nav>
</div>
<!-- CONFIGURACIÓN -->
<a href="/IMPORTADORES/administrador/configuracion"
class="nav-link px-3 py-2 <?= str_contains($_SERVER['REQUEST_URI'], '/IMPORTADORES/administrador/configuracion') ? 'active' : '' ?>">
⚙️ Configuración
</a>
</div>
</div>

View File

@@ -38,27 +38,34 @@
<!-- INICIO -->
<a href="/IMPORTADORES/agencias/dashboard"
class="nav-link px-3 py-2 <?= $_SERVER['REQUEST_URI'] === '/IMPORTADORES/agencias/dashboard' ? 'active' : '' ?>">
class="nav-link px-3 py-2 <?= str_contains($_SERVER['REQUEST_URI'], '/IMPORTADORES/agencias/dashboard') ? 'active' : '' ?>">
🏠 Inicio
</a>
<!-- SOLICITUDES VINCULACIÓN -->
<a href="/IMPORTADORES/vinculaciones/solicitudesVinculacion"
class="nav-link px-3 py-2 <?= str_contains($_SERVER['REQUEST_URI'], '/IMPORTADORES/vinculaciones/solicitudesVinculacion') ? 'active' : '' ?>">
🔗 Vinculación
</a>
<!-- USUARIOS ACTIVOS -->
<a href="/IMPORTADORES/agencias/activos"
class="nav-link px-3 py-2 <?= $_SERVER['REQUEST_URI'] === '/IMPORTADORES/agencias/activos' ? 'active' : '' ?>">
Usuarios Activos
<a href="/IMPORTADORES/vinculaciones/usuariosVinculados"
class="nav-link px-3 py-2 <?= str_contains($_SERVER['REQUEST_URI'], '/IMPORTADORES/vinculaciones/usuariosVinculados') ? 'active' : '' ?>">
👥 Usuarios Vinculados
</a>
<!-- ALTA DE AGENTES -->
<a href="/IMPORTADORES/agencias/alta"
class="nav-link px-3 py-2 <?= $_SERVER['REQUEST_URI'] === '/IMPORTADORES/agencias/alta' ? 'active' : '' ?>">
class="nav-link px-3 py-2 <?= str_contains($_SERVER['REQUEST_URI'], '/IMPORTADORES/agencias/alta') ? 'active' : '' ?>">
📥 Alta de Agentes
</a>
<!-- BITÁCORA -->
<a href="/IMPORTADORES/agencias/bitacora"
class="nav-link px-3 py-2 <?= $_SERVER['REQUEST_URI'] === '/IMPORTADORES/agencias/bitacora' ? 'active' : '' ?>">
🕓 Bitácora
<!-- CONFIGURACIÓN -->
<a href="/IMPORTADORES/configuracion"
class="nav-link px-3 py-2 <?= str_contains($_SERVER['REQUEST_URI'], '/configuracion') ? 'active' : '' ?>">
⚙️ Configuración
</a>
</nav>
</div>
@@ -71,27 +78,33 @@
<div class="offcanvas-body">
<!-- INICIO -->
<a href="/IMPORTADORES/agencias/dashboard"
class="nav-link px-3 py-2 <?= $_SERVER['REQUEST_URI'] === '/IMPORTADORES/agencias/dashboard' ? 'active' : '' ?>">
<a href="/IMPORTADORES/agencias/dashboard"
class="nav-link px-3 py-2 <?= str_contains($_SERVER['REQUEST_URI'], '/IMPORTADORES/agencias/dashboard') ? 'active' : '' ?>">
🏠 Inicio
</a>
<!-- SOLICITUDES VINCULACIÓN -->
<a href="/IMPORTADORES/agencias/solicitudesVinculacion"
class="nav-link px-3 py-2 <?= str_contains($_SERVER['REQUEST_URI'], '/IMPORTADORES/agencias/solicitudesVinculacion') ? 'active' : '' ?>">
🔗 Vinculación
</a>
<!-- USUARIOS ACTIVOS -->
<a href="/IMPORTADORES/agencias/activos"
class="nav-link px-3 py-2 <?= $_SERVER['REQUEST_URI'] === '/IMPORTADORES/agencias/activos' ? 'active' : '' ?>">
✅ Usuarios Activos
<a href="/IMPORTADORES/vinculaciones/usuariosVinculados"
class="nav-link px-3 py-2 <?= str_contains($_SERVER['REQUEST_URI'], '/IMPORTADORES/vinculaciones/usuariosVinculados') ? 'active' : '' ?>">
✅ Usuarios Vinculados
</a>
<!-- ALTA DE AGENTES -->
<a href="/IMPORTADORES/agencias/alta"
class="nav-link px-3 py-2 <?= $_SERVER['REQUEST_URI'] === '/IMPORTADORES/agencias/alta' ? 'active' : '' ?>">
class="nav-link px-3 py-2 <?= str_contains($_SERVER['REQUEST_URI'], '/IMPORTADORES/agencias/alta') ? 'active' : '' ?>">
📥 Alta de Agentes
</a>
<!-- BITÁCORA -->
<a href="/IMPORTADORES/agencias/bitacora"
class="nav-link px-3 py-2 <?= $_SERVER['REQUEST_URI'] === '/IMPORTADORES/agencias/bitacora' ? 'active' : '' ?>">
🕓 Bitácora
<!-- CONFIGURACIÓN -->
<a href="/IMPORTADORES/configuracion"
class="nav-link px-3 py-2 <?= str_contains($_SERVER['REQUEST_URI'], '/configuracion') ? 'active' : '' ?>">
⚙️ Configuración
</a>
</div>

View File

@@ -1,10 +1,3 @@
<?php
if (!isset($_SESSION['usuario_id']) || $_SESSION['tipo_usuario'] !== 'agente_aduanal') {
header('Location: /IMPORTADORES/login');
exit;
}
?>
<style>
.nav .nav-link { font-weight: normal; color: white; transition: all 0.3s ease; }
.nav .nav-link:hover,
@@ -58,55 +51,12 @@ if (!isset($_SESSION['usuario_id']) || $_SESSION['tipo_usuario'] !== 'agente_adu
🏠 Inicio
</a>
<!-- IMPORTADORES ACTIVOS -->
<!-- IMPORTADORES VINCULADOS -->
<a href="/IMPORTADORES/agentes/activos"
class="nav-link px-3 py-2 <?= $_SERVER['REQUEST_URI'] === '/IMPORTADORES/agentes/activos' ? 'active' : '' ?>">
✅ Importadores Activos
🔗 Vinculados
</a>
<!-- SOLICITUDES PENDIENTES -->
<a href="/IMPORTADORES/agentes/solicitudes_pendientes"
class="nav-link px-3 py-2 <?= $_SERVER['REQUEST_URI'] === '/IMPORTADORES/agentes/solicitudes_pendientes' ? 'active' : '' ?>">
📥 Solicitudes de Registro
</a>
<!-- BITÁCORA -->
<a href="/IMPORTADORES/agentes/bitacora"
class="nav-link px-3 py-2 <?= $_SERVER['REQUEST_URI'] === '/IMPORTADORES/agentes/bitacora' ? 'active' : '' ?>">
🕓 Bitácora
</a>
<!-- LOCACIONES -->
<?php if ($esVistaLocaciones): ?>
<?php $enDashboardLocaciones = $_SERVER['REQUEST_URI'] === '/IMPORTADORES/locaciones/lista'; ?>
<a class="nav-link px-3 py-2 d-flex justify-content-between align-items-center <?= $esVistaLocaciones ? 'active' : '' ?>"
href="<?= $enDashboardLocaciones ? '#submenuLocaciones' : '/IMPORTADORES/locaciones/lista' ?>"
<?= $enDashboardLocaciones ? 'data-bs-toggle="collapse"' : '' ?>
role="button" aria-expanded="true" aria-controls="submenuLocaciones"
onclick="<?= $enDashboardLocaciones ? '' : 'event.preventDefault(); window.location.href = \'/IMPORTADORES/locaciones/lista\';' ?>">
🗺️ Locaciones
<span class="badge bg-secondary">2</span>
</a>
<div class="collapse show" id="submenuLocaciones">
<nav class="nav flex-column ms-3">
<a href="/IMPORTADORES/locaciones/lista"
class="nav-link px-3 py-1 <?= $_SERVER['REQUEST_URI'] === '/IMPORTADORES/locaciones/lista' ? 'active' : '' ?>">
• Ver Locaciones
</a>
<a href="/IMPORTADORES/locaciones/alta"
class="nav-link px-3 py-1 <?= str_contains($_SERVER['REQUEST_URI'], '/locaciones/alta') ? 'active' : '' ?>">
• Agregar Locaciones
</a>
</nav>
</div>
<?php elseif ($esVistaAgentes || $esVistaPatente): ?>
<a href="/IMPORTADORES/locaciones/lista"
class="nav-link px-3 py-2 d-flex justify-content-between align-items-center">
🗺️ Locaciones
<span class="badge bg-secondary">2</span>
</a>
<?php endif; ?>
<!-- Patentes -->
<?php if ($esVistaPatente): ?>
<?php $enDashboardPatente = $_SERVER['REQUEST_URI'] === '/IMPORTADORES/patente/dashboard'; ?>
@@ -138,6 +88,43 @@ if (!isset($_SESSION['usuario_id']) || $_SESSION['tipo_usuario'] !== 'agente_adu
</a>
<?php endif; ?>
<!-- LOCACIONES -->
<?php if ($esVistaLocaciones): ?>
<?php $enDashboardLocaciones = $_SERVER['REQUEST_URI'] === '/IMPORTADORES/locaciones/lista'; ?>
<a class="nav-link px-3 py-2 d-flex justify-content-between align-items-center <?= $esVistaLocaciones ? 'active' : '' ?>"
href="<?= $enDashboardLocaciones ? '#submenuLocaciones' : '/IMPORTADORES/locaciones/lista' ?>"
<?= $enDashboardLocaciones ? 'data-bs-toggle="collapse"' : '' ?>
role="button" aria-expanded="true" aria-controls="submenuLocaciones"
onclick="<?= $enDashboardLocaciones ? '' : 'event.preventDefault(); window.location.href = \'/IMPORTADORES/locaciones/lista\';' ?>">
🗺️ Locaciones
<span class="badge bg-secondary">2</span>
</a>
<div class="collapse show" id="submenuLocaciones">
<nav class="nav flex-column ms-3">
<a href="/IMPORTADORES/locaciones/lista"
class="nav-link px-3 py-1 <?= $_SERVER['REQUEST_URI'] === '/IMPORTADORES/locaciones/lista' ? 'active' : '' ?>">
• Ver Locaciones
</a>
<a href="/IMPORTADORES/locaciones/alta"
class="nav-link px-3 py-1 <?= str_contains($_SERVER['REQUEST_URI'], '/locaciones/alta') ? 'active' : '' ?>">
• Agregar Locaciones
</a>
</nav>
</div>
<?php elseif ($esVistaAgentes || $esVistaPatente): ?>
<a href="/IMPORTADORES/locaciones/lista"
class="nav-link px-3 py-2 d-flex justify-content-between align-items-center">
🗺️ Locaciones
<span class="badge bg-secondary">2</span>
</a>
<?php endif; ?>
<!-- CONFIGURACIÓN -->
<a href="/IMPORTADORES/configuracion"
class="nav-link px-3 py-2 <?= str_contains($_SERVER['REQUEST_URI'], '/configuracion') ? 'active' : '' ?>">
⚙️ Configuración
</a>
</nav>
</div>
@@ -161,12 +148,6 @@ if (!isset($_SESSION['usuario_id']) || $_SESSION['tipo_usuario'] !== 'agente_adu
✅ Importadores Activos
</a>
<!-- SOLICITUDES PENDIENTES -->
<a href="/IMPORTADORES/agentes/solicitudes_pendientes"
class="nav-link px-3 py-2 <?= $_SERVER['REQUEST_URI'] === '/IMPORTADORES/agentes/solicitudes_pendientes' ? 'active' : '' ?>">
📥 Solicitudes de Registro
</a>
<!-- BITÁCORA -->
<a href="/IMPORTADORES/agentes/bitacora"
class="nav-link px-3 py-2 <?= $_SERVER['REQUEST_URI'] === '/IMPORTADORES/agentes/bitacora' ? 'active' : '' ?>">
@@ -235,5 +216,11 @@ if (!isset($_SESSION['usuario_id']) || $_SESSION['tipo_usuario'] !== 'agente_adu
</a>
<?php endif; ?>
<!-- CONFIGURACIÓN -->
<a href="/IMPORTADORES/configuracion"
class="nav-link px-3 py-2 <?= str_contains($_SERVER['REQUEST_URI'], '/configuracion') ? 'active' : '' ?>">
⚙️ Configuración
</a>
</div>
</div>

View File

@@ -1,9 +1,130 @@
<!-- views/partials/sidebar_configuracion.php -->
<?php
if (!isset($_SESSION['usuario_id']) || $_SESSION['tipo_usuario'] !== 'importador') {
// Verificar que el usuario esté autenticado
if (!isset($_SESSION['usuario_id'])) {
header('Location: /IMPORTADORES/login');
exit;
}
// Obtener información del usuario
$tipoUsuario = $_SESSION['tipo_usuario'] ?? '';
$agenciaId = $_SESSION['agencia_id'] ?? null;
$usuarioId = $_SESSION['usuario_id'] ?? null;
// Definir permisos por tipo de usuario
$permisos = [
'super_admin' => [
'informacion_general' => true,
'automatizaciones' => true,
'preferencias' => true,
'seguridad' => true,
'bitacoras' => [
'registro_accesos' => true,
'registro_usuarios' => true,
'registro_agencias' => true,
'mi_actividad' => true
],
],
'admin_agencia' => [
'informacion_general' => true,
'automatizaciones' => true,
'preferencias' => true,
'seguridad' => true,
'bitacoras' => [
'acceso_usuarios_agencia' => true,
'registro_vinculaciones' => true,
'mi_actividad' => true
],
],
'agente_aduanal' => [
'informacion_general' => true,
'automatizaciones' => true,
'preferencias' => true,
'seguridad' => true,
'bitacoras' => [
'acceso_usuarios_agencia' => true,
'mi_actividad' => true
],
],
'importador' => [
'informacion_general' => true,
'automatizaciones' => true,
'preferencias' => true,
'seguridad' => true,
'bitacoras' => [
'vinculaciones_usuario' => true,
'mi_actividad' => true
],
],
];
// Obtener permisos del usuario actual
$permisosUsuario = $permisos[$tipoUsuario] ?? $permisos['importador'];
$permisosBitacoras = $permisosUsuario['bitacoras'] ?? [];
// Función para verificar si el usuario tiene acceso a bitácoras
function tieneAccesoBitacoras($permisosBitacoras) {
if (is_bool($permisosBitacoras)) {
return $permisosBitacoras;
}
return !empty($permisosBitacoras) && array_filter($permisosBitacoras);
}
// Contar bitácoras disponibles según permisos
function contarBitacorasDisponibles($permisosBitacoras) {
if (!is_array($permisosBitacoras)) {
return is_bool($permisosBitacoras) && $permisosBitacoras ? 1 : 0;
}
return count(array_filter($permisosBitacoras));
}
$cantidadBitacoras = contarBitacorasDisponibles($permisosBitacoras);
// Función para obtener el texto descriptivo según el permiso
function obtenerTextoPermiso($permiso, $tipoUsuario) {
$textos = [
'registro_accesos' => ($tipoUsuario === 'super_admin') ? 'Registro de accesos' : null,
'registro_usuarios' => ($tipoUsuario === 'super_admin') ? 'Registro de usuarios' : null,
'registro_agencias' => ($tipoUsuario === 'super_admin') ? 'Registro de agencias' : null,
'acceso_usuarios_agencias' => ($tipoUsuario === 'admin_agencia' || $tipoUsuario === 'agente_aduanal') ? 'Registros de accesos' : null,
'registro_vinculaciones' => ($tipoUsuario === 'admin_agencia' || $tipoUsuario === 'agente_aduanal') ? 'Vinculaciones (Agencia)' : null,
'mi_actividad' => in_array($tipoUsuario, ['super_admin', 'admin_agencia', 'agente_aduanal', 'importador']) ? 'Mi actividad' : null,
'vinculaciones_usuario' => ($tipoUsuario === 'importador') ? 'Mis vinculaciones' : null,
];
return $textos[$permiso] ?? ucfirst(str_replace('_', ' ', $permiso));
}
// Función para obtener el icono según el permiso
function obtenerIconoPermiso($permiso) {
$iconos = [
'registro_accesos' => '🌐',
'registro_usuarios' => '👥',
'registro_agencias' => '🏪',
'acceso_usuarios_agencia' => '👥',
'registro_vinculaciones' => '🔗',
'mi_actividad' => '👤',
'vinculaciones_usuario' => '🔗'
];
return $iconos[$permiso] ?? '📋';
}
// Función para obtener la URL según el permiso
function obtenerUrlPermiso($permiso) {
$urls = [
'registro_accesos' => '/IMPORTADORES/bitacoras/sistema',
'registro_usuarios' => '/IMPORTADORES/bitacoras/usuarios',
'registro_agencias' => '/IMPORTADORES/bitacoras/agencias',
'acceso_usuarios_agencia' => '/IMPORTADORES/bitacoras/sistemaAgencia',
'registro_vinculaciones' => '/IMPORTADORES/bitacoras/vinculaciones',
'mis_vinculaciones' => '/IMPORTADORES/bitacoras/misVinculaciones',
'mi_actividad' => '/IMPORTADORES/bitacoras/miAcceso',
'vinculaciones_usuario' => '/IMPORTADORES/bitacoras/vinculacionesUsuario'
];
return $urls[$permiso] ?? '/IMPORTADORES/bitacoras/index';
}
?>
<style>
@@ -15,7 +136,14 @@ if (!isset($_SESSION['usuario_id']) || $_SESSION['tipo_usuario'] !== 'importador
.nav .nav-link { font-weight: normal; color: #343a40; background-color: transparent; }
.nav .nav-link:hover,
.nav .nav-link.active { background-color: #e9ecef; color: #212529; }
.permission-scope { background-color: rgba(108, 117, 125, 0.2); }
.submenu-item:hover { background-color: rgba(233, 236, 239, 0.8) !important; }
}
.user-badge { font-size: 0.75rem; padding: 2px 6px; border-radius: 3px; }
.permission-indicator { font-size: 0.7rem; color: #28a745; margin-left: 5px; }
.permission-scope { font-size: 0.65rem; color: #6c757d; margin-left: 5px; padding: 1px 4px; background-color: rgba(108, 117, 125, 0.1); border-radius: 2px; }
.submenu-item { font-size: 0.9rem; padding-left: 1rem !important; }
.submenu-item:hover { background-color: rgba(73, 80, 87, 0.7) !important; }
</style>
<!-- NAVBAR -->
@@ -25,9 +153,22 @@ if (!isset($_SESSION['usuario_id']) || $_SESSION['tipo_usuario'] !== 'importador
<button class="btn btn-outline-light d-md-none me-2" type="button" data-bs-toggle="offcanvas" data-bs-target="#sidebarMenu">
</button>
<span class="navbar-brand">SIIH | Configuración</span>
<span class="navbar-brand">
SIIH | Configuración
<span class="user-badge bg-info text-dark ms-2"><?= strtoupper(str_replace('_', ' ', $tipoUsuario)) ?></span>
</span>
<div class="d-flex ms-auto">
<a href="/IMPORTADORES/importadores/dashboard" class="btn btn-outline-light btn-sm" style="margin: 0 0 0 25px;">
<?php
// Definir la URL de regreso según el tipo de usuario
$urlRegreso = match($tipoUsuario) {
'super_admin' => '/IMPORTADORES/administrador/dashboard',
'admin_agencia' => '/IMPORTADORES/agencias/dashboard',
'agente_aduanal' => '/IMPORTADORES/agentes/dashboard',
'importador' => '/IMPORTADORES/importadores/dashboard',
default => '/IMPORTADORES/importadores/dashboard'
};
?>
<a href="<?= $urlRegreso ?>" class="btn btn-outline-light btn-sm" style="margin: 0 0 0 25px;">
← Panel Principal
</a>
</div>
@@ -41,118 +182,148 @@ if (!isset($_SESSION['usuario_id']) || $_SESSION['tipo_usuario'] !== 'importador
<!-- Definimos variables de ubicación -->
<?php
// Detecta si estamos en alguna parte de configuración
$esConfiguracion = str_contains($_SERVER['REQUEST_URI'], '/configuracion');
// Detecta si estamos en alguna ruta de bitácoras
$esVistaBitacoras = str_contains($_SERVER['REQUEST_URI'], '/bitacoras');
$esConfiguracion = str_contains($_SERVER['REQUEST_URI'], '/configuracion');
// Detecta si estamos en alguna parte de bitácoras
$esVistaBitacoras = str_contains($_SERVER['REQUEST_URI'], '/bitacoras');
// Detecta si estamos en alguna parta de seguridad
$esVistaSeguridad = str_contains($_SERVER['REQUEST_URI'], '/seguridad');
$esVistaSeguridad = str_contains($_SERVER['REQUEST_URI'], '/seguridad');
// Detecta si estamos en alguna parte de preferencias
$esVistaPreferencias = str_contains($_SERVER['REQUEST_URI'], '/preferencias');
$esVistaPreferencias = str_contains($_SERVER['REQUEST_URI'], '/preferencias');
// Detecta si estamos en alguna parte de automatizaciones
$esVistaAutomatizaciones = str_contains($_SERVER['REQUEST_URI'], '/automatizaciones');
?>
<!-- INFORMACIÓN GENERAL -->
<a href="/IMPORTADORES/configuracion/index"
class="nav-link px-3 py-2 <?= str_contains($_SERVER['REQUEST_URI'], '/configuracion/dashboard_configuracion') ? 'active' : '' ?>">
Información General
</a>
<?php if ($permisosUsuario['informacion_general']): ?>
<a href="/IMPORTADORES/configuracion/index"
class="nav-link px-3 py-2 <?= str_contains($_SERVER['REQUEST_URI'], '/configuracion/dashboard_configuracion') ? 'active' : '' ?>">
Información General
</a>
<?php endif; ?>
<!-- AUTOMATIZACIONES -->
<?php if ($esVistaAutomatizaciones): ?>
<?php $enDashboardAutomatizaciones = $_SERVER['REQUEST_URI'] === '/IMPORTADORES/automatizaciones/index'; ?>
<a href="/IMPORTADORES/automatizaciones/index"
class="nav-link px-3 py-2 <?= str_contains($_SERVER['REQUEST_URI'], '/automatizaciones/index') ? 'active' : '' ?>">
⚙️ Automatizaciones
</a>
<?php elseif ($esConfiguracion || $esVistaSeguridad || $esVistaPreferencias || $esVistaBitacoras): ?>
<a href="/IMPORTADORES/automatizaciones/index"
class="nav-link px-3 py-2 <?= str_contains($_SERVER['REQUEST_URI'], '/automatizaciones/index') ? 'active' : '' ?>">
⚙️ Automatizaciones
</a>
<?php if ($permisosUsuario['automatizaciones']): ?>
<?php if ($esVistaAutomatizaciones): ?>
<?php $enDashboardAutomatizaciones = $_SERVER['REQUEST_URI'] === '/IMPORTADORES/automatizaciones/index'; ?>
<a href="/IMPORTADORES/automatizaciones/index"
class="nav-link px-3 py-2 <?= str_contains($_SERVER['REQUEST_URI'], '/automatizaciones/index') ? 'active' : '' ?>">
⚙️ Automatizaciones
</a>
<?php elseif ($esConfiguracion || $esVistaSeguridad || $esVistaPreferencias || $esVistaBitacoras): ?>
<a href="/IMPORTADORES/automatizaciones/index"
class="nav-link px-3 py-2 <?= str_contains($_SERVER['REQUEST_URI'], '/automatizaciones/index') ? 'active' : '' ?>">
⚙️ Automatizaciones
</a>
<?php endif; ?>
<?php endif; ?>
<!-- PREFERENCIAS -->
<?php if ($esVistaPreferencias): ?>
<?php $enDashboardPreferencias = $_SERVER['REQUEST_URI'] === '/IMPORTADORES/preferencias/index'; ?>
<a class="nav-link px-3 py-2 d-flex justify-content-between align-items-center <?= $esVistaPreferencias ? 'active' : '' ?>"
href="<?= $enDashboardPreferencias ? '#submenuPreferencias' : '/IMPORTADORES/preferencias/index' ?>"
<?= $enDashboardPreferencias ? 'data-bd-toggle="collapse"' : '' ?>
role="button" aria-expanded="true" aria-controls="submenuPreferencias"
onclick="<? $enDashboardPreferencias ? '' : 'event.preventDefault(); window.location.href = \'/IMPORTADORES/preferencias/index\';' ?>">
✅ Preferencias
<span class="badge bg-secondary">1</span>
</a>
<div class="collapse show" id="submenuPreferencias">
<nav class="nav flex-column ms-3">
<a href="/IMPORTADORES/preferencias/notificaciones"
class="nav-link px-3 py-1 <?= $_SERVER['REQUEST_URI'] === '/IMPORTADORES/preferencias/notificaciones' ? 'active' : '' ?>">
• Gestión de Notificaciones
</a>
</nav>
</div>
<?php elseif ($esConfiguracion || $esVistaAutomatizaciones || $esVistaSeguridad || $esVistaBitacoras): ?>
<a href="/IMPORTADORES/preferencias/index"
class="nav-link px-3 py-2 d-flex justify-content-between align-items-center">
✅ Preferencias
<span class="badge bg-secondary">1</span>
</a>
<?php if ($permisosUsuario['preferencias']): ?>
<?php if ($esVistaPreferencias): ?>
<?php $enDashboardPreferencias = $_SERVER['REQUEST_URI'] === '/IMPORTADORES/preferencias/index'; ?>
<a class="nav-link px-3 py-2 d-flex justify-content-between align-items-center <?= $esVistaPreferencias ? 'active' : '' ?>"
href="<?= $enDashboardPreferencias ? '#submenuPreferencias' : '/IMPORTADORES/preferencias/index' ?>"
<?= $enDashboardPreferencias ? 'data-bd-toggle="collapse"' : '' ?>
role="button" aria-expanded="true" aria-controls="submenuPreferencias"
onclick="<? $enDashboardPreferencias ? '' : 'event.preventDefault(); window.location.href = \'/IMPORTADORES/preferencias/index\';' ?>">
✅ Preferencias
<span class="badge bg-secondary">1</span>
</a>
<div class="collapse show" id="submenuPreferencias">
<nav class="nav flex-column ms-3">
<a href="/IMPORTADORES/preferencias/notificaciones"
class="nav-link px-3 py-1 <?= $_SERVER['REQUEST_URI'] === '/IMPORTADORES/preferencias/notificaciones' ? 'active' : '' ?>">
• Gestión de Notificaciones
</a>
</nav>
</div>
<?php elseif ($esConfiguracion || $esVistaAutomatizaciones || $esVistaSeguridad || $esVistaBitacoras): ?>
<a href="/IMPORTADORES/preferencias/index"
class="nav-link px-3 py-2 d-flex justify-content-between align-items-center">
✅ Preferencias
<span class="badge bg-secondary">1</span>
</a>
<?php endif; ?>
<?php endif; ?>
<!-- SEGURIDAD -->
<?php if ($esVistaSeguridad): ?>
<?php $enDashboardSeguridad = $_SERVER['REQUEST_URI'] === '/IMPORTADORES/seguridad/index'; ?>
<a class="nav-link px-3 py-2 d-flex justify-content-between align-items-center <?= $esVistaSeguridad ? 'active' : '' ?>"
href="<?= $enDashboardSeguridad ? '#submenuSeguridad' : '/IMPORTADORES/seguridad/index' ?>"
<?= $enDashboardSeguridad ? 'data-bs-toggle="collapse"' : '' ?>
role="button" aria-expanded="true" aria-controls="submenuSeguridad"
onclick="<?= $enDashboardSeguridad ? '' : 'event.preventDefault(); window.location.href = \'/IMPORTADORES/seguridad/index\';' ?>">
👮 Seguridad
<span class="badge bg-secondary">1</span>
</a>
<div class="collapse show" id="submenuSeguridad">
<nav class="nav flex-column ms-3">
<a href="/IMPORTADORES/seguridad/opciones"
class="nav-link px-3 py-1 <?= $_SERVER['REQUEST_URI'] === '/IMPORTADORES/seguridad/opciones' ? 'active' : '' ?>">
• Opciones de Seguridad
</a>
</nav>
</div>
<?php elseif ($esConfiguracion || $esVistaAutomatizaciones || $esVistaPreferencias || $esVistaBitacoras): ?>
<a href="/IMPORTADORES/seguridad/index"
class="nav-link px-3 py-2 d-flex justify-content-between align-items-center">
👮 Seguridad
<span class="badge bg-secondary">1</span>
</a>
<?php if ($permisosUsuario['seguridad']): ?>
<?php if ($esVistaSeguridad): ?>
<?php $enDashboardSeguridad = $_SERVER['REQUEST_URI'] === '/IMPORTADORES/seguridad/index'; ?>
<a class="nav-link px-3 py-2 d-flex justify-content-between align-items-center <?= $esVistaSeguridad ? 'active' : '' ?>"
href="<?= $enDashboardSeguridad ? '#submenuSeguridad' : '/IMPORTADORES/seguridad/index' ?>"
<?= $enDashboardSeguridad ? 'data-bs-toggle="collapse"' : '' ?>
role="button" aria-expanded="true" aria-controls="submenuSeguridad"
onclick="<?= $enDashboardSeguridad ? '' : 'event.preventDefault(); window.location.href = \'/IMPORTADORES/seguridad/index\';' ?>">
👮 Seguridad
<span class="badge bg-secondary">1</span>
</a>
<div class="collapse show" id="submenuSeguridad">
<nav class="nav flex-column ms-3">
<a href="/IMPORTADORES/seguridad/opciones"
class="nav-link px-3 py-1 <?= $_SERVER['REQUEST_URI'] === '/IMPORTADORES/seguridad/opciones' ? 'active' : '' ?>">
• Opciones de Seguridad
</a>
</nav>
</div>
<?php elseif ($esConfiguracion || $esVistaAutomatizaciones || $esVistaPreferencias || $esVistaBitacoras): ?>
<a href="/IMPORTADORES/seguridad/index"
class="nav-link px-3 py-2 d-flex justify-content-between align-items-center">
👮 Seguridad
<span class="badge bg-secondary">1</span>
</a>
<?php endif; ?>
<?php endif; ?>
<!-- BITÁCORAS -->
<?php if ($esVistaBitacoras): ?>
<!-- BITÁCORAS CON PERMISOS ESPECÍFICOS -->
<?php if (tieneAccesoBitacoras($permisosBitacoras)): ?>
<?php $enDashboardBitacoras = $_SERVER['REQUEST_URI'] === '/IMPORTADORES/bitacoras/index'; ?>
<!-- Estamos en cualquier página relacionada a bitácoras -->
<a class="nav-link px-3 py-2 d-flex justify-content-between align-items-center <?= $esVistaBitacoras ? 'active' : '' ?>"
href="<?= $enDashboardBitacoras ? '#submenuBitacoras' : '/IMPORTADORES/bitacoras/index' ?>"
<?= $enDashboardBitacoras ? 'data-bs-toggle="collapse"' : '' ?>
role="button" aria-expanded="true" aria-controls="submenuBitacoras"
onclick="<?= $enDashboardBitacoras ? '' : 'event.preventDefault(); window.location.href = \'/IMPORTADORES/bitacoras/index\';' ?>">
📖 Bitácoras
<span class="badge bg-secondary">1</span>
<span class="badge bg-secondary"><?= $cantidadBitacoras ?></span>
</a>
<div class="collapse show" id="submenuBitacoras">
<div class="collapse <?= $esVistaBitacoras ? 'show' : '' ?>" id="submenuBitacoras">
<nav class="nav flex-column ms-3">
<a href="/IMPORTADORES/bitacoras/lista"
class="nav-link px-3 py-1 <?= $_SERVER['REQUEST_URI'] === '/IMPORTADORES/bitacoras/lista' ? 'active' : '' ?>">
• Registro de Accesos
</a>
<?php foreach ($permisosBitacoras as $permiso => $tienePermiso): ?>
<?php if ($tienePermiso): ?>
<?php
$url = obtenerUrlPermiso($permiso);
$texto = obtenerTextoPermiso($permiso, $tipoUsuario);
$icono = obtenerIconoPermiso($permiso);
$esActivo = $_SERVER['REQUEST_URI'] === $url;
// Scope según dashboard
$scope = '';
if ($tipoUsuario === 'super_admin') {
$scope = 'Global';
} elseif ($tipoUsuario === 'admin_agencia' || $tipoUsuario === 'agente_aduanal') {
if (in_array($permiso, ['acceso_usuarios_agencia', 'registro_vinculaciones'])) {
$scope = 'Agencia';
} elseif (in_array($permiso, ['mis_vinculaciones', 'mi_actividad'])) {
$scope = 'Personal';
}
} elseif ($tipoUsuario === 'importador') {
$scope = 'Personal';
}
?>
<a href="<?= $url ?>"
class="nav-link submenu-item px-3 py-2 d-flex justify-content-between align-items-center <?= $esActivo ? 'active' : '' ?>">
<span>
<?= $icono ?> <?= htmlspecialchars($texto) ?>
<?php if ($scope): ?>
<span class="permission-scope"><?= $scope ?></span>
<?php endif; ?>
</span>
</a>
<?php endif; ?>
<?php endforeach; ?>
</nav>
</div>
<?php elseif ($esConfiguracion || $esVistaAutomatizaciones || $esVistaSeguridad || $esVistaPreferencias): ?>
<!-- Si estamos en configuración, pero no en bitácoras -->
<a href="/IMPORTADORES/bitacoras/index"
class="nav-link px-3 py-2 d-flex justify-content-between align-items-center">
📖 Bitácoras
<span class="badge bg-secondary">1</span>
</a>
<?php endif; ?>
</nav>
@@ -167,106 +338,136 @@ if (!isset($_SESSION['usuario_id']) || $_SESSION['tipo_usuario'] !== 'importador
<div class="offcanvas-body">
<!-- INFORMACIÓN GENERAL -->
<a href="/IMPORTADORES/configuracion/index"
class="nav-link px-3 py-2 <?= str_contains($_SERVER['REQUEST_URI'], '/configuracion/index') ? 'active' : '' ?>">
Información General
</a>
<?php if ($permisosUsuario['informacion_general']): ?>
<a href="/IMPORTADORES/configuracion/index"
class="nav-link px-3 py-2 <?= str_contains($_SERVER['REQUEST_URI'], '/configuracion/dashboard_configuracion') ? 'active' : '' ?>">
Información General
</a>
<?php endif; ?>
<!-- AUTOMATIZACIONES -->
<?php if ($esVistaAutomatizaciones): ?>
<?php $enDashboardAutomatizaciones = $_SERVER['REQUEST_URI'] === '/IMPORTADORES/automatizaciones/index'; ?>
<a href="/IMPORTADORES/automatizaciones/index"
class="nav-link px-3 py-2 <?= str_contains($_SERVER['REQUEST_URI'], '/automatizaciones/index') ? 'active' : '' ?>">
⚙️ Automatizaciones
</a>
<?php elseif ($esConfiguracion || $esVistaSeguridad || $esVistaPreferencias || $esVistaBitacoras): ?>
<a href="/IMPORTADORES/automatizaciones/index"
class="nav-link px-3 py-2 <?= str_contains($_SERVER['REQUEST_URI'], '/automatizaciones/index') ? 'active' : '' ?>">
⚙️ Automatizaciones
</a>
<?php if ($permisosUsuario['automatizaciones']): ?>
<?php if ($esVistaAutomatizaciones): ?>
<?php $enDashboardAutomatizaciones = $_SERVER['REQUEST_URI'] === '/IMPORTADORES/automatizaciones/index'; ?>
<a href="/IMPORTADORES/automatizaciones/index"
class="nav-link px-3 py-2 <?= str_contains($_SERVER['REQUEST_URI'], '/automatizaciones/index') ? 'active' : '' ?>">
⚙️ Automatizaciones
</a>
<?php elseif ($esConfiguracion || $esVistaSeguridad || $esVistaPreferencias || $esVistaBitacoras): ?>
<a href="/IMPORTADORES/automatizaciones/index"
class="nav-link px-3 py-2 <?= str_contains($_SERVER['REQUEST_URI'], '/automatizaciones/index') ? 'active' : '' ?>">
⚙️ Automatizaciones
</a>
<?php endif; ?>
<?php endif; ?>
<!-- PREFERENCIAS -->
<?php if ($esVistaPreferencias): ?>
<?php $enDashboardPreferencias = $_SERVER['REQUEST_URI'] === '/IMPORTADORES/preferencias/index'; ?>
<a class="nav-link px-3 py-2 d-flex justify-content-between align-items-center <?= $esVistaPreferencias ? 'active' : '' ?>"
href="<?= $enDashboardPreferencias ? '#submenuPreferencias' : '/IMPORTADORES/preferencias/index' ?>"
<?= $enDashboardPreferencias ? 'data-bd-toggle="collapse"' : '' ?>
role="button" aria-expanded="true" aria-controls="submenuPreferencias"
onclick="<? $enDashboardPreferencias ? '' : 'event.preventDefault(); window.location.href = \'/IMPORTADORES/preferencias/index\';' ?>">
✅ Preferencias
<span class="badge bg-secondary">1</span>
</a>
<div class="collapse show" id="submenuPreferencias">
<nav class="nav flex-column ms-3">
<a href="/IMPORTADORES/preferencias/notificaciones"
class="nav-link px-3 py-1 <?= $_SERVER['REQUEST_URI'] === '/IMPORTADORES/preferencias/notificaciones' ? 'active' : '' ?>">
• Gestión de Notificaciones
</a>
</nav>
</div>
<?php elseif ($esConfiguracion || $esVistaAutomatizaciones || $esVistaSeguridad || $esVistaBitacoras): ?>
<a href="/IMPORTADORES/preferencias/index"
class="nav-link px-3 py-2 d-flex justify-content-between align-items-center">
✅ Preferencias
<span class="badge bg-secondary">1</span>
</a>
<?php if ($permisosUsuario['preferencias']): ?>
<?php if ($esVistaPreferencias): ?>
<?php $enDashboardPreferencias = $_SERVER['REQUEST_URI'] === '/IMPORTADORES/preferencias/index'; ?>
<a class="nav-link px-3 py-2 d-flex justify-content-between align-items-center <?= $esVistaPreferencias ? 'active' : '' ?>"
href="<?= $enDashboardPreferencias ? '#submenuPreferencias' : '/IMPORTADORES/preferencias/index' ?>"
<?= $enDashboardPreferencias ? 'data-bd-toggle="collapse"' : '' ?>
role="button" aria-expanded="true" aria-controls="submenuPreferencias"
onclick="<? $enDashboardPreferencias ? '' : 'event.preventDefault(); window.location.href = \'/IMPORTADORES/preferencias/index\';' ?>">
✅ Preferencias
<span class="badge bg-secondary">1</span>
</a>
<div class="collapse show" id="submenuPreferencias">
<nav class="nav flex-column ms-3">
<a href="/IMPORTADORES/preferencias/notificaciones"
class="nav-link px-3 py-1 <?= $_SERVER['REQUEST_URI'] === '/IMPORTADORES/preferencias/notificaciones' ? 'active' : '' ?>">
• Gestión de Notificaciones
</a>
</nav>
</div>
<?php elseif ($esConfiguracion || $esVistaAutomatizaciones || $esVistaSeguridad || $esVistaBitacoras): ?>
<a href="/IMPORTADORES/preferencias/index"
class="nav-link px-3 py-2 d-flex justify-content-between align-items-center">
✅ Preferencias
<span class="badge bg-secondary">1</span>
</a>
<?php endif; ?>
<?php endif; ?>
<!-- SEGURIDAD -->
<?php if ($esVistaSeguridad): ?>
<?php $enDashboardSeguridad = $_SERVER['REQUEST_URI'] === '/IMPORTADORES/seguridad/index'; ?>
<a class="nav-link px-3 py-2 d-flex justify-content-between align-items-center <?= $esVistaSeguridad ? 'active' : '' ?>"
href="<?= $enDashboardSeguridad ? '#submenuSeguridad' : '/IMPORTADORES/seguridad/index' ?>"
<?= $enDashboardSeguridad ? 'data-bs-toggle="collapse"' : '' ?>
role="button" aria-expanded="true" aria-controls="submenuSeguridad"
onclick="<?= $enDashboardSeguridad ? '' : 'event.preventDefault(); window.location.href = \'/IMPORTADORES/seguridad/index\';' ?>">
👮 Seguridad
<span class="badge bg-secondary">1</span>
</a>
<div class="collapse show" id="submenuSeguridad">
<nav class="nav flex-column ms-3">
<a href="/IMPORTADORES/seguridad/opciones"
class="nav-link px-3 py-1 <?= $_SERVER['REQUEST_URI'] === '/IMPORTADORES/seguridad/opciones' ? 'active' : '' ?>">
• Opciones de Seguridad
</a>
</nav>
</div>
<?php elseif ($esConfiguracion || $esVistaAutomatizaciones || $esVistaPreferencias || $esVistaBitacoras): ?>
<a href="/IMPORTADORES/seguridad/index"
class="nav-link px-3 py-2 d-flex justify-content-between align-items-center">
👮 Seguridad
<span class="badge bg-secondary">1</span>
</a>
<?php if ($permisosUsuario['seguridad']): ?>
<?php if ($esVistaSeguridad): ?>
<?php $enDashboardSeguridad = $_SERVER['REQUEST_URI'] === '/IMPORTADORES/seguridad/index'; ?>
<a class="nav-link px-3 py-2 d-flex justify-content-between align-items-center <?= $esVistaSeguridad ? 'active' : '' ?>"
href="<?= $enDashboardSeguridad ? '#submenuSeguridad' : '/IMPORTADORES/seguridad/index' ?>"
<?= $enDashboardSeguridad ? 'data-bs-toggle="collapse"' : '' ?>
role="button" aria-expanded="true" aria-controls="submenuSeguridad"
onclick="<?= $enDashboardSeguridad ? '' : 'event.preventDefault(); window.location.href = \'/IMPORTADORES/seguridad/index\';' ?>">
👮 Seguridad
<span class="badge bg-secondary">1</span>
</a>
<div class="collapse show" id="submenuSeguridad">
<nav class="nav flex-column ms-3">
<a href="/IMPORTADORES/seguridad/opciones"
class="nav-link px-3 py-1 <?= $_SERVER['REQUEST_URI'] === '/IMPORTADORES/seguridad/opciones' ? 'active' : '' ?>">
• Opciones de Seguridad
</a>
</nav>
</div>
<?php elseif ($esConfiguracion || $esVistaAutomatizaciones || $esVistaPreferencias || $esVistaBitacoras): ?>
<a href="/IMPORTADORES/seguridad/index"
class="nav-link px-3 py-2 d-flex justify-content-between align-items-center">
👮 Seguridad
<span class="badge bg-secondary">1</span>
</a>
<?php endif; ?>
<?php endif; ?>
<!-- BITÁCORAS -->
<?php if ($esVistaBitacoras): ?>
<!-- BITÁCORAS CON PERMISOS ESPECÍFICOS -->
<?php if (tieneAccesoBitacoras($permisosBitacoras)): ?>
<?php $enDashboardBitacoras = $_SERVER['REQUEST_URI'] === '/IMPORTADORES/bitacoras/index'; ?>
<!-- Estamos en cualquier página relacionada a bitácoras -->
<a class="nav-link px-3 py-2 d-flex justify-content-between align-items-center <?= $esVistaBitacoras ? 'active' : '' ?>"
href="<?= $enDashboardBitacoras ? '#submenuBitacoras' : '/IMPORTADORES/bitacoras/index' ?>"
<?= $enDashboardBitacoras ? 'data-bs-toggle="collapse"' : '' ?>
role="button" aria-expanded="true" aria-controls="submenuBitacoras"
onclick="<?= $enDashboardBitacoras ? '' : 'event.preventDefault(); window.location.href = \'/IMPORTADORES/bitacoras/index\';' ?>">
📖 Bitácoras
<span class="badge bg-secondary">1</span>
<span class="badge bg-secondary"><?= $cantidadBitacoras ?></span>
</a>
<div class="collapse show" id="submenuBitacoras">
<div class="collapse <?= $esVistaBitacoras ? 'show' : '' ?>" id="submenuBitacoras">
<nav class="nav flex-column ms-3">
<a href="/IMPORTADORES/bitacoras/lista"
class="nav-link px-3 py-1 <?= $_SERVER['REQUEST_URI'] === '/IMPORTADORES/bitacoras/lista' ? 'active' : '' ?>">
• Registro de Accesos
</a>
<?php foreach ($permisosBitacoras as $permiso => $tienePermiso): ?>
<?php if ($tienePermiso): ?>
<?php
$url = obtenerUrlPermiso($permiso);
$texto = obtenerTextoPermiso($permiso, $tipoUsuario);
$icono = obtenerIconoPermiso($permiso);
$esActivo = $_SERVER['REQUEST_URI'] === $url;
// Scope según dashboard
$scope = '';
if ($tipoUsuario === 'super_admin') {
$scope = 'Global';
} elseif ($tipoUsuario === 'admin_agencia' || $tipoUsuario === 'agente_aduanal') {
if (in_array($permiso, ['acceso_usuarios_agencia', 'registro_vinculaciones'])) {
$scope = 'Agencia';
} elseif (in_array($permiso, ['mis_vinculaciones', 'mi_actividad'])) {
$scope = 'Personal';
}
} elseif ($tipoUsuario === 'importador') {
$scope = 'Personal';
}
?>
<a href="<?= $url ?>"
class="nav-link submenu-item px-3 py-2 d-flex justify-content-between align-items-center <?= $esActivo ? 'active' : '' ?>">
<span>
<?= $icono ?> <?= htmlspecialchars($texto) ?>
<?php if ($scope): ?>
<span class="permission-scope"><?= $scope ?></span>
<?php endif; ?>
</span>
</a>
<?php endif; ?>
<?php endforeach; ?>
</nav>
</div>
<?php elseif ($esConfiguracion || $esVistaAutomatizaciones || $esVistaPreferencias || $esVistaSeguridad): ?>
<!-- Si estamos en configuración, pero no en bitácoras -->
<a href="/IMPORTADORES/bitacoras/index"
class="nav-link px-3 py-2 d-flex justify-content-between align-items-center">
📖 Bitácoras
<span class="badge bg-secondary">1</span>
</a>
<?php endif; ?>
</div>

View File

@@ -1,11 +1,3 @@
<!-- views/partials/sidebar_importador.php -->
<?php
if (!isset($_SESSION['usuario_id']) || $_SESSION['tipo_usuario'] !== 'importador') {
header('Location: /IMPORTADORES/login');
exit;
}
?>
<style>
.nav .nav-link { font-weight: normal; color: white; transition: all 0.3s ease; }
.nav .nav-link:hover,
@@ -46,35 +38,56 @@ if (!isset($_SESSION['usuario_id']) || $_SESSION['tipo_usuario'] !== 'importador
<!-- INICIO -->
<a href="/IMPORTADORES/importadores/dashboard"
class="nav-link px-3 py-2 <?= $_SERVER['REQUEST_URI'] === '/IMPORTADORES/importadores/dashboard' ? 'active' : '' ?>">
class="nav-link px-3 py-2 <?= str_contains($_SERVER['REQUEST_URI'], '/IMPORTADORES/importadores/dashboard') ? 'active' : '' ?>">
🏠 Inicio
</a>
<!-- LOCACIONES -->
<a href="/IMPORTADORES/importadores/lista"
class="nav-link px-3 py-2 <?= $_SERVER['REQUEST_URI'] === '/IMPORTADORES/importadores/lista' ? 'active' : '' ?>">
class="nav-link px-3 py-2 <?= str_contains($_SERVER['REQUEST_URI'], '/IMPORTADORES/importadores/lista') ? 'active' : '' ?>">
🗺️ Locaciones
</a>
<!-- VINCULACIONES -->
<a class="nav-link px-3 py-2 d-flex justify-content-between align-items-center
<?= str_contains($_SERVER['REQUEST_URI'], '/IMPORTADORES/vinculaciones') ? 'active' : '' ?>"
data-bs-toggle="collapse" href="#submenuVinculaciones" role="button" aria-expanded="false">
🔗 Vinculación
<span class="badge bg-secondary">2</span>
</a>
<div class="collapse <?= str_contains($_SERVER['REQUEST_URI'], '/IMPORTADORES/vinculaciones') ? 'show' : '' ?>" id="submenuVinculaciones">
<nav class="nav flex-column ms-3">
<a href="/IMPORTADORES/vinculaciones/vinculacionesUsuario"
class="nav-link px-3 py-2 <?= str_contains($_SERVER['REQUEST_URI'], '/IMPORTADORES/vinculaciones/vinculacionesUsuario') ? 'active' : '' ?>">
• Mis vinculaciones
</a>
<a href="/IMPORTADORES/vinculaciones/nuevaVinculacion"
class="nav-link px-3 py-2 <?= str_contains($_SERVER['REQUEST_URI'], '/IMPORTADORES/vinculaciones/nuevaVinculacion') ? 'active' : '' ?>">
• Nueva vinculación
</a>
</nav>
</div>
<!-- TRANSPORTISTAS -->
<a class="nav-link px-3 py-2 d-flex justify-content-between align-items-center
<?= str_contains($_SERVER['REQUEST_URI'], '/transportistas') ? 'active' : '' ?>"
<?= str_contains($_SERVER['REQUEST_URI'], '/IMPORTADORES/transportistas') ? 'active' : '' ?>"
data-bs-toggle="collapse" href="#submenuTransportistas" role="button" aria-expanded="false">
👨‍✈️ Transportistas
<span class="badge bg-secondary">3</span>
</a>
<div class="collapse <?= str_contains($_SERVER['REQUEST_URI'], '/transportistas') ? 'show' : '' ?>" id="submenuTransportistas">
<div class="collapse <?= str_contains($_SERVER['REQUEST_URI'], '/IMPORTADORES/transportistas') ? 'show' : '' ?>" id="submenuTransportistas">
<nav class="nav flex-column ms-3">
<a href="/IMPORTADORES/transportistas/lista"
class="nav-link px-3 py-1 <?= $_SERVER['REQUEST_URI'] === '/IMPORTADORES/transportistas/lista' ? 'active' : '' ?>">
class="nav-link px-3 py-1 <?= str_contains($_SERVER['REQUEST_URI'], '/IMPORTADORES/transportistas/lista') ? 'active' : '' ?>">
• Mis transportistas
</a>
<a href="/IMPORTADORES/transportistas/alta"
class="nav-link px-3 py-1 <?= str_contains($_SERVER['REQUEST_URI'], '/transportistas/alta') ? 'active' : '' ?>">
class="nav-link px-3 py-1 <?= str_contains($_SERVER['REQUEST_URI'], '/IMPORTADORES/transportistas/alta') ? 'active' : '' ?>">
• Alta de transportista
</a>
<a href="/IMPORTADORES/transportistas/bulk_upload"
class="nav-link px-3 py-1 <?= str_contains($_SERVER['REQUEST_URI'], '/transportistas/bulk_upload') ? 'active' : '' ?>">
class="nav-link px-3 py-1 <?= str_contains($_SERVER['REQUEST_URI'], '/IMPORTADORES/transportistas/bulk_upload') ? 'active' : '' ?>">
• Carga masiva
</a>
</nav>
@@ -82,23 +95,23 @@ if (!isset($_SESSION['usuario_id']) || $_SESSION['tipo_usuario'] !== 'importador
<!-- TRANSPORTES -->
<a class="nav-link px-3 py-2 d-flex justify-content-between align-items-center
<?= str_contains($_SERVER['REQUEST_URI'], '/transportes') ? 'active' : '' ?>"
<?= str_contains($_SERVER['REQUEST_URI'], '/IMPORTADORES/transportes') ? 'active' : '' ?>"
data-bs-toggle="collapse" href="#submenuTransportes" role="button" aria-expanded="false">
🚚 Transportes
<span class="badge bg-secondary">3</span>
</a>
<div class="collapse <?= str_contains($_SERVER['REQUEST_URI'], '/transportes') ? 'show' : '' ?>" id="submenuTransportes">
<div class="collapse <?= str_contains($_SERVER['REQUEST_URI'], '/IMPORTADORES/transportes') ? 'show' : '' ?>" id="submenuTransportes">
<nav class="nav flex-column ms-3">
<a href="/IMPORTADORES/transportes/lista"
class="nav-link px-3 py-1 <?= $_SERVER['REQUEST_URI'] === '/IMPORTADORES/transportes/lista' ? 'active' : '' ?>">
class="nav-link px-3 py-1 <?= str_contains($_SERVER['REQUEST_URI'], '/IMPORTADORES/transportes/lista') ? 'active' : '' ?>">
• Mis transportes
</a>
<a href="/IMPORTADORES/transportes/crear"
class="nav-link px-3 py-1 <?= str_contains($_SERVER['REQUEST_URI'], '/transportes/crear') ? 'active' : '' ?>">
class="nav-link px-3 py-1 <?= str_contains($_SERVER['REQUEST_URI'], '/IMPORTADORES/transportes/crear') ? 'active' : '' ?>">
• Nuevo transporte
</a>
<a href="/IMPORTADORES/transportes/masivo"
class="nav-link px-3 py-1 <?= str_contains($_SERVER['REQUEST_URI'], '/transportes/masivo') ? 'active' : '' ?>">
class="nav-link px-3 py-1 <?= str_contains($_SERVER['REQUEST_URI'], '/IMPORTADORES/transportes/masivo') ? 'active' : '' ?>">
• Carga masiva
</a>
</nav>
@@ -111,29 +124,31 @@ if (!isset($_SESSION['usuario_id']) || $_SESSION['tipo_usuario'] !== 'importador
🚛 Choferes
<span class="badge bg-secondary">2</span>
</a>
<div class="collapse <?= str_contains($_SERVER['REQUEST_URI'], '/choferes') ? 'show' : '' ?>" id="submenuChoferes">
<div class="collapse <?= str_contains($_SERVER['REQUEST_URI'], '/IMPORTADORES/choferes') ? 'show' : '' ?>" id="submenuChoferes">
<nav class="nav flex-column ms-3">
<a href="/IMPORTADORES/choferes/lista"
class="nav-link px-3 py-1 <?= $_SERVER['REQUEST_URI'] === '/IMPORTADORES/choferes/lista' ? 'active' : '' ?>">
class="nav-link px-3 py-1 <?= str_contains($_SERVER['REQUEST_URI'], '/IMPORTADORES/choferes/lista') ? 'active' : '' ?>">
• Mis choferes
</a>
<a href="/IMPORTADORES/choferes/crear"
class="nav-link px-3 py-1 <?= str_contains($_SERVER['REQUEST_URI'], '/choferes/crear') ? 'active' : '' ?>">
class="nav-link px-3 py-1 <?= str_contains($_SERVER['REQUEST_URI'], '/IMPORTADORES/choferes/crear') ? 'active' : '' ?>">
• Nuevo chofer
</a>
</nav>
</div>
<!-- PROVEEDORES -->
<a class="nav-link px-3 py-2 d-flex justify-content-between align-items-center <?= str_contains($_SERVER['REQUEST_URI'], '/IMPORTADORES/proveedores') ? 'active' : '' ?>"
data-bs-toggle="collapse" href="#submenuProveedores" role="button" aria-expanded="<?= str_contains($_SERVER['REQUEST_URI'], '/IMPORTADORES/proveedores') ? 'true' : 'false' ?>"
<a class="nav-link px-3 py-2 d-flex justify-content-between align-items-center
<?= str_contains($_SERVER['REQUEST_URI'], '/IMPORTADORES/proveedores') ? 'active' : '' ?>"
data-bs-toggle="collapse" href="#submenuProveedores" role="button"
aria-expanded="<?= str_contains($_SERVER['REQUEST_URI'], '/IMPORTADORES/proveedores') ? 'true' : 'false' ?>"
aria-controls="submenuProveedores">
🏭 Proveedores
<span class="badge bg-secondary">2</span>
</a>
<div class="collapse <?= str_contains($_SERVER['REQUEST_URI'], '/IMPORTADORES/proveedores') ? 'show' : '' ?>" id="submenuProveedores">
<nav class="nav flex-column ms-3">
<a href="/IMPORTADORES/proveedores" class="nav-link px-3 py-1 <?= $_SERVER['REQUEST_URI'] === '/IMPORTADORES/proveedores' ? 'active' : '' ?>">
<a href="/IMPORTADORES/proveedores" class="nav-link px-3 py-1 <?= str_contains($_SERVER['REQUEST_URI'], '/IMPORTADORES/proveedores') ? 'active' : '' ?>">
• Lista de Proveedores
</a>
<a href="/IMPORTADORES/proveedores/crear" class="nav-link px-3 py-1 <?= str_contains($_SERVER['REQUEST_URI'], '/IMPORTADORES/proveedores/crear') ? 'active' : '' ?>">
@@ -149,14 +164,14 @@ if (!isset($_SESSION['usuario_id']) || $_SESSION['tipo_usuario'] !== 'importador
📄 Solicitudes Importación
<span class="badge bg-secondary">2</span>
</a>
<div class="collapse <?= str_contains($_SERVER['REQUEST_URI'], '/solicitud_importacion') ? 'show' : '' ?>" id="submenuSolicitudes">
<div class="collapse <?= str_contains($_SERVER['REQUEST_URI'], '/IMPORTADORES/solicitud_importacion') ? 'show' : '' ?>" id="submenuSolicitudes">
<nav class="nav flex-column ms-3">
<a href="/IMPORTADORES/solicitud_importacion/lista"
class="nav-link px-3 py-1 <?= $_SERVER['REQUEST_URI'] === '/IMPORTADORES/solicitud_importacion/lista' ? 'active' : '' ?>">
• Mis solicitudes
</a>
<a href="/IMPORTADORES/solicitud_importacion/crear"
class="nav-link px-3 py-1 <?= str_contains($_SERVER['REQUEST_URI'], '/solicitud_importacion/crear') ? 'active' : '' ?>">
class="nav-link px-3 py-1 <?= str_contains($_SERVER['REQUEST_URI'], '/IMPORTADORES/solicitud_importacion/crear') ? 'active' : '' ?>">
• Nueva solicitud
</a>
</nav>
@@ -164,12 +179,12 @@ if (!isset($_SESSION['usuario_id']) || $_SESSION['tipo_usuario'] !== 'importador
<!-- EXPEDIENTE ELECTRÓNICO -->
<a class="nav-link px-3 py-2 d-flex justify-content-between align-items-center
<?= str_contains($_SERVER['REQUEST_URI'], '/expediente') ? 'active' : '' ?>"
<?= str_contains($_SERVER['REQUEST_URI'], '/IMPORTADORES/expediente') ? 'active' : '' ?>"
data-bs-toggle="collapse" href="#submenuExpediente" role="button" aria-expanded="false">
📁 Expediente Electrónico
<span class="badge bg-secondary">1</span>
</a>
<div class="collapse <?= str_contains($_SERVER['REQUEST_URI'], '/expediente') ? 'show' : '' ?>" id="submenuExpediente">
<div class="collapse <?= str_contains($_SERVER['REQUEST_URI'], '/IMPORTADORES/expediente') ? 'show' : '' ?>" id="submenuExpediente">
<nav class="nav flex-column ms-3">
<a href="/IMPORTADORES/expediente/index"
class="nav-link px-3 py-1 <?= $_SERVER['REQUEST_URI'] === '/IMPORTADORES/expediente/consultar' ? 'active' : '' ?>">
@@ -180,7 +195,7 @@ if (!isset($_SESSION['usuario_id']) || $_SESSION['tipo_usuario'] !== 'importador
<!-- CONFIGURACIÓN -->
<a href="/IMPORTADORES/configuracion"
class="nav-link px-3 py-2 <?= str_contains($_SERVER['REQUEST_URI'], '/configuracion') ? 'active' : '' ?>">
class="nav-link px-3 py-2 <?= str_contains($_SERVER['REQUEST_URI'], '/IMPORTADORES/configuracion') ? 'active' : '' ?>">
⚙️ Configuración
</a>
@@ -194,37 +209,59 @@ if (!isset($_SESSION['usuario_id']) || $_SESSION['tipo_usuario'] !== 'importador
<button type="button" class="btn-close" data-bs-dismiss="offcanvas"></button>
</div>
<div class="offcanvas-body">
<!-- INICIO -->
<a href="/IMPORTADORES/importadores/dashboard"
class="nav-link px-3 py-2 <?= $_SERVER['REQUEST_URI'] === '/IMPORTADORES/importadores/dashboard' ? 'active' : '' ?>">
class="nav-link px-3 py-2 <?= str_contains($_SERVER['REQUEST_URI'], '/IMPORTADORES/importadores/dashboard') ? 'active' : '' ?>">
🏠 Inicio
</a>
<!-- LOCACIONES -->
<a href="/IMPORTADORES/importadores/lista"
class="nav-link px-3 py-2 <?= $_SERVER['REQUEST_URI'] === '/IMPORTADORES/importadores/lista' ? 'active' : '' ?>">
class="nav-link px-3 py-2 <?= str_contains($_SERVER['REQUEST_URI'], '/IMPORTADORES/importadores/lista') ? 'active' : '' ?>">
🗺️ Locaciones
</a>
<!-- VINCULACIONES -->
<a class="nav-link px-3 py-2 d-flex justify-content-between align-items-center
<?= str_contains($_SERVER['REQUEST_URI'], '/IMPORTADORES/vinculaciones') ? 'active' : '' ?>"
data-bs-toggle="collapse" href="#submenuVinculaciones" role="button" aria-expanded="false">
🔗 Vinculación
<span class="badge bg-secondary">2</span>
</a>
<div class="collapse <?= str_contains($_SERVER['REQUEST_URI'], '/IMPORTADORES/vinculaciones') ? 'show' : '' ?>" id="submenuVinculaciones">
<nav class="nav flex-column ms-3">
<a href="/IMPORTADORES/vinculaciones/vinculacionesUsuario"
class="nav-link px-3 py-2 <?= str_contains($_SERVER['REQUEST_URI'], '/IMPORTADORES/vinculaciones/vinculacionesUsuario') ? 'active' : '' ?>">
• Mis vinculaciones
</a>
<a href="/IMPORTADORES/vinculaciones/nuevaVinculacion"
class="nav-link px-3 py-2 <?= str_contains($_SERVER['REQUEST_URI'], '/IMPORTADORES/vinculaciones/nuevaVinculacion') ? 'active' : '' ?>">
• Nueva vinculación
</a>
</nav>
</div>
<!-- TRANSPORTISTAS -->
<a class="nav-link px-3 py-2 d-flex justify-content-between align-items-center
<?= str_contains($_SERVER['REQUEST_URI'], '/transportistas') ? 'active' : '' ?>"
<?= str_contains($_SERVER['REQUEST_URI'], '/IMPORTADORES/transportistas') ? 'active' : '' ?>"
data-bs-toggle="collapse" href="#submenuTransportistas" role="button" aria-expanded="false">
👨‍✈️ Transportistas
<span class="badge bg-secondary">3</span>
</a>
<div class="collapse <?= str_contains($_SERVER['REQUEST_URI'], '/transportistas') ? 'show' : '' ?>" id="submenuTransportistas">
<div class="collapse <?= str_contains($_SERVER['REQUEST_URI'], '/IMPORTADORES/transportistas') ? 'show' : '' ?>" id="submenuTransportistas">
<nav class="nav flex-column ms-3">
<a href="/IMPORTADORES/transportistas/lista"
class="nav-link px-3 py-1 <?= $_SERVER['REQUEST_URI'] === '/IMPORTADORES/transportistas/lista' ? 'active' : '' ?>">
class="nav-link px-3 py-1 <?= str_contains($_SERVER['REQUEST_URI'], '/IMPORTADORES/transportistas/lista') ? 'active' : '' ?>">
• Mis transportistas
</a>
<a href="/IMPORTADORES/transportistas/alta"
class="nav-link px-3 py-1 <?= str_contains($_SERVER['REQUEST_URI'], '/transportistas/alta') ? 'active' : '' ?>">
class="nav-link px-3 py-1 <?= str_contains($_SERVER['REQUEST_URI'], '/IMPORTADORES/transportistas/alta') ? 'active' : '' ?>">
• Alta de transportista
</a>
<a href="/IMPORTADORES/transportistas/bulk_upload"
class="nav-link px-3 py-1 <?= str_contains($_SERVER['REQUEST_URI'], '/transportistas/bulk_upload') ? 'active' : '' ?>">
class="nav-link px-3 py-1 <?= str_contains($_SERVER['REQUEST_URI'], '/IMPORTADORES/transportistas/bulk_upload') ? 'active' : '' ?>">
• Carga masiva
</a>
</nav>
@@ -232,23 +269,23 @@ if (!isset($_SESSION['usuario_id']) || $_SESSION['tipo_usuario'] !== 'importador
<!-- TRANSPORTES -->
<a class="nav-link px-3 py-2 d-flex justify-content-between align-items-center
<?= str_contains($_SERVER['REQUEST_URI'], '/transportes') ? 'active' : '' ?>"
<?= str_contains($_SERVER['REQUEST_URI'], '/IMPORTADORES/transportes') ? 'active' : '' ?>"
data-bs-toggle="collapse" href="#submenuTransportes" role="button" aria-expanded="false">
🚚 Transportes
<span class="badge bg-secondary">3</span>
</a>
<div class="collapse <?= str_contains($_SERVER['REQUEST_URI'], '/transportes') ? 'show' : '' ?>" id="submenuTransportes">
<div class="collapse <?= str_contains($_SERVER['REQUEST_URI'], '/IMPORTADORES/transportes') ? 'show' : '' ?>" id="submenuTransportes">
<nav class="nav flex-column ms-3">
<a href="/IMPORTADORES/transportes/lista"
class="nav-link px-3 py-1 <?= $_SERVER['REQUEST_URI'] === '/IMPORTADORES/transportes/lista' ? 'active' : '' ?>">
class="nav-link px-3 py-1 <?= str_contains($_SERVER['REQUEST_URI'], '/IMPORTADORES/transportes/lista') ? 'active' : '' ?>">
• Mis transportes
</a>
<a href="/IMPORTADORES/transportes/crear"
class="nav-link px-3 py-1 <?= str_contains($_SERVER['REQUEST_URI'], '/transportes/crear') ? 'active' : '' ?>">
class="nav-link px-3 py-1 <?= str_contains($_SERVER['REQUEST_URI'], '/IMPORTADORES/transportes/crear') ? 'active' : '' ?>">
• Nuevo transporte
</a>
<a href="/IMPORTADORES/transportes/masivo"
class="nav-link px-3 py-1 <?= str_contains($_SERVER['REQUEST_URI'], '/transportes/masivo') ? 'active' : '' ?>">
class="nav-link px-3 py-1 <?= str_contains($_SERVER['REQUEST_URI'], '/IMPORTADORES/transportes/masivo') ? 'active' : '' ?>">
• Carga masiva
</a>
</nav>
@@ -261,29 +298,31 @@ if (!isset($_SESSION['usuario_id']) || $_SESSION['tipo_usuario'] !== 'importador
🚛 Choferes
<span class="badge bg-secondary">2</span>
</a>
<div class="collapse <?= str_contains($_SERVER['REQUEST_URI'], '/choferes') ? 'show' : '' ?>" id="submenuChoferes">
<div class="collapse <?= str_contains($_SERVER['REQUEST_URI'], '/IMPORTADORES/choferes') ? 'show' : '' ?>" id="submenuChoferes">
<nav class="nav flex-column ms-3">
<a href="/IMPORTADORES/choferes/lista"
class="nav-link px-3 py-1 <?= $_SERVER['REQUEST_URI'] === '/IMPORTADORES/choferes/lista' ? 'active' : '' ?>">
class="nav-link px-3 py-1 <?= str_contains($_SERVER['REQUEST_URI'], '/IMPORTADORES/choferes/lista') ? 'active' : '' ?>">
• Mis choferes
</a>
<a href="/IMPORTADORES/choferes/crear"
class="nav-link px-3 py-1 <?= str_contains($_SERVER['REQUEST_URI'], '/choferes/crear') ? 'active' : '' ?>">
class="nav-link px-3 py-1 <?= str_contains($_SERVER['REQUEST_URI'], '/IMPORTADORES/choferes/crear') ? 'active' : '' ?>">
• Nuevo chofer
</a>
</nav>
</div>
<!-- PROVEEDORES -->
<a class="nav-link px-3 py-2 d-flex justify-content-between align-items-center <?= str_contains($_SERVER['REQUEST_URI'], '/IMPORTADORES/proveedores') ? 'active' : '' ?>"
data-bs-toggle="collapse" href="#submenuProveedores" role="button" aria-expanded="<?= str_contains($_SERVER['REQUEST_URI'], '/IMPORTADORES/proveedores') ? 'true' : 'false' ?>"
<a class="nav-link px-3 py-2 d-flex justify-content-between align-items-center
<?= str_contains($_SERVER['REQUEST_URI'], '/IMPORTADORES/proveedores') ? 'active' : '' ?>"
data-bs-toggle="collapse" href="#submenuProveedores" role="button"
aria-expanded="<?= str_contains($_SERVER['REQUEST_URI'], '/IMPORTADORES/proveedores') ? 'true' : 'false' ?>"
aria-controls="submenuProveedores">
🏭 Proveedores
<span class="badge bg-secondary">2</span>
</a>
<div class="collapse <?= str_contains($_SERVER['REQUEST_URI'], '/IMPORTADORES/proveedores') ? 'show' : '' ?>" id="submenuProveedores">
<nav class="nav flex-column ms-3">
<a href="/IMPORTADORES/proveedores" class="nav-link px-3 py-1 <?= $_SERVER['REQUEST_URI'] === '/IMPORTADORES/proveedores' ? 'active' : '' ?>">
<a href="/IMPORTADORES/proveedores" class="nav-link px-3 py-1 <?= str_contains($_SERVER['REQUEST_URI'], '/IMPORTADORES/proveedores') ? 'active' : '' ?>">
• Lista de Proveedores
</a>
<a href="/IMPORTADORES/proveedores/crear" class="nav-link px-3 py-1 <?= str_contains($_SERVER['REQUEST_URI'], '/IMPORTADORES/proveedores/crear') ? 'active' : '' ?>">
@@ -299,14 +338,14 @@ if (!isset($_SESSION['usuario_id']) || $_SESSION['tipo_usuario'] !== 'importador
📄 Solicitudes Importación
<span class="badge bg-secondary">2</span>
</a>
<div class="collapse <?= str_contains($_SERVER['REQUEST_URI'], '/solicitud_importacion') ? 'show' : '' ?>" id="submenuSolicitudes">
<div class="collapse <?= str_contains($_SERVER['REQUEST_URI'], '/IMPORTADORES/solicitud_importacion') ? 'show' : '' ?>" id="submenuSolicitudes">
<nav class="nav flex-column ms-3">
<a href="/IMPORTADORES/solicitud_importacion/lista"
class="nav-link px-3 py-1 <?= $_SERVER['REQUEST_URI'] === '/IMPORTADORES/solicitud_importacion/lista' ? 'active' : '' ?>">
• Mis solicitudes
</a>
<a href="/IMPORTADORES/solicitud_importacion/crear"
class="nav-link px-3 py-1 <?= str_contains($_SERVER['REQUEST_URI'], '/solicitud_importacion/crear') ? 'active' : '' ?>">
class="nav-link px-3 py-1 <?= str_contains($_SERVER['REQUEST_URI'], '/IMPORTADORES/solicitud_importacion/crear') ? 'active' : '' ?>">
• Nueva solicitud
</a>
</nav>
@@ -314,12 +353,12 @@ if (!isset($_SESSION['usuario_id']) || $_SESSION['tipo_usuario'] !== 'importador
<!-- EXPEDIENTE ELECTRÓNICO -->
<a class="nav-link px-3 py-2 d-flex justify-content-between align-items-center
<?= str_contains($_SERVER['REQUEST_URI'], '/expediente') ? 'active' : '' ?>"
<?= str_contains($_SERVER['REQUEST_URI'], '/IMPORTADORES/expediente') ? 'active' : '' ?>"
data-bs-toggle="collapse" href="#submenuExpediente" role="button" aria-expanded="false">
📁 Expediente Electrónico
<span class="badge bg-secondary">1</span>
</a>
<div class="collapse <?= str_contains($_SERVER['REQUEST_URI'], '/expediente') ? 'show' : '' ?>" id="submenuExpediente">
<div class="collapse <?= str_contains($_SERVER['REQUEST_URI'], '/IMPORTADORES/expediente') ? 'show' : '' ?>" id="submenuExpediente">
<nav class="nav flex-column ms-3">
<a href="/IMPORTADORES/expediente/index"
class="nav-link px-3 py-1 <?= $_SERVER['REQUEST_URI'] === '/IMPORTADORES/expediente/consultar' ? 'active' : '' ?>">
@@ -330,7 +369,7 @@ if (!isset($_SESSION['usuario_id']) || $_SESSION['tipo_usuario'] !== 'importador
<!-- CONFIGURACIÓN -->
<a href="/IMPORTADORES/configuracion"
class="nav-link px-3 py-2 <?= str_contains($_SERVER['REQUEST_URI'], '/configuracion') ? 'active' : '' ?>">
class="nav-link px-3 py-2 <?= str_contains($_SERVER['REQUEST_URI'], '/IMPORTADORES/configuracion') ? 'active' : '' ?>">
⚙️ Configuración
</a>

View File

@@ -1,61 +0,0 @@
<style>
body { font-family: 'Segoe UI', sans-serif; }
.table td, .table th { vertical-align: middle; }
.sidebar { width: 220px; background-color: #343a40; position: fixed; top: 56px; bottom: 0; left: 0; z-index: 1045; padding-top: 1rem; height: 100vh; overflow-y: auto; border-right: 1px solid #dee2e6; }
.sidebar a { color: white; text-decoration: none; }
.sidebar .nav-link.active,
.sidebar .nav-link:hover { background-color: #495057; }
.content { margin-left: 230px; margin-top: 70px; }
@media (max-width: 991.98px) {
.sidebar { position: relative; width: 100%; height: auto; border-right: none; }
.content { margin-left: 0; margin-top: 20px; }
}
</style>
<!-- 🔷 NAVBAR -->
<nav class="navbar navbar-dark bg-dark fixed-top">
<div class="container-fluid">
<a class="navbar-brand" href="#">SIIH | Admin</a>
</div>
</nav>
<!-- SIDEBAR FIJA (escritorio) -->
<div class="d-none d-md-block bg-dark text-white position-fixed h-100 pt-5" style="width: 250px;">
<nav class="nav flex-column">
<a href="/IMPORTADORES/sistemas/alta_usuarios" class="nav-link px-3 py-2 <?= $_GET['url'] === 'sistemas/alta_usuarios' ? 'active' : '' ?>">👥 Usuarios</a>
<a href="/IMPORTADORES/sistemas/agencias_activas" class="nav-link px-3 py-2 <?= $_GET['url'] === 'sistemas/agencias_activas' ? 'active' : '' ?>">🏪 Agencias</a>
<a href="/IMPORTADORES/sistemas/bitacora_login" class="nav-link px-3 py-2 <?= $_GET['url'] === 'sistemas/bitacora_login' ? 'active' : '' ?>">🕵️ Bitácora Accesos</a>
<a href="/IMPORTADORES/sistemas/bitacora_usuarios" class="nav-link px-3 py-2 <?= $_GET['url'] === 'sistemas/bitacora_usuarios' ? 'active' : '' ?>">🛠️ Bitácora Usuarios</a>
<a href="#" class="nav-link px-3 py-2">📦 Configuración</a>
<a href="/IMPORTADORES/sistemas/logout" class="nav-link px-3 py-2 text-danger">🚪 Cerrar sesión</a>
</nav>
</div>
<!-- OFFCANVAS PARA MÓVILES -->
<div class="offcanvas offcanvas-start d-md-none" tabindex="-1" id="sidebarMenu" style="top: 56px;">
<div class="offcanvas-header">
<h5 class="offcanvas-title">Menú</h5>
<button type="button" class="btn-close" data-bs-dismiss="offcanvas"></button>
</div>
<div class="offcanvas-body">
<!-- INICIO -->
<a href="/IMPORTADORES/sistemas/alta_usuarios" class="nav-link px-3 py-2 <?= $_GET['url'] === 'sistemas/alta_usuarios' ? 'active' : '' ?>">👥 Usuarios</a>
<a href="/IMPORTADORES/sistemas/agencias_activas" class="nav-link px-3 py-2 <?= $_GET['url'] === 'sistemas/agencias_activas' ? 'active' : '' ?>">🏪 Agencias</a>
<a href="/IMPORTADORES/sistemas/bitacora_login" class="nav-link px-3 py-2 <?= $_GET['url'] === 'sistemas/bitacora_login' ? 'active' : '' ?>">🕵️ Bitácora Accesos</a>
<a href="/IMPORTADORES/sistemas/bitacora_usuarios" class="nav-link px-3 py-2 <?= $_GET['url'] === 'sistemas/bitacora_usuarios' ? 'active' : '' ?>">🛠️ Bitácora Usuarios</a>
<a href="#" class="nav-link px-3 py-2">📦 Configuración</a>
<a href="/IMPORTADORES/sistemas/logout" class="nav-link px-3 py-2 text-danger">🚪 Cerrar sesión</a>
</div>
</div>

View File

@@ -263,20 +263,20 @@
e.preventDefault(); // Prevenir envío por defecto
// Obtener todos los campos correctamente
const aduana = document.getElementById('aduana').value;
const patente = document.getElementById('patente').value;
const aduana = document.getElementById('aduana').value;
const patente = document.getElementById('patente').value;
const agente_aduanal = document.getElementById('agente_aduanal').value.trim();
const rfc = document.getElementById('rfc').value.trim();
const curp = document.getElementById('curp').value.trim();
const razon_social = document.getElementById('razon_social').value.trim();
const rfc = document.getElementById('rfc').value.trim();
const curp = document.getElementById('curp').value.trim();
const razon_social = document.getElementById('razon_social').value.trim();
// Expresiones regulares
const rfcRegex = /^[A-ZÑ&]{3,4}\d{6}[A-Z0-9]{3}$/;
const curpRegex = /^[A-Z]{4}[0-9]{6}[HM](AS|BC|BS|CC|CL|CM|CS|CH|DF|DG|GT|GR|HG|JC|MC|MN|MS|NT|NL|OC|PL|QT|QR|SP|SL|SR|TC|TS|TL|VZ|YN|ZS)[A-Z]{3}[A-Z0-9]{2}$/;
const rfcRegex = /^[A-ZÑ&]{3,4}\d{6}[A-Z0-9]{3}$/;
const curpRegex = /^[A-Z]{4}[0-9]{6}[HM](AS|BC|BS|CC|CL|CM|CS|CH|DF|DG|GT|GR|HG|JC|MC|MN|MS|NT|NL|OC|PL|QT|QR|SP|SL|SR|TC|TS|TL|VZ|YN|ZS)[A-Z]{3}[A-Z0-9]{2}$/;
const soloNumerosRegex = /^[0-9]+$/;
if (!aduana) {
Swal.fire({ icon: 'error', title: 'Campo requerido', text: 'La aduana es obligatoria.', confirmButtonColor: '#dc3545' });
Swal.fire({ icon: 'warning', title: 'Campo requerido', text: 'La aduana es obligatoria.', confirmButtonColor: '#dc3545' });
document.getElementById('aduana').focus();
return;
}
@@ -286,7 +286,7 @@
return;
}
if (!patente) {
Swal.fire({ icon: 'error', title: 'Campo requerido', text: 'La patente es obligatoria.', confirmButtonColor: '#dc3545' });
Swal.fire({ icon: 'warning', title: 'Campo requerido', text: 'La patente es obligatoria.', confirmButtonColor: '#dc3545' });
document.getElementById('patente').focus();
return;
}
@@ -296,12 +296,12 @@
return;
}
if (!agente_aduanal) {
Swal.fire({ icon: 'error', title: 'Campo requerido', text: 'El agente aduanal es obligatorio.', confirmButtonColor: '#dc3545' });
Swal.fire({ icon: 'warning', title: 'Campo requerido', text: 'El agente aduanal es obligatorio.', confirmButtonColor: '#dc3545' });
document.getElementById('agente_aduanal').focus();
return;
}
if (!rfc) {
Swal.fire({ icon: 'error', title: 'Campo requerido', text: 'El RFC es obligatorio.', confirmButtonColor: '#dc3545' });
Swal.fire({ icon: 'warning', title: 'Campo requerido', text: 'El RFC es obligatorio.', confirmButtonColor: '#dc3545' });
document.getElementById('rfc').focus();
return;
}
@@ -311,7 +311,7 @@
return;
}
if (!curp) {
Swal.fire({ icon: 'error', title: 'Campo requerido', text: 'El CURP es obligatorio.', confirmButtonColor: '#dc3545' });
Swal.fire({ icon: 'warning', title: 'Campo requerido', text: 'El CURP es obligatorio.', confirmButtonColor: '#dc3545' });
document.getElementById('curp').focus();
return;
}
@@ -322,7 +322,7 @@
return;
}
if (!razon_social) {
Swal.fire({ icon: 'error', title: 'Campo requerido', text: 'La razón social es obligatoria.', confirmButtonColor: '#dc3545' });
Swal.fire({ icon: 'warning', title: 'Campo requerido', text: 'La razón social es obligatoria.', confirmButtonColor: '#dc3545' });
document.getElementById('razon_social').focus();
return;
}

View File

@@ -286,20 +286,20 @@
e.preventDefault(); // Prevenir envío por defecto
// Obtener todos los campos correctamente
const aduana = document.getElementById('aduana').value;
const patente = document.getElementById('patente').value;
const aduana = document.getElementById('aduana').value;
const patente = document.getElementById('patente').value;
const agente_aduanal = document.getElementById('agente_aduanal').value.trim();
const rfc = document.getElementById('rfc').value.trim();
const curp = document.getElementById('curp').value.trim();
const razon_social = document.getElementById('razon_social').value.trim();
const rfc = document.getElementById('rfc').value.trim();
const curp = document.getElementById('curp').value.trim();
const razon_social = document.getElementById('razon_social').value.trim();
// Expresiones regulares
const rfcRegex = /^[A-ZÑ&]{3,4}\d{6}[A-Z0-9]{3}$/;
const curpRegex = /^[A-Z]{4}[0-9]{6}[HM](AS|BC|BS|CC|CL|CM|CS|CH|DF|DG|GT|GR|HG|JC|MC|MN|MS|NT|NL|OC|PL|QT|QR|SP|SL|SR|TC|TS|TL|VZ|YN|ZS)[A-Z]{3}[A-Z0-9]{2}$/;
const rfcRegex = /^[A-ZÑ&]{3,4}\d{6}[A-Z0-9]{3}$/;
const curpRegex = /^[A-Z]{4}[0-9]{6}[HM](AS|BC|BS|CC|CL|CM|CS|CH|DF|DG|GT|GR|HG|JC|MC|MN|MS|NT|NL|OC|PL|QT|QR|SP|SL|SR|TC|TS|TL|VZ|YN|ZS)[A-Z]{3}[A-Z0-9]{2}$/;
const soloNumerosRegex = /^[0-9]+$/;
if (!aduana) {
Swal.fire({ icon: 'error', title: 'Campo requerido', text: 'La aduana es obligatoria.', confirmButtonColor: '#dc3545' });
Swal.fire({ icon: 'warning', title: 'Campo requerido', text: 'La aduana es obligatoria.', confirmButtonColor: '#dc3545' });
document.getElementById('aduana').focus();
return;
}
@@ -309,7 +309,7 @@
return;
}
if (!patente) {
Swal.fire({ icon: 'error', title: 'Campo requerido', text: 'La patente es obligatoria.', confirmButtonColor: '#dc3545' });
Swal.fire({ icon: 'warning', title: 'Campo requerido', text: 'La patente es obligatoria.', confirmButtonColor: '#dc3545' });
document.getElementById('patente').focus();
return;
}
@@ -319,12 +319,12 @@
return;
}
if (!agente_aduanal) {
Swal.fire({ icon: 'error', title: 'Campo requerido', text: 'El agente aduanal es obligatorio.', confirmButtonColor: '#dc3545' });
Swal.fire({ icon: 'warning', title: 'Campo requerido', text: 'El agente aduanal es obligatorio.', confirmButtonColor: '#dc3545' });
document.getElementById('agente_aduanal').focus();
return;
}
if (!rfc) {
Swal.fire({ icon: 'error', title: 'Campo requerido', text: 'El RFC es obligatorio.', confirmButtonColor: '#dc3545' });
Swal.fire({ icon: 'warning', title: 'Campo requerido', text: 'El RFC es obligatorio.', confirmButtonColor: '#dc3545' });
document.getElementById('rfc').focus();
return;
}
@@ -334,7 +334,7 @@
return;
}
if (!curp) {
Swal.fire({ icon: 'error', title: 'Campo requerido', text: 'El CURP es obligatorio.', confirmButtonColor: '#dc3545' });
Swal.fire({ icon: 'warning', title: 'Campo requerido', text: 'El CURP es obligatorio.', confirmButtonColor: '#dc3545' });
document.getElementById('curp').focus();
return;
}
@@ -345,7 +345,7 @@
return;
}
if (!razon_social) {
Swal.fire({ icon: 'error', title: 'Campo requerido', text: 'La razón social es obligatoria.', confirmButtonColor: '#dc3545' });
Swal.fire({ icon: 'warning', title: 'Campo requerido', text: 'La razón social es obligatoria.', confirmButtonColor: '#dc3545' });
document.getElementById('razon_social').focus();
return;
}

View File

@@ -103,27 +103,27 @@
e.preventDefault(); // Prevenir envío por defecto
// Obtener todos los campos
const clave = document.getElementById('Clave').value.trim();
const nombre = document.getElementById('Nombre').value.trim();
const rfc = document.getElementById('RFC').value.trim().toUpperCase();
const ciudad = document.getElementById('Ciudad').value.trim();
const telefono = document.getElementById('Telefono').value.trim();
const pais = document.getElementById('Pais').value.trim();
const clave = document.getElementById('Clave').value.trim();
const nombre = document.getElementById('Nombre').value.trim();
const rfc = document.getElementById('RFC').value.trim().toUpperCase();
const ciudad = document.getElementById('Ciudad').value.trim();
const telefono = document.getElementById('Telefono').value.trim();
const pais = document.getElementById('Pais').value.trim();
const entidadFederativa = document.getElementById('EntidadFederativa').value.trim();
const municipio = document.getElementById('Municipio').value.trim();
const colonia = document.getElementById('Colonia').value.trim();
const codigoPostal = document.getElementById('CodigoPostal').value.trim();
const municipio = document.getElementById('Municipio').value.trim();
const colonia = document.getElementById('Colonia').value.trim();
const codigoPostal = document.getElementById('CodigoPostal').value.trim();
// Expresiones regulares
const rfcRegex = /^[A-ZÑ&]{3,4}\d{6}[A-Z0-9]{3}$/;
const telefonoRegex = /^\d{3}\s\d{7}$/;
const rfcRegex = /^[A-ZÑ&]{3,4}\d{6}[A-Z0-9]{3}$/;
const telefonoRegex = /^\d{3}\s\d{7}$/;
const codigoPostalRegex = /^\d{5}$/;
const soloLetrasRegex = /^[a-zA-ZáéíóúÁÉÍÓÚñÑ\s]+$/;
const soloLetrasRegex = /^[a-zA-ZáéíóúÁÉÍÓÚñÑ\s]+$/;
const alfanumericoRegex = /^[a-zA-Z0-9áéíóúÁÉÍÓÚñÑ\s]+$/;
// VALIDAR CAMPOS OBLIGATORIOS
if (!clave) {
Swal.fire({ icon: 'error', title: 'Clave requerida', text: 'Por favor ingresa la clave del proveedor.', confirmButtonColor: '#dc3545' });
Swal.fire({ icon: 'warning', title: 'Clave requerida', text: 'Por favor ingresa la clave del proveedor.', confirmButtonColor: '#dc3545' });
document.getElementById('Clave').focus();
return;
}
@@ -139,7 +139,7 @@
return;
}
if (!nombre) {
Swal.fire({ icon: 'error', title: 'Nombre requerido', text: 'Por favor ingresa el nombre del proveedor.', confirmButtonColor: '#dc3545' });
Swal.fire({ icon: 'warning', title: 'Nombre requerido', text: 'Por favor ingresa el nombre del proveedor.', confirmButtonColor: '#dc3545' });
document.getElementById('Nombre').focus();
return;
}
@@ -149,7 +149,7 @@
return;
}
if (!rfc) {
Swal.fire({ icon: 'error', title: 'RFC requerido', text: 'Por favor ingresa el RFC del proveedor.', confirmButtonColor: '#dc3545' });
Swal.fire({ icon: 'warning', title: 'RFC requerido', text: 'Por favor ingresa el RFC del proveedor.', confirmButtonColor: '#dc3545' });
document.getElementById('RFC').focus();
return;
}
@@ -159,7 +159,7 @@
return;
}
if (!ciudad) {
Swal.fire({ icon: 'error', title: 'Ciudad requerida', text: 'Por favor ingresa la ciudad del proveedor.', confirmButtonColor: '#dc3545' });
Swal.fire({ icon: 'warning', title: 'Ciudad requerida', text: 'Por favor ingresa la ciudad del proveedor.', confirmButtonColor: '#dc3545' });
document.getElementById('Ciudad').focus();
return;
}

View File

@@ -42,6 +42,12 @@ $color2 = explode(',', $config['colores_primarios'] ?? '#003366,#0055A5')
footer { background-color: #e9ecef; padding: 20px; text-align: center; font-size: 14px; color: #666; }
.recaptcha { margin-top: 20px; }
.logo-siih { height: 90px; margin-right: 10px; }
.form-toggle { margin-bottom: 2rem; text-align: center; }
.form-toggle .btn { margin: 0 0.5rem; min-width: 150px; }
.form-container { display: none; }
.form-container.active { display: block; }
.form-title { text-align: center; margin-bottom: 2rem; color: #333; }
.transition-fade { transition: opacity 0.3s ease-in-out; }
</style>
</head>
<body>
@@ -72,44 +78,109 @@ $color2 = explode(',', $config['colores_primarios'] ?? '#003366,#0055A5')
</div>
</nav>
<!-- Contenedor del formulario -->
<div class="container card-form">
<form action="/IMPORTADORES/registro/enviar" method="POST" enctype="multipart/form-data" id="formRegistro">
<div class="row g-3">
<div class="col-md-6">
<label for="company_name" class="form-label">Nombre de la empresa *</label>
<input type="text" class="form-control" name="company_name" required>
</div>
<div class="col-md-6">
<label for="rfc" class="form-label">RFC *</label>
<input type="text" class="form-control" name="rfc" maxlength="13" required>
</div>
<div class="col-md-6">
<label for="email" class="form-label">Correo electrónico *</label>
<input type="email" class="form-control" name="email" required>
</div>
<div class="col-md-6">
<label for="phone" class="form-label">Teléfono *</label>
<input type="text" class="form-control" name="phone" required>
</div>
<div class="col-md-12">
<label for="opinion_file" class="form-label">Padrón de importadores o encargo conferido (PDF) *</label>
<input type="file" class="form-control" name="opinion_file" id="opinion_file" accept=".pdf" required>
</div>
<div class="col-md-12 text-center recaptcha">
<div class="g-recaptcha" data-sitekey="6LfkMB8rAAAAAH-BEC1cMYij1HEhw4sTaEOl-UCA"></div>
</div>
<div class="col-md-12 text-end mt-4">
<button type="submit" class="btn btn-primary px-4 py-2">Enviar solicitud</button>
</div>
<!-- Contenedor de formularios -->
<div class="container">
<div class="card-form">
<!-- Botón para alternar entre formularios -->
<div class="form-toggle">
<button type="button" class="btn btn-white" id="btnToggle" onclick="alternarFormulario()">🔄 Registro de Agencia Aduanal</button>
</div>
</form>
<!-- Formulario de Importador -->
<div id="formImportadorContainer" class="form-container active transition-fade">
<h3 class="form-title">Registro de Importador</h3>
<form action="/IMPORTADORES/registro/enviarSoliImportador" method="POST" enctype="multipart/form-data" id="formRegistro">
<div class="row g-3">
<div class="col-md-6">
<label for="company_name" class="form-label">Nombre de la empresa *</label>
<input type="text" class="form-control" name="company_name" id="company_name" required>
</div>
<div class="col-md-6">
<label for="rfc" class="form-label">RFC *</label>
<input type="text" class="form-control" name="rfc" maxlength="13" id="rfc" required>
</div>
<div class="col-md-6">
<label for="email" class="form-label">Correo electrónico *</label>
<input type="email" class="form-control" name="email" id="email" required>
</div>
<div class="col-md-6">
<label for="phone" class="form-label">Teléfono *</label>
<input type="text" class="form-control" name="phone" id="phone" required>
</div>
<div class="col-md-12">
<label for="opinion_file" class="form-label">Padrón de importadores o encargo conferido (PDF) *</label>
<input type="file" class="form-control" name="opinion_file" id="opinion_file" accept=".pdf" required>
</div>
<div class="col-md-12 text-center recaptcha">
<div class="g-recaptcha" data-sitekey="6LfkMB8rAAAAAH-BEC1cMYij1HEhw4sTaEOl-UCA"></div>
</div>
<div class="col-md-12 text-end mt-4">
<button type="submit" class="btn btn-primary px-4 py-2">Enviar solicitud</button>
</div>
</div>
</form>
</div>
<!-- Formulario de Agencia Aduanal -->
<div id="formAgenciaContainer" class="form-container transition-fade">
<h3 class="form-title">Registro de Agencia Aduanal</h3>
<form action="/IMPORTADORES/registro/enviarSoliAgencia" method="POST" enctype="multipart/form-data" id="formRegistroAgencia">
<div class="row g-3">
<div class="col-md-6">
<label for="nombre_agencia" class="form-label">Nombre de la agencia *</label>
<input type="text" class="form-control" name="nombre_agencia" id="nombre_agencia" required>
</div>
<div class="col-md-6">
<label for="rfc_agencia" class="form-label">RFC *</label>
<input type="text" class="form-control" name="rfc_agencia" maxlength="13" id="rfc_agencia" required>
</div>
<div class="col-md-6">
<label for="correo" class="form-label">Correo electrónico *</label>
<input type="email" class="form-control" name="correo" id="correo" required>
</div>
<div class="col-md-6">
<label for="telefono" class="form-label">Teléfono *</label>
<input type="text" class="form-control" name="telefono" maxlength="11" id="telefono" required>
</div>
<div class="col-md-12">
<label for="direccion" class="form-label">Dirección *</label>
<input type="text" class="form-control" name="direccion" id="direccion"
placeholder="Ej: Calle, num.externo, num.interno, colonia, municipio, código postal, estado" required>
</div>
<div class="col-md-12">
<label for="opinion_file" class="form-label">Padrón de importadores o encargo conferido (PDF) *</label>
<input type="file" class="form-control" name="opinion_file" id="opinion_file" accept=".pdf" required>
</div><hr>
<p><strong>Administrador de la Agencia</strong></p>
<div class="col-md-6">
<label for="nombre_admin" class="form-label">Nombre *</label>
<input type="text" class="form-control" name="nombre_admin" id="nombre_admin" required>
</div>
<div class="col-md-6">
<label for="correo_admin" class="form-label">Correo Administrador *</label>
<input type="email" class="form-control" name="correo_admin" id="correo_admin" required>
</div>
<div class="col-md-12 text-center recaptcha">
<div class="g-recaptcha" data-sitekey="6LfkMB8rAAAAAH-BEC1cMYij1HEhw4sTaEOl-UCA"></div>
</div>
<div class="col-md-12 text-end mt-4">
<button type="submit" class="btn btn-primary px-4 py-2">Enviar solicitud</button>
</div>
</div>
</form>
</div>
</div>
</div>
<div class="container ">
@@ -126,59 +197,214 @@ $color2 = explode(',', $config['colores_primarios'] ?? '#003366,#0055A5')
<!-- Validación JS -->
<script>
let formularioActual = 'importador';
function alternarFormulario() {
const formImportador = document.getElementById('formImportadorContainer');
const formAgencia = document.getElementById('formAgenciaContainer');
const btnToggle = document.getElementById('btnToggle');
if (formularioActual === 'importador') {
// Cambiar a formulario de agencia
formImportador.classList.remove('active');
formAgencia.classList.add('active');
btnToggle.textContent = '🔄 Registro de Importador';
btnToggle.classList.remove('btn-white');
btnToggle.classList.add('btn-white');
formularioActual = 'agencia';
} else {
// Cambiar a formulario de importador
formAgencia.classList.remove('active');
formImportador.classList.add('active');
btnToggle.textContent = '🔄 Registro de Agencia Aduanal';
btnToggle.classList.remove('btn-white');
btnToggle.classList.add('btn-white');
formularioActual = 'importador';
}
// Resetear reCAPTCHA si está disponible
if (typeof grecaptcha !== 'undefined') {
grecaptcha.reset();
}
}
// Inicializar al cargar la página
document.addEventListener('DOMContentLoaded', function() {
// Por defecto mostrar el formulario de importador
const formImportador = document.getElementById('formImportadorContainer');
formImportador.classList.add('active');
});
// Registro de Importador
document.getElementById('formRegistro').addEventListener('submit', function(e) {
const rfcInput = document.querySelector('input[name="rfc"]');
const emailInput = document.querySelector('input[name="email"]');
const phoneInput = document.querySelector('input[name="phone"]');
const archivo = document.getElementById('opinion_file').files[0];
e.preventDefault();
const nombre = document.getElementById('company_name').value.trim();
const rfc = document.getElementById('rfc').value.trim().toUpperCase();
const email = document.getElementById('email').value.trim();
const phone = document.getElementById('phone').value.trim();
const archivo = document.getElementById('opinion_file').files[0];
const recaptcha = grecaptcha.getResponse();
const rfc = rfcInput.value.trim().toUpperCase();
const email = emailInput.value.trim();
const phone = phoneInput.value.trim();
// Expresiones regulares
const emailRegex = /^[a-zA-Z0-9._+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
const rfcRegex = /^[A-ZÑ&]{3,4}\d{6}[A-Z0-9]{3}$/;
const phoneRegex = /^\d{3}\s\d{7}$/;
// Validación de nombre
if (!nombre) {
Swal.fire({ icon: 'warning', title: 'Campo requerido', text: 'El nombre es obligatorio.', confirmButtonColor: '#dc3545' });
document.getElementById('company_name').focus();
return;
}
// Validación de correo
if (!email) {
Swal.fire({ icon: 'warning', title: 'Campo requerido', text: 'El correo es obligatorio.', confirmButtonColor: '#dc3545' });
document.getElementById('email').focus();
return;
}
if (!emailRegex.test(email)) {
Swal.fire({ icon: 'error', title: 'Correo inválido', text: 'No es una dirección de correo válida.', confirmButtonColor: '#dc3545' });
document.getElementById('email').focus();
return;
}
// Validación de RFC
if (!rfc) {
Swal.fire({ icon: 'warning', title: 'Campo requerido', text: 'El RFC es obligatorio.', confirmButtonColor: '#dc3545' });
document.getElementById('rfc').focus();
return;
}
if (!rfcRegex.test(rfc)) {
Swal.fire({ icon: 'error', title: 'RFC inválido', text: 'El RFC debe tener 12 o 13 caracteres con el formato correcto (ej. ABC123456XYZ).', confirmButtonColor: '#dc3545' });
document.getElementById('rfc').focus();
return;
}
// Validación de teléfono
if (!phone) {
Swal.fire({ icon: 'warning', title: 'Campo requerido', text: 'El teléfono es obligatorio.', confirmButtonColor: '#dc3545' });
document.getElementById('phone').focus();
return;
}
if (!phoneRegex.test(phone)) {
Swal.fire({ icon: 'error', title: 'Teléfono inválido', text: 'Debe tener 10 dígitos en el formato: 3 dígitos (LADA), espacio, y 7 dígitos. Ejemplo: 123 4567890', confirmButtonColor: '#dc3545' });
document.getElementById('phone').focus();
return;
}
// Validación de archivo PDF
if (!archivo) {
Swal.fire({ icon: 'warning', title: 'Campo requerido', text: 'El padrón de importadores es obligatorio.', confirmButtonColor: '#dc3545' });
document.getElementById('direccion').focus();
return;
}
if (!archivo || archivo.type !== 'application/pdf') {
Swal.fire({ icon: 'error', title: 'Formato de documento inválido', text: 'Solo se permite subir archivos PDF.', confirmButtonColor: '#dc3545' });
return; }
// Validación de reCAPTCHA
if (recaptcha.length === 0) {
Swal.fire({ icon: 'warning', title: 'Verificación requerida', text: 'Por favor completa el reCAPTCHA.', confirmButtonColor: '#dc3545' });
return;
}
// Si todas las validaciones pasan, el formulario se envía.
this.submit();
});
// Registro de Agencia
document.getElementById('formRegistroAgencia').addEventListener('submit', function(e) {
e.preventDefault();
const nombre = document.getElementById('nombre_agencia').value.trim();
const rfc_agencia = document.getElementById('rfc_agencia').value.trim().toUpperCase();
const correo = document.getElementById('correo').value.trim();
const telefono = document.getElementById('telefono').value.trim();
const direccion = document.getElementById('direccion').value.trim();
const archivo = document.getElementById('opinion_file').files[0];
const nombre_admin = document.getElementById('nombre_admin').value.trim();
const correo_admin = document.getElementById('correo_admin').value.trim();
const recaptcha = grecaptcha.getResponse();
// Expresiones regulares
const correoRegex = /^[a-z0-9._+-]+@[a-z0-9.-]+\.[a-z]{2,}$/;
const rfcRegex = /^[A-ZÑ&]{3,4}\d{6}[A-Z0-9]{3}$/;
const correoRegex = /^[a-zA-Z0-9._+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
const rfcRegex = /^[A-ZÑ&]{3,4}\d{6}[A-Z0-9]{3}$/;
const telefonoRegex = /^\d{3}\s\d{7}$/;
// Validación de nombre
if (!nombre) {
Swal.fire({ icon: 'warning', title: 'Campo requerido', text: 'El nombre es obligatorio.', confirmButtonColor: '#dc3545' });
document.getElementById('nombre_agencia').focus();
return;
}
// Validación de correo
if (!correoRegex.test(email)) {
e.preventDefault();
Swal.fire("Correo inválido", "No es una dirección de correo válida. Solo se permiten minúsculas.", "error");
emailInput.focus();
if (!correo) {
Swal.fire({ icon: 'warning', title: 'Campo requerido', text: 'El correo es obligatorio.', confirmButtonColor: '#dc3545' });
document.getElementById('correo').focus();
return;
}
if (!correoRegex.test(correo)) {
Swal.fire({ icon: 'error', title: 'Correo inválido', text: 'No es una dirección de correo válida.', confirmButtonColor: '#dc3545' });
document.getElementById('correo').focus();
return;
}
// Validación de RFC
if (!rfcRegex.test(rfc)) {
e.preventDefault();
Swal.fire("RFC inválido", "El RFC debe tener 12 o 13 caracteres con el formato correcto (ej. ABC123456XYZ).", "error");
rfcInput.focus();
if (!rfc_agencia) {
Swal.fire({ icon: 'warning', title: 'Campo requerido', text: 'El RFC es obligatorio.', confirmButtonColor: '#dc3545' });
document.getElementById('rfc_agencia').focus();
return;
}
if (!rfcRegex.test(rfc_agencia)) {
Swal.fire({ icon: 'error', title: 'RFC inválido', text: 'El RFC debe tener 12 o 13 caracteres con el formato correcto (ej. ABC123456XYZ).', confirmButtonColor: '#dc3545' });
document.getElementById('rfc_agencia').focus();
return;
}
// Validación de teléfono
if (!telefonoRegex.test(phone)) {
e.preventDefault();
Swal.fire("Teléfono inválido", "Debe tener 10 dígitos en el formato: 3 dígitos (LADA), espacio, y 7 dígitos. Ejemplo: 123 4567890", "error");
phoneInput.focus();
if (!telefono) {
Swal.fire({ icon: 'warning', title: 'Campo requerido', text: 'El teléfono es obligatorio.', confirmButtonColor: '#dc3545' });
document.getElementById('telefono').focus();
return;
}
if (!telefonoRegex.test(telefono)) {
Swal.fire({ icon: 'error', title: 'Teléfono inválido', text: 'Debe tener 10 dígitos en el formato: 3 dígitos (LADA), espacio, y 7 dígitos (ej. 123 4567890).', confirmButtonColor: '#dc3545' });
document.getElementById('telefono').focus();
return;
}
// Validación de dirección
if (!direccion) {
Swal.fire({ icon: 'warning', title: 'Campo requerido', text: 'La dirección es obligatoria.', confirmButtonColor: '#dc3545' });
document.getElementById('direccion').focus();
return;
}
// Validación de archivo PDF
if (!archivo || archivo.type !== 'application/pdf') {
e.preventDefault();
Swal.fire("Error", "Solo se permite subir archivos PDF.", "error");
if (!archivo) {
Swal.fire({ icon: 'warning', title: 'Campo requerido', text: 'El padrón de importadores es obligatorio.', confirmButtonColor: '#dc3545' });
document.getElementById('direccion').focus();
return;
}
if (!archivo || archivo.type !== 'application/pdf') {
Swal.fire({ icon: 'error', title: 'Formato de documento inválido', text: 'Solo se permite subir archivos PDF.', confirmButtonColor: '#dc3545' });
return; }
// Validación de administrador
if (!nombre_admin) {
Swal.fire({ icon: 'warning', title: 'Campo requerido', text: 'El nombre del administrador es obligatorio.', confirmButtonColor: '#dc3545' });
document.getElementById('nombre_admin').focus();
return;
}
// Validación de correo de administrador
if (!correo_admin) {
Swal.fire({ icon: 'warning', title: 'Campo requerido', text: 'El correo de administrador es obligatorio.', confirmButtonColor: '#dc3545' });
document.getElementById('correo_admin').focus();
return;
}
if (!correoRegex.test(correo_admin)) {
Swal.fire({ icon: 'error', title: 'Correo inválido', text: 'No es una dirección de correo válida.', confirmButtonColor: '#dc3545' });
document.getElementById('correo_admin').focus();
return;
}
// Validación de reCAPTCHA
if (recaptcha.length === 0) {
e.preventDefault();
Swal.fire("Verificación requerida", "Por favor completa el reCAPTCHA.", "warning");
return;
if (recaptcha.length === 0) {
Swal.fire({ icon: 'warning', title: 'Verificación requerida', text: 'Por favor completa el reCAPTCHA.', confirmButtonColor: '#dc3545' });
return;
}
// Si todas las validaciones pasan, el formulario se envía.
this.submit();
});
</script>

View File

@@ -38,7 +38,7 @@ $color2 = explode(',', $config['colores_primarios'] ?? '#003366,#0055A5')
Swal.fire({
icon: 'success',
title: '¡Gracias por tu solicitud!',
html: `<p>Tu información fue enviada correctamente.<br>Un agente aduanal revisará tu expediente y te contactará por correo.</p>`,
html: `<p>Tu información fue enviada correctamente.<br>Un administrador revisará tu expediente y te contactará por correo.</p>`,
confirmButtonText: 'Volver al inicio',
confirmButtonColor: '<?= $color1 ?>',
allowOutsideClick: false,

View File

@@ -128,22 +128,22 @@
// Validaciones de campos obligatorios
if (!vehiculo) {
Swal.fire({ icon:'error', 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;
}
if (!ident_fiscal) {
Swal.fire({ icon:'error', 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;
}
if (!transportista) {
Swal.fire({ icon:'error', 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;
}
if (fotoF && fotoF.size > 2 * 1024 * 1024) { // 2 MB
return Swal.fire({ icon:'error', title:'Foto demasiado grande', text:'La imagen no debe exceder 2 MB.' });
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();

View File

@@ -131,22 +131,22 @@
// Validaciones de campos obligatorios
if (!veh) {
Swal.fire({ icon:'error', 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;
}
if (!fisc) {
Swal.fire({ icon:'error', 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('identificador_fiscal').focus();
return;
}
if (!trans) {
Swal.fire({ icon:'error', 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;
}
if (fotoF && fotoF.size > 2 * 1024 * 1024) { // 2 MB
return Swal.fire({ icon:'error', title:'Foto demasiado grande', text:'La imagen no debe exceder 2 MB.' });
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();

View File

@@ -112,80 +112,94 @@
<script>
document.getElementById('formAltaTransportista').addEventListener('submit', function(e) {
e.preventDefault();
const rfcInput = document.getElementById('rfc');
const curpInput = document.getElementById('curp');
const rfcInput = document.getElementById('rfc');
const curpInput = document.getElementById('curp');
const telefonoInput = document.getElementById('telefono');
const rfc = rfcInput.value.trim().toUpperCase();
const curp = curpInput.value.trim();
const telefono = telefonoInput.value.trim();
const clave = document.getElementById('clave').value.trim();
const nombre = document.getElementById('nombre').value.trim();
const caat = document.getElementById('caat').value.trim();
const pais = document.getElementById('pais').value;
const entidad= document.getElementById('entidad').value;
const ciudad = document.getElementById('ciudad').value;
const rfc = rfcInput.value.trim().toUpperCase();
const curp = curpInput.value.trim();
const telefono = telefonoInput.value.trim();
const clave = document.getElementById('clave').value.trim();
const nombre = document.getElementById('nombre').value.trim();
const caat = document.getElementById('caat').value.trim();
const pais = document.getElementById('pais').value;
const entidad = document.getElementById('entidad').value;
const ciudad = document.getElementById('ciudad').value;
const domicilio = document.getElementById('domicilio').value.trim();
const curpRegex = /^[A-Z]{4}[0-9]{6}[HM](AS|BC|BS|CC|CL|CM|CS|CH|DF|DG|GT|GR|HG|JC|MC|MN|MS|NT|NL|OC|PL|QT|QR|SP|SL|SR|TC|TS|TL|VZ|YN|ZS)[A-Z]{3}[A-Z0-9]{2}$/;
const rfcRegex = /^[A-ZÑ&]{3,4}\d{6}[A-Z0-9]{3}$/;
const telefonoRegex= /^\d{3}\s\d{7}$/;
const curpRegex = /^[A-Z]{4}[0-9]{6}[HM](AS|BC|BS|CC|CL|CM|CS|CH|DF|DG|GT|GR|HG|JC|MC|MN|MS|NT|NL|OC|PL|QT|QR|SP|SL|SR|TC|TS|TL|VZ|YN|ZS)[A-Z]{3}[A-Z0-9]{2}$/;
const rfcRegex = /^[A-ZÑ&]{3,4}\d{6}[A-Z0-9]{3}$/;
const telefonoRegex = /^\d{3}\s\d{7}$/;
if (!clave) {
Swal.fire({ icon:'error',title:'Campo requerido',text:'La clave es obligatoria.',confirmButtonColor:'#dc3545'});
document.getElementById('clave').focus();return;
Swal.fire({ icon: 'warning', title: 'Campo requerido', text: 'La clave es obligatoria.', confirmButtonColor: '#dc3545'});
document.getElementById('clave').focus();
return;
}
if (clave.length>8) {
Swal.fire({ icon:'error',title:'Clave inválida',text:'La clave no puede tener más de 8 caracteres.',confirmButtonColor:'#dc3545'});
document.getElementById('clave').focus();return;
Swal.fire({ icon: 'error', title: 'Clave inválida', text: 'La clave no puede tener más de 8 caracteres.', confirmButtonColor: '#dc3545'});
document.getElementById('clave').focus();
return;
}
if (!nombre) {
Swal.fire({ icon:'error',title:'Campo requerido',text:'El nombre es obligatorio.',confirmButtonColor:'#dc3545'});
document.getElementById('nombre').focus();return;
Swal.fire({ icon: 'warning', title: 'Campo requerido', text: 'El nombre es obligatorio.', confirmButtonColor: '#dc3545'});
document.getElementById('nombre').focus();
return;
}
if (!rfc) {
Swal.fire({ icon:'error',title:'Campo requerido',text:'El RFC es obligatorio.',confirmButtonColor:'#dc3545'});
document.getElementById('rfc').focus();return;
Swal.fire({ icon: 'warning', title: 'Campo requerido', text: 'El RFC es obligatorio.', confirmButtonColor: '#dc3545'});
document.getElementById('rfc').focus();
return;
}
if (!rfcRegex.test(rfc)) {
Swal.fire({ icon:'error',title:'RFC inválido',text:'El RFC debe tener 12 o 13 caracteres con el formato correcto (ej. ABC123456XYZ).',confirmButtonColor:'#dc3545'});
document.getElementById('rfc').focus();return;
Swal.fire({ icon: 'error', title: 'RFC inválido', text: 'El RFC debe tener 12 o 13 caracteres con el formato correcto (ej. ABC123456XYZ).', confirmButtonColor: '#dc3545'});
document.getElementById('rfc').focus();
return;
}
if (curp && !curpRegex.test(curp)) {
Swal.fire({ icon:'error',title:'CURP inválido',text:'El CURP no tiene el formato correcto.',confirmButtonColor:'#dc3545'});
document.getElementById('curp').focus();return;
Swal.fire({ icon: 'error', title: 'CURP inválido', text: 'El CURP no tiene el formato correcto.', confirmButtonColor: '#dc3545'});
document.getElementById('curp').focus();
return;
}
if (!telefono) {
Swal.fire({ icon:'error',title:'Campo requerido',text:'El teléfono es obligatorio.',confirmButtonColor:'#dc3545'});
document.getElementById('telefono').focus();return;
Swal.fire({ icon: 'warning', title: 'Campo requerido', text: 'El teléfono es obligatorio.', confirmButtonColor: '#dc3545'});
document.getElementById('telefono').focus();
return;
}
if (!telefonoRegex.test(telefono)) {
Swal.fire({ icon:'error',title:'Teléfono inválido',text:'Debe tener 10 dígitos en el formato: 3 dígitos (LADA), espacio, y 7 dígitos. Ejemplo: 123 4567890',confirmButtonColor:'#dc3545'});
document.getElementById('telefono').focus();return;
Swal.fire({ icon: 'error', title: 'Teléfono inválido', text: 'Debe tener 10 dígitos en el formato: 3 dígitos (LADA), espacio, y 7 dígitos (ej. 123 4567890).', onfirmButtonColor:'#dc3545'});
document.getElementById('telefono').focus();
return;
}
if (telefono.length>11) {
Swal.fire({ icon:'error',title:'Teléfono inválido',text:'El teléfono no puede tener más de 11 caracteres.',confirmButtonColor:'#dc3545'});
document.getElementById('telefono').focus();return;
Swal.fire({ icon: 'error', title: 'Teléfono inválido', text: 'El teléfono no puede tener más de 11 caracteres.', confirmButtonColor: '#dc3545'});
document.getElementById('telefono').focus();
return;
}
if (!caat) {
Swal.fire({ icon:'error',title:'Campo requerido',text:'El código caat es obligatorio.',confirmButtonColor:'#dc3545'});
document.getElementById('caat').focus();return;
Swal.fire({ icon: 'warning', title: 'Campo requerido', text: 'El código caat es obligatorio.', confirmButtonColor: '#dc3545'});
document.getElementById('caat').focus();
return;
}
if (!pais) {
Swal.fire({ icon:'error',title:'Campo requerido',text:'Debe seleccionar un país.',confirmButtonColor:'#dc3545'});
document.getElementById('pais').focus();return;
Swal.fire({ icon: 'warning', title: 'Campo requerido', text: 'Debe seleccionar un país.', confirmButtonColor: '#dc3545'});
document.getElementById('pais').focus();
return;
}
if (!entidad) {
Swal.fire({ icon:'error',title:'Campo requerido',text:'Debe seleccionar un estado.',confirmButtonColor:'#dc3545'});
document.getElementById('entidad').focus();return;
Swal.fire({ icon: 'warning', title: 'Campo requerido', text: 'Debe seleccionar un estado.', confirmButtonColor: '#dc3545'});
document.getElementById('entidad').focus();
return;
}
if (!ciudad) {
Swal.fire({ icon:'error',title:'Campo requerido',text:'Debe seleccionar una ciudad.',confirmButtonColor:'#dc3545'});
document.getElementById('ciudad').focus();return;
Swal.fire({ icon: 'warning', title: 'Campo requerido', text: 'Debe seleccionar una ciudad.', confirmButtonColor: '#dc3545'});
document.getElementById('ciudad').focus();
return;
}
if (!domicilio) {
Swal.fire({ icon:'error',title:'Campo requerido',text:'El domicilio es obligatorio.',confirmButtonColor:'#dc3545'});
document.getElementById('domicilio').focus();return;
Swal.fire({ icon: 'warning', title: 'Campo requerido', text: 'El domicilio es obligatorio.', confirmButtonColor: '#dc3545'});
document.getElementById('domicilio').focus();
return;
}
document.getElementById('rfc').value = rfc;
document.getElementById('curp').value = curp;
@@ -252,7 +266,6 @@
hidden.value = '0';
}
});
</script>
</body>

View File

@@ -121,29 +121,29 @@
document.getElementById('formEditTransportista').addEventListener('submit', function(e) {
e.preventDefault(); // Prevenir envío por defecto
const rfcInput = document.getElementById('rfc');
const curpInput = document.getElementById('curp');
const rfcInput = document.getElementById('rfc');
const curpInput = document.getElementById('curp');
const telefonoInput = document.getElementById('telefono');
const rfc = rfcInput.value.trim().toUpperCase();
const curp = curpInput.value.trim();
const telefono = telefonoInput.value.trim();
const clave = document.getElementById('clave').value.trim();
const nombre = document.getElementById('nombre').value.trim();
const caat = document.getElementById('caat').value.trim();
const pais = document.getElementById('pais').value;
const entidad = document.getElementById('entidad').value;
const ciudad = document.getElementById('ciudad').value;
const rfc = rfcInput.value.trim().toUpperCase();
const curp = curpInput.value.trim();
const telefono = telefonoInput.value.trim();
const clave = document.getElementById('clave').value.trim();
const nombre = document.getElementById('nombre').value.trim();
const caat = document.getElementById('caat').value.trim();
const pais = document.getElementById('pais').value;
const entidad = document.getElementById('entidad').value;
const ciudad = document.getElementById('ciudad').value;
const domicilio = document.getElementById('domicilio').value.trim();
// Expresiones regulares
const curpRegex = /^[A-Z]{4}[0-9]{6}[HM](AS|BC|BS|CC|CL|CM|CS|CH|DF|DG|GT|GR|HG|JC|MC|MN|MS|NT|NL|OC|PL|QT|QR|SP|SL|SR|TC|TS|TL|VZ|YN|ZS)[A-Z]{3}[A-Z0-9]{2}$/;
const rfcRegex = /^[A-ZÑ&]{3,4}\d{6}[A-Z0-9]{3}$/;
const curpRegex = /^[A-Z]{4}[0-9]{6}[HM](AS|BC|BS|CC|CL|CM|CS|CH|DF|DG|GT|GR|HG|JC|MC|MN|MS|NT|NL|OC|PL|QT|QR|SP|SL|SR|TC|TS|TL|VZ|YN|ZS)[A-Z]{3}[A-Z0-9]{2}$/;
const rfcRegex = /^[A-ZÑ&]{3,4}\d{6}[A-Z0-9]{3}$/;
const telefonoRegex = /^\d{3}\s\d{7}$/;
// Validaciones de campos obligatorios
if (!clave) {
Swal.fire({ icon: 'error', title: 'Campo requerido', text: 'La clave es obligatoria.', confirmButtonColor: '#dc3545' });
Swal.fire({ icon: 'warning', title: 'Campo requerido', text: 'La clave es obligatoria.', confirmButtonColor: '#dc3545' });
document.getElementById('clave').focus();
return;
}
@@ -153,12 +153,12 @@
return;
}
if (!nombre) {
Swal.fire({ icon: 'error', title: 'Campo requerido', text: 'El nombre es obligatorio.', confirmButtonColor: '#dc3545' });
Swal.fire({ icon: 'warning', title: 'Campo requerido', text: 'El nombre es obligatorio.', confirmButtonColor: '#dc3545' });
document.getElementById('nombre').focus();
return;
}
if (!rfc) {
Swal.fire({ icon: 'error', title: 'Campo requerido', text: 'El RFC es obligatorio.', confirmButtonColor: '#dc3545' });
Swal.fire({ icon: 'warning', title: 'Campo requerido', text: 'El RFC es obligatorio.', confirmButtonColor: '#dc3545' });
document.getElementById('rfc').focus();
return;
}
@@ -169,13 +169,12 @@
}
// Validación de CURP (solo si se ingresó)
if (curp && !curpRegex.test(curp)) {
Swal.fire({ icon: 'error', title: 'CURP inválido', text: 'El CURP no tiene el formato correcto.', confirmButtonColor: '#dc3545'
});
Swal.fire({ icon: 'error', title: 'CURP inválido', text: 'El CURP no tiene el formato correcto.', confirmButtonColor: '#dc3545' });
document.getElementById('curp').focus();
return;
}
if (!telefono) {
Swal.fire({ icon: 'error', title: 'Campo requerido', text: 'El teléfono es obligatorio.', confirmButtonColor: '#dc3545' });
Swal.fire({ icon: 'warning', title: 'Campo requerido', text: 'El teléfono es obligatorio.', confirmButtonColor: '#dc3545' });
document.getElementById('telefono').focus();
return;
}
@@ -190,27 +189,27 @@
return;
}
if (!caat) {
Swal.fire({ icon: 'error', title: 'Campo requerido', text: 'El código caat es obligatorio.', confirmButtonColor: '#dc3545' });
Swal.fire({ icon: 'warning', title: 'Campo requerido', text: 'El código caat es obligatorio.', confirmButtonColor: '#dc3545' });
document.getElementById('caat').focus();
return;
}
if (!pais) {
Swal.fire({ icon: 'error', title: 'Campo requerido', text: 'Debe seleccionar un país.', confirmButtonColor: '#dc3545' });
Swal.fire({ icon: 'warning', title: 'Campo requerido', text: 'Debe seleccionar un país.', confirmButtonColor: '#dc3545' });
document.getElementById('pais').focus();
return;
}
if (!entidad) {
Swal.fire({ icon: 'error', title: 'Campo requerido', text: 'Debe seleccionar un estado.', confirmButtonColor: '#dc3545' });
Swal.fire({ icon: 'warning', title: 'Campo requerido', text: 'Debe seleccionar un estado.', confirmButtonColor: '#dc3545' });
document.getElementById('entidad').focus();
return;
}
if (!ciudad) {
Swal.fire({ icon: 'error', title: 'Campo requerido', text: 'Debe seleccionar una ciudad.', confirmButtonColor: '#dc3545' });
Swal.fire({ icon: 'warning', title: 'Campo requerido', text: 'Debe seleccionar una ciudad.', confirmButtonColor: '#dc3545' });
document.getElementById('ciudad').focus();
return;
}
if (!domicilio) {
Swal.fire({ icon: 'error', title: 'Campo requerido', text: 'El domicilio es obligatorio.', confirmButtonColor: '#dc3545' });
Swal.fire({ icon: 'warning', title: 'Campo requerido', text: 'El domicilio es obligatorio.', confirmButtonColor: '#dc3545' });
document.getElementById('domicilio').focus();
return;
}

View File

@@ -0,0 +1,123 @@
<?php include __DIR__ . '/../partials/sidebar_importador.php'; ?>
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<title>Dashboard | Administrador</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
<link href="https://cdn.datatables.net/1.13.4/css/jquery.dataTables.min.css" rel="stylesheet">
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
<script src="https://code.jquery.com/jquery-3.7.0.min.js"></script>
<script src="https://cdn.datatables.net/1.13.4/js/jquery.dataTables.min.js"></script>
<style>
body { font-family: 'Segoe UI', sans-serif; background-color: #f4f6f9; }
.sidebar { width: 220px; height: 100vh; background-color: #343a40; position: fixed; top: 0; left: 0; padding-top: 56px; z-index: 1040; }
.sidebar .nav-link { font-weight: normal; color: white; transition: all 0.3s ease; }
.sidebar .nav-link:hover,
.sidebar .nav-link.active { background-color: #495057; color: #fff; }
.content { margin-top: 56px; padding: 40px 20px; position: relative; z-index: 1; background-color: #f4f6f9; transition: margin-left 0.3s ease; }
.navbar { position: fixed; top: 0; width: 100%; z-index: 1050; }
/* A partir de dispositivos medianos (>=768px), deja espacio lateral */
@media (min-width: 768px) { .content { margin-left: 250px; /* Ancho del sidebar */ } }
/* En móviles, sin margen lateral */
@media (max-width: 767.98px) {
.content { margin-left: 0; }
.sidebar .nav-link { font-weight: normal; color: #343a40; background-color: transparent; }
.sidebar .nav-link:hover,
.sidebar .nav-link.active { background-color: #e9ecef; color: #212529; }
}
.card { border-radius: 12px; }
.table thead th { background: #343a40; color: #fff; }
</style>
</head>
<body>
<!-- 📄 CONTENIDO -->
<div class="content">
<h4 class="mb-4"> Nueva Vinculación</h4>
<!-- TABLA DE AGENCIAS -->
<div class="card p-3 shadow-sm">
<h5 class="mb-3">🔗 Solicitar Vinculación con Agencias Aduanales</h5>
<div class="table-responsive">
<table class="table table-striped table-hover align-middle" id="tabla-solicitar-vinculaciones">
<thead>
<tr>
<th>#</th>
<th>Nombre</th>
<th>RFC</th>
<th>Correo</th>
<th>Teléfono</th>
<th>Dirección</th>
<th>Fecha de Registro</th>
<th>Acciones</th>
</tr>
</thead>
<tbody>
<?php if (!empty($agencias)): ?>
<?php foreach ($agencias as $ag): ?>
<tr>
<td><?= $ag['id_agencia'] ?></td>
<td><?= htmlspecialchars(decrypt($ag['nombre_agencia'])) ?></td>
<td><?= htmlspecialchars($ag['rfc_agencia']) ?></td>
<td><?= htmlspecialchars(decrypt($ag['email'])) ?></td>
<td><?= htmlspecialchars($ag['telefono']) ?></td>
<td><?= htmlspecialchars($ag['direccion']) ?></td>
<td><?= $ag['creado_en'] ? $ag['creado_en']->format('Y-m-d H:i') : '' ?></td>
<td>
<?php if (!empty($ag['estado_solicitud']) && $ag['estado_solicitud'] === 'PENDIENTE'): ?>
<a href="/IMPORTADORES/vinculaciones/cancelar?id=<?= $ag['id_agencia'] ?>" class="btn btn-sm btn-danger">Cancelar solicitud</a>
<?php else: ?>
<a href="/IMPORTADORES/vinculaciones/vincular?id=<?= $ag['id_agencia'] ?>" class="btn btn-sm btn-success">Vincular</a>
<?php endif; ?>
</td>
</tr>
<?php endforeach; ?>
<?php else: ?>
<tr>
<td colspan="8" class="text-center">No hay agencias registradas aún.</td>
</tr>
<?php endif; ?>
</tbody>
</table>
</div>
</div>
</div>
<script>
$(document).ready(function () {
$('#tabla-solicitar-vinculaciones').DataTable({
order: [],
language: {
url: 'https://cdn.datatables.net/plug-ins/1.13.4/i18n/es-ES.json'
}
});
});
</script>
<?php if (isset($_GET['success'])): ?>
<script>
<?php if ($_GET['success'] === 'sended_request'): ?>
Swal.fire({ icon: 'success', title: 'Solicitud enviada',
text: 'La solicitud de vinculación fue enviada correctamente.',
confirmButtonColor: '#198754' });
<?php elseif ($_GET['success'] === 'cancelled'): ?>
Swal.fire({ icon: 'success', title: 'Solicitud cancelada', text: 'La solicitud fue cancelada correctamente.', confirmButtonColor: '#198754' });
<?php endif; ?>
</script>
<?php endif; ?>
<?php if (isset($_GET['error'])): ?>
<script>
<?php if ($_GET['error'] === 'failed_request'): ?>
Swal.fire({ icon: 'error', title: 'Solicitud fallida', text: 'No se pudo enviar la solicitud.', confirmButtonColor: '#dc3545' });
<?php elseif ($_GET['error'] === 'pending_request'): ?>
Swal.fire({ icon: 'error', title: 'Solicitud pendiente', text: 'Ya tienes una solicitud pendiente.', confirmButtonColor: '#dc3545' });
<?php endif; ?>
</script>
<?php endif; ?>
</body>
</html>

View File

@@ -0,0 +1,111 @@
<?php include __DIR__ . '/../partials/sidebar_importador.php'; ?>
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<title>Dashboard | Administrador</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
<link href="https://cdn.datatables.net/1.13.4/css/jquery.dataTables.min.css" rel="stylesheet">
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
<script src="https://code.jquery.com/jquery-3.7.0.min.js"></script>
<script src="https://cdn.datatables.net/1.13.4/js/jquery.dataTables.min.js"></script>
<style>
body { font-family: 'Segoe UI', sans-serif; background-color: #f4f6f9; }
.sidebar { width: 220px; height: 100vh; background-color: #343a40; position: fixed; top: 0; left: 0; padding-top: 56px; z-index: 1040; }
.sidebar .nav-link { font-weight: normal; color: white; transition: all 0.3s ease; }
.sidebar .nav-link:hover,
.sidebar .nav-link.active { background-color: #495057; color: #fff; }
.content { margin-top: 56px; padding: 40px 20px; position: relative; z-index: 1; background-color: #f4f6f9; transition: margin-left 0.3s ease; }
.navbar { position: fixed; top: 0; width: 100%; z-index: 1050; }
/* A partir de dispositivos medianos (>=768px), deja espacio lateral */
@media (min-width: 768px) { .content { margin-left: 250px; /* Ancho del sidebar */ } }
/* En móviles, sin margen lateral */
@media (max-width: 767.98px) {
.content { margin-left: 0; }
.sidebar .nav-link { font-weight: normal; color: #343a40; background-color: transparent; }
.sidebar .nav-link:hover,
.sidebar .nav-link.active { background-color: #e9ecef; color: #212529; }
}
.card { border-radius: 12px; }
.table thead th { background: #343a40; color: #fff; }
</style>
</head>
<body>
<!-- 📄 CONTENIDO -->
<div class="content">
<h4 class="mb-4">🔗 Mis vinculaciones</h4>
<!-- TABLA DE VINCULACIÓN USUARIO -->
<div class="card p-3 shadow-sm">
<h5 class="mb-3">🏪 Agencia Vinculadas</h5>
<div class="table-responsive">
<table class="table table-striped table-hover align-middle" id="tabla-vinculaciones-usuario">
<thead>
<tr>
<th>#</th>
<th>Nombre</th>
<th>RFC</th>
<th>Teléfono</th>
<th>Dirección</th>
<th>Fecha de Vinculación</th>
<th>Acciones</th>
</tr>
</thead>
<tbody>
<?php if (!empty($vinculaciones)): ?>
<?php foreach ($vinculaciones as $v): ?>
<tr>
<td><?= htmlspecialchars($v['id_relacion'] ?? '') ?></td>
<td><?= htmlspecialchars(decrypt($v['nombre_agencia'] ?? '')) ?></td>
<td><?= htmlspecialchars($v['rfc_agencia'] ?? '') ?></td>
<td><?= htmlspecialchars($v['telefono'] ?? '') ?></td>
<td><?= htmlspecialchars($v['direccion'] ?? '') ?></td>
<td><?= !empty($v['fecha_vinculacion']) ? $v['fecha_vinculacion']->format('Y-m-d H:i') : '' ?></td>
<td>
<a href="/IMPORTADORES/vinculaciones/desvincularUsuario?id=<?= $v['id_relacion'] ?>" class="btn btn-sm btn-danger">Desvincular</a>
</td>
</tr>
<?php endforeach; ?>
<?php endif; ?>
</tbody>
</table>
</div>
</div>
</div>
<script>
$(document).ready(function () {
$('#tabla-vinculaciones-usuario').DataTable({
order: [],
language: {
url: 'https://cdn.datatables.net/plug-ins/1.13.4/i18n/es-ES.json'
}
});
});
</script>
<?php if (isset($_GET['success'])): ?>
<script>
<?php if ($_GET['success'] === 'unlinked'): ?>
Swal.fire({ icon: 'success', title: 'Usuario desvinculado',
text: 'El usuario se ha desvinculado correctamente.',
confirmButtonColor: '#198754' });
<?php endif; ?>
</script>
<?php endif; ?>
<?php if (isset($_GET['error'])): ?>
<script>
<?php if ($_GET['error'] === 'unlinked_failed'): ?>
Swal.fire({ icon: 'error', title: 'Desvinculación fallida', text: 'No se ha podido desvincular el usuario.', confirmButtonColor: '#dc3545' });
<?php elseif ($_GET['error'] === 'invalid_id'): ?>
Swal.fire({ icon: 'error', title: 'ID inválido', text: 'El identificador de la relación no es válido.', confirmButtonColor: '#dc3545' });
<?php endif; ?>
</script>
<?php endif; ?>
</body>
</html>