up
This commit is contained in:
@@ -0,0 +1 @@
|
||||
<?php return ['slug'=>'enfermero_portal','name'=>'Portal Enfermero','icon'=>'fas fa-stethoscope','category'=>'sistema','route'=>'/enfermero_portal.php','is_active'=>true,'sort_order'=>90,'oleada'=>0,'description'=>'Portal exclusivo para el personal de enfermería','links'=>[['name'=>'Portal Enfermero','icon'=>'fas fa-stethoscope','route'=>'/enfermero_portal.php']]];
|
||||
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
/**
|
||||
* modules/enfermero_portal/views/index.php
|
||||
* Bridge al archivo legacy mientras se migra al nuevo sistema ERP.
|
||||
* Acceso via: /erp.php?m=enfermero_portal&v=index o /enfermero_portal/index
|
||||
*/
|
||||
if (!defined('APP_ROOT')) {
|
||||
define('APP_ROOT', dirname(__DIR__, 3));
|
||||
}
|
||||
require_once APP_ROOT . '/enfermero_portal.php';
|
||||
@@ -0,0 +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']]];
|
||||
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
/**
|
||||
* modules/lab_configuracion/views/index.php
|
||||
* Bridge al archivo legacy mientras se migra al nuevo sistema ERP.
|
||||
* Acceso via: /erp.php?m=lab_configuracion&v=index o /lab_configuracion/index
|
||||
*/
|
||||
if (!defined('APP_ROOT')) {
|
||||
define('APP_ROOT', dirname(__DIR__, 3));
|
||||
}
|
||||
require_once APP_ROOT . '/lab_configuracion.php';
|
||||
@@ -0,0 +1 @@
|
||||
<?php return ['slug'=>'lab_dashboard','name'=>'Dashboard Lab','icon'=>'fas fa-tachometer-alt','category'=>'lab','route'=>'/lab_dashboard.php','is_active'=>true,'sort_order'=>10,'oleada'=>0,'description'=>'Panel principal del módulo de laboratorio','links'=>[['name'=>'Dashboard','icon'=>'fas fa-tachometer-alt','route'=>'/lab_dashboard.php']]];
|
||||
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
/**
|
||||
* modules/lab_dashboard/views/index.php
|
||||
* Bridge al archivo legacy mientras se migra al nuevo sistema ERP.
|
||||
* Acceso via: /erp.php?m=lab_dashboard&v=index o /lab_dashboard/index
|
||||
*/
|
||||
if (!defined('APP_ROOT')) {
|
||||
define('APP_ROOT', dirname(__DIR__, 3));
|
||||
}
|
||||
require_once APP_ROOT . '/lab_dashboard.php';
|
||||
@@ -0,0 +1 @@
|
||||
<?php return ['slug'=>'lab_domicilios','name'=>'Domicilios','icon'=>'fas fa-house-medical','category'=>'lab','route'=>'/lab_domicilios.php','is_active'=>true,'sort_order'=>20,'oleada'=>0,'description'=>'Agenda y gestión de servicios a domicilio','links'=>[['name'=>'Domicilios','icon'=>'fas fa-house-medical','route'=>'/lab_domicilios.php']]];
|
||||
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
/**
|
||||
* modules/lab_domicilios/views/index.php
|
||||
* Bridge al archivo legacy mientras se migra al nuevo sistema ERP.
|
||||
* Acceso via: /erp.php?m=lab_domicilios&v=index o /lab_domicilios/index
|
||||
*/
|
||||
if (!defined('APP_ROOT')) {
|
||||
define('APP_ROOT', dirname(__DIR__, 3));
|
||||
}
|
||||
require_once APP_ROOT . '/lab_domicilios.php';
|
||||
@@ -0,0 +1 @@
|
||||
<?php return ['slug'=>'lab_enfermeras','name'=>'Enfermeros','icon'=>'fas fa-user-nurse','category'=>'lab','route'=>'/lab_enfermeras.php','is_active'=>true,'sort_order'=>22,'oleada'=>0,'description'=>'Gestión de personal clínico y enfermeros','links'=>[['name'=>'Enfermeros','icon'=>'fas fa-user-nurse','route'=>'/lab_enfermeras.php']]];
|
||||
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
/**
|
||||
* modules/lab_enfermeras/views/index.php
|
||||
* Bridge al archivo legacy mientras se migra al nuevo sistema ERP.
|
||||
* Acceso via: /erp.php?m=lab_enfermeras&v=index o /lab_enfermeras/index
|
||||
*/
|
||||
if (!defined('APP_ROOT')) {
|
||||
define('APP_ROOT', dirname(__DIR__, 3));
|
||||
}
|
||||
require_once APP_ROOT . '/lab_enfermeras.php';
|
||||
@@ -0,0 +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']]];
|
||||
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
/**
|
||||
* modules/lab_formularios/views/index.php
|
||||
* Bridge al archivo legacy mientras se migra al nuevo sistema ERP.
|
||||
* Acceso via: /erp.php?m=lab_formularios&v=index o /lab_formularios/index
|
||||
*/
|
||||
if (!defined('APP_ROOT')) {
|
||||
define('APP_ROOT', dirname(__DIR__, 3));
|
||||
}
|
||||
require_once APP_ROOT . '/lab_formularios.php';
|
||||
@@ -0,0 +1 @@
|
||||
<?php return ['slug'=>'lab_ordenes','name'=>'Órdenes Médicas','icon'=>'fas fa-file-medical','category'=>'lab','route'=>'/lab_ordenes.php','is_active'=>true,'sort_order'=>23,'oleada'=>0,'description'=>'Gestión de órdenes médicas con PDF y autorización','links'=>[['name'=>'Órdenes','icon'=>'fas fa-file-medical','route'=>'/lab_ordenes.php']]];
|
||||
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
/**
|
||||
* modules/lab_ordenes/views/index.php
|
||||
* Bridge al archivo legacy mientras se migra al nuevo sistema ERP.
|
||||
* Acceso via: /erp.php?m=lab_ordenes&v=index o /lab_ordenes/index
|
||||
*/
|
||||
if (!defined('APP_ROOT')) {
|
||||
define('APP_ROOT', dirname(__DIR__, 3));
|
||||
}
|
||||
require_once APP_ROOT . '/lab_ordenes.php';
|
||||
@@ -0,0 +1 @@
|
||||
<?php return ['slug'=>'lab_pacientes','name'=>'Pacientes','icon'=>'fas fa-users','category'=>'lab','route'=>'/lab_pacientes.php','is_active'=>true,'sort_order'=>21,'oleada'=>0,'description'=>'Fichas clínicas e historial de pacientes','links'=>[['name'=>'Pacientes','icon'=>'fas fa-users','route'=>'/lab_pacientes.php']]];
|
||||
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
/**
|
||||
* modules/lab_pacientes/views/index.php
|
||||
* Bridge al archivo legacy mientras se migra al nuevo sistema ERP.
|
||||
* Acceso via: /erp.php?m=lab_pacientes&v=index o /lab_pacientes/index
|
||||
*/
|
||||
if (!defined('APP_ROOT')) {
|
||||
define('APP_ROOT', dirname(__DIR__, 3));
|
||||
}
|
||||
require_once APP_ROOT . '/lab_pacientes.php';
|
||||
@@ -0,0 +1 @@
|
||||
<?php return ['slug'=>'lab_reportes','name'=>'Reportes','icon'=>'fas fa-chart-bar','category'=>'reportes','route'=>'/lab_reportes.php','is_active'=>true,'sort_order'=>50,'oleada'=>0,'description'=>'Reportes de ingresos, rendimiento y pagos','links'=>[['name'=>'Reportes','icon'=>'fas fa-chart-bar','route'=>'/lab_reportes.php'],['name'=>'Actividad','icon'=>'fas fa-history','route'=>'/lab_actividad.php']]];
|
||||
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
/**
|
||||
* modules/lab_reportes/views/index.php
|
||||
* Bridge al archivo legacy mientras se migra al nuevo sistema ERP.
|
||||
* Acceso via: /erp.php?m=lab_reportes&v=index o /lab_reportes/index
|
||||
*/
|
||||
if (!defined('APP_ROOT')) {
|
||||
define('APP_ROOT', dirname(__DIR__, 3));
|
||||
}
|
||||
require_once APP_ROOT . '/lab_reportes.php';
|
||||
@@ -0,0 +1 @@
|
||||
<?php return ['slug'=>'usuarios','name'=>'Usuarios & Roles','icon'=>'fas fa-users-cog','category'=>'sistema','route'=>'/lab_usuarios.php','is_active'=>true,'sort_order'=>81,'oleada'=>0,'description'=>'Gestión de usuarios del sistema y asignación de roles','links'=>[['name'=>'Usuarios & Roles','icon'=>'fas fa-users-cog','route'=>'/lab_usuarios.php'],['name'=>'Módulos','icon'=>'fas fa-th-large','route'=>'/erp.php?m=usuarios&v=modulos']]];
|
||||
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
/**
|
||||
* modules/usuarios/views/index.php
|
||||
* Bridge al archivo legacy mientras se migra al nuevo sistema ERP.
|
||||
* Acceso via: /erp.php?m=usuarios&v=index o /usuarios/index
|
||||
*/
|
||||
if (!defined('APP_ROOT')) {
|
||||
define('APP_ROOT', dirname(__DIR__, 3));
|
||||
}
|
||||
require_once APP_ROOT . '/lab_usuarios.php';
|
||||
@@ -0,0 +1,190 @@
|
||||
<?php
|
||||
/**
|
||||
* modules/usuarios/views/modulos.php
|
||||
* Interfaz de administración de módulos del ERP.
|
||||
* Acceso: /erp.php?m=usuarios&v=modulos
|
||||
*
|
||||
* Permite al superadmin / admin:
|
||||
* - Ver todos los módulos registrados
|
||||
* - Activar / desactivar módulos con toggle
|
||||
* - Re-ordenar sort_order
|
||||
*/
|
||||
|
||||
if (!defined('APP_ROOT')) {
|
||||
define('APP_ROOT', dirname(__DIR__, 3));
|
||||
}
|
||||
|
||||
require_once APP_ROOT . '/config/config.php';
|
||||
require_once APP_ROOT . '/core/Auth.php';
|
||||
require_once APP_ROOT . '/core/Rbac.php';
|
||||
require_once APP_ROOT . '/core/Helpers.php';
|
||||
require_once APP_ROOT . '/core/ModuleRegistry.php';
|
||||
require_once APP_ROOT . '/core/Layout.php';
|
||||
|
||||
Auth::requireLogin();
|
||||
Rbac::requireModule('usuarios');
|
||||
|
||||
// ── Manejador de acciones AJAX ────────────────────────────────────────────────
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
header('Content-Type: application/json; charset=utf-8');
|
||||
$body = inputJson();
|
||||
$action = $body['action'] ?? '';
|
||||
|
||||
switch ($action) {
|
||||
case 'toggle':
|
||||
$slug = preg_replace('/[^a-z0-9_]/', '', $body['slug'] ?? '');
|
||||
$active = (bool)($body['active'] ?? false);
|
||||
if (!$slug) { jsonError('Slug inválido'); }
|
||||
$ok = ModuleRegistry::setActive($slug, $active);
|
||||
$ok ? jsonOk([], $active ? 'Módulo activado' : 'Módulo desactivado') : jsonError('No se pudo actualizar', 500);
|
||||
break;
|
||||
|
||||
case 'reorder':
|
||||
$slug = preg_replace('/[^a-z0-9_]/', '', $body['slug'] ?? '');
|
||||
$order = (int)($body['order'] ?? 99);
|
||||
if (!$slug) { jsonError('Slug inválido'); }
|
||||
$ok = ModuleRegistry::setSortOrder($slug, $order);
|
||||
$ok ? jsonOk([], 'Orden actualizado') : jsonError('No se pudo actualizar', 500);
|
||||
break;
|
||||
|
||||
default:
|
||||
jsonError('Acción desconocida');
|
||||
}
|
||||
exit;
|
||||
}
|
||||
|
||||
// ── Vista HTML ────────────────────────────────────────────────────────────────
|
||||
$modules = ModuleRegistry::getAll();
|
||||
|
||||
// Etiquetas de oleada
|
||||
$oleadaLabel = [0 => 'Producción', 1 => 'Oleada 1', 2 => 'Oleada 2'];
|
||||
$oleadaBadge = [0 => 'bg-success', 1 => 'bg-primary', 2 => 'bg-secondary'];
|
||||
|
||||
Layout::open('Gestión de Módulos', 'fas fa-th-large');
|
||||
?>
|
||||
|
||||
<div class="d-flex justify-content-between align-items-center mb-3">
|
||||
<p class="text-muted mb-0">
|
||||
Activa o desactiva módulos del ERP. Los módulos desactivados no aparecen en el sidebar
|
||||
ni son accesibles para ningún usuario.
|
||||
</p>
|
||||
<button class="btn btn-outline-secondary btn-sm" onclick="location.reload()">
|
||||
<i class="fas fa-sync-alt me-1"></i>Recargar
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Tabla de módulos -->
|
||||
<div class="card shadow-sm">
|
||||
<div class="card-body p-0">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover mb-0 align-middle">
|
||||
<thead class="table-light">
|
||||
<tr>
|
||||
<th>Módulo</th>
|
||||
<th>Slug</th>
|
||||
<th>Categoría</th>
|
||||
<th>Oleada</th>
|
||||
<th class="text-center">Orden</th>
|
||||
<th class="text-center">Activo</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php
|
||||
$sorted = $modules;
|
||||
uasort($sorted, fn($a, $b) => [$a['oleada'], $a['sort_order']] <=> [$b['oleada'], $b['sort_order']]);
|
||||
foreach ($sorted as $slug => $mod):
|
||||
$active = $mod['is_active'];
|
||||
$oleadaIdx = (int)($mod['oleada'] ?? 0);
|
||||
$badgeClass = $oleadaBadge[$oleadaIdx] ?? 'bg-secondary';
|
||||
$badgeText = $oleadaLabel[$oleadaIdx] ?? "Oleada $oleadaIdx";
|
||||
?>
|
||||
<tr data-slug="<?= esc($slug) ?>">
|
||||
<td>
|
||||
<i class="<?= esc($mod['icon']) ?> me-2 text-muted"></i>
|
||||
<strong><?= esc($mod['name']) ?></strong>
|
||||
<?php if ($mod['description']): ?>
|
||||
<br><small class="text-muted"><?= esc($mod['description']) ?></small>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
<td><code class="small"><?= esc($slug) ?></code></td>
|
||||
<td><span class="badge bg-light text-dark border"><?= esc($mod['category']) ?></span></td>
|
||||
<td><span class="badge <?= $badgeClass ?>"><?= esc($badgeText) ?></span></td>
|
||||
<td class="text-center">
|
||||
<input type="number" class="form-control form-control-sm text-center order-input"
|
||||
value="<?= (int)$mod['sort_order'] ?>" min="1" max="999"
|
||||
data-slug="<?= esc($slug) ?>" style="width:70px;margin:auto;">
|
||||
</td>
|
||||
<td class="text-center">
|
||||
<div class="form-check form-switch d-inline-block">
|
||||
<input class="form-check-input toggle-module" type="checkbox"
|
||||
role="switch"
|
||||
id="mod-<?= esc($slug) ?>"
|
||||
data-slug="<?= esc($slug) ?>"
|
||||
<?= $active ? 'checked' : '' ?>
|
||||
style="cursor:pointer;width:2.5rem;height:1.25rem;">
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p class="text-muted small mt-3">
|
||||
<i class="fas fa-info-circle me-1"></i>
|
||||
Los cambios se aplican de inmediato. Si un módulo desactivado tiene permisos asignados en roles,
|
||||
esos permisos se conservan en BD pero el módulo no será accesible hasta reactivarlo.
|
||||
</p>
|
||||
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
|
||||
// Toggle activo/inactivo
|
||||
document.querySelectorAll('.toggle-module').forEach(toggle => {
|
||||
toggle.addEventListener('change', async function () {
|
||||
const slug = this.dataset.slug;
|
||||
const active = this.checked;
|
||||
this.disabled = true;
|
||||
try {
|
||||
await erp.fetch('', {
|
||||
method: 'POST',
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
body: JSON.stringify({action: 'toggle', slug, active})
|
||||
});
|
||||
erp.toast(active ? `Módulo "${slug}" activado` : `Módulo "${slug}" desactivado`,
|
||||
active ? 'success' : 'warning');
|
||||
} catch (e) {
|
||||
this.checked = !active; // revertir
|
||||
erp.toast('Error al actualizar: ' + e.message, 'danger');
|
||||
} finally {
|
||||
this.disabled = false;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Cambiar orden (al perder el foco)
|
||||
document.querySelectorAll('.order-input').forEach(input => {
|
||||
input.addEventListener('change', async function () {
|
||||
const slug = this.dataset.slug;
|
||||
const order = parseInt(this.value, 10);
|
||||
if (isNaN(order) || order < 1) return;
|
||||
try {
|
||||
await erp.fetch('', {
|
||||
method: 'POST',
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
body: JSON.stringify({action: 'reorder', slug, order})
|
||||
});
|
||||
erp.toast(`Orden de "${slug}" actualizado`, 'info');
|
||||
} catch (e) {
|
||||
erp.toast('Error al actualizar: ' + e.message, 'danger');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
</script>
|
||||
|
||||
<?php
|
||||
Layout::close();
|
||||
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
/**
|
||||
* modules/whatsapp/module.php
|
||||
* Descriptor del módulo WhatsApp Bot.
|
||||
*/
|
||||
return [
|
||||
'slug' => 'whatsapp',
|
||||
'name' => 'WhatsApp Bot',
|
||||
'icon' => 'fab fa-whatsapp',
|
||||
'category' => 'bot',
|
||||
'route' => '/index.php',
|
||||
'is_active' => true,
|
||||
'sort_order' => 1,
|
||||
'oleada' => 0,
|
||||
'description' => 'Bot, conversaciones, plantillas y menús de WhatsApp',
|
||||
'links' => [
|
||||
['name' => 'Conversaciones', 'icon' => 'fas fa-comments', 'route' => '/conversations.php'],
|
||||
['name' => 'Bot & Menús', 'icon' => 'fas fa-robot', 'route' => '/index.php'],
|
||||
['name' => 'Plantillas', 'icon' => 'fas fa-file-alt', 'route' => '/index.php#templates'],
|
||||
['name' => 'Programados', 'icon' => 'fas fa-clock', 'route' => '/scheduled_messages.php'],
|
||||
],
|
||||
];
|
||||
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
/**
|
||||
* modules/whatsapp/views/index.php
|
||||
* Bridge al archivo legacy mientras se migra al nuevo sistema ERP.
|
||||
* Acceso via: /erp.php?m=whatsapp&v=index o /whatsapp/index
|
||||
*/
|
||||
if (!defined('APP_ROOT')) {
|
||||
define('APP_ROOT', dirname(__DIR__, 3));
|
||||
}
|
||||
require_once APP_ROOT . '/index.php';
|
||||
Reference in New Issue
Block a user