Preparando proyecto para migración a repositorio MVE
This commit is contained in:
@@ -27,36 +27,190 @@ if (empty($claves_pedimentos)) {
|
||||
<html lang="es">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>📋 Nuevo Pedimento de Importación</title>
|
||||
<title>Nuevo Pedimento de Importación</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>
|
||||
<!-- Font Awesome -->
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css">
|
||||
<!-- Animate.css -->
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.min.css">
|
||||
<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: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; } }
|
||||
/* 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; }
|
||||
:root {
|
||||
--primary: #2563eb;
|
||||
--primary-dark: #1d4ed8;
|
||||
--secondary: #64748b;
|
||||
--success: #059669;
|
||||
--warning: #d97706;
|
||||
--danger: #dc2626;
|
||||
--info: #0891b2;
|
||||
--light: #f8fafc;
|
||||
--dark: #0f172a;
|
||||
--border-color: #e2e8f0;
|
||||
--text-muted: #64748b;
|
||||
--bg-body: #f8fafc;
|
||||
}
|
||||
.card { border-radius: 12px; }
|
||||
.fade-in-up { animation: fadeInUp 0.8s ease-out; }
|
||||
@keyframes fadeInUp {
|
||||
from { opacity: 0; transform: translateY(30px); }
|
||||
to { opacity: 1; transform: translateY(0); }
|
||||
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;
|
||||
background-color: var(--bg-body);
|
||||
color: var(--dark);
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.main-content {
|
||||
margin-left: 280px;
|
||||
padding: 2rem;
|
||||
min-height: 100vh;
|
||||
transition: margin-left 0.3s ease;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.main-content {
|
||||
margin-left: 0;
|
||||
padding: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
.page-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 2rem;
|
||||
flex-wrap: wrap;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.page-title {
|
||||
color: var(--dark);
|
||||
font-weight: 600;
|
||||
font-size: 1.75rem;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.btn-modern {
|
||||
background: var(--primary);
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 0.625rem 1.25rem;
|
||||
border-radius: 6px;
|
||||
font-weight: 500;
|
||||
font-size: 0.875rem;
|
||||
transition: all 0.2s ease;
|
||||
text-decoration: none;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.btn-modern:hover {
|
||||
background: var(--primary-dark);
|
||||
color: white;
|
||||
transform: translateY(-1px);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.btn-modern.success {
|
||||
background: var(--success);
|
||||
}
|
||||
|
||||
.btn-modern.success:hover {
|
||||
background: #047857;
|
||||
}
|
||||
|
||||
.btn-modern.secondary {
|
||||
background: var(--secondary);
|
||||
}
|
||||
|
||||
.btn-modern.secondary:hover {
|
||||
background: #475569;
|
||||
}
|
||||
|
||||
.form-card {
|
||||
background: white;
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 12px;
|
||||
padding: 2rem;
|
||||
margin-bottom: 1.5rem;
|
||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.form-section-title {
|
||||
color: var(--dark);
|
||||
font-weight: 600;
|
||||
font-size: 1.1rem;
|
||||
margin-bottom: 1.5rem;
|
||||
padding-bottom: 0.75rem;
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.form-section-icon {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
background: var(--primary);
|
||||
color: white;
|
||||
border-radius: 4px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
|
||||
.form-label {
|
||||
color: var(--dark);
|
||||
font-weight: 500;
|
||||
font-size: 0.875rem;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.form-control, .form-select {
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 6px;
|
||||
padding: 0.625rem 0.75rem;
|
||||
font-size: 0.875rem;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.form-control:focus, .form-select:focus {
|
||||
border-color: var(--primary);
|
||||
box-shadow: 0 0 0 0.2rem rgba(37, 99, 235, 0.1);
|
||||
}
|
||||
|
||||
.alert-modern {
|
||||
border: none;
|
||||
border-radius: 8px;
|
||||
padding: 1rem 1.25rem;
|
||||
margin-bottom: 1.5rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.75rem;
|
||||
}
|
||||
|
||||
.alert-modern.warning {
|
||||
background: rgba(217, 119, 6, 0.1);
|
||||
color: #92400e;
|
||||
border-left: 4px solid var(--warning);
|
||||
}
|
||||
|
||||
.alert-modern.info {
|
||||
background: rgba(8, 145, 178, 0.1);
|
||||
color: #0e7490;
|
||||
border-left: 4px solid var(--info);
|
||||
}
|
||||
|
||||
.fade-in {
|
||||
animation: fadeIn 0.5s ease-out;
|
||||
}
|
||||
|
||||
@keyframes fadeIn {
|
||||
from { opacity: 0; transform: translateY(10px); }
|
||||
to { opacity: 1; transform: translateY(0); }
|
||||
}
|
||||
.title-glow { text-shadow: 0 0 10px rgba(0, 123, 255, 0.3); }
|
||||
/* Efecto para botones */
|
||||
@@ -69,25 +223,28 @@ if (empty($claves_pedimentos)) {
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="content">
|
||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||
<h4 class="animate__animated animate__fadeInDown title-glow">📋 Nuevo Pedimento de Importación</h4>
|
||||
<a href="/IMPORTADORES/catalogo_pedimentos/lista" class="btn btn-secondary btn-animated">
|
||||
<i class="fas fa-arrow-left"></i> Regresar a Lista
|
||||
<div class="main-content">
|
||||
<!-- Page Header -->
|
||||
<div class="page-header">
|
||||
<h1 class="page-title">Nuevo Pedimento de Importación</h1>
|
||||
<a href="/IMPORTADORES/catalogo_pedimentos/lista" class="btn-modern secondary">
|
||||
<i class="fas fa-arrow-left"></i>
|
||||
Regresar a Lista
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="card fade-in-up">
|
||||
<div class="card-body">
|
||||
<form action="/IMPORTADORES/catalogo_pedimentos/guardar" method="POST" id="formPedimento">
|
||||
<div class="row">
|
||||
<!-- Información Básica -->
|
||||
<div class="col-md-6">
|
||||
<div class="card mb-3">
|
||||
<div class="card-header">
|
||||
<h5 class="mb-0"><i class="fas fa-info-circle"></i> Información del Pedimento</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<!-- Form Container -->
|
||||
<form action="/IMPORTADORES/catalogo_pedimentos/guardar" method="POST" id="formPedimento" class="fade-in">
|
||||
<div class="row">
|
||||
<!-- Información del Pedimento -->
|
||||
<div class="col-lg-6">
|
||||
<div class="form-card">
|
||||
<h3 class="form-section-title">
|
||||
<div class="form-section-icon">
|
||||
<i class="fas fa-info-circle"></i>
|
||||
</div>
|
||||
Información del Pedimento
|
||||
</h3>
|
||||
<div class="mb-3">
|
||||
<label for="pedimento" class="form-label">Número de Pedimento <span class="text-danger">*</span></label>
|
||||
<input type="text" class="form-control" id="pedimento" name="pedimento" required
|
||||
@@ -280,11 +437,13 @@ if (empty($claves_pedimentos)) {
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<div class="d-flex justify-content-end gap-2">
|
||||
<a href="/IMPORTADORES/catalogo_pedimentos/lista" class="btn btn-secondary btn-animated">
|
||||
<i class="fas fa-times"></i> Cancelar
|
||||
<a href="/IMPORTADORES/catalogo_pedimentos/lista" class="btn-modern secondary">
|
||||
<i class="fas fa-times"></i>
|
||||
Cancelar
|
||||
</a>
|
||||
<button type="submit" class="btn btn-success btn-animated" <?= empty($claves_pedimentos) ? 'disabled' : '' ?>>
|
||||
<i class="fas fa-save"></i> Guardar Pedimento
|
||||
<button type="submit" class="btn-modern success" <?= empty($claves_pedimentos) ? 'disabled' : '' ?>>
|
||||
<i class="fas fa-save"></i>
|
||||
Guardar Pedimento
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -294,9 +453,8 @@ if (empty($claves_pedimentos)) {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- jQuery -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
|
||||
<!-- SweetAlert2 -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
|
||||
|
||||
<script>
|
||||
|
||||
@@ -32,22 +32,169 @@ if (!empty($previo['FechaFinal'])) {
|
||||
<html lang="es">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>📋 Editar Pedimento</title>
|
||||
<title>Editar Pedimento</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>
|
||||
<!-- Font Awesome -->
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css">
|
||||
<!-- Animate.css -->
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.min.css">
|
||||
<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: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; }
|
||||
:root {
|
||||
--primary: #2563eb;
|
||||
--primary-dark: #1d4ed8;
|
||||
--secondary: #64748b;
|
||||
--success: #059669;
|
||||
--warning: #d97706;
|
||||
--danger: #dc2626;
|
||||
--info: #0891b2;
|
||||
--light: #f8fafc;
|
||||
--dark: #0f172a;
|
||||
--border-color: #e2e8f0;
|
||||
--text-muted: #64748b;
|
||||
--bg-body: #f8fafc;
|
||||
}
|
||||
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;
|
||||
background-color: var(--bg-body);
|
||||
color: var(--dark);
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.main-content {
|
||||
margin-left: 280px;
|
||||
padding: 2rem;
|
||||
min-height: 100vh;
|
||||
transition: margin-left 0.3s ease;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.main-content {
|
||||
margin-left: 0;
|
||||
padding: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
.page-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 2rem;
|
||||
flex-wrap: wrap;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.page-title {
|
||||
color: var(--dark);
|
||||
font-weight: 600;
|
||||
font-size: 1.75rem;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.btn-modern {
|
||||
background: var(--primary);
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 0.625rem 1.25rem;
|
||||
border-radius: 6px;
|
||||
font-weight: 500;
|
||||
font-size: 0.875rem;
|
||||
transition: all 0.2s ease;
|
||||
text-decoration: none;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.btn-modern:hover {
|
||||
background: var(--primary-dark);
|
||||
color: white;
|
||||
transform: translateY(-1px);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.btn-modern.success {
|
||||
background: var(--success);
|
||||
}
|
||||
|
||||
.btn-modern.success:hover {
|
||||
background: #047857;
|
||||
}
|
||||
|
||||
.btn-modern.secondary {
|
||||
background: var(--secondary);
|
||||
}
|
||||
|
||||
.btn-modern.secondary:hover {
|
||||
background: #475569;
|
||||
}
|
||||
|
||||
.form-card {
|
||||
background: white;
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 12px;
|
||||
padding: 2rem;
|
||||
margin-bottom: 1.5rem;
|
||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.form-section-title {
|
||||
color: var(--dark);
|
||||
font-weight: 600;
|
||||
font-size: 1.1rem;
|
||||
margin-bottom: 1.5rem;
|
||||
padding-bottom: 0.75rem;
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.form-section-icon {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
background: var(--primary);
|
||||
color: white;
|
||||
border-radius: 4px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
|
||||
.form-label {
|
||||
color: var(--dark);
|
||||
font-weight: 500;
|
||||
font-size: 0.875rem;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.form-control, .form-select {
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 6px;
|
||||
padding: 0.625rem 0.75rem;
|
||||
font-size: 0.875rem;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.form-control:focus, .form-select:focus {
|
||||
border-color: var(--primary);
|
||||
box-shadow: 0 0 0 0.2rem rgba(37, 99, 235, 0.1);
|
||||
}
|
||||
|
||||
.fade-in {
|
||||
animation: fadeIn 0.5s ease-out;
|
||||
}
|
||||
|
||||
@keyframes fadeIn {
|
||||
from { opacity: 0; transform: translateY(10px); }
|
||||
to { opacity: 1; transform: translateY(0); }
|
||||
}
|
||||
/* A partir de dispositivos medianos (>=768px), deja espacio lateral */
|
||||
@media (min-width: 768px) { .content { margin-left: 250px; } }
|
||||
/* En móviles, sin margen lateral */
|
||||
@@ -77,20 +224,21 @@ if (!empty($previo['FechaFinal'])) {
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="content">
|
||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||
<h4 class="animate__animated animate__fadeInDown title-glow">📋 Editar Pedimento #<?= htmlspecialchars($previo['IdPrevio']) ?></h4>
|
||||
<a href="/IMPORTADORES/catalogo_pedimentos/lista" class="btn btn-secondary btn-animated">
|
||||
<i class="fas fa-arrow-left"></i> Regresar a Lista
|
||||
<div class="main-content">
|
||||
<!-- Page Header -->
|
||||
<div class="page-header">
|
||||
<h1 class="page-title">Editar Pedimento #<?= htmlspecialchars($previo['IdPrevio']) ?></h1>
|
||||
<a href="/IMPORTADORES/catalogo_pedimentos/lista" class="btn-modern secondary">
|
||||
<i class="fas fa-arrow-left"></i>
|
||||
Regresar a Lista
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="card fade-in-up">
|
||||
<div class="card-body">
|
||||
<form action="/IMPORTADORES/catalogo_pedimentos/actualizar" method="POST" id="formPedimento">
|
||||
<input type="hidden" name="id_previo" value="<?= htmlspecialchars($previo['IdPrevio']) ?>">
|
||||
|
||||
<div class="row">
|
||||
<!-- Form Container -->
|
||||
<form action="/IMPORTADORES/catalogo_pedimentos/actualizar" method="POST" id="formPedimento" class="fade-in">
|
||||
<input type="hidden" name="id_previo" value="<?= htmlspecialchars($previo['IdPrevio']) ?>">
|
||||
|
||||
<div class="row">
|
||||
<!-- Información Básica -->
|
||||
<div class="col-md-6">
|
||||
<div class="card mb-3">
|
||||
@@ -310,14 +458,11 @@ if (!empty($previo['FechaFinal'])) {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<!-- jQuery -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
|
||||
<!-- SweetAlert2 -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
|
||||
|
||||
<script>
|
||||
|
||||
@@ -4,160 +4,837 @@
|
||||
<html lang="es">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>📋 Catálogo de Pedimentos</title>
|
||||
<title>Catálogo de Pedimentos</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>
|
||||
<!-- Font Awesome -->
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css">
|
||||
<!-- Animate.css -->
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.min.css">
|
||||
<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: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; } }
|
||||
/* 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; }
|
||||
:root {
|
||||
--primary: #2563eb;
|
||||
--primary-dark: #1d4ed8;
|
||||
--secondary: #64748b;
|
||||
--success: #059669;
|
||||
--warning: #d97706;
|
||||
--danger: #dc2626;
|
||||
--info: #0891b2;
|
||||
--light: #f8fafc;
|
||||
--dark: #0f172a;
|
||||
--border-color: #e2e8f0;
|
||||
--text-muted: #64748b;
|
||||
--bg-body: #f8fafc;
|
||||
}
|
||||
.card { border-radius: 12px; }
|
||||
/* Animaciones personalizadas */
|
||||
.card-hover { transition: all 0.3s ease; cursor: pointer; }
|
||||
.card-hover:hover { transform: translateY(-8px) scale(1.02); box-shadow: 0 1rem 2rem rgba(0, 0, 0, 0.15) !important; }
|
||||
.btn-pulse { animation: pulse 2s infinite; }
|
||||
@keyframes pulse {
|
||||
0% { transform: scale(1); }
|
||||
50% { transform: scale(1.05); }
|
||||
100% { transform: scale(1); }
|
||||
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.fade-in-up { animation: fadeInUp 0.8s ease-out; }
|
||||
@keyframes fadeInUp {
|
||||
from { opacity: 0; transform: translateY(30px); }
|
||||
to { opacity: 1; transform: translateY(0); }
|
||||
|
||||
body {
|
||||
font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;
|
||||
background-color: var(--bg-body);
|
||||
color: var(--dark);
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.main-content {
|
||||
margin-left: 280px;
|
||||
padding: 2rem;
|
||||
min-height: 100vh;
|
||||
transition: margin-left 0.3s ease;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.main-content {
|
||||
margin-left: 0;
|
||||
padding: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
.page-header {
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.page-title {
|
||||
color: var(--dark);
|
||||
font-weight: 600;
|
||||
font-size: 1.75rem;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.page-subtitle {
|
||||
color: var(--text-muted);
|
||||
font-weight: 400;
|
||||
font-size: 0.95rem;
|
||||
}
|
||||
|
||||
.feature-card {
|
||||
background: white;
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 12px;
|
||||
padding: 1.5rem;
|
||||
height: 100%;
|
||||
transition: all 0.2s ease;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.feature-card:hover {
|
||||
transform: translateY(-4px);
|
||||
box-shadow: 0 8px 25px rgba(0, 0, 0, 0.1);
|
||||
border-color: var(--primary);
|
||||
}
|
||||
|
||||
.feature-icon {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
border-radius: 8px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-bottom: 1rem;
|
||||
font-size: 1.25rem;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.feature-icon.primary { background: var(--primary); }
|
||||
.feature-icon.success { background: var(--success); }
|
||||
.feature-icon.info { background: var(--info); }
|
||||
|
||||
.feature-title {
|
||||
font-weight: 600;
|
||||
font-size: 1.1rem;
|
||||
color: var(--dark);
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.feature-description {
|
||||
color: var(--text-muted);
|
||||
font-size: 0.9rem;
|
||||
margin-bottom: 1.5rem;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.btn-feature {
|
||||
background: var(--primary);
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 0.625rem 1.25rem;
|
||||
border-radius: 6px;
|
||||
font-weight: 500;
|
||||
font-size: 0.875rem;
|
||||
transition: all 0.2s ease;
|
||||
width: 100%;
|
||||
text-decoration: none;
|
||||
display: inline-block;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.btn-feature:hover {
|
||||
background: var(--primary-dark);
|
||||
color: white;
|
||||
transform: translateY(-1px);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.btn-feature.success {
|
||||
background: var(--success);
|
||||
}
|
||||
|
||||
.btn-feature.success:hover {
|
||||
background: #047857;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.btn-feature.info {
|
||||
background: var(--info);
|
||||
}
|
||||
|
||||
.btn-feature.info:hover {
|
||||
background: #0e7490;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.info-card {
|
||||
background: white;
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 12px;
|
||||
padding: 1.5rem;
|
||||
margin-top: 2rem;
|
||||
}
|
||||
|
||||
.info-card-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 1rem;
|
||||
padding-bottom: 0.75rem;
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
}
|
||||
|
||||
.info-icon {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 6px;
|
||||
background: rgba(37, 99, 235, 0.1);
|
||||
color: var(--primary);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-right: 0.75rem;
|
||||
}
|
||||
|
||||
.info-title {
|
||||
font-weight: 600;
|
||||
font-size: 1.1rem;
|
||||
color: var(--dark);
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.info-text {
|
||||
color: var(--text-muted);
|
||||
font-size: 0.95rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.feature-list {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.feature-list li {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0.5rem 0;
|
||||
color: var(--text-muted);
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.feature-list li i {
|
||||
color: var(--success);
|
||||
margin-right: 0.75rem;
|
||||
width: 16px;
|
||||
}
|
||||
|
||||
.fade-in {
|
||||
animation: fadeIn 0.5s ease-out;
|
||||
}
|
||||
|
||||
@keyframes fadeIn {
|
||||
from { opacity: 0; transform: translateY(10px); }
|
||||
to { opacity: 1; transform: translateY(0); }
|
||||
}
|
||||
/* Responsive animations */
|
||||
@media (max-width: 768px) { .card-hover:hover { transform: translateY(-4px) scale(1.01); } }
|
||||
/* Efecto de glow para elementos activos */
|
||||
.btn.active { box-shadow: 0 0 20px rgba(var(--bs-primary-rgb), 0.5); }
|
||||
/* Animación para el título */
|
||||
.title-glow { text-shadow: 0 0 10px rgba(0, 123, 255, 0.3); }
|
||||
/* Efecto para botones */
|
||||
.btn-animated { transition: all 0.3s ease; position: relative; overflow: hidden; }
|
||||
.btn-animated:hover { transform: translateY(-2px); box-shadow: 0 5px 15px rgba(0, 0, 0, 0.2); }
|
||||
.btn-animated::before { content: ''; position: absolute; top: 0; left: -100%; width: 100%; height: 100%;
|
||||
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2), transparent); transition: left 0.5s; }
|
||||
.btn-animated:hover::before { left: 100%; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="content">
|
||||
<h4 class="mb-4 animate__animated animate__fadeInDown title-glow">📋 Catálogo de Pedimentos</h4>
|
||||
|
||||
<div class="row mb-4">
|
||||
<div class="col-md-6 col-lg-4 mb-3">
|
||||
<div class="card bg-primary text-white card-hover fade-in-up">
|
||||
<div class="card-body">
|
||||
<div class="d-flex justify-content-between">
|
||||
<div>
|
||||
<h5 class="card-title">Lista de Pedimentos</h5>
|
||||
<p class="card-text">Visualizar y gestionar todos los pedimentos registrados</p>
|
||||
</div>
|
||||
<div class="align-self-center">
|
||||
<i class="fas fa-list fa-2x"></i>
|
||||
</div>
|
||||
</div>
|
||||
<a href="/IMPORTADORES/catalogo_pedimentos/lista" class="btn btn-outline-light btn-sm btn-animated">
|
||||
Ver Lista <i class="fas fa-arrow-right"></i>
|
||||
</a>
|
||||
<div class="main-content">
|
||||
<!-- Page Header -->
|
||||
<div class="page-header">
|
||||
<h1 class="page-title">Catálogo de Pedimentos</h1>
|
||||
<p class="page-subtitle">Gestiona y administra todos los pedimentos de importación del sistema</p>
|
||||
</div>
|
||||
|
||||
<!-- Features Grid -->
|
||||
<div class="row g-4 mb-4">
|
||||
<div class="col-lg-4 col-md-6">
|
||||
<div class="feature-card fade-in" onclick="window.location.href='/IMPORTADORES/catalogo_pedimentos/lista'">
|
||||
<div class="feature-icon primary">
|
||||
<i class="fas fa-list"></i>
|
||||
</div>
|
||||
<h3 class="feature-title">Lista de Pedimentos</h3>
|
||||
<p class="feature-description">Visualiza y gestiona todos los pedimentos registrados en el sistema con herramientas de búsqueda y filtrado avanzado.</p>
|
||||
<a href="/IMPORTADORES/catalogo_pedimentos/lista" class="btn-feature">
|
||||
Ver Lista
|
||||
<i class="fas fa-arrow-right ms-2"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6 col-lg-4 mb-3">
|
||||
<div class="card bg-success text-white card-hover fade-in-up">
|
||||
<div class="card-body">
|
||||
<div class="d-flex justify-content-between">
|
||||
<div>
|
||||
<h5 class="card-title">Nuevo Pedimento</h5>
|
||||
<p class="card-text">Registrar un nuevo pedimento en el sistema</p>
|
||||
</div>
|
||||
<div class="align-self-center">
|
||||
<i class="fas fa-plus-circle fa-2x"></i>
|
||||
</div>
|
||||
</div>
|
||||
<a href="/IMPORTADORES/catalogo_pedimentos/crear" class="btn btn-outline-light btn-sm btn-animated">
|
||||
Crear Nuevo <i class="fas fa-plus"></i>
|
||||
</a>
|
||||
<div class="col-lg-4 col-md-6">
|
||||
<div class="feature-card fade-in" onclick="window.location.href='/IMPORTADORES/catalogo_pedimentos/crear'">
|
||||
<div class="feature-icon success">
|
||||
<i class="fas fa-plus-circle"></i>
|
||||
</div>
|
||||
<h3 class="feature-title">Nuevo Pedimento</h3>
|
||||
<p class="feature-description">Registra un nuevo pedimento con toda la información requerida y documentación necesaria para el proceso de importación.</p>
|
||||
<a href="/IMPORTADORES/catalogo_pedimentos/crear" class="btn-feature success">
|
||||
Crear Nuevo
|
||||
<i class="fas fa-plus ms-2"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6 col-lg-4 mb-3">
|
||||
<div class="card bg-info text-white card-hover fade-in-up">
|
||||
<div class="card-body">
|
||||
<div class="d-flex justify-content-between">
|
||||
<div>
|
||||
<h5 class="card-title">Reportes</h5>
|
||||
<p class="card-text">Generar reportes y estadísticas de pedimentos</p>
|
||||
</div>
|
||||
<div class="align-self-center">
|
||||
<i class="fas fa-chart-bar fa-2x"></i>
|
||||
</div>
|
||||
</div>
|
||||
<button class="btn btn-outline-light btn-sm btn-animated" onclick="proximamente()">
|
||||
Ver Reportes <i class="fas fa-chart-line"></i>
|
||||
<div class="col-lg-4 col-md-6">
|
||||
<div class="feature-card fade-in">
|
||||
<div class="feature-icon info">
|
||||
<i class="fas fa-upload"></i>
|
||||
</div>
|
||||
<h3 class="feature-title">Importar Archivo</h3>
|
||||
<p class="feature-description">Carga masiva de pedimentos desde archivo de texto con formato específico del sistema aduanero.</p>
|
||||
<button class="btn-feature info" onclick="importarPedimentos()">
|
||||
Importar Datos
|
||||
<i class="fas fa-file-import ms-2"></i>
|
||||
</button>
|
||||
<div class="mt-2">
|
||||
<button class="btn btn-sm btn-outline-secondary" onclick="verificarPedimentosExistentes()" style="border-radius: 6px; font-size: 0.8rem;">
|
||||
<i class="fas fa-search me-1"></i>Ver Existentes
|
||||
</button>
|
||||
<button class="btn btn-sm btn-outline-danger" onclick="limpiarBaseDatos()" style="border-radius: 6px; font-size: 0.8rem;">
|
||||
<i class="fas fa-trash me-1"></i>Limpiar BD
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-lg-4 col-md-6">
|
||||
<div class="feature-card fade-in">
|
||||
<div class="feature-icon warning" style="background: var(--warning);">
|
||||
<i class="fas fa-chart-bar"></i>
|
||||
</div>
|
||||
<h3 class="feature-title">Reportes</h3>
|
||||
<p class="feature-description">Genera reportes detallados y estadísticas de pedimentos para análisis y seguimiento de operaciones.</p>
|
||||
<button class="btn-feature" style="background: var(--warning);" onclick="proximamente()">
|
||||
Ver Reportes
|
||||
<i class="fas fa-chart-line ms-2"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<div class="card fade-in-up">
|
||||
<div class="card-header">
|
||||
<h5 class="mb-0"><i class="fas fa-info-circle"></i> Información del Módulo</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<p class="text-muted">
|
||||
El catálogo de pedimentos le permite gestionar de manera eficiente todos los pedimentos
|
||||
de importación. Desde aquí puede:
|
||||
</p>
|
||||
<ul class="list-unstyled">
|
||||
<li><i class="fas fa-check text-success"></i> Registrar nuevos pedimentos con toda la información requerida</li>
|
||||
<li><i class="fas fa-check text-success"></i> Consultar y editar pedimentos existentes</li>
|
||||
<li><i class="fas fa-check text-success"></i> Realizar búsquedas avanzadas por diversos criterios</li>
|
||||
<li><i class="fas fa-check text-success"></i> Exportar información para reportes</li>
|
||||
<li><i class="fas fa-check text-success"></i> Mantener un histórico completo de operaciones</li>
|
||||
</ul>
|
||||
<!-- Information Card -->
|
||||
<div class="info-card">
|
||||
<div class="info-card-header">
|
||||
<div class="info-icon">
|
||||
<i class="fas fa-info-circle"></i>
|
||||
</div>
|
||||
<h3 class="info-title">Información del Módulo</h3>
|
||||
</div>
|
||||
<p class="info-text">
|
||||
El catálogo de pedimentos te permite gestionar de manera eficiente todos los pedimentos
|
||||
de importación. Desde aquí puedes realizar las siguientes acciones:
|
||||
</p>
|
||||
<ul class="feature-list">
|
||||
<li>
|
||||
<i class="fas fa-check"></i>
|
||||
Registrar nuevos pedimentos con toda la información requerida
|
||||
</li>
|
||||
<li>
|
||||
<i class="fas fa-check"></i>
|
||||
Consultar y editar pedimentos existentes
|
||||
</li>
|
||||
<li>
|
||||
<i class="fas fa-check"></i>
|
||||
Realizar búsquedas avanzadas por diversos criterios
|
||||
</li>
|
||||
<li>
|
||||
<i class="fas fa-check"></i>
|
||||
Exportar información para reportes y análisis
|
||||
</li>
|
||||
<li>
|
||||
<i class="fas fa-check"></i>
|
||||
Mantener un histórico completo de operaciones
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Modal para Importar Archivo -->
|
||||
<div class="modal fade" id="modalImportarPedimentos" tabindex="-1" aria-labelledby="modalImportarLabel" aria-hidden="true">
|
||||
<div class="modal-dialog modal-lg">
|
||||
<div class="modal-content" style="border-radius: 12px; border: none;">
|
||||
<div class="modal-header" style="background: linear-gradient(135deg, var(--info) 0%, #0e7490 100%); color: white; border-radius: 12px 12px 0 0;">
|
||||
<h5 class="modal-title" id="modalImportarLabel">
|
||||
<i class="fas fa-upload me-2"></i>
|
||||
Importar Pedimentos desde Archivo
|
||||
</h5>
|
||||
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="modal-body p-4">
|
||||
<div class="alert alert-info d-flex align-items-start" style="border: none; border-radius: 8px; background: rgba(8, 145, 178, 0.1); border-left: 4px solid var(--info);">
|
||||
<i class="fas fa-info-circle me-2 mt-1" style="color: var(--info);"></i>
|
||||
<div>
|
||||
<strong>Formato del archivo:</strong>
|
||||
<p class="mb-1">El sistema reconoce archivos con los siguientes códigos:</p>
|
||||
<ul class="mb-2 small">
|
||||
<li><strong>501:</strong> Datos del pedimento (número, RFC, fechas)</li>
|
||||
<li><strong>505:</strong> Información de facturas</li>
|
||||
<li><strong>551:</strong> Partidas de mercancías</li>
|
||||
</ul>
|
||||
<p class="mb-0 small text-muted">
|
||||
<i class="fas fa-calendar-day me-1"></i>
|
||||
<strong>Archivos julianos:</strong> Se aceptan extensiones de día del año (.001 a .366)
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<form id="formImportarPedimentos" enctype="multipart/form-data">
|
||||
<div class="mb-3">
|
||||
<label for="archivoImportar" class="form-label fw-semibold">
|
||||
<i class="fas fa-file-text me-1"></i>
|
||||
Seleccionar archivos
|
||||
</label>
|
||||
<input type="file" class="form-control" id="archivoImportar" name="archivos[]" multiple required style="border-radius: 8px; border: 1px solid var(--border-color);">
|
||||
<div class="form-text">Formatos soportados: .txt, .dat, archivos julianos (.001-.366). Puedes seleccionar varios archivos.</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" id="validarDuplicados" name="validar_duplicados" checked>
|
||||
<label class="form-check-label fw-medium" for="validarDuplicados">
|
||||
Validar pedimentos duplicados
|
||||
</label>
|
||||
<div class="form-text">Evita importar pedimentos que ya existen en el sistema</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="previewContainer" style="display: none;" class="mb-3">
|
||||
<h6 class="fw-semibold mb-2">
|
||||
<i class="fas fa-eye me-1"></i>
|
||||
Vista previa del archivo
|
||||
</h6>
|
||||
<div id="previewContent" class="p-3" style="background: #f8fafc; border-radius: 8px; border: 1px solid var(--border-color); max-height: 200px; overflow-y: auto; font-family: monospace; font-size: 0.85rem;"></div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="modal-footer" style="border-top: 1px solid var(--border-color); border-radius: 0 0 12px 12px;">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal" style="border-radius: 8px;">
|
||||
<i class="fas fa-times me-1"></i>
|
||||
Cancelar
|
||||
</button>
|
||||
<button type="button" class="btn btn-primary" id="btnProcesarArchivo" style="border-radius: 8px; background: var(--info); border-color: var(--info);" disabled>
|
||||
<i class="fas fa-cogs me-1"></i>
|
||||
Procesar Archivos
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- jQuery -->
|
||||
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
|
||||
<!-- SweetAlert2 -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
|
||||
|
||||
<script>
|
||||
// Toast helper (Bootstrap 5): muestra un toast en la esquina superior derecha
|
||||
function ensureToastContainer() {
|
||||
let container = document.getElementById('toastContainer');
|
||||
if (!container) {
|
||||
container = document.createElement('div');
|
||||
container.id = 'toastContainer';
|
||||
container.className = 'position-fixed';
|
||||
container.style.top = 'calc(var(--navbar-height, 70px) + 10px)';
|
||||
container.style.right = '16px';
|
||||
container.style.zIndex = '11000';
|
||||
document.body.appendChild(container);
|
||||
}
|
||||
return container;
|
||||
}
|
||||
|
||||
function mostrarToastImportacion(estadisticas) {
|
||||
const ped = Number(estadisticas.pedimentos_procesados || 0);
|
||||
const fac = Number(estadisticas.facturas_procesadas || 0);
|
||||
const par = Number(estadisticas.partidas_procesadas || 0);
|
||||
const dup = Number(estadisticas.duplicados || 0);
|
||||
const err = Number(estadisticas.errores || 0);
|
||||
|
||||
const variant = err > 0 ? 'danger' : (dup > 0 ? 'warning' : 'success');
|
||||
const title = err > 0 ? 'Importación con advertencias' : 'Importación completada';
|
||||
|
||||
const container = ensureToastContainer();
|
||||
const toast = document.createElement('div');
|
||||
toast.className = `toast align-items-center text-bg-${variant} border-0 shadow`;
|
||||
toast.setAttribute('role', 'alert');
|
||||
toast.setAttribute('aria-live', 'assertive');
|
||||
toast.setAttribute('aria-atomic', 'true');
|
||||
|
||||
toast.innerHTML = `
|
||||
<div class="d-flex">
|
||||
<div class="toast-body">
|
||||
<div class="fw-semibold mb-1"><i class="fas ${variant==='success' ? 'fa-check-circle' : (variant==='warning' ? 'fa-exclamation-triangle' : 'fa-times-circle')} me-1"></i>${title}</div>
|
||||
<div class="small">
|
||||
<span class="me-2">Pedimentos <span class="badge bg-light text-dark">${ped}</span></span>
|
||||
<span class="me-2">Facturas <span class="badge bg-light text-dark">${fac}</span></span>
|
||||
<span class="me-2">Partidas <span class="badge bg-light text-dark">${par}</span></span>
|
||||
${dup > 0 ? `<span class=\"me-2\">Duplicados <span class=\"badge bg-dark\">${dup}</span></span>` : ''}
|
||||
${err > 0 ? `<span class=\"me-2\">Errores <span class=\"badge bg-dark\">${err}</span></span>` : ''}
|
||||
</div>
|
||||
</div>
|
||||
<button type="button" class="btn-close btn-close-white me-2 m-auto" data-bs-dismiss="toast" aria-label="Close"></button>
|
||||
</div>`;
|
||||
|
||||
container.appendChild(toast);
|
||||
const bsToast = new bootstrap.Toast(toast, { autohide: true, delay: 7000 });
|
||||
bsToast.show();
|
||||
toast.addEventListener('hidden.bs.toast', () => toast.remove());
|
||||
}
|
||||
// Base URL del proyecto (generada desde PHP)
|
||||
const BASE_URL = '<?php echo "http" . (isset($_SERVER["HTTPS"]) && $_SERVER["HTTPS"] === "on" ? "s" : "") . "://" . $_SERVER["HTTP_HOST"] . "/IMPORTADORES"; ?>';
|
||||
console.log('Base URL:', BASE_URL);
|
||||
|
||||
// Initialize page animations
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const fadeElements = document.querySelectorAll('.fade-in');
|
||||
fadeElements.forEach((element, index) => {
|
||||
element.style.animationDelay = (index * 0.1) + 's';
|
||||
});
|
||||
|
||||
// Add click handlers for cards
|
||||
const cards = document.querySelectorAll('.feature-card');
|
||||
cards.forEach(card => {
|
||||
card.addEventListener('mouseenter', function() {
|
||||
this.style.transform = 'translateY(-6px)';
|
||||
});
|
||||
|
||||
card.addEventListener('mouseleave', function() {
|
||||
this.style.transform = 'translateY(0)';
|
||||
});
|
||||
});
|
||||
|
||||
// Configurar eventos del modal de importación
|
||||
configurarModalImportacion();
|
||||
});
|
||||
|
||||
// Funciones para las acciones de las cards
|
||||
function irListaPedimentos() {
|
||||
window.location.href = 'lista.php';
|
||||
}
|
||||
|
||||
function irCrearPedimento() {
|
||||
window.location.href = 'crear.php';
|
||||
}
|
||||
|
||||
function importarPedimentos() {
|
||||
const modal = new bootstrap.Modal(document.getElementById('modalImportarPedimentos'));
|
||||
modal.show();
|
||||
}
|
||||
|
||||
function proximamente() {
|
||||
Swal.fire({
|
||||
icon: 'info',
|
||||
title: 'Próximamente',
|
||||
text: 'Esta función estará disponible próximamente.',
|
||||
confirmButtonColor: '#0d6efd'
|
||||
confirmButtonColor: '#2563eb',
|
||||
customClass: {
|
||||
popup: 'rounded-3'
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Configuración del modal de importación
|
||||
function configurarModalImportacion() {
|
||||
const archivoInput = document.getElementById('archivoImportar');
|
||||
const previewContainer = document.getElementById('previewContainer');
|
||||
const previewContent = document.getElementById('previewContent');
|
||||
const btnProcesar = document.getElementById('btnProcesarArchivo');
|
||||
|
||||
if (!archivoInput) return; // Si no existe el elemento, salir
|
||||
|
||||
// Manejar selección de archivos (múltiples)
|
||||
archivoInput.addEventListener('change', function(e) {
|
||||
const archivos = Array.from(e.target.files || []);
|
||||
if (archivos.length > 0) {
|
||||
// Validar extensiones de todos los archivos seleccionados
|
||||
const extensionesValidas = ['txt', 'dat'];
|
||||
for (const file of archivos) {
|
||||
const nombreArchivo = file.name.toLowerCase();
|
||||
const extension = nombreArchivo.split('.').pop();
|
||||
const esJuliana = /^\d{3}$/.test(extension); // 3 dígitos exactos
|
||||
if (!extensionesValidas.includes(extension) && !esJuliana) {
|
||||
Swal.fire({
|
||||
icon: 'error',
|
||||
title: 'Archivo no válido',
|
||||
text: `"${file.name}" no es válido. Usa .txt, .dat o extensión juliana (.001-.366)`,
|
||||
confirmButtonColor: '#dc2626',
|
||||
customClass: { popup: 'rounded-3' }
|
||||
});
|
||||
archivoInput.value = '';
|
||||
previewContainer.style.display = 'none';
|
||||
btnProcesar.disabled = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Mostrar vista previa del PRIMER archivo y el total seleccionado
|
||||
const reader = new FileReader();
|
||||
reader.onload = function(e) {
|
||||
const contenido = e.target.result;
|
||||
mostrarVistaPrevia(contenido, archivos.length);
|
||||
btnProcesar.disabled = false;
|
||||
};
|
||||
reader.readAsText(archivos[0]);
|
||||
} else {
|
||||
previewContainer.style.display = 'none';
|
||||
btnProcesar.disabled = true;
|
||||
}
|
||||
});
|
||||
|
||||
// Manejar procesamiento del archivo
|
||||
btnProcesar.addEventListener('click', function() {
|
||||
procesarArchivoImportacion();
|
||||
});
|
||||
}
|
||||
|
||||
function mostrarVistaPrevia(contenido, totalArchivos = 1) {
|
||||
const previewContainer = document.getElementById('previewContainer');
|
||||
const previewContent = document.getElementById('previewContent');
|
||||
const lineas = contenido.split('\n').slice(0, 10); // Mostrar solo las primeras 10 líneas
|
||||
|
||||
const preview = lineas.map(linea => {
|
||||
// Resaltar diferentes tipos de registros
|
||||
let clase = '';
|
||||
if (linea.startsWith('501')) {
|
||||
clase = 'style="color: #059669; font-weight: bold;"'; // Verde para pedimentos
|
||||
} else if (linea.startsWith('505')) {
|
||||
clase = 'style="color: #d97706; font-weight: bold;"'; // Naranja para facturas
|
||||
} else if (linea.startsWith('551')) {
|
||||
clase = 'style="color: #2563eb; font-weight: bold;"'; // Azul para partidas
|
||||
}
|
||||
return `<div ${clase}>${linea}</div>`;
|
||||
}).join('');
|
||||
|
||||
let extra = '';
|
||||
if (contenido.split('\n').length > 10) {
|
||||
extra += '<div style="color: #6b7280; font-style: italic;">... y más líneas</div>';
|
||||
}
|
||||
if (totalArchivos > 1) {
|
||||
const restantes = totalArchivos - 1;
|
||||
extra += `<div class="mt-2" style="color: #374151; font-weight: 600;">+ ${restantes} archivo${restantes === 1 ? '' : 's'} más seleccionado${restantes === 1 ? '' : 's'}</div>`;
|
||||
}
|
||||
previewContent.innerHTML = preview + extra;
|
||||
previewContainer.style.display = 'block';
|
||||
}
|
||||
|
||||
function procesarArchivoImportacion() {
|
||||
const formData = new FormData(document.getElementById('formImportarPedimentos'));
|
||||
|
||||
// Mostrar loading en el botón
|
||||
const btnProcesar = document.getElementById('btnProcesarArchivo');
|
||||
const textoOriginal = btnProcesar.innerHTML;
|
||||
btnProcesar.innerHTML = '<i class="fas fa-spinner fa-spin me-1"></i>Procesando...';
|
||||
btnProcesar.disabled = true;
|
||||
|
||||
// Llamada AJAX al backend
|
||||
const url = `${BASE_URL}/public/importar_pedimentos.php`;
|
||||
console.log('Attempting to fetch:', url);
|
||||
|
||||
fetch(url, {
|
||||
method: 'POST',
|
||||
body: formData
|
||||
})
|
||||
.then(response => {
|
||||
console.log('Response status:', response.status);
|
||||
console.log('Response headers:', response.headers);
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
return response.json();
|
||||
})
|
||||
.then(data => {
|
||||
console.log('Response data:', data);
|
||||
// Restaurar botón
|
||||
btnProcesar.innerHTML = textoOriginal;
|
||||
btnProcesar.disabled = false;
|
||||
|
||||
if (data.success) {
|
||||
// Verificar si es respuesta de prueba o de importación real
|
||||
if (data.estadisticas) {
|
||||
// Respuesta de importación real
|
||||
const estadisticas = data.estadisticas;
|
||||
// Mostrar toast superior derecho en lugar del modal
|
||||
mostrarToastImportacion(estadisticas);
|
||||
} else if (data.diagnostico) {
|
||||
// Respuesta de diagnóstico
|
||||
let mensaje = `🔍 Diagnóstico del Sistema:\n\n`;
|
||||
|
||||
// Mostrar checks
|
||||
if (data.checks) {
|
||||
mensaje += `📊 Estado de Componentes:\n`;
|
||||
mensaje += `• Sesión: ${data.checks.sesion ? '✅' : '❌'}\n`;
|
||||
mensaje += `• Usuario ID: ${data.checks.usuario_id || 'N/A'}\n`;
|
||||
mensaje += `• Tipo Usuario: ${data.checks.tipo_usuario || 'N/A'}\n`;
|
||||
mensaje += `• Método POST: ${data.checks.metodo_post ? '✅' : '❌'}\n`;
|
||||
mensaje += `• Archivo recibido: ${data.checks.archivo_recibido ? '✅' : '❌'}\n`;
|
||||
mensaje += `• Conexión DB: ${data.checks.conexion_db ? '✅' : '❌'}\n`;
|
||||
mensaje += `• Dir. temporal: ${data.checks.directorio_temp_existe ? '✅' : '❌'}\n\n`;
|
||||
}
|
||||
|
||||
// Mostrar errores potenciales
|
||||
if (data.errores_potenciales && data.errores_potenciales.length > 0) {
|
||||
mensaje += `⚠️ Problemas detectados:\n`;
|
||||
data.errores_potenciales.forEach(error => {
|
||||
mensaje += `• ${error}\n`;
|
||||
});
|
||||
mensaje += `\n`;
|
||||
}
|
||||
|
||||
// Información del archivo
|
||||
if (data.checks?.archivo_recibido) {
|
||||
mensaje += `📁 Archivo: ${data.checks.archivo_nombre}\n`;
|
||||
if (data.total_lineas) {
|
||||
mensaje += `📏 Total líneas: ${data.total_lineas}\n`;
|
||||
}
|
||||
}
|
||||
|
||||
const icono = data.errores_potenciales.length > 0 ? 'warning' : 'success';
|
||||
const titulo = data.errores_potenciales.length > 0 ? 'Problemas Detectados' : 'Sistema OK';
|
||||
|
||||
Swal.fire({
|
||||
icon: icono,
|
||||
title: titulo,
|
||||
text: mensaje,
|
||||
confirmButtonColor: icono === 'warning' ? '#d97706' : '#059669',
|
||||
customClass: {
|
||||
popup: 'rounded-3'
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// Respuesta de prueba genérica
|
||||
let mensaje = `✅ Conexión exitosa!\n\n`;
|
||||
mensaje += `📁 Archivo: ${data.files_data?.nombre || 'No detectado'}\n`;
|
||||
mensaje += `📏 Tamaño: ${data.files_data?.tamaño || 'N/A'} bytes\n`;
|
||||
mensaje += `🕒 Timestamp: ${data.timestamp}\n`;
|
||||
mensaje += `👤 Usuario: ${data.usuario}\n\n`;
|
||||
mensaje += `ℹ️ Esto es una prueba de conexión.`;
|
||||
|
||||
Swal.fire({
|
||||
icon: 'info',
|
||||
title: 'Prueba de Conexión',
|
||||
text: mensaje,
|
||||
confirmButtonColor: '#2563eb',
|
||||
customClass: {
|
||||
popup: 'rounded-3'
|
||||
}
|
||||
});
|
||||
}
|
||||
} else {
|
||||
// Mostrar error
|
||||
Swal.fire({
|
||||
icon: 'error',
|
||||
title: 'Error en la Importación',
|
||||
text: data.message || 'Ocurrió un error al procesar el archivo',
|
||||
confirmButtonColor: '#dc2626',
|
||||
customClass: {
|
||||
popup: 'rounded-3'
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Cerrar modal solo si fue exitoso
|
||||
if (data.success) {
|
||||
const modal = bootstrap.Modal.getInstance(document.getElementById('modalImportarPedimentos'));
|
||||
modal.hide();
|
||||
|
||||
// Limpiar formulario
|
||||
document.getElementById('formImportarPedimentos').reset();
|
||||
document.getElementById('previewContainer').style.display = 'none';
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
// Restaurar botón en caso de error
|
||||
btnProcesar.innerHTML = textoOriginal;
|
||||
btnProcesar.disabled = false;
|
||||
|
||||
console.error('Error:', error);
|
||||
Swal.fire({
|
||||
icon: 'error',
|
||||
title: 'Error de Conexión',
|
||||
text: 'No se pudo conectar con el servidor. Verifique su conexión e intente nuevamente.',
|
||||
confirmButtonColor: '#dc2626',
|
||||
customClass: {
|
||||
popup: 'rounded-3'
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Funciones para gestión de datos
|
||||
function verificarPedimentosExistentes() {
|
||||
fetch(`${BASE_URL}/public/verificar_pedimentos.php`)
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
const stats = data.estadisticas;
|
||||
let mensaje = `📊 Pedimentos en Base de Datos:\n\n`;
|
||||
mensaje += `• Total: ${stats.total_pedimentos}\n`;
|
||||
mensaje += `• Usuarios: ${stats.usuarios_distintos}\n`;
|
||||
|
||||
if (stats.total_pedimentos > 0) {
|
||||
mensaje += `• Primer pedimento: ${stats.primer_pedimento?.date || 'N/A'}\n`;
|
||||
mensaje += `• Último pedimento: ${stats.ultimo_pedimento?.date || 'N/A'}\n\n`;
|
||||
|
||||
if (data.pedimentos_existentes.length > 0) {
|
||||
mensaje += `🔍 Últimos 5 pedimentos:\n`;
|
||||
data.pedimentos_existentes.slice(0, 5).forEach(p => {
|
||||
mensaje += `• ${p.numero_pedimento} (${p.fecha_creacion?.date || 'N/A'})\n`;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Swal.fire({
|
||||
icon: 'info',
|
||||
title: 'Pedimentos Existentes',
|
||||
text: mensaje,
|
||||
confirmButtonColor: '#2563eb'
|
||||
});
|
||||
}
|
||||
})
|
||||
.catch(error => console.error('Error:', error));
|
||||
}
|
||||
|
||||
function limpiarBaseDatos() {
|
||||
Swal.fire({
|
||||
title: '⚠️ Confirmar Limpieza',
|
||||
text: 'Esto eliminará TODOS los pedimentos, facturas y partidas. ¿Estás seguro?',
|
||||
icon: 'warning',
|
||||
showCancelButton: true,
|
||||
confirmButtonColor: '#dc2626',
|
||||
cancelButtonColor: '#6b7280',
|
||||
confirmButtonText: 'Sí, limpiar',
|
||||
cancelButtonText: 'Cancelar'
|
||||
}).then((result) => {
|
||||
if (result.isConfirmed) {
|
||||
fetch(`${BASE_URL}/public/limpiar_pedimentos.php`, {
|
||||
method: 'POST'
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
Swal.fire({
|
||||
icon: 'success',
|
||||
title: 'Base de Datos Limpiada',
|
||||
text: 'Todos los registros han sido eliminados. Ahora puedes importar pedimentos sin duplicados.',
|
||||
confirmButtonColor: '#059669'
|
||||
});
|
||||
} else {
|
||||
Swal.fire({
|
||||
icon: 'error',
|
||||
title: 'Error al Limpiar',
|
||||
text: data.message || 'No se pudo limpiar la base de datos',
|
||||
confirmButtonColor: '#dc2626'
|
||||
});
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
Swal.fire({
|
||||
icon: 'error',
|
||||
title: 'Error de Conexión',
|
||||
text: 'No se pudo conectar al servidor para limpiar la BD',
|
||||
confirmButtonColor: '#dc2626'
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Limpiar modal al cerrar
|
||||
const modal = document.getElementById('modalImportarPedimentos');
|
||||
if (modal) {
|
||||
modal.addEventListener('hidden.bs.modal', function() {
|
||||
document.getElementById('formImportarPedimentos').reset();
|
||||
document.getElementById('previewContainer').style.display = 'none';
|
||||
document.getElementById('btnProcesarArchivo').disabled = true;
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -4,80 +4,316 @@
|
||||
<html lang="es">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>📋 Catálogo de Pedimentos</title>
|
||||
<title>Lista de Pedimentos</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">
|
||||
<!-- Font Awesome -->
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css">
|
||||
<!-- Animate.css -->
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.min.css">
|
||||
<link href="https://cdn.datatables.net/1.13.4/css/dataTables.bootstrap5.min.css" rel="stylesheet">
|
||||
<style>
|
||||
table.dataTable thead th { background:#343a40; color:#fff; }
|
||||
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: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; } }
|
||||
/* 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; }
|
||||
:root {
|
||||
--primary: #2563eb;
|
||||
--primary-dark: #1d4ed8;
|
||||
--secondary: #64748b;
|
||||
--success: #059669;
|
||||
--warning: #d97706;
|
||||
--danger: #dc2626;
|
||||
--info: #0891b2;
|
||||
--light: #f8fafc;
|
||||
--dark: #0f172a;
|
||||
--border-color: #e2e8f0;
|
||||
--text-muted: #64748b;
|
||||
--bg-body: #f8fafc;
|
||||
}
|
||||
.card { border-radius: 12px; }
|
||||
/* Animaciones personalizadas */
|
||||
.card-hover { transition: all 0.3s ease; cursor: pointer; }
|
||||
.card-hover:hover { transform: translateY(-8px) scale(1.02); box-shadow: 0 1rem 2rem rgba(0, 0, 0, 0.15) !important; }
|
||||
.btn-pulse { animation: pulse 2s infinite; }
|
||||
@keyframes pulse {
|
||||
0% { transform: scale(1); }
|
||||
50% { transform: scale(1.05); }
|
||||
100% { transform: scale(1); }
|
||||
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.fade-in-up { animation: fadeInUp 0.8s ease-out; }
|
||||
@keyframes fadeInUp {
|
||||
from { opacity: 0; transform: translateY(30px); }
|
||||
to { opacity: 1; transform: translateY(0); }
|
||||
|
||||
body {
|
||||
font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;
|
||||
background-color: var(--bg-body);
|
||||
color: var(--dark);
|
||||
line-height: 1.6;
|
||||
}
|
||||
/* Responsive animations */
|
||||
@media (max-width: 768px) { .card-hover:hover { transform: translateY(-4px) scale(1.01); } }
|
||||
/* Efecto de glow para elementos activos */
|
||||
.btn.active { box-shadow: 0 0 20px rgba(var(--bs-primary-rgb), 0.5); }
|
||||
/* Animación para el título */
|
||||
.title-glow { text-shadow: 0 0 10px rgba(0, 123, 255, 0.3); }
|
||||
/* Efecto para botones */
|
||||
.btn-animated { transition: all 0.3s ease; position: relative; overflow: hidden; }
|
||||
.btn-animated:hover { transform: translateY(-2px); box-shadow: 0 5px 15px rgba(0, 0, 0, 0.2); }
|
||||
.btn-animated::before { content: ''; position: absolute; top: 0; left: -100%; width: 100%; height: 100%;
|
||||
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2), transparent); transition: left 0.5s; }
|
||||
.btn-animated:hover::before { left: 100%; }
|
||||
|
||||
.main-content {
|
||||
margin-left: 280px;
|
||||
padding: 2rem;
|
||||
min-height: 100vh;
|
||||
transition: margin-left 0.3s ease;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.main-content {
|
||||
margin-left: 0;
|
||||
padding: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
.page-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 2rem;
|
||||
flex-wrap: wrap;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.page-title {
|
||||
color: var(--dark);
|
||||
font-weight: 600;
|
||||
font-size: 1.75rem;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.btn-modern {
|
||||
background: var(--primary);
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 0.625rem 1.25rem;
|
||||
border-radius: 6px;
|
||||
font-weight: 500;
|
||||
font-size: 0.875rem;
|
||||
transition: all 0.2s ease;
|
||||
text-decoration: none;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.btn-modern:hover {
|
||||
background: var(--primary-dark);
|
||||
color: white;
|
||||
transform: translateY(-1px);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.btn-modern.success {
|
||||
background: var(--success);
|
||||
}
|
||||
|
||||
.btn-modern.success:hover {
|
||||
background: #047857;
|
||||
}
|
||||
|
||||
.btn-modern.secondary {
|
||||
background: var(--secondary);
|
||||
}
|
||||
|
||||
.btn-modern.secondary:hover {
|
||||
background: #475569;
|
||||
}
|
||||
|
||||
.btn-modern.danger {
|
||||
background: var(--danger);
|
||||
}
|
||||
|
||||
.btn-modern.danger:hover {
|
||||
background: #b91c1c;
|
||||
}
|
||||
|
||||
.data-card {
|
||||
background: white;
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 12px;
|
||||
padding: 0;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
|
||||
}
|
||||
|
||||
.table-container {
|
||||
border-radius: 12px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.table {
|
||||
margin-bottom: 0;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.table thead th {
|
||||
background: linear-gradient(135deg, var(--primary) 0%, var(--primary-dark) 100%);
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 1rem 0.75rem;
|
||||
font-weight: 600;
|
||||
font-size: 0.875rem;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.025em;
|
||||
border-bottom: 3px solid rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
.table tbody td {
|
||||
padding: 0.875rem 0.75rem;
|
||||
border-color: var(--border-color);
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.table tbody td:first-child {
|
||||
font-weight: 600;
|
||||
color: var(--primary);
|
||||
font-family: 'JetBrains Mono', 'Courier New', monospace;
|
||||
}
|
||||
|
||||
.table tbody tr {
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.table tbody tr:hover {
|
||||
background-color: rgba(37, 99, 235, 0.05);
|
||||
transform: translateY(-1px);
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.badge-modern {
|
||||
padding: 0.4rem 0.8rem;
|
||||
border-radius: 20px;
|
||||
font-size: 0.75rem;
|
||||
font-weight: 600;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.025em;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.25rem;
|
||||
}
|
||||
|
||||
.badge-success {
|
||||
background: linear-gradient(135deg, var(--success), #047857);
|
||||
color: white;
|
||||
box-shadow: 0 2px 4px rgba(5, 150, 105, 0.2);
|
||||
}
|
||||
|
||||
.badge-secondary {
|
||||
background: linear-gradient(135deg, var(--secondary), #475569);
|
||||
color: white;
|
||||
box-shadow: 0 2px 4px rgba(100, 116, 139, 0.2);
|
||||
}
|
||||
|
||||
.btn-action {
|
||||
padding: 0.5rem 0.75rem;
|
||||
border-radius: 8px;
|
||||
font-size: 0.75rem;
|
||||
margin: 0 0.25rem;
|
||||
transition: all 0.2s ease;
|
||||
border: 1px solid transparent;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.25rem;
|
||||
}
|
||||
|
||||
.btn-action:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
|
||||
.btn-action.edit {
|
||||
background: var(--primary);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.btn-action.edit:hover {
|
||||
background: var(--primary-dark);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.btn-action.delete {
|
||||
background: var(--danger);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.btn-action.delete:hover {
|
||||
background: #b91c1c;
|
||||
color: white;
|
||||
}
|
||||
|
||||
/* DataTables customization */
|
||||
.dataTables_wrapper .dataTables_length,
|
||||
.dataTables_wrapper .dataTables_filter,
|
||||
.dataTables_wrapper .dataTables_info,
|
||||
.dataTables_wrapper .dataTables_paginate {
|
||||
margin: 1rem;
|
||||
}
|
||||
|
||||
.dataTables_wrapper .dataTables_length select,
|
||||
.dataTables_wrapper .dataTables_filter input {
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 6px;
|
||||
padding: 0.375rem 0.75rem;
|
||||
}
|
||||
|
||||
.dataTables_wrapper .dataTables_paginate .paginate_button {
|
||||
padding: 0.375rem 0.75rem !important;
|
||||
margin: 0 0.125rem !important;
|
||||
border-radius: 6px !important;
|
||||
border: 1px solid var(--border-color) !important;
|
||||
color: var(--dark) !important;
|
||||
}
|
||||
|
||||
.dataTables_wrapper .dataTables_paginate .paginate_button.current {
|
||||
background: var(--primary) !important;
|
||||
color: white !important;
|
||||
border-color: var(--primary) !important;
|
||||
}
|
||||
|
||||
.dataTables_wrapper .dataTables_paginate .paginate_button:hover {
|
||||
background: var(--primary) !important;
|
||||
color: white !important;
|
||||
border-color: var(--primary) !important;
|
||||
}
|
||||
|
||||
.fade-in {
|
||||
animation: fadeIn 0.5s ease-out;
|
||||
}
|
||||
|
||||
@keyframes fadeIn {
|
||||
from { opacity: 0; transform: translateY(10px); }
|
||||
to { opacity: 1; transform: translateY(0); }
|
||||
}
|
||||
/* Drawer lateral */
|
||||
.drawer { position: fixed; inset: 0; z-index: 99999; display: none; }
|
||||
.drawer.open { display: block; }
|
||||
.drawer-overlay { position: absolute; inset: 0; background: rgba(0,0,0,0.35); z-index: 99998; }
|
||||
.drawer-panel { position: absolute; top: 0; right: -520px; width: 520px; max-width: 94vw; height: 100%; background: #fff; border-left: 1px solid var(--border-color); box-shadow: -8px 0 24px rgba(0,0,0,0.1); transition: right 0.3s ease; display: flex; flex-direction: column; z-index: 99999; }
|
||||
.drawer.open .drawer-panel { right: 0; }
|
||||
.drawer-header { padding: 1rem 1rem; border-bottom: 1px solid var(--border-color); display: flex; justify-content: space-between; align-items: center; }
|
||||
.drawer-title { font-weight: 600; font-size: 1.1rem; }
|
||||
.drawer-subtitle { color: var(--text-muted); font-size: 0.9rem; }
|
||||
.page-subtitle { color: var(--text-muted); font-weight: 500; font-size: 1rem; margin-left: 0.75rem; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="content">
|
||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||
<h4 class="animate__animated animate__fadeInDown title-glow">📋 Lista de Pedimentos</h4>
|
||||
<div>
|
||||
<a href="/IMPORTADORES/catalogo_pedimentos/crear" class="btn btn-success btn-animated me-2">
|
||||
<i class="fas fa-plus"></i> Nuevo Pedimento
|
||||
<div class="main-content">
|
||||
<!-- Page Header -->
|
||||
<div class="page-header">
|
||||
<h1 class="page-title">Lista de Pedimentos <span id="headerPedimentoActual" class="page-subtitle"></span></h1>
|
||||
<div class="d-flex gap-2">
|
||||
<a href="/IMPORTADORES/catalogo_pedimentos/crear" class="btn-modern success">
|
||||
<i class="fas fa-plus"></i>
|
||||
Nuevo Pedimento
|
||||
</a>
|
||||
<a href="/IMPORTADORES/catalogo_pedimentos" class="btn btn-secondary btn-animated">
|
||||
<i class="fas fa-arrow-left"></i> Regresar
|
||||
<button type="button" class="btn-modern" style="background: var(--info);" onclick="abrirModalImportar()">
|
||||
<i class="fas fa-upload"></i>
|
||||
Importar Archivos
|
||||
</button>
|
||||
<button type="button" id="btnImportarWinsaai" class="btn-modern secondary" onclick="importarDesdeWinsaai()" title="Requiere configuración en Automatizaciones (WINSAAI)">
|
||||
<i class="fas fa-cloud-download-alt"></i>
|
||||
Importar desde WINSAAI
|
||||
</button>
|
||||
<a href="/IMPORTADORES/catalogo_pedimentos" class="btn-modern secondary">
|
||||
<i class="fas fa-arrow-left"></i>
|
||||
Regresar
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card p-3 shadow-sm card-hover position-relative h-auto fade-in-up">
|
||||
<div class="table-responsive">
|
||||
<table id="tablaPedimentos" class="display nowrap" style="width:100%">
|
||||
<!-- Data Table -->
|
||||
<div class="data-card fade-in">
|
||||
<div class="table-container">
|
||||
<table id="tablaPedimentos" class="table table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>ID</th>
|
||||
@@ -97,16 +333,152 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- jQuery -->
|
||||
<!-- Modal para Importar Archivos -->
|
||||
<div class="modal fade" id="modalImportarPedimentos" tabindex="-1" aria-labelledby="modalImportarLabel" aria-hidden="true">
|
||||
<div class="modal-dialog modal-lg">
|
||||
<div class="modal-content" style="border-radius: 12px; border: none;">
|
||||
<div class="modal-header" style="background: linear-gradient(135deg, var(--info) 0%, #0e7490 100%); color: white; border-radius: 12px 12px 0 0;">
|
||||
<h5 class="modal-title" id="modalImportarLabel">
|
||||
<i class="fas fa-upload me-2"></i>
|
||||
Importar Pedimentos desde Archivos
|
||||
</h5>
|
||||
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="modal-body p-4">
|
||||
<div class="alert alert-info d-flex align-items-start" style="border: none; border-radius: 8px; background: rgba(8, 145, 178, 0.1); border-left: 4px solid var(--info);">
|
||||
<i class="fas fa-info-circle me-2 mt-1" style="color: var(--info);"></i>
|
||||
<div>
|
||||
<strong>Formato del archivo:</strong>
|
||||
<p class="mb-1">El sistema reconoce archivos con los siguientes códigos:</p>
|
||||
<ul class="mb-2 small">
|
||||
<li><strong>501:</strong> Datos del pedimento (número, RFC, fechas)</li>
|
||||
<li><strong>505:</strong> Información de facturas</li>
|
||||
<li><strong>551:</strong> Partidas de mercancías</li>
|
||||
</ul>
|
||||
<p class="mb-0 small text-muted">
|
||||
<i class="fas fa-calendar-day me-1"></i>
|
||||
<strong>Archivos julianos:</strong> Se aceptan extensiones de día del año (.001 a .366)
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<form id="formImportarPedimentos" enctype="multipart/form-data">
|
||||
<div class="mb-3">
|
||||
<label for="archivoImportar" class="form-label fw-semibold">
|
||||
<i class="fas fa-file-text me-1"></i>
|
||||
Seleccionar archivos
|
||||
</label>
|
||||
<input type="file" class="form-control" id="archivoImportar" name="archivos[]" multiple required style="border-radius: 8px; border: 1px solid var(--border-color);">
|
||||
<div class="form-text">Formatos soportados: .txt, .dat, archivos julianos (.001-.366). Puedes seleccionar varios archivos.</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" id="validarDuplicados" name="validar_duplicados" checked>
|
||||
<label class="form-check-label fw-medium" for="validarDuplicados">
|
||||
Validar pedimentos duplicados
|
||||
</label>
|
||||
<div class="form-text">Evita importar pedimentos que ya existen en el sistema</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="previewContainer" style="display: none;" class="mb-3">
|
||||
<h6 class="fw-semibold mb-2">
|
||||
<i class="fas fa-eye me-1"></i>
|
||||
Vista previa del archivo
|
||||
</h6>
|
||||
<div id="previewContent" class="p-3" style="background: #f8fafc; border-radius: 8px; border: 1px solid var(--border-color); max-height: 200px; overflow-y: auto; font-family: monospace; font-size: 0.85rem;"></div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="modal-footer" style="border-top: 1px solid var(--border-color); border-radius: 0 0 12px 12px;">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal" style="border-radius: 8px;">
|
||||
<i class="fas fa-times me-1"></i>
|
||||
Cancelar
|
||||
</button>
|
||||
<button type="button" class="btn btn-primary" id="btnProcesarArchivo" style="border-radius: 8px; background: var(--info); border-color: var(--info);" disabled>
|
||||
<i class="fas fa-cogs me-1"></i>
|
||||
Procesar Archivos
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Drawer lateral de detalle -->
|
||||
<div id="drawerDetalle" class="drawer" aria-hidden="true">
|
||||
<div class="drawer-overlay" onclick="cerrarDetalle()"></div>
|
||||
<div class="drawer-panel">
|
||||
<div class="drawer-header">
|
||||
<div>
|
||||
<div class="drawer-title">Pedimento <span id="drawerNumero"></span></div>
|
||||
<div class="drawer-subtitle" id="drawerSubtitulo"></div>
|
||||
</div>
|
||||
<button type="button" class="btn btn-outline-secondary btn-sm" onclick="cerrarDetalle()" aria-label="Cerrar">×</button>
|
||||
</div>
|
||||
<div class="drawer-body p-3 overflow-auto">
|
||||
<ul class="nav nav-tabs" id="detalleTabs" role="tablist">
|
||||
<li class="nav-item" role="presentation">
|
||||
<button class="nav-link active" id="tab-facturas" data-bs-toggle="tab" data-bs-target="#pane-facturas" type="button" role="tab">Facturas</button>
|
||||
</li>
|
||||
<li class="nav-item" role="presentation">
|
||||
<button class="nav-link" id="tab-partidas" data-bs-toggle="tab" data-bs-target="#pane-partidas" type="button" role="tab">Partidas</button>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="tab-content pt-3">
|
||||
<div class="tab-pane fade show active" id="pane-facturas" role="tabpanel">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-sm table-striped align-middle">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>#</th>
|
||||
<th>Número</th>
|
||||
<th>Fecha</th>
|
||||
<th>Proveedor</th>
|
||||
<th>Moneda</th>
|
||||
<th>Total</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="tbodyFacturas"></tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tab-pane fade" id="pane-partidas" role="tabpanel">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-sm table-striped align-middle">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>#</th>
|
||||
<th>Fracción</th>
|
||||
<th>Descripción</th>
|
||||
<th>Cantidad</th>
|
||||
<th>UM</th>
|
||||
<th>Valor Aduana</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="tbodyPartidas"></tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
|
||||
<!-- DataTables JS -->
|
||||
<script src="https://cdn.datatables.net/1.13.4/js/jquery.dataTables.min.js"></script>
|
||||
<!-- SweetAlert2 -->
|
||||
<script src="https://cdn.datatables.net/1.13.4/js/dataTables.bootstrap5.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
|
||||
|
||||
<script>
|
||||
$(document).ready(function() {
|
||||
// Inicializar DataTable
|
||||
// Base URL del proyecto (igual que en index)
|
||||
const BASE_URL = '<?php echo "http" . (isset($_SERVER["HTTPS"]) && $_SERVER["HTTPS"] === "on" ? "s" : "") . "://" . $_SERVER["HTTP_HOST"] . "/IMPORTADORES"; ?>';
|
||||
|
||||
let hasWinsaaiConfig = false;
|
||||
|
||||
$(document).ready(function() {
|
||||
// Initialize DataTable with professional styling
|
||||
$('#tablaPedimentos').DataTable({
|
||||
"processing": true,
|
||||
"serverSide": true,
|
||||
@@ -127,9 +499,9 @@
|
||||
"name": "Status",
|
||||
"render": function(data, type, row) {
|
||||
if (data === 'Activo') {
|
||||
return '<span class="badge bg-success">Activo</span>';
|
||||
return '<span class="badge-modern badge-success"><i class="fas fa-check-circle"></i>Activo</span>';
|
||||
} else {
|
||||
return '<span class="badge bg-secondary">Inactivo</span>';
|
||||
return '<span class="badge-modern badge-secondary"><i class="fas fa-pause-circle"></i>Inactivo</span>';
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -140,12 +512,20 @@
|
||||
"render": function(data, type, row) {
|
||||
const id = row[0];
|
||||
return `
|
||||
<a href="/IMPORTADORES/catalogo_pedimentos/editar?id=${id}" class="btn btn-sm btn-primary btn-animated" title="Editar">
|
||||
<i class="fas fa-edit"></i>
|
||||
</a>
|
||||
<button type="button" class="btn btn-sm btn-danger btn-animated" onclick="confirmarEliminar(${id})" title="Eliminar">
|
||||
<i class="fas fa-trash"></i>
|
||||
</button>
|
||||
<div class="d-flex justify-content-center gap-1">
|
||||
<button type="button" class="btn-action view" onclick="abrirDetalle(${id})" title="Ver detalle">
|
||||
<i class="fas fa-list"></i>
|
||||
<span>Detalle</span>
|
||||
</button>
|
||||
<a href="/IMPORTADORES/catalogo_pedimentos/editar?id=${id}" class="btn-action edit" title="Editar pedimento">
|
||||
<i class="fas fa-edit"></i>
|
||||
<span>Editar</span>
|
||||
</a>
|
||||
<button type="button" class="btn-action delete" onclick="confirmarEliminar(${id})" title="Eliminar pedimento">
|
||||
<i class="fas fa-trash-alt"></i>
|
||||
<span>Eliminar</span>
|
||||
</button>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
}
|
||||
@@ -156,8 +536,23 @@
|
||||
"responsive": true,
|
||||
"order": [[4, "desc"]],
|
||||
"pageLength": 25,
|
||||
"lengthMenu": [[10, 25, 50, 100], [10, 25, 50, 100]]
|
||||
"lengthMenu": [[10, 25, 50, 100], [10, 25, 50, 100]],
|
||||
"dom": "<'row'<'col-sm-6'l><'col-sm-6'f>>" +
|
||||
"<'row'<'col-sm-12'tr>>" +
|
||||
"<'row'<'col-sm-5'i><'col-sm-7'p>>"
|
||||
});
|
||||
|
||||
// Add fade-in animation
|
||||
setTimeout(() => {
|
||||
document.querySelector('.fade-in').style.opacity = '1';
|
||||
document.querySelector('.fade-in').style.transform = 'translateY(0)';
|
||||
}, 100);
|
||||
|
||||
// Configurar modal importar
|
||||
configurarModalImportacion();
|
||||
|
||||
// Cargar estado de configuración WINSAAI
|
||||
cargarConfigWinsaai();
|
||||
});
|
||||
|
||||
function confirmarEliminar(id) {
|
||||
@@ -168,7 +563,11 @@
|
||||
showCancelButton: true,
|
||||
confirmButtonText: 'Sí, eliminar',
|
||||
cancelButtonText: 'Cancelar',
|
||||
confirmButtonColor: '#d33'
|
||||
confirmButtonColor: '#dc2626',
|
||||
cancelButtonColor: '#64748b',
|
||||
customClass: {
|
||||
popup: 'rounded-3'
|
||||
}
|
||||
}).then(result => {
|
||||
if (result.isConfirmed) {
|
||||
window.location = `/IMPORTADORES/catalogo_pedimentos/eliminar?id=${id}`;
|
||||
@@ -176,18 +575,378 @@
|
||||
});
|
||||
}
|
||||
|
||||
// Mostrar alertas de éxito/error
|
||||
// Success/Error alerts
|
||||
<?php if (isset($_GET['created'])): ?>
|
||||
Swal.fire({ icon: 'success', title: 'Pedimento creado', text: 'El pedimento se creó correctamente.', confirmButtonColor: '#198754' });
|
||||
Swal.fire({
|
||||
icon: 'success',
|
||||
title: 'Pedimento creado',
|
||||
text: 'El pedimento se creó correctamente.',
|
||||
confirmButtonColor: '#059669',
|
||||
customClass: { popup: 'rounded-3' }
|
||||
});
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if (isset($_GET['updated'])): ?>
|
||||
Swal.fire({ icon: 'success', title: 'Pedimento actualizado', text: 'Los datos fueron modificados correctamente.', confirmButtonColor: '#198754' });
|
||||
Swal.fire({
|
||||
icon: 'success',
|
||||
title: 'Pedimento actualizado',
|
||||
text: 'Los datos fueron modificados correctamente.',
|
||||
confirmButtonColor: '#059669',
|
||||
customClass: { popup: 'rounded-3' }
|
||||
});
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if (isset($_GET['deleted'])): ?>
|
||||
Swal.fire({ icon: 'success', title: 'Pedimento eliminado', text: 'El pedimento fue eliminado correctamente.', confirmButtonColor: '#198754' });
|
||||
Swal.fire({
|
||||
icon: 'success',
|
||||
title: 'Pedimento eliminado',
|
||||
text: 'El pedimento fue eliminado correctamente.',
|
||||
confirmButtonColor: '#059669',
|
||||
customClass: { popup: 'rounded-3' }
|
||||
});
|
||||
<?php endif; ?>
|
||||
|
||||
// --- Importación de pedimentos (múltiples) ---
|
||||
async function cargarConfigWinsaai() {
|
||||
try {
|
||||
const resp = await fetch(`${BASE_URL}/winsaai/get_config`);
|
||||
const data = await resp.json();
|
||||
hasWinsaaiConfig = !!(data && data.success && data.data);
|
||||
} catch (e) {
|
||||
hasWinsaaiConfig = false;
|
||||
}
|
||||
// Ajustar estilo/tooltip del botón según estado
|
||||
const btn = document.getElementById('btnImportarWinsaai');
|
||||
if (!btn) return;
|
||||
if (hasWinsaaiConfig) {
|
||||
btn.classList.remove('secondary');
|
||||
btn.style.background = 'var(--success)';
|
||||
btn.title = 'Configuración detectada: listo para importar (próximamente)';
|
||||
} else {
|
||||
btn.classList.add('secondary');
|
||||
btn.style.background = 'var(--secondary)';
|
||||
btn.title = 'Requiere configurar WINSAAI en Automatizaciones';
|
||||
}
|
||||
}
|
||||
|
||||
function importarDesdeWinsaai() {
|
||||
if (!hasWinsaaiConfig) {
|
||||
Swal.fire({
|
||||
icon: 'warning',
|
||||
title: 'Configura WINSAAI primero',
|
||||
text: 'Para importar desde WINSAAI necesitas configurar la conexión en la sección Automatizaciones.',
|
||||
showCancelButton: true,
|
||||
confirmButtonText: 'Ir a Automatizaciones',
|
||||
cancelButtonText: 'Cancelar',
|
||||
confirmButtonColor: '#2563eb',
|
||||
cancelButtonColor: '#64748b',
|
||||
customClass: { popup: 'rounded-3' }
|
||||
}).then(r => {
|
||||
if (r.isConfirmed) {
|
||||
window.location.href = `${BASE_URL}/automatizaciones`;
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// Placeholder: funcionalidad próxima
|
||||
Swal.fire({
|
||||
icon: 'info',
|
||||
title: 'Próximamente',
|
||||
text: 'La importación directa desde WINSAAI estará disponible pronto.',
|
||||
confirmButtonColor: '#2563eb',
|
||||
customClass: { popup: 'rounded-3' }
|
||||
});
|
||||
}
|
||||
|
||||
function abrirModalImportar() {
|
||||
const modal = new bootstrap.Modal(document.getElementById('modalImportarPedimentos'));
|
||||
modal.show();
|
||||
}
|
||||
|
||||
function configurarModalImportacion() {
|
||||
const archivoInput = document.getElementById('archivoImportar');
|
||||
const previewContainer = document.getElementById('previewContainer');
|
||||
const previewContent = document.getElementById('previewContent');
|
||||
const btnProcesar = document.getElementById('btnProcesarArchivo');
|
||||
|
||||
if (!archivoInput) return;
|
||||
|
||||
archivoInput.addEventListener('change', function(e) {
|
||||
const archivos = Array.from(e.target.files || []);
|
||||
if (archivos.length > 0) {
|
||||
const extensionesValidas = ['txt', 'dat'];
|
||||
for (const file of archivos) {
|
||||
const nombreArchivo = file.name.toLowerCase();
|
||||
const extension = nombreArchivo.split('.').pop();
|
||||
const esJuliana = /^\d{3}$/.test(extension);
|
||||
if (!extensionesValidas.includes(extension) && !esJuliana) {
|
||||
Swal.fire({
|
||||
icon: 'error',
|
||||
title: 'Archivo no válido',
|
||||
text: `"${file.name}" no es válido. Usa .txt, .dat o extensión juliana (.001-.366)`,
|
||||
confirmButtonColor: '#dc2626',
|
||||
customClass: { popup: 'rounded-3' }
|
||||
});
|
||||
archivoInput.value = '';
|
||||
previewContainer.style.display = 'none';
|
||||
btnProcesar.disabled = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const reader = new FileReader();
|
||||
reader.onload = function(e) {
|
||||
const contenido = e.target.result;
|
||||
mostrarVistaPrevia(contenido, archivos.length);
|
||||
btnProcesar.disabled = false;
|
||||
};
|
||||
reader.readAsText(archivos[0]);
|
||||
} else {
|
||||
previewContainer.style.display = 'none';
|
||||
btnProcesar.disabled = true;
|
||||
}
|
||||
});
|
||||
|
||||
btnProcesar.addEventListener('click', function() {
|
||||
procesarArchivoImportacion();
|
||||
});
|
||||
}
|
||||
|
||||
function mostrarVistaPrevia(contenido, totalArchivos = 1) {
|
||||
const previewContainer = document.getElementById('previewContainer');
|
||||
const previewContent = document.getElementById('previewContent');
|
||||
const lineas = contenido.split('\n').slice(0, 10);
|
||||
|
||||
const preview = lineas.map(linea => {
|
||||
let clase = '';
|
||||
if (linea.startsWith('501')) clase = 'style="color: #059669; font-weight: bold;"';
|
||||
else if (linea.startsWith('505')) clase = 'style="color: #d97706; font-weight: bold;"';
|
||||
else if (linea.startsWith('551')) clase = 'style="color: #2563eb; font-weight: bold;"';
|
||||
return `<div ${clase}>${linea}</div>`;
|
||||
}).join('');
|
||||
|
||||
let extra = '';
|
||||
if (contenido.split('\n').length > 10) {
|
||||
extra += '<div style="color: #6b7280; font-style: italic;">... y más líneas</div>';
|
||||
}
|
||||
if (totalArchivos > 1) {
|
||||
const restantes = totalArchivos - 1;
|
||||
extra += `<div class="mt-2" style="color: #374151; font-weight: 600;">+ ${restantes} archivo${restantes === 1 ? '' : 's'} más seleccionado${restantes === 1 ? '' : 's'}</div>`;
|
||||
}
|
||||
previewContent.innerHTML = preview + extra;
|
||||
previewContainer.style.display = 'block';
|
||||
}
|
||||
|
||||
function procesarArchivoImportacion() {
|
||||
const formData = new FormData(document.getElementById('formImportarPedimentos'));
|
||||
|
||||
const btnProcesar = document.getElementById('btnProcesarArchivo');
|
||||
const textoOriginal = btnProcesar.innerHTML;
|
||||
btnProcesar.innerHTML = '<i class="fas fa-spinner fa-spin me-1"></i>Procesando...';
|
||||
btnProcesar.disabled = true;
|
||||
|
||||
const url = `${BASE_URL}/public/importar_pedimentos.php`;
|
||||
|
||||
fetch(url, {
|
||||
method: 'POST',
|
||||
body: formData
|
||||
})
|
||||
.then(async response => {
|
||||
let data;
|
||||
try { data = await response.json(); } catch (_) { data = null; }
|
||||
if (!response.ok) {
|
||||
const msg = (data && data.message) ? data.message : `HTTP ${response.status}`;
|
||||
throw new Error(msg);
|
||||
}
|
||||
return data;
|
||||
})
|
||||
.then(data => {
|
||||
btnProcesar.innerHTML = textoOriginal;
|
||||
btnProcesar.disabled = false;
|
||||
|
||||
if (data.success) {
|
||||
const estadisticas = data.estadisticas || {};
|
||||
let mensaje = `Se procesaron ${estadisticas.pedimentos_procesados || 0} pedimentos exitosamente.\n\n`;
|
||||
mensaje += `• Pedimentos: ${estadisticas.pedimentos_procesados || 0}\n`;
|
||||
mensaje += `• Facturas: ${estadisticas.facturas_procesadas || 0}\n`;
|
||||
mensaje += `• Partidas: ${estadisticas.partidas_procesadas || 0}\n`;
|
||||
if ((estadisticas.duplicados || 0) > 0) mensaje += `• Duplicados omitidos: ${estadisticas.duplicados}\n`;
|
||||
if ((estadisticas.errores || 0) > 0) mensaje += `• Errores: ${estadisticas.errores}`;
|
||||
|
||||
Swal.fire({
|
||||
icon: 'success',
|
||||
title: 'Importación Completada',
|
||||
text: mensaje,
|
||||
confirmButtonColor: '#059669',
|
||||
customClass: { popup: 'rounded-3' }
|
||||
});
|
||||
|
||||
const modal = bootstrap.Modal.getInstance(document.getElementById('modalImportarPedimentos'));
|
||||
modal.hide();
|
||||
document.getElementById('formImportarPedimentos').reset();
|
||||
document.getElementById('previewContainer').style.display = 'none';
|
||||
|
||||
// Recargar la tabla para ver nuevos pedimentos
|
||||
$('#tablaPedimentos').DataTable().ajax.reload(null, false);
|
||||
} else {
|
||||
Swal.fire({
|
||||
icon: 'error',
|
||||
title: 'Error en la Importación',
|
||||
text: data.message || 'Ocurrió un error al procesar los archivos',
|
||||
confirmButtonColor: '#dc2626',
|
||||
customClass: { popup: 'rounded-3' }
|
||||
});
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
btnProcesar.innerHTML = textoOriginal;
|
||||
btnProcesar.disabled = false;
|
||||
Swal.fire({
|
||||
icon: 'error',
|
||||
title: 'Error de Conexión',
|
||||
text: error.message || 'No se pudo conectar con el servidor.',
|
||||
confirmButtonColor: '#dc2626',
|
||||
customClass: { popup: 'rounded-3' }
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// --- Drawer de detalle de pedimento ---
|
||||
async function abrirDetalle(id) {
|
||||
const drawer = document.getElementById('drawerDetalle');
|
||||
const numeroEl = document.getElementById('drawerNumero');
|
||||
const subEl = document.getElementById('drawerSubtitulo');
|
||||
const tbodyF = document.getElementById('tbodyFacturas');
|
||||
const tbodyP = document.getElementById('tbodyPartidas');
|
||||
const headerPed = document.getElementById('headerPedimentoActual');
|
||||
if (!drawer) return;
|
||||
|
||||
// Limpia contenidos previos
|
||||
numeroEl.textContent = '';
|
||||
subEl.textContent = '';
|
||||
tbodyF.innerHTML = '<tr><td colspan="6" class="text-center text-muted">Cargando facturas...</td></tr>';
|
||||
tbodyP.innerHTML = '<tr><td colspan="6" class="text-center text-muted">Cargando partidas...</td></tr>';
|
||||
|
||||
// Abre el drawer inmediatamente para mejor UX
|
||||
drawer.classList.add('open');
|
||||
drawer.setAttribute('aria-hidden', 'false');
|
||||
|
||||
try {
|
||||
// Encabezado del pedimento
|
||||
const rPed = await fetch(`${BASE_URL}/catalogo_pedimentos/ajax_pedimento?id=${encodeURIComponent(id)}`);
|
||||
const dPed = await rPed.json();
|
||||
if (!dPed || !dPed.success || !dPed.data) throw new Error(dPed.message || 'No se encontró el pedimento');
|
||||
const ped = dPed.data;
|
||||
const numeroPed = ped.numero_pedimento || ped.numero || ped.id || id;
|
||||
numeroEl.textContent = numeroPed;
|
||||
const nombre = ped.nombre_importador || ped.importador || '';
|
||||
const rfc = ped.rfc_importador || ped.rfc || '';
|
||||
const fecha = (ped.fecha_creacion || ped.fecha || '').toString().substring(0, 10);
|
||||
subEl.textContent = `${nombre}${nombre && rfc ? ' • ' : ''}${rfc}${(nombre||rfc) && fecha ? ' • ' : ''}${fecha}`;
|
||||
|
||||
if (headerPed) {
|
||||
headerPed.textContent = `• Pedimento ${numeroPed}`;
|
||||
}
|
||||
|
||||
// Carga datos de detalle
|
||||
await Promise.all([
|
||||
cargarFacturas(id),
|
||||
cargarPartidas(id)
|
||||
]);
|
||||
} catch (err) {
|
||||
Swal.fire({
|
||||
icon: 'error',
|
||||
title: 'No se pudo cargar el detalle',
|
||||
text: err.message || 'Error desconocido',
|
||||
confirmButtonColor: '#dc2626',
|
||||
customClass: { popup: 'rounded-3' }
|
||||
});
|
||||
cerrarDetalle();
|
||||
}
|
||||
}
|
||||
|
||||
function cerrarDetalle() {
|
||||
const drawer = document.getElementById('drawerDetalle');
|
||||
const headerPed = document.getElementById('headerPedimentoActual');
|
||||
if (!drawer) return;
|
||||
drawer.classList.remove('open');
|
||||
drawer.setAttribute('aria-hidden', 'true');
|
||||
if (headerPed) headerPed.textContent = '';
|
||||
}
|
||||
|
||||
async function cargarFacturas(pedimentoId) {
|
||||
const tbody = document.getElementById('tbodyFacturas');
|
||||
try {
|
||||
const r = await fetch(`${BASE_URL}/catalogo_pedimentos/ajax_facturas?pedimento_id=${encodeURIComponent(pedimentoId)}`);
|
||||
const d = await r.json();
|
||||
if (!d || !d.success) throw new Error(d.message || 'Error al cargar facturas');
|
||||
const arr = Array.isArray(d.data) ? d.data : [];
|
||||
if (arr.length === 0) {
|
||||
tbody.innerHTML = '<tr><td colspan="6" class="text-center text-muted">Sin facturas</td></tr>';
|
||||
return;
|
||||
}
|
||||
const rows = arr.map((f, idx) => {
|
||||
const numero = f.numero_factura || f.factura || f.numero || '';
|
||||
const fecha = (f.fecha || f.fecha_factura || '').toString().substring(0,10);
|
||||
const proveedor = f.proveedor || f.nombre_proveedor || f.vendedor || '';
|
||||
const moneda = f.moneda || '';
|
||||
const totalRaw = f.total ?? f.importe_total ?? f.subtotal ?? f.valor_factura;
|
||||
const total = (typeof totalRaw === 'number') ? totalRaw.toLocaleString(undefined, {minimumFractionDigits: 2, maximumFractionDigits: 2}) : (totalRaw || '');
|
||||
return `<tr>
|
||||
<td>${idx+1}</td>
|
||||
<td>${numero || '-'}</td>
|
||||
<td>${fecha || '-'}</td>
|
||||
<td>${proveedor || '-'}</td>
|
||||
<td>${moneda || '-'}</td>
|
||||
<td class="text-end">${total || '-'}</td>
|
||||
</tr>`;
|
||||
}).join('');
|
||||
tbody.innerHTML = rows;
|
||||
} catch (e) {
|
||||
tbody.innerHTML = '<tr><td colspan="6" class="text-center text-danger">Error cargando facturas</td></tr>';
|
||||
}
|
||||
}
|
||||
|
||||
async function cargarPartidas(pedimentoId) {
|
||||
const tbody = document.getElementById('tbodyPartidas');
|
||||
try {
|
||||
const r = await fetch(`${BASE_URL}/catalogo_pedimentos/ajax_partidas?pedimento_id=${encodeURIComponent(pedimentoId)}`);
|
||||
const d = await r.json();
|
||||
if (!d || !d.success) throw new Error(d.message || 'Error al cargar partidas');
|
||||
const arr = Array.isArray(d.data) ? d.data : [];
|
||||
if (arr.length === 0) {
|
||||
tbody.innerHTML = '<tr><td colspan="6" class="text-center text-muted">Sin partidas</td></tr>';
|
||||
return;
|
||||
}
|
||||
const rows = arr.map((p, idx) => {
|
||||
const fraccion = p.fraccion || p.fraccion_arancelaria || '';
|
||||
const descripcion = p.descripcion || p.descripcion_mercancia || '';
|
||||
const cantidad = p.cantidad ?? '';
|
||||
const um = p.um || p.unidad || '';
|
||||
const valorRaw = p.valor_aduana ?? p.valor_comercial ?? p.valor ?? '';
|
||||
const valor = (typeof valorRaw === 'number') ? valorRaw.toLocaleString(undefined, {minimumFractionDigits: 2, maximumFractionDigits: 2}) : (valorRaw || '');
|
||||
return `<tr>
|
||||
<td>${idx+1}</td>
|
||||
<td>${fraccion || '-'}</td>
|
||||
<td>${descripcion || '-'}</td>
|
||||
<td class="text-end">${cantidad || '-'}</td>
|
||||
<td>${um || '-'}</td>
|
||||
<td class="text-end">${valor || '-'}</td>
|
||||
</tr>`;
|
||||
}).join('');
|
||||
tbody.innerHTML = rows;
|
||||
} catch (e) {
|
||||
tbody.innerHTML = '<tr><td colspan="6" class="text-center text-danger">Error cargando partidas</td></tr>';
|
||||
}
|
||||
}
|
||||
|
||||
// Cerrar con Escape
|
||||
document.addEventListener('keyup', (ev) => {
|
||||
if (ev.key === 'Escape') {
|
||||
cerrarDetalle();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
</body>
|
||||
|
||||
@@ -9,92 +9,142 @@
|
||||
<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>
|
||||
<!-- Font Awesome -->
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.0/css/all.min.css">
|
||||
<!-- Animate.css para animaciones adicionales -->
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.min.css">
|
||||
<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: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; }
|
||||
@media (min-width: 768px) { .content { margin-left: 250px; } }
|
||||
@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; }
|
||||
/* Animaciones personalizadas */
|
||||
.card-hover { transition: all 0.3s ease; cursor: pointer; }
|
||||
.card-hover:hover { transform: translateY(-8px) scale(1.02); box-shadow: 0 1rem 2rem rgba(0, 0, 0, 0.15) !important; }
|
||||
.btn-pulse { animation: pulse 2s infinite; }
|
||||
@keyframes pulse {
|
||||
0% { transform: scale(1); }
|
||||
50% { transform: scale(1.05); }
|
||||
100% { transform: scale(1); }
|
||||
}
|
||||
.fade-in-up { animation: fadeInUp 0.8s ease-out; }
|
||||
@keyframes fadeInUp {
|
||||
from { opacity: 0; transform: translateY(30px); }
|
||||
to { opacity: 1; transform: translateY(0); }
|
||||
}
|
||||
/* Responsive animations */
|
||||
@media (max-width: 768px) { .card-hover:hover { transform: translateY(-4px) scale(1.01); } }
|
||||
/* Efecto de glow para elementos activos */
|
||||
.btn.active { box-shadow: 0 0 20px rgba(var(--bs-primary-rgb), 0.5); }
|
||||
/* Animación para el título */
|
||||
.title-glow { text-shadow: 0 0 10px rgba(0, 123, 255, 0.3); }
|
||||
/* Efecto para botones */
|
||||
.btn-animated { transition: all 0.3s ease; position: relative; overflow: hidden; }
|
||||
.btn-animated:hover { transform: translateY(-2px); box-shadow: 0 5px 15px rgba(0, 0, 0, 0.2); }
|
||||
.btn-animated::before { content: ''; position: absolute; top: 0; left: -100%; width: 100%; height: 100%;
|
||||
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2), transparent); transition: left 0.5s; }
|
||||
.btn-animated:hover::before { left: 100%; }
|
||||
.table th, .table td { vertical-align: middle; }
|
||||
.folder-icon { color: #f0ad4e; font-size: 1.2rem; }
|
||||
body { font-family: 'Segoe UI', sans-serif; background-color: #f6f8fb; }
|
||||
.content { margin-top: 56px; padding: 32px 20px; background-color: #f6f8fb; }
|
||||
@media (min-width: 768px) { .content { margin-left: var(--sidebar-width, 280px); } }
|
||||
.title { font-weight: 700; }
|
||||
.kpi-card { border: 1px solid #e9ecef; border-radius: 12px; background: #fff; }
|
||||
.kpi-value { font-size: 1.25rem; font-weight: 700; }
|
||||
.search-wrap { gap: .75rem; }
|
||||
.card-exp { border: 1px solid #eef1f5; border-radius: 14px; transition: box-shadow .2s ease, transform .2s ease; }
|
||||
.card-exp:hover { box-shadow: 0 .75rem 1.5rem rgba(0,0,0,.08); transform: translateY(-2px); }
|
||||
.chip { display: inline-flex; align-items: center; gap: .35rem; padding: .15rem .5rem; border-radius: 999px; font-size: .78rem; border: 1px solid #e5e7eb; background: #f8fafc; color: #334155; }
|
||||
.chip i { font-size: .85rem; }
|
||||
.muted { color: #6b7280; }
|
||||
.ped-code { font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New', monospace; letter-spacing: .5px; }
|
||||
.actions .btn { border-radius: 10px; }
|
||||
.empty-state { border: 2px dashed #cbd5e1; border-radius: 14px; padding: 32px; background: #fff; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="content">
|
||||
<h4 class="mb-4 animate__animated animate__fadeInDown title_glow">📁 Expedientes Electrónicos</h4>
|
||||
<div class="card p-3 shadow-sm card-hover position-relative h-auto fade-in-up">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover table-bordered">
|
||||
<thead class="table-dark">
|
||||
<tr>
|
||||
<th>Pedimento</th>
|
||||
<th>Fecha Factura</th>
|
||||
<th>Aduana</th>
|
||||
<th>Proveedor</th>
|
||||
<th>Archivos</th>
|
||||
<th>Tamaño Total</th>
|
||||
<th>Acciones</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($expedientes as $exp): ?>
|
||||
<tr>
|
||||
<td><i class="fas fa-folder folder-icon me-2"></i><?= htmlspecialchars($exp['numero_pedimento']) ?></td>
|
||||
<td><?= is_a($exp['fecha_factura'], 'DateTime') ? $exp['fecha_factura']->format('Y-m-d') : htmlspecialchars($exp['fecha_factura']) ?></td>
|
||||
<?php
|
||||
$totalPed = count($expedientes ?? []);
|
||||
$totalArch = 0; $totalSize = 0;
|
||||
foreach(($expedientes ?? []) as $e){ $totalArch += (int)($e['total_archivos'] ?? 0); $totalSize += (float)($e['total_tamano'] ?? 0); }
|
||||
?>
|
||||
|
||||
<td><?= htmlspecialchars($exp['aduana']) ?></td>
|
||||
<td><?= htmlspecialchars($exp['proveedor_clave']) ?></td>
|
||||
<td><?= $exp['total_archivos'] ?> archivos</td>
|
||||
<td><?= number_format($exp['total_tamano'], 2) ?> KB</td>
|
||||
<td>
|
||||
<a href="/IMPORTADORES/expediente/ver/<?= $exp['id_solicitud'] ?>" class="btn btn-outline-primary btn-sm mt-auto w-auto btn-animated">📂 Ver</a>
|
||||
<a href="/IMPORTADORES/expediente/subir/<?= $exp['id_solicitud'] ?>" class="btn btn-outline-success btn-sm mt-auto w-auto btn-animated">⬆ Subir</a>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="content">
|
||||
<div class="d-flex flex-wrap align-items-end justify-content-between mb-3">
|
||||
<div>
|
||||
<h3 class="title mb-1">📁 Expediente electrónico</h3>
|
||||
<div class="text-muted">Consulta y organiza los documentos por pedimento</div>
|
||||
</div>
|
||||
<div class="search-wrap d-flex align-items-center">
|
||||
<input id="searchExp" type="search" class="form-control" placeholder="Buscar pedimento, proveedor o aduana">
|
||||
<select id="filterArch" class="form-select">
|
||||
<option value="all">Todos</option>
|
||||
<option value="with">Con archivos</option>
|
||||
<option value="without">Sin archivos</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row g-3 mb-4">
|
||||
<div class="col-6 col-md-3">
|
||||
<div class="kpi-card p-3">
|
||||
<div class="muted">Pedimentos</div>
|
||||
<div class="kpi-value"><?= (int)$totalPed ?></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-6 col-md-3">
|
||||
<div class="kpi-card p-3">
|
||||
<div class="muted">Archivos</div>
|
||||
<div class="kpi-value"><?= (int)$totalArch ?></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12 col-md-6">
|
||||
<div class="kpi-card p-3 d-flex align-items-center justify-content-between">
|
||||
<div>
|
||||
<div class="muted">Tamaño total</div>
|
||||
<div class="kpi-value"><?= number_format($totalSize, 2) ?> KB</div>
|
||||
</div>
|
||||
<div class="text-muted small">Incluye todos los archivos subidos a los pedimentos</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php if (empty($expedientes)): ?>
|
||||
<div class="empty-state text-center">
|
||||
<div class="mb-2">Aún no hay pedimentos listados.</div>
|
||||
<div class="text-muted">Cuando importes pedimentos, podrás cargar y consultar documentos desde aquí.</div>
|
||||
</div>
|
||||
<?php else: ?>
|
||||
<div class="row" id="gridExp">
|
||||
<?php foreach ($expedientes as $exp):
|
||||
$ff = $exp['fecha_factura'] ?? null;
|
||||
$fecha = '-';
|
||||
if ($ff instanceof DateTime) { $fecha = $ff->format('Y-m-d'); }
|
||||
else if (is_array($ff) && isset($ff['date'])) { $fecha = substr($ff['date'],0,10); }
|
||||
elseif ($ff) { $fecha = htmlspecialchars((string)$ff); }
|
||||
$archCount = (int)($exp['total_archivos'] ?? 0);
|
||||
$hasFiles = $archCount > 0;
|
||||
?>
|
||||
<div class="col-12 col-md-6 col-xl-4 exp-card"
|
||||
data-key="<?= htmlspecialchars(($exp['pedimento_display'] ?? $exp['numero_pedimento'] ?? '').' '.($exp['proveedor'] ?? '').' '.($exp['aduana'] ?? '')) ?>"
|
||||
data-files="<?= $hasFiles ? 'with' : 'without' ?>">
|
||||
<div class="card-exp bg-white p-3 h-100">
|
||||
<div class="d-flex align-items-center justify-content-between mb-2">
|
||||
<div class="ped-code h5 mb-0"><?= htmlspecialchars($exp['pedimento_display'] ?? ($exp['numero_pedimento'] ?? '')) ?></div>
|
||||
<span class="chip <?= $hasFiles ? 'border-success text-success' : ''?>">
|
||||
<i class="fa-solid fa-paperclip"></i><?= $archCount ?> archivos
|
||||
</span>
|
||||
</div>
|
||||
<div class="d-flex flex-wrap gap-2 mb-2">
|
||||
<span class="chip"><i class="fa-solid fa-user-tie"></i><?= htmlspecialchars($exp['proveedor'] ?? '-') ?></span>
|
||||
<span class="chip"><i class="fa-regular fa-calendar"></i><?= $fecha ?></span>
|
||||
<span class="chip"><i class="fa-solid fa-warehouse"></i><?= htmlspecialchars($exp['aduana'] ?? '-') ?></span>
|
||||
<span class="chip"><i class="fa-id-badge"></i><?= htmlspecialchars($exp['patente'] ?? '-') ?></span>
|
||||
</div>
|
||||
<div class="muted small mb-3">Tamaño: <?= number_format((float)($exp['total_tamano'] ?? 0), 2) ?> KB</div>
|
||||
<div class="actions d-flex gap-2">
|
||||
<a href="/IMPORTADORES/expediente/ver_pedimento/<?= (int)$exp['pedimento_id'] ?>" class="btn btn-outline-primary btn-sm flex-fill">
|
||||
<i class="fa-regular fa-folder-open me-1"></i> Ver expediente
|
||||
</a>
|
||||
<a href="/IMPORTADORES/expediente/subir_pedimento/<?= (int)$exp['pedimento_id'] ?>" class="btn btn-outline-success btn-sm">
|
||||
<i class="fa-solid fa-upload"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
|
||||
<script>
|
||||
(function(){
|
||||
const q = document.getElementById('searchExp');
|
||||
const f = document.getElementById('filterArch');
|
||||
const cards = Array.from(document.querySelectorAll('#gridExp .exp-card'));
|
||||
function apply(){
|
||||
const term = (q?.value || '').toLowerCase();
|
||||
const filt = f?.value || 'all';
|
||||
cards.forEach(c => {
|
||||
const key = (c.getAttribute('data-key')||'').toLowerCase();
|
||||
const has = c.getAttribute('data-files');
|
||||
const match = !term || key.includes(term);
|
||||
const byFiles = (filt==='all') || (filt==='with' && has==='with') || (filt==='without' && has==='without');
|
||||
c.style.display = (match && byFiles) ? '' : 'none';
|
||||
});
|
||||
}
|
||||
q?.addEventListener('input', apply);
|
||||
f?.addEventListener('change', apply);
|
||||
apply();
|
||||
})();
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -8,64 +8,67 @@
|
||||
<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>
|
||||
<!-- Font Awesome -->
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css">
|
||||
<!-- Animate.css para animaciones adicionales -->
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.min.css">
|
||||
<style>
|
||||
body { font-family: 'Segoe UI', sans-serif; background-color: #f4f6f9; }
|
||||
.content { margin-top: 56px; padding: 40px 20px; background-color: #f4f6f9; }
|
||||
@media (min-width: 768px) { .content { margin-left: 250px; } }
|
||||
body { font-family: 'Segoe UI', sans-serif; background-color: #f6f8fb; }
|
||||
.content { margin-top: 56px; padding: 32px 20px; background-color: #f6f8fb; }
|
||||
@media (min-width: 768px) { .content { margin-left: var(--sidebar-width, 280px); } }
|
||||
.card { border-radius: 12px; }
|
||||
/* Animaciones personalizadas */
|
||||
.card-hover { transition: all 0.3s ease; cursor: pointer; }
|
||||
.card-hover:hover { transform: translateY(-8px) scale(1.02); box-shadow: 0 1rem 2rem rgba(0, 0, 0, 0.15) !important; }
|
||||
.btn-pulse { animation: pulse 2s infinite; }
|
||||
@keyframes pulse {
|
||||
0% { transform: scale(1); }
|
||||
50% { transform: scale(1.05); }
|
||||
100% { transform: scale(1); }
|
||||
}
|
||||
.fade-in-up { animation: fadeInUp 0.8s ease-out; }
|
||||
@keyframes fadeInUp {
|
||||
from { opacity: 0; transform: translateY(30px); }
|
||||
to { opacity: 1; transform: translateY(0); }
|
||||
}
|
||||
/* Responsive animations */
|
||||
@media (max-width: 768px) { .card-hover:hover { transform: translateY(-4px) scale(1.01); } }
|
||||
/* Efecto de glow para elementos activos */
|
||||
.btn.active { box-shadow: 0 0 20px rgba(var(--bs-primary-rgb), 0.5); }
|
||||
/* Animación para el título */
|
||||
.title-glow { text-shadow: 0 0 10px rgba(0, 123, 255, 0.3); }
|
||||
/* Efecto para botones */
|
||||
.btn-animated { transition: all 0.3s ease; position: relative; overflow: hidden; }
|
||||
.btn-animated:hover { transform: translateY(-2px); box-shadow: 0 5px 15px rgba(0, 0, 0, 0.2); }
|
||||
.btn-animated::before { content: ''; position: absolute; top: 0; left: -100%; width: 100%; height: 100%;
|
||||
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2), transparent); transition: left 0.5s; }
|
||||
.btn-animated:hover::before { left: 100%; }
|
||||
.table th, .table td { vertical-align: middle; }
|
||||
.folder-icon { color: #f0ad4e; font-size: 1.2rem; }
|
||||
.dropzone { border: 2px dashed #cbd5e1; border-radius: 12px; background: #fff; padding: 32px; text-align: center; cursor: pointer; transition: background .15s ease, border-color .15s ease; }
|
||||
.dropzone.dragover { background: #f1f5f9; border-color: #94a3b8; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="content">
|
||||
<h4 class="mb-4 animate__animated animate__fadeInDown title_glow">📤 Subir Archivos al Expediente</h4>
|
||||
<div class="card p-4 bg-white shadow-sm card-hover position-relative h-auto fade-in-up">
|
||||
<form action="/IMPORTADORES/expediente/subir_handler" method="POST" enctype="multipart/form-data">
|
||||
|
||||
<input type="hidden" name="id_solicitud" value="<?= $id_solicitud ?>">
|
||||
<h4 class="mb-4">📤 Subir archivos al expediente</h4>
|
||||
<div class="card p-4 bg-white shadow-sm">
|
||||
<form action="/IMPORTADORES/expediente/subir_handler" method="POST" enctype="multipart/form-data" id="formUpload">
|
||||
<?php if (isset($pedimento_id)): ?>
|
||||
<input type="hidden" name="pedimento_id" value="<?= (int)$pedimento_id ?>">
|
||||
<?php else: ?>
|
||||
<input type="hidden" name="id_solicitud" value="<?= $id_solicitud ?>">
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="archivo" class="form-label">Selecciona archivo(s)</label>
|
||||
<input class="form-control" type="file" name="archivos[]" id="archivo" multiple required>
|
||||
<div id="dz" class="dropzone">
|
||||
<i class="fa-solid fa-cloud-arrow-up fa-2xl mb-2"></i>
|
||||
<div class="mb-1">Arrastra y suelta los archivos aquí</div>
|
||||
<div class="text-muted small">o haz clic para seleccionar</div>
|
||||
<input class="form-control d-none" type="file" name="archivos[]" id="archivo" multiple required>
|
||||
</div>
|
||||
<div id="fileList" class="mt-3 small text-muted"></div>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn btn-success mt-auto w-auto btn-animated">Subir</button>
|
||||
<a href="/IMPORTADORES/expediente/index" class="btn btn-secondary ms-2 mt-auto w-auto btn-animated">Cancelar</a>
|
||||
<div class="d-flex gap-2">
|
||||
<button type="submit" class="btn btn-success">Subir</button>
|
||||
<a href="/IMPORTADORES/expediente/index" class="btn btn-secondary">Cancelar</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
(function(){
|
||||
const dz = document.getElementById('dz');
|
||||
const input = document.getElementById('archivo');
|
||||
const list = document.getElementById('fileList');
|
||||
function openPicker(){ input.click(); }
|
||||
function updateList(){
|
||||
const files = Array.from(input.files || []);
|
||||
if (!files.length) { list.textContent = ''; return; }
|
||||
list.innerHTML = files.map(f => `• ${f.name} (${(f.size/1024).toFixed(1)} KB)`).join('<br>');
|
||||
}
|
||||
dz.addEventListener('click', openPicker);
|
||||
dz.addEventListener('dragover', (e)=>{ e.preventDefault(); dz.classList.add('dragover'); });
|
||||
dz.addEventListener('dragleave', ()=> dz.classList.remove('dragover'));
|
||||
dz.addEventListener('drop', (e)=>{
|
||||
e.preventDefault(); dz.classList.remove('dragover');
|
||||
if (e.dataTransfer?.files?.length){ input.files = e.dataTransfer.files; updateList(); }
|
||||
});
|
||||
input.addEventListener('change', updateList);
|
||||
})();
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -7,104 +7,123 @@
|
||||
<title>📂 Ver Expediente</title>
|
||||
<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>
|
||||
<!-- Font Awesome -->
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css">
|
||||
<!-- Animate.css para animaciones adicionales -->
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.min.css">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.0/css/all.min.css">
|
||||
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
|
||||
<style>
|
||||
body { font-family: 'Segoe UI', sans-serif; background-color: #f4f6f9; }
|
||||
.content { margin-top: 56px; padding: 40px 20px; background-color: #f4f6f9; }
|
||||
@media (min-width: 768px) { .content { margin-left: 250px; } }
|
||||
.file-icon { margin-right: 8px; }
|
||||
.card { border-radius: 12px; }
|
||||
/* Animaciones personalizadas */
|
||||
.card-hover { transition: all 0.3s ease; cursor: pointer; }
|
||||
.card-hover:hover { transform: translateY(-8px) scale(1.02); box-shadow: 0 1rem 2rem rgba(0, 0, 0, 0.15) !important; }
|
||||
.btn-pulse { animation: pulse 2s infinite; }
|
||||
@keyframes pulse {
|
||||
0% { transform: scale(1); }
|
||||
50% { transform: scale(1.05); }
|
||||
100% { transform: scale(1); }
|
||||
}
|
||||
.fade-in-up { animation: fadeInUp 0.8s ease-out; }
|
||||
@keyframes fadeInUp {
|
||||
from { opacity: 0; transform: translateY(30px); }
|
||||
to { opacity: 1; transform: translateY(0); }
|
||||
}
|
||||
/* Responsive animations */
|
||||
@media (max-width: 768px) { .card-hover:hover { transform: translateY(-4px) scale(1.01); } }
|
||||
/* Efecto de glow para elementos activos */
|
||||
.btn.active { box-shadow: 0 0 20px rgba(var(--bs-primary-rgb), 0.5); }
|
||||
/* Animación para el título */
|
||||
.title-glow { text-shadow: 0 0 10px rgba(0, 123, 255, 0.3); }
|
||||
/* Efecto para botones */
|
||||
.btn-animated { transition: all 0.3s ease; position: relative; overflow: hidden; }
|
||||
.btn-animated:hover { transform: translateY(-2px); box-shadow: 0 5px 15px rgba(0, 0, 0, 0.2); }
|
||||
.btn-animated::before { content: ''; position: absolute; top: 0; left: -100%; width: 100%; height: 100%;
|
||||
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2), transparent); transition: left 0.5s; }
|
||||
.btn-animated:hover::before { left: 100%; }
|
||||
.table th, .table td { vertical-align: middle; }
|
||||
.folder-icon { color: #f0ad4e; font-size: 1.2rem; }
|
||||
body { font-family: 'Segoe UI', sans-serif; background-color: #f6f8fb; }
|
||||
.content { margin-top: 56px; padding: 32px 20px; background-color: #f6f8fb; }
|
||||
@media (min-width: 768px) { .content { margin-left: var(--sidebar-width, 280px); } }
|
||||
.ped-code { font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New', monospace; letter-spacing: .5px; }
|
||||
.chip { display: inline-flex; align-items: center; gap: .35rem; padding: .15rem .5rem; border-radius: 999px; font-size: .78rem; border: 1px solid #e5e7eb; background: #f8fafc; color: #334155; }
|
||||
.kpi-card { border: 1px solid #e9ecef; border-radius: 12px; background: #fff; }
|
||||
.file-card { border: 1px solid #eef1f5; border-radius: 12px; background:#fff; transition: box-shadow .2s ease, transform .2s ease; }
|
||||
.file-card:hover { box-shadow: 0 .75rem 1.5rem rgba(0,0,0,.08); transform: translateY(-2px); }
|
||||
.file-icon { width: 36px; height: 36px; display: inline-flex; align-items: center; justify-content: center; border-radius: 8px; background: #f1f5f9; }
|
||||
.toolbar .btn { border-radius: 10px; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="content">
|
||||
<h4 class="mb-4 animate__animated animate__fadeInDown title_glow">📂 Archivos del Expediente</h4>
|
||||
<div class="card p-4 bg-white shadow-sm card-hover position-relative h-auto fade-in-up">
|
||||
<?php if (empty($archivos)): ?>
|
||||
|
||||
<p>No hay archivos subidos para esta solicitud.</p>
|
||||
<a href="/IMPORTADORES/expediente/subir/<?= $id_solicitud ?>" class="btn btn-success mt-auto w-auto btn-animated">➕ Subir archivos</a>
|
||||
<?php
|
||||
$archCount = count($archivos ?? []);
|
||||
$totalSize = 0; foreach(($archivos ?? []) as $a){ $totalSize += (float)($a['tamano_archivo'] ?? 0); }
|
||||
$displayCode = $pedimento['pedimento_display'] ?? ($pedimento_id ?? '');
|
||||
?>
|
||||
|
||||
<?php else: ?>
|
||||
<div class="mb-3">
|
||||
<a href="/IMPORTADORES/expediente/descargar_zip/<?= $id_solicitud ?>" class="btn btn-outline-dark mt-auto w-auto btn-animated">📦 Descargar todo en ZIP</a>
|
||||
<div class="content">
|
||||
<div class="d-flex flex-wrap align-items-center justify-content-between mb-3">
|
||||
<div>
|
||||
<div class="text-muted small">Pedimento</div>
|
||||
<h3 class="ped-code mb-1"><?= htmlspecialchars($displayCode) ?></h3>
|
||||
<div class="d-flex flex-wrap gap-2">
|
||||
<span class="chip"><i class="fa-solid fa-warehouse"></i><?= htmlspecialchars($pedimento['aduana'] ?? '-') ?></span>
|
||||
<span class="chip"><i class="fa-id-badge"></i><?= htmlspecialchars($pedimento['patente'] ?? '-') ?></span>
|
||||
<span class="chip"><i class="fa-regular fa-calendar"></i><?= htmlspecialchars($pedimento['anio'] ?? '-') ?></span>
|
||||
<?php if (!empty($pedimento['rfc_importador'])): ?>
|
||||
<span class="chip"><i class="fa-solid fa-building"></i><?= htmlspecialchars($pedimento['rfc_importador']) ?></span>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<table class="table table-hover table-bordered">
|
||||
<thead class="table-light">
|
||||
<tr>
|
||||
<th>Archivo </th>
|
||||
<th>Tipo</th>
|
||||
<th>Tamaño (KB)</th>
|
||||
<th>Subido por</th>
|
||||
<th>Fecha</th>
|
||||
<th>Acciones</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($archivos as $file): ?>
|
||||
<tr>
|
||||
<td>
|
||||
<?php
|
||||
$icon = 'fa-file';
|
||||
$ext = strtolower(pathinfo($file['nombre_archivo'], PATHINFO_EXTENSION));
|
||||
if (in_array($ext, ['pdf'])) $icon = 'fa-file-pdf text-danger';
|
||||
elseif (in_array($ext, ['jpg', 'jpeg', 'png'])) $icon = 'fa-file-image text-info';
|
||||
elseif (in_array($ext, ['doc', 'docx'])) $icon = 'fa-file-word text-primary';
|
||||
elseif (in_array($ext, ['xls', 'xlsx'])) $icon = 'fa-file-excel text-success';
|
||||
elseif (in_array($ext, ['zip', 'rar'])) $icon = 'fa-file-archive text-warning';
|
||||
?>
|
||||
<i class="fas <?= $icon ?> file-icon"></i>
|
||||
<?= htmlspecialchars($file['nombre_archivo']) ?>
|
||||
</td>
|
||||
<td><?= htmlspecialchars($file['tipo_archivo']) ?></td>
|
||||
<td><?= number_format($file['tamano_archivo'], 2) ?></td>
|
||||
<td><?= htmlspecialchars($file['creado_por']) ?></td>
|
||||
<td><?= $file['creado_en']->format('Y-m-d H:i') ?></td>
|
||||
<td class="d-flex gap-2">
|
||||
<a href="/IMPORTADORES/expediente/ver_archivo/<?= $file['id'] ?>" class="btn btn-sm btn-outline-secondary" target="_blank">👁 Ver</a>
|
||||
<a href="/IMPORTADORES/<?= $file['ruta_archivo'] ?>" download class="btn btn-sm btn-outline-primary">⬇ Descargar</a>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
<?php endif; ?>
|
||||
<a href="/IMPORTADORES/expediente/index" class="btn btn-secondary mt-3">← Volver</a>
|
||||
</div>
|
||||
<div class="toolbar d-flex flex-wrap gap-2 align-items-center">
|
||||
<input id="searchFiles" type="search" class="form-control" placeholder="Buscar por nombre o tipo">
|
||||
<?php if (isset($pedimento_id)): ?>
|
||||
<a href="/IMPORTADORES/expediente/subir_pedimento/<?= (int)$pedimento_id ?>" class="btn btn-success"><i class="fa-solid fa-upload me-1"></i> Subir</a>
|
||||
<a href="/IMPORTADORES/expediente/descargar_zip_pedimento/<?= (int)$pedimento_id ?>" class="btn btn-outline-dark"><i class="fa-solid fa-box-archive me-1"></i> ZIP</a>
|
||||
<?php else: ?>
|
||||
<a href="/IMPORTADORES/expediente/subir/<?= $id_solicitud ?>" class="btn btn-success"><i class="fa-solid fa-upload me-1"></i> Subir</a>
|
||||
<a href="/IMPORTADORES/expediente/descargar_zip/<?= $id_solicitud ?>" class="btn btn-outline-dark"><i class="fa-solid fa-box-archive me-1"></i> ZIP</a>
|
||||
<?php endif; ?>
|
||||
<a href="/IMPORTADORES/expediente/index" class="btn btn-secondary">← Volver</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row g-3 mb-4">
|
||||
<div class="col-6 col-md-3">
|
||||
<div class="kpi-card p-3">
|
||||
<div class="text-muted">Archivos</div>
|
||||
<div class="h5 mb-0"><?= (int)$archCount ?></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-6 col-md-3">
|
||||
<div class="kpi-card p-3">
|
||||
<div class="text-muted">Tamaño total</div>
|
||||
<div class="h5 mb-0"><?= number_format($totalSize, 2) ?> KB</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php if (empty($archivos)): ?>
|
||||
<div class="p-4 bg-white border rounded-3 text-center">
|
||||
No hay archivos subidos para este expediente.
|
||||
</div>
|
||||
<?php else: ?>
|
||||
<div class="row" id="gridFiles">
|
||||
<?php foreach ($archivos as $file):
|
||||
$icon = 'fa-file';
|
||||
$ext = strtolower(pathinfo($file['nombre_archivo'], PATHINFO_EXTENSION));
|
||||
if (in_array($ext, ['pdf'])) $icon = 'fa-file-pdf text-danger';
|
||||
elseif (in_array($ext, ['jpg', 'jpeg', 'png'])) $icon = 'fa-file-image text-info';
|
||||
elseif (in_array($ext, ['doc', 'docx'])) $icon = 'fa-file-word text-primary';
|
||||
elseif (in_array($ext, ['xls', 'xlsx'])) $icon = 'fa-file-excel text-success';
|
||||
elseif (in_array($ext, ['zip', 'rar'])) $icon = 'fa-file-archive text-warning';
|
||||
$fecha = $file['creado_en'] instanceof DateTime ? $file['creado_en']->format('Y-m-d H:i') : '';
|
||||
?>
|
||||
<div class="col-12 col-md-6 col-xl-4 file-item" data-key="<?= htmlspecialchars(($file['nombre_archivo'] ?? '').' '.($file['tipo_archivo'] ?? '')) ?>">
|
||||
<div class="file-card p-3 h-100">
|
||||
<div class="d-flex align-items-start gap-3 mb-2">
|
||||
<div class="file-icon"><i class="fas <?= $icon ?>"></i></div>
|
||||
<div class="flex-grow-1">
|
||||
<div class="fw-semibold"><?= htmlspecialchars($file['nombre_archivo']) ?></div>
|
||||
<div class="text-muted small">Tipo: <?= htmlspecialchars($file['tipo_archivo']) ?> · <?= number_format($file['tamano_archivo'], 2) ?> KB</div>
|
||||
<div class="text-muted small">Subido por <?= htmlspecialchars($file['creado_por']) ?> · <?= $fecha ?></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="d-flex gap-2">
|
||||
<a href="/IMPORTADORES/expediente/ver_archivo/<?= $file['id'] ?>" class="btn btn-sm btn-outline-secondary" target="_blank"><i class="fa-regular fa-eye"></i></a>
|
||||
<a href="/IMPORTADORES/<?= $file['ruta_archivo'] ?>" download class="btn btn-sm btn-outline-primary"><i class="fa-solid fa-download"></i></a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
|
||||
<script>
|
||||
(function(){
|
||||
const q = document.getElementById('searchFiles');
|
||||
const items = Array.from(document.querySelectorAll('#gridFiles .file-item'));
|
||||
function apply(){
|
||||
const term = (q?.value || '').toLowerCase();
|
||||
items.forEach(it => {
|
||||
const key = (it.getAttribute('data-key')||'').toLowerCase();
|
||||
it.style.display = !term || key.includes(term) ? '' : 'none';
|
||||
});
|
||||
}
|
||||
q?.addEventListener('input', apply);
|
||||
apply();
|
||||
})();
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -355,10 +355,10 @@ $mensajeMantenimiento = $config['mensaje_mantenimiento'] ?? 'El sistema se encue
|
||||
<div class="row align-items-center">
|
||||
<div class="col-lg-6">
|
||||
<div class="hero-content">
|
||||
<h1>Gestión Moderna de <span style="color: #10b981;">Importaciones</span></h1>
|
||||
<h1>Manifestación de Valor <span style="color: #10b981;">Electrónica</span></h1>
|
||||
<p class="lead">
|
||||
Simplifica tus procesos aduaneros con nuestra plataforma integral.
|
||||
Desde pedimentos hasta seguimiento, todo en un solo lugar.
|
||||
Simplifica tus trámites de MVE con nuestra plataforma especializada.
|
||||
Desde la solicitud hasta la respuesta del SAT, gestiona todo digitalmente.
|
||||
</p>
|
||||
<div class="d-flex flex-column flex-md-row gap-3">
|
||||
<a href="/IMPORTADORES/registro" class="btn-modern btn-primary-modern">
|
||||
@@ -392,7 +392,7 @@ $mensajeMantenimiento = $config['mensaje_mantenimiento'] ?? 'El sistema se encue
|
||||
<div class="col-6 col-md-3">
|
||||
<div class="stat-item">
|
||||
<span class="stat-number">25K+</span>
|
||||
<div class="stat-label">Trámites</div>
|
||||
<div class="stat-label">MVE Procesadas</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-6 col-md-3">
|
||||
@@ -414,20 +414,20 @@ $mensajeMantenimiento = $config['mensaje_mantenimiento'] ?? 'El sistema se encue
|
||||
<!-- FEATURES -->
|
||||
<section class="section-modern" id="features">
|
||||
<div class="container">
|
||||
<h2 class="section-title">Todo lo que necesitas</h2>
|
||||
<h2 class="section-title">MVE Digital Completa</h2>
|
||||
<p class="section-subtitle">
|
||||
Herramientas profesionales diseñadas para optimizar cada etapa del proceso de importación
|
||||
Herramientas especializadas para gestionar eficientemente tu Manifestación de Valor Electrónica
|
||||
</p>
|
||||
|
||||
<div class="row g-4">
|
||||
<div class="col-lg-4 col-md-6">
|
||||
<div class="feature-card">
|
||||
<div class="feature-icon">
|
||||
<i class="fas fa-file-invoice"></i>
|
||||
<i class="fas fa-file-contract"></i>
|
||||
</div>
|
||||
<h5 class="feature-title">Gestión de Pedimentos</h5>
|
||||
<h5 class="feature-title">Solicitud MVE Automática</h5>
|
||||
<p class="feature-description">
|
||||
Crea, gestiona y da seguimiento a tus pedimentos de importación con validación automática de datos.
|
||||
Genera automáticamente tu Manifestación de Valor Electrónica con base en los datos del pedimento y facturas.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -435,11 +435,11 @@ $mensajeMantenimiento = $config['mensaje_mantenimiento'] ?? 'El sistema se encue
|
||||
<div class="col-lg-4 col-md-6">
|
||||
<div class="feature-card">
|
||||
<div class="feature-icon">
|
||||
<i class="fas fa-shield-check"></i>
|
||||
<i class="fas fa-clock"></i>
|
||||
</div>
|
||||
<h5 class="feature-title">Cumplimiento Automatizado</h5>
|
||||
<h5 class="feature-title">Seguimiento en Tiempo Real</h5>
|
||||
<p class="feature-description">
|
||||
Mantente al día con regulaciones aduaneras y recibe alertas sobre cambios normativos relevantes.
|
||||
Monitorea el estatus de tus MVE desde el envío hasta la respuesta del SAT con notificaciones automáticas.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -447,11 +447,11 @@ $mensajeMantenimiento = $config['mensaje_mantenimiento'] ?? 'El sistema se encue
|
||||
<div class="col-lg-4 col-md-6">
|
||||
<div class="feature-card">
|
||||
<div class="feature-icon">
|
||||
<i class="fas fa-analytics"></i>
|
||||
<i class="fas fa-file-signature"></i>
|
||||
</div>
|
||||
<h5 class="feature-title">Reportes Inteligentes</h5>
|
||||
<h5 class="feature-title">Firma Digital Integrada</h5>
|
||||
<p class="feature-description">
|
||||
Analiza tus operaciones con dashboards interactivos y reportes personalizables en tiempo real.
|
||||
Firma electrónicamente tus MVE con validación biométrica y cumplimiento normativo SAT.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -459,11 +459,11 @@ $mensajeMantenimiento = $config['mensaje_mantenimiento'] ?? 'El sistema se encue
|
||||
<div class="col-lg-4 col-md-6">
|
||||
<div class="feature-card">
|
||||
<div class="feature-icon">
|
||||
<i class="fas fa-users"></i>
|
||||
<i class="fas fa-database"></i>
|
||||
</div>
|
||||
<h5 class="feature-title">Colaboración</h5>
|
||||
<h5 class="feature-title">Historial Completo</h5>
|
||||
<p class="feature-description">
|
||||
Conecta con agentes aduanales, transportistas y proveedores en un ecosistema colaborativo.
|
||||
Mantén un registro detallado de todas tus MVE con expediente digital y documentación respaldatoria.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -471,11 +471,11 @@ $mensajeMantenimiento = $config['mensaje_mantenimiento'] ?? 'El sistema se encue
|
||||
<div class="col-lg-4 col-md-6">
|
||||
<div class="feature-card">
|
||||
<div class="feature-icon">
|
||||
<i class="fas fa-mobile-alt"></i>
|
||||
<i class="fas fa-calculator"></i>
|
||||
</div>
|
||||
<h5 class="feature-title">Acceso Universal</h5>
|
||||
<h5 class="feature-title">Validación Automática</h5>
|
||||
<p class="feature-description">
|
||||
Accede desde cualquier dispositivo con nuestra aplicación web responsiva y segura.
|
||||
Valida automáticamente precios, cantidades y valores con base de datos actualizada del SAT.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -483,11 +483,11 @@ $mensajeMantenimiento = $config['mensaje_mantenimiento'] ?? 'El sistema se encue
|
||||
<div class="col-lg-4 col-md-6">
|
||||
<div class="feature-card">
|
||||
<div class="feature-icon">
|
||||
<i class="fas fa-lock"></i>
|
||||
<i class="fas fa-shield-alt"></i>
|
||||
</div>
|
||||
<h5 class="feature-title">Seguridad Total</h5>
|
||||
<h5 class="feature-title">Cumplimiento Normativo</h5>
|
||||
<p class="feature-description">
|
||||
Protección enterprise con encriptación avanzada, backups automáticos y certificaciones de seguridad.
|
||||
Garantiza el cumplimiento total con las disposiciones del SAT para MVE según la normatividad vigente.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -498,9 +498,9 @@ $mensajeMantenimiento = $config['mensaje_mantenimiento'] ?? 'El sistema se encue
|
||||
<!-- CTA -->
|
||||
<section class="cta-section">
|
||||
<div class="container">
|
||||
<h2 class="section-title" style="margin-bottom: 1rem;">¿Listo para optimizar tus importaciones?</h2>
|
||||
<h2 class="section-title" style="margin-bottom: 1rem;">¿Listo para digitalizar tus MVE?</h2>
|
||||
<p class="section-subtitle" style="margin-bottom: 3rem;">
|
||||
Únete a más de 1,200 importadores que ya confían en nuestra plataforma
|
||||
Únete a más de 1,200 importadores que ya gestionan sus Manifestaciones de Valor Electrónica digitalmente
|
||||
</p>
|
||||
<a href="/IMPORTADORES/registro" class="btn-modern btn-primary-modern">
|
||||
<i class="fas fa-arrow-right"></i>
|
||||
|
||||
@@ -6,209 +6,357 @@
|
||||
<meta charset="UTF-8">
|
||||
<title>Dashboard | Importador</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>
|
||||
<!-- Font Awesome -->
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css">
|
||||
<!-- Animate.css para animaciones adicionales -->
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.min.css">
|
||||
<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: #4dd4ac ; color: white; }
|
||||
.btn-teal:hover { background-color: #20c997; color: black; }
|
||||
.text-teal { color: #4dd4ac; }
|
||||
.btn-lime { background-color: #84cc16; color: white; }
|
||||
.text-lime { color: #84cc16; }
|
||||
.btn-lime:hover { background-color: #65a30d; color: black; }
|
||||
.btn-cyan { background-color: #06b6d4; color: white; }
|
||||
.text-cyan { color: #06b6d4; }
|
||||
.btn-cyan:hover { background-color: #0891b2; color: white; }
|
||||
.btn-violet { background-color: #8b5cf6; color: white; }
|
||||
.text-violet { color: #8b5cf6; }
|
||||
.btn-violet:hover { background-color: #7c3aed; color: white; }
|
||||
.btn-lila { background-color: #c4b5fd; /* violeta claro */ color: white; border: none; }
|
||||
.btn-lila:hover { background-color: #a78bfa; /* tono ligeramente más fuerte */ color: white; }
|
||||
.text-lila { color: #c4b5fd; }
|
||||
/* 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; }
|
||||
:root {
|
||||
--primary: #2563eb;
|
||||
--primary-dark: #1d4ed8;
|
||||
--secondary: #64748b;
|
||||
--success: #059669;
|
||||
--warning: #d97706;
|
||||
--danger: #dc2626;
|
||||
--info: #0891b2;
|
||||
--light: #f8fafc;
|
||||
--dark: #0f172a;
|
||||
--border-color: #e2e8f0;
|
||||
--text-muted: #64748b;
|
||||
--bg-body: #f8fafc;
|
||||
}
|
||||
.card { border-radius: 12px; }
|
||||
/* Animaciones personalizadas */
|
||||
.card-hover { transition: all 0.3s ease; cursor: pointer; }
|
||||
.card-hover:hover { transform: translateY(-8px) saqcle(1.02); box-shadow: 0 1rem 2rem rgba(0, 0, 0, 0.15) !important; }
|
||||
.btn-pulse { animation: pulse 2s infinite; }
|
||||
@keyframes pulse {
|
||||
0% { transform: scale(1); }
|
||||
50% { transform: scale(1.05); }
|
||||
100% { transform: scale(1); }
|
||||
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.fade-in-up { animation: fadeInUp 0.8s ease-out; }
|
||||
@keyframes fadeInUp {
|
||||
from { opacity: 0; transform: translateY(30px); }
|
||||
to { opacity: 1; transform: translateY(0); }
|
||||
|
||||
body {
|
||||
font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;
|
||||
background-color: var(--bg-body);
|
||||
color: var(--dark);
|
||||
line-height: 1.6;
|
||||
}
|
||||
.loading-spinner { display: none; }
|
||||
/* Botón con efecto de loading */
|
||||
.btn-loading { position: relative; color: transparent !important; pointer-events: none; }
|
||||
.btn-loading::after { content: ''; position: absolute; width: 20px; height: 20px; top: 50%; left: 50%; margin-left: -10px;
|
||||
margin-top: -10px; border: 2px solid #fff; border-radius: 50%; border-top-color: transparent; animation: spin 1s linear infinite; }
|
||||
.btn-loading .loading-spinner { display: inline-block; }
|
||||
.btn-loading .btn-text { display: none; }
|
||||
@keyframes spin { to { transform: rotate(360deg); } }
|
||||
/* Responsive animations */
|
||||
@media (max-width: 768px) { .card-hover:hover { transform: translateY(-4px) scale(1.01); } }
|
||||
/* Efecto de glow para elementos activos */
|
||||
.btn.active { box-shadow: 0 0 20px rgba(var(--bs-primary-rgb), 0.5); }
|
||||
/* Animación para el título */
|
||||
.title-glow { text-shadow: 0 0 10px rgba(0, 123, 255, 0.3); }
|
||||
/* Efecto para botones */
|
||||
.btn-animated { transition: all 0.3s ease; position: relative; overflow: hidden; }
|
||||
.btn-animated:hover { transform: translateY(-2px); box-shadow: 0 5px 15px rgba(0, 0, 0, 0.2); }
|
||||
.btn-animated::before { content: ''; position: absolute; top: 0; left: -100%; width: 100%; height: 100%;
|
||||
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2), transparent); transition: left 0.5s; }
|
||||
.btn-animated:hover::before { left: 100%; }
|
||||
/* Animación para alertas */
|
||||
.alert-animated { animation: slideInDown 0.5s ease-out; }
|
||||
@keyframes slideInDown {
|
||||
from { transform: translateY(-100%); opacity: 0; }
|
||||
to { transform: translateY(0); opacity: 1; }
|
||||
|
||||
.main-content {
|
||||
margin-left: 280px;
|
||||
padding: 2rem;
|
||||
min-height: 100vh;
|
||||
transition: margin-left 0.3s ease;
|
||||
}
|
||||
/* Efecto de parpadeo para elementos obligatorios */
|
||||
.border-warning-animated { animation: borderGlow 2s ease-in-out infinite alternate; }
|
||||
@keyframes borderGlow {
|
||||
from { border-color: #ffc107; box-shadow: 0 0 5px rgba(255, 193, 7, 0.5); }
|
||||
to { border-color: #ffcd39; box-shadow: 0 0 20px rgba(255, 193, 7, 0.8); }
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.main-content {
|
||||
margin-left: 0;
|
||||
padding: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
.page-header {
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.page-title {
|
||||
color: var(--dark);
|
||||
font-weight: 600;
|
||||
font-size: 1.75rem;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.page-subtitle {
|
||||
color: var(--text-muted);
|
||||
font-weight: 400;
|
||||
font-size: 0.95rem;
|
||||
}
|
||||
|
||||
.dashboard-card {
|
||||
background: white;
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 12px;
|
||||
padding: 1.5rem;
|
||||
height: 100%;
|
||||
transition: all 0.2s ease;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.dashboard-card:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 8px 25px rgba(0, 0, 0, 0.1);
|
||||
border-color: var(--primary);
|
||||
}
|
||||
|
||||
.card-icon {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
border-radius: 8px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-bottom: 1rem;
|
||||
font-size: 1.25rem;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.card-icon.primary { background: var(--primary); }
|
||||
.card-icon.success { background: var(--success); }
|
||||
.card-icon.warning { background: var(--warning); }
|
||||
.card-icon.info { background: var(--info); }
|
||||
.card-icon.danger { background: var(--danger); }
|
||||
|
||||
.card-title {
|
||||
font-weight: 600;
|
||||
font-size: 1.1rem;
|
||||
color: var(--dark);
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.card-description {
|
||||
color: var(--text-muted);
|
||||
font-size: 0.9rem;
|
||||
margin-bottom: 1.5rem;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.card-action {
|
||||
margin-top: auto;
|
||||
}
|
||||
|
||||
.btn-card {
|
||||
background: var(--primary);
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 0.625rem 1.25rem;
|
||||
border-radius: 6px;
|
||||
font-weight: 500;
|
||||
font-size: 0.875rem;
|
||||
transition: all 0.2s ease;
|
||||
width: 100%;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.btn-card:hover {
|
||||
background: var(--primary-dark);
|
||||
color: white;
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
.btn-card.success {
|
||||
background: var(--success);
|
||||
}
|
||||
|
||||
.btn-card.success:hover {
|
||||
background: #047857;
|
||||
}
|
||||
|
||||
.btn-card.warning {
|
||||
background: var(--warning);
|
||||
}
|
||||
|
||||
.btn-card.warning:hover {
|
||||
background: #b45309;
|
||||
}
|
||||
|
||||
.btn-card.info {
|
||||
background: var(--info);
|
||||
}
|
||||
|
||||
.btn-card.info:hover {
|
||||
background: #0e7490;
|
||||
}
|
||||
|
||||
.btn-card.danger {
|
||||
background: var(--danger);
|
||||
}
|
||||
|
||||
.btn-card.danger:hover {
|
||||
background: #b91c1c;
|
||||
}
|
||||
|
||||
.badge-required {
|
||||
position: absolute;
|
||||
top: 1rem;
|
||||
right: 1rem;
|
||||
background: var(--warning);
|
||||
color: white;
|
||||
font-size: 0.75rem;
|
||||
padding: 0.25rem 0.5rem;
|
||||
border-radius: 4px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.alert-professional {
|
||||
background: white;
|
||||
border: 1px solid var(--border-color);
|
||||
border-left: 4px solid var(--info);
|
||||
border-radius: 8px;
|
||||
padding: 1.5rem;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.alert-professional.warning {
|
||||
border-left-color: var(--warning);
|
||||
}
|
||||
|
||||
.alert-icon {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border-radius: 8px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-right: 1rem;
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
|
||||
.alert-icon.info {
|
||||
background: rgba(8, 145, 178, 0.1);
|
||||
color: var(--info);
|
||||
}
|
||||
|
||||
.alert-icon.warning {
|
||||
background: rgba(217, 119, 6, 0.1);
|
||||
color: var(--warning);
|
||||
}
|
||||
|
||||
.alert-title {
|
||||
font-weight: 600;
|
||||
font-size: 1rem;
|
||||
color: var(--dark);
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.alert-text {
|
||||
color: var(--text-muted);
|
||||
font-size: 0.9rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.btn-loading {
|
||||
opacity: 0.7;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.btn-loading::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
margin-left: -8px;
|
||||
margin-top: -8px;
|
||||
border: 2px solid transparent;
|
||||
border-top: 2px solid currentColor;
|
||||
border-radius: 50%;
|
||||
animation: spin 1s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
to { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
.fade-in {
|
||||
animation: fadeIn 0.5s ease-out;
|
||||
}
|
||||
|
||||
@keyframes fadeIn {
|
||||
from { opacity: 0; transform: translateY(10px); }
|
||||
to { opacity: 1; transform: translateY(0); }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="content">
|
||||
<h4 class="mb-4 animate__animated animate__fadeInDown title_glow">📦 Panel del Importador</h4>
|
||||
<div class="main-content">
|
||||
<!-- Page Header -->
|
||||
<div class="page-header">
|
||||
<h1 class="page-title">Dashboard Importador</h1>
|
||||
<p class="page-subtitle">Gestiona tus catálogos y accede a las funcionalidades del sistema</p>
|
||||
</div>
|
||||
|
||||
<?php if ($catalogosData['catalogos_activos']): ?>
|
||||
<div class="row g-4">
|
||||
<?php
|
||||
$delay = 0;
|
||||
foreach ($catalogosData['catalogos'] as $catalogo):
|
||||
$delay += 0.1; // Incrementar el retraso para cada catálogo
|
||||
?>
|
||||
<div class="col-md-4 animate__animated animate__fadeInUp" style="animation-delay: <?= $delay ?>s;">
|
||||
<div class="card shadow-sm p-3 card-hover <?= $catalogo['obligatorio'] ? 'border-warning border-warning-animated' : '' ?>">
|
||||
<div class="card-body p-0 d-flex flex-column">
|
||||
<h5 class="text-<?= $catalogo['color'] ?> mb-3">
|
||||
<i class="<?= obtenerIconoCatalogo($catalogo['nombre']) ?> me-2"></i><?= $catalogo['nombre'] ?>
|
||||
</h5>
|
||||
<p class="text-muted small flex-grow-1 mb-4"><?= $catalogo['descripcion'] ?></p>
|
||||
<div class="mt-auto">
|
||||
<button
|
||||
class="btn btn-<?= $catalogo['color'] ?> btn-sm mt-auto w-100 btn-animated <?= $_SERVER['REQUEST_URI'] === $catalogo['ruta'] ? 'active' : '' ?>"
|
||||
onclick="navigateWithAnimation(this, '<?= $catalogo['ruta'] ?>')"
|
||||
data-url="<?= $catalogo['ruta'] ?>">
|
||||
<span class="loading-spinner spinner-border spinner-border-sm me-2" role="status" aria-hidden="true"></span>
|
||||
<span class="btn-text"><?= $catalogo['accion'] ?></span>
|
||||
</button>
|
||||
</div>
|
||||
<?php if ($catalogo['obligatorio']): ?>
|
||||
<small class="text-warning mt-2 animate__animated animate__pulse animate__infinite">
|
||||
<i class="fas fa-exclamation-triangle me-1"></i>
|
||||
Obligatorio
|
||||
</small>
|
||||
<?php endif; ?>
|
||||
<!-- Catálogos Grid -->
|
||||
<div class="row g-4 mb-4">
|
||||
<?php foreach ($catalogosData['catalogos'] as $catalogo): ?>
|
||||
<div class="col-lg-4 col-md-6 fade-in">
|
||||
<div class="dashboard-card" onclick="navigateWithAnimation(this, '<?= $catalogo['ruta'] ?>')">
|
||||
<?php if ($catalogo['obligatorio']): ?>
|
||||
<div class="badge-required">Obligatorio</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="card-icon <?= getCardColorClass($catalogo['color']) ?>">
|
||||
<i class="<?= obtenerIconoCatalogo($catalogo['nombre']) ?>"></i>
|
||||
</div>
|
||||
|
||||
<h3 class="card-title"><?= $catalogo['nombre'] ?></h3>
|
||||
<p class="card-description"><?= $catalogo['descripcion'] ?></p>
|
||||
|
||||
<div class="card-action">
|
||||
<button class="btn-card <?= $catalogo['color'] ?>">
|
||||
<?= $catalogo['accion'] ?>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
|
||||
<?php if (count($catalogosData['catalogos']) === 2): // Solo obligatorios ?>
|
||||
<div class="row mt-4">
|
||||
<div class="col-12">
|
||||
<div class="alert alert-info alert-animated">
|
||||
<div class="d-flex align-items-center">
|
||||
<div class="me-3">
|
||||
<i class="fas fa-lightbulb fa-2x text-info animate__animated animate__pulse animate__infinite"></i>
|
||||
</div>
|
||||
<div class="flex-grow-1">
|
||||
<h6 class="alert-heading">💡 Consejo</h6>
|
||||
<p class="mb-2">Parece que no tienes catálogos activados. Puedes activar los que necesites desde tus preferencias.</p>
|
||||
<button class="btn btn-primary btn-sm btn-pulse" onclick="navigateWithAnimation(this, '/IMPORTADORES/preferencias/catalogos')">
|
||||
<span class="loading-spinner spinner-border spinner-border-sm me-2" role="status" aria-hidden="true"></span>
|
||||
<span class="btn-text">Configurar Catálogos</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<?php if (count($catalogosData['catalogos']) === 2): ?>
|
||||
<!-- Info Alert -->
|
||||
<div class="alert-professional">
|
||||
<div class="d-flex align-items-start">
|
||||
<div class="alert-icon info">
|
||||
<i class="fas fa-lightbulb"></i>
|
||||
</div>
|
||||
<div>
|
||||
<h4 class="alert-title">Consejo del Sistema</h4>
|
||||
<p class="alert-text">Tienes disponibles solo los catálogos obligatorios. Puedes activar catálogos adicionales desde tu panel de preferencias para acceder a más funcionalidades.</p>
|
||||
<button class="btn-card info" onclick="navigateWithAnimation(this, '/IMPORTADORES/preferencias/catalogos')">
|
||||
Configurar Catálogos
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php else: ?>
|
||||
<!-- Si los catálogos están completamente desactivados -->
|
||||
<div class="row g-4">
|
||||
<div class="col-12">
|
||||
<div class="alert alert-warning alert-animated">
|
||||
<div class="d-flex align-items-center">
|
||||
<div class="me-3">
|
||||
<i class="fas fa-lock fa-2x text-warning animate__animated animate__swing animate__infinite"></i>
|
||||
</div>
|
||||
<div class="flex-grow-1">
|
||||
<h5 class="alert-heading">🔒 Catálogos Desactivados</h5>
|
||||
<p class="mb-3">Los catálogos están desactivados en tus preferencias. Solo tienes acceso a las funciones básicas del sistema.</p>
|
||||
<button class="btn btn-primary btn-sm btn-pulse" onclick="navigateWithAnimation(this, '/IMPORTADORES/preferencias/catalogos')">
|
||||
<span class="loading-spinner spinner-border spinner-border-sm me-2" role="status" aria-hidden="true"></span>
|
||||
<span class="btn-text">Activar Catálogos</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Warning Alert -->
|
||||
<div class="alert-professional warning mb-4">
|
||||
<div class="d-flex align-items-start">
|
||||
<div class="alert-icon warning">
|
||||
<i class="fas fa-exclamation-triangle"></i>
|
||||
</div>
|
||||
<div>
|
||||
<h4 class="alert-title">Catálogos Desactivados</h4>
|
||||
<p class="alert-text">Los catálogos están desactivados en tu configuración. Solo tienes acceso a las funciones básicas del sistema.</p>
|
||||
<button class="btn-card warning" onclick="navigateWithAnimation(this, '/IMPORTADORES/preferencias/catalogos')">
|
||||
Activar Catálogos
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Mostrar solo catálogos obligatorios -->
|
||||
<?php
|
||||
$delay = 0;
|
||||
foreach ($catalogosData['catalogos'] as $catalogo):
|
||||
$delay += 0.1;
|
||||
?>
|
||||
<div class="col-md-4 animate__animated animate__fadeInUp" style="animation-delay: <?= $delay ?>s;">
|
||||
<div class="card shadow-sm p-3 card-hover border-warning border-warning-animated h-100">
|
||||
<div class="card-body p-0 d-flex flex-column">
|
||||
<h5 class="text-<?= $catalogo['color'] ?> mb-3">
|
||||
<i class="<?= obtenerIconoCatalogo($catalogo['nombre']) ?> me-2"></i>
|
||||
<?= $catalogo['nombre'] ?>
|
||||
</h5>
|
||||
<p class="text-muted small flex-grow-1 mb-4"><?= $catalogo['descripcion'] ?></p>
|
||||
<div class="mt-auto">
|
||||
<button
|
||||
class="btn btn-<?= $catalogo['color'] ?> btn-sm mt-auto w-100 btn-animated <?= $_SERVER['REQUEST_URI'] === $catalogo['ruta'] ? 'active' : '' ?>"
|
||||
onclick="navigateWithAnimation(this, '<?= $catalogo['ruta'] ?>')"
|
||||
data-url="<?= $catalogo['ruta'] ?>">
|
||||
<span class="loading-spinner spinner-border spinner-border-sm me-2" role="status" aria-hidden="true"></span>
|
||||
<span class="btn-text"><?= $catalogo['accion'] ?></span>
|
||||
</button>
|
||||
</div>
|
||||
<small class="text-warning mt-2 d-flex align-items-center">
|
||||
<i class="fas fa-lock me-1 animate__animated animate__pulse animate__infinite"></i>
|
||||
Función básica
|
||||
</small>
|
||||
</div>
|
||||
|
||||
<!-- Basic Functions Grid -->
|
||||
<div class="row g-4">
|
||||
<?php foreach ($catalogosData['catalogos'] as $catalogo): ?>
|
||||
<div class="col-lg-4 col-md-6 fade-in">
|
||||
<div class="dashboard-card" onclick="navigateWithAnimation(this, '<?= $catalogo['ruta'] ?>')">
|
||||
<div class="badge-required">Básico</div>
|
||||
|
||||
<div class="card-icon <?= getCardColorClass($catalogo['color']) ?>">
|
||||
<i class="<?= obtenerIconoCatalogo($catalogo['nombre']) ?>"></i>
|
||||
</div>
|
||||
|
||||
<h3 class="card-title"><?= $catalogo['nombre'] ?></h3>
|
||||
<p class="card-description"><?= $catalogo['descripcion'] ?></p>
|
||||
|
||||
<div class="card-action">
|
||||
<button class="btn-card <?= $catalogo['color'] ?>">
|
||||
<?= $catalogo['accion'] ?>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -217,79 +365,74 @@
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<script>
|
||||
// Función para navegación con animación
|
||||
function navigateWithAnimation(button, url) {
|
||||
// Prevenir múltiples clics
|
||||
// Navigation function with professional loading animation
|
||||
function navigateWithAnimation(element, url) {
|
||||
// Find the button within the card
|
||||
const button = element.querySelector('.btn-card') || element;
|
||||
|
||||
// Prevent double clicks
|
||||
if (button.classList.contains('btn-loading')) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Añadir estado de carga
|
||||
// Add loading state
|
||||
button.classList.add('btn-loading');
|
||||
button.disabled = true;
|
||||
|
||||
// Añadir efecto de salida a la tarjeta padre
|
||||
const card = button.closest('.card');
|
||||
if (card) {
|
||||
card.style.transform = 'scale(0.95)';
|
||||
card.style.opacity = '0.7';
|
||||
card.style.transition = 'all 0.3s ease';
|
||||
}
|
||||
|
||||
// Simular carga y navegar
|
||||
setTimeout(function() {
|
||||
// Add subtle card animation
|
||||
const card = element.closest('.dashboard-card') || element;
|
||||
card.style.transform = 'scale(0.98)';
|
||||
card.style.opacity = '0.8';
|
||||
|
||||
// Navigate after short delay
|
||||
setTimeout(() => {
|
||||
window.location.href = url;
|
||||
}, 1000);
|
||||
}, 600);
|
||||
}
|
||||
|
||||
// Animación de entrada escalonada para las tarjetas
|
||||
|
||||
// Initialize page animations
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const cards = document.querySelectorAll('.card-hover');
|
||||
|
||||
// Efecto de revelado progresivo
|
||||
cards.forEach((card, index) => {
|
||||
card.style.animationDelay = (index * 0.1) + 's';
|
||||
// Add staggered fade-in animation to cards
|
||||
const fadeElements = document.querySelectorAll('.fade-in');
|
||||
fadeElements.forEach((element, index) => {
|
||||
element.style.animationDelay = (index * 0.1) + 's';
|
||||
});
|
||||
|
||||
// Añadir efecto de hover mejorado
|
||||
|
||||
// Add smooth hover effects to cards
|
||||
const cards = document.querySelectorAll('.dashboard-card');
|
||||
cards.forEach(card => {
|
||||
card.addEventListener('mouseenter', function() {
|
||||
this.style.transform = 'translateY(-5px) scale(1.02)';
|
||||
this.style.transform = 'translateY(-4px)';
|
||||
});
|
||||
|
||||
card.addEventListener('mouseleave', function() {
|
||||
this.style.transform = 'translateY(0) scale(1)';
|
||||
this.style.transform = 'translateY(0)';
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// Efecto de ondas en los botones
|
||||
document.querySelectorAll('.btn-animated').forEach(button => {
|
||||
button.addEventListener('click', function(e) {
|
||||
let ripple = document.createElement('span');
|
||||
ripple.classList.add('ripple');
|
||||
this.appendChild(ripple);
|
||||
|
||||
let x = e.clientX - e.target.offsetLeft;
|
||||
let y = e.clientY - e.target.offsetTop;
|
||||
|
||||
ripple.style.left = x + 'px';
|
||||
ripple.style.top = y + 'px';
|
||||
|
||||
setTimeout(() => {
|
||||
ripple.remove();
|
||||
}, 1000);
|
||||
});
|
||||
});
|
||||
|
||||
// Animación para alertas que se pueden cerrar
|
||||
document.querySelectorAll('.alert').forEach(alert => {
|
||||
alert.addEventListener('closed.bs.alert', function() {
|
||||
this.style.animation = 'slideOutUp 0.5s ease-in';
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
<?php
|
||||
// Helper functions for the dashboard
|
||||
function getCardColorClass($color) {
|
||||
$colorMap = [
|
||||
'indigo' => 'primary',
|
||||
'teal' => 'success',
|
||||
'orange' => 'warning',
|
||||
'cyan' => 'info',
|
||||
'lime' => 'success',
|
||||
'violet' => 'primary',
|
||||
'lila' => 'primary'
|
||||
];
|
||||
return $colorMap[$color] ?? 'primary';
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
?>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
2948
views/mve/lista.php
2948
views/mve/lista.php
File diff suppressed because it is too large
Load Diff
@@ -1,91 +1,618 @@
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet">
|
||||
|
||||
<style>
|
||||
/* Estilos inspirados en Tailwind */
|
||||
.sidebar-container { background-color: #f9fafb; border-right: 1px solid #e5e7eb; }
|
||||
.sidebar-dark { background-color: #1f2937; border-right: 1px solid #374151; }
|
||||
.sidebar-nav .nav-link { color: #374151; font-weight: 500; padding: 0.5rem 0.75rem; margin-bottom: 0.125rem;
|
||||
border-radius: 0.5rem; transition: all 0.15s ease-in-out; display: flex; align-items: center; }
|
||||
.sidebar-nav .nav-link:hover { background-color: #f3f4f6; color: #111827; }
|
||||
.sidebar-nav .nav-link.active { background-color: #f3f4f6; color: #111827; }
|
||||
.sidebar-nav .nav-link .icon { width: 1.25rem; height: 1.25rem; margin-right: 0.75rem; color: #6b7280; transition: color 0.15s ease-in-out; flex-shrink: 0; }
|
||||
.sidebar-nav .nav-link:hover .icon,
|
||||
.sidebar-nav .nav-link.active .icon { color: #374151; }
|
||||
.sidebar-nav .badge { background-color: #f3f4f6; color: #374151; font-size: 0.75rem; font-weight: 500; padding: 0.25rem 0.5rem; border-radius: 9999px; margin-left: auto; }
|
||||
.sidebar-nav .badge.badge-primary { background-color: #dbeafe; color: #1e40af; }
|
||||
.submenu .nav-link { padding-left: 2.75rem; color: #6b7280; font-weight: 400; }
|
||||
.submenu .nav-link:hover { background-color: #f3f4f6; color: #374151; }
|
||||
.submenu .nav-link.active { background-color: #f3f4f6; color: #374151; }
|
||||
.collapse-icon { width: 0.75rem; height: 0.75rem; margin-left: auto; transition: transform 0.15s ease-in-out; }
|
||||
.nav-link[aria-expanded="true"] .collapse-icon { transform: rotate(180deg); }
|
||||
/* Estilos para modo oscuro */
|
||||
.sidebar-dark .sidebar-nav .nav-link { color: #d1d5db; }
|
||||
.sidebar-dark .sidebar-nav .nav-link:hover { background-color: #374151; color: #ffffff; }
|
||||
.sidebar-dark .sidebar-nav .nav-link.active { background-color: #374151; color: #ffffff; }
|
||||
.sidebar-dark .sidebar-nav .nav-link .icon { color: #9ca3af; }
|
||||
.sidebar-dark .sidebar-nav .nav-link:hover .icon,
|
||||
.sidebar-dark .sidebar-nav .nav-link.active .icon { color: #ffffff; }
|
||||
.sidebar-dark .sidebar-nav .badge { background-color: #374151; color: #d1d5db; }
|
||||
.sidebar-dark .sidebar-nav .badge.badge-primary { background-color: #1e3a8a; color: #93c5fd; }
|
||||
.sidebar-dark .submenu .nav-link { color: #9ca3af; }
|
||||
.sidebar-dark .submenu .nav-link:hover { background-color: #374151; color: #d1d5db; }
|
||||
.sidebar-dark .submenu .nav-link.active { background-color: #374151; color: #d1d5db; }
|
||||
/* Navbar */
|
||||
.navbar-brand { font-weight: 600; font-size: 1.125rem; }
|
||||
.menu-toggle { border: 1px solid #6b7280; border-radius: 0.5rem; padding: 0.5rem; transition: all 0.15s ease-in-out; }
|
||||
.menu-toggle:hover { background-color: #f3f4f6; }
|
||||
/* Offcanvas para móviles */
|
||||
.offcanvas { background-color: #ffffff; }
|
||||
.offcanvas-header { border-bottom: 1px solid #e5e7eb; padding: 1rem 1.5rem; }
|
||||
.offcanvas-title { font-weight: 600; font-size: 1.125rem; color: #111827; }
|
||||
.offcanvas-body { padding: 1rem 1.5rem; }
|
||||
/* Responsive adjustments */
|
||||
/* Variables CSS para un diseño profesional limpio */
|
||||
:root {
|
||||
--sidebar-bg: #ffffff;
|
||||
--sidebar-width: 280px;
|
||||
--navbar-height: 70px;
|
||||
--primary-color: #3b82f6;
|
||||
--primary-dark: #2563eb;
|
||||
--text-primary: #1f2937;
|
||||
--text-secondary: #6b7280;
|
||||
--text-muted: #9ca3af;
|
||||
--text-light: #ffffff;
|
||||
--bg-hover: #f8fafc;
|
||||
--bg-active: #f1f5f9;
|
||||
--bg-selected: #eff6ff;
|
||||
--border-color: #e5e7eb;
|
||||
--border-light: #f3f4f6;
|
||||
--shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.05);
|
||||
--shadow-md: 0 4px 6px rgba(0, 0, 0, 0.07);
|
||||
--shadow-lg: 0 10px 25px rgba(0, 0, 0, 0.08);
|
||||
--transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
--navbar-bg: linear-gradient(135deg, var(--primary, #0f172a) 0%, var(--secondary, #334155) 100%);
|
||||
}
|
||||
|
||||
/* Reset y fuentes base */
|
||||
* {
|
||||
font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
|
||||
}
|
||||
|
||||
/* Asegurar espacio para navbar fijo */
|
||||
body {
|
||||
padding-top: var(--navbar-height) !important;
|
||||
margin-top: 0 !important;
|
||||
}
|
||||
|
||||
/* Prevenir que otros estilos interfieran con el navbar */
|
||||
html, body {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
/* Reset específico para el navbar */
|
||||
.navbar-professional * {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
/* Asegurar que ningún elemento padre afecte el posicionamiento */
|
||||
.navbar-professional {
|
||||
transform: none !important;
|
||||
will-change: auto !important;
|
||||
}
|
||||
|
||||
/* Estilos del contenedor principal del sidebar */
|
||||
.sidebar-container {
|
||||
background: var(--sidebar-bg);
|
||||
border-right: 1px solid var(--border-color);
|
||||
box-shadow: var(--shadow-lg);
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.sidebar-container::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: linear-gradient(180deg, transparent 0%, rgba(0, 0, 0, 0.01) 100%);
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
/* Estilos de navegación mejorados */
|
||||
.sidebar-nav {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.sidebar-nav .nav-item {
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.sidebar-nav .nav-link {
|
||||
color: var(--text-primary);
|
||||
font-weight: 500;
|
||||
font-size: 0.95rem;
|
||||
padding: 14px 20px;
|
||||
border-radius: 12px;
|
||||
transition: var(--transition);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
text-decoration: none;
|
||||
position: relative;
|
||||
margin: 0 12px;
|
||||
border: 1px solid transparent;
|
||||
}
|
||||
|
||||
.sidebar-nav .nav-link::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
width: 4px;
|
||||
height: 0;
|
||||
background: var(--primary-color);
|
||||
border-radius: 2px;
|
||||
transition: height 0.3s ease;
|
||||
}
|
||||
|
||||
.sidebar-nav .nav-link:hover {
|
||||
background: var(--bg-hover);
|
||||
border-color: var(--border-light);
|
||||
transform: translateY(-1px);
|
||||
box-shadow: var(--shadow-sm);
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.sidebar-nav .nav-link:hover::before {
|
||||
height: 24px;
|
||||
}
|
||||
|
||||
.sidebar-nav .nav-link.active {
|
||||
background: var(--bg-selected);
|
||||
border-color: var(--primary-color);
|
||||
border-color: rgba(59, 130, 246, 0.2);
|
||||
box-shadow: var(--shadow-sm);
|
||||
font-weight: 600;
|
||||
color: var(--primary-color);
|
||||
}
|
||||
|
||||
.sidebar-nav .nav-link.active::before {
|
||||
height: 28px;
|
||||
}
|
||||
|
||||
/* Iconos mejorados */
|
||||
.sidebar-nav .nav-link .icon {
|
||||
width: 22px;
|
||||
height: 22px;
|
||||
margin-right: 16px;
|
||||
color: var(--text-secondary);
|
||||
transition: var(--transition);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.sidebar-nav .nav-link:hover .icon {
|
||||
color: var(--text-primary);
|
||||
transform: scale(1.05);
|
||||
}
|
||||
|
||||
.sidebar-nav .nav-link.active .icon {
|
||||
color: var(--primary-color);
|
||||
transform: scale(1.05);
|
||||
}
|
||||
|
||||
/* Badges modernos */
|
||||
.sidebar-nav .badge {
|
||||
background: var(--border-light);
|
||||
color: var(--text-secondary);
|
||||
font-size: 0.75rem;
|
||||
font-weight: 600;
|
||||
padding: 6px 10px;
|
||||
border-radius: 20px;
|
||||
margin-left: auto;
|
||||
border: 1px solid var(--border-color);
|
||||
}
|
||||
|
||||
.sidebar-nav .badge.badge-primary {
|
||||
background: rgba(59, 130, 246, 0.1);
|
||||
color: var(--primary-color);
|
||||
border-color: rgba(59, 130, 246, 0.2);
|
||||
}
|
||||
|
||||
/* Submenús mejorados */
|
||||
.submenu {
|
||||
padding-left: 0;
|
||||
margin-top: 8px;
|
||||
border-left: 2px solid var(--border-color);
|
||||
margin-left: 32px;
|
||||
}
|
||||
|
||||
.submenu .nav-link {
|
||||
padding: 10px 20px 10px 24px;
|
||||
font-size: 0.875rem;
|
||||
font-weight: 400;
|
||||
color: var(--text-secondary);
|
||||
margin: 0 12px 0 0;
|
||||
border-radius: 0 10px 10px 0;
|
||||
}
|
||||
|
||||
.submenu .nav-link::before {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.submenu .nav-link:hover {
|
||||
color: var(--text-primary);
|
||||
background: var(--bg-hover);
|
||||
}
|
||||
|
||||
.submenu .nav-link.active {
|
||||
color: var(--primary-color);
|
||||
background: var(--bg-selected);
|
||||
}
|
||||
|
||||
/* Iconos de colapso mejorados */
|
||||
.collapse-icon {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
margin-left: auto;
|
||||
transition: transform 0.4s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
color: var(--text-muted);
|
||||
}
|
||||
|
||||
.nav-link:hover .collapse-icon {
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
.nav-link[aria-expanded="true"] .collapse-icon {
|
||||
transform: rotate(180deg);
|
||||
color: var(--primary-color);
|
||||
}
|
||||
|
||||
/* Navbar profesional con posicionamiento reforzado */
|
||||
.navbar-professional {
|
||||
background: var(--navbar-bg) !important;
|
||||
height: var(--navbar-height) !important;
|
||||
box-shadow: var(--shadow-md) !important;
|
||||
border: none !important;
|
||||
backdrop-filter: blur(10px);
|
||||
position: fixed !important;
|
||||
top: 0 !important;
|
||||
left: 0 !important;
|
||||
right: 0 !important;
|
||||
width: 100% !important;
|
||||
z-index: 9999 !important;
|
||||
overflow: hidden;
|
||||
margin: 0 !important;
|
||||
padding: 0 !important;
|
||||
}
|
||||
|
||||
/* Forzar que el navbar siempre esté en la parte superior */
|
||||
.navbar-professional.fixed-top {
|
||||
position: fixed !important;
|
||||
top: 0 !important;
|
||||
left: 0 !important;
|
||||
right: 0 !important;
|
||||
z-index: 9999 !important;
|
||||
}
|
||||
|
||||
.navbar-brand {
|
||||
font-weight: 700;
|
||||
font-size: 1.4rem;
|
||||
color: var(--text-light) !important;
|
||||
letter-spacing: -0.5px;
|
||||
margin-bottom: 0 !important;
|
||||
padding: 0 !important;
|
||||
}
|
||||
|
||||
.navbar-brand .highlight {
|
||||
color: #ffd700;
|
||||
font-weight: 300;
|
||||
}
|
||||
|
||||
/* Contenedor del navbar reforzado */
|
||||
.navbar-professional .container-fluid {
|
||||
padding-left: 1rem !important;
|
||||
padding-right: 1rem !important;
|
||||
margin: 0 !important;
|
||||
width: 100% !important;
|
||||
max-width: none !important;
|
||||
}
|
||||
|
||||
/* Botón de menú mejorado */
|
||||
.menu-toggle {
|
||||
border: 2px solid rgba(255, 255, 255, 0.3);
|
||||
border-radius: 10px;
|
||||
padding: 10px 12px;
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
color: var(--text-light);
|
||||
font-size: 1.2rem;
|
||||
transition: var(--transition);
|
||||
backdrop-filter: blur(5px);
|
||||
}
|
||||
|
||||
.menu-toggle:hover {
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
border-color: rgba(255, 255, 255, 0.5);
|
||||
transform: scale(1.05);
|
||||
}
|
||||
|
||||
/* Botones de acción mejorados */
|
||||
.btn-professional {
|
||||
padding: 10px 20px;
|
||||
border-radius: 10px;
|
||||
font-weight: 500;
|
||||
font-size: 0.9rem;
|
||||
transition: var(--transition);
|
||||
border: 2px solid transparent;
|
||||
}
|
||||
|
||||
.btn-outline-light-professional {
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
border-color: rgba(255, 255, 255, 0.3);
|
||||
color: var(--text-light);
|
||||
backdrop-filter: blur(5px);
|
||||
}
|
||||
|
||||
.btn-outline-light-professional:hover {
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
border-color: rgba(255, 255, 255, 0.5);
|
||||
color: var(--text-light);
|
||||
transform: translateY(-2px);
|
||||
box-shadow: var(--shadow-md);
|
||||
}
|
||||
|
||||
/* Estilos para el contenido de bienvenida */
|
||||
.welcome-container {
|
||||
flex-shrink: 0;
|
||||
display: flex !important;
|
||||
align-items: center;
|
||||
height: 100%;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.welcome-text {
|
||||
font-size: 0.95rem;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
max-width: 200px;
|
||||
color: white !important;
|
||||
margin: 0;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
#navbar-content {
|
||||
display: flex !important;
|
||||
align-items: center;
|
||||
height: 100%;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
/* Offcanvas mejorado */
|
||||
.offcanvas-professional {
|
||||
background: var(--sidebar-bg);
|
||||
border: none;
|
||||
border-left: 1px solid var(--border-color);
|
||||
}
|
||||
|
||||
.offcanvas-header {
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
padding: 1.5rem;
|
||||
background: var(--bg-hover);
|
||||
}
|
||||
|
||||
.offcanvas-title {
|
||||
font-weight: 700;
|
||||
font-size: 1.3rem;
|
||||
color: var(--text-primary);
|
||||
letter-spacing: -0.3px;
|
||||
}
|
||||
|
||||
.btn-close-white-professional {
|
||||
background: var(--border-light);
|
||||
border-radius: 8px;
|
||||
padding: 8px;
|
||||
transition: var(--transition);
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.btn-close-white-professional:hover {
|
||||
background: var(--border-color);
|
||||
transform: scale(1.1);
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.offcanvas-body {
|
||||
padding: 1.5rem;
|
||||
}
|
||||
|
||||
/* Efectos de scroll personalizado */
|
||||
.sidebar-scroll::-webkit-scrollbar {
|
||||
width: 6px;
|
||||
}
|
||||
|
||||
.sidebar-scroll::-webkit-scrollbar-track {
|
||||
background: var(--border-light);
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.sidebar-scroll::-webkit-scrollbar-thumb {
|
||||
background: var(--border-color);
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.sidebar-scroll::-webkit-scrollbar-thumb:hover {
|
||||
background: var(--text-muted);
|
||||
}
|
||||
|
||||
/* Separadores visuales */
|
||||
.nav-separator {
|
||||
height: 1px;
|
||||
background: var(--border-color);
|
||||
margin: 16px 20px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.nav-separator::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
width: 40px;
|
||||
height: 1px;
|
||||
background: var(--text-muted);
|
||||
opacity: 0.3;
|
||||
}
|
||||
|
||||
/* Animaciones mejoradas */
|
||||
@keyframes slideIn {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateX(-20px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateX(0);
|
||||
}
|
||||
}
|
||||
|
||||
.nav-item {
|
||||
animation: slideIn 0.3s ease-out;
|
||||
}
|
||||
|
||||
.nav-item:nth-child(1) { animation-delay: 0.05s; }
|
||||
.nav-item:nth-child(2) { animation-delay: 0.1s; }
|
||||
.nav-item:nth-child(3) { animation-delay: 0.15s; }
|
||||
.nav-item:nth-child(4) { animation-delay: 0.2s; }
|
||||
.nav-item:nth-child(5) { animation-delay: 0.25s; }
|
||||
.nav-item:nth-child(6) { animation-delay: 0.3s; }
|
||||
.nav-item:nth-child(7) { animation-delay: 0.35s; }
|
||||
.nav-item:nth-child(8) { animation-delay: 0.4s; }
|
||||
.nav-item:nth-child(9) { animation-delay: 0.45s; }
|
||||
.nav-item:nth-child(10) { animation-delay: 0.5s; }
|
||||
|
||||
/* Responsive mejorado */
|
||||
@media (max-width: 767.98px) {
|
||||
.sidebar-nav .nav-link { color: #374151; }
|
||||
.sidebar-nav .nav-link:hover { background-color: #f3f4f6; color: #111827; }
|
||||
.sidebar-nav .nav-link.active { background-color: #f3f4f6; color: #111827; }
|
||||
:root {
|
||||
--navbar-height: 60px;
|
||||
}
|
||||
|
||||
.navbar-professional {
|
||||
height: 60px;
|
||||
padding-left: 1rem;
|
||||
padding-right: 1rem;
|
||||
}
|
||||
|
||||
.navbar-professional .container-fluid {
|
||||
padding-left: 0.5rem;
|
||||
padding-right: 0.5rem;
|
||||
}
|
||||
|
||||
.navbar-brand {
|
||||
font-size: 1.0rem;
|
||||
flex-shrink: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.navbar-brand span:first-child {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.menu-toggle {
|
||||
padding: 6px 8px;
|
||||
margin-right: 0.5rem;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.welcome-text {
|
||||
font-size: 0.85rem;
|
||||
max-width: 150px;
|
||||
}
|
||||
|
||||
.welcome-container {
|
||||
flex-shrink: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.sidebar-nav .nav-link {
|
||||
margin: 0 8px;
|
||||
padding: 12px 16px;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.offcanvas-professional {
|
||||
width: 280px !important;
|
||||
}
|
||||
|
||||
#navbar-content {
|
||||
flex-shrink: 1;
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
|
||||
/* Para pantallas muy pequeñas */
|
||||
@media (max-width: 575.98px) {
|
||||
.navbar-brand {
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.welcome-text {
|
||||
font-size: 0.8rem;
|
||||
max-width: 120px;
|
||||
}
|
||||
|
||||
.navbar-professional .container-fluid {
|
||||
padding-left: 0.25rem;
|
||||
padding-right: 0.25rem;
|
||||
}
|
||||
}
|
||||
|
||||
/* Estados especiales */
|
||||
.nav-link.loading {
|
||||
opacity: 0.6;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.nav-link.loading .icon {
|
||||
animation: spin 1s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
from { transform: rotate(0deg); }
|
||||
to { transform: rotate(360deg); }
|
||||
}
|
||||
</style>
|
||||
|
||||
<!-- NAVBAR -->
|
||||
<nav class="navbar navbar-dark bg-dark fixed-top">
|
||||
<div class="container-fluid">
|
||||
<!-- Botón de menú para móviles -->
|
||||
<button class="menu-toggle d-md-none me-3" 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">
|
||||
<div id="navbar-content">
|
||||
<!-- NAVBAR PROFESIONAL -->
|
||||
<nav class="navbar navbar-professional fixed-top navbar-dark">
|
||||
<div class="container-fluid px-4 h-100">
|
||||
<div class="d-flex align-items-center justify-content-between w-100 h-100">
|
||||
<!-- Lado izquierdo: Menú + Brand -->
|
||||
<div class="d-flex align-items-center h-100">
|
||||
<!-- Botón de menú para móviles -->
|
||||
<button class="menu-toggle d-md-none me-3" type="button" data-bs-toggle="offcanvas" data-bs-target="#sidebarMenu">
|
||||
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<line x1="3" y1="6" x2="21" y2="6"></line>
|
||||
<line x1="3" y1="12" x2="21" y2="12"></line>
|
||||
<line x1="3" y1="18" x2="21" y2="18"></line>
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
<!-- Brand mejorado -->
|
||||
<div class="navbar-brand d-flex align-items-center mb-0">
|
||||
<svg class="me-2" width="32" height="32" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M12 2L2 7l10 5 10-5-10-5zM2 17l10 5 10-5M2 12l10 5 10-5"/>
|
||||
</svg>
|
||||
<span>SIIH <span class="highlight">|</span> Administrador</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Lado derecho: Contenido del navbar -->
|
||||
<div class="d-flex align-items-center h-100">
|
||||
<?php
|
||||
// Verifica si estás en el dashboard principal del administrador
|
||||
$esDashboard = str_contains($_SERVER['REQUEST_URI'], '/administrador/dashboard');
|
||||
|
||||
if ($esDashboard): ?>
|
||||
<span id="welcome-text">Bienvenido, <?= htmlspecialchars($_SESSION['usuario_nombre']) ?></span>
|
||||
<div class="welcome-container">
|
||||
<svg class="me-2 d-none d-sm-block" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"></path>
|
||||
<circle cx="12" cy="7" r="4"></circle>
|
||||
</svg>
|
||||
<span id="welcome-text" class="welcome-text">Bienvenido, <?= htmlspecialchars($_SESSION['usuario_nombre']) ?></span>
|
||||
</div>
|
||||
<?php else: ?>
|
||||
<a href="/IMPORTADORES/sistemas/logout" class="btn btn-outline-light btn-sm ms-2 W-auto btn-animated" id="logout-btn">Cerrar Sesión</a>
|
||||
<a href="/IMPORTADORES/sistemas/logout"
|
||||
class="btn btn-outline-light-professional btn-professional d-flex align-items-center"
|
||||
id="logout-btn">
|
||||
<svg class="me-2" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4"></path>
|
||||
<polyline points="16,17 21,12 16,7"></polyline>
|
||||
<line x1="21" y1="12" x2="9" y2="12"></line>
|
||||
</svg>
|
||||
Cerrar Sesión
|
||||
</a>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<!-- SIDEBAR FIJA (escritorio) -->
|
||||
<div class="d-none d-md-block sidebar-container sidebar-dark position-fixed h-100" style="width: 250px; top: 0; padding-top: 4rem; z-index: 1000;">
|
||||
<!-- SIDEBAR PROFESIONAL (escritorio) -->
|
||||
<div class="d-none d-md-block sidebar-container position-fixed h-100 sidebar-scroll" style="width: var(--sidebar-width); top: 0; padding-top: var(--navbar-height); z-index: 1000;">
|
||||
<div class="px-3 py-4 h-100 overflow-y-auto">
|
||||
<nav class="sidebar-nav">
|
||||
<ul class="nav flex-column">
|
||||
<!-- INICIO -->
|
||||
<li class="nav-item">
|
||||
<a href="/IMPORTADORES/administrador/dashboard" class="nav-link active">
|
||||
<svg class="icon" fill="currentColor" viewBox="0 0 22 21">
|
||||
<path d="M16.975 11H10V4.025a1 1 0 0 0-1.066-.998 8.5 8.5 0 1 0 9.039 9.039.999.999 0 0 0-1-1.066h.002Z"/>
|
||||
<path d="M12.5 0c-.157 0-.311.01-.565.027A1 1 0 0 0 11 1.02V10h8.975a1 1 0 0 0 1-.935c.013-.188.028-.374.028-.565A8.51 8.51 0 0 0 12.5 0Z"/>
|
||||
<svg class="icon" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24">
|
||||
<path d="M3 9l9-7 9 7v11a2 2 0 01-2 2H5a2 2 0 01-2-2z"/>
|
||||
<polyline points="9,22 9,12 15,12 15,22"/>
|
||||
</svg>
|
||||
<span>Inicio</span>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<div class="nav-separator"></div>
|
||||
<!-- AGENCIAS -->
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" data-bs-toggle="collapse" href="#submenuAgencias" role="button" aria-expanded="false">
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
386
views/partials/sidebar_agencia_old.php
Normal file
386
views/partials/sidebar_agencia_old.php
Normal file
@@ -0,0 +1,386 @@
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
|
||||
<style>
|
||||
/* Estilos inspirados en Tailwind */
|
||||
.sidebar-container { background-color: #f9fafb; border-right: 1px solid #e5e7eb; }
|
||||
.sidebar-dark { background-color: #1f2937; border-right: 1px solid #374151; }
|
||||
.sidebar-nav .nav-link { color: #374151; font-weight: 500; padding: 0.5rem 0.75rem; margin-bottom: 0.125rem;
|
||||
border-radius: 0.5rem; transition: all 0.15s ease-in-out; display: flex; align-items: center; }
|
||||
.sidebar-nav .nav-link:hover { background-color: #f3f4f6; color: #111827; }
|
||||
.sidebar-nav .nav-link.active { background-color: #f3f4f6; color: #111827; }
|
||||
.sidebar-nav .nav-link .icon { width: 1.25rem; height: 1.25rem; margin-right: 0.75rem; color: #6b7280; transition: color 0.15s ease-in-out; flex-shrink: 0; }
|
||||
.sidebar-nav .nav-link:hover .icon,
|
||||
.sidebar-nav .nav-link.active .icon { color: #374151; }
|
||||
.sidebar-nav .badge { background-color: #f3f4f6; color: #374151; font-size: 0.75rem; font-weight: 500; padding: 0.25rem 0.5rem; border-radius: 9999px; margin-left: auto; }
|
||||
.sidebar-nav .badge.badge-primary { background-color: #dbeafe; color: #1e40af; }
|
||||
.submenu .nav-link { padding-left: 2.75rem; color: #6b7280; font-weight: 400; }
|
||||
.submenu .nav-link:hover { background-color: #f3f4f6; color: #374151; }
|
||||
.submenu .nav-link.active { background-color: #f3f4f6; color: #374151; }
|
||||
.collapse-icon { width: 0.75rem; height: 0.75rem; margin-left: auto; transition: transform 0.15s ease-in-out; }
|
||||
.nav-link[aria-expanded="true"] .collapse-icon { transform: rotate(180deg); }
|
||||
/* Estilos para modo oscuro */
|
||||
.sidebar-dark .sidebar-nav .nav-link { color: #d1d5db; }
|
||||
.sidebar-dark .sidebar-nav .nav-link:hover { background-color: #374151; color: #ffffff; }
|
||||
.sidebar-dark .sidebar-nav .nav-link.active { background-color: #374151; color: #ffffff; }
|
||||
.sidebar-dark .sidebar-nav .nav-link .icon { color: #9ca3af; }
|
||||
.sidebar-dark .sidebar-nav .nav-link:hover .icon,
|
||||
.sidebar-dark .sidebar-nav .nav-link.active .icon { color: #ffffff; }
|
||||
.sidebar-dark .sidebar-nav .badge { background-color: #374151; color: #d1d5db; }
|
||||
.sidebar-dark .sidebar-nav .badge.badge-primary { background-color: #1e3a8a; color: #93c5fd; }
|
||||
.sidebar-dark .submenu .nav-link { color: #9ca3af; }
|
||||
.sidebar-dark .submenu .nav-link:hover { background-color: #374151; color: #d1d5db; }
|
||||
.sidebar-dark .submenu .nav-link.active { background-color: #374151; color: #d1d5db; }
|
||||
/* Navbar */
|
||||
.navbar-brand { font-weight: 600; font-size: 1.125rem; }
|
||||
.menu-toggle { border: 1px solid #6b7280; border-radius: 0.5rem; padding: 0.5rem; transition: all 0.15s ease-in-out; }
|
||||
.menu-toggle:hover { background-color: #f3f4f6; }
|
||||
/* Offcanvas para móviles */
|
||||
.offcanvas { background-color: #ffffff; }
|
||||
.offcanvas-header { border-bottom: 1px solid #e5e7eb; padding: 1rem 1.5rem; }
|
||||
.offcanvas-title { font-weight: 600; font-size: 1.125rem; color: #111827; }
|
||||
.offcanvas-body { padding: 1rem 1.5rem; }
|
||||
/* Responsive adjustments */
|
||||
@media (max-width: 767.98px) {
|
||||
.sidebar-nav .nav-link { color: #374151; }
|
||||
.sidebar-nav .nav-link:hover { background-color: #f3f4f6; color: #111827; }
|
||||
.sidebar-nav .nav-link.active { background-color: #f3f4f6; color: #111827; }
|
||||
}
|
||||
</style>
|
||||
|
||||
<!-- 🔷 NAVBAR -->
|
||||
<nav class="navbar navbar-dark bg-dark fixed-top">
|
||||
<div class="container-fluid">
|
||||
<!-- Botón de menú para móviles -->
|
||||
<button class="menu-toggle d-md-none me-3" type="button" data-bs-toggle="offcanvas" data-bs-target="#sidebarMenu">
|
||||
☰
|
||||
</button>
|
||||
<span class="navbar-brand">SIIH | Agencia Aduanal</span>
|
||||
<div class="d-flex ms-auto text-white">
|
||||
<div class="navbar-content">
|
||||
<?php
|
||||
// Verifica si estás en el dashboard principal del importador
|
||||
$esDashboard = str_contains($_SERVER['REQUEST_URI'], '/IMPORTADORES/agencias/dashboard');
|
||||
|
||||
if ($esDashboard): ?>
|
||||
<span id="welcome-text">Bienvenido, <?= htmlspecialchars($_SESSION['usuario_nombre']) ?></span>
|
||||
<?php else: ?>
|
||||
<a href="/IMPORTADORES/sistemas/logout" class="btn btn-outline-light btn-sm ms-2 W-auto btn-animated">Cerrar Sesión</a>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<!-- SIDEBAR FIJA (escritorio) -->
|
||||
<div class="d-none d-md-block sidebar-container sidebar-dark position-fixed h-100" style="width: 250px; top: 0; padding-top: 4rem; z-index: 1000;">
|
||||
<div class="px-3 py-4 h-100 overflow-y-auto">
|
||||
<nav class="sidebar-nav">
|
||||
<ul class="nav flex-column">
|
||||
<!-- INICIO -->
|
||||
<li class="nav-item">
|
||||
<a href="/IMPORTADORES/agencias/dashboard" class="nav-link active">
|
||||
<svg class="icon" fill="currentColor" viewBox="0 0 22 21">
|
||||
<path d="M16.975 11H10V4.025a1 1 0 0 0-1.066-.998 8.5 8.5 0 1 0 9.039 9.039.999.999 0 0 0-1-1.066h.002Z"/>
|
||||
<path d="M12.5 0c-.157 0-.311.01-.565.027A1 1 0 0 0 11 1.02V10h8.975a1 1 0 0 0 1-.935c.013-.188.028-.374.028-.565A8.51 8.51 0 0 0 12.5 0Z"/>
|
||||
</svg>
|
||||
<span>Inicio</span>
|
||||
</a>
|
||||
</li>
|
||||
<!-- SOLICITUDES VINCULACIÓN -->
|
||||
<li class="nav-item">
|
||||
<a href="/IMPORTADORES/vinculaciones/solicitudesVinculacion" class="nav-link">
|
||||
<svg class="icon" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path d="M12.586 4.586a2 2 0 112.828 2.828l-3 3a2 2 0 01-2.828 0 1 1 0 00-1.414 1.414 4 4 0 005.656 0l3-3a4 4 0 00-5.656-5.656l-1.5 1.5a1 1 0 101.414 1.414l1.5-1.5z"/>
|
||||
<path d="M7.414 15.414a2 2 0 01-2.828-2.828l3-3a2 2 0 012.828 0 1 1 0 001.414-1.414 4 4 0 00-5.656 0l-3 3a4 4 0 105.656 5.656l1.5-1.5a1 1 0 10-1.414-1.414l-1.5 1.5z"/>
|
||||
</svg>
|
||||
<span>Vinculación</span>
|
||||
</a>
|
||||
</li>
|
||||
<!-- USUARIOS VINCULADOS -->
|
||||
<li class="nav-item">
|
||||
<a href="/IMPORTADORES/vinculaciones/vinculacionesAgencia" class="nav-link">
|
||||
<svg class="icon" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path d="M14 2a3.963 3.963 0 0 0-1.4.267 6.439 6.439 0 0 1-1.331 6.638A4 4 0 1 0 14 2Zm1 9h-1.264A6.957 6.957 0 0 1 15 15v2a2.97 2.97 0 0 1-.184 1H19a1 1 0 0 0 1-1v-1a5.006 5.006 0 0 0-5-5ZM6.5 9a4.5 4.5 0 1 0 0-9 4.5 4.5 0 0 0 0 9ZM8 10H5a5.006 5.006 0 0 0-5 5v2a1 1 0 0 0 1 1h11a1 1 0 0 0 1-1v-2a5.006 5.006 0 0 0-5-5Z"/>
|
||||
</svg>
|
||||
<span>Usuarios Vinculados</span>
|
||||
</a>
|
||||
</li>
|
||||
<!-- Patentes -->
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" data-bs-toggle="collapse" href="#submenuPatentesDesktop" role="button" aria-expanded="false" aria-controls="submenuPatentesDesktop">
|
||||
<svg class="icon" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path fill-rule="evenodd" d="M4 4a2 2 0 012-2h4.586A2 2 0 0112 2.586L15.414 6A2 2 0 0116 7.414V16a2 2 0 01-2 2H6a2 2 0 01-2-2V4zm2 6a1 1 0 011-1h6a1 1 0 110 2H7a1 1 0 01-1-1zm1 3a1 1 0 100 2h6a1 1 0 100-2H7z" clip-rule="evenodd"/>
|
||||
</svg>
|
||||
<span>Patentes</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="submenuPatentesDesktop">
|
||||
<ul class="nav flex-column submenu">
|
||||
<li class="nav-item">
|
||||
<a href="/IMPORTADORES/patente/lista" class="nav-link">
|
||||
Ver Patentes
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a href="/IMPORTADORES/patente/alta" class="nav-link">
|
||||
Nueva Patente
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</li>
|
||||
<!-- LOCACIONES -->
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" data-bs-toggle="collapse" href="#submenuLocacionesDesktop" role="button" aria-expanded="false" aria-controls="submenuLocacionesDesktop">
|
||||
<svg class="icon" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path d="M10 2C6.686 2 4 4.686 4 8c0 5.25 6 12 6 12s6-6.75 6-12c0-3.314-2.686-6-6-6zm0 8c-1.105 0-2-.895-2-2s.895-2 2-2 2 .895 2 2-.895 2-2 2z"/>
|
||||
</svg>
|
||||
<span>Locaciones</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="submenuLocacionesDesktop">
|
||||
<ul class="nav flex-column submenu">
|
||||
<li class="nav-item">
|
||||
<a href="/IMPORTADORES/locaciones/lista" class="nav-link">
|
||||
Ver Locaciones
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a href="/IMPORTADORES/locaciones/alta" class="nav-link">
|
||||
Agregar Locaciones
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</li>
|
||||
<!-- ALTA DE AGENTES -->
|
||||
<li class="nav-item">
|
||||
<a href="/IMPORTADORES/agencias/alta" class="nav-link">
|
||||
<svg class="icon" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path fill-rule="evenodd" d="M10 9a3 3 0 100-6 3 3 0 000 6zm-7 9a7 7 0 1114 0H3z" clip-rule="evenodd"/>
|
||||
</svg>
|
||||
<span>Alta de agentes</span>
|
||||
</a>
|
||||
</li>
|
||||
<!-- CONFIGURACIÓN -->
|
||||
<li class="nav-item">
|
||||
<a href="/IMPORTADORES/configuracion" class="nav-link">
|
||||
<svg class="icon" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path fill-rule="evenodd" d="M11.983 1.034a1 1 0 00-1.966 0l-.26 1.857a6.996 6.996 0 00-1.223.51l-1.7-1.04a1 1 0 00-1.366.366l-1 1.732a1 1 0 00.366 1.366l1.7 1.04a6.984 6.984 0 000 1.02l-1.7 1.04a1 1 0 00-.366 1.366l1 1.732a1 1 0 001.366.366l1.7-1.04c.392.212.803.386 1.223.51l.26 1.857a1 1 0 001.966 0l.26-1.857c.42-.124.831-.298 1.223-.51l1.7 1.04a1 1 0 001.366-.366l1-1.732a1 1 0 00-.366-1.366l-1.7-1.04a6.984 6.984 0 000-1.02l1.7-1.04a1 1 0 00.366-1.366l-1-1.732a1 1 0 00-1.366-.366l-1.7 1.04a6.996 6.996 0 00-1.223-.51l-.26-1.857zM10 13a3 3 0 100-6 3 3 0 000 6z" clip-rule="evenodd"/>
|
||||
</svg>
|
||||
<span>Configuración</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- OFFCANVAS PARA MÓVILES -->
|
||||
<div class="offcanvas offcanvas-start d-md-none" tabindex="-1" id="sidebarMenu" style="top: 0; width: 250px;">
|
||||
<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">
|
||||
<nav class="sidebar-nav">
|
||||
<ul class="nav flex-column">
|
||||
<!-- INICIO -->
|
||||
<li class="nav-item">
|
||||
<a href="/IMPORTADORES/agentes/dashboard" class="nav-link active">
|
||||
<svg class="icon" fill="currentColor" viewBox="0 0 22 21">
|
||||
<path d="M16.975 11H10V4.025a1 1 0 0 0-1.066-.998 8.5 8.5 0 1 0 9.039 9.039.999.999 0 0 0-1-1.066h.002Z"/>
|
||||
<path d="M12.5 0c-.157 0-.311.01-.565.027A1 1 0 0 0 11 1.02V10h8.975a1 1 0 0 0 1-.935c.013-.188.028-.374.028-.565A8.51 8.51 0 0 0 12.5 0Z"/>
|
||||
</svg>
|
||||
<span>Inicio</span>
|
||||
</a>
|
||||
</li>
|
||||
<!-- SOLICITUDES VINCULACIÓN -->
|
||||
<li class="nav-item">
|
||||
<a href="/IMPORTADORES/vinculaciones/solicitudesVinculacion" class="nav-link">
|
||||
<svg class="icon" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path d="M12.586 4.586a2 2 0 112.828 2.828l-3 3a2 2 0 01-2.828 0 1 1 0 00-1.414 1.414 4 4 0 005.656 0l3-3a4 4 0 00-5.656-5.656l-1.5 1.5a1 1 0 101.414 1.414l1.5-1.5z"/>
|
||||
<path d="M7.414 15.414a2 2 0 01-2.828-2.828l3-3a2 2 0 012.828 0 1 1 0 001.414-1.414 4 4 0 00-5.656 0l-3 3a4 4 0 105.656 5.656l1.5-1.5a1 1 0 10-1.414-1.414l-1.5 1.5z"/>
|
||||
</svg>
|
||||
<span>Vinculación</span>
|
||||
</a>
|
||||
</li>
|
||||
<!-- USUARIOS VINCULADOS -->
|
||||
<li class="nav-item">
|
||||
<a href="/IMPORTADORES/vinculaciones/vinculacionesAgencia" class="nav-link">
|
||||
<svg class="icon" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path d="M14 2a3.963 3.963 0 0 0-1.4.267 6.439 6.439 0 0 1-1.331 6.638A4 4 0 1 0 14 2Zm1 9h-1.264A6.957 6.957 0 0 1 15 15v2a2.97 2.97 0 0 1-.184 1H19a1 1 0 0 0 1-1v-1a5.006 5.006 0 0 0-5-5ZM6.5 9a4.5 4.5 0 1 0 0-9 4.5 4.5 0 0 0 0 9ZM8 10H5a5.006 5.006 0 0 0-5 5v2a1 1 0 0 0 1 1h11a1 1 0 0 0 1-1v-2a5.006 5.006 0 0 0-5-5Z"/>
|
||||
</svg>
|
||||
<span>Usuarios Vinculados</span>
|
||||
</a>
|
||||
</li>
|
||||
<!-- Patentes -->
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" data-bs-toggle="collapse" href="#submenuPatentesMobile" role="button" aria-expanded="false" aria-controls="submenuPatentesMobile">
|
||||
<svg class="icon" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path fill-rule="evenodd" d="M4 4a2 2 0 012-2h4.586A2 2 0 0112 2.586L15.414 6A2 2 0 0116 7.414V16a2 2 0 01-2 2H6a2 2 0 01-2-2V4zm2 6a1 1 0 011-1h6a1 1 0 110 2H7a1 1 0 01-1-1zm1 3a1 1 0 100 2h6a1 1 0 100-2H7z" clip-rule="evenodd"/>
|
||||
</svg>
|
||||
<span>Patentes</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="submenuPatentesMobile">
|
||||
<ul class="nav flex-column submenu">
|
||||
<li class="nav-item">
|
||||
<a href="/IMPORTADORES/patente/lista" class="nav-link">
|
||||
Ver Patentes
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a href="/IMPORTADORES/patente/alta" class="nav-link">
|
||||
Nueva Patente
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</li>
|
||||
<!-- LOCACIONES -->
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" data-bs-toggle="collapse" href="#submenuLocacionesMobile" role="button" aria-expanded="false" aria-controls="submenuLocacionesMobile">
|
||||
<svg class="icon" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path d="M10 2C6.686 2 4 4.686 4 8c0 5.25 6 12 6 12s6-6.75 6-12c0-3.314-2.686-6-6-6zm0 8c-1.105 0-2-.895-2-2s.895-2 2-2 2 .895 2 2-.895 2-2 2z"/>
|
||||
</svg>
|
||||
<span>Locaciones</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="submenuLocacionesMobile">
|
||||
<ul class="nav flex-column submenu">
|
||||
<li class="nav-item">
|
||||
<a href="/IMPORTADORES/locaciones/lista" class="nav-link">
|
||||
Ver Locaciones
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a href="/IMPORTADORES/locaciones/alta" class="nav-link">
|
||||
Agregar Locaciones
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</li>
|
||||
<!-- ALTA DE AGENTES -->
|
||||
<li class="nav-item">
|
||||
<a href="/IMPORTADORES/agencias/alta" class="nav-link">
|
||||
<svg class="icon" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path fill-rule="evenodd" d="M10 9a3 3 0 100-6 3 3 0 000 6zm-7 9a7 7 0 1114 0H3z" clip-rule="evenodd"/>
|
||||
</svg>
|
||||
<span>Alta de agentes</span>
|
||||
</a>
|
||||
</li>
|
||||
<!-- CONFIGURACIÓN -->
|
||||
<li class="nav-item">
|
||||
<a href="/IMPORTADORES/configuracion" class="nav-link">
|
||||
<svg class="icon" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path fill-rule="evenodd" d="M11.983 1.034a1 1 0 00-1.966 0l-.26 1.857a6.996 6.996 0 00-1.223.51l-1.7-1.04a1 1 0 00-1.366.366l-1 1.732a1 1 0 00.366 1.366l1.7 1.04a6.984 6.984 0 000 1.02l-1.7 1.04a1 1 0 00-.366 1.366l1 1.732a1 1 0 001.366.366l1.7-1.04c.392.212.803.386 1.223.51l.26 1.857a1 1 0 001.966 0l.26-1.857c.42-.124.831-.298 1.223-.51l1.7 1.04a1 1 0 001.366-.366l1-1.732a1 1 0 00-.366-1.366l-1.7-1.04a6.984 6.984 0 000-1.02l1.7-1.04a1 1 0 00.366-1.366l-1-1.732a1 1 0 00-1.366-.366l-1.7 1.04a6.996 6.996 0 00-1.223-.51l-.26-1.857zM10 13a3 3 0 100-6 3 3 0 000 6z" clip-rule="evenodd"/>
|
||||
</svg>
|
||||
<span>Configuración</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<script>
|
||||
// Simular lógica de dashboard
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const esDashboard = window.location.pathname.includes('/agencias/dashboard');
|
||||
const welcomeText = document.getElementById('welcome-text');
|
||||
const logoutBtn = document.getElementById('logout-btn');
|
||||
|
||||
if (!esDashboard) {
|
||||
if (welcomeText) {
|
||||
welcomeText.style.display = 'none';
|
||||
}
|
||||
if (logoutBtn) {
|
||||
logoutBtn.style.display = 'inline-block';
|
||||
}
|
||||
}
|
||||
|
||||
// Manejar estados activos basados en URL actual
|
||||
const currentPath = window.location.pathname;
|
||||
const navLinks = document.querySelectorAll('.nav-link');
|
||||
|
||||
navLinks.forEach(link => {
|
||||
const href = link.getAttribute('href');
|
||||
if (href && currentPath.includes(href)) {
|
||||
link.classList.add('active');
|
||||
}
|
||||
});
|
||||
|
||||
document.querySelectorAll('[data-bs-toggle="collapse"]').forEach(trigger => {
|
||||
// Desactivar Bootstrap
|
||||
trigger.removeAttribute('data-bs-toggle');
|
||||
|
||||
// Inicializar estado correcto
|
||||
const target = document.querySelector(trigger.getAttribute('href'));
|
||||
if (target) {
|
||||
target.style.transition = 'height 0.3s ease, opacity 0.2s ease';
|
||||
target.style.overflow = 'hidden';
|
||||
|
||||
if (!target.classList.contains('show')) {
|
||||
target.style.height = '0';
|
||||
target.style.opacity = '0';
|
||||
}
|
||||
}
|
||||
|
||||
trigger.addEventListener('click', function(e) {
|
||||
e.preventDefault();
|
||||
const target = document.querySelector(this.getAttribute('href'));
|
||||
const icon = this.querySelector('.collapse-icon');
|
||||
|
||||
if (!target) return;
|
||||
|
||||
const isExpanding = this.getAttribute('aria-expanded') !== 'true';
|
||||
|
||||
if (isExpanding) {
|
||||
// Abrir
|
||||
target.classList.add('show');
|
||||
target.style.height = '0';
|
||||
target.style.opacity = '0';
|
||||
target.style.display = 'block';
|
||||
|
||||
// Forzar reflow
|
||||
void target.offsetHeight;
|
||||
|
||||
target.style.height = target.scrollHeight + 'px';
|
||||
target.style.opacity = '1';
|
||||
|
||||
this.setAttribute('aria-expanded', 'true');
|
||||
if (icon) icon.style.transform = 'rotate(180deg)';
|
||||
} else {
|
||||
// Cerrar
|
||||
target.style.height = target.scrollHeight + 'px';
|
||||
target.style.opacity = '1';
|
||||
|
||||
// Forzar reflow
|
||||
void target.offsetHeight;
|
||||
|
||||
target.style.height = '0';
|
||||
target.style.opacity = '0';
|
||||
|
||||
setTimeout(() => {
|
||||
target.classList.remove('show');
|
||||
target.style.display = '';
|
||||
}, 300);
|
||||
|
||||
this.setAttribute('aria-expanded', 'false');
|
||||
if (icon) icon.style.transform = 'rotate(0deg)';
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
</script>
|
||||
File diff suppressed because it is too large
Load Diff
315
views/partials/sidebar_agente_old.php
Normal file
315
views/partials/sidebar_agente_old.php
Normal file
@@ -0,0 +1,315 @@
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
|
||||
<style>
|
||||
/* Estilos inspirados en Tailwind */
|
||||
.sidebar-container { background-color: #f9fafb; border-right: 1px solid #e5e7eb; }
|
||||
.sidebar-dark { background-color: #1f2937; border-right: 1px solid #374151; }
|
||||
.sidebar-nav .nav-link { color: #374151; font-weight: 500; padding: 0.5rem 0.75rem; margin-bottom: 0.125rem;
|
||||
border-radius: 0.5rem; transition: all 0.15s ease-in-out; display: flex; align-items: center; }
|
||||
.sidebar-nav .nav-link:hover { background-color: #f3f4f6; color: #111827; }
|
||||
.sidebar-nav .nav-link.active { background-color: #f3f4f6; color: #111827; }
|
||||
.sidebar-nav .nav-link .icon { width: 1.25rem; height: 1.25rem; margin-right: 0.75rem; color: #6b7280; transition: color 0.15s ease-in-out; flex-shrink: 0; }
|
||||
.sidebar-nav .nav-link:hover .icon,
|
||||
.sidebar-nav .nav-link.active .icon { color: #374151; }
|
||||
.sidebar-nav .badge { background-color: #f3f4f6; color: #374151; font-size: 0.75rem; font-weight: 500; padding: 0.25rem 0.5rem; border-radius: 9999px; margin-left: auto; }
|
||||
.sidebar-nav .badge.badge-primary { background-color: #dbeafe; color: #1e40af; }
|
||||
.submenu .nav-link { padding-left: 2.75rem; color: #6b7280; font-weight: 400; }
|
||||
.submenu .nav-link:hover { background-color: #f3f4f6; color: #374151; }
|
||||
.submenu .nav-link.active { background-color: #f3f4f6; color: #374151; }
|
||||
.collapse-icon { width: 0.75rem; height: 0.75rem; margin-left: auto; transition: transform 0.15s ease-in-out; }
|
||||
.nav-link[aria-expanded="true"] .collapse-icon { transform: rotate(180deg); }
|
||||
/* Estilos para modo oscuro */
|
||||
.sidebar-dark .sidebar-nav .nav-link { color: #d1d5db; }
|
||||
.sidebar-dark .sidebar-nav .nav-link:hover { background-color: #374151; color: #ffffff; }
|
||||
.sidebar-dark .sidebar-nav .nav-link.active { background-color: #374151; color: #ffffff; }
|
||||
.sidebar-dark .sidebar-nav .nav-link .icon { color: #9ca3af; }
|
||||
.sidebar-dark .sidebar-nav .nav-link:hover .icon,
|
||||
.sidebar-dark .sidebar-nav .nav-link.active .icon { color: #ffffff; }
|
||||
.sidebar-dark .sidebar-nav .badge { background-color: #374151; color: #d1d5db; }
|
||||
.sidebar-dark .sidebar-nav .badge.badge-primary { background-color: #1e3a8a; color: #93c5fd; }
|
||||
.sidebar-dark .submenu .nav-link { color: #9ca3af; }
|
||||
.sidebar-dark .submenu .nav-link:hover { background-color: #374151; color: #d1d5db; }
|
||||
.sidebar-dark .submenu .nav-link.active { background-color: #374151; color: #d1d5db; }
|
||||
/* Navbar */
|
||||
.navbar-brand { font-weight: 600; font-size: 1.125rem; }
|
||||
.menu-toggle { border: 1px solid #6b7280; border-radius: 0.5rem; padding: 0.5rem; transition: all 0.15s ease-in-out; }
|
||||
.menu-toggle:hover { background-color: #f3f4f6; }
|
||||
/* Offcanvas para móviles */
|
||||
.offcanvas { background-color: #ffffff; }
|
||||
.offcanvas-header { border-bottom: 1px solid #e5e7eb; padding: 1rem 1.5rem; }
|
||||
.offcanvas-title { font-weight: 600; font-size: 1.125rem; color: #111827; }
|
||||
.offcanvas-body { padding: 1rem 1.5rem; }
|
||||
/* Responsive adjustments */
|
||||
@media (max-width: 767.98px) {
|
||||
.sidebar-nav .nav-link { color: #374151; }
|
||||
.sidebar-nav .nav-link:hover { background-color: #f3f4f6; color: #111827; }
|
||||
.sidebar-nav .nav-link.active { background-color: #f3f4f6; color: #111827; }
|
||||
}
|
||||
</style>
|
||||
|
||||
<!-- 🔷 NAVBAR -->
|
||||
<nav class="navbar navbar-dark bg-dark fixed-top">
|
||||
<div class="container-fluid">
|
||||
<!-- Botón de menú para móviles -->
|
||||
<button class="menu-toggle d-md-none me-3" type="button" data-bs-toggle="offcanvas" data-bs-target="#sidebarMenu">
|
||||
☰
|
||||
</button>
|
||||
<span class="navbar-brand">SIIH | Agente Aduanal</span>
|
||||
<div class="d-flex ms-auto text-white">
|
||||
<div class="navbar-content">
|
||||
<?php
|
||||
// Verifica si estás en el dashboard principal del importador
|
||||
$esDashboard = str_contains($_SERVER['REQUEST_URI'], '/agentes/dashboard');
|
||||
|
||||
if ($esDashboard): ?>
|
||||
<span id="welcome-text">Bienvenido, <?= htmlspecialchars($_SESSION['usuario_nombre']) ?></span>
|
||||
<?php else: ?>
|
||||
<a href="/IMPORTADORES/sistemas/logout" class="btn btn-outline-light btn-sm ms-2 W-auto btn-animated">Cerrar Sesión</a>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<!-- SIDEBAR FIJA (escritorio) -->
|
||||
<div class="d-none d-md-block sidebar-container sidebar-dark position-fixed h-100" style="width: 250px; top: 0; padding-top: 4rem; z-index: 1000;">
|
||||
<div class="px-3 py-4 h-100 overflow-y-auto">
|
||||
<nav class="sidebar-nav">
|
||||
<ul class="nav flex-column">
|
||||
<!-- INICIO -->
|
||||
<li class="nav-item">
|
||||
<a href="/IMPORTADORES/agentes/dashboard" class="nav-link active">
|
||||
<svg class="icon" fill="currentColor" viewBox="0 0 22 21">
|
||||
<path d="M16.975 11H10V4.025a1 1 0 0 0-1.066-.998 8.5 8.5 0 1 0 9.039 9.039.999.999 0 0 0-1-1.066h.002Z"/>
|
||||
<path d="M12.5 0c-.157 0-.311.01-.565.027A1 1 0 0 0 11 1.02V10h8.975a1 1 0 0 0 1-.935c.013-.188.028-.374.028-.565A8.51 8.51 0 0 0 12.5 0Z"/>
|
||||
</svg>
|
||||
<span>Inicio</span>
|
||||
</a>
|
||||
</li>
|
||||
<!-- IMPORTADORES VINCULADOS -->
|
||||
<li class="nav-item">
|
||||
<a href="/IMPORTADORES/agentes/vinculados" class="nav-link">
|
||||
<svg class="icon" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path d="M12.586 4.586a2 2 0 112.828 2.828l-3 3a2 2 0 01-2.828 0 1 1 0 00-1.414 1.414 4 4 0 005.656 0l3-3a4 4 0 00-5.656-5.656l-1.5 1.5a1 1 0 101.414 1.414l1.5-1.5z"/>
|
||||
<path d="M7.414 15.414a2 2 0 01-2.828-2.828l3-3a2 2 0 012.828 0 1 1 0 001.414-1.414 4 4 0 00-5.656 0l-3 3a4 4 0 105.656 5.656l1.5-1.5a1 1 0 10-1.414-1.414l-1.5 1.5z"/>
|
||||
</svg>
|
||||
<span>Vinculación</span>
|
||||
</a>
|
||||
</li>
|
||||
<!-- Patentes -->
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" data-bs-toggle="collapse" href="#submenuPatentesDesktop" role="button" aria-expanded="false" aria-controls="submenuPatentesDesktop">
|
||||
<svg class="icon" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path fill-rule="evenodd" d="M4 4a2 2 0 012-2h4.586A2 2 0 0112 2.586L15.414 6A2 2 0 0116 7.414V16a2 2 0 01-2 2H6a2 2 0 01-2-2V4zm2 6a1 1 0 011-1h6a1 1 0 110 2H7a1 1 0 01-1-1zm1 3a1 1 0 100 2h6a1 1 0 100-2H7z" clip-rule="evenodd"/>
|
||||
</svg>
|
||||
<span>Patentes</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="submenuPatentesDesktop">
|
||||
<ul class="nav flex-column submenu">
|
||||
<li class="nav-item">
|
||||
<a href="/IMPORTADORES/patente/lista" class="nav-link">
|
||||
Ver Patentes
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a href="/IMPORTADORES/patente/alta" class="nav-link">
|
||||
Nueva Patente
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</li>
|
||||
<!-- LOCACIONES -->
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" data-bs-toggle="collapse" href="#submenuLocacionesDesktop" role="button" aria-expanded="false" aria-controls="submenuLocacionesDesktop">
|
||||
<svg class="icon" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path d="M10 2C6.686 2 4 4.686 4 8c0 5.25 6 12 6 12s6-6.75 6-12c0-3.314-2.686-6-6-6zm0 8c-1.105 0-2-.895-2-2s.895-2 2-2 2 .895 2 2-.895 2-2 2z"/>
|
||||
</svg>
|
||||
<span>Locaciones</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="submenuLocacionesDesktop">
|
||||
<ul class="nav flex-column submenu">
|
||||
<li class="nav-item">
|
||||
<a href="/IMPORTADORES/locaciones/lista" class="nav-link">
|
||||
Ver Locaciones
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a href="/IMPORTADORES/locaciones/alta" class="nav-link">
|
||||
Agregar Locaciones
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</li>
|
||||
<!-- CONFIGURACIÓN -->
|
||||
<li class="nav-item">
|
||||
<a href="/IMPORTADORES/configuracion" class="nav-link">
|
||||
<svg class="icon" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path fill-rule="evenodd" d="M11.983 1.034a1 1 0 00-1.966 0l-.26 1.857a6.996 6.996 0 00-1.223.51l-1.7-1.04a1 1 0 00-1.366.366l-1 1.732a1 1 0 00.366 1.366l1.7 1.04a6.984 6.984 0 000 1.02l-1.7 1.04a1 1 0 00-.366 1.366l1 1.732a1 1 0 001.366.366l1.7-1.04c.392.212.803.386 1.223.51l.26 1.857a1 1 0 001.966 0l.26-1.857c.42-.124.831-.298 1.223-.51l1.7 1.04a1 1 0 001.366-.366l1-1.732a1 1 0 00-.366-1.366l-1.7-1.04a6.984 6.984 0 000-1.02l1.7-1.04a1 1 0 00.366-1.366l-1-1.732a1 1 0 00-1.366-.366l-1.7 1.04a6.996 6.996 0 00-1.223-.51l-.26-1.857zM10 13a3 3 0 100-6 3 3 0 000 6z" clip-rule="evenodd"/>
|
||||
</svg>
|
||||
<span>Configuración</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- OFFCANVAS PARA MÓVILES -->
|
||||
<div class="offcanvas offcanvas-start d-md-none" tabindex="-1" id="sidebarMenu" style="top: 0; width: 250px;">
|
||||
<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">
|
||||
<nav class="sidebar-nav">
|
||||
<ul class="nav flex-column">
|
||||
<!-- INICIO -->
|
||||
<li class="nav-item">
|
||||
<a href="/IMPORTADORES/agentes/dashboard" class="nav-link active">
|
||||
<svg class="icon" fill="currentColor" viewBox="0 0 22 21">
|
||||
<path d="M16.975 11H10V4.025a1 1 0 0 0-1.066-.998 8.5 8.5 0 1 0 9.039 9.039.999.999 0 0 0-1-1.066h.002Z"/>
|
||||
<path d="M12.5 0c-.157 0-.311.01-.565.027A1 1 0 0 0 11 1.02V10h8.975a1 1 0 0 0 1-.935c.013-.188.028-.374.028-.565A8.51 8.51 0 0 0 12.5 0Z"/>
|
||||
</svg>
|
||||
<span>Inicio</span>
|
||||
</a>
|
||||
</li>
|
||||
<!-- IMPORTADORES VINCULADOS -->
|
||||
<li class="nav-item">
|
||||
<a href="/IMPORTADORES/agentes/vinculados" class="nav-link">
|
||||
<svg class="icon" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path d="M12.586 4.586a2 2 0 112.828 2.828l-3 3a2 2 0 01-2.828 0 1 1 0 00-1.414 1.414 4 4 0 005.656 0l3-3a4 4 0 00-5.656-5.656l-1.5 1.5a1 1 0 101.414 1.414l1.5-1.5z"/>
|
||||
<path d="M7.414 15.414a2 2 0 01-2.828-2.828l3-3a2 2 0 012.828 0 1 1 0 001.414-1.414 4 4 0 00-5.656 0l-3 3a4 4 0 105.656 5.656l1.5-1.5a1 1 0 10-1.414-1.414l-1.5 1.5z"/>
|
||||
</svg>
|
||||
<span>Vinculación</span>
|
||||
</a>
|
||||
</li>
|
||||
<!-- Patentes -->
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" data-bs-toggle="collapse" href="#submenuPatentesMobile" role="button" aria-expanded="false" aria-controls="submenuPatentesMobile">
|
||||
<svg class="icon" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path fill-rule="evenodd" d="M4 4a2 2 0 012-2h4.586A2 2 0 0112 2.586L15.414 6A2 2 0 0116 7.414V16a2 2 0 01-2 2H6a2 2 0 01-2-2V4zm2 6a1 1 0 011-1h6a1 1 0 110 2H7a1 1 0 01-1-1zm1 3a1 1 0 100 2h6a1 1 0 100-2H7z" clip-rule="evenodd"/>
|
||||
</svg>
|
||||
<span>Patentes</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="submenuPatentesMobile">
|
||||
<ul class="nav flex-column submenu">
|
||||
<li class="nav-item">
|
||||
<a href="/IMPORTADORES/patente/lista" class="nav-link">
|
||||
Ver Patentes
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a href="/IMPORTADORES/patente/alta" class="nav-link">
|
||||
Nueva Patente
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</li>
|
||||
<!-- LOCACIONES -->
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" data-bs-toggle="collapse" href="#submenuLocacionesMobile" role="button" aria-expanded="false" aria-controls="submenuLocacionesMobile">
|
||||
<svg class="icon" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path d="M10 2C6.686 2 4 4.686 4 8c0 5.25 6 12 6 12s6-6.75 6-12c0-3.314-2.686-6-6-6zm0 8c-1.105 0-2-.895-2-2s.895-2 2-2 2 .895 2 2-.895 2-2 2z"/>
|
||||
</svg>
|
||||
<span>Locaciones</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="submenuLocacionesMobile">
|
||||
<ul class="nav flex-column submenu">
|
||||
<li class="nav-item">
|
||||
<a href="/IMPORTADORES/locaciones/lista" class="nav-link">
|
||||
Ver Locaciones
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a href="/IMPORTADORES/locaciones/alta" class="nav-link">
|
||||
Agregar Locaciones
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</li>
|
||||
<!-- CONFIGURACIÓN -->
|
||||
<li class="nav-item">
|
||||
<a href="/IMPORTADORES/configuracion" class="nav-link">
|
||||
<svg class="icon" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path fill-rule="evenodd" d="M11.983 1.034a1 1 0 00-1.966 0l-.26 1.857a6.996 6.996 0 00-1.223.51l-1.7-1.04a1 1 0 00-1.366.366l-1 1.732a1 1 0 00.366 1.366l1.7 1.04a6.984 6.984 0 000 1.02l-1.7 1.04a1 1 0 00-.366 1.366l1 1.732a1 1 0 001.366.366l1.7-1.04c.392.212.803.386 1.223.51l.26 1.857a1 1 0 001.966 0l.26-1.857c.42-.124.831-.298 1.223-.51l1.7 1.04a1 1 0 001.366-.366l1-1.732a1 1 0 00-.366-1.366l-1.7-1.04a6.984 6.984 0 000-1.02l1.7-1.04a1 1 0 00.366-1.366l-1-1.732a1 1 0 00-1.366-.366l-1.7 1.04a6.996 6.996 0 00-1.223-.51l-.26-1.857zM10 13a3 3 0 100-6 3 3 0 000 6z" clip-rule="evenodd"/>
|
||||
</svg>
|
||||
<span>Configuración</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<script>
|
||||
// Simular lógica de dashboard
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const esDashboard = window.location.pathname.includes('/agentes/dashboard');
|
||||
const welcomeText = document.getElementById('welcome-text');
|
||||
const logoutBtn = document.getElementById('logout-btn');
|
||||
|
||||
if (!esDashboard) {
|
||||
if (welcomeText) {
|
||||
welcomeText.style.display = 'none';
|
||||
}
|
||||
if (logoutBtn) {
|
||||
logoutBtn.style.display = 'inline-block';
|
||||
}
|
||||
}
|
||||
|
||||
// Manejar estados activos basados en URL actual
|
||||
const currentPath = window.location.pathname;
|
||||
const navLinks = document.querySelectorAll('.nav-link');
|
||||
|
||||
navLinks.forEach(link => {
|
||||
const href = link.getAttribute('href');
|
||||
if (href && currentPath.includes(href)) {
|
||||
link.classList.add('active');
|
||||
}
|
||||
});
|
||||
|
||||
// Manejar collapse manualmente
|
||||
document.querySelectorAll('[data-bs-toggle="collapse"]').forEach(trigger => {
|
||||
trigger.addEventListener('click', function(e) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
const targetId = this.getAttribute('href');
|
||||
const targetElement = document.querySelector(targetId);
|
||||
const isExpanded = this.getAttribute('aria-expanded') === 'true';
|
||||
|
||||
if (targetElement) {
|
||||
if (isExpanded) {
|
||||
// Cerrar
|
||||
targetElement.classList.remove('show');
|
||||
this.setAttribute('aria-expanded', 'false');
|
||||
} else {
|
||||
// Abrir
|
||||
targetElement.classList.add('show');
|
||||
this.setAttribute('aria-expanded', 'true');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Remover el atributo data-bs-toggle para evitar conflictos
|
||||
trigger.removeAttribute('data-bs-toggle');
|
||||
});
|
||||
});
|
||||
</script>
|
||||
File diff suppressed because it is too large
Load Diff
594
views/partials/sidebar_configuracion_old.php
Normal file
594
views/partials/sidebar_configuracion_old.php
Normal file
@@ -0,0 +1,594 @@
|
||||
<!-- views/partials/sidebar_configuracion.php -->
|
||||
<?php
|
||||
// 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'] ?? '';
|
||||
$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_cambios' => 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,
|
||||
'registro_vinculaciones' => 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);
|
||||
|
||||
// Variables para detectar las vistas actuales
|
||||
$currentPath = $_SERVER['REQUEST_URI'];
|
||||
$esConfiguracion = $currentPath === '/IMPORTADORES/configuracion/index' || $currentPath === '/IMPORTADORES/configuracion';
|
||||
$esVistaAutomatizaciones = strpos($currentPath, '/IMPORTADORES/automatizaciones/') === 0;
|
||||
$esVistaPreferencias = strpos($currentPath, '/IMPORTADORES/preferencias/') === 0;
|
||||
$esVistaSeguridad = strpos($currentPath, '/IMPORTADORES/seguridad/') === 0;
|
||||
$esVistaBitacoras = strpos($currentPath, '/IMPORTADORES/bitacoras/') === 0 && $currentPath !== '/IMPORTADORES/configuracion';
|
||||
$esVistaReset = strpos($currentPath, '/IMPORTADORES/reset/') === 0;
|
||||
|
||||
// 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_cambios' => ($tipoUsuario === 'super_admin') ? 'Cambios de usuarios' : null,
|
||||
'registro_usuarios' => ($tipoUsuario === 'super_admin') ? 'Registro de usuarios' : null,
|
||||
'registro_agencias' => ($tipoUsuario === 'super_admin') ? 'Registro de agencias' : null,
|
||||
'acceso_usuarios_agencia' => ($tipoUsuario === 'admin_agencia' || $tipoUsuario === 'agente_aduanal') ? 'Registro 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 la URL según el permiso
|
||||
function obtenerUrlPermiso($permiso) {
|
||||
$urls = [
|
||||
'registro_accesos' => '/IMPORTADORES/bitacoras/sistema', // ✅
|
||||
'registro_cambios' => '/IMPORTADORES/bitacoras/cambios', // ✅
|
||||
'registro_usuarios' => '/IMPORTADORES/bitacoras/usuarios', //
|
||||
'registro_agencias' => '/IMPORTADORES/bitacoras/agencias', // ✅
|
||||
'acceso_usuarios_agencia' => '/IMPORTADORES/bitacoras/sistemaAgencia', // ✅
|
||||
'registro_vinculaciones' => '/IMPORTADORES/bitacoras/vinculaciones', // ✅
|
||||
'mi_actividad' => '/IMPORTADORES/bitacoras/miAcceso', // ✅
|
||||
'vinculaciones_usuario' => '/IMPORTADORES/bitacoras/vinculacionesUsuario' // ✅
|
||||
];
|
||||
|
||||
return $urls[$permiso] ?? '/IMPORTADORES/bitacoras/index';
|
||||
}
|
||||
?>
|
||||
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
|
||||
<style>
|
||||
/* Estilos inspirados en Tailwind */
|
||||
.sidebar-container { background-color: #f9fafb; border-right: 1px solid #e5e7eb; }
|
||||
.sidebar-dark { background-color: #1f2937; border-right: 1px solid #374151; }
|
||||
.sidebar-nav .nav-link { color: #374151; font-weight: 500; padding: 0.5rem 0.75rem; margin-bottom: 0.125rem;
|
||||
border-radius: 0.5rem; transition: all 0.15s ease-in-out; display: flex; align-items: center; }
|
||||
.sidebar-nav .nav-link:hover { background-color: #f3f4f6; color: #111827; }
|
||||
.sidebar-nav .nav-link.active { background-color: #f3f4f6; color: #111827; }
|
||||
.sidebar-nav .nav-link .icon { width: 1.25rem; height: 1.25rem; margin-right: 0.75rem; color: #6b7280; transition: color 0.15s ease-in-out; flex-shrink: 0; }
|
||||
.sidebar-nav .nav-link:hover .icon,
|
||||
.sidebar-nav .nav-link.active .icon { color: #374151; }
|
||||
.sidebar-nav .badge { background-color: #f3f4f6; color: #374151; font-size: 0.75rem; font-weight: 500; padding: 0.25rem 0.5rem; border-radius: 9999px; margin-left: auto; }
|
||||
.sidebar-nav .badge.badge-primary { background-color: #dbeafe; color: #1e40af; }
|
||||
.submenu .nav-link { padding-left: 2.75rem; color: #6b7280; font-weight: 400; }
|
||||
.submenu .nav-link:hover { background-color: #f3f4f6; color: #374151; }
|
||||
.submenu .nav-link.active { background-color: #f3f4f6; color: #374151; }
|
||||
.collapse-icon { width: 0.75rem; height: 0.75rem; margin-left: auto; transition: transform 0.15s ease-in-out; }
|
||||
.nav-link[aria-expanded="true"] .collapse-icon { transform: rotate(180deg); }
|
||||
/* Estilos para modo oscuro */
|
||||
.sidebar-dark .sidebar-nav .nav-link { color: #d1d5db; }
|
||||
.sidebar-dark .sidebar-nav .nav-link:hover { background-color: #374151; color: #ffffff; }
|
||||
.sidebar-dark .sidebar-nav .nav-link.active { background-color: #374151; color: #ffffff; }
|
||||
.sidebar-dark .sidebar-nav .nav-link .icon { color: #9ca3af; }
|
||||
.sidebar-dark .sidebar-nav .nav-link:hover .icon,
|
||||
.sidebar-dark .sidebar-nav .nav-link.active .icon { color: #ffffff; }
|
||||
.sidebar-dark .sidebar-nav .badge { background-color: #374151; color: #d1d5db; }
|
||||
.sidebar-dark .sidebar-nav .badge.badge-primary { background-color: #1e3a8a; color: #93c5fd; }
|
||||
.sidebar-dark .submenu .nav-link { color: #9ca3af; }
|
||||
.sidebar-dark .submenu .nav-link:hover { background-color: #374151; color: #d1d5db; }
|
||||
.sidebar-dark .submenu .nav-link.active { background-color: #374151; color: #d1d5db; }
|
||||
/* Navbar */
|
||||
.navbar-brand { font-weight: 600; font-size: 1.125rem; }
|
||||
.menu-toggle { border: 1px solid #6b7280; border-radius: 0.5rem; padding: 0.5rem; transition: all 0.15s ease-in-out; }
|
||||
.menu-toggle:hover { background-color: #f3f4f6; }
|
||||
/* Offcanvas para móviles */
|
||||
.offcanvas { background-color: #ffffff; }
|
||||
.offcanvas-header { border-bottom: 1px solid #e5e7eb; padding: 1rem 1.5rem; }
|
||||
.offcanvas-title { font-weight: 600; font-size: 1.125rem; color: #111827; }
|
||||
.offcanvas-body { padding: 1rem 1.5rem; }
|
||||
/* Responsive adjustments */
|
||||
@media (max-width: 767.98px) {
|
||||
.sidebar-nav .nav-link { color: #374151; }
|
||||
.sidebar-nav .nav-link:hover { background-color: #f3f4f6; color: #111827; }
|
||||
.sidebar-nav .nav-link.active { background-color: #f3f4f6; color: #111827; }
|
||||
.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; }
|
||||
/* 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 -->
|
||||
<nav class="navbar navbar-dark bg-dark fixed-top">
|
||||
<div class="container-fluid">
|
||||
<!-- Botón de menú para móviles -->
|
||||
<button class="menu-toggle d-md-none me-3" type="button" data-bs-toggle="offcanvas" data-bs-target="#sidebarMenu">
|
||||
☰
|
||||
</button>
|
||||
<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">
|
||||
<?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 ms-2 W-auto btn-animated" style="margin: 0 0 0 25px;">
|
||||
← Panel Principal
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<!-- SIDEBAR FIJA (escritorio) -->
|
||||
<div class="d-none d-md-block sidebar-container sidebar-dark position-fixed h-100" style="width: 250px; top: 0; padding-top: 4rem; z-index: 1000;">
|
||||
<div class="px-3 py-4 h-100 overflow-y-auto">
|
||||
<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 ? '#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>
|
||||
<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="submenuPreferenciasDesktop">
|
||||
<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 ? '#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>
|
||||
<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="submenuSeguridadDesktop">
|
||||
<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 ? '#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"/>
|
||||
</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="submenuBitacorasDesktop">
|
||||
<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 px-3 py-2 d-flex justify-content-between align-items-center <?= $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>
|
||||
</div>
|
||||
|
||||
<!-- OFFCANVAS PARA MÓVILES -->
|
||||
<div class="offcanvas offcanvas-start d-md-none" tabindex="-1" id="sidebarMenu" style="top: 0; width: 250px;">
|
||||
<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">
|
||||
<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>
|
||||
</div>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
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');
|
||||
|
||||
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);
|
||||
});
|
||||
|
||||
// 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>
|
||||
File diff suppressed because it is too large
Load Diff
@@ -19,16 +19,10 @@
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.min.css">
|
||||
<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: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; }
|
||||
@media (min-width: 768px) { .content { margin-left: 250px; } }
|
||||
@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; }
|
||||
}
|
||||
/* Contenido principal: sin solaparse con sidebar/navbar */
|
||||
.content { margin-top: 0; padding: 24px 20px; position: relative; z-index: 1; background-color: #f4f6f9; transition: margin-left 0.3s ease; }
|
||||
@media (min-width: 768px) { .content { margin-left: var(--sidebar-width, 280px); } }
|
||||
@media (max-width: 767.98px) { .content { margin-left: 0; } }
|
||||
.card { border-radius: 12px; }
|
||||
/* Animaciones personalizadas */
|
||||
.card-hover { transition: all 0.3s ease; cursor: pointer; }
|
||||
@@ -57,7 +51,7 @@
|
||||
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2), transparent); transition: left 0.5s; }
|
||||
.btn-animated:hover::before { left: 100%; }
|
||||
.hide { display: none !important; }
|
||||
.alert { top: 75px; left: 275px; right: 50px; position: fixed; z-index: 1050; width: calc(100% - 285px); }
|
||||
.alert { top: calc(var(--navbar-height, 70px) + 5px); left: calc(var(--sidebar-width, 280px) + 15px); right: 15px; position: fixed; z-index: 1050; width: calc(100% - var(--sidebar-width, 280px) - 30px); }
|
||||
/* ========== ANIMACIONES PARA INPUTS TEXTO ========== */
|
||||
.form-control:not(.no-animation) { transition: all 0.3s ease; border: 2px solid #dee2e6; position: relative; }
|
||||
.form-control:not(.no-animation).shake { animation: shake 0.5s ease-in-out; }
|
||||
@@ -241,7 +235,7 @@
|
||||
/* Indicador de progreso */
|
||||
.progress-indicator {
|
||||
position: fixed;
|
||||
top: 60px;
|
||||
top: calc(var(--navbar-height, 70px) + 10px);
|
||||
right: 20px;
|
||||
background: white;
|
||||
border-radius: 8px;
|
||||
@@ -293,7 +287,7 @@
|
||||
/* Auto-save indicator */
|
||||
.auto-save-indicator {
|
||||
position: fixed;
|
||||
top: 60px;
|
||||
top: calc(var(--navbar-height, 70px) + 10px);
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
background: #28a745;
|
||||
@@ -308,7 +302,7 @@
|
||||
|
||||
.auto-save-indicator.show {
|
||||
opacity: 1;
|
||||
top: 80px;
|
||||
top: calc(var(--navbar-height, 70px) + 30px);
|
||||
}
|
||||
|
||||
/* Templates rápidos */
|
||||
@@ -926,7 +920,7 @@
|
||||
<!-- Panel de referencia (columna derecha) - MEJORADO -->
|
||||
<div class="col-lg-3">
|
||||
<!-- Card de Pedimentos -->
|
||||
<div class="card bg-light shadow-sm fade-in-up mb-3" style="animation-delay: 0.2s; position: sticky; top: 20px;">
|
||||
<div class="card bg-light shadow-sm fade-in-up mb-3" style="animation-delay: 0.2s; position: sticky; top: calc(var(--navbar-height, 70px) + 20px);">
|
||||
<div class="card-header bg-primary text-white py-2">
|
||||
<h6 class="mb-0"><i class="fas fa-list-ul me-1"></i> Pedimentos Disponibles</h6>
|
||||
</div>
|
||||
@@ -1161,7 +1155,6 @@
|
||||
|
||||
<!-- Choices.js JS -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/choices.js/public/assets/scripts/choices.min.js"></script>
|
||||
<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/select2@4.1.0/dist/js/select2.min.js"></script>
|
||||
|
||||
<script>
|
||||
|
||||
Reference in New Issue
Block a user