Sidebars - animaciones

This commit is contained in:
2025-07-18 12:21:26 -06:00
parent 1900091b99
commit 37dcfffd7b
69 changed files with 1282 additions and 710 deletions

View File

@@ -177,6 +177,17 @@ function obtenerUrlPermiso($permiso) {
.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; }
/* Transiciones para collapse manual */
.collapse {
overflow: hidden;
transition: height 0.3s ease;
}
/* Estilo para íconos */
.collapse-icon {
transition: transform 0.3s ease;
margin-left: auto;
}
</style>
<!-- NAVBAR -->
@@ -241,10 +252,10 @@ function obtenerUrlPermiso($permiso) {
<?php if ($permisosUsuario['preferencias']): ?>
<?php $enDashboardPreferencias = $_SERVER['REQUEST_URI'] === '/IMPORTADORES/preferencias/index'; ?>
<a class="nav-link <?= $esVistaPreferencias ? 'active' : '' ?>"
href="<?= $enDashboardPreferencias ? '#submenuPreferencias' : '/IMPORTADORES/preferencias/index' ?>"
<?= $enDashboardPreferencias ? 'data-bs-toggle="collapse"' : '' ?>
role="button" aria-expanded="false" aria-controls="submenuPreferencias"
onclick="<?= $enDashboardPreferencias ? 'return true;' : 'event.preventDefault(); window.location.href = \'/IMPORTADORES/preferencias/index\';' ?>">
href="<?= $enDashboardPreferencias ? '#submenuPreferenciasDesktop' : '/IMPORTADORES/preferencias/index' ?>"
<?= $enDashboardPreferencias ? 'data-bs-toggle="collapse"' : '' ?>
role="button" aria-expanded="false" aria-controls="submenuPreferenciasDesktop"
onclick="<?= $enDashboardPreferencias ? 'return true;' : 'event.preventDefault(); window.location.href = \'/IMPORTADORES/preferencias/index\';' ?>">
<svg class="icon" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M11.3 1.046a1 1 0 00-2.6 0L7.528 3H4a1 1 0 000 2h1v10H4a1 1 0 000 2h3.528l1.172 1.954a1 1 0 001.6 0L12.472 17H16a1 1 0 000-2h-1V5h1a1 1 0 100-2h-3.528L11.3 1.046zM9 6a1 1 0 011 1v6a1 1 0 11-2 0V7a1 1 0 011-1z" clip-rule="evenodd"/>
</svg>
@@ -253,7 +264,7 @@ function obtenerUrlPermiso($permiso) {
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m1 1 4 4 4-4"/>
</svg>
</a>
<div class="collapse" id="submenuPreferencias">
<div class="collapse" id="submenuPreferenciasDesktop">
<ul class="nav flex-column submenu">
<li class="nav-item">
<a href="/IMPORTADORES/preferencias/notificaciones" class="nav-link">
@@ -274,10 +285,10 @@ function obtenerUrlPermiso($permiso) {
<?php if ($permisosUsuario['seguridad']): ?>
<?php $enDashboardSeguridad = $_SERVER['REQUEST_URI'] === '/IMPORTADORES/seguridad/index'; ?>
<a class="nav-link <?= $esVistaSeguridad ? 'active' : '' ?>"
href="<?= $enDashboardSeguridad ? '#submenuSeguridad' : '/IMPORTADORES/seguridad/index' ?>"
<?= $enDashboardSeguridad ? 'data-bs-toggle="collapse"' : '' ?>
role="button" aria-expanded="false" aria-controls="submenuSeguridad"
onclick="<?= $enDashboardSeguridad ? 'return true;' : 'event.preventDefault(); window.location.href = \'/IMPORTADORES/seguridad/index\';' ?>">
href="<?= $enDashboardSeguridad ? '#submenuSeguridadDesktop' : '/IMPORTADORES/seguridad/index' ?>"
<?= $enDashboardSeguridad ? 'data-bs-toggle="collapse"' : '' ?>
role="button" aria-expanded="false" aria-controls="submenuSeguridadDesktop"
onclick="<?= $enDashboardSeguridad ? 'return true;' : 'event.preventDefault(); window.location.href = \'/IMPORTADORES/seguridad/index\';' ?>">
<svg class="icon" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M10 2a4 4 0 00-4 4v2H5a2 2 0 00-2 2v6a2 2 0 002 2h10a2 2 0 002-2v-6a2 2 0 00-2-2h-1V6a4 4 0 00-4-4zm2 6V6a2 2 0 10-4 0v2h4z" clip-rule="evenodd"/>
</svg>
@@ -286,7 +297,7 @@ function obtenerUrlPermiso($permiso) {
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m1 1 4 4 4-4"/>
</svg>
</a>
<div class="collapse" id="submenuSeguridad">
<div class="collapse" id="submenuSeguridadDesktop">
<nav class="nav flex-column submenu">
<li class="nav-item">
<a href="/IMPORTADORES/seguridad/opciones" class="nav-link">
@@ -302,10 +313,10 @@ function obtenerUrlPermiso($permiso) {
<?php if (tieneAccesoBitacoras($permisosBitacoras)): ?>
<?php $enDashboardBitacoras = $_SERVER['REQUEST_URI'] === '/IMPORTADORES/bitacoras/index'; ?>
<a class="nav-link <?= $esVistaBitacoras ? 'active' : '' ?>"
href="<?= $enDashboardBitacoras ? '#submenuBitacoras' : '/IMPORTADORES/bitacoras/index' ?>"
<?= $enDashboardBitacoras ? 'data-bs-toggle="collapse"' : '' ?>
role="button" aria-expanded="<?= $enDashboardBitacoras ? 'false' : 'false' ?>" aria-controls="submenuBitacoras"
onclick="<?= $enDashboardBitacoras ? 'return true;' : 'event.preventDefault(); window.location.href = \'/IMPORTADORES/bitacoras/index\';' ?>">
href="<?= $enDashboardBitacoras ? '#submenuBitacorasDesktop' : '/IMPORTADORES/bitacoras/index' ?>"
<?= $enDashboardBitacoras ? 'data-bs-toggle="collapse"' : '' ?>
role="button" aria-expanded="<?= $enDashboardBitacoras ? 'false' : 'false' ?>" aria-controls="submenuBitacorasDesktop"
onclick="<?= $enDashboardBitacoras ? 'return true;' : 'event.preventDefault(); window.location.href = \'/IMPORTADORES/bitacoras/index\';' ?>">
<svg class="icon" fill="currentColor" viewBox="0 0 20 20">
<path d="M4 3a2 2 0 00-2 2v10a2 2 0 002 2h12a1 1 0 001-1V4a1 1 0 00-1-1H4zm0 2h11v10H4V5z"/>
<path d="M6 7h5v1H6V7zm0 3h8v1H6v-1z"/>
@@ -315,7 +326,7 @@ function obtenerUrlPermiso($permiso) {
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m1 1 4 4 4-4"/>
</svg>
</a>
<div class="collapse" id="submenuBitacoras">
<div class="collapse" id="submenuBitacorasDesktop">
<nav class="nav flex-column submenu">
<?php foreach ($permisosBitacoras as $permiso => $tienePermiso): ?>
<li class="nav-item">
@@ -369,7 +380,148 @@ function obtenerUrlPermiso($permiso) {
<div class="offcanvas-body">
<nav class="sidebar-nav">
<ul class="nav flex-column">
<!-- INFORMACIÓN GENERAL -->
<li class="nav-item">
<?php if ($permisosUsuario['informacion_general']): ?>
<a href="/IMPORTADORES/configuracion/index" class="nav-link">
<svg class="icon" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M18 10c0 4.418-3.582 8-8 8s-8-3.582-8-8 3.582-8 8-8 8 3.582 8 8zm-8-4a1 1 0 100 2 1 1 0 000-2zm-1 4a1 1 0 000 2h1v3a1 1 0 102 0v-4a1 1 0 00-1-1h-2z" clip-rule="evenodd" />
</svg>
<span>Información general</span>
</a>
<?php endif; ?>
</li>
<!-- AUTOMATIZACIONES -->
<li class="nav-item">
<?php if ($permisosUsuario['automatizaciones']): ?>
<a href="/IMPORTADORES/automatizaciones/index" class="nav-link">
<svg class="icon" fill="currentColor" viewBox="0 0 20 20">
<path d="M13 7H7v6h6V7z" />
<path fill-rule="evenodd" d="M5 3a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2V9.586a1 1 0 00-.293-.707l-4.586-4.586A1 1 0 0012.414 4H5zm7 1.414L16.586 10H13a1 1 0 01-1-1V4.414z" clip-rule="evenodd"/>
</svg>
<span>Automatizaciones</span>
</a>
<?php endif; ?>
</li>
<!-- PREFERENCIAS -->
<li class="nav-item">
<?php if ($permisosUsuario['preferencias']): ?>
<?php $enDashboardPreferencias = $_SERVER['REQUEST_URI'] === '/IMPORTADORES/preferencias/index'; ?>
<a class="nav-link <?= $esVistaPreferencias ? 'active' : '' ?>"
href="<?= $enDashboardPreferencias ? '#submenuPreferenciasMobile' : '/IMPORTADORES/preferencias/index' ?>"
<?= $enDashboardPreferencias ? 'data-bs-toggle="collapse"' : '' ?>
role="button" aria-expanded="false" aria-controls="submenuPreferenciasMobile"
onclick="<?= $enDashboardPreferencias ? 'return true;' : 'event.preventDefault(); window.location.href = \'/IMPORTADORES/preferencias/index\';' ?>">
<svg class="icon" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M11.3 1.046a1 1 0 00-2.6 0L7.528 3H4a1 1 0 000 2h1v10H4a1 1 0 000 2h3.528l1.172 1.954a1 1 0 001.6 0L12.472 17H16a1 1 0 000-2h-1V5h1a1 1 0 100-2h-3.528L11.3 1.046zM9 6a1 1 0 011 1v6a1 1 0 11-2 0V7a1 1 0 011-1z" clip-rule="evenodd"/>
</svg>
<span>Preferencias</span>
<svg class="collapse-icon" fill="none" stroke="currentColor" viewBox="0 0 10 6">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m1 1 4 4 4-4"/>
</svg>
</a>
<div class="collapse" id="submenuPreferenciasMobile">
<ul class="nav flex-column submenu">
<li class="nav-item">
<a href="/IMPORTADORES/preferencias/notificaciones" class="nav-link">
Gestión de Notificaciones
</a>
</li>
<li class="nav-item">
<a href="/IMPORTADORES/preferencias/catalogos" class="nav-link">
Visualización de Catálogos
</a>
</li>
</ul>
</div>
<?php endif; ?>
</li>
<!-- SEGURIDAD -->
<li class="nav-item">
<?php if ($permisosUsuario['seguridad']): ?>
<?php $enDashboardSeguridad = $_SERVER['REQUEST_URI'] === '/IMPORTADORES/seguridad/index'; ?>
<a class="nav-link <?= $esVistaSeguridad ? 'active' : '' ?>"
href="<?= $enDashboardSeguridad ? '#submenuSeguridadMobile' : '/IMPORTADORES/seguridad/index' ?>"
<?= $enDashboardSeguridad ? 'data-bs-toggle="collapse"' : '' ?>
role="button" aria-expanded="false" aria-controls="submenuSeguridadMobile"
onclick="<?= $enDashboardSeguridad ? 'return true;' : 'event.preventDefault(); window.location.href = \'/IMPORTADORES/seguridad/index\';' ?>">
<svg class="icon" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M10 2a4 4 0 00-4 4v2H5a2 2 0 00-2 2v6a2 2 0 002 2h10a2 2 0 002-2v-6a2 2 0 00-2-2h-1V6a4 4 0 00-4-4zm2 6V6a2 2 0 10-4 0v2h4z" clip-rule="evenodd"/>
</svg>
<span>Seguridad</span>
<svg class="collapse-icon" fill="none" stroke="currentColor" viewBox="0 0 10 6">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m1 1 4 4 4-4"/>
</svg>
</a>
<div class="collapse" id="submenuSeguridadMobile">
<nav class="nav flex-column submenu">
<li class="nav-item">
<a href="/IMPORTADORES/seguridad/opciones" class="nav-link">
Opciones de Seguridad
</a>
</li>
</nav>
</div>
<?php endif; ?>
</li>
<!-- BITÁCORAS CON PERMISOS ESPECÍFICOS -->
<li class="nav-item">
<?php if (tieneAccesoBitacoras($permisosBitacoras)): ?>
<?php $enDashboardBitacoras = $_SERVER['REQUEST_URI'] === '/IMPORTADORES/bitacoras/index'; ?>
<a class="nav-link <?= $esVistaBitacoras ? 'active' : '' ?>"
href="<?= $enDashboardBitacoras ? '#submenuBitacorasMobile' : '/IMPORTADORES/bitacoras/index' ?>"
<?= $enDashboardBitacoras ? 'data-bs-toggle="collapse"' : '' ?>
role="button" aria-expanded="<?= $enDashboardBitacoras ? 'false' : 'false' ?>" aria-controls="submenuBitacorasMobile"
onclick="<?= $enDashboardBitacoras ? 'return true;' : 'event.preventDefault(); window.location.href = \'/IMPORTADORES/bitacoras/index\';' ?>">
<svg class="icon" fill="currentColor" viewBox="0 0 20 20">
<path d="M4 3a2 2 0 00-2 2v10a2 2 0 002 2h12a1 1 0 001-1V4a1 1 0 00-1-1H4zm0 2h11v10H4V5z"/>
<path d="M6 7h5v1H6V7zm0 3h8v1H6v-1z"/>
</svg>
<span>Bitácoras</span>
<svg class="collapse-icon" fill="none" stroke="currentColor" viewBox="0 0 10 6">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m1 1 4 4 4-4"/>
</svg>
</a>
<div class="collapse" id="submenuBitacorasMobile">
<nav class="nav flex-column submenu">
<?php foreach ($permisosBitacoras as $permiso => $tienePermiso): ?>
<li class="nav-item">
<?php if ($tienePermiso): ?>
<?php
$url = obtenerUrlPermiso($permiso);
$texto = obtenerTextoPermiso($permiso, $tipoUsuario);
$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 <?= $esActivo ? 'active' : '' ?>">
<span>
<?= htmlspecialchars($texto) ?>
<?php if ($scope): ?>
<span class="permission-scope"><?= $scope ?></span>
<?php endif; ?>
</span>
</a>
<?php endif; ?>
</li>
<?php endforeach; ?>
</nav>
</div>
<?php endif; ?>
</li>
</ul>
</nav>
</div>
@@ -378,55 +530,65 @@ function obtenerUrlPermiso($permiso) {
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
<script>
document.addEventListener('DOMContentLoaded', function() {
// Detectar la ruta actual
const currentPath = window.location.pathname;
// Función para abrir collapse si la página actual está en esa sección
function inicializarCollapses() {
// Preferencias
if (currentPath.includes('/preferencias/')) {
const preferencesCollapse = document.getElementById('submenuPreferencias');
if (preferencesCollapse) {
preferencesCollapse.classList.add('show');
const trigger = document.querySelector('[aria-controls="submenuPreferencias"]');
if (trigger) trigger.setAttribute('aria-expanded', 'true');
}
}
const currentPath = window.location.pathname.replace(/\/$/, '');
// Inicializar todos los collapses
document.querySelectorAll('[data-bs-toggle="collapse"]').forEach(trigger => {
const targetId = trigger.getAttribute('data-bs-target') || trigger.getAttribute('href');
const target = document.querySelector(targetId);
const icon = trigger.querySelector('.collapse-icon');
// Seguridad
if (currentPath.includes('/seguridad/')) {
const securityCollapse = document.getElementById('submenuSeguridad');
if (securityCollapse) {
securityCollapse.classList.add('show');
const trigger = document.querySelector('[aria-controls="submenuSeguridad"]');
if (trigger) trigger.setAttribute('aria-expanded', 'true');
}
}
// Bitácoras
if (currentPath.includes('/bitacoras/')) {
const bitacorasCollapse = document.getElementById('submenuBitacoras');
if (bitacorasCollapse) {
bitacorasCollapse.classList.add('show');
const trigger = document.querySelector('[aria-controls="submenuBitacoras"]');
if (trigger) trigger.setAttribute('aria-expanded', 'true');
}
}
}
// Marcar enlaces activos
function marcarEnlacesActivos() {
const navLinks = document.querySelectorAll('.nav-link');
navLinks.forEach(link => {
const href = link.getAttribute('href');
if (href && currentPath === href) {
link.classList.add('active');
}
if (!target) return;
// 1. Inicialización automática basada en la ruta
const shouldExpand = Array.from(target.querySelectorAll('a')).some(link => {
const href = link.getAttribute('href')?.replace(/\/$/, '');
return href && currentPath.startsWith(href);
});
}
// Inicializar todo
inicializarCollapses();
marcarEnlacesActivos();
// 2. Configurar el estado inicial
if (shouldExpand) {
target.classList.add('show');
trigger.setAttribute('aria-expanded', 'true');
if (icon) icon.style.transform = 'rotate(180deg)';
}
// 3. Manejar clicks manuales
trigger.addEventListener('click', function(e) {
// Solo prevenir el comportamiento por defecto si es necesario
if (this.getAttribute('href') === '#' || this.getAttribute('data-bs-target') === '#') {
e.preventDefault();
}
const isExpanded = this.getAttribute('aria-expanded') === 'true';
// Rotar el ícono inmediatamente
if (icon) {
icon.style.transform = isExpanded ? 'rotate(0deg)' : 'rotate(180deg)';
}
// Actualizar atributo ARIA
this.setAttribute('aria-expanded', !isExpanded);
});
// 4. Manejar eventos de Bootstrap para sincronización
target.addEventListener('show.bs.collapse', () => {
trigger.setAttribute('aria-expanded', 'true');
if (icon) icon.style.transform = 'rotate(180deg)';
});
target.addEventListener('hide.bs.collapse', () => {
trigger.setAttribute('aria-expanded', 'false');
if (icon) icon.style.transform = 'rotate(0deg)';
});
});
// Marcar enlaces activos
document.querySelectorAll('.nav-link').forEach(link => {
const href = link.getAttribute('href')?.replace(/\/$/, '');
if (href && (currentPath === href || currentPath.startsWith(href + '/'))) {
link.classList.add('active');
}
});
});
</script>