up
This commit is contained in:
@@ -9,7 +9,7 @@ requireAdmin();
|
|||||||
|
|
||||||
$db = Database::getInstance();
|
$db = Database::getInstance();
|
||||||
|
|
||||||
$roles = $db->fetchAll("SELECT id, name, slug, description, color, is_system, created_at FROM roles ORDER BY id ASC");
|
$roles = $db->fetchAll("SELECT id, name, slug, description, color, home_page, is_system, created_at FROM roles ORDER BY id ASC");
|
||||||
|
|
||||||
foreach ($roles as &$role) {
|
foreach ($roles as &$role) {
|
||||||
$mods = $db->fetchAll(
|
$mods = $db->fetchAll(
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ $name = trim($body['name'] ?? '');
|
|||||||
$slug = trim($body['slug'] ?? '');
|
$slug = trim($body['slug'] ?? '');
|
||||||
$description = trim($body['description'] ?? '');
|
$description = trim($body['description'] ?? '');
|
||||||
$color = trim($body['color'] ?? '#6c757d');
|
$color = trim($body['color'] ?? '#6c757d');
|
||||||
|
$home_page = trim($body['home_page'] ?? '') ?: null;
|
||||||
$modules = $body['modules'] ?? [];
|
$modules = $body['modules'] ?? [];
|
||||||
|
|
||||||
if (!$name) jsonError('El nombre del rol es obligatorio');
|
if (!$name) jsonError('El nombre del rol es obligatorio');
|
||||||
@@ -39,16 +40,16 @@ if ($id) {
|
|||||||
if (!$role) jsonError('Rol no encontrado', 404);
|
if (!$role) jsonError('Rol no encontrado', 404);
|
||||||
|
|
||||||
$db->query(
|
$db->query(
|
||||||
"UPDATE roles SET name=?, slug=?, description=?, color=?, updated_at=NOW() WHERE id=?",
|
"UPDATE roles SET name=?, slug=?, description=?, color=?, home_page=?, updated_at=NOW() WHERE id=?",
|
||||||
[$name, $slug, $description, $color, $id]
|
[$name, $slug, $description, $color, $home_page, $id]
|
||||||
);
|
);
|
||||||
// Reconstruir módulos solo si no es sistema, o siempre (admin puede editar módulos)
|
// Reconstruir módulos solo si no es sistema, o siempre (admin puede editar módulos)
|
||||||
$db->query("DELETE FROM role_modules WHERE role_id = ?", [$id]);
|
$db->query("DELETE FROM role_modules WHERE role_id = ?", [$id]);
|
||||||
} else {
|
} else {
|
||||||
// Crear
|
// Crear
|
||||||
$db->query(
|
$db->query(
|
||||||
"INSERT INTO roles (name, slug, description, color) VALUES (?,?,?,?)",
|
"INSERT INTO roles (name, slug, description, color, home_page) VALUES (?,?,?,?,?)",
|
||||||
[$name, $slug, $description, $color]
|
[$name, $slug, $description, $color, $home_page]
|
||||||
);
|
);
|
||||||
$id = $db->lastInsertId();
|
$id = $db->lastInsertId();
|
||||||
}
|
}
|
||||||
|
|||||||
+88
-16
@@ -57,19 +57,6 @@ body {
|
|||||||
box-shadow: 2px 0 10px rgba(0, 0, 0, 0.1);
|
box-shadow: 2px 0 10px rgba(0, 0, 0, 0.1);
|
||||||
}
|
}
|
||||||
|
|
||||||
.sidebar-header {
|
|
||||||
padding: 20px;
|
|
||||||
text-align: center;
|
|
||||||
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
|
|
||||||
background: rgba(255, 255, 255, 0.05);
|
|
||||||
}
|
|
||||||
|
|
||||||
.sidebar-header h4 {
|
|
||||||
font-weight: 600;
|
|
||||||
margin: 0;
|
|
||||||
font-size: 1.2rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sidebar-menu {
|
.sidebar-menu {
|
||||||
list-style: none;
|
list-style: none;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
@@ -82,12 +69,13 @@ body {
|
|||||||
|
|
||||||
.sidebar-menu .nav-link {
|
.sidebar-menu .nav-link {
|
||||||
display: block;
|
display: block;
|
||||||
padding: 15px 20px;
|
padding: 11px 20px;
|
||||||
color: rgba(255, 255, 255, 0.8);
|
color: rgba(255, 255, 255, 0.82);
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
transition: all 0.3s ease;
|
transition: all 0.25s ease;
|
||||||
position: relative;
|
position: relative;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
font-size: 0.9rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sidebar-menu .nav-link::before {
|
.sidebar-menu .nav-link::before {
|
||||||
@@ -155,6 +143,90 @@ body {
|
|||||||
background: #dc3545;
|
background: #dc3545;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ── Sidebar – cabecera mejorada ─────────────────────────────────── */
|
||||||
|
.sidebar-header {
|
||||||
|
padding: 18px 20px;
|
||||||
|
text-align: center;
|
||||||
|
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
|
||||||
|
background: rgba(0, 0, 0, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-header h4 {
|
||||||
|
font-size: 1.05rem;
|
||||||
|
font-weight: 700;
|
||||||
|
letter-spacing: 0.02em;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Sidebar – etiquetas de categoría ────────────────────────────── */
|
||||||
|
.sidebar-menu li.sidebar-section {
|
||||||
|
border-bottom: none;
|
||||||
|
padding: 12px 18px 4px;
|
||||||
|
font-size: 0.67rem;
|
||||||
|
font-weight: 800;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.13em;
|
||||||
|
color: rgba(255, 255, 255, 0.38);
|
||||||
|
margin-top: 4px;
|
||||||
|
pointer-events: none;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 5px;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-menu li.sidebar-section::after {
|
||||||
|
content: '';
|
||||||
|
flex: 1;
|
||||||
|
height: 1px;
|
||||||
|
background: rgba(255, 255, 255, 0.1);
|
||||||
|
margin-left: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Sidebar – usuario al pie ────────────────────────────────────── */
|
||||||
|
.sidebar-user {
|
||||||
|
padding: 12px 18px;
|
||||||
|
border-top: 1px solid rgba(255, 255, 255, 0.1);
|
||||||
|
background: rgba(0, 0, 0, 0.18);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-user-name {
|
||||||
|
font-size: 0.85rem;
|
||||||
|
font-weight: 600;
|
||||||
|
color: rgba(255, 255, 255, 0.9);
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-user-role {
|
||||||
|
font-size: 0.7rem;
|
||||||
|
color: rgba(255, 255, 255, 0.48);
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.08em;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Sidebar – scrollbar ─────────────────────────────────────────── */
|
||||||
|
.sidebar::-webkit-scrollbar {
|
||||||
|
width: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar::-webkit-scrollbar-track {
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar::-webkit-scrollbar-thumb {
|
||||||
|
background: rgba(255, 255, 255, 0.18);
|
||||||
|
border-radius: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar::-webkit-scrollbar-thumb:hover {
|
||||||
|
background: rgba(255, 255, 255, 0.32);
|
||||||
|
}
|
||||||
|
|
||||||
/* Main Content */
|
/* Main Content */
|
||||||
.main-content {
|
.main-content {
|
||||||
margin-left: 280px;
|
margin-left: 280px;
|
||||||
|
|||||||
+8
-1
@@ -499,7 +499,8 @@ function authenticateUser($username, $password) {
|
|||||||
// Cargar módulos del rol (slug y permiso)
|
// Cargar módulos del rol (slug y permiso)
|
||||||
$modules = [];
|
$modules = [];
|
||||||
$modulePermissions = [];
|
$modulePermissions = [];
|
||||||
$roleSlug = $admin['role'] ?? 'admin';
|
$roleSlug = $admin['role'] ?? 'admin';
|
||||||
|
$homePage = null;
|
||||||
if (!empty($admin['role_id'])) {
|
if (!empty($admin['role_id'])) {
|
||||||
$modStmt = $pdo->prepare(
|
$modStmt = $pdo->prepare(
|
||||||
"SELECT module_slug, COALESCE(permission,'write') AS permission
|
"SELECT module_slug, COALESCE(permission,'write') AS permission
|
||||||
@@ -510,6 +511,11 @@ function authenticateUser($username, $password) {
|
|||||||
$modules[] = $row['module_slug'];
|
$modules[] = $row['module_slug'];
|
||||||
$modulePermissions[$row['module_slug']] = $row['permission'];
|
$modulePermissions[$row['module_slug']] = $row['permission'];
|
||||||
}
|
}
|
||||||
|
// Leer home_page del rol
|
||||||
|
$rpStmt = $pdo->prepare("SELECT home_page FROM roles WHERE id = ?");
|
||||||
|
$rpStmt->execute([$admin['role_id']]);
|
||||||
|
$rp = $rpStmt->fetch(PDO::FETCH_ASSOC);
|
||||||
|
$homePage = $rp['home_page'] ?? null;
|
||||||
} elseif ($roleSlug === 'admin') {
|
} elseif ($roleSlug === 'admin') {
|
||||||
// Fallback: admin sin role_id tiene todos los módulos
|
// Fallback: admin sin role_id tiene todos los módulos
|
||||||
$modules = array_keys(SYSTEM_MODULES);
|
$modules = array_keys(SYSTEM_MODULES);
|
||||||
@@ -524,6 +530,7 @@ function authenticateUser($username, $password) {
|
|||||||
'email' => $admin['email'],
|
'email' => $admin['email'],
|
||||||
'role' => $roleSlug,
|
'role' => $roleSlug,
|
||||||
'role_id' => $admin['role_id'] ?? null,
|
'role_id' => $admin['role_id'] ?? null,
|
||||||
|
'home_page' => $homePage,
|
||||||
'enfermera_id' => $admin['enfermera_id'] ?? null,
|
'enfermera_id' => $admin['enfermera_id'] ?? null,
|
||||||
'modules' => $modules,
|
'modules' => $modules,
|
||||||
'module_permissions' => $modulePermissions,
|
'module_permissions' => $modulePermissions,
|
||||||
|
|||||||
+61
-10
@@ -20,16 +20,17 @@ class ModuleRegistry
|
|||||||
private static ?array $registry = null;
|
private static ?array $registry = null;
|
||||||
|
|
||||||
/** Orden de visualización de categorías en el sidebar */
|
/** Orden de visualización de categorías en el sidebar */
|
||||||
private const CATEGORY_ORDER = ['bot', 'lab', 'turnero', 'clinico', 'reportes', 'sistema'];
|
private const CATEGORY_ORDER = ['bot', 'lab', 'formularios', 'turnero', 'clinico', 'reportes', 'sistema'];
|
||||||
|
|
||||||
/** Etiquetas e íconos de cada categoría */
|
/** Etiquetas e íconos de cada categoría */
|
||||||
private const CATEGORIES = [
|
private const CATEGORIES = [
|
||||||
'bot' => ['label' => 'WhatsApp', 'icon' => 'fab fa-whatsapp'],
|
'bot' => ['label' => 'WhatsApp', 'icon' => 'fab fa-whatsapp'],
|
||||||
'lab' => ['label' => 'Laboratorio', 'icon' => 'fas fa-flask'],
|
'lab' => ['label' => 'Domicilios', 'icon' => 'fas fa-house-medical'],
|
||||||
'turnero' => ['label' => 'Turnero', 'icon' => 'fas fa-ticket-alt'],
|
'formularios'=> ['label' => 'Formulario', 'icon' => 'fas fa-file-invoice'],
|
||||||
'clinico' => ['label' => 'Clínico', 'icon' => 'fas fa-vials'],
|
'turnero' => ['label' => 'Turnero', 'icon' => 'fas fa-ticket-alt'],
|
||||||
'reportes'=> ['label' => 'Reportes', 'icon' => 'fas fa-chart-bar'],
|
'clinico' => ['label' => 'Clínico', 'icon' => 'fas fa-vials'],
|
||||||
'sistema' => ['label' => 'Sistema', 'icon' => 'fas fa-cog'],
|
'reportes' => ['label' => 'Reportes', 'icon' => 'fas fa-chart-bar'],
|
||||||
|
'sistema' => ['label' => 'Sistema', 'icon' => 'fas fa-cog'],
|
||||||
];
|
];
|
||||||
|
|
||||||
// ─── API pública ────────────────────────────────────────────────────────
|
// ─── API pública ────────────────────────────────────────────────────────
|
||||||
@@ -47,20 +48,26 @@ class ModuleRegistry
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Retorna solo los módulos ACTIVOS que el usuario actual puede ver.
|
* Retorna solo los módulos ACTIVOS que el usuario actual puede ver.
|
||||||
* Si el usuario es admin sin role_id → todos los activos.
|
*
|
||||||
|
* IMPORTANTE: consulta role_modules desde BD en tiempo real para que los
|
||||||
|
* cambios hechos en lab_usuarios.php se reflejen sin necesidad de re-login.
|
||||||
|
*
|
||||||
|
* Excepción: superadmin y admin sin role_id tienen acceso total (retrocompat).
|
||||||
*
|
*
|
||||||
* @return array<string, array> Indexado por slug.
|
* @return array<string, array> Indexado por slug.
|
||||||
*/
|
*/
|
||||||
public static function forCurrentUser(): array
|
public static function forCurrentUser(): array
|
||||||
{
|
{
|
||||||
self::load();
|
self::load();
|
||||||
|
$allowed = self::liveUserModules(); // null = acceso total
|
||||||
|
|
||||||
$result = [];
|
$result = [];
|
||||||
foreach (self::$registry as $slug => $mod) {
|
foreach (self::$registry as $slug => $mod) {
|
||||||
if (!$mod['is_active']) {
|
if (!$mod['is_active']) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
// hasModule() definida en config.php / core/Rbac.php (retrocompat)
|
// null = superadmin / admin legacy → ve todo
|
||||||
if (function_exists('hasModule') && !hasModule($slug)) {
|
if ($allowed !== null && !in_array($slug, $allowed, true)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
$result[$slug] = $mod;
|
$result[$slug] = $mod;
|
||||||
@@ -68,6 +75,50 @@ class ModuleRegistry
|
|||||||
return $result;
|
return $result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Obtiene los slugs de módulos permitidos para el usuario actual consultando
|
||||||
|
* role_modules en BD (ignora la caché de sesión para ser siempre fresco).
|
||||||
|
*
|
||||||
|
* Retorna null si el usuario tiene acceso total (superadmin / admin legacy).
|
||||||
|
*
|
||||||
|
* @return string[]|null
|
||||||
|
*/
|
||||||
|
private static function liveUserModules(): ?array
|
||||||
|
{
|
||||||
|
$role = $_SESSION['admin_user']['role'] ?? 'admin';
|
||||||
|
$roleId = $_SESSION['admin_user']['role_id'] ?? null;
|
||||||
|
|
||||||
|
// Superadmin → acceso total siempre
|
||||||
|
if ($role === 'superadmin') {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Admin sin role_id asignado → retrocompat, acceso total
|
||||||
|
if ($role === 'admin' && !$roleId) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sin role_id en cualquier otro rol → usar sesión como fallback
|
||||||
|
if (!$roleId) {
|
||||||
|
return $_SESSION['admin_user']['modules'] ?? [];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Consulta en tiempo real: reflejan cambios en lab_usuarios.php al instante
|
||||||
|
try {
|
||||||
|
$db = Database::getInstance();
|
||||||
|
$pdo = $db->getConnection();
|
||||||
|
$stmt = $pdo->prepare(
|
||||||
|
"SELECT module_slug FROM role_modules WHERE role_id = ?"
|
||||||
|
);
|
||||||
|
$stmt->execute([(int) $roleId]);
|
||||||
|
return array_column($stmt->fetchAll(PDO::FETCH_ASSOC), 'module_slug');
|
||||||
|
} catch (Exception $e) {
|
||||||
|
error_log("ModuleRegistry::liveUserModules error: " . $e->getMessage());
|
||||||
|
// Fallback a sesión si la BD no responde
|
||||||
|
return $_SESSION['admin_user']['modules'] ?? [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retorna módulos activos agrupados por categoría para el sidebar.
|
* Retorna módulos activos agrupados por categoría para el sidebar.
|
||||||
* Solo incluye módulos accesibles por el usuario actual.
|
* Solo incluye módulos accesibles por el usuario actual.
|
||||||
|
|||||||
+44
-7
@@ -4,6 +4,9 @@
|
|||||||
* Solo accesible para administradores.
|
* Solo accesible para administradores.
|
||||||
*/
|
*/
|
||||||
require_once 'config/config.php';
|
require_once 'config/config.php';
|
||||||
|
if (file_exists(__DIR__ . '/core/ModuleRegistry.php')) {
|
||||||
|
require_once __DIR__ . '/core/ModuleRegistry.php';
|
||||||
|
}
|
||||||
|
|
||||||
if (!isUserLoggedIn()) { header('Location: login.php'); exit; }
|
if (!isUserLoggedIn()) { header('Location: login.php'); exit; }
|
||||||
if (isEnfermero()) { header('Location: enfermero_portal.php'); exit; }
|
if (isEnfermero()) { header('Location: enfermero_portal.php'); exit; }
|
||||||
@@ -12,6 +15,20 @@ $adminNombre = $_SESSION['admin_user']['full_name'] ?? $_SESSION['admin_user']['
|
|||||||
|
|
||||||
// Catálogo de módulos disponible desde PHP (para el <script>)
|
// Catálogo de módulos disponible desde PHP (para el <script>)
|
||||||
$systemModules = SYSTEM_MODULES;
|
$systemModules = SYSTEM_MODULES;
|
||||||
|
|
||||||
|
// Lista enriquecida de módulos para el select de home_page (slug → ruta)
|
||||||
|
$systemModulesRaw = [];
|
||||||
|
if (class_exists('ModuleRegistry')) {
|
||||||
|
foreach (ModuleRegistry::getAll() as $slug => $mod) {
|
||||||
|
if (!empty($mod['route'])) {
|
||||||
|
$systemModulesRaw[] = ['name' => $mod['name'], 'route' => $mod['route']];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
foreach (SYSTEM_MODULES as $slug => $label) {
|
||||||
|
$systemModulesRaw[] = ['name' => $label, 'route' => '/' . $slug . '.php'];
|
||||||
|
}
|
||||||
|
}
|
||||||
?>
|
?>
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="es">
|
<html lang="es">
|
||||||
@@ -223,6 +240,23 @@ require_once __DIR__ . '/shared/components/sidebar.php';
|
|||||||
<label class="form-label fw-semibold">Descripción</label>
|
<label class="form-label fw-semibold">Descripción</label>
|
||||||
<textarea id="r_description" class="form-control" rows="2" placeholder="Descripción corta del rol"></textarea>
|
<textarea id="r_description" class="form-control" rows="2" placeholder="Descripción corta del rol"></textarea>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="col-12">
|
||||||
|
<label class="form-label fw-semibold">
|
||||||
|
<i class="fas fa-home me-1 text-primary"></i>
|
||||||
|
Página de inicio al hacer login
|
||||||
|
</label>
|
||||||
|
<select id="r_home_page" class="form-select">
|
||||||
|
<option value="">— Automático (según módulos asignados) —</option>
|
||||||
|
<?php foreach ($systemModulesRaw as $sm): ?>
|
||||||
|
<option value="<?= htmlspecialchars($sm['route'], ENT_QUOTES, 'UTF-8') ?>">
|
||||||
|
<?= htmlspecialchars($sm['name'], ENT_QUOTES, 'UTF-8') ?>
|
||||||
|
(<?= htmlspecialchars($sm['route'], ENT_QUOTES, 'UTF-8') ?>)
|
||||||
|
</option>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
<option value="/index.php">Panel principal (/index.php)</option>
|
||||||
|
</select>
|
||||||
|
<div class="form-text">Al iniciar sesión, el usuario con este rol será redirigido a la página seleccionada.</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<label class="form-label fw-semibold">Módulos permitidos</label>
|
<label class="form-label fw-semibold">Módulos permitidos</label>
|
||||||
@@ -303,6 +337,7 @@ const roles = {
|
|||||||
document.getElementById('r_slug').value = '';
|
document.getElementById('r_slug').value = '';
|
||||||
document.getElementById('r_description').value = '';
|
document.getElementById('r_description').value = '';
|
||||||
document.getElementById('r_color').value = '#0d6efd';
|
document.getElementById('r_color').value = '#0d6efd';
|
||||||
|
document.getElementById('r_home_page').value = '';
|
||||||
document.getElementById('r_error').classList.add('d-none');
|
document.getElementById('r_error').classList.add('d-none');
|
||||||
this._buildModuleGrid([]);
|
this._buildModuleGrid([]);
|
||||||
document.getElementById('modalRolTitulo').textContent = 'Nuevo Rol';
|
document.getElementById('modalRolTitulo').textContent = 'Nuevo Rol';
|
||||||
@@ -317,6 +352,7 @@ const roles = {
|
|||||||
document.getElementById('r_slug').value = r.slug;
|
document.getElementById('r_slug').value = r.slug;
|
||||||
document.getElementById('r_description').value = r.description ?? '';
|
document.getElementById('r_description').value = r.description ?? '';
|
||||||
document.getElementById('r_color').value = r.color ?? '#0d6efd';
|
document.getElementById('r_color').value = r.color ?? '#0d6efd';
|
||||||
|
document.getElementById('r_home_page').value = r.home_page ?? '';
|
||||||
document.getElementById('r_error').classList.add('d-none');
|
document.getElementById('r_error').classList.add('d-none');
|
||||||
this._buildModuleGrid(r.modules);
|
this._buildModuleGrid(r.modules);
|
||||||
document.getElementById('modalRolTitulo').textContent = 'Editar Rol: ' + r.name;
|
document.getElementById('modalRolTitulo').textContent = 'Editar Rol: ' + r.name;
|
||||||
@@ -345,17 +381,18 @@ const roles = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
async guardar() {
|
async guardar() {
|
||||||
const id = document.getElementById('r_id').value;
|
const id = document.getElementById('r_id').value;
|
||||||
const name = document.getElementById('r_name').value.trim();
|
const name = document.getElementById('r_name').value.trim();
|
||||||
const slug = document.getElementById('r_slug').value.trim();
|
const slug = document.getElementById('r_slug').value.trim();
|
||||||
const desc = document.getElementById('r_description').value.trim();
|
const desc = document.getElementById('r_description').value.trim();
|
||||||
const color = document.getElementById('r_color').value;
|
const color = document.getElementById('r_color').value;
|
||||||
const errEl = document.getElementById('r_error');
|
const homePage = document.getElementById('r_home_page').value.trim() || null;
|
||||||
|
const errEl = document.getElementById('r_error');
|
||||||
errEl.classList.add('d-none');
|
errEl.classList.add('d-none');
|
||||||
|
|
||||||
const modules = [...document.querySelectorAll('#r_modules_grid input:checked')].map(el => el.value);
|
const modules = [...document.querySelectorAll('#r_modules_grid input:checked')].map(el => el.value);
|
||||||
|
|
||||||
const body = { name, slug, description: desc, color, modules };
|
const body = { name, slug, description: desc, color, home_page: homePage, modules };
|
||||||
if (id) body.id = parseInt(id);
|
if (id) body.id = parseInt(id);
|
||||||
|
|
||||||
const res = await fetch('api/lab/save_role.php', {
|
const res = await fetch('api/lab/save_role.php', {
|
||||||
|
|||||||
@@ -57,6 +57,18 @@ if ($_POST && !$loginBlocked) {
|
|||||||
// Redirigir según el rol
|
// Redirigir según el rol
|
||||||
$roleSlug = $adminUser['role'] ?? 'admin';
|
$roleSlug = $adminUser['role'] ?? 'admin';
|
||||||
$modules = $adminUser['modules'] ?? [];
|
$modules = $adminUser['modules'] ?? [];
|
||||||
|
|
||||||
|
// 1. Página de inicio configurada explícitamente para el rol
|
||||||
|
if (!empty($adminUser['home_page'])) {
|
||||||
|
$safePath = filter_var($adminUser['home_page'], FILTER_SANITIZE_URL);
|
||||||
|
// Solo rutas relativas (evitar redirects abiertos)
|
||||||
|
if (str_starts_with($safePath, '/') && !str_starts_with($safePath, '//')) {
|
||||||
|
header('Location: ' . $safePath);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Reglas heredadas (compatibilidad hacia atrás)
|
||||||
if ($roleSlug === 'enfermero') {
|
if ($roleSlug === 'enfermero') {
|
||||||
header('Location: enfermero_portal.php');
|
header('Location: enfermero_portal.php');
|
||||||
} elseif (count($modules) === 1 && $modules[0] === 'lab_formularios') {
|
} elseif (count($modules) === 1 && $modules[0] === 'lab_formularios') {
|
||||||
|
|||||||
@@ -0,0 +1,15 @@
|
|||||||
|
-- =============================================================================
|
||||||
|
-- Migración: 20260416_roles_home_page
|
||||||
|
-- Agrega la columna home_page a la tabla roles para definir la página de inicio
|
||||||
|
-- por rol. El valor es la ruta relativa (ej. /lab_dashboard.php, /index.php).
|
||||||
|
-- NULL = comportamiento heredado (login.php decide según reglas antiguas).
|
||||||
|
-- =============================================================================
|
||||||
|
|
||||||
|
ALTER TABLE `roles`
|
||||||
|
ADD COLUMN IF NOT EXISTS `home_page` VARCHAR(200) DEFAULT NULL
|
||||||
|
COMMENT 'Ruta de inicio al hacer login. NULL = usa reglas heredadas.'
|
||||||
|
AFTER `color`;
|
||||||
|
|
||||||
|
-- Registrar migración
|
||||||
|
INSERT IGNORE INTO migrations (migration, executed_at)
|
||||||
|
VALUES ('20260416_roles_home_page', NOW());
|
||||||
@@ -1 +1 @@
|
|||||||
<?php return ['slug'=>'lab_configuracion','name'=>'Configuración Lab','icon'=>'fas fa-sliders-h','category'=>'sistema','route'=>'/lab_configuracion.php','is_active'=>true,'sort_order'=>80,'oleada'=>0,'description'=>'Tarifas, laboratorio y configuraciones del sistema','links'=>[['name'=>'Configuración','icon'=>'fas fa-sliders-h','route'=>'/lab_configuracion.php']]];
|
<?php return ['slug'=>'lab_configuracion','name'=>'Identidad del Lab','icon'=>'fas fa-file-pdf','category'=>'formularios','route'=>'/lab_configuracion.php','is_active'=>true,'sort_order'=>32,'oleada'=>0,'description'=>'Personaliza el encabezado, logo y datos que aparecen en los documentos PDF','links'=>[['name'=>'Identidad del Lab','icon'=>'fas fa-file-pdf','route'=>'/lab_configuracion.php']]];
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
<?php return ['slug'=>'lab_formularios','name'=>'Formularios','icon'=>'fas fa-wpforms','category'=>'lab','route'=>'/lab_formularios.php','is_active'=>true,'sort_order'=>24,'oleada'=>0,'description'=>'Builder de formularios drag-drop con firma digital','links'=>[['name'=>'Formularios','icon'=>'fas fa-wpforms','route'=>'/lab_formularios.php']]];
|
<?php return ['slug'=>'lab_formularios','name'=>'Formularios','icon'=>'fas fa-wpforms','category'=>'formularios','route'=>'/lab_formularios.php','is_active'=>true,'sort_order'=>30,'oleada'=>0,'description'=>'Builder de formularios drag-drop con firma digital','links'=>[['name'=>'Formularios','icon'=>'fas fa-wpforms','route'=>'/lab_formularios.php']]];
|
||||||
|
|||||||
Reference in New Issue
Block a user