191 lines
7.7 KiB
PHP
191 lines
7.7 KiB
PHP
<?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();
|