38458-vm/election_history.php
Flatlogic Bot 5ac812ef05 Final3
2026-02-16 03:29:05 +00:00

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>