Files
QrCode-Attendance-System/src-backup/admin/users.php
2026-01-07 14:09:59 +08:00

767 lines
36 KiB
PHP

<?php
session_start();
// Check if user is logged in and is admin
if (!isset($_SESSION['user_id']) || $_SESSION['role'] !== 'admin') {
header("Location: ../auth/login.php");
exit();
}
// Use absolute path with error checking
$db_path = __DIR__ . '/../includes/database.php';
if (!file_exists($db_path)) {
die("Database configuration file not found at: $db_path");
}
require_once $db_path;
// Check if $pdo is set
if (!isset($pdo)) {
die("Database connection failed. \$pdo variable not set.");
}
$title = "User Management";
// Try different paths for header.php
$header_paths = [
'header.php', // Same directory
__DIR__ . '/header.php', // Absolute path in admin
__DIR__ . '/../header.php', // Parent directory (attendance_system)
__DIR__ . '/../includes/header.php', // Includes directory
__DIR__ . '/../templates/header.php', // Templates directory
'C:/xampp/htdocs/attendance_system/header.php',
'C:/xampp/htdocs/attendance_system/admin/header.php'
];
$header_found = false;
foreach ($header_paths as $path) {
if (file_exists($path)) {
include $path;
$header_found = true;
break;
}
}
if (!$header_found) {
// If header not found, create basic HTML structure
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title><?php echo $title; ?></title>
<!-- Bootstrap 5 -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet">
<!-- Bootstrap Icons -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.8.1/font/bootstrap-icons.css">
<!-- DataTables -->
<link rel="stylesheet" href="https://cdn.datatables.net/1.11.5/css/dataTables.bootstrap5.min.css">
<style>
body {
background-color: #f5f7fb;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
margin: 0;
padding: 0;
font-size: 0.9rem;
}
.navbar {
background: #37502c;
}
.card {
border-radius: 10px;
box-shadow: 0 5px 15px rgba(0,0,0,0.08);
margin-bottom: 15px;
border: none;
}
.card-header {
background: linear-gradient(135deg, #205e03 0%, #9bdb34 100%);
color: white;
border-radius: 10px 10px 0 0;
border: none;
padding: 12px 15px;
}
.table {
border-radius: 8px;
overflow: hidden;
box-shadow: 0 2px 8px rgba(0,0,0,0.05);
margin-bottom: 0;
font-size: 0.85rem;
}
.table th {
font-size: 0.8rem;
padding: 8px 12px;
background-color: #f8f9fa;
border-bottom: 2px solid #dee2e6;
}
.table td {
padding: 6px 12px;
vertical-align: middle;
}
.table tbody tr:hover {
background-color: #f8f9fa;
}
.status-active {
background-color: #d4edda;
color: #155724;
padding: 3px 6px;
border-radius: 4px;
font-size: 0.8rem;
}
.status-inactive {
background-color: #f8d7da;
color: #721c24;
padding: 3px 6px;
border-radius: 4px;
font-size: 0.8rem;
}
.badge {
font-size: 0.75rem;
padding: 3px 6px;
}
.action-buttons .btn {
padding: 3px 6px;
font-size: 0.8rem;
margin: 0 2px;
}
.search-box {
position: relative;
}
.search-box i {
position: absolute;
left: 10px;
top: 50%;
transform: translateY(-50%);
color: #6c757d;
}
.search-box input {
padding-left: 35px;
font-size: 0.85rem;
height: 36px;
}
.form-control, .form-select {
font-size: 0.85rem;
padding: 6px 10px;
height: 36px;
}
.stat-card {
padding: 12px;
}
.stat-card h3 {
font-size: 1.5rem;
}
.stat-card h6 {
font-size: 0.8rem;
}
.stat-card i {
font-size: 1.8rem;
}
</style>
</head>
<body>
<!-- Simple Navigation -->
<nav class="navbar navbar-expand-lg navbar-dark">
<div class="container-fluid">
<a class="navbar-brand" href="#">
<i class="bi bi-qr-code-scan me-2"></i>AMS - User Management
</a>
<div class="d-flex align-items-center text-white">
<span class="me-3" style="font-size: 0.9rem;">
<i class="bi bi-person-circle me-1"></i>
<?php echo htmlspecialchars($_SESSION['full_name'] ?? 'User'); ?>
</span>
<a href="../auth/logout.php" class="btn btn-outline-light btn-sm" style="font-size: 0.8rem;">
<i class="bi bi-box-arrow-right"></i> Logout
</a>
</div>
</div>
</nav>
<div class="container-fluid mt-3">
<?php
}
?>
<!-- ================ START OF YOUR USER MANAGEMENT CONTENT ================ -->
<!-- Display messages -->
<?php if (isset($_SESSION['message'])): ?>
<?php $message_type = $_SESSION['message_type'] ?? 'success'; ?>
<div class="alert alert-<?php echo $message_type; ?> alert-dismissible fade show py-2" role="alert" style="font-size: 0.85rem;">
<?php echo $_SESSION['message']; ?>
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
</div>
<?php
unset($_SESSION['message']);
unset($_SESSION['message_type']);
?>
<?php endif; ?>
<div class="content-wrapper">
<div class="container-fluid">
<div class="row mb-3">
<div class="col">
<div class="card">
<div class="card-header d-flex justify-content-between align-items-center py-2">
<h6 class="mb-0" style="font-size: 0.9rem;"><i class="bi bi-people-fill me-1"></i>User Management</h6>
<button class="btn btn-primary btn-sm py-1 px-2" data-bs-toggle="modal" data-bs-target="#addUserModal" style="font-size: 0.8rem;">
<i class="bi bi-person-plus me-1"></i> Add User
</button>
</div>
<div class="card-body p-3">
<!-- Search and Filter -->
<div class="row mb-2 g-2">
<div class="col-md-6">
<div class="search-box">
<i class="bi bi-search"></i>
<input type="text" class="form-control" id="searchInput" placeholder="Search users...">
</div>
</div>
<div class="col-md-3">
<select class="form-select" id="roleFilter">
<option value="">All Roles</option>
<option value="admin">Admin</option>
<option value="teacher">Teacher</option>
</select>
</div>
<div class="col-md-3">
<select class="form-select" id="statusFilter">
<option value="">All Status</option>
<option value="1">Active</option>
<option value="0">Inactive</option>
</select>
</div>
</div>
<!-- Users Table - COMPACT VERSION -->
<div class="table-responsive">
<table class="table table-hover table-sm" id="usersTable">
<thead>
<tr>
<th width="3%">#</th>
<th width="20%">Full Name</th>
<th width="18%">Email</th>
<th width="12%">Username</th>
<th width="10%">Role</th>
<th width="10%">Status</th>
<th width="12%">Created</th>
<th width="15%">Actions</th>
</tr>
</thead>
<tbody>
<?php
try {
$query = "SELECT * FROM users ORDER BY created_at DESC";
$stmt = $pdo->query($query);
$users = $stmt->fetchAll(PDO::FETCH_ASSOC);
$count = 1;
if (count($users) > 0):
foreach ($users as $user):
$status_class = $user['status'] ? 'status-active' : 'status-inactive';
$status_text = $user['status'] ? 'Active' : 'Inactive';
$role_badge = '';
switch($user['role']) {
case 'admin': $role_badge = 'danger'; break;
case 'teacher': $role_badge = 'success'; break;
default: $role_badge = 'secondary';
}
?>
<tr>
<td class="text-muted"><?php echo $count++; ?></td>
<td>
<div class="d-flex align-items-center">
<div class="rounded-circle bg-light p-1 me-2">
<i class="bi bi-person text-dark" style="font-size: 0.8rem;"></i>
</div>
<div>
<div style="font-size: 0.85rem; font-weight: 500;"><?php echo htmlspecialchars($user['full_name']); ?></div>
<?php if ($user['id'] == $_SESSION['user_id']): ?>
<span class="badge bg-warning" style="font-size: 0.65rem;">You</span>
<?php endif; ?>
</div>
</div>
</td>
<td>
<small class="text-truncate d-inline-block" style="max-width: 150px;" title="<?php echo htmlspecialchars($user['email'] ?? 'N/A'); ?>">
<?php echo htmlspecialchars($user['email'] ?? 'N/A'); ?>
</small>
</td>
<td><code style="font-size: 0.8rem;"><?php echo htmlspecialchars($user['username']); ?></code></td>
<td>
<span class="badge bg-<?php echo $role_badge; ?>">
<?php echo ucfirst($user['role']); ?>
</span>
</td>
<td>
<span class="<?php echo $status_class; ?>">
<?php echo $status_text; ?>
</span>
</td>
<td><small><?php echo date('m/d/Y', strtotime($user['created_at'])); ?></small></td>
<td>
<div class="d-flex">
<button class="btn btn-sm btn-outline-primary py-0 px-1"
onclick="editUser(<?php echo $user['id']; ?>)"
title="Edit User">
<i class="bi bi-pencil" style="font-size: 0.8rem;"></i>
</button>
<button class="btn btn-sm btn-outline-<?php echo $user['status'] ? 'warning' : 'success'; ?> py-0 px-1 mx-1"
onclick="toggleStatus(<?php echo $user['id']; ?>, <?php echo $user['status']; ?>)"
title="<?php echo $user['status'] ? 'Deactivate' : 'Activate'; ?>">
<i class="bi bi-<?php echo $user['status'] ? 'x-circle' : 'check-circle'; ?>" style="font-size: 0.8rem;"></i>
</button>
<?php if ($user['id'] != $_SESSION['user_id']): ?>
<button class="btn btn-sm btn-outline-danger py-0 px-1"
onclick="deleteUser(<?php echo $user['id']; ?>, '<?php echo htmlspecialchars($user['full_name']); ?>')"
title="Delete User">
<i class="bi bi-trash" style="font-size: 0.8rem;"></i>
</button>
<?php endif; ?>
</div>
</td>
</tr>
<?php
endforeach;
else:
?>
<tr>
<td colspan="8" class="text-center text-muted py-2">
<i class="bi bi-people me-1"></i> No users found.
</td>
</tr>
<?php
endif;
} catch (PDOException $e) {
?>
<tr>
<td colspan="8" class="text-center text-danger py-2">
<i class="bi bi-exclamation-triangle me-1"></i> Error loading users
</td>
</tr>
<?php } ?>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
<!-- User Stats - Compact Version -->
<div class="row g-2">
<div class="col-md-3">
<div class="card stat-card py-2">
<div class="card-body p-2">
<div class="d-flex justify-content-between align-items-center">
<div>
<h6 class="text-muted mb-1" style="font-size: 0.75rem;">Total Users</h6>
<h4 class="mb-0" style="font-size: 1.2rem;">
<?php
$query = "SELECT COUNT(*) as total FROM users";
$stmt = $pdo->query($query);
echo $stmt->fetch()['total'] ?? 0;
?>
</h4>
</div>
<i class="bi bi-people text-primary" style="font-size: 1.5rem;"></i>
</div>
</div>
</div>
</div>
<div class="col-md-3">
<div class="card stat-card py-2">
<div class="card-body p-2">
<div class="d-flex justify-content-between align-items-center">
<div>
<h6 class="text-muted mb-1" style="font-size: 0.75rem;">Active Users</h6>
<h4 class="mb-0" style="font-size: 1.2rem;">
<?php
$query = "SELECT COUNT(*) as active FROM users WHERE status = 1";
$stmt = $pdo->query($query);
echo $stmt->fetch()['active'] ?? 0;
?>
</h4>
</div>
<i class="bi bi-check-circle text-success" style="font-size: 1.5rem;"></i>
</div>
</div>
</div>
</div>
<div class="col-md-3">
<div class="card stat-card py-2">
<div class="card-body p-2">
<div class="d-flex justify-content-between align-items-center">
<div>
<h6 class="text-muted mb-1" style="font-size: 0.75rem;">Admins</h6>
<h4 class="mb-0" style="font-size: 1.2rem;">
<?php
$query = "SELECT COUNT(*) as admins FROM users WHERE role = 'admin'";
$stmt = $pdo->query($query);
echo $stmt->fetch()['admins'] ?? 0;
?>
</h4>
</div>
<i class="bi bi-shield-check text-danger" style="font-size: 1.5rem;"></i>
</div>
</div>
</div>
</div>
<div class="col-md-3">
<div class="card stat-card py-2">
<div class="card-body p-2">
<div class="d-flex justify-content-between align-items-center">
<div>
<h6 class="text-muted mb-1" style="font-size: 0.75rem;">Teachers</h6>
<h4 class="mb-0" style="font-size: 1.2rem;">
<?php
$query = "SELECT COUNT(*) as teachers FROM users WHERE role = 'teacher'";
$stmt = $pdo->query($query);
echo $stmt->fetch()['teachers'] ?? 0;
?>
</h4>
</div>
<i class="bi bi-person-badge text-info" style="font-size: 1.5rem;"></i>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- ================ MODALS SECTION ================ -->
<!-- Add User Modal -->
<div class="modal fade" id="addUserModal" tabindex="-1" aria-labelledby="addUserModalLabel" aria-hidden="true">
<div class="modal-dialog modal-md">
<div class="modal-content">
<div class="modal-header py-2">
<h6 class="modal-title mb-0" id="addUserModalLabel"><i class="bi bi-person-plus me-1"></i>Add New User</h6>
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<form action="process_user.php" method="POST" id="addUserForm">
<div class="modal-body p-3">
<input type="hidden" name="action" value="add">
<div class="row g-2">
<div class="col-md-6 mb-2">
<label for="full_name" class="form-label" style="font-size: 0.85rem;">Full Name <span class="text-danger">*</span></label>
<input type="text" class="form-control form-control-sm" id="full_name" name="full_name" required>
</div>
<div class="col-md-6 mb-2">
<label for="username" class="form-label" style="font-size: 0.85rem;">Username <span class="text-danger">*</span></label>
<input type="text" class="form-control form-control-sm" id="username" name="username" required>
</div>
</div>
<div class="mb-2">
<label for="email" class="form-label" style="font-size: 0.85rem;">Email Address</label>
<input type="email" class="form-control form-control-sm" id="email" name="email">
</div>
<div class="row g-2">
<div class="col-md-6 mb-2">
<label for="password" class="form-label" style="font-size: 0.85rem;">Password <span class="text-danger">*</span></label>
<input type="password" class="form-control form-control-sm" id="password" name="password" required>
</div>
<div class="col-md-6 mb-2">
<label for="confirm_password" class="form-label" style="font-size: 0.85rem;">Confirm Password <span class="text-danger">*</span></label>
<input type="password" class="form-control form-control-sm" id="confirm_password" name="confirm_password" required>
</div>
</div>
<div class="row g-2">
<div class="col-md-6 mb-2">
<label for="role" class="form-label" style="font-size: 0.85rem;">Role <span class="text-danger">*</span></label>
<select class="form-select form-select-sm" id="role" name="role" required>
<option value="">Select Role</option>
<option value="admin">Admin</option>
<option value="teacher">Teacher</option>
</select>
</div>
<div class="col-md-6 mb-2">
<label for="status" class="form-label" style="font-size: 0.85rem;">Status</label>
<select class="form-select form-select-sm" id="status" name="status">
<option value="1">Active</option>
<option value="0">Inactive</option>
</select>
</div>
</div>
</div>
<div class="modal-footer py-2">
<button type="button" class="btn btn-secondary btn-sm" data-bs-dismiss="modal">Cancel</button>
<button type="submit" class="btn btn-primary btn-sm">Add User</button>
</div>
</form>
</div>
</div>
</div>
<!-- Edit User Modal -->
<div class="modal fade" id="editUserModal" tabindex="-1" aria-labelledby="editUserModalLabel" aria-hidden="true">
<div class="modal-dialog modal-md">
<div class="modal-content">
<div class="modal-header py-2">
<h6 class="modal-title mb-0" id="editUserModalLabel"><i class="bi bi-pencil me-1"></i>Edit User</h6>
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<form action="process_user.php" method="POST" id="editUserForm">
<div class="modal-body p-3">
<input type="hidden" name="action" value="edit">
<input type="hidden" id="edit_user_id" name="user_id">
<div class="row g-2">
<div class="col-md-6 mb-2">
<label for="edit_full_name" class="form-label" style="font-size: 0.85rem;">Full Name <span class="text-danger">*</span></label>
<input type="text" class="form-control form-control-sm" id="edit_full_name" name="full_name" required>
</div>
<div class="col-md-6 mb-2">
<label for="edit_username" class="form-label" style="font-size: 0.85rem;">Username <span class="text-danger">*</span></label>
<input type="text" class="form-control form-control-sm" id="edit_username" name="username" required>
</div>
</div>
<div class="mb-2">
<label for="edit_email" class="form-label" style="font-size: 0.85rem;">Email Address</label>
<input type="email" class="form-control form-control-sm" id="edit_email" name="email">
</div>
<div class="row g-2">
<div class="col-md-6 mb-2">
<label for="edit_password" class="form-label" style="font-size: 0.85rem;">New Password (leave blank)</label>
<input type="password" class="form-control form-control-sm" id="edit_password" name="password">
</div>
<div class="col-md-6 mb-2">
<label for="edit_confirm_password" class="form-label" style="font-size: 0.85rem;">Confirm New Password</label>
<input type="password" class="form-control form-control-sm" id="edit_confirm_password" name="confirm_password">
</div>
</div>
<div class="row g-2">
<div class="col-md-6 mb-2">
<label for="edit_role" class="form-label" style="font-size: 0.85rem;">Role <span class="text-danger">*</span></label>
<select class="form-select form-select-sm" id="edit_role" name="role" required>
<option value="">Select Role</option>
<option value="admin">Admin</option>
<option value="teacher">Teacher</option>
</select>
</div>
<div class="col-md-6 mb-2">
<label for="edit_status" class="form-label" style="font-size: 0.85rem;">Status</label>
<select class="form-select form-select-sm" id="edit_status" name="status">
<option value="1">Active</option>
<option value="0">Inactive</option>
</select>
</div>
</div>
</div>
<div class="modal-footer py-2">
<button type="button" class="btn btn-secondary btn-sm" data-bs-dismiss="modal">Cancel</button>
<button type="submit" class="btn btn-primary btn-sm">Update User</button>
</div>
</form>
</div>
</div>
</div>
<!-- Delete Confirmation Modal -->
<div class="modal fade" id="deleteModal" tabindex="-1" aria-labelledby="deleteModalLabel" aria-hidden="true">
<div class="modal-dialog modal-sm">
<div class="modal-content">
<div class="modal-header bg-danger text-white py-2">
<h6 class="modal-title mb-0" id="deleteModalLabel"><i class="bi bi-exclamation-triangle me-1"></i>Confirm Delete</h6>
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body p-3">
<p class="mb-2" style="font-size: 0.85rem;">Delete user: <strong id="deleteUserName"></strong>?</p>
<p class="text-danger mb-0" style="font-size: 0.8rem;"><i class="bi bi-exclamation-circle me-1"></i>Cannot be undone.</p>
</div>
<div class="modal-footer py-2">
<button type="button" class="btn btn-secondary btn-sm" data-bs-dismiss="modal">Cancel</button>
<button type="button" class="btn btn-danger btn-sm" id="confirmDelete">Delete</button>
</div>
</div>
</div>
</div>
<!-- ================ JAVASCRIPT SECTION ================ -->
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"></script>
<script src="https://cdn.datatables.net/1.11.5/js/jquery.dataTables.min.js"></script>
<script src="https://cdn.datatables.net/1.11.5/js/dataTables.bootstrap5.min.js"></script>
<script>
// Table filtering and search - COMPACT VERSION
$(document).ready(function() {
const table = $('#usersTable').DataTable({
pageLength: 15, // More rows per page
lengthMenu: [[10, 15, 25, 50, -1], [10, 15, 25, 50, "All"]],
responsive: true,
dom: '<"row"<"col-sm-12"fr>>rt<"row"<"col-sm-6"i><"col-sm-6"p>>',
language: {
search: "_INPUT_",
searchPlaceholder: "Search...",
lengthMenu: "Show _MENU_ entries",
info: "Showing _START_ to _END_ of _TOTAL_ users",
paginate: {
first: "First",
last: "Last",
next: "→",
previous: "←"
}
},
columnDefs: [
{ orderable: false, targets: [7] } // Make actions column not sortable
]
});
$('#searchInput').on('keyup', function() {
table.search(this.value).draw();
});
$('#roleFilter').on('change', function() {
table.column(4).search(this.value).draw();
});
$('#statusFilter').on('change', function() {
table.column(5).search(this.value).draw();
});
});
// Edit User Function
function editUser(userId) {
fetch(`get_user.php?id=${userId}`)
.then(response => response.json())
.then(data => {
if (data.success) {
const user = data.user;
document.getElementById('edit_user_id').value = user.id;
document.getElementById('edit_full_name').value = user.full_name;
document.getElementById('edit_username').value = user.username;
document.getElementById('edit_email').value = user.email || '';
document.getElementById('edit_role').value = user.role;
document.getElementById('edit_status').value = user.status;
const editModal = new bootstrap.Modal(document.getElementById('editUserModal'));
editModal.show();
} else {
alert('Error loading user data: ' + data.message);
}
})
.catch(error => {
console.error('Error:', error);
alert('Error loading user data');
});
}
// Toggle User Status
function toggleStatus(userId, currentStatus) {
if (confirm(`Are you sure you want to ${currentStatus ? 'deactivate' : 'activate'} this user?`)) {
const formData = new FormData();
formData.append('action', 'toggle_status');
formData.append('user_id', userId);
fetch('process_user.php', {
method: 'POST',
body: formData
})
.then(response => response.json())
.then(data => {
if (data.success) {
location.reload();
} else {
alert('Error: ' + data.message);
}
})
.catch(error => {
console.error('Error:', error);
alert('Error updating user status');
});
}
}
// Delete User Function
let userToDelete = null;
let userNameToDelete = '';
function deleteUser(userId, userName) {
userToDelete = userId;
userNameToDelete = userName;
document.getElementById('deleteUserName').textContent = userName;
const deleteModal = new bootstrap.Modal(document.getElementById('deleteModal'));
deleteModal.show();
}
// Confirm Delete
document.getElementById('confirmDelete').addEventListener('click', function() {
if (userToDelete) {
const formData = new FormData();
formData.append('action', 'delete');
formData.append('user_id', userToDelete);
fetch('process_user.php', {
method: 'POST',
body: formData
})
.then(response => response.json())
.then(data => {
if (data.success) {
location.reload();
} else {
alert('Error: ' + data.message);
bootstrap.Modal.getInstance(document.getElementById('deleteModal')).hide();
}
})
.catch(error => {
console.error('Error:', error);
alert('Error deleting user');
bootstrap.Modal.getInstance(document.getElementById('deleteModal')).hide();
});
}
});
// Form Validation
document.getElementById('addUserForm').addEventListener('submit', function(e) {
const password = document.getElementById('password').value;
const confirmPassword = document.getElementById('confirm_password').value;
if (password !== confirmPassword) {
e.preventDefault();
alert('Passwords do not match!');
return false;
}
if (password.length < 6) {
e.preventDefault();
alert('Password must be at least 6 characters long!');
return false;
}
});
document.getElementById('editUserForm').addEventListener('submit', function(e) {
const password = document.getElementById('edit_password').value;
const confirmPassword = document.getElementById('edit_confirm_password').value;
if (password !== confirmPassword) {
e.preventDefault();
alert('Passwords do not match!');
return false;
}
if (password && password.length < 6) {
e.preventDefault();
alert('Password must be at least 6 characters long!');
return false;
}
});
</script>
<?php
// Check if we need to close HTML tags (if no header was found)
if (!$header_found) {
echo '</div></body></html>';
}
?>