38458-vm/voter_management.php
2026-02-15 19:49:48 +00:00

255 lines
12 KiB
PHP

<?php
require_once 'auth_helper.php';
require_login();
$user = get_user();
$pdo = db();
$electionId = get_active_election_id();
$election = get_active_election();
// Statistics (Filtered by Election if possible, otherwise global)
// For now, let's assume we want to see voters assigned to the current election
$totalVoters = $pdo->prepare("SELECT COUNT(*) FROM users u JOIN election_assignments ea ON u.id = ea.user_id WHERE ea.election_id = ? AND ea.role_in_election = 'Voter'");
$totalVoters->execute([$electionId]);
$totalVoters = $totalVoters->fetchColumn();
$votedCount = $pdo->prepare("SELECT COUNT(DISTINCT voter_id) FROM votes WHERE election_id = ?");
$votedCount->execute([$electionId]);
$votedCount = $votedCount->fetchColumn();
$notVotedCount = $totalVoters - $votedCount;
// Distribution (Filtered by Election)
$trackStats = $pdo->prepare("SELECT u.track, COUNT(*) as count FROM users u JOIN election_assignments ea ON u.id = ea.user_id WHERE ea.election_id = ? AND ea.role_in_election = 'Voter' GROUP BY u.track ORDER BY u.track");
$trackStats->execute([$electionId]);
$trackStats = $trackStats->fetchAll(PDO::FETCH_ASSOC);
$gradeStats = $pdo->prepare("SELECT u.grade_level, COUNT(*) as count FROM users u JOIN election_assignments ea ON u.id = ea.user_id WHERE ea.election_id = ? AND ea.role_in_election = 'Voter' GROUP BY u.grade_level ORDER BY u.grade_level");
$gradeStats->execute([$electionId]);
$gradeStats = $gradeStats->fetchAll(PDO::FETCH_ASSOC);
// Filters
$search = $_GET['search'] ?? '';
$filterTrack = $_GET['track'] ?? 'All Tracks';
$filterGrade = $_GET['grade'] ?? 'All Grades';
// Query Construction
$query = "SELECT u.*,
(SELECT COUNT(*) FROM votes v WHERE v.voter_id = u.id AND v.election_id = ?) as has_voted
FROM users u
JOIN election_assignments ea ON u.id = ea.user_id
WHERE ea.election_id = ? AND ea.role_in_election = 'Voter'";
$params = [$electionId, $electionId];
if ($search) {
$query .= " AND (u.email LIKE ? OR u.name LIKE ? OR u.student_id LIKE ?)";
$params[] = "%$search%";
$params[] = "%$search%";
$params[] = "%$search%";
}
if ($filterTrack !== 'All Tracks') {
$query .= " AND u.track = ?";
$params[] = $filterTrack;
}
if ($filterGrade !== 'All Grades') {
$query .= " AND u.grade_level = ?";
$params[] = $filterGrade;
}
$stmt = $pdo->prepare($query);
$stmt->execute($params);
$voters = $stmt->fetchAll();
// Get unique values for filters
$tracks = $pdo->query("SELECT DISTINCT track FROM users WHERE track IS NOT NULL ORDER BY track")->fetchAll(PDO::FETCH_COLUMN);
$grades = $pdo->query("SELECT DISTINCT grade_level FROM users WHERE grade_level IS NOT NULL ORDER BY grade_level")->fetchAll(PDO::FETCH_COLUMN);
$projectDescription = $_SERVER['PROJECT_DESCRIPTION'] ?? 'Online Election System for Senior High School';
?>
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Voter Management | <?= htmlspecialchars($projectDescription) ?></title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
<link rel="stylesheet" href="assets/css/dashboard.css?v=<?= time() ?>">
<link rel="stylesheet" href="assets/css/voter_management.css?v=<?= time() ?>">
<script src="https://unpkg.com/lucide@latest"></script>
</head>
<body class="dashboard-body">
<?php require_once 'includes/sidebar.php'; ?>
<!-- Main Content -->
<div class="main-wrapper">
<header class="top-header">
<div class="search-bar">
<i data-lucide="search" style="width: 16px; color: #94a3b8;"></i>
<input type="text" placeholder="Quick search...">
</div>
<div class="user-profile">
<div class="user-info">
<div class="user-name"><?= htmlspecialchars($user['name'] ?? 'System Administrator') ?></div>
<div class="user-role"><?= htmlspecialchars($user['role'] ?? 'Admin') ?></div>
</div>
<div class="user-avatar">
<?= strtoupper(substr($user['name'] ?? 'S', 0, 1)) ?>
</div>
</div>
</header>
<main class="dashboard-content animate-fade-in">
<div class="dashboard-header">
<div style="display: flex; align-items: center; gap: 16px;">
<div class="header-icon-container">
<i data-lucide="users" style="width: 24px; color: #4f46e5;"></i>
</div>
<div>
<h1 style="margin: 0; font-size: 1.5rem; color: #1e293b;">Voters List</h1>
<p style="margin: 4px 0 0 0; color: #64748b; font-size: 0.875rem;">Managing voters for <?= htmlspecialchars($election['title'] ?? 'Selected Election') ?></p>
</div>
</div>
</div>
<!-- Stats Grid -->
<div class="voter-stats-grid animate-stagger">
<div class="voter-stat-card">
<div class="voter-stat-label">TOTAL VOTERS</div>
<div class="voter-stat-value" style="color: #2563eb;"><?= number_format($totalVoters) ?></div>
</div>
<div class="voter-stat-card">
<div class="voter-stat-label">VOTERS WHO VOTED</div>
<div class="voter-stat-value" style="color: #64748b;"><?= number_format($votedCount) ?></div>
</div>
<div class="voter-stat-card">
<div class="voter-stat-label" style="color: #ef4444;">VOTERS WHO HAVEN'T VOTED</div>
<div class="voter-stat-value" style="color: #ef4444;"><?= number_format($notVotedCount) ?></div>
</div>
</div>
<div style="display: flex; justify-content: flex-end; gap: 12px; margin-bottom: 24px;" class="animate-stagger">
<button class="btn-action btn-add" onclick="openModal('addVoterModal')">
<i data-lucide="plus" style="width: 14px;"></i>
ADD VOTER
</button>
<button class="btn-action btn-import" onclick="openModal('importModal')">
<i data-lucide="upload" style="width: 14px;"></i>
Import CSV
</button>
</div>
<!-- Filters & Table Section -->
<div class="content-section animate-fade-in">
<form id="filterForm" method="GET" class="filter-bar">
<div class="filter-group" style="flex: 2;">
<label>SEARCH</label>
<div class="search-input-wrapper">
<i data-lucide="search" style="width: 14px; color: #94a3b8;"></i>
<input type="text" name="search" value="<?= htmlspecialchars($search) ?>" placeholder="Search by email">
</div>
</div>
<div class="filter-group">
<label>TRACK</label>
<select name="track" onchange="this.form.submit()">
<option>All Tracks</option>
<?php foreach ($tracks as $t): ?>
<option value="<?= htmlspecialchars($t) ?>" <?= $filterTrack === $t ? 'selected' : '' ?>><?= htmlspecialchars($t) ?></option>
<?php endforeach; ?>
</select>
</div>
<div class="filter-group">
<label>GRADE</label>
<select name="grade" onchange="this.form.submit()">
<option>All Grades</option>
<?php foreach ($grades as $g): ?>
<option value="<?= htmlspecialchars($g) ?>" <?= $filterGrade == $g ? 'selected' : '' ?>>Grade <?= htmlspecialchars($g) ?></option>
<?php endforeach; ?>
</select>
</div>
</form>
<table class="voters-table">
<thead>
<tr>
<th>USER ID</th>
<th>NAME</th>
<th>EMAIL</th>
<th>TRACK</th>
<th>GRADE</th>
<th>STATUS</th>
<th>ACTIONS</th>
</tr>
</thead>
<tbody>
<?php if (empty($voters)): ?>
<tr>
<td colspan="7" style="text-align: center; color: #94a3b8; padding: 32px;">No voters assigned to this election.</td>
</tr>
<?php else: ?>
<?php foreach ($voters as $voter): ?>
<tr>
<td><?= htmlspecialchars($voter['student_id']) ?></td>
<td><?= htmlspecialchars($voter['name']) ?></td>
<td><?= htmlspecialchars($voter['email']) ?></td>
<td><?= htmlspecialchars($voter['track']) ?></td>
<td>Grade <?= htmlspecialchars($voter['grade_level']) ?></td>
<td>
<span class="status-indicator <?= $voter['has_voted'] ? 'voted' : 'pending' ?>">
<?= $voter['has_voted'] ? 'Voted' : 'Pending' ?>
</span>
</td>
<td class="actions-cell">
<button title="Edit"><i data-lucide="edit-2"></i></button>
<button title="Delete" style="color: #ef4444;"><i data-lucide="trash-2"></i></button>
</td>
</tr>
<?php endforeach; ?>
<?php endif; ?>
</tbody>
</table>
</div>
</main>
</div>
<!-- Modals (Simplified for this polishing phase) -->
<div id="addVoterModal" class="modal">
<div class="modal-content">
<div class="modal-header">
<h2>Add New Voter</h2>
<button onclick="closeModal('addVoterModal')" style="border:none; background:none; cursor:pointer;"><i data-lucide="x"></i></button>
</div>
<p style="font-size: 0.875rem; color: #64748b;">This will assign an existing user or create a new one for this election.</p>
<!-- Add Voter form logic here -->
<div class="modal-footer">
<button onclick="closeModal('addVoterModal')" class="btn-cancel">Close</button>
</div>
</div>
</div>
<script>
lucide.createIcons();
function openModal(id) {
document.getElementById(id).style.display = 'flex';
}
function closeModal(id) {
document.getElementById(id).style.display = 'none';
}
window.onclick = function(event) {
if (event.target.classList.contains('modal')) {
event.target.style.display = 'none';
}
}
</script>
</body>
</html>