roles_list.html
140 lignes · 6578 octets
{% extends "base.html" %} {% block title %}{{ page_title }}{% endblock %} {% block navbar %} {% include "components/navbar.html" %} {% endblock %} {% block content %} <div class="container mx-auto px-4 py-8"> <!-- Breadcrumb --> <div class="text-sm breadcrumbs mb-6"> <ul> <li><a href="/">{{ breadcrumb_home }}</a></li> <li><a href="/admin">{{ breadcrumb_admin }}</a></li> <li>{{ breadcrumb_roles }}</li> </ul> </div> <div class="flex justify-between items-center mb-6"> <h1 class="text-3xl font-bold">{{ title }}</h1> <a href="/admin/roles/create" class="btn btn-primary"> <svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 mr-2" fill="none" viewBox="0 0 24 24" stroke="currentColor"> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4v16m8-8H4" /> </svg> {{ create_role }} </a> </div> <!-- Roles Table --> <div class="card bg-base-100 shadow-xl"> <div class="card-body p-0"> {% if roles.is_empty() %} <div class="p-8 text-center text-base-content/60"> <svg xmlns="http://www.w3.org/2000/svg" class="h-12 w-12 mx-auto mb-4 opacity-40" fill="none" viewBox="0 0 24 24" stroke="currentColor"> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m5.618-4.016A11.955 11.955 0 0112 2.944a11.955 11.955 0 01-8.618 3.04A12.02 12.02 0 003 9c0 5.591 3.824 10.29 9 11.622 5.176-1.332 9-6.03 9-11.622 0-1.042-.133-2.052-.382-3.016z" /> </svg> <p>{{ no_roles }}</p> </div> {% else %} <div class="overflow-x-auto"> <table class="table"> <thead> <tr> <th>{{ table_name }}</th> <th>{{ table_description }}</th> <th class="text-center">{{ table_users }}</th> <th class="text-center">{{ table_permissions }}</th> <th class="text-right">{{ table_actions }}</th> </tr> </thead> <tbody> {% for role in roles %} <tr> <td> <div class="font-bold">{{ role.name }}</div> {% if role.is_protected %} <span class="badge badge-ghost badge-sm">{{ protected_hint }}</span> {% endif %} </td> <td>{{ role.description }}</td> <td class="text-center"> <span class="badge badge-outline">{{ role.user_count }}</span> </td> <td class="text-center"> <span class="badge badge-outline">{{ role.permission_count }}</span> </td> <td class="text-right"> <div class="flex justify-end gap-2"> <a href="/admin/roles/{{ role.id }}" class="btn btn-ghost btn-sm"> <svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor"> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z" /> </svg> {{ edit }} </a> {% if !role.is_protected %} <button class="btn btn-ghost btn-sm text-error btn-delete-role" data-role-id="{{ role.id }}" data-role-name="{{ role.name }}"> <svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor"> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" /> </svg> {{ delete_label }} </button> {% endif %} </div> </td> </tr> {% endfor %} </tbody> </table> </div> {% endif %} </div> </div> </div> <!-- Delete Confirmation Modal --> <dialog id="deleteRoleModal" class="modal"> <div class="modal-box"> <h3 class="text-lg font-bold">{{ delete_confirm_title }}</h3> <p class="py-4">{{ delete_confirm_message }} <strong id="deleteRoleName"></strong>?</p> <div class="modal-action"> <form method="dialog"> <button class="btn">{{ cancel }}</button> </form> <button id="confirmDeleteBtn" class="btn btn-error">{{ delete_button }}</button> </div> </div> <form method="dialog" class="modal-backdrop"> <button>close</button> </form> </dialog> <script> let deleteRoleId = null; // SEC-FIX: Use data-* attributes instead of inline onclick to prevent XSS document.querySelectorAll('.btn-delete-role').forEach(function(btn) { btn.addEventListener('click', function() { deleteRoleId = this.dataset.roleId; document.getElementById('deleteRoleName').textContent = this.dataset.roleName; document.getElementById('deleteRoleModal').showModal(); }); }); document.getElementById('confirmDeleteBtn').addEventListener('click', function() { if (!deleteRoleId) return; fetch('/admin/roles/' + deleteRoleId, { method: 'DELETE', headers: { 'X-CSRF-Token': '{{ csrf_token }}', } }).then(function() { window.location.reload(); }); document.getElementById('deleteRoleModal').close(); }); </script> {% endblock %}
GitRust