Atajos de catálogos personalizables

This commit is contained in:
2025-07-03 11:45:26 -06:00
parent 495f908a25
commit 78a48ee596
11 changed files with 1626 additions and 751 deletions

View File

@@ -106,9 +106,9 @@ function aprobar_usuario()
$email_encrypt = encrypt($email);
// Insertar en usuarios_sistema y obtener ID en la misma consulta
$sqlInsert = "INSERT INTO usuarios_sistema (nombre, email, password_hash, tipo_usuario, activo, creado_en, dos_factores, creado_por)
$sqlInsert = "INSERT INTO usuarios_sistema (nombre, email, password_hash, tipo_usuario, activo, creado_en, dos_factores, creado_por, preferencias_catalogos)
OUTPUT INSERTED.id_usuario
VALUES (?, ?, ?, ?, 1, GETDATE(), 0, ?)";
VALUES (?, ?, ?, ?, 1, GETDATE(), 0, ?, 1)";
$stmtInsert = sqlsrv_query($conn, $sqlInsert, [
$nombre_encrypt, $email_encrypt, $password_hash, $tipo, $_SESSION['usuario_id']
@@ -140,6 +140,17 @@ function aprobar_usuario()
throw new Exception("Error al insertar información general: " . print_r(sqlsrv_errors(), true));
}
// Insertar en preferencias de catalogos
$sqlPref = "INSERT INTO preferencias_catalogos_usuario
(id_usuario, locaciones, vinculacion, transportistas, transportes, choferes, proveedores,
productos_frecuentes, solicitudes_importacion, expediente_electronico, configuracion, cerrar_sesion)
VALUES (?, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1)";
$stmtPref = sqlsrv_query($conn, $sqlPref, [$id_usuario]);
if (!$stmtPref) {
throw new Exception("Error al insertar preferencias de catalogos: " . print_r(sqlsrv_errors(), true));
}
// Actualizar solicitud
$sqlUpdate = "UPDATE solicitudes_importadores
SET request_status = 'approved', approval_date = GETDATE(), approved_by = ?
@@ -283,6 +294,7 @@ function guardar_usuario()
// 4. Insertar usuario
$sqlInsert = "INSERT INTO usuarios_sistema (nombre, email, password_hash, tipo_usuario, activo, creado_en, dos_factores, creado_por)
OUTPUT INSERTED.id_usuario
VALUES (?, ?, ?, ?, 1, GETDATE(), 0, ?)";
$params = [$nombre_encrypted, $email_encrypted, $password_hash, $tipo, $_SESSION['usuario_id']];
$stmtInsert = sqlsrv_query($conn, $sqlInsert, $params);
@@ -293,12 +305,31 @@ function guardar_usuario()
}
// 5. Obtener ID del nuevo usuario (para posibles tablas relacionadas)
$sqlGetId = "SELECT SCOPE_IDENTITY() AS id_usuario";
$stmtGetId = sqlsrv_query($conn, $sqlGetId);
$idUsuarioRow = sqlsrv_fetch_array($stmtGetId, SQLSRV_FETCH_ASSOC);
$idUsuarioRow = sqlsrv_fetch_array($stmtInsert, SQLSRV_FETCH_ASSOC);
$id_usuario = $idUsuarioRow['id_usuario'] ?? null;
// 6. Enviar correo de bienvenida
// 6. Insertar en informacion_general
$sqlInfo = "INSERT INTO informacion_general
(id_usuario, nombre, correo)
VALUES (?, ?, ?)";
$stmtInfo = sqlsrv_query($conn, $sqlInfo, [$id_usuario, $nombre, $email]);
if (!$stmtInfo) {
throw new Exception("Error al insertar información general: " . print_r(sqlsrv_errors(), true));
}
// 7. Insertar en preferencias de catalogos
$sqlPref = "INSERT INTO preferencias_catalogos_usuario
(id_usuario, locaciones, vinculacion, transportistas, transportes, choferes, proveedores,
productos_frecuentes, solicitudes_importacion, expediente_electronico, configuracion, cerrar_sesion)
VALUES (?, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1)";
$stmtPref = sqlsrv_query($conn, $sqlPref, [$id_usuario]);
if (!$stmtPref) {
throw new Exception("Error al insertar preferencias de catalogos: " . print_r(sqlsrv_errors(), true));
}
// 8. Enviar correo de bienvenida
$mail = new PHPMailer(true);
try {
$mail->isSMTP();

View File

@@ -3,14 +3,165 @@ require_once __DIR__ . '/../helpers/session.php';
require_once __DIR__ . '/../../config/database.php';
require_once __DIR__ . '/../helpers/crypto.php';
// Función para obtener catálogos visibles del usuario
function obtenerCatalogosVisibles($idUsuario) {
$conn = getConnection();
// Obtener configuración general y tipo de usuario
$query = "SELECT preferencias_catalogos, tipo_usuario FROM usuarios_sistema WHERE id_usuario = ?";
$stmt = sqlsrv_prepare($conn, $query, [$idUsuario]);
if (!$stmt || !sqlsrv_execute($stmt)) {
sqlsrv_close($conn);
return ['catalogos_activos' => false, 'catalogos' => []];
}
$usuario = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC);
if (!$usuario) {
sqlsrv_close($conn);
return ['catalogos_activos' => false, 'catalogos' => []];
}
$catalogos_general = $usuario['preferencias_catalogos'] ?? 0;
$tipoUsuario = $usuario['tipo_usuario'] ?? 'importador';
sqlsrv_free_stmt($stmt);
// Si los catálogos están desactivados, solo mostrar obligatorios
if (!$catalogos_general) {
// Obtener solo catálogos obligatorios
$query = "SELECT nombre, descripcion, color, ruta, accion
FROM tipos_catalogos
WHERE tipo_usuario = ? AND nombre IN ('Configuración', 'Cerrar sesión')
ORDER BY id";
$stmt = sqlsrv_prepare($conn, $query, [$tipoUsuario]);
if (!$stmt || !sqlsrv_execute($stmt)) {
sqlsrv_close($conn);
return ['catalogos_activos' => false, 'catalogos' => []];
}
$catalogos = [];
while ($row = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC)) {
$catalogos[] = [
'nombre' => $row['nombre'],
'descripcion' => $row['descripcion'],
'color' => $row['color'],
'ruta' => $row['ruta'],
'accion' => $row['accion'],
'visible' => true,
'obligatorio' => true
];
}
sqlsrv_free_stmt($stmt);
sqlsrv_close($conn);
return ['catalogos_activos' => false, 'catalogos' => $catalogos];
}
// Obtener todos los catálogos para este tipo de usuario
$query = "SELECT nombre, descripcion, color, ruta, accion
FROM tipos_catalogos
WHERE tipo_usuario = ?
ORDER BY id";
$stmt = sqlsrv_prepare($conn, $query, [$tipoUsuario]);
if (!$stmt || !sqlsrv_execute($stmt)) {
sqlsrv_close($conn);
return ['catalogos_activos' => false, 'catalogos' => []];
}
// Crear array de catálogos disponibles
$catalogos_disponibles = [];
while ($row = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC)) {
$catalogos_disponibles[] = [
'nombre' => $row['nombre'],
'descripcion' => $row['descripcion'],
'color' => $row['color'],
'ruta' => $row['ruta'],
'accion' => $row['accion'],
'campo_bd' => strtolower(str_replace([' ', 'ó', 'ú', 'á', 'é', 'í'], ['_', 'o', 'u', 'a', 'e', 'i'], $row['nombre']))
];
}
sqlsrv_free_stmt($stmt);
// Obtener preferencias específicas del usuario
$preferencias = [];
switch ($tipoUsuario) {
case 'importador':
$query = "SELECT * FROM preferencias_catalogos_usuario WHERE id_usuario = ?";
$stmt = sqlsrv_prepare($conn, $query, [$idUsuario]);
if ($stmt && sqlsrv_execute($stmt)) {
$preferenciasRow = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC);
if ($preferenciasRow) {
$preferencias = $preferenciasRow;
}
sqlsrv_free_stmt($stmt);
}
break;
// Aquí agregarías otros casos cuando implementes otros tipos de usuario
}
// Determinar qué catálogos mostrar
$catalogos_visibles = [];
foreach ($catalogos_disponibles as $catalogo) {
$campo = $catalogo['campo_bd'];
$esObligatorio = in_array($catalogo['nombre'], ['Configuración', 'Cerrar sesión']);
if ($esObligatorio) {
// Catálogos obligatorios siempre visibles
$catalogo['visible'] = true;
$catalogo['obligatorio'] = true;
} else {
// Verificar si está activado en preferencias
$catalogo['visible'] = isset($preferencias[$campo]) && $preferencias[$campo] == 1;
$catalogo['obligatorio'] = false;
}
if ($catalogo['visible']) {
$catalogos_visibles[] = $catalogo;
}
}
sqlsrv_close($conn);
return [
'catalogos_activos' => true,
'catalogos' => $catalogos_visibles,
'tipo_usuario' => $tipoUsuario
];
}
// Función para obtener icono según el nombre del catálogo
function obtenerIconoCatalogo($nombre) {
$iconos = [
'Locaciones' => '📍',
'Vinculación' => '🔗',
'Transportistas' => '🚚',
'Transportes' => '🚛',
'Choferes' => '👨‍✈️',
'Proveedores' => '🏭',
'Productos frecuentes' => '⭐',
'Solicitudes importación' => '📄',
'Expediente electrónico' => '📁',
'Configuración' => '⚙️',
'Cerrar sesión' => '🚪'
];
return $iconos[$nombre] ?? '📁';
}
// Función para el dashboard del importador
function dashboard()
{
if (!isset($_SESSION['usuario_id']) || $_SESSION['tipo_usuario'] !== 'importador') {
header('Location: /IMPORTADORES/login');
$idUsuario = $_SESSION['usuario_id'] ?? null;
if (!$idUsuario) {
header("Location: /IMPORTADORES/login");
exit;
}
$nombreImportador = $_SESSION['usuario_nombre'];
// Obtener catálogos visibles
$catalogosData = obtenerCatalogosVisibles($idUsuario);
include __DIR__ . '/../../views/importadores/dashboard_importador.php';
}

View File

@@ -540,4 +540,297 @@ function esDiaResumen($config_dias)
return in_array($dia_letra, array_map('trim', $dias_activos));
}
}
?>
function catalogos()
{
$conn = getConnection();
$idUsuario = $_SESSION['usuario_id'] ?? null;
if (!$idUsuario) {
header("Location: /IMPORTADORES/login");
exit;
}
// Obtener configuración general de catalogos y tipo de usuario
$query = "SELECT preferencias_catalogos, tipo_usuario FROM usuarios_sistema WHERE id_usuario = ?";
$stmt = sqlsrv_prepare($conn, $query, [$idUsuario]);
if (!$stmt || !sqlsrv_execute($stmt)) {
die("Error al obtener configuración: " . print_r(sqlsrv_errors(), true));
}
$usuario = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC);
if (!$usuario) {
die("Usuario no encontrado");
}
$catalogos = $usuario['preferencias_catalogos'] ?? 0;
$tipoUsuario = $usuario['tipo_usuario'] ?? 'importador';
sqlsrv_free_stmt($stmt);
// Obtener preferencias específicas según el tipo de usuario
switch ($tipoUsuario) {
case 'importador':
$query = "SELECT * FROM preferencias_catalogos_usuario WHERE id_usuario = ?";
$stmt = sqlsrv_prepare($conn, $query, [$idUsuario]);
if ($stmt && sqlsrv_execute($stmt)) {
$preferenciasRow = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC);
sqlsrv_free_stmt($stmt);
} else {
$preferenciasRow = false;
}
// Si no existe registro, crear valores por defecto para importador
if (!$preferenciasRow) {
$preferencias = [
'locaciones' => 0,
'vinculacion' => 0,
'transportistas' => 0,
'transportes' => 0,
'choferes' => 0,
'proveedores' => 0,
'productos_frecuentes' => 0,
'solicitudes_importacion' => 0,
'expediente_electronico' => 0,
'configuracion' => 0,
'cerrar_sesion' => 0
];
} else {
$preferencias = $preferenciasRow;
}
break;
// case 'agente_aduanal':
// $query = "SELECT * FROM preferencias_catalogos_agente WHERE id_usuario = ?";
// $stmt = sqlsrv_prepare($conn, $query, [$idUsuario]);
// if ($stmt && sqlsrv_execute($stmt)) {
// $preferenciasRow = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC);
// sqlsrv_free_stmt($stmt);
// } else {
// $preferenciasRow = false;
// }
// Si no existe registro, crear valores por defecto para agente aduanal
// if (!$preferenciasRow) {
// $preferencias = [
// Aquí se definirán los campos específicos para agente aduanal
// cuando se creen las tablas correspondientes
// 'configuracion' => 0,
// 'cerrar_sesion' => 0
// ];
// } else {
// $preferencias = $preferenciasRow;
// }
// break;
// case 'admin_agencia':
// $query = "SELECT * FROM preferencias_catalogos_agencia WHERE id_usuario = ?";
// $stmt = sqlsrv_prepare($conn, $query, [$idUsuario]);
// if ($stmt && sqlsrv_execute($stmt)) {
// $preferenciasRow = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC);
// sqlsrv_free_stmt($stmt);
// } else {
// $preferenciasRow = false;
// }
// Si no existe registro, crear valores por defecto para admin agencia
// if (!$preferenciasRow) {
// $preferencias = [
// Aquí se definirán los campos específicos para admin agencia
// cuando se creen las tablas correspondientes
// 'configuracion' => 0,
// 'cerrar_sesion' => 0
// ];
// } else {
// $preferencias = $preferenciasRow;
// }
// break;
// case 'super_admin':
// $query = "SELECT * FROM preferencias_catalogos_admin WHERE id_usuario = ?";
// $stmt = sqlsrv_prepare($conn, $query, [$idUsuario]);
// if ($stmt && sqlsrv_execute($stmt)) {
// $preferenciasRow = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC);
// sqlsrv_free_stmt($stmt);
// } else {
// $preferenciasRow = false;
// }
// Si no existe registro, crear valores por defecto para super admin
// if (!$preferenciasRow) {
// $preferencias = [
// Aquí se definirán los campos específicos para super admin
// cuando se creen las tablas correspondientes
// 'configuracion' => 0,
// 'cerrar_sesion' => 0
// ];
// } else {
// $preferencias = $preferenciasRow;
// }
// break;
default:
// Tipo de usuario no reconocido, usar valores por defecto mínimos
$preferencias = [
'configuracion' => 0,
'cerrar_sesion' => 0
];
break;
}
// Obtener tipos de catalogos disponibles según el tipo de usuario
$query = "SELECT nombre, descripcion, color FROM tipos_catalogos WHERE tipo_usuario = ? ORDER BY id";
$stmt = sqlsrv_prepare($conn, $query, [$tipoUsuario]);
if (!$stmt || !sqlsrv_execute($stmt)) {
die("Error al obtener tipos: " . print_r(sqlsrv_errors(), true));
}
// Crear array asociativo para la vista
$tipos = [];
while ($row = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC)) {
$tipos[$row['nombre']] = [
'descripcion' => $row['descripcion'],
'color' => $row['color']
];
}
sqlsrv_free_stmt($stmt);
sqlsrv_close($conn);
include __DIR__ . '/../../views/preferencias/catalogos.php';
}
function guardarCatalogosAjax()
{
// Verificar que sea una petición AJAX
if (!isset($_SERVER['HTTP_X_REQUESTED_WITH']) ||
$_SERVER['HTTP_X_REQUESTED_WITH'] !== 'XMLHttpRequest') {
http_response_code(400);
echo json_encode(['success' => false, 'message' => 'Petición no válida']);
exit;
}
// Obtener datos JSON del cuerpo de la petición
$input = json_decode(file_get_contents('php://input'), true);
if (!$input || !isset($input['tipo'])) {
http_response_code(400);
echo json_encode(['success' => false, 'message' => 'Datos no válidos']);
exit;
}
$conn = getConnection();
$idUsuario = $_SESSION['usuario_id'] ?? null;
if (!$idUsuario) {
http_response_code(401);
echo json_encode(['success' => false, 'message' => 'Usuario no autenticado']);
exit;
}
// Obtener tipo de usuario
$query = "SELECT tipo_usuario FROM usuarios_sistema WHERE id_usuario = ?";
$stmt = sqlsrv_prepare($conn, $query, [$idUsuario]);
if (!$stmt || !sqlsrv_execute($stmt)) {
http_response_code(500);
echo json_encode(['success' => false, 'message' => 'Error al obtener información del usuario']);
exit;
}
$usuario = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC);
$tipoUsuario = $usuario['tipo_usuario'] ?? 'importador';
sqlsrv_free_stmt($stmt);
try {
$tipo = $input['tipo'];
switch ($tipo) {
case 'catalogos_general':
// Actualizar preferencias generales de catálogos
$valor = isset($input['valor']) && $input['valor'] ? 1 : 0;
$query = "UPDATE usuarios_sistema SET preferencias_catalogos = ? WHERE id_usuario = ?";
$stmt = sqlsrv_prepare($conn, $query, [$valor, $idUsuario]);
if (!$stmt || !sqlsrv_execute($stmt)) {
throw new Exception('Error al actualizar preferencias generales');
}
sqlsrv_free_stmt($stmt);
break;
case 'tipo_catalogo':
// Actualizar preferencia específica de catálogo
if (!isset($input['nombre']) || !isset($input['valor'])) {
throw new Exception('Datos incompletos para tipo de catálogo');
}
$nombre = $input['nombre'];
$valor = $input['valor'] ? 1 : 0;
// Determinar la tabla según el tipo de usuario
$tabla = '';
switch ($tipoUsuario) {
case 'importador':
$tabla = 'preferencias_catalogos_usuario';
break;
case 'agente_aduanal':
$tabla = 'preferencias_catalogos_agente';
break;
case 'admin_agencia':
$tabla = 'preferencias_catalogos_agencia';
break;
case 'super_admin':
$tabla = 'preferencias_catalogos_admin';
break;
default:
throw new Exception('Tipo de usuario no válido');
}
// Verificar si existe un registro para este usuario
$query = "SELECT id_preferencia FROM $tabla WHERE id_usuario = ?";
$stmt = sqlsrv_prepare($conn, $query, [$idUsuario]);
if (!$stmt || !sqlsrv_execute($stmt)) {
throw new Exception('Error al verificar preferencias existentes');
}
$existe = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC);
sqlsrv_free_stmt($stmt);
if ($existe) {
// Actualizar registro existente
$query = "UPDATE $tabla SET [$nombre] = ? WHERE id_usuario = ?";
$stmt = sqlsrv_prepare($conn, $query, [$valor, $idUsuario]);
} else {
// Crear nuevo registro
$query = "INSERT INTO $tabla (id_usuario, [$nombre]) VALUES (?, ?)";
$stmt = sqlsrv_prepare($conn, $query, [$idUsuario, $valor]);
}
if (!$stmt || !sqlsrv_execute($stmt)) {
throw new Exception('Error al actualizar preferencia de catálogo');
}
sqlsrv_free_stmt($stmt);
break;
default:
throw new Exception('Tipo de operación no válido');
}
// Confirmar transacción
sqlsrv_commit($conn);
// Respuesta exitosa
echo json_encode([
'success' => true,
'message' => 'Preferencias actualizadas correctamente'
]);
} catch (Exception $e) {
// Revertir transacción en caso de error
sqlsrv_rollback($conn);
http_response_code(500);
echo json_encode([
'success' => false,
'message' => $e->getMessage()
]);
} finally {
sqlsrv_close($conn);
}
}

61
catalogos.txt Normal file
View File

@@ -0,0 +1,61 @@
CREATE TABLE preferencias_catalogos_usuario (
id_preferencia INT IDENTITY(1,1) NOT NULL,
id_usuario INT NOT NULL,
locaciones BIT DEFAULT 0,
vinculacion BIT DEFAULT 0,
transportistas BIT DEFAULT 0,
transportes BIT DEFAULT 0,
choferes BIT DEFAULT 0,
proveedores BIT DEFAULT 0,
productos_frecuentes BIT DEFAULT 0,
solicitudes_importacion BIT DEFAULT 0,
expediente_electronico BIT DEFAULT 0,
configuracion BIT DEFAULT 0,
cerrar_sesion BIT DEFAULT 0,
FOREIGN KEY (id_usuario) REFERENCES usuarios_sistema(id_usuario)
);
ALTER TABLE usuarios_sistema
ADD [preferencias_catalogos] BIT DEFAULT 1;
CREATE TABLE tipos_catalogos (
id INT PRIMARY KEY IDENTITY,
tipo_usuario VARCHAR(20) NOT NULL,
nombre VARCHAR(255) NOT NULL,
descripcion VARCHAR(500) NULL,
color VARCHAR(20) NOT NULL,
ruta VARCHAR(100) NOT NULL,
accion VARCAHR(50) NOT NULL
);
INSERT INTO tipos_catalogos (tipo_usuario, nombre, descripcion, color, ruta, accion) VALUES
('importador', 'Locaciones', 'Visualiza las locaciones registradas.', 'lime', '/IMPORTADORES/importadores/lista', 'Ver locaciones'),
('importador', 'Vinculación', 'Visualiza o establece nuevas vinculaciones con agencias aduanales.', 'teal', '/IMPORTADORES/viculaciones/vinculacionesUsuario', 'Ver vinculaciones'),
('importador', 'Transportistas', 'Agrega o consulta los choferes que colaboran contigo.', 'success', '/IMPORTADORES/transportistas/lista', 'Ver transportistas'),
('importador', 'Transportes', 'Gestiona y carga tus unidades de transporte.', 'info', '/IMPORTADORES/transportes/lista', 'Ver transportes'),
('importador', 'Choferes', 'Gestiona y carga a tus choferes.', 'cyan', '/IMPORTADORES/choferes/lista', 'Ver choferes'),
('importador', 'Proveedores', 'Agrega tus proveedores.', 'primary', '/IMPORTADORES/proveedores/crear', 'Nuevo proveedor'),
('importador', 'Productos frecuentes', 'Agrega productos frecuentes de tu interez.', 'violet', '/IMPORTADORES/productos_frecuentes/alta', 'Nuevo producto'),
('importador', 'Solicitudes importación', 'Inicia nuevas solicitudes de pedimentos.', 'indigo', '/IMPORTADORES/solicitud_importacion/crear', 'Nueva solicitud'),
('importador', 'Expediente electrónico', 'Revisa el expediente electrónico para tus solicitudes.', 'orange', '/IMPORTADORES/expediente/index', 'Ver expediente'),
('importador', 'Configuración', 'Administra los datos de tu empresa y preferencias.', 'warning', '/IMPORTADORES/configuracion', 'Ir a configuración'),
('importador', 'Cerrar sesión', 'Salir del sistema de forma segura.', 'danger', '/IMPORTADORES/sistemas/logout', 'Cerrar sesión');
UPDATE usuarios_sistema
SET preferencias_catalogos = 1
WHERE preferencias_catalogos IS NULL OR preferencias_catalogos = 0;
-- 2. Insertar en preferencias_catalogos_usuario solo para los que aún no tienen registro
INSERT INTO preferencias_catalogos_usuario (
id_usuario, locaciones, vinculacion, transportistas, transportes, choferes, proveedores,
productos_frecuentes, solicitudes_importacion, expediente_electronico, configuracion, cerrar_sesion
)
SELECT u.id_usuario, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
FROM usuarios_sistema u
WHERE u.tipo_usuario = 'importador'
AND NOT EXISTS (
SELECT 1 FROM preferencias_catalogos_usuario p
WHERE p.id_usuario = u.id_usuario
);

View File

@@ -25,6 +25,15 @@
.btn-teal { background-color: #20c997; color: white; }
.btn-teal:hover { background-color: #4dd4ac; color: black; }
.text-teal { color: #20c997; }
.btn-lime { background-color: #84cc16; color: white; }
.text-lime { color: #84cc16; }
.btn-lime:hover { background-color: #65a30d; color: white; }
.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; }
/* 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 */
@@ -42,92 +51,73 @@
<div class="content">
<h4 class="mb-4">📦 Panel del Importador</h4>
<?php if ($catalogosData['catalogos_activos']): ?>
<div class="row g-4">
<?php foreach ($catalogosData['catalogos'] as $catalogo): ?>
<div class="col-md-4">
<div class="card shadow-sm p-3">
<h5 class="text-primary">Transportes</h5>
<p>Gestiona y carga tus unidades de transporte.</p>
<a href="/IMPORTADORES/transportes/lista"
class="btn btn-primary btn-sm mt-2 <?= $_SERVER['REQUEST_URI'] === '/IMPORTADORES/transportes/lista' ? 'active' : '' ?>">
Ver transportes
<div class="card shadow-sm p-3 <?= $catalogo['obligatorio'] ? 'border-warning' : '' ?>">
<h5 class="text-<?= $catalogo['color'] ?>">
<?= obtenerIconoCatalogo($catalogo['nombre']) ?> <?= $catalogo['nombre'] ?>
</h5>
<p class="text-muted small"><?= $catalogo['descripcion'] ?></p>
<a href="<?= $catalogo['ruta'] ?>"
class="btn btn-<?= $catalogo['color'] ?> btn-sm mt-2 <?= $_SERVER['REQUEST_URI'] === $catalogo['ruta'] ? 'active' : '' ?>">
<?= $catalogo['accion'] ?>
</a>
<?php if ($catalogo['obligatorio']): ?>
<?php endif; ?>
</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">
<h6>💡 Consejo</h6>
<p class="mb-2">Parece que no tienes catálogos activados. Puedes activar los que necesites desde tus preferencias.</p>
<a href="/IMPORTADORES/preferencias/catalogos" class="btn btn-primary btn-sm">
Configurar Catálogos
</a>
</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">
<h5>🔒 Catálogos Desactivados</h5>
<p>Los catálogos están desactivados en tus preferencias. Solo tienes acceso a las funciones básicas del sistema.</p>
<a href="/IMPORTADORES/preferencias/catalogos" class="btn btn-primary btn-sm">
Activar Catálogos
</a>
</div>
</div>
<!-- Mostrar solo catálogos obligatorios -->
<?php foreach ($catalogosData['catalogos'] as $catalogo): ?>
<div class="col-md-4">
<div class="card shadow-sm p-3">
<h5 class="text-success">Transportistas</h5>
<p>Agrega o consulta los choferes que colaboran contigo.</p>
<a href="/IMPORTADORES/transportistas/lista"
class="btn btn-success btn-sm mt-2 <?= $_SERVER['REQUEST_URI'] === '/IMPORTADORES/transportistas/lista' ? 'active' : '' ?>">
Ver transportistas
<div class="card shadow-sm p-3 border-warning">
<h5 class="text-<?= $catalogo['color'] ?>">
<?= obtenerIconoCatalogo($catalogo['nombre']) ?> <?= $catalogo['nombre'] ?>
</h5>
<p class="text-muted small"><?= $catalogo['descripcion'] ?></p>
<a href="<?= $catalogo['ruta'] ?>"
class="btn btn-<?= $catalogo['color'] ?> btn-sm mt-2 <?= $_SERVER['REQUEST_URI'] === $catalogo['ruta'] ? 'active' : '' ?>">
<?= $catalogo['accion'] ?>
</a>
<small class="text-warning mt-1">
<i class="fas fa-lock"></i> Función básica
</small>
</div>
</div>
<div class="col-md-4">
<div class="card shadow-sm p-3">
<h5 class="text-info">Solicitudes de Importación</h5>
<p>Inicia nuevas solicitudes de pedimentos.</p>
<a href="/IMPORTADORES/solicitud_importacion/crear"
class="btn btn-info btn-sm mt-2 <?= str_contains($_SERVER['REQUEST_URI'], '/solicitud_importacion/crear') ? 'active' : '' ?>">
Nueva solicitud
</a>
</div>
</div>
<div class="col-md-4">
<div class="card shadow-sm p-3">
<h5 class="text-indigo">Proveedores</h5>
<p>Agrega tus proveedores.</p>
<a href="/IMPORTADORES/proveedores/crear"
class="btn btn-indigo btn-sm mt-2 <?= str_contains($_SERVER['REQUEST_URI'], '/proveedores/crear') ? 'active' : '' ?>">
Nuevo proveedor
</a>
</div>
</div>
<div class="col-md-4">
<div class="card shadow-sm p-3">
<h5 class="text-warning">Configuración</h5>
<p>Administra los datos de tu empresa y preferencias.</p>
<a href="/IMPORTADORES/configuracion"
class="btn btn-warning btn-sm mt-2 <?= str_contains($_SERVER['REQUEST_URI'], '/configuracion') ? 'active' : '' ?>">
Configurar
</a>
</div>
</div>
<div class="col-md-4">
<div class="card shadow-sm p-3">
<h5 class="text-orange">Locaciones</h5>
<p>Visualiza las locaciones registradas.</p>
<a href="/IMPORTADORES/importadores/lista"
class="btn btn-orange btn-sm mt-2 <?= str_contains($_SERVER['REQUEST_URI'], '/importadores/lista') ? 'active' : '' ?>">
Ver locaciones
</a>
</div>
</div>
<div class="col-md-4">
<div class="card shadow-sm p-3">
<h5 class="text-teal">Vinculaciones</h5>
<p>Visualiza o establece nuevas vinculaciones con agencias aduanales.</p>
<a href="/IMPORTADORES/vinculaciones/vinculacionesUsuario"
class="btn btn-teal btn-sm mt-2 <?= str_contains($_SERVER['REQUEST_URI'], '/importadores/vinculaciones') ? 'active' : '' ?>">
Ver vinculaciones
</a>
</div>
</div>
<div class="col-md-4">
<div class="card shadow-sm p-3">
<h5 class="text-danger">Cerrar sesión</h5>
<p>Salir del sistema de forma segura.</p>
<a href="/IMPORTADORES/sistemas/logout" class="btn btn-danger btn-sm mt-2">Logout</a>
</div>
</div>
<?php endforeach; ?>
</div>
<?php endif; ?>
</div>
</body>

View File

@@ -233,7 +233,7 @@ function obtenerUrlPermiso($permiso) {
role="button" aria-expanded="true" aria-controls="submenuPreferencias"
onclick="<? $enDashboardPreferencias ? '' : 'event.preventDefault(); window.location.href = \'/IMPORTADORES/preferencias/index\';' ?>">
✅ Preferencias
<span class="badge bg-secondary">1</span>
<span class="badge bg-secondary">2</span>
</a>
<div class="collapse show" id="submenuPreferencias">
<nav class="nav flex-column ms-3">
@@ -241,13 +241,17 @@ function obtenerUrlPermiso($permiso) {
class="nav-link px-3 py-1 <?= $_SERVER['REQUEST_URI'] === '/IMPORTADORES/preferencias/notificaciones' ? 'active' : '' ?>">
• Gestión de Notificaciones
</a>
<a href="/IMPORTADORES/preferencias/catalogos"
class="nav-link px-3 py-1 <?= $_SERVER['REQUEST_URI'] === '/IMPORTADORES/preferencias/catalogos' ? 'active' : '' ?>">
• Visualización de Catálogos
</a>
</nav>
</div>
<?php elseif ($esConfiguracion || $esVistaAutomatizaciones || $esVistaSeguridad || $esVistaBitacoras || $esVistaReset): ?>
<a href="/IMPORTADORES/preferencias/index"
class="nav-link px-3 py-2 d-flex justify-content-between align-items-center">
✅ Preferencias
<span class="badge bg-secondary">1</span>
<span class="badge bg-secondary">2</span>
</a>
<?php endif; ?>
<?php endif; ?>
@@ -377,7 +381,7 @@ function obtenerUrlPermiso($permiso) {
role="button" aria-expanded="true" aria-controls="submenuPreferencias"
onclick="<? $enDashboardPreferencias ? '' : 'event.preventDefault(); window.location.href = \'/IMPORTADORES/preferencias/index\';' ?>">
✅ Preferencias
<span class="badge bg-secondary">1</span>
<span class="badge bg-secondary">2</span>
</a>
<div class="collapse show" id="submenuPreferencias">
<nav class="nav flex-column ms-3">
@@ -385,13 +389,17 @@ function obtenerUrlPermiso($permiso) {
class="nav-link px-3 py-1 <?= $_SERVER['REQUEST_URI'] === '/IMPORTADORES/preferencias/notificaciones' ? 'active' : '' ?>">
• Gestión de Notificaciones
</a>
<a href="/IMPORTADORES/preferencias/catalogos"
class="nav-link px-3 py-1 <?= $_SERVER['REQUEST_URI'] === '/IMPORTADORES/preferencias/catalogos' ? 'active' : '' ?>">
• Visualización de Catálogos
</a>
</nav>
</div>
<?php elseif ($esConfiguracion || $esVistaAutomatizaciones || $esVistaSeguridad || $esVistaBitacoras || $esVistaReset): ?>
<a href="/IMPORTADORES/preferencias/index"
class="nav-link px-3 py-2 d-flex justify-content-between align-items-center">
✅ Preferencias
<span class="badge bg-secondary">1</span>
<span class="badge bg-secondary">2</span>
</a>
<?php endif; ?>
<?php endif; ?>

View File

@@ -70,7 +70,7 @@
<a class="nav-link px-3 py-2 d-flex justify-content-between align-items-center
<?= str_contains($_SERVER['REQUEST_URI'], '/IMPORTADORES/transportistas') ? 'active' : '' ?>"
data-bs-toggle="collapse" href="#submenuTransportistas" role="button" aria-expanded="false">
👨‍✈️ Transportistas
🚚 Transportistas
<span class="badge bg-secondary">3</span>
</a>
<div class="collapse <?= str_contains($_SERVER['REQUEST_URI'], '/IMPORTADORES/transportistas') ? 'show' : '' ?>" id="submenuTransportistas">
@@ -94,7 +94,7 @@
<a class="nav-link px-3 py-2 d-flex justify-content-between align-items-center
<?= str_contains($_SERVER['REQUEST_URI'], '/IMPORTADORES/transportes') ? 'active' : '' ?>"
data-bs-toggle="collapse" href="#submenuTransportes" role="button" aria-expanded="false">
🚚 Transportes
🚛 Transportes
<span class="badge bg-secondary">3</span>
</a>
<div class="collapse <?= str_contains($_SERVER['REQUEST_URI'], '/IMPORTADORES/transportes') ? 'show' : '' ?>" id="submenuTransportes">
@@ -118,7 +118,7 @@
<a class="nav-link px-3 py-2 d-flex justify-content-between align-items-center
<?= str_contains($_SERVER['REQUEST_URI'], '/choferes') ? 'active' : '' ?>"
data-bs-toggle="collapse" href="#submenuChoferes" role="button" aria-expanded="false">
🚛 Choferes
👨‍✈️ Choferes
<span class="badge bg-secondary">2</span>
</a>
<div class="collapse <?= str_contains($_SERVER['REQUEST_URI'], '/IMPORTADORES/choferes') ? 'show' : '' ?>" id="submenuChoferes">
@@ -266,7 +266,7 @@
<a class="nav-link px-3 py-2 d-flex justify-content-between align-items-center
<?= str_contains($_SERVER['REQUEST_URI'], '/IMPORTADORES/transportistas') ? 'active' : '' ?>"
data-bs-toggle="collapse" href="#submenuTransportistas" role="button" aria-expanded="false">
👨‍✈️ Transportistas
🚚 Transportistas
<span class="badge bg-secondary">3</span>
</a>
<div class="collapse <?= str_contains($_SERVER['REQUEST_URI'], '/IMPORTADORES/transportistas') ? 'show' : '' ?>" id="submenuTransportistas">
@@ -290,7 +290,7 @@
<a class="nav-link px-3 py-2 d-flex justify-content-between align-items-center
<?= str_contains($_SERVER['REQUEST_URI'], '/IMPORTADORES/transportes') ? 'active' : '' ?>"
data-bs-toggle="collapse" href="#submenuTransportes" role="button" aria-expanded="false">
🚚 Transportes
🚛 Transportes
<span class="badge bg-secondary">3</span>
</a>
<div class="collapse <?= str_contains($_SERVER['REQUEST_URI'], '/IMPORTADORES/transportes') ? 'show' : '' ?>" id="submenuTransportes">
@@ -314,7 +314,7 @@
<a class="nav-link px-3 py-2 d-flex justify-content-between align-items-center
<?= str_contains($_SERVER['REQUEST_URI'], '/choferes') ? 'active' : '' ?>"
data-bs-toggle="collapse" href="#submenuChoferes" role="button" aria-expanded="false">
🚛 Choferes
👨‍✈️ Choferes
<span class="badge bg-secondary">2</span>
</a>
<div class="collapse <?= str_contains($_SERVER['REQUEST_URI'], '/IMPORTADORES/choferes') ? 'show' : '' ?>" id="submenuChoferes">

View File

@@ -0,0 +1,348 @@
<?php include __DIR__ . '/../partials/sidebar_configuracion.php'; ?>
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<title>📊 Visualización de Catálogos</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://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css" rel="stylesheet">
<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; }
.btn-indigo { background-color: #6610f2; color: white; }
.btn-indigo:hover { background-color: #520dc2; color: white; }
.text-indigo { color: #520dc2; }
.btn-orange { background-color: orangeRed; color: white; }
.btn-orange:hover { background-color: #ff3600; color: white; }
.text-orange { color: orangeRed; }
.btn-teal { background-color: #20c997; color: white; }
.btn-teal:hover { background-color: #4dd4ac; color: black; }
.text-teal { color: #20c997; }
.btn-lime { background-color: #84cc16; color: white; }
.text-lime { color: #84cc16; }
.btn-lime:hover { background-color: #65a30d; color: white; }
.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; }
/* 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; }
}
.card { border-radius: 12px; }
.fade-transition { transition: all 0.3s ease-in-out; }
.section-divider { border-bottom: 1px solid #e9ecef; padding-bottom: 1.5rem; margin-bottom: 1.5rem; }
.section-divider:last-child { border-bottom: none; margin-bottom: 0; padding-bottom: 0; }
/* Indicador de guardado */
.save-indicator { position: fixed; top: 20px; right: 20px; padding: 12px 20px; border-radius: 8px; font-weight: 500; font-size: 14px; z-index: 9999;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); transition: all 0.3s ease; display: flex; align-items: center; }
.save-indicator.saving { background-color: #ffc107; color: #856404; border: 1px solid #ffeaa7; }
.save-indicator.saved { background-color: #d4edda; color: #155724; border: 1px solid #c3e6cb; }
.save-indicator.error { background-color: #f8d7da; color: #721c24; border: 1px solid #f5c6cb; }
/* Transiciones suaves para los switches */
.form-check-input:disabled { opacity: 0.6; cursor: not-allowed; }
.fade-transition { transition: opacity 0.3s ease; }
/* Mejorar la apariencia de los switches de catálogos */
.tipo-catalogo { margin-right: 10px; }
.form-check-label { cursor: pointer; }
.form-check-label .form-text { margin-top: 4px; font-size: 0.875rem; }
/* Animación para el spinner */
@keyframes spinner-border { to { transform: rotate(360deg); } }
.spinner-border { display: inline-block; width: 1rem; height: 1rem; vertical-align: text-bottom; border: 0.125em solid currentColor; border-right-color: transparent;
border-radius: 50%; animation: spinner-border 0.75s linear infinite; }
.spinner-border-sm { width: 0.875rem; height: 0.875rem; border-width: 0.125em; }
</style>
</head>
<body>
<div class="content">
<h4 class="mb-4">📊 Visualización de Catálogos</h4>
<!-- Mensajes de éxito o error iniciales -->
<?php if (isset($_GET['success'])): ?>
<div class="success-message">
✅ Las preferencias de catálogos se han guardado correctamente.
</div>
<?php endif; ?>
<?php if (isset($_GET['error'])): ?>
<div class="error-message">
❌ Error: <?= htmlspecialchars($_GET['error']) ?>
</div>
<?php endif; ?>
<div class="card shadow-sm p-4 mb-4 bg-white">
<!-- Switch general de notificaciones -->
<div class="section-divider">
<div class="form-check form-switch">
<input class="form-check-input" type="checkbox" id="preferencias-catalogos"
data-tipo="catalogos_general"
<?= !empty($catalogos) ? 'checked' : '' ?>>
<label class="form-check-label fw-bold" for="preferencias-catalogos">
Catálogos
</label>
</div>
</div>
<!-- Tipos de catalogos -->
<div id="tipos-catalogos" class="fade-transition" style="opacity:1;">
<div class="section-divider">
<h6 class="mb-3">📋 Preferencias de Catálogos</h6>
<div class="row">
<?php if (!empty($tipos)): ?>
<?php foreach ($tipos as $nombre => $info): ?>
<?php
// Convertir el nombre a formato de campo (minúsculas y guiones bajos)
$nombreCampo = strtolower(str_replace([' ', 'ó', 'ú', 'á', 'é', 'í'], ['_', 'o', 'u', 'a', 'e', 'i'], $nombre));
$nombreCampo = str_replace(['ñ'], ['n'], $nombreCampo);
// Crear ID único para cada checkbox
$idCampo = 'catalogo_' . $nombreCampo;
// Verificar si el campo existe en preferencias
$checked = isset($preferencias[$nombreCampo]) && !empty($preferencias[$nombreCampo]);
?>
<div class="col-md-6 mb-3">
<div class="form-check form-switch">
<input class="form-check-input tipo-catalogo" type="checkbox"
name="<?= $nombreCampo ?>" id="<?= $idCampo ?>"
data-tipo="tipo-catalogo" data-nombre="<?= $nombreCampo ?>"
<?= $checked ? 'checked' : '' ?>>
<label class="form-check-label" for="<?= $idCampo ?>">
<strong><?= htmlspecialchars($nombre) ?></strong>
<div class="form-text text-<?= htmlspecialchars($info['color']) ?>">
<?= htmlspecialchars($info['descripcion']) ?>
</div>
</label>
</div>
</div>
<?php endforeach; ?>
<?php else: ?>
<div class="col-12">
<div class="alert alert-info">
No hay catálogos disponibles para tu tipo de usuario.
</div>
</div>
<?php endif; ?>
</div>
</div>
</div>
</div>
</div>
<!-- Indicador de guardado -->
<div id="save-indicator" class="save-indicator" style="display: none;">
<span class="indicator-text">Guardando...</span>
<div class="spinner-border spinner-border-sm ms-2" style="display: none;"></div>
</div>
<script>
// Estado global para controlar las peticiones
let isAutoSaving = false;
let saveTimeout = null;
// Referencias a elementos
const saveIndicator = document.getElementById('save-indicator');
const indicatorText = saveIndicator.querySelector('.indicator-text');
const spinner = saveIndicator.querySelector('.spinner-border');
// Función para mostrar indicador de guardado
function showSaveIndicator(type, message) {
// Limpiar clases anteriores
saveIndicator.className = 'save-indicator';
// Agregar clase según el tipo
saveIndicator.classList.add(type);
indicatorText.textContent = message;
// Mostrar/ocultar spinner
if (type === 'saving') {
spinner.style.display = 'inline-block';
} else {
spinner.style.display = 'none';
}
saveIndicator.style.display = 'block';
// Auto-ocultar después de unos segundos (excepto cuando está guardando)
if (type !== 'saving') {
setTimeout(() => {
saveIndicator.style.display = 'none';
}, type === 'error' ? 5000 : 3000);
}
}
// Función para realizar guardado automático
async function autoSave(tipo, data) {
if (isAutoSaving) return;
isAutoSaving = true;
showSaveIndicator('saving', 'Guardando cambios...');
try {
const response = await fetch('/IMPORTADORES/preferencias/guardarCatalogosAjax', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Requested-With': 'XMLHttpRequest'
},
body: JSON.stringify({
tipo: tipo,
...data
})
});
const result = await response.json();
if (result.success) {
showSaveIndicator('saved', '✓ Cambios guardados');
} else {
throw new Error(result.message || 'Error desconocido');
}
} catch (error) {
console.error('Error al guardar:', error);
showSaveIndicator('error', '✗ Error: ' + error.message);
// En caso de error, podríamos revertir el estado del switch
// pero por ahora solo mostramos el error
} finally {
isAutoSaving = false;
}
}
// Función para mostrar/ocultar secciones con animación
function toggleSection(checkbox, targetId) {
const target = document.getElementById(targetId);
const isChecked = checkbox.checked;
if (isChecked) {
target.style.display = 'block';
setTimeout(() => {
target.style.opacity = '1';
}, 10);
} else {
target.style.opacity = '0';
setTimeout(() => {
target.style.display = 'none';
}, 300);
}
}
// Función para debounce (evitar múltiples guardados rápidos)
function debounce(func, wait) {
return function executedFunction(...args) {
const later = () => {
clearTimeout(saveTimeout);
func(...args);
};
clearTimeout(saveTimeout);
saveTimeout = setTimeout(later, wait);
};
}
// === FUNCIONALIDAD DE CATÁLOGOS ===
// Event listener para switch general de catálogos
document.getElementById('preferencias-catalogos').addEventListener('change', function() {
const isChecked = this.checked;
// Deshabilitar temporalmente otros switches para evitar conflictos
const allSwitches = document.querySelectorAll('.form-check-input');
allSwitches.forEach(sw => sw.disabled = true);
// Guardar cambio
autoSave('catalogos_general', {
valor: isChecked
}).finally(() => {
// Rehabilitar switches
allSwitches.forEach(sw => sw.disabled = false);
});
// Mostrar/ocultar sección de tipos
toggleSection(this, 'tipos-catalogos');
// Si se desactivan los catálogos, también desactivar todos los tipos
if (!isChecked) {
// Desmarcar todos los tipos de catálogo
document.querySelectorAll('.tipo-catalogo').forEach(checkbox => {
checkbox.checked = false;
});
}
});
// Event listeners para tipos específicos de catálogo
document.querySelectorAll('.tipo-catalogo').forEach(checkbox => {
checkbox.addEventListener('change', function() {
const isChecked = this.checked;
const tipoNombre = this.dataset.nombre;
// Verificar que los catálogos generales estén activados
const catalogosGenerales = document.getElementById('preferencias-catalogos').checked;
if (!catalogosGenerales) {
showSaveIndicator('error', 'Primero debes activar las preferencias de catálogos');
this.checked = false;
return;
}
// Deshabilitar switch temporalmente
this.disabled = true;
// Guardar cambio
autoSave('tipo_catalogo', {
nombre: tipoNombre,
valor: isChecked
}).finally(() => {
this.disabled = false;
});
});
});
// === FIN FUNCIONALIDAD DE CATÁLOGOS ===
// Prevenir envío accidental del formulario
document.addEventListener('keydown', function(e) {
if (e.key === 'Enter' && e.target.type !== 'submit') {
e.preventDefault();
}
});
// Mensaje de confirmación antes de salir si hay guardado en progreso
window.addEventListener('beforeunload', function(e) {
if (isAutoSaving) {
e.preventDefault();
e.returnValue = 'Hay cambios pendientes de guardar. ¿Estás seguro de que quieres salir?';
return e.returnValue;
}
});
// Inicialización al cargar la página
document.addEventListener('DOMContentLoaded', function() {
// Verificar estado inicial del switch general
const catalogosGenerales = document.getElementById('preferencias-catalogos');
if (catalogosGenerales && !catalogosGenerales.checked) {
const tiposCatalogos = document.getElementById('tipos-catalogos');
if (tiposCatalogos) {
tiposCatalogos.style.opacity = '0';
tiposCatalogos.style.display = 'none';
}
}
});
</script>
</body>
</html>

View File

@@ -45,6 +45,17 @@
</div>
</div>
<div class="col-md-4">
<div class="card shadow-sm p-3">
<h5 class="text-success">Catalogos</h5>
<p>Personaliza la visualización de tus catalogos.</p>
<a href="/IMPORTADORES/preferencias/catalogos"
class="btn btn-success btn-sm mt-2 <?= $_SERVER['REQUEST_URI'] === '/IMPORTADORES/preferencias/catalogos' ? 'active' : '' ?>">
Personalizar Catálogos
</a>
</div>
</div>
</div>
</div>

View File

@@ -79,7 +79,6 @@
<!-- Tipos de notificaciones -->
<div id="tipos-notificaciones" class="fade-transition" style="opacity:1;">
<div class="section-divider">
<h6 class="mb-3">📋 Tipos de Notificaciones</h6>
<div class="row">
@@ -205,10 +204,7 @@
<!-- Configuración específica del resumen diario -->
<div id="resumen-diario-config" class="fade-transition" style="display: none; opacity: 0;">
<div class="resumen-config">
<h6 class="mb-3">
⏰ Configuración del Resumen Diario
</h6>
<h6 class="mb-3">⏰ Configuración del Resumen Diario</h6>
<div class="row">
<div class="col-md-6 mb-3">
<label for="resumen_hora" class="form-label fw-bold">Hora de envío</label>
@@ -222,15 +218,9 @@
<!-- Botones de preselección -->
<div class="day-preset">
<button type="button" class="btn btn-outline-primary btn-sm preset-btn active" data-preset="L-V">
Lunes a Viernes
</button>
<button type="button" class="btn btn-outline-primary btn-sm preset-btn" data-preset="TODOS">
Todos los días
</button>
<button type="button" class="btn btn-outline-primary btn-sm preset-btn" data-preset="PERSONALIZADO">
Personalizado
</button>
<button type="button" class="btn btn-outline-primary btn-sm preset-btn active" data-preset="L-V">Lunes a Viernes</button>
<button type="button" class="btn btn-outline-primary btn-sm preset-btn" data-preset="TODOS">Todos los días</button>
<button type="button" class="btn btn-outline-primary btn-sm preset-btn" data-preset="PERSONALIZADO"> Personalizado</button>
</div>
<!-- Selector individual de días -->
@@ -301,7 +291,6 @@
</div>
</div>
</div>
<br>
<!-- Configuración de correo adicional -->
<div class="section-divider">
@@ -309,16 +298,13 @@
<input class="form-check-input" type="checkbox" id="notificaciones_extra"
data-tipo="notificaciones_extra"
<?= $notificaciones['notificaciones_extra'] ? 'checked' : '' ?>>
<label class="form-check-label fw-bold" for="notificaciones_extra">
Enviar notificaciones a correo adicional
</label>
<label class="form-check-label fw-bold" for="notificaciones_extra">Enviar notificaciones a correo adicional</label>
<div class="form-text">Recibe una copia de las notificaciones en un correo adicional</div>
</div>
<!-- Campo informativo del correo adicional -->
<div id="correo-extra-section" class="correo-extra-section fade-transition"
<?= !$notificaciones['notificaciones_extra'] ? 'style="display:none; opacity:0;"' : 'style="opacity:1;"' ?>>
<?php if (!empty($correoExtra)): ?>
<div class="alert alert-info d-flex align-items-center">
<div>
@@ -326,9 +312,7 @@
<code><?= htmlspecialchars($correoExtra) ?></code>
<div class="form-text mt-1">
Las notificaciones se enviarán también a este correo.
<a href="/IMPORTADORES/seguridad/index" class="text-decoration-none">
Gestionar en Seguridad →
</a>
<a href="/IMPORTADORES/seguridad/index" class="text-decoration-none">Gestionar en Seguridad →</a>
</div>
</div>
</div>
@@ -341,16 +325,13 @@
<strong>⚠️ No hay correo adicional registrado</strong><br>
<div class="form-text">
Para usar esta función, primero debes registrar un correo adicional en la sección de Seguridad.
<a href="/IMPORTADORES/seguridad/index" class="text-decoration-none">
Ir a Seguridad →
</a>
<a href="/IMPORTADORES/seguridad/index" class="text-decoration-none">Ir a Seguridad →</a>
</div>
</div>
</div>
<?php endif; ?>
</div>
</div>
</div>
</div>

View File

@@ -38,6 +38,7 @@
<!-- 📄 CONTENIDO -->
<div class="content">
<h4 class="mb-4">🔗 Mis vinculaciones</h4>
<a href="/IMPORTADORES/vinculaciones/nuevaVinculacion" class="btn btn-success mb-3"> Nueva Vinculación</a>
<!-- TABLA DE VINCULACIÓN USUARIO -->
<div class="card p-3 shadow-sm">
<h5 class="mb-3">🏪 Agencia Vinculadas</h5>