327 lines
12 KiB
PHP
327 lines
12 KiB
PHP
<?php
|
|
require_once 'auth_helper.php';
|
|
require_login();
|
|
$user = get_user();
|
|
$pdo = db();
|
|
|
|
// Fetch all elections for the history
|
|
$elections = $pdo->query("SELECT * FROM elections WHERE archived = FALSE ORDER BY start_date_and_time DESC")->fetchAll();
|
|
|
|
// Extract years for the "Jump to School Year" dropdown
|
|
$years = [];
|
|
foreach ($elections as $e) {
|
|
$year = date('Y', strtotime($e['start_date_and_time']));
|
|
$years[$year] = $year;
|
|
}
|
|
krsort($years);
|
|
|
|
$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>Election History | <?= 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() ?>">
|
|
<script src="https://unpkg.com/lucide@latest"></script>
|
|
<style>
|
|
.history-controls {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: flex-start;
|
|
margin-bottom: 24px;
|
|
}
|
|
.history-title-area h1 {
|
|
margin: 0 0 4px 0;
|
|
font-size: 1.5rem;
|
|
color: #1e293b;
|
|
}
|
|
.history-title-area p {
|
|
margin: 0;
|
|
color: #64748b;
|
|
font-size: 0.875rem;
|
|
}
|
|
.year-selector {
|
|
padding: 8px 12px;
|
|
border-radius: 8px;
|
|
border: 1px solid var(--border-color);
|
|
background: white;
|
|
color: #4b5563;
|
|
font-size: 0.875rem;
|
|
outline: none;
|
|
}
|
|
|
|
.election-history-list {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 12px;
|
|
}
|
|
|
|
.election-item {
|
|
background: white;
|
|
border: 1px solid var(--border-color);
|
|
border-radius: 12px;
|
|
overflow: hidden;
|
|
transition: all 0.2s;
|
|
}
|
|
|
|
.election-item-header {
|
|
padding: 20px 24px;
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
cursor: pointer;
|
|
}
|
|
|
|
.election-item-header:hover {
|
|
background: #f8fafc;
|
|
}
|
|
|
|
.election-item-title {
|
|
font-weight: 600;
|
|
color: #2563eb;
|
|
font-size: 1rem;
|
|
}
|
|
|
|
.election-item-right {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 16px;
|
|
}
|
|
|
|
.status-badge {
|
|
padding: 4px 12px;
|
|
border-radius: 9999px;
|
|
font-size: 0.7rem;
|
|
font-weight: 700;
|
|
text-transform: uppercase;
|
|
}
|
|
.status-ongoing { background: #fffbeb; color: #d97706; }
|
|
.status-finished { background: #ecfdf5; color: #10b981; }
|
|
|
|
.election-item-body {
|
|
display: none;
|
|
padding: 24px;
|
|
border-top: 1px solid var(--border-color);
|
|
background: #fafafa;
|
|
}
|
|
|
|
.election-item.active .election-item-body {
|
|
display: block;
|
|
}
|
|
|
|
.election-item.active .chevron-icon {
|
|
transform: rotate(180deg);
|
|
}
|
|
|
|
.details-grid {
|
|
display: grid;
|
|
grid-template-columns: repeat(2, 1fr);
|
|
gap: 24px;
|
|
margin-bottom: 24px;
|
|
}
|
|
|
|
.detail-card {
|
|
background: white;
|
|
border: 1px solid var(--border-color);
|
|
border-radius: 8px;
|
|
padding: 20px;
|
|
}
|
|
|
|
.detail-label {
|
|
font-size: 0.7rem;
|
|
font-weight: 600;
|
|
color: #64748b;
|
|
text-transform: uppercase;
|
|
margin-bottom: 12px;
|
|
}
|
|
|
|
.detail-value {
|
|
font-size: 1.5rem;
|
|
font-weight: 700;
|
|
color: #1e293b;
|
|
}
|
|
|
|
.detail-sub {
|
|
font-size: 0.875rem;
|
|
color: #1e293b;
|
|
font-weight: 500;
|
|
}
|
|
|
|
.results-section {
|
|
background: white;
|
|
border: 1px solid var(--border-color);
|
|
border-radius: 8px;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.results-title {
|
|
padding: 16px 20px;
|
|
font-weight: 700;
|
|
font-size: 0.9rem;
|
|
color: #1e293b;
|
|
border-bottom: 1px solid var(--border-color);
|
|
}
|
|
|
|
.results-table {
|
|
width: 100%;
|
|
border-collapse: collapse;
|
|
}
|
|
|
|
.results-table th {
|
|
background: #f9fafb;
|
|
padding: 10px 20px;
|
|
text-align: left;
|
|
font-size: 0.7rem;
|
|
font-weight: 600;
|
|
color: #64748b;
|
|
text-transform: uppercase;
|
|
}
|
|
|
|
.results-table td {
|
|
padding: 12px 20px;
|
|
border-bottom: 1px solid var(--border-color);
|
|
font-size: 0.875rem;
|
|
color: #4b5563;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body class="dashboard-body <?= ($user['theme'] ?? 'light') === 'dark' ? 'dark-theme' : '' ?>">
|
|
|
|
<?php require_once 'includes/sidebar.php'; ?>
|
|
|
|
<!-- Main Content -->
|
|
<div class="main-wrapper">
|
|
<?php require_once 'includes/header.php'; ?>
|
|
|
|
<main class="dashboard-content animate-fade-in">
|
|
<div class="history-controls">
|
|
<div class="history-title-area">
|
|
<h1>Election History</h1>
|
|
<p>Voter turnout and candidate results per election year</p>
|
|
</div>
|
|
<select class="year-selector" onchange="jumpToYear(this.value)">
|
|
<option value="">Jump to School Year</option>
|
|
<?php foreach ($years as $y): ?>
|
|
<option value="year-<?= $y ?>">SY <?= $y ?>-<?= $y+1 ?></option>
|
|
<?php endforeach; ?>
|
|
</select>
|
|
</div>
|
|
|
|
<div class="election-history-list animate-stagger">
|
|
<?php foreach ($elections as $election):
|
|
$electionYear = date('Y', strtotime($election['start_date_and_time']));
|
|
$voterCount = $pdo->prepare("SELECT COUNT(DISTINCT voter_id) FROM votes WHERE election_id = ?");
|
|
$voterCount->execute([$election['id']]);
|
|
$totalVoters = $voterCount->fetchColumn();
|
|
|
|
$results = $pdo->prepare("
|
|
SELECT c.*, u.name as candidate_name, p.name as position_name,
|
|
(SELECT COUNT(*) FROM votes v WHERE v.candidate_id = c.id) as vote_count
|
|
FROM candidates c
|
|
JOIN users u ON c.user_id = u.id
|
|
JOIN positions p ON c.position_id = p.id
|
|
WHERE c.election_id = ?
|
|
ORDER BY p.sort_order, vote_count DESC
|
|
");
|
|
$results->execute([$election['id']]);
|
|
$candidates = $results->fetchAll();
|
|
?>
|
|
<div class="election-item" id="year-<?= $electionYear ?>">
|
|
<div class="election-item-header" onclick="toggleAccordion(this)">
|
|
<div class="election-item-title"><?= htmlspecialchars($election['title']) ?></div>
|
|
<div class="election-item-right">
|
|
<a href="view_results.php?id=<?= $election['id'] ?>" class="btn-generate" onclick="event.stopPropagation()" style="background: #4f46e5; color: white; padding: 6px 12px; border-radius: 6px; font-size: 0.75rem; font-weight: 600; text-decoration: none; display: flex; align-items: center; gap: 6px;">
|
|
<i data-lucide="file-text" style="width: 14px;"></i>
|
|
GENERATE RESULTS
|
|
</a>
|
|
<span class="status-badge status-<?= strtolower($election['status']) ?>">
|
|
<?= htmlspecialchars($election['status']) ?>
|
|
</span>
|
|
<i data-lucide="chevron-down" class="chevron-icon" style="width: 18px; color: #2563eb;"></i>
|
|
</div>
|
|
</div>
|
|
<div class="election-item-body">
|
|
<div class="details-grid">
|
|
<div class="detail-card">
|
|
<div class="detail-label">Total Voters</div>
|
|
<div class="detail-value"><?= number_format($totalVoters) ?></div>
|
|
</div>
|
|
<div class="detail-card">
|
|
<div class="detail-label">Election Period</div>
|
|
<div class="detail-sub">
|
|
<?= date('M d, Y', strtotime($election['start_date_and_time'])) ?> to
|
|
<?= date('M d, Y', strtotime($election['end_date_and_time'])) ?>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="results-section">
|
|
<div class="results-title">Candidate Results</div>
|
|
<table class="results-table">
|
|
<thead>
|
|
<tr>
|
|
<th>Candidate Name</th>
|
|
<th>Position</th>
|
|
<th>Party</th>
|
|
<th>Votes</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<?php if (empty($candidates)): ?>
|
|
<tr>
|
|
<td colspan="4" style="text-align: center; padding: 20px; color: #94a3b8;">
|
|
No candidate results available for this election.
|
|
</td>
|
|
</tr>
|
|
<?php else: ?>
|
|
<?php foreach ($candidates as $cand): ?>
|
|
<tr>
|
|
<td style="font-weight: 500; color: #1e293b;"><?= htmlspecialchars($cand['candidate_name']) ?></td>
|
|
<td><?= htmlspecialchars($cand['position_name']) ?></td>
|
|
<td><?= htmlspecialchars($cand['party_name'] ?? 'Independent') ?></td>
|
|
<td style="font-weight: 600; color: #2563eb;"><?= number_format($cand['vote_count']) ?></td>
|
|
</tr>
|
|
<?php endforeach; ?>
|
|
<?php endif; ?>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<?php endforeach; ?>
|
|
</div>
|
|
</main>
|
|
</div>
|
|
|
|
<script>
|
|
lucide.createIcons();
|
|
|
|
function toggleAccordion(header) {
|
|
const item = header.parentElement;
|
|
const isActive = item.classList.contains('active');
|
|
|
|
if (isActive) {
|
|
item.classList.remove('active');
|
|
} else {
|
|
item.classList.add('active');
|
|
}
|
|
}
|
|
|
|
function jumpToYear(id) {
|
|
if (!id) return;
|
|
const el = document.getElementById(id);
|
|
if (el) {
|
|
el.scrollIntoView({ behavior: 'smooth', block: 'start' });
|
|
el.classList.add('active');
|
|
}
|
|
}
|
|
lucide.createIcons();
|
|
</script>
|
|
</body>
|
|
</html>
|