Spaces:
Sleeping
Sleeping
| {% extends "layout.html" %} | |
| {% block title %}Quản lý tài khoản - Admin Portal{% endblock %} | |
| {% block styles %} | |
| <style> | |
| .admin-card { | |
| border-radius: var(--radius-xl); | |
| border: 1px solid var(--color-border); | |
| background: #ffffff; | |
| box-shadow: 0 4px 20px rgba(0, 0, 0, 0.02); | |
| } | |
| .role-badge { | |
| font-weight: 700; | |
| padding: 4px 10px; | |
| border-radius: 6px; | |
| font-size: 0.72rem; | |
| text-transform: uppercase; | |
| display: inline-block; | |
| } | |
| .role-admin { | |
| background-color: #dbeafe; | |
| color: #1d4ed8; | |
| border: 1px solid #bfdbfe; | |
| } | |
| .role-user { | |
| background-color: #f1f5f9; | |
| color: #475569; | |
| border: 1px solid #e2e8f0; | |
| } | |
| .table-actions { | |
| display: flex; | |
| gap: 8px; | |
| justify-content: flex-end; | |
| } | |
| .btn-action-edit { | |
| color: var(--color-primary); | |
| background: rgba(59, 130, 246, 0.1); | |
| border: none; | |
| padding: 6px 12px; | |
| border-radius: 6px; | |
| font-size: 0.8rem; | |
| font-weight: 600; | |
| transition: all 0.15s; | |
| } | |
| .btn-action-edit:hover { | |
| background: var(--color-primary); | |
| color: #ffffff; | |
| } | |
| .btn-action-delete { | |
| color: #ef4444; | |
| background: rgba(239, 68, 68, 0.1); | |
| border: none; | |
| padding: 6px 12px; | |
| border-radius: 6px; | |
| font-size: 0.8rem; | |
| font-weight: 600; | |
| transition: all 0.15s; | |
| } | |
| .btn-action-delete:hover { | |
| background: #ef4444; | |
| color: #ffffff; | |
| } | |
| </style> | |
| {% endblock %} | |
| {% block content %} | |
| <div class="d-flex align-items-center justify-content-between mb-4"> | |
| <div> | |
| <h3 class="fw-bold mb-1"><i class="fas fa-users-cog text-primary me-2"></i>Quản lý tài khoản</h3> | |
| <p class="text-muted mb-0 small">Khu vực quản trị cấp tài khoản và cấu hình quyền hạn cho hệ thống.</p> | |
| </div> | |
| <button class="btn-primary-custom px-4" data-bs-toggle="modal" data-bs-target="#createModal"> | |
| <i class="fas fa-plus me-2"></i>Thêm tài khoản | |
| </button> | |
| </div> | |
| <div class="card admin-card"> | |
| <div class="card-body p-4"> | |
| <div class="table-responsive"> | |
| <table class="table table-hover align-middle mb-0"> | |
| <thead> | |
| <tr class="text-muted small uppercase" style="letter-spacing: 0.05em;"> | |
| <th style="width: 80px;">ID</th> | |
| <th>Email/Tài khoản</th> | |
| <th>Vai trò</th> | |
| <th>Ngày tạo</th> | |
| <th class="text-end" style="width: 180px;">Hành động</th> | |
| </tr> | |
| </thead> | |
| <tbody> | |
| {% for user in users %} | |
| <tr> | |
| <td class="font-monospace text-muted">{{ user.id }}</td> | |
| <td> | |
| <div class="fw-bold text-dark">{{ user.email }}</div> | |
| </td> | |
| <td> | |
| {% if user.role == 'admin' %} | |
| <span class="role-badge role-admin"><i class="fas fa-shield-alt me-1"></i>Admin</span> | |
| {% else %} | |
| <span class="role-badge role-user"><i class="fas fa-user me-1"></i>User</span> | |
| {% endif %} | |
| </td> | |
| <td class="text-muted small"> | |
| {{ user.created_at[:16]|replace('T', ' ') if user.created_at else 'N/A' }} | |
| </td> | |
| <td> | |
| <div class="table-actions"> | |
| <button class="btn-action-edit" | |
| onclick="openEditModal('{{ user.id }}', '{{ user.email }}', '{{ user.role }}', '{{ user.permissions|tojson|forceescape }}')"> | |
| <i class="fas fa-edit me-1"></i>Sửa | |
| </button> | |
| {% if session.get('user_id') != user.id %} | |
| <form action="/admin/users/delete/{{ user.id }}" method="POST" onsubmit="return confirm('Bạn có chắc chắn muốn xóa tài khoản này?')" style="display:inline;"> | |
| <button type="submit" class="btn-action-delete"> | |
| <i class="fas fa-trash-alt me-1"></i>Xóa | |
| </button> | |
| </form> | |
| {% endif %} | |
| </div> | |
| </td> | |
| </tr> | |
| {% endfor %} | |
| </tbody> | |
| </table> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- ============================================== | |
| MODAL: CREATE USER | |
| ============================================== --> | |
| <div class="modal fade" id="createModal" tabindex="-1" aria-hidden="true"> | |
| <div class="modal-dialog modal-dialog-centered modal-lg"> | |
| <div class="modal-content border-0 shadow"> | |
| <form action="/admin/users/create" method="POST"> | |
| <div class="modal-header border-bottom-0 p-4"> | |
| <h5 class="modal-title fw-bold"><i class="fas fa-user-plus text-primary me-2"></i>Thêm tài khoản mới</h5> | |
| <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button> | |
| </div> | |
| <div class="modal-body p-4 pt-0"> | |
| <div class="row"> | |
| <div class="col-md-6 mb-3"> | |
| <label class="form-label fw-bold small text-muted">Tài khoản đăng nhập <span class="text-danger">*</span></label> | |
| <input type="text" name="email" class="form-control" placeholder="Tên tài khoản (hoặc email)" required> | |
| </div> | |
| <div class="col-md-6 mb-3"> | |
| <label class="form-label fw-bold small text-muted">Mật khẩu <span class="text-danger">*</span></label> | |
| <input type="password" name="password" class="form-control" required> | |
| </div> | |
| </div> | |
| <div class="mb-4"> | |
| <label class="form-label fw-bold small text-muted">Quyền hạn</label> | |
| <select id="createRole" name="role" class="form-select"> | |
| <option value="user" selected>Người dùng (User)</option> | |
| <option value="admin">Quản trị viên (Admin)</option> | |
| </select> | |
| </div> | |
| <div class="permission-section mb-3"> | |
| <label class="form-label fw-bold small text-muted">Cấp quyền sử dụng công cụ</label> | |
| <div class="row g-3"> | |
| {% for group in available_tools|groupby('group') %} | |
| <div class="col-md-6"> | |
| <div class="card p-3 border-0 bg-light" style="border-radius: 12px;"> | |
| <div class="fw-bold small text-primary mb-2"><i class="fas fa-folder me-1"></i>{{ group.grouper }}</div> | |
| {% for tool in group.list %} | |
| <div class="form-check mb-2"> | |
| <input class="form-check-input" type="checkbox" name="tools" value="{{ tool.path }}" id="chk-create-{{ tool.path|replace('/', '') }}"> | |
| <label class="form-check-label small" for="chk-create-{{ tool.path|replace('/', '') }}"> | |
| {{ tool.name }} | |
| </label> | |
| </div> | |
| {% endfor %} | |
| </div> | |
| </div> | |
| {% endfor %} | |
| </div> | |
| </div> | |
| </div> | |
| <div class="modal-footer border-top-0 p-4 pt-0"> | |
| <button type="button" class="btn btn-light rounded-pill px-4" data-bs-dismiss="modal">Hủy</button> | |
| <button type="submit" class="btn btn-primary rounded-pill px-4">Tạo tài khoản</button> | |
| </div> | |
| </form> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- ============================================== | |
| MODAL: EDIT USER | |
| ============================================== --> | |
| <div class="modal fade" id="editModal" tabindex="-1" aria-hidden="true"> | |
| <div class="modal-dialog modal-dialog-centered modal-lg"> | |
| <div class="modal-content border-0 shadow"> | |
| <form id="editForm" method="POST"> | |
| <div class="modal-header border-bottom-0 p-4"> | |
| <h5 class="modal-title fw-bold"><i class="fas fa-user-edit text-primary me-2"></i>Cập nhật tài khoản</h5> | |
| <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button> | |
| </div> | |
| <div class="modal-body p-4 pt-0"> | |
| <div class="row"> | |
| <div class="col-md-6 mb-3"> | |
| <label class="form-label fw-bold small text-muted">Tài khoản đăng nhập <span class="text-danger">*</span></label> | |
| <input type="text" id="editEmail" name="email" class="form-control" required> | |
| </div> | |
| <div class="col-md-6 mb-3"> | |
| <label class="form-label fw-bold small text-muted">Đặt lại mật khẩu (Để trống nếu giữ nguyên)</label> | |
| <input type="password" name="password" class="form-control" placeholder="••••••••"> | |
| </div> | |
| </div> | |
| <div class="mb-4"> | |
| <label class="form-label fw-bold small text-muted">Quyền hạn</label> | |
| <select id="editRole" name="role" class="form-select"> | |
| <option value="user">Người dùng (User)</option> | |
| <option value="admin">Quản trị viên (Admin)</option> | |
| </select> | |
| </div> | |
| <div class="permission-section mb-3"> | |
| <label class="form-label fw-bold small text-muted">Cấp quyền sử dụng công cụ</label> | |
| <div class="row g-3"> | |
| {% for group in available_tools|groupby('group') %} | |
| <div class="col-md-6"> | |
| <div class="card p-3 border-0 bg-light" style="border-radius: 12px;"> | |
| <div class="fw-bold small text-primary mb-2"><i class="fas fa-folder me-1"></i>{{ group.grouper }}</div> | |
| {% for tool in group.list %} | |
| <div class="form-check mb-2"> | |
| <input class="form-check-input" type="checkbox" name="tools" value="{{ tool.path }}" id="chk-edit-{{ tool.path|replace('/', '') }}"> | |
| <label class="form-check-label small" for="chk-edit-{{ tool.path|replace('/', '') }}"> | |
| {{ tool.name }} | |
| </label> | |
| </div> | |
| {% endfor %} | |
| </div> | |
| </div> | |
| {% endfor %} | |
| </div> | |
| </div> | |
| </div> | |
| <div class="modal-footer border-top-0 p-4 pt-0"> | |
| <button type="button" class="btn btn-light rounded-pill px-4" data-bs-dismiss="modal">Hủy</button> | |
| <button type="submit" class="btn btn-primary rounded-pill px-4">Lưu cập nhật</button> | |
| </div> | |
| </form> | |
| </div> | |
| </div> | |
| </div> | |
| {% endblock %} | |
| {% block scripts %} | |
| <script> | |
| function togglePermissionSection(modalId, roleSelectId, sectionClass) { | |
| const roleSelect = document.getElementById(roleSelectId); | |
| const section = document.querySelector(`#${modalId} .${sectionClass}`); | |
| if (!roleSelect || !section) return; | |
| const updateVisibility = () => { | |
| if (roleSelect.value === 'admin') { | |
| section.style.display = 'none'; | |
| } else { | |
| section.style.display = 'block'; | |
| } | |
| }; | |
| roleSelect.addEventListener('change', updateVisibility); | |
| updateVisibility(); // run initial | |
| } | |
| document.addEventListener('DOMContentLoaded', () => { | |
| togglePermissionSection('createModal', 'createRole', 'permission-section'); | |
| togglePermissionSection('editModal', 'editRole', 'permission-section'); | |
| }); | |
| function openEditModal(id, email, role, permissionsJson) { | |
| document.getElementById('editForm').action = `/admin/users/update/${id}`; | |
| document.getElementById('editEmail').value = email; | |
| const roleSelect = document.getElementById('editRole'); | |
| roleSelect.value = role; | |
| roleSelect.dispatchEvent(new Event('change')); | |
| // Parse and check selected permissions | |
| const permissions = JSON.parse(permissionsJson || '[]'); | |
| document.querySelectorAll('#editModal input[type="checkbox"]').forEach(cb => cb.checked = false); | |
| permissions.forEach(p => { | |
| const cb = document.querySelector(`#editModal input[value="${p}"]`); | |
| if (cb) cb.checked = true; | |
| }); | |
| // Show modal using Bootstrap API | |
| const modal = new bootstrap.Modal(document.getElementById('editModal')); | |
| modal.show(); | |
| } | |
| </script> | |
| {% endblock %} | |