154 lines
8.1 KiB
HTML
154 lines
8.1 KiB
HTML
{% extends "base.html" %}
|
|
|
|
{% block title %}Roles y Grupos de Acceso - 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-person-badge text-primary me-2"></i>Roles y Grupos de Acceso
|
|
</h1>
|
|
<p class="text-muted small mb-0">Administración de perfiles de usuario y matriz granular de permisos por módulo del sistema.</p>
|
|
</div>
|
|
<div>
|
|
<a href="/admin/role_add" class="btn btn-primary">
|
|
<i class="bi bi-plus-circle me-1"></i>Nuevo Rol
|
|
</a>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Roles Grid / Table -->
|
|
<div class="card border-0 shadow-sm">
|
|
<div class="table-responsive">
|
|
<table class="table table-hover align-middle mb-0">
|
|
<thead class="bg-body-tertiary">
|
|
<tr>
|
|
<th class="ps-4">Nombre del Rol</th>
|
|
<th>Descripción</th>
|
|
<th>Integrantes</th>
|
|
<th>Resumen de Permisos</th>
|
|
<th class="text-end pe-4">Acciones</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{% for role in roles %}
|
|
<tr>
|
|
<td class="ps-4">
|
|
<div class="d-flex align-items-center">
|
|
<div class="badge-brand rounded-circle p-2 me-3 d-inline-flex align-items-center justify-content-center" style="width: 38px; height: 38px;">
|
|
<i class="bi bi-shield-check fs-6 text-white"></i>
|
|
</div>
|
|
<div>
|
|
<div class="fw-bold text-primary">{{ role.name }}</div>
|
|
{% if role.is_system %}
|
|
<span class="badge bg-secondary-subtle text-secondary border small">Protegido del Sistema</span>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
</td>
|
|
<td>
|
|
<span class="text-muted small">{{ role.description or _('Sin descripción provista.') }}</span>
|
|
</td>
|
|
<td>
|
|
<span class="badge bg-primary-subtle text-primary border px-2 py-1">
|
|
<i class="bi bi-people me-1"></i>{{ role.user_count or (role.users and role.users|length) or 0 }} usuarios
|
|
</span>
|
|
</td>
|
|
<td>
|
|
<div class="d-flex flex-wrap gap-1">
|
|
{% for mod in modules %}
|
|
{% set p_level = role.permissions[mod.id] if role.permissions else 'none' %}
|
|
{% if p_level == 'read_write' %}
|
|
<span class="badge bg-success-subtle text-success border small" title="{{ mod.name }}: Lectura y Escritura">
|
|
<i class="bi {{ mod.icon }} me-1"></i>{{ mod.name }}: RW
|
|
</span>
|
|
{% elif p_level == 'read' %}
|
|
<span class="badge bg-info-subtle text-info border small" title="{{ mod.name }}: Solo Lectura">
|
|
<i class="bi {{ mod.icon }} me-1"></i>{{ mod.name }}: R
|
|
</span>
|
|
{% else %}
|
|
<span class="badge bg-light text-muted border small opacity-75" title="{{ mod.name }}: Sin Acceso">
|
|
<i class="bi {{ mod.icon }} me-1"></i>{{ mod.name }}: -
|
|
</span>
|
|
{% endif %}
|
|
{% endfor %}
|
|
</div>
|
|
</td>
|
|
<td class="text-end pe-4">
|
|
<div class="btn-group btn-group-sm">
|
|
<a href="/admin/roles/edit/{{ role.id }}" class="btn btn-outline-primary" title="Configurar Permisos">
|
|
<i class="bi bi-pencil-square me-1"></i>Permisos
|
|
</a>
|
|
{% if not role.is_system %}
|
|
<button type="button" class="btn btn-outline-danger btn-delete-role"
|
|
data-role-id="{{ role.id }}"
|
|
data-role-name="{{ role.name }}"
|
|
data-action-url="/admin/role_delete"
|
|
title="Eliminar Rol"
|
|
{% if (role.user_count or 0) > 0 or (role.users and role.users|length > 0) %}disabled{% endif %}>
|
|
<i class="bi bi-trash"></i>
|
|
</button>
|
|
{% endif %}
|
|
</div>
|
|
</td>
|
|
</tr>
|
|
{% endfor %}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Modal Global de Eliminación fuera de la tabla (evita desplazamientos visuales) -->
|
|
<div class="modal fade" id="deleteRoleModal" tabindex="-1" aria-hidden="true" data-bs-backdrop="static">
|
|
<div class="modal-dialog modal-dialog-centered">
|
|
<div class="modal-content border-0 shadow-lg" style="border-radius: 14px;">
|
|
<div class="modal-header border-0 pb-0">
|
|
<div class="d-flex align-items-center">
|
|
<div class="badge bg-danger-subtle text-danger p-2 rounded-circle me-2">
|
|
<i class="bi bi-exclamation-triangle-fill fs-5"></i>
|
|
</div>
|
|
<h5 class="modal-title fw-bold text-danger mb-0">Confirmar Eliminación de Rol</h5>
|
|
</div>
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
|
</div>
|
|
<div class="modal-body text-start py-3">
|
|
<p class="mb-2">¿Está seguro de que desea eliminar el rol <strong id="deleteRoleName" class="text-primary"></strong>?</p>
|
|
<p class="text-muted small mb-0"><i class="bi bi-info-circle me-1"></i>Esta acción no se puede deshacer.</p>
|
|
</div>
|
|
<div class="modal-footer border-0 pt-0">
|
|
<button type="button" class="btn btn-outline-secondary" data-bs-dismiss="modal">Cancelar</button>
|
|
<form id="deleteRoleForm" method="POST" class="d-inline">
|
|
<button type="submit" class="btn btn-danger px-3">
|
|
<i class="bi bi-trash me-1"></i>Eliminar Rol
|
|
</button>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% endblock %}
|
|
|
|
{% block extra_js %}
|
|
<script>
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
var deleteModalEl = document.getElementById('deleteRoleModal');
|
|
var deleteModal = deleteModalEl ? new bootstrap.Modal(deleteModalEl) : null;
|
|
var deleteRoleName = document.getElementById('deleteRoleName');
|
|
var deleteRoleForm = document.getElementById('deleteRoleForm');
|
|
|
|
document.querySelectorAll('.btn-delete-role').forEach(function(btn) {
|
|
btn.addEventListener('click', function() {
|
|
var roleName = this.getAttribute('data-role-name');
|
|
var actionUrl = this.getAttribute('data-action-url');
|
|
if (deleteRoleName) deleteRoleName.textContent = roleName;
|
|
if (deleteRoleForm) deleteRoleForm.action = actionUrl;
|
|
if (deleteModal) deleteModal.show();
|
|
});
|
|
});
|
|
});
|
|
</script>
|
|
{% endblock %}
|