195 lines
11 KiB
HTML
195 lines
11 KiB
HTML
{% extends "base.html" %}
|
|
|
|
{% block title %}{% if role %}Editar Rol: {{ role.name }}{% else %}Nuevo Rol{% endif %} - Edu-Space Admin{% endblock %}
|
|
|
|
{% block content %}
|
|
<div class="container-fluid pt-5 mt-4">
|
|
<!-- Header -->
|
|
<div class="d-flex justify-content-between align-items-center mb-4 flex-wrap gap-2">
|
|
<div>
|
|
<h1 class="h3 mb-1">
|
|
<i class="bi bi-shield-lock text-primary me-2"></i>
|
|
{% if role %}Editar Rol y Permisos de Acceso{% else %}Crear Rol y Permisos de Acceso{% endif %}
|
|
</h1>
|
|
<p class="text-muted small mb-0">Configure permisos granulares por módulo con la matriz de acceso estilo FortiGate.</p>
|
|
</div>
|
|
<div>
|
|
<a href="/admin/roles_list" class="btn btn-outline-secondary">
|
|
<i class="bi bi-arrow-left me-1"></i>Volver a Roles
|
|
</a>
|
|
</div>
|
|
</div>
|
|
|
|
<form method="POST">
|
|
{% if role %}
|
|
<input type="hidden" name="id" value="{{ role.id }}">
|
|
{% endif %}
|
|
<div class="row">
|
|
<!-- Left Column: Role Details -->
|
|
<div class="col-lg-4 mb-4">
|
|
<div class="card border-0 shadow-sm h-100">
|
|
<div class="card-header bg-body-tertiary py-3">
|
|
<h5 class="card-title h6 mb-0">
|
|
<i class="bi bi-person-badge text-primary me-2"></i>Información del Rol
|
|
</h5>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="mb-3">
|
|
<label for="name" class="form-label fw-semibold">Nombre del Rol <span class="text-danger">*</span></label>
|
|
<input type="text" class="form-control" id="name" name="name"
|
|
value="{{ role.name if role else '' }}" required
|
|
placeholder="ej. Docente, Operador, Bedelía"
|
|
{% if role and role.is_system %}readonly title="El nombre del rol del sistema no se puede modificar"{% endif %}>
|
|
{% if role and role.is_system %}
|
|
<small class="text-muted d-block mt-1">
|
|
<i class="bi bi-info-circle me-1"></i>Rol del sistema protegido.
|
|
</small>
|
|
{% endif %}
|
|
</div>
|
|
|
|
<div class="mb-3">
|
|
<label for="description" class="form-label fw-semibold">Descripción</label>
|
|
<textarea class="form-control" id="description" name="description" rows="3"
|
|
placeholder="Describa las responsabilidades o alcance de este rol...">{{ role.description if role else '' }}</textarea>
|
|
</div>
|
|
|
|
<hr class="my-4">
|
|
|
|
<div class="d-grid gap-2">
|
|
<button type="submit" class="btn btn-primary py-2 fw-semibold">
|
|
<i class="bi bi-check-lg me-1"></i>Guardar Rol y Permisos
|
|
</button>
|
|
<a href="/admin/roles_list" class="btn btn-outline-secondary">
|
|
Cancelar
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Right Column: FortiGate Permissions Matrix -->
|
|
<div class="col-lg-8 mb-4">
|
|
<div class="fg-permission-card">
|
|
<!-- Matrix Header -->
|
|
<div class="card-header d-flex justify-content-between align-items-center">
|
|
<span class="fw-bold text-primary">
|
|
<i class="bi bi-grid-3x3-gap-fill me-2"></i>Permisos de Acceso
|
|
</span>
|
|
|
|
<!-- Set All Action Dropdown (FortiGate style) -->
|
|
<div class="dropdown">
|
|
<button class="btn btn-fg-set-all dropdown-toggle" type="button" id="setAllDropdown" data-bs-toggle="dropdown" aria-expanded="false">
|
|
<i class="bi bi-lightning-charge me-1"></i>Establecer Todos
|
|
</button>
|
|
<ul class="dropdown-menu dropdown-menu-end shadow-sm" aria-labelledby="setAllDropdown">
|
|
<li>
|
|
<button type="button" class="dropdown-item py-2" onclick="setAllPermissions('none')">
|
|
<i class="bi bi-slash-circle text-danger me-2"></i>Establecer Todos en Ninguno
|
|
</button>
|
|
</li>
|
|
<li>
|
|
<button type="button" class="dropdown-item py-2" onclick="setAllPermissions('read')">
|
|
<i class="bi bi-eye text-info me-2"></i>Establecer Todos en Solo Lectura
|
|
</button>
|
|
</li>
|
|
<li>
|
|
<button type="button" class="dropdown-item py-2" onclick="setAllPermissions('read_write')">
|
|
<i class="bi bi-pencil text-success me-2"></i>Establecer Todos en Lectura y Escritura
|
|
</button>
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Matrix Table -->
|
|
<div class="table-responsive">
|
|
<table class="fg-table">
|
|
<thead>
|
|
<tr>
|
|
<th style="width: 48%;">Módulo del Sistema</th>
|
|
<th style="width: 52%;" class="text-end pe-4">Nivel de Permiso</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{% for mod in modules %}
|
|
{% set cur_level = (current_perms[mod.id] if current_perms else (role.permissions[mod.id] if (role and role.permissions) else 'none')) or 'none' %}
|
|
<tr data-module-row="{{ mod.id }}">
|
|
<td>
|
|
<div class="d-flex align-items-center">
|
|
<div class="bg-primary bg-opacity-10 text-primary rounded p-2 me-3 d-inline-flex align-items-center justify-content-center" style="width: 36px; height: 36px;">
|
|
<i class="bi {{ mod.icon }} fs-6"></i>
|
|
</div>
|
|
<div>
|
|
<div class="fw-semibold">{{ mod.name }}</div>
|
|
<small class="text-muted d-block">{{ mod.description }}</small>
|
|
</div>
|
|
</div>
|
|
</td>
|
|
<td class="text-end pe-4">
|
|
<!-- Hidden Input to submit via POST -->
|
|
<input type="hidden" name="perm_{{ mod.id }}" id="input_perm_{{ mod.id }}" value="{{ cur_level }}">
|
|
|
|
<!-- FortiGate Segmented Control -->
|
|
<div class="fg-segmented-control" role="group" aria-label="Nivel de permiso para {{ mod.name }}">
|
|
<button type="button"
|
|
class="fg-seg-btn {% if cur_level == 'none' %}active{% endif %}"
|
|
data-val="none"
|
|
data-target="input_perm_{{ mod.id }}"
|
|
onclick="selectPerm(this, 'input_perm_{{ mod.id }}', 'none')">
|
|
<i class="bi bi-slash-circle"></i> Ninguno
|
|
</button>
|
|
<button type="button"
|
|
class="fg-seg-btn {% if cur_level == 'read' %}active{% endif %}"
|
|
data-val="read"
|
|
data-target="input_perm_{{ mod.id }}"
|
|
onclick="selectPerm(this, 'input_perm_{{ mod.id }}', 'read')">
|
|
<i class="bi bi-eye"></i> Lectura
|
|
</button>
|
|
<button type="button"
|
|
class="fg-seg-btn {% if cur_level == 'read_write' %}active{% endif %}"
|
|
data-val="read_write"
|
|
data-target="input_perm_{{ mod.id }}"
|
|
onclick="selectPerm(this, 'input_perm_{{ mod.id }}', 'read_write')">
|
|
<i class="bi bi-pencil"></i> Lectura/Escritura
|
|
</button>
|
|
</div>
|
|
</td>
|
|
</tr>
|
|
{% endfor %}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
{% endblock %}
|
|
|
|
{% block extra_js %}
|
|
<script>
|
|
function selectPerm(btn, inputId, value) {
|
|
const input = document.getElementById(inputId);
|
|
if (input) {
|
|
input.value = value;
|
|
}
|
|
|
|
// Toggle active state in this segmented control
|
|
const parentControl = btn.closest('.fg-segmented-control');
|
|
if (parentControl) {
|
|
parentControl.querySelectorAll('.fg-seg-btn').forEach(b => b.classList.remove('active'));
|
|
btn.classList.add('active');
|
|
}
|
|
}
|
|
|
|
function setAllPermissions(level) {
|
|
document.querySelectorAll('.fg-segmented-control').forEach(control => {
|
|
const targetBtn = control.querySelector(`button[data-val="${level}"]`);
|
|
if (targetBtn) {
|
|
targetBtn.click();
|
|
}
|
|
});
|
|
}
|
|
</script>
|
|
{% endblock %}
|