125 lines
5.0 KiB
PHP
125 lines
5.0 KiB
PHP
<?php
|
|
declare(strict_types=1);
|
|
require_once __DIR__ . '/auth_helper.php';
|
|
require_login();
|
|
$user = get_user();
|
|
|
|
$id = $_GET['id'] ?? '';
|
|
if (!$id) {
|
|
header("Location: index.php");
|
|
exit;
|
|
}
|
|
|
|
$pdo = db();
|
|
$stmt = $pdo->prepare("SELECT * FROM elections WHERE id = ?");
|
|
$stmt->execute([$id]);
|
|
$election = $stmt->fetch();
|
|
|
|
if (!$election || $election['status'] !== 'Ongoing') {
|
|
die("Election is not currently ongoing.");
|
|
}
|
|
|
|
// Check if already voted
|
|
$check = $pdo->prepare("SELECT COUNT(*) FROM votes WHERE election_id = ? AND voter_id = ?");
|
|
$check->execute([$id, $user['id']]);
|
|
if ($check->fetchColumn() > 0) {
|
|
header("Location: view_results.php?id=$id&error=AlreadyVoted");
|
|
exit;
|
|
}
|
|
|
|
$positions = $pdo->prepare("SELECT * FROM positions WHERE election_id = ? ORDER BY sort_order ASC");
|
|
$positions->execute([$id]);
|
|
$positions = $positions->fetchAll();
|
|
?>
|
|
<!doctype html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="utf-8" />
|
|
<title>Vote: <?= htmlspecialchars($election['title']) ?></title>
|
|
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
|
|
<link rel="stylesheet" href="assets/css/style.css?v=<?= time() ?>">
|
|
<style>
|
|
.candidate-card {
|
|
border: 2px solid #e2e8f0;
|
|
border-radius: 8px;
|
|
padding: 1rem;
|
|
cursor: pointer;
|
|
transition: all 0.2s;
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 1rem;
|
|
margin-bottom: 0.5rem;
|
|
}
|
|
.candidate-card:hover { border-color: #2563eb; background: #f0f9ff; }
|
|
input[type="radio"]:checked + .candidate-card {
|
|
border-color: #2563eb;
|
|
background: #eff6ff;
|
|
box-shadow: 0 0 0 1px #2563eb;
|
|
}
|
|
input[type="radio"] { display: none; }
|
|
.ballot-section { margin-bottom: 2rem; }
|
|
.ballot-header { border-bottom: 2px solid #1e293b; padding-bottom: 0.5rem; margin-bottom: 1.5rem; }
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<nav class="navbar">
|
|
<a href="index.php" class="brand">E-Vote Pro</a>
|
|
<div>
|
|
<span>Logged in as <?= htmlspecialchars($user['name']) ?></span>
|
|
</div>
|
|
</nav>
|
|
|
|
<div class="container" style="max-width: 800px;">
|
|
<div class="text-center mb-5">
|
|
<h1 style="font-size: 2rem; font-weight: 800;"><?= htmlspecialchars($election['title']) ?></h1>
|
|
<p class="text-muted">Please select your candidates carefully. Your vote is immutable once cast.</p>
|
|
</div>
|
|
|
|
<form action="api/submit_vote.php" method="POST">
|
|
<input type="hidden" name="election_id" value="<?= $id ?>">
|
|
|
|
<?php foreach ($positions as $pos): ?>
|
|
<div class="ballot-section">
|
|
<div class="ballot-header">
|
|
<h2 style="margin: 0; font-size: 1.25rem;"><?= htmlspecialchars($pos['name']) ?></h2>
|
|
<small class="text-muted">Select <?= $pos['max_votes'] ?> candidate(s)</small>
|
|
</div>
|
|
|
|
<?php
|
|
$cStmt = $pdo->prepare("SELECT c.*, u.name FROM candidates c JOIN users u ON c.user_id = u.id WHERE c.position_id = ? AND c.approved = TRUE");
|
|
$cStmt->execute([$pos['id']]);
|
|
$candidates = $cStmt->fetchAll();
|
|
?>
|
|
|
|
<?php if (empty($candidates)): ?>
|
|
<p class="text-muted">No candidates for this position.</p>
|
|
<?php else: ?>
|
|
<div class="candidates-grid">
|
|
<?php foreach ($candidates as $cand): ?>
|
|
<label>
|
|
<input type="radio" name="votes[<?= $pos['id'] ?>]" value="<?= $cand['id'] ?>" required>
|
|
<div class="candidate-card">
|
|
<div style="width: 40px; height: 40px; background: #e2e8f0; border-radius: 50%; display: flex; align-items: center; justify-content: center; font-weight: bold; color: #64748b;">
|
|
<?= substr($cand['name'], 0, 1) ?>
|
|
</div>
|
|
<div>
|
|
<div style="font-weight: 700;"><?= htmlspecialchars($cand['name']) ?></div>
|
|
<div style="font-size: 0.75rem; color: #64748b;"><?= htmlspecialchars($cand['party_name'] ?: 'Independent') ?></div>
|
|
</div>
|
|
</div>
|
|
</label>
|
|
<?php endforeach; ?>
|
|
</div>
|
|
<?php endif; ?>
|
|
</div>
|
|
<?php endforeach; ?>
|
|
|
|
<div style="margin-top: 3rem; text-align: center; border-top: 1px solid #e2e8f0; padding-top: 2rem;">
|
|
<p style="font-size: 0.875rem; color: #64748b; margin-bottom: 1.5rem;">By clicking "Cast My Vote", I acknowledge that my selection is final.</p>
|
|
<button type="submit" class="btn btn-primary" style="padding: 1rem 3rem; font-size: 1.1rem; background: #1e293b;">Cast My Vote</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</body>
|
|
</html>
|