Autosave: 20260204-211347

This commit is contained in:
Flatlogic Bot 2026-02-04 21:13:47 +00:00
parent b813668e12
commit b3fd5e275d
14 changed files with 1745 additions and 180 deletions

View File

@ -0,0 +1,85 @@
<?php
require_once __DIR__ . '/Authentication_Handler.php';
require_once __DIR__ . '/../../db/config.php';
$Auth = new Authentication_Handler();
$Auth->Check_Auth();
if ($_SESSION['Access_Level'] < 1) {
header('Location: ../../Screens/Voting_Screen.php');
exit;
}
function generateUserID($db) {
$tables = ['Officers', 'Voters', 'Candidates'];
do {
$id = sprintf("%02d-%04d", rand(0, 99), rand(0, 9999));
$exists = false;
foreach ($tables as $table) {
$stmt = $db->prepare("SELECT COUNT(*) FROM $table WHERE User_ID = ?");
$stmt->execute([$id]);
if ($stmt->fetchColumn() > 0) {
$exists = true;
break;
}
}
} while ($exists);
return $id;
}
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$db = db();
$name = $_POST['name'] ?? '';
$email = $_POST['email'] ?? '';
$position = $_POST['position'] ?? '';
$party = $_POST['party'] ?? '';
$grade = $_POST['grade'] ?? '';
$track = $_POST['track'] ?? '';
$user_id = $_POST['user_id'] ?? '';
$password = $_POST['password'] ?? '';
// User ID generation if empty
if (empty($user_id)) {
$userId = generateUserID($db);
} else {
$userId = $user_id;
}
// Default password
if (empty($password)) {
$password = "School@123";
}
$hashedPassword = password_hash($password, PASSWORD_DEFAULT);
// Handle Photo Upload
$photoPath = '';
if (isset($_FILES['photo']) && $_FILES['photo']['error'] === UPLOAD_ERR_OK) {
$uploadDir = '../../assets/images/candidates/';
if (!is_dir($uploadDir)) {
mkdir($uploadDir, 0775, true);
}
$fileExt = pathinfo($_FILES['photo']['name'], PATHINFO_EXTENSION);
$fileName = $userId . '.' . $fileExt;
$targetPath = $uploadDir . $fileName;
if (move_uploaded_file($_FILES['photo']['tmp_name'], $targetPath)) {
$photoPath = 'assets/images/candidates/' . $fileName;
}
}
try {
$stmt = $db->prepare("INSERT INTO Candidates (User_ID, Name, Email, Position, Party, Grade_Level, Track_Cluster, Photo, Password) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)");
$stmt->execute([$userId, $name, $email, $position, $party, $grade, $track, $photoPath, $hashedPassword]);
// Log the action
$Auth->Log_Action($_SESSION['User_ID'], $_SESSION['User_Role'], 'Add Candidate', "Added candidate $name for $position. ID: $userId");
header('Location: ../../Screens/Candidates.php?success=Candidate+added+with+ID:+' . $userId);
} catch (Exception $e) {
// Redirect with error
header('Location: ../../Screens/Candidates.php?error=' . urlencode($e->getMessage()));
}
exit;
}

View File

@ -0,0 +1,77 @@
<?php
require_once __DIR__ . '/Authentication_Handler.php';
require_once __DIR__ . '/../../db/config.php';
$Auth = new Authentication_Handler();
$Auth->Check_Auth();
if ($_SESSION['Access_Level'] < 3) {
die("Unauthorized access");
}
function generateUserID($db) {
$tables = ['Officers', 'Voters', 'Candidates'];
do {
$id = sprintf("%02d-%04d", rand(0, 99), rand(0, 9999));
$exists = false;
foreach ($tables as $table) {
$stmt = $db->prepare("SELECT COUNT(*) FROM $table WHERE User_ID = ?");
$stmt->execute([$id]);
if ($stmt->fetchColumn() > 0) {
$exists = true;
break;
}
}
} while ($exists);
return $id;
}
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$db = db();
$name = $_POST['name'] ?? '';
$email = $_POST['email'] ?? '';
$user_id = $_POST['user_id'] ?? '';
$role = $_POST['role'] ?? '';
$password = $_POST['password'] ?? '';
if (empty($name) || empty($email) || empty($role)) {
header("Location: ../../Screens/Officers.php?error=Missing+fields");
exit;
}
// User ID logic
if (empty($user_id)) {
$user_id = generateUserID($db);
} else {
// Enforce format 00-0000 if provided? The user said "Make User-IDs... to be 7 characters long in this format: '00-0000'"
// If it doesn't match, we could either reject or fix it. Let's just use it if provided for now, but the requirement suggests a strict format.
// Actually, let's just use the provided ID if it's not empty, but the generation logic is there if it is.
}
// Password logic
if (empty($password)) {
$password = "School@123"; // Default password
}
// Determine Access Level
$accessLevel = 1;
if ($role === 'Admin') $accessLevel = 3;
elseif ($role === 'COMEA Adviser') $accessLevel = 2;
$hashedPassword = password_hash($password, PASSWORD_DEFAULT);
try {
$stmt = $db->prepare("INSERT INTO Officers (User_ID, Name, Email, Role, Access_Level, Password) VALUES (?, ?, ?, ?, ?, ?)");
$stmt->execute([$user_id, $name, $email, $role, $accessLevel, $hashedPassword]);
// Log to Audit Trail
$Auth->Log_Action($_SESSION['User_ID'], $_SESSION['User_Role'], 'Add Officer', "Registered new officer: $name ($role). User ID: $user_id");
header("Location: ../../Screens/Officers.php?success=Officer+registered+with+ID:+$user_id");
} catch (PDOException $e) {
header("Location: ../../Screens/Officers.php?error=Registration+failed:+" . urlencode($e->getMessage()));
}
} else {
header("Location: ../../Screens/Officers.php");
}

View File

@ -0,0 +1,69 @@
<?php
require_once __DIR__ . '/Authentication_Handler.php';
require_once __DIR__ . '/../../db/config.php';
$Auth = new Authentication_Handler();
$Auth->Check_Auth();
if ($_SESSION['Access_Level'] < 1) {
die("Unauthorized access");
}
function generateUserID($db) {
$tables = ['Officers', 'Voters', 'Candidates'];
do {
$id = sprintf("%02d-%04d", rand(0, 99), rand(0, 9999));
$exists = false;
foreach ($tables as $table) {
$stmt = $db->prepare("SELECT COUNT(*) FROM $table WHERE User_ID = ?");
$stmt->execute([$id]);
if ($stmt->fetchColumn() > 0) {
$exists = true;
break;
}
}
} while ($exists);
return $id;
}
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$db = db();
$email = $_POST['email'] ?? '';
$user_id = $_POST['user_id'] ?? '';
$track = $_POST['track'] ?? '';
$grade = $_POST['grade'] ?? '';
$section = $_POST['section'] ?? '';
$password = $_POST['password'] ?? '';
if (empty($email) || empty($track) || empty($grade) || empty($section)) {
header("Location: ../../Screens/Voters.php?error=Missing+fields");
exit;
}
// User ID logic
if (empty($user_id)) {
$user_id = generateUserID($db);
}
// Password logic
if (empty($password)) {
$password = "School@123";
}
$hashedPassword = password_hash($password, PASSWORD_DEFAULT);
try {
$stmt = $db->prepare("INSERT INTO Voters (User_ID, Email, Password, Track_Cluster, Grade_Level, Section) VALUES (?, ?, ?, ?, ?, ?)");
$stmt->execute([$user_id, $email, $hashedPassword, $track, $grade, $section]);
// Log to Audit Trail
$Auth->Log_Action($_SESSION['User_ID'], $_SESSION['User_Role'], 'Add Voter', "Registered new voter: $email. User ID: $user_id");
header("Location: ../../Screens/Voters.php?success=Voter+registered+with+ID:+$user_id");
} catch (PDOException $e) {
header("Location: ../../Screens/Voters.php?error=Registration+failed:+" . urlencode($e->getMessage()));
}
} else {
header("Location: ../../Screens/Voters.php");
}

View File

@ -0,0 +1,46 @@
<?php
require_once __DIR__ . '/Authentication_Handler.php';
require_once __DIR__ . '/../../db/config.php';
$Auth = new Authentication_Handler();
$Auth->Check_Auth();
if ($_SESSION['Access_Level'] < 3) {
die("Unauthorized access");
}
$user_id = $_GET['user_id'] ?? '';
if (empty($user_id)) {
header("Location: ../../Screens/Officers.php?error=No+user+ID+provided");
exit;
}
if ($user_id === $_SESSION['User_ID']) {
header("Location: ../../Screens/Officers.php?error=You+cannot+delete+yourself");
exit;
}
$db = db();
try {
// Get officer name for logging
$stmt = $db->prepare("SELECT Name, Role FROM Officers WHERE User_ID = ?");
$stmt->execute([$user_id]);
$officer = $stmt->fetch();
if (!$officer) {
header("Location: ../../Screens/Officers.php?error=Officer+not+found");
exit;
}
$stmt = $db->prepare("DELETE FROM Officers WHERE User_ID = ?");
$stmt->execute([$user_id]);
// Log to Audit Trail
$Auth->Log_Action($_SESSION['User_ID'], $_SESSION['User_Role'], 'Delete Officer', "Deleted officer: {$officer['Name']} ({$officer['Role']}). User ID: $user_id");
header("Location: ../../Screens/Officers.php?success=Officer+deleted+successfully");
} catch (PDOException $e) {
header("Location: ../../Screens/Officers.php?error=Delete+failed:+" . urlencode($e->getMessage()));
}

View File

@ -0,0 +1,59 @@
<?php
require_once __DIR__ . '/Authentication_Handler.php';
require_once __DIR__ . '/../../db/config.php';
$Auth = new Authentication_Handler();
$Auth->Check_Auth();
if ($_SESSION['Access_Level'] < 3) {
die("Unauthorized access");
}
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$db = db();
$original_id = $_POST['original_user_id'] ?? '';
$user_id = $_POST['user_id'] ?? '';
$name = $_POST['name'] ?? '';
$email = $_POST['email'] ?? '';
$role = $_POST['role'] ?? '';
$password = $_POST['password'] ?? '';
if (empty($original_id) || empty($user_id) || empty($name) || empty($email) || empty($role)) {
header("Location: ../../Screens/Officers.php?error=Missing+fields");
exit;
}
// Determine Access Level
$accessLevel = 1;
if ($role === 'Admin') $accessLevel = 3;
elseif ($role === 'COMEA Adviser') $accessLevel = 2;
try {
if (!empty($password)) {
$hashedPassword = password_hash($password, PASSWORD_DEFAULT);
$stmt = $db->prepare("UPDATE Officers SET User_ID = ?, Name = ?, Email = ?, Role = ?, Access_Level = ?, Password = ? WHERE User_ID = ?");
$stmt->execute([$user_id, $name, $email, $role, $accessLevel, $hashedPassword, $original_id]);
} else {
$stmt = $db->prepare("UPDATE Officers SET User_ID = ?, Name = ?, Email = ?, Role = ?, Access_Level = ? WHERE User_ID = ?");
$stmt->execute([$user_id, $name, $email, $role, $accessLevel, $original_id]);
}
// If the user edited their own ID, update the session
if ($original_id === $_SESSION['User_ID']) {
$_SESSION['User_ID'] = $user_id;
$_SESSION['User_Name'] = $name;
$_SESSION['User_Role'] = $role;
$_SESSION['Access_Level'] = $accessLevel;
}
// Log to Audit Trail
$Auth->Log_Action($_SESSION['User_ID'], $_SESSION['User_Role'], 'Edit Officer', "Updated officer: $name ($role). User ID: $user_id");
header("Location: ../../Screens/Officers.php?success=Officer+updated+successfully");
} catch (PDOException $e) {
header("Location: ../../Screens/Officers.php?error=Update+failed:+" . urlencode($e->getMessage()));
}
} else {
header("Location: ../../Screens/Officers.php");
}

589
Screens/Candidates.php Normal file
View File

@ -0,0 +1,589 @@
<?php
require_once __DIR__ . '/../Logic/Backend/Authentication_Handler.php';
require_once __DIR__ . '/../db/config.php';
$Auth = new Authentication_Handler();
$Auth->Check_Auth();
if ($_SESSION['Access_Level'] < 1) {
header('Location: Voting_Screen.php');
exit;
}
$db = db();
// Fetch latest election
$election = $db->query("SELECT * FROM Election_History ORDER BY Election_ID DESC LIMIT 1")->fetch();
$electionStatus = $election['Status'] ?? 'Preparing';
$electionId = $election['Election_ID'] ?? 0;
// Parse Parties and Positions
$electionParties = json_decode($election['Parties'] ?? '[]', true) ?: [];
$electionPositionsRaw = json_decode($election['Positions'] ?? '[]', true) ?: [];
// Normalize Positions (Ensure they are objects with name and type)
$electionPositions = [];
$migrationNeeded = false;
foreach ($electionPositionsRaw as $p) {
if (is_array($p) && isset($p['name'])) {
$electionPositions[] = $p;
} else {
$electionPositions[] = ['name' => (string)$p, 'type' => 'Uniform'];
$migrationNeeded = true;
}
}
if ($migrationNeeded && $electionId) {
$stmt = $db->prepare("UPDATE Election_History SET Positions = ? WHERE Election_ID = ?");
$stmt->execute([json_encode($electionPositions), $electionId]);
}
// Handle POST actions for Editors (Preparing Status)
if ($_SERVER['REQUEST_METHOD'] === 'POST' && $electionStatus === 'Preparing') {
if (isset($_POST['action'])) {
$changed = false;
if ($_POST['action'] === 'add_position' && !empty($_POST['new_position'])) {
$newPosName = trim($_POST['new_position']);
$newPosType = $_POST['position_type'] ?? 'Uniform';
$exists = false;
foreach ($electionPositions as $p) {
if ($p['name'] === $newPosName) { $exists = true; break; }
}
if (!$exists) {
$electionPositions[] = ['name' => $newPosName, 'type' => $newPosType];
$changed = true;
}
} elseif ($_POST['action'] === 'remove_position') {
$posNameToRemove = $_POST['position'];
$electionPositions = array_values(array_filter($electionPositions, fn($p) => $p['name'] !== $posNameToRemove));
$changed = true;
} elseif ($_POST['action'] === 'add_party' && !empty($_POST['new_party'])) {
$newParty = trim($_POST['new_party']);
if (!in_array($newParty, $electionParties)) {
$electionParties[] = $newParty;
$changed = true;
}
} elseif ($_POST['action'] === 'remove_party') {
$partyToRemove = $_POST['party'];
$electionParties = array_values(array_filter($electionParties, fn($p) => $p !== $partyToRemove));
$changed = true;
}
if ($changed) {
$stmt = $db->prepare("UPDATE Election_History SET Parties = ?, Positions = ? WHERE Election_ID = ?");
$stmt->execute([json_encode($electionParties), json_encode($electionPositions), $electionId]);
header("Location: Candidates.php");
exit;
}
}
}
// Fetch all available tracks from Voters table
$availableTracks = $db->query("SELECT DISTINCT Track_Cluster FROM Voters WHERE Track_Cluster IS NOT NULL AND Track_Cluster != '' ORDER BY Track_Cluster")->fetchAll(PDO::FETCH_COLUMN);
// Filters (for Ongoing/Finished)
$search = $_GET['search'] ?? '';
$filterPosition = $_GET['position'] ?? '';
$filterParty = $_GET['party'] ?? '';
$filterGrade = $_GET['grade'] ?? '';
// Fetch Stats
$totalCandidates = $db->query("SELECT COUNT(*) FROM Candidates")->fetchColumn();
$positionsCount = count($electionPositions) ?: $db->query("SELECT COUNT(DISTINCT Position) FROM Candidates")->fetchColumn();
$partiesCount = count($electionParties) ?: $db->query("SELECT COUNT(DISTINCT Party) FROM Candidates")->fetchColumn();
// Fetch Breakdowns
$positionsBreakdown = $db->query("SELECT Position as label, COUNT(*) as value FROM Candidates GROUP BY Position ORDER BY value DESC")->fetchAll();
$partiesBreakdown = $db->query("SELECT Party as label, COUNT(*) as value FROM Candidates GROUP BY Party ORDER BY value DESC")->fetchAll();
$grades = $db->query("SELECT Grade_Level as label, COUNT(*) as value FROM Candidates GROUP BY Grade_Level ORDER BY Grade_Level ASC")->fetchAll();
// Build Query for List
$queryStr = "SELECT * FROM Candidates WHERE 1=1";
$params = [];
if ($search) {
$queryStr .= " AND (Name LIKE ? OR Email LIKE ? OR Party LIKE ?)";
$params[] = "%$search%";
$params[] = "%$search%";
$params[] = "%$search%";
}
if ($filterPosition) {
$queryStr .= " AND Position = ?";
$params[] = $filterPosition;
}
if ($filterParty) {
$queryStr .= " AND Party = ?";
$params[] = $filterParty;
}
if ($filterGrade) {
$queryStr .= " AND Grade_Level = ?";
$params[] = $filterGrade;
}
$queryStr .= " ORDER BY Position ASC, Name ASC";
$stmt = $db->prepare($queryStr);
$stmt->execute($params);
$candidatesList = $stmt->fetchAll();
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Candidate Management | Online School Election System</title>
<link rel="stylesheet" href="../Design/Style.css?v=<?php echo time(); ?>">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<style>
.Candidate_Actions_Btn {
height: 44px;
padding: 0 24px;
display: inline-flex;
align-items: center;
justify-content: center;
gap: 10px;
font-size: 0.9rem;
margin-top: 0;
width: auto;
}
.Candidate_Photo_Circle {
width: 40px;
height: 40px;
border-radius: 50%;
background: var(--Primary_Light);
color: var(--Primary_Color);
display: flex;
justify-content: center;
align-items: center;
font-weight: 700;
overflow: hidden;
object-fit: cover;
}
.Editor_Grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 24px;
margin-bottom: 32px;
}
.Editor_Card {
background: white;
border-radius: 12px;
padding: 24px;
border: 1px solid var(--Border_Color);
}
.Item_Tag {
display: inline-flex;
flex-direction: column;
background: var(--Primary_Light);
color: var(--Primary_Color);
padding: 8px 14px;
border-radius: 12px;
font-size: 0.85rem;
font-weight: 600;
margin: 0 8px 8px 0;
position: relative;
min-width: 120px;
}
.Item_Tag_Header {
display: flex;
justify-content: space-between;
align-items: center;
gap: 8px;
margin-bottom: 4px;
}
.Item_Tag_Type {
font-size: 0.65rem;
text-transform: uppercase;
opacity: 0.7;
font-weight: 700;
}
.Item_Tag i {
cursor: pointer;
font-size: 0.75rem;
opacity: 0.7;
transition: opacity 0.2s;
}
.Item_Tag i:hover {
opacity: 1;
}
.Status_Indicator {
display: inline-flex;
align-items: center;
gap: 8px;
padding: 6px 16px;
border-radius: 30px;
font-size: 0.8rem;
font-weight: 700;
text-transform: uppercase;
}
.Status_Preparing { background: #FEF3C7; color: #92400E; }
.Status_Ongoing { background: #DCFCE7; color: #166534; }
.Status_Finished { background: #F1F5F9; color: #475569; }
</style>
</head>
<body>
<div class="Dashboard_Container">
<!-- Sidebar -->
<aside class="Sidebar">
<div class="Sidebar_Header">
<div class="Logo_Text">
<div class="Logo_Title" style="font-size: 1.1rem; color: var(--Primary_Color);">ElectionSystem</div>
<div class="Logo_Subtitle">Administrator Portal</div>
</div>
</div>
<nav class="Sidebar_Nav">
<a href="Election_Dashboard.php" class="Nav_Item">
<i class="fas fa-th-large"></i> Election Dashboard
</a>
<a href="Election_History.php" class="Nav_Item">
<i class="fas fa-vote-yea"></i> Election History
</a>
<a href="Voters.php" class="Nav_Item">
<i class="fas fa-users"></i> Voter Management
</a>
<a href="Candidates.php" class="Nav_Item Active">
<i class="fas fa-user-tie"></i> Candidate Management
</a>
<a href="Officers.php" class="Nav_Item">
<i class="fas fa-user-shield"></i> Officers Management
</a>
<a href="Audit_Trail.html" class="Nav_Item">
<i class="fas fa-file-alt"></i> Reports & Audit
</a>
</nav>
<div style="padding: 24px; border-top: 1px solid var(--Border_Color);">
<a href="Logout.php" class="Nav_Item" style="padding: 0; color: var(--Error_Color);">
<i class="fas fa-sign-out-alt"></i> Logout
</a>
</div>
</aside>
<!-- Main Content -->
<main class="Main_Content">
<!-- Top Bar -->
<header class="Top_Bar">
<div class="Search_Box">
<i class="fas fa-search" style="color: var(--Secondary_Color);"></i>
<input type="text" placeholder="Quick search...">
</div>
<div class="Top_Bar_Actions">
<div style="display: flex; align-items: center; gap: 12px;">
<div style="text-align: right;">
<div style="font-weight: 700; font-size: 0.9rem;"><?php echo htmlspecialchars($_SESSION['User_Name']); ?></div>
<div style="font-size: 0.75rem; color: var(--Text_Secondary);"><?php echo htmlspecialchars($_SESSION['User_Role']); ?></div>
</div>
<div style="width: 40px; height: 40px; background: var(--Primary_Light); color: var(--Primary_Color); border-radius: 50%; display: flex; justify-content: center; align-items: center; font-weight: 700;">
<?php echo strtoupper(substr($_SESSION['User_Name'] ?? 'A', 0, 1)); ?>
</div>
</div>
</div>
</header>
<!-- Content Body -->
<div class="Content_Body">
<div class="Page_Title_Section">
<div style="display: flex; align-items: center; gap: 15px;">
<div style="width: 48px; height: 48px; background: var(--Primary_Light); color: var(--Primary_Color); border-radius: 12px; display: flex; justify-content: center; align-items: center; font-size: 1.5rem;">
<i class="fas fa-user-tie"></i>
</div>
<div>
<h1 class="Page_Title">Candidate Management</h1>
<p class="Page_Subtitle">Managing SY <?php echo $election['Year'] ?? date('Y'); ?> Election</p>
</div>
</div>
<div>
<div class="Status_Indicator Status_<?php echo $electionStatus; ?>">
<i class="fas fa-circle" style="font-size: 0.6rem;"></i>
<?php echo $electionStatus; ?>
</div>
</div>
</div>
<?php if ($electionStatus === 'Preparing'): ?>
<!-- EDITORS (Preparing Mode) -->
<div class="Editor_Grid">
<!-- Positions Editor -->
<div class="Editor_Card">
<h3 class="Card_Title" style="margin-bottom: 15px;"><i class="fas fa-id-badge" style="color: var(--Primary_Color); margin-right: 10px;"></i> Define Positions</h3>
<div style="min-height: 80px; margin-bottom: 20px;">
<?php foreach ($electionPositions as $pos): ?>
<span class="Item_Tag">
<div class="Item_Tag_Header">
<span><?php echo htmlspecialchars($pos['name']); ?></span>
<form method="POST" style="display: inline;">
<input type="hidden" name="action" value="remove_position">
<input type="hidden" name="position" value="<?php echo htmlspecialchars($pos['name']); ?>">
<i class="fas fa-times" onclick="this.parentElement.submit()"></i>
</form>
</div>
<div class="Item_Tag_Type"><?php echo htmlspecialchars($pos['type'] ?? 'Uniform'); ?></div>
</span>
<?php endforeach; ?>
</div>
<form method="POST" style="display: flex; flex-direction: column; gap: 10px;">
<input type="hidden" name="action" value="add_position">
<div style="display: flex; gap: 10px;">
<input type="text" name="new_position" class="Input" placeholder="Position name..." required style="flex: 2;">
<select name="position_type" class="Select" required style="flex: 1; height: 48px;">
<option value="Uniform">Uniform</option>
<option value="Track/Strand Specific">Track/Strand Specific</option>
</select>
<button type="submit" class="Button_Primary" style="width: auto; margin-top: 0; padding: 0 20px;"><i class="fas fa-plus"></i></button>
</div>
</form>
</div>
<!-- Parties Editor -->
<div class="Editor_Card">
<h3 class="Card_Title" style="margin-bottom: 15px;"><i class="fas fa-flag" style="color: var(--Success_Color); margin-right: 10px;"></i> Define Parties</h3>
<div style="min-height: 80px; margin-bottom: 20px;">
<?php foreach ($electionParties as $party): ?>
<span class="Item_Tag" style="background: #DCFCE7; color: #166534; flex-direction: row; align-items: center; justify-content: space-between; min-width: auto; padding: 6px 14px; border-radius: 20px;">
<?php echo htmlspecialchars($party); ?>
<form method="POST" style="display: inline; margin-left: 8px;">
<input type="hidden" name="action" value="remove_party">
<input type="hidden" name="party" value="<?php echo htmlspecialchars($party); ?>">
<i class="fas fa-times" onclick="this.parentElement.submit()"></i>
</form>
</span>
<?php endforeach; ?>
</div>
<form method="POST" style="display: flex; gap: 10px;">
<input type="hidden" name="action" value="add_party">
<input type="text" name="new_party" class="Input" placeholder="Enter party name..." required>
<button type="submit" class="Button_Primary" style="width: auto; margin-top: 0; padding: 0 20px; background: var(--Success_Color);"><i class="fas fa-plus"></i></button>
</form>
</div>
</div>
<!-- Add Candidate Form (Bottom) -->
<div class="Card">
<div class="Card_Header">
<h2 class="Card_Title">Register New Candidate</h2>
</div>
<form method="POST" action="../Logic/Backend/Add_Candidate_Handler.php" enctype="multipart/form-data" style="display: grid; grid-template-columns: repeat(3, 1fr); gap: 20px;">
<div class="Form_Group">
<label class="Label">Full Name</label>
<input type="text" name="name" class="Input" placeholder="e.g. Juan Dela Cruz" required>
</div>
<div class="Form_Group">
<label class="Label">Email Address</label>
<input type="email" name="email" class="Input" placeholder="juan@school.edu" required>
</div>
<div class="Form_Group">
<label class="Label">User ID (Leave blank for random)</label>
<input type="text" name="user_id" class="Input" placeholder="00-0000">
</div>
<div class="Form_Group">
<label class="Label">Position</label>
<select name="position" class="Select" style="width: 100%; height: 50px;" required>
<option value="">Select Position</option>
<?php foreach ($electionPositions as $p): ?>
<option value="<?php echo htmlspecialchars($p['name']); ?>"><?php echo htmlspecialchars($p['name']); ?> (<?php echo $p['type']; ?>)</option>
<?php endforeach; ?>
</select>
</div>
<div class="Form_Group">
<label class="Label">Political Party</label>
<select name="party" class="Select" style="width: 100%; height: 50px;" required>
<option value="">Select Party</option>
<option value="Independent">Independent</option>
<?php foreach ($electionParties as $p): ?>
<option value="<?php echo htmlspecialchars($p); ?>"><?php echo htmlspecialchars($p); ?></option>
<?php endforeach; ?>
</select>
</div>
<div class="Form_Group">
<label class="Label">Grade Level</label>
<select name="grade" class="Select" style="width: 100%; height: 50px;" required>
<option value="11">Grade 11</option>
<option value="12">Grade 12</option>
</select>
</div>
<div class="Form_Group">
<label class="Label">Track / Strand</label>
<select name="track" class="Select" style="width: 100%; height: 50px;" required>
<option value="">Select Track/Strand</option>
<?php foreach ($availableTracks as $t): ?>
<option value="<?php echo htmlspecialchars($t); ?>"><?php echo htmlspecialchars($t); ?></option>
<?php endforeach; ?>
</select>
</div>
<div class="Form_Group">
<label class="Label">Password (Leave blank for default)</label>
<input type="password" name="password" class="Input" placeholder="Set password">
</div>
<div class="Form_Group">
<label class="Label">Candidate Photo</label>
<input type="file" name="photo" class="Input" accept="image/*">
</div>
<div class="Form_Group" style="grid-column: span 3; display: flex; justify-content: flex-end;">
<button type="submit" class="Button_Primary" style="margin-top: 0; width: 200px;"><i class="fas fa-user-plus"></i> Add Candidate</button>
</div>
</form>
</div>
<?php else: ?>
<!-- DASHBOARD VIEW (Ongoing/Finished Mode) -->
<!-- Primary Stats -->
<div class="Stat_Cards_Grid" style="margin-bottom: 20px;">
<div class="Stat_Card">
<div class="Stat_Label" style="text-transform: uppercase; font-size: 0.7rem; font-weight: 700;">Total Candidates</div>
<div class="Stat_Value" style="color: var(--Primary_Color); font-size: 2.5rem; margin-top: 10px;"><?php echo $totalCandidates; ?></div>
</div>
<div class="Stat_Card">
<div class="Stat_Label" style="text-transform: uppercase; font-size: 0.7rem; font-weight: 700; color: var(--Secondary_Color);">Unique Positions</div>
<div class="Stat_Value" style="color: var(--Secondary_Color); font-size: 2.5rem; margin-top: 10px;"><?php echo $positionsCount; ?></div>
</div>
<div class="Stat_Card">
<div class="Stat_Label" style="text-transform: uppercase; font-size: 0.7rem; font-weight: 700; color: var(--Success_Color);">Active Parties</div>
<div class="Stat_Value" style="color: var(--Success_Color); font-size: 2.5rem; margin-top: 10px;"><?php echo $partiesCount; ?></div>
</div>
</div>
<!-- Breakdown Stats -->
<div class="Summary_Breakdowns">
<div class="Breakdown_Card">
<h3 class="Breakdown_Title">Candidates by Position</h3>
<?php if (empty($positionsBreakdown)): ?><p style="font-size: 0.8rem; color: var(--Text_Secondary);">No data available</p><?php endif; ?>
<?php foreach (array_slice($positionsBreakdown, 0, 5) as $pos): ?>
<div class="Breakdown_Item">
<span><?php echo htmlspecialchars($pos['label'] ?: 'N/A'); ?></span>
<span style="font-weight: 700;"><?php echo $pos['value']; ?></span>
</div>
<?php endforeach; ?>
</div>
<div class="Breakdown_Card">
<h3 class="Breakdown_Title">Candidates by Party</h3>
<?php if (empty($partiesBreakdown)): ?><p style="font-size: 0.8rem; color: var(--Text_Secondary);">No data available</p><?php endif; ?>
<?php foreach (array_slice($partiesBreakdown, 0, 5) as $party): ?>
<div class="Breakdown_Item">
<span><?php echo htmlspecialchars($party['label'] ?: 'Independent'); ?></span>
<span style="font-weight: 700;"><?php echo $party['value']; ?></span>
</div>
<?php endforeach; ?>
</div>
<div class="Breakdown_Card">
<h3 class="Breakdown_Title">Candidates by Grade</h3>
<?php if (empty($grades)): ?><p style="font-size: 0.8rem; color: var(--Text_Secondary);">No data available</p><?php endif; ?>
<?php foreach ($grades as $grade): ?>
<div class="Breakdown_Item">
<span>Grade <?php echo htmlspecialchars($grade['label'] ?: 'N/A'); ?></span>
<span style="font-weight: 700;"><?php echo $grade['value']; ?></span>
</div>
<?php endforeach; ?>
</div>
</div>
<!-- Table Card -->
<div class="Card" style="padding: 0; overflow: hidden;">
<!-- Filters -->
<form method="GET" class="Filter_Bar" style="display: flex; flex-wrap: nowrap; gap: 20px; align-items: flex-end; background: transparent; border: none; border-bottom: 1px solid var(--Border_Color); border-radius: 0; margin-bottom: 0; padding: 24px;">
<div class="Filter_Group" style="flex: 2;">
<label>Search</label>
<div class="Search_Input_Wrapper">
<i class="fas fa-search"></i>
<input type="text" name="search" class="Input" placeholder="Search by name, email or party" value="<?php echo htmlspecialchars($search); ?>">
</div>
</div>
<div class="Filter_Group" style="flex: 1;">
<label>Position</label>
<select name="position" class="Select" onchange="this.form.submit()" style="width: 100%;">
<option value="">All Positions</option>
<?php foreach ($electionPositions as $p): ?>
<option value="<?php echo htmlspecialchars($p['name']); ?>" <?php echo $filterPosition == $p['name'] ? 'selected' : ''; ?>><?php echo htmlspecialchars($p['name']); ?></option>
<?php endforeach; ?>
</select>
</div>
<div class="Filter_Group" style="flex: 1;">
<label>Party</label>
<select name="party" class="Select" onchange="this.form.submit()" style="width: 100%;">
<option value="">All Parties</option>
<?php foreach ($electionParties as $p): ?>
<option value="<?php echo htmlspecialchars($p); ?>" <?php echo $filterParty == $p ? 'selected' : ''; ?>><?php echo htmlspecialchars($p); ?></option>
<?php endforeach; ?>
</select>
</div>
<div class="Filter_Group" style="flex: 1;">
<label>Grade</label>
<select name="grade" class="Select" onchange="this.form.submit()" style="width: 100%;">
<option value="">All Grades</option>
<?php
$allGradesList = $db->query("SELECT DISTINCT Grade_Level FROM Candidates WHERE Grade_Level IS NOT NULL ORDER BY Grade_Level")->fetchAll(PDO::FETCH_COLUMN);
foreach ($allGradesList as $g): ?>
<option value="<?php echo htmlspecialchars($g); ?>" <?php echo $filterGrade == $g ? 'selected' : ''; ?>>Grade <?php echo htmlspecialchars($g); ?></option>
<?php endforeach; ?>
</select>
</div>
</form>
<div class="Table_Wrapper">
<table>
<thead>
<tr>
<th>Candidate</th>
<th>Position</th>
<th>Party</th>
<th>Grade/Track</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
<?php if (empty($candidatesList)): ?>
<tr>
<td colspan="5" style="text-align: center; padding: 40px; color: var(--Text_Secondary);">
No candidates found matching your criteria.
</td>
</tr>
<?php else: ?>
<?php foreach ($candidatesList as $candidate): ?>
<tr>
<td>
<div style="display: flex; align-items: center; gap: 12px;">
<div class="Candidate_Photo_Circle">
<?php if (!empty($candidate['Photo'])): ?>
<img src="<?php echo htmlspecialchars($candidate['Photo']); ?>" alt="" style="width: 100%; height: 100%; object-fit: cover;">
<?php else: ?>
<?php echo strtoupper(substr($candidate['Name'] ?? 'C', 0, 1)); ?>
<?php endif; ?>
</div>
<div>
<div style="font-weight: 600;"><?php echo htmlspecialchars($candidate['Name']); ?></div>
<div style="font-size: 0.75rem; color: var(--Text_Secondary);"><?php echo htmlspecialchars($candidate['User_ID']); ?> | <?php echo htmlspecialchars($candidate['Email']); ?></div>
</div>
</div>
</td>
<td>
<span class="Badge" style="background: var(--Primary_Light); color: var(--Primary_Color);"><?php echo htmlspecialchars($candidate['Position'] ?: 'N/A'); ?></span>
</td>
<td>
<div style="font-weight: 500;"><?php echo htmlspecialchars($candidate['Party'] ?: 'Independent'); ?></div>
</td>
<td>
<div style="font-size: 0.9rem;">Grade <?php echo htmlspecialchars($candidate['Grade_Level'] ?: 'N/A'); ?></div>
<div style="font-size: 0.75rem; color: var(--Text_Secondary);"><?php echo htmlspecialchars($candidate['Track_Cluster'] ?: 'N/A'); ?></div>
</td>
<td>
<div style="display: flex; gap: 8px;">
<button title="Edit" style="background: none; border: none; color: var(--Secondary_Color); cursor: pointer; font-size: 1rem;"><i class="fas fa-edit"></i></button>
<button title="Delete" style="background: none; border: none; color: var(--Error_Color); cursor: pointer; font-size: 1rem;"><i class="fas fa-trash"></i></button>
</div>
</td>
</tr>
<?php endforeach; ?>
<?php endif; ?>
</tbody>
</table>
</div>
<div style="padding: 15px 24px; font-size: 0.85rem; color: var(--Text_Secondary); border-top: 1px solid var(--Border_Color);">
Showing <?php echo count($candidatesList); ?> of <?php echo $totalCandidates; ?> candidates
</div>
</div>
<?php endif; ?>
</div>
</main>
</div>
</body>
</html>

View File

@ -10,14 +10,25 @@ if ($_SESSION['Access_Level'] < 1) {
exit;
}
// Fetch Stats
$db = db();
// Handle Status Change
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['update_status'])) {
$newStatus = $_POST['new_status'];
$electionId = $_POST['election_id'];
$stmt = $db->prepare("UPDATE Election_History SET Status = ? WHERE Election_ID = ?");
$stmt->execute([$newStatus, $electionId]);
header("Location: Election_Dashboard.php");
exit;
}
// Fetch Stats
$totalVoters = $db->query("SELECT COUNT(*) FROM Voters")->fetchColumn();
$totalCandidates = $db->query("SELECT COUNT(*) FROM Candidates")->fetchColumn();
$totalVotes = $db->query("SELECT COUNT(*) FROM Voters WHERE Has_Voted = 1")->fetchColumn();
// Fetch Active Elections
$activeElections = $db->query("SELECT * FROM Election_History WHERE Status = 'Active' ORDER BY Start_Date DESC")->fetchAll();
// Fetch Active/Preparing/Ongoing Elections
$activeElections = $db->query("SELECT * FROM Election_History WHERE Status IN ('Preparing', 'Ongoing', 'Active') ORDER BY Start_Date DESC")->fetchAll();
?>
<!DOCTYPE html>
@ -28,6 +39,18 @@ $activeElections = $db->query("SELECT * FROM Election_History WHERE Status = 'Ac
<title>Election Dashboard | Online School Election System</title>
<link rel="stylesheet" href="../Design/Style.css?v=<?php echo time(); ?>">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<style>
.Status_Badge {
padding: 4px 12px;
border-radius: 20px;
font-size: 0.75rem;
font-weight: 700;
text-transform: uppercase;
}
.Status_Active, .Status_Ongoing { background: #DCFCE7; color: #166534; }
.Status_Preparing { background: #FEF3C7; color: #92400E; }
.Status_Completed, .Status_Finished { background: #F1F5F9; color: #475569; }
</style>
</head>
<body>
<div class="Dashboard_Container">
@ -49,10 +72,10 @@ $activeElections = $db->query("SELECT * FROM Election_History WHERE Status = 'Ac
<a href="Voters.php" class="Nav_Item">
<i class="fas fa-users"></i> Voter Management
</a>
<a href="Candidates.html" class="Nav_Item">
<a href="Candidates.php" class="Nav_Item">
<i class="fas fa-user-tie"></i> Candidate Management
</a>
<a href="Officers.html" class="Nav_Item">
<a href="Officers.php" class="Nav_Item">
<i class="fas fa-user-shield"></i> Officers Management
</a>
<a href="Audit_Trail.html" class="Nav_Item">
@ -118,7 +141,7 @@ $activeElections = $db->query("SELECT * FROM Election_History WHERE Status = 'Ac
<!-- Main Content: Active Elections -->
<div class="Card">
<div class="Card_Header">
<h2 class="Card_Title">Active Elections</h2>
<h2 class="Card_Title">Active & Upcoming Elections</h2>
<button class="Button_Primary" style="width: auto; padding: 8px 16px; font-size: 0.85rem;">
<i class="fas fa-plus"></i> New Election
</button>
@ -128,16 +151,15 @@ $activeElections = $db->query("SELECT * FROM Election_History WHERE Status = 'Ac
<thead>
<tr>
<th>Election Title</th>
<th>Start Date</th>
<th>End Date</th>
<th>Period</th>
<th>Status</th>
<th>Actions</th>
<th>Quick Actions</th>
</tr>
</thead>
<tbody>
<?php if (empty($activeElections)): ?>
<tr>
<td colspan="5" style="text-align: center; padding: 40px; color: var(--Text_Secondary);">
<td colspan="4" style="text-align: center; padding: 40px; color: var(--Text_Secondary);">
No active elections found. <a href="#" style="color: var(--Primary_Color); font-weight: 600;">Create one now.</a>
</td>
</tr>
@ -145,12 +167,22 @@ $activeElections = $db->query("SELECT * FROM Election_History WHERE Status = 'Ac
<?php foreach ($activeElections as $election): ?>
<tr>
<td style="font-weight: 600;">School Year <?php echo $election['Year']; ?> Election</td>
<td><?php echo date('M d, Y', strtotime($election['Start_Date'])); ?></td>
<td><?php echo date('M d, Y', strtotime($election['End_Date'])); ?></td>
<td><span class="Badge Badge_Success">Active</span></td>
<td><?php echo date('M d', strtotime($election['Start_Date'])); ?> - <?php echo date('M d, Y', strtotime($election['End_Date'])); ?></td>
<td>
<a href="#" style="color: var(--Primary_Color); margin-right: 12px;"><i class="fas fa-eye"></i></a>
<a href="#" style="color: var(--Secondary_Color);"><i class="fas fa-cog"></i></a>
<span class="Status_Badge Status_<?php echo $election['Status']; ?>">
<?php echo $election['Status']; ?>
</span>
</td>
<td>
<form method="POST" style="display: inline-flex; gap: 8px; align-items: center;">
<input type="hidden" name="election_id" value="<?php echo $election['Election_ID']; ?>">
<select name="new_status" class="Select" style="font-size: 0.75rem; padding: 4px 8px;">
<option value="Preparing" <?php echo $election['Status'] == 'Preparing' ? 'selected' : ''; ?>>Preparing</option>
<option value="Ongoing" <?php echo $election['Status'] == 'Ongoing' ? 'selected' : ''; ?>>Ongoing</option>
<option value="Finished" <?php echo $election['Status'] == 'Finished' ? 'selected' : ''; ?>>Finished</option>
</select>
<button type="submit" name="update_status" class="Button_Primary" style="width: auto; padding: 6px 10px; margin-top: 0; font-size: 0.7rem;">Update</button>
</form>
</td>
</tr>
<?php endforeach; ?>

View File

@ -57,10 +57,10 @@ $history = $db->query("SELECT * FROM Election_History ORDER BY Year DESC")->fetc
<a href="Voters.php" class="Nav_Item">
<i class="fas fa-users"></i> Voter Management
</a>
<a href="Candidates.html" class="Nav_Item">
<a href="Candidates.php" class="Nav_Item">
<i class="fas fa-user-tie"></i> Candidate Management
</a>
<a href="Officers.html" class="Nav_Item">
<a href="Officers.php" class="Nav_Item">
<i class="fas fa-user-shield"></i> Officers Management
</a>
<a href="Audit_Trail.html" class="Nav_Item">

View File

@ -1,30 +1,8 @@
<?php
session_start();
require_once __DIR__ . '/../Logic/Backend/Authentication_Handler.php';
$Error = '';
$ShowModal = false;
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$ShowModal = true;
$Handler = new Authentication_Handler();
$Result = $Handler->Login(
$_POST['User_Type'],
$_POST['Email'],
$_POST['Password'],
$_POST['User_ID']
);
if ($Result['success']) {
if ($_SESSION['Access_Level'] >= 1) {
header('Location: Election_Dashboard.php');
} else {
header('Location: Voting_Screen.php');
}
exit;
} else {
$Error = $Result['error'];
}
if (isset($_SESSION['User_ID'])) {
header('Location: Election_Dashboard.php');
exit;
}
?>
<!DOCTYPE html>
@ -32,124 +10,145 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Click to Vote - School Election System</title>
<title>Login | Online School Election System</title>
<link rel="stylesheet" href="../Design/Style.css?v=<?php echo time(); ?>">
<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;800&display=swap" rel="stylesheet">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<style>
.Landing_Wrapper {
background-image: url('../assets/pasted-20260204-200305-388a4105.jpg');
}
.Landing_Info_Card {
background: rgba(255, 255, 255, 0.05);
backdrop-filter: blur(10px);
padding: 48px;
border-radius: 32px;
border: 1px solid rgba(255, 255, 255, 0.1);
max-width: 850px;
width: 90%;
margin: 0 auto;
}
.Landing_Title {
font-size: clamp(2rem, 6vw, 3.5rem);
margin-bottom: 16px;
}
.Landing_Description {
margin-bottom: 32px;
font-size: 1.1rem;
}
</style>
</head>
<body>
<div class="Landing_Wrapper">
<div class="Landing_Header">
<!-- Top Left Pill (Logo Section) -->
<header class="Landing_Header">
<div class="Logo_Pill">
<div class="Logo_Circle">
<img src="../assets/pasted-20260204-200612-dffb96f0.png" alt="INHS Logo">
<img src="../assets/pasted-20260204-200612-dffb96f0.png" alt="School Logo">
</div>
<div class="Logo_Text">
<div class="Logo_Title">Iloilo National High School</div>
<div class="Logo_Subtitle">Luna St., La Paz, Iloilo City</div>
</div>
</div>
</div>
</header>
<div class="Landing_Content">
<h1 class="Landing_Title">Click to Vote</h1>
<p class="Landing_Description">
This portal is currently a <strong>PROTOTYPE MODEL</strong> under active development to demonstrate the digital election process.
Please be aware that all features are in a testing phase and interactions are for demonstration purposes only.
We are continuously refining the system to ensure a seamless experience for the final implementation.
</p>
<button class="Btn_Landing_Login" onclick="ToggleModal(true)">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
<path d="M15 3h4a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2h-4"></path>
<polyline points="10 17 15 12 10 7"></polyline>
<line x1="15" y1="12" x2="3" y2="12"></line>
</svg>
Login
</button>
</div>
<!-- Main Landing Content wrapped in a card for visual separation -->
<main class="Landing_Content">
<div class="Landing_Info_Card">
<h1 class="Landing_Title">CLICK TO VOTE</h1>
<p class="Landing_Description">
This portal is currently a <strong>PROTOTYPE MODEL</strong> under active development to demonstrate the digital election process.
Please be aware that all features are in a testing phase and interactions are for demonstration purposes only. We are
continuously refining the system to ensure a seamless experience for the final implementation.
</p>
<button id="Open_Login_Btn" class="Btn_Landing_Login">
<i class="fas fa-sign-in-alt"></i> LOGIN
</button>
</div>
</main>
<div class="Landing_Footer">
© 2026 Click to Vote System [PROTOTYPE]. All Rights Reserved.
</div>
<footer class="Landing_Footer">
&copy; 2026 Click to Vote System [PROTOTYPE]. All Rights Reserved.
</footer>
</div>
<!-- Login Modal -->
<div id="Login_Modal" class="Modal_Overlay <?php echo $ShowModal ? 'Active' : ''; ?>">
<div id="Login_Modal" class="Modal_Overlay">
<div class="Login_Card">
<button class="Btn_Close_Modal" onclick="ToggleModal(false)">&times;</button>
<h2 class="Title_Large">Sign In</h2>
<p class="Text_Muted">Access the election management system</p>
<?php if ($Error): ?>
<div class="Alert Alert_Error"><?php echo $Error; ?></div>
<?php endif; ?>
<form action="Login.php" method="POST">
<div class="Form_Group">
<label class="Label" for="User_Type">User Type</label>
<select class="Input" name="User_Type" id="User_Type" required>
<option value="Admin">Admin</option>
<option value="COMEA Adviser">COMEA Adviser</option>
<option value="COMEA Officer">COMEA Officer</option>
<option value="Voter">Voter</option>
</select>
<button class="Btn_Close_Modal" id="Close_Login_Btn" title="Close">
<i class="fas fa-times"></i>
</button>
<div style="text-align: center; margin-bottom: 24px;">
<div style="width: 64px; height: 64px; background: var(--Primary_Light); color: var(--Primary_Color); border-radius: 16px; display: flex; justify-content: center; align-items: center; font-size: 2rem; margin: 0 auto 16px;">
<i class="fas fa-vote-yea"></i>
</div>
<h1 class="Title_Large">Welcome Back</h1>
<p class="Text_Muted">Sign in to manage the school election</p>
</div>
<div id="Error_Alert" class="Alert Alert_Error" style="padding: 12px; border-radius: 10px; font-size: 0.85rem; margin-bottom: 20px; display: none; background: #FEE2E2; color: #EF4444; border: 1px solid #FCA5A5;">
<i class="fas fa-exclamation-circle"></i> <span id="Error_Message"></span>
</div>
<form action="../Logic/Backend/Authentication_Handler.php" method="POST">
<div class="Form_Group">
<label class="Label" for="User_ID">User ID</label>
<input class="Input" type="text" name="User_ID" id="User_ID" placeholder="e.g. ADMIN-001" required>
</div>
<div class="Form_Group">
<label class="Label" for="Email">Email Address</label>
<input class="Input" type="email" name="Email" id="Email" placeholder="name@school.edu" required>
</div>
<div class="Form_Group">
<label class="Label" for="Password">Password</label>
<div class="Password_Toggle_Wrapper">
<input class="Input" type="password" name="Password" id="Password" required>
<button type="button" class="Toggle_Button" onclick="TogglePassword()">SHOW</button>
<div style="position: relative;">
<i class="fas fa-id-card" style="position: absolute; left: 16px; top: 50%; transform: translateY(-50%); color: var(--Text_Secondary);"></i>
<input class="Input" type="text" name="User_ID" id="User_ID" placeholder="e.g. 00-0000" required style="padding-left: 48px;">
</div>
</div>
<button type="submit" class="Button_Primary">Login to System</button>
<div class="Form_Group">
<label class="Label" for="Password">Password</label>
<div style="position: relative;">
<i class="fas fa-lock" style="position: absolute; left: 16px; top: 50%; transform: translateY(-50%); color: var(--Text_Secondary);"></i>
<input class="Input" type="password" name="Password" id="Password" placeholder="••••••••" required style="padding-left: 48px;">
</div>
</div>
<div class="Form_Group" style="margin-top: 10px;">
<label class="Label" style="display: flex; align-items: center; gap: 8px; font-weight: 500; cursor: pointer; text-transform: none; letter-spacing: normal;">
<input type="checkbox" name="User_Type" value="Voter" style="width: 16px; height: 16px;">
Sign in as Voter
</label>
</div>
<button type="submit" class="Button_Primary">
Sign In
</button>
</form>
</div>
</div>
<script>
function ToggleModal(show) {
const modal = document.getElementById('Login_Modal');
if (show) {
modal.classList.add('Active');
} else {
modal.classList.remove('Active');
}
}
const loginModal = document.getElementById('Login_Modal');
const openLoginBtn = document.getElementById('Open_Login_Btn');
const closeLoginBtn = document.getElementById('Close_Login_Btn');
function TogglePassword() {
const passwordInput = document.getElementById('Password');
const toggleBtn = event.currentTarget;
if (passwordInput.type === 'password') {
passwordInput.type = 'text';
toggleBtn.textContent = 'HIDE';
} else {
passwordInput.type = 'password';
toggleBtn.textContent = 'SHOW';
}
}
// Close modal on escape key
document.addEventListener('keydown', (e) => {
if (e.key === 'Escape') ToggleModal(false);
openLoginBtn.addEventListener('click', () => {
loginModal.classList.add('Active');
});
// Close modal on clicking outside the card
document.getElementById('Login_Modal').addEventListener('click', (e) => {
if (e.target.id === 'Login_Modal') ToggleModal(false);
closeLoginBtn.addEventListener('click', () => {
loginModal.classList.remove('Active');
});
// Close modal when clicking outside the card
loginModal.addEventListener('click', (e) => {
if (e.target === loginModal) {
loginModal.classList.remove('Active');
}
});
const urlParams = new URLSearchParams(window.location.search);
if (urlParams.has('error')) {
loginModal.classList.add('Active');
const errorAlert = document.getElementById('Error_Alert');
const errorMessage = document.getElementById('Error_Message');
errorAlert.style.display = 'block';
errorMessage.textContent = urlParams.get('error');
}
</script>
</body>
</html>

487
Screens/Officers.php Normal file
View File

@ -0,0 +1,487 @@
<?php
require_once __DIR__ . '/../Logic/Backend/Authentication_Handler.php';
require_once __DIR__ . '/../db/config.php';
$Auth = new Authentication_Handler();
$Auth->Check_Auth();
// Only Admins can access Officer Management (Access Level 3)
if ($_SESSION['Access_Level'] < 3) {
header('Location: Election_Dashboard.php');
exit;
}
$db = db();
// Fetch Officers grouped by role
$admins = $db->query("SELECT * FROM Officers WHERE Role = 'Admin' ORDER BY Name ASC")->fetchAll();
$advisers = $db->query("SELECT * FROM Officers WHERE Role = 'COMEA Adviser' ORDER BY Name ASC")->fetchAll();
$comeaOfficers = $db->query("SELECT * FROM Officers WHERE Role = 'COMEA Officer' ORDER BY Name ASC")->fetchAll();
// Get counts for stats
$totalAdmins = count($admins);
$totalAdvisers = count($advisers);
$totalOfficers = count($comeaOfficers);
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Officer Management | Online School Election System</title>
<link rel="stylesheet" href="../Design/Style.css?v=<?php echo time(); ?>">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<style>
.Officer_Card_Container {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(350px, 1fr));
gap: 24px;
margin-top: 24px;
}
.Officer_Role_Card {
background: white;
border-radius: 16px;
border: 1px solid var(--Border_Color);
overflow: hidden;
display: flex;
flex-direction: column;
}
.Role_Card_Header {
padding: 20px 24px;
border-bottom: 1px solid var(--Border_Color);
display: flex;
justify-content: space-between;
align-items: center;
}
.Role_Card_Title {
font-size: 1.1rem;
font-weight: 700;
display: flex;
align-items: center;
gap: 12px;
}
.Role_Icon {
width: 36px;
height: 36px;
border-radius: 8px;
display: flex;
justify-content: center;
align-items: center;
font-size: 1rem;
}
.Icon_Admin { background: #EEF2FF; color: #4F46E5; }
.Icon_Adviser { background: #FFF7ED; color: #EA580C; }
.Icon_Officer { background: #ECFDF5; color: #059669; }
.Officer_List {
padding: 12px;
flex: 1;
}
.Officer_Item {
padding: 12px;
border-radius: 12px;
display: flex;
align-items: center;
gap: 12px;
transition: background 0.2s;
}
.Officer_Item:hover {
background: var(--Background_Color);
}
.Officer_Avatar {
width: 44px;
height: 44px;
border-radius: 50%;
background: var(--Primary_Light);
color: var(--Primary_Color);
display: flex;
justify-content: center;
align-items: center;
font-weight: 700;
overflow: hidden;
}
.Officer_Info {
flex: 1;
}
.Officer_Name {
font-weight: 600;
font-size: 0.95rem;
}
.Officer_Email {
font-size: 0.75rem;
color: var(--Text_Secondary);
}
.Officer_Actions {
display: flex;
gap: 4px;
}
.Officer_Action_Btn {
padding: 8px;
border: none;
background: none;
color: var(--Text_Secondary);
cursor: pointer;
border-radius: 6px;
transition: all 0.2s;
}
.Officer_Action_Btn:hover {
background: #F1F5F9;
color: var(--Primary_Color);
}
.Btn_Delete:hover {
background: #FEE2E2;
color: var(--Error_Color);
}
.Add_Section {
background: white;
border-radius: 16px;
padding: 24px;
border: 1px solid var(--Border_Color);
margin-bottom: 32px;
}
.Form_Grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 20px;
}
/* Modal Styles */
.Modal {
display: none;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0,0,0,0.5);
z-index: 1000;
justify-content: center;
align-items: center;
backdrop-filter: blur(4px);
}
.Modal_Content {
background: white;
padding: 32px;
border-radius: 20px;
width: 90%;
max-width: 500px;
position: relative;
box-shadow: 0 20px 25px -5px rgba(0,0,0,0.1);
}
.Modal_Header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 24px;
}
.Modal_Title {
font-size: 1.25rem;
font-weight: 700;
}
.Modal_Close {
cursor: pointer;
font-size: 1.25rem;
color: var(--Text_Secondary);
}
</style>
</head>
<body>
<div class="Dashboard_Container">
<!-- Sidebar -->
<aside class="Sidebar">
<div class="Sidebar_Header">
<div class="Logo_Text">
<div class="Logo_Title" style="font-size: 1.1rem; color: var(--Primary_Color);">ElectionSystem</div>
<div class="Logo_Subtitle">Administrator Portal</div>
</div>
</div>
<nav class="Sidebar_Nav">
<a href="Election_Dashboard.php" class="Nav_Item">
<i class="fas fa-th-large"></i> Election Dashboard
</a>
<a href="Election_History.php" class="Nav_Item">
<i class="fas fa-vote-yea"></i> Election History
</a>
<a href="Voters.php" class="Nav_Item">
<i class="fas fa-users"></i> Voter Management
</a>
<a href="Candidates.php" class="Nav_Item">
<i class="fas fa-user-tie"></i> Candidate Management
</a>
<a href="Officers.php" class="Nav_Item Active">
<i class="fas fa-user-shield"></i> Officers Management
</a>
<a href="Audit_Trail.html" class="Nav_Item">
<i class="fas fa-file-alt"></i> Reports & Audit
</a>
</nav>
<div style="padding: 24px; border-top: 1px solid var(--Border_Color);">
<a href="Logout.php" class="Nav_Item" style="padding: 0; color: var(--Error_Color);">
<i class="fas fa-sign-out-alt"></i> Logout
</a>
</div>
</aside>
<!-- Main Content -->
<main class="Main_Content">
<!-- Top Bar -->
<header class="Top_Bar">
<div class="Search_Box">
<i class="fas fa-search" style="color: var(--Secondary_Color);"></i>
<input type="text" placeholder="Search officers...">
</div>
<div class="Top_Bar_Actions">
<div style="display: flex; align-items: center; gap: 12px;">
<div style="text-align: right;">
<div style="font-weight: 700; font-size: 0.9rem;"><?php echo htmlspecialchars($_SESSION['User_Name']); ?></div>
<div style="font-size: 0.75rem; color: var(--Text_Secondary);"><?php echo htmlspecialchars($_SESSION['User_Role']); ?></div>
</div>
<div style="width: 40px; height: 40px; background: var(--Primary_Light); color: var(--Primary_Color); border-radius: 50%; display: flex; justify-content: center; align-items: center; font-weight: 700;">
<?php echo strtoupper(substr($_SESSION['User_Name'] ?? 'A', 0, 1)); ?>
</div>
</div>
</div>
</header>
<!-- Content Body -->
<div class="Content_Body">
<?php if (isset($_GET['success'])): ?>
<div class="Badge Badge_Success" style="width: 100%; padding: 15px; margin-bottom: 20px; text-align: center;">
<i class="fas fa-check-circle"></i> <?php echo htmlspecialchars($_GET['success']); ?>
</div>
<?php endif; ?>
<?php if (isset($_GET['error'])): ?>
<div class="Badge" style="width: 100%; padding: 15px; margin-bottom: 20px; text-align: center; background: #FEE2E2; color: #EF4444;">
<i class="fas fa-exclamation-circle"></i> <?php echo htmlspecialchars($_GET['error']); ?>
</div>
<?php endif; ?>
<div class="Page_Title_Section">
<div style="display: flex; align-items: center; gap: 15px;">
<div style="width: 48px; height: 48px; background: var(--Primary_Light); color: var(--Primary_Color); border-radius: 12px; display: flex; justify-content: center; align-items: center; font-size: 1.5rem;">
<i class="fas fa-user-shield"></i>
</div>
<div>
<h1 class="Page_Title">Officer Management</h1>
<p class="Page_Subtitle">Manage system administrators and COMEA personnel</p>
</div>
</div>
</div>
<!-- Registration Form -->
<div class="Add_Section">
<h2 class="Card_Title" style="margin-bottom: 20px;"><i class="fas fa-plus-circle" style="color: var(--Primary_Color);"></i> Register New Officer</h2>
<form action="../Logic/Backend/Add_Officer_Handler.php" method="POST">
<div class="Form_Grid">
<div class="Form_Group">
<label class="Label">Full Name</label>
<input type="text" name="name" class="Input" placeholder="Enter name" required>
</div>
<div class="Form_Group">
<label class="Label">Email</label>
<input type="email" name="email" class="Input" placeholder="email@school.edu" required>
</div>
<div class="Form_Group">
<label class="Label">User ID (Leave blank for random)</label>
<input type="text" name="user_id" class="Input" placeholder="00-0000">
</div>
<div class="Form_Group">
<label class="Label">Role</label>
<select name="role" class="Select" style="width: 100%; height: 50px;" required>
<option value="Admin">Admin</option>
<option value="COMEA Adviser">COMEA Adviser</option>
<option value="COMEA Officer">COMEA Officer</option>
</select>
</div>
<div class="Form_Group">
<label class="Label">Password (Leave blank for default)</label>
<input type="password" name="password" class="Input" placeholder="Set password">
</div>
<div class="Form_Group" style="display: flex; align-items: flex-end;">
<button type="submit" class="Button_Primary" style="margin-top: 0; width: 100%;">
<i class="fas fa-save"></i> Save Officer
</button>
</div>
</div>
</form>
</div>
<!-- Roles Cards -->
<div class="Officer_Card_Container">
<!-- Admin Card -->
<div class="Officer_Role_Card">
<div class="Role_Card_Header">
<div class="Role_Card_Title">
<div class="Role_Icon Icon_Admin"><i class="fas fa-user-cog"></i></div>
Admins
</div>
<span class="Badge Badge_Success"><?php echo $totalAdmins; ?> Active</span>
</div>
<div class="Officer_List">
<?php foreach ($admins as $admin): ?>
<div class="Officer_Item">
<div class="Officer_Avatar">
<?php if (!empty($admin['Photo'])): ?>
<img src="<?php echo htmlspecialchars($admin['Photo']); ?>" alt="" style="width: 100%; height: 100%; object-fit: cover;">
<?php else: ?>
<?php echo strtoupper(substr($admin['Name'], 0, 1)); ?>
<?php endif; ?>
</div>
<div class="Officer_Info">
<div class="Officer_Name"><?php echo htmlspecialchars($admin['Name']); ?></div>
<div class="Officer_Email"><?php echo htmlspecialchars($admin['User_ID']); ?> | <?php echo htmlspecialchars($admin['Email']); ?></div>
</div>
<div class="Officer_Actions">
<button class="Officer_Action_Btn" title="Edit" onclick='openEditModal(<?php echo json_encode($admin); ?>)'><i class="fas fa-edit"></i></button>
<?php if ($admin['User_ID'] !== $_SESSION['User_ID']): ?>
<button class="Officer_Action_Btn Btn_Delete" title="Delete" onclick="deleteOfficer('<?php echo $admin['User_ID']; ?>', '<?php echo htmlspecialchars($admin['Name']); ?>')"><i class="fas fa-trash-alt"></i></button>
<?php endif; ?>
</div>
</div>
<?php endforeach; ?>
</div>
</div>
<!-- Adviser Card -->
<div class="Officer_Role_Card">
<div class="Role_Card_Header">
<div class="Role_Card_Title">
<div class="Role_Icon Icon_Adviser"><i class="fas fa-user-tie"></i></div>
COMEA Advisers
</div>
<span class="Badge" style="background: #FFF7ED; color: #EA580C;"><?php echo $totalAdvisers; ?> Active</span>
</div>
<div class="Officer_List">
<?php if (empty($advisers)): ?>
<p style="padding: 24px; text-align: center; color: var(--Text_Secondary); font-size: 0.85rem;">No advisers registered yet.</p>
<?php endif; ?>
<?php foreach ($advisers as $adviser): ?>
<div class="Officer_Item">
<div class="Officer_Avatar">
<?php if (!empty($adviser['Photo'])): ?>
<img src="<?php echo htmlspecialchars($adviser['Photo']); ?>" alt="" style="width: 100%; height: 100%; object-fit: cover;">
<?php else: ?>
<?php echo strtoupper(substr($adviser['Name'], 0, 1)); ?>
<?php endif; ?>
</div>
<div class="Officer_Info">
<div class="Officer_Name"><?php echo htmlspecialchars($adviser['Name']); ?></div>
<div class="Officer_Email"><?php echo htmlspecialchars($adviser['User_ID']); ?> | <?php echo htmlspecialchars($adviser['Email']); ?></div>
</div>
<div class="Officer_Actions">
<button class="Officer_Action_Btn" title="Edit" onclick='openEditModal(<?php echo json_encode($adviser); ?>)'><i class="fas fa-edit"></i></button>
<button class="Officer_Action_Btn Btn_Delete" title="Delete" onclick="deleteOfficer('<?php echo $adviser['User_ID']; ?>', '<?php echo htmlspecialchars($adviser['Name']); ?>')"><i class="fas fa-trash-alt"></i></button>
</div>
</div>
<?php endforeach; ?>
</div>
</div>
<!-- Officer Card -->
<div class="Officer_Role_Card">
<div class="Role_Card_Header">
<div class="Role_Card_Title">
<div class="Role_Icon Icon_Officer"><i class="fas fa-user-shield"></i></div>
COMEA Officers
</div>
<span class="Badge" style="background: #ECFDF5; color: #059669;"><?php echo $totalOfficers; ?> Active</span>
</div>
<div class="Officer_List">
<?php if (empty($comeaOfficers)): ?>
<p style="padding: 24px; text-align: center; color: var(--Text_Secondary); font-size: 0.85rem;">No officers registered yet.</p>
<?php endif; ?>
<?php foreach ($comeaOfficers as $officer): ?>
<div class="Officer_Item">
<div class="Officer_Avatar">
<?php if (!empty($officer['Photo'])): ?>
<img src="<?php echo htmlspecialchars($officer['Photo']); ?>" alt="" style="width: 100%; height: 100%; object-fit: cover;">
<?php else: ?>
<?php echo strtoupper(substr($officer['Name'], 0, 1)); ?>
<?php endif; ?>
</div>
<div class="Officer_Info">
<div class="Officer_Name"><?php echo htmlspecialchars($officer['Name']); ?></div>
<div class="Officer_Email"><?php echo htmlspecialchars($officer['User_ID']); ?> | <?php echo htmlspecialchars($officer['Email']); ?></div>
</div>
<div class="Officer_Actions">
<button class="Officer_Action_Btn" title="Edit" onclick='openEditModal(<?php echo json_encode($officer); ?>)'><i class="fas fa-edit"></i></button>
<button class="Officer_Action_Btn Btn_Delete" title="Delete" onclick="deleteOfficer('<?php echo $officer['User_ID']; ?>', '<?php echo htmlspecialchars($officer['Name']); ?>')"><i class="fas fa-trash-alt"></i></button>
</div>
</div>
<?php endforeach; ?>
</div>
</div>
</div>
</div>
</main>
</div>
<!-- Edit Modal -->
<div id="EditModal" class="Modal">
<div class="Modal_Content">
<div class="Modal_Header">
<h2 class="Modal_Title">Edit Officer</h2>
<span class="Modal_Close" onclick="closeModal()">&times;</span>
</div>
<form action="../Logic/Backend/Edit_Officer_Handler.php" method="POST">
<input type="hidden" name="original_user_id" id="edit_original_id">
<div class="Form_Group">
<label class="Label">Full Name</label>
<input type="text" name="name" id="edit_name" class="Input" required>
</div>
<div class="Form_Group" style="margin-top: 15px;">
<label class="Label">Email</label>
<input type="email" name="email" id="edit_email" class="Input" required>
</div>
<div class="Form_Group" style="margin-top: 15px;">
<label class="Label">User ID</label>
<input type="text" name="user_id" id="edit_user_id" class="Input" required>
</div>
<div class="Form_Group" style="margin-top: 15px;">
<label class="Label">Role</label>
<select name="role" id="edit_role" class="Select" style="width: 100%; height: 50px;" required>
<option value="Admin">Admin</option>
<option value="COMEA Adviser">COMEA Adviser</option>
<option value="COMEA Officer">COMEA Officer</option>
</select>
</div>
<div class="Form_Group" style="margin-top: 15px;">
<label class="Label">New Password (Leave blank to keep current)</label>
<input type="password" name="password" class="Input" placeholder="Enter new password">
</div>
<button type="submit" class="Button_Primary" style="width: 100%; margin-top: 24px;">Update Officer</button>
</form>
</div>
</div>
<script>
function openEditModal(officer) {
document.getElementById('edit_original_id').value = officer.User_ID;
document.getElementById('edit_user_id').value = officer.User_ID;
document.getElementById('edit_name').value = officer.Name;
document.getElementById('edit_email').value = officer.Email;
document.getElementById('edit_role').value = officer.Role;
document.getElementById('EditModal').style.display = 'flex';
}
function closeModal() {
document.getElementById('EditModal').style.display = 'none';
}
function deleteOfficer(id, name) {
if (confirm(`Are you sure you want to delete officer ${name} (${id})? This action cannot be undone.`)) {
window.location.href = `../Logic/Backend/Delete_Officer_Handler.php?user_id=${id}`;
}
}
// Close modal when clicking outside
window.onclick = function(event) {
if (event.target == document.getElementById('EditModal')) {
closeModal();
}
}
</script>
</body>
</html>

View File

@ -63,6 +63,58 @@ $votersList = $stmt->fetchAll();
<title>Voter Management | Online School Election System</title>
<link rel="stylesheet" href="../Design/Style.css?v=<?php echo time(); ?>">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<style>
.Voter_Actions_Btn {
height: 44px;
padding: 0 24px;
display: inline-flex;
align-items: center;
justify-content: center;
gap: 10px;
font-size: 0.9rem;
margin-top: 0;
width: auto;
}
/* Modal Styles */
.Modal {
display: none;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0,0,0,0.5);
z-index: 1000;
justify-content: center;
align-items: center;
backdrop-filter: blur(4px);
}
.Modal_Content {
background: white;
padding: 32px;
border-radius: 20px;
width: 90%;
max-width: 500px;
position: relative;
box-shadow: 0 20px 25px -5px rgba(0,0,0,0.1);
}
.Modal_Header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 24px;
}
.Modal_Title {
font-size: 1.25rem;
font-weight: 700;
}
.Modal_Close {
cursor: pointer;
font-size: 1.25rem;
color: var(--Text_Secondary);
}
</style>
</head>
<body>
<div class="Dashboard_Container">
@ -84,10 +136,10 @@ $votersList = $stmt->fetchAll();
<a href="Voters.php" class="Nav_Item Active">
<i class="fas fa-users"></i> Voter Management
</a>
<a href="Candidates.html" class="Nav_Item">
<a href="Candidates.php" class="Nav_Item">
<i class="fas fa-user-tie"></i> Candidate Management
</a>
<a href="Officers.html" class="Nav_Item">
<a href="Officers.php" class="Nav_Item">
<i class="fas fa-user-shield"></i> Officers Management
</a>
<a href="Audit_Trail.html" class="Nav_Item">
@ -124,6 +176,17 @@ $votersList = $stmt->fetchAll();
<!-- Content Body -->
<div class="Content_Body">
<?php if (isset($_GET['success'])): ?>
<div class="Badge Badge_Success" style="width: 100%; padding: 15px; margin-bottom: 20px; text-align: center;">
<i class="fas fa-check-circle"></i> <?php echo htmlspecialchars($_GET['success']); ?>
</div>
<?php endif; ?>
<?php if (isset($_GET['error'])): ?>
<div class="Badge" style="width: 100%; padding: 15px; margin-bottom: 20px; text-align: center; background: #FEE2E2; color: #EF4444;">
<i class="fas fa-exclamation-circle"></i> <?php echo htmlspecialchars($_GET['error']); ?>
</div>
<?php endif; ?>
<div class="Page_Title_Section">
<div style="display: flex; align-items: center; gap: 15px;">
<div style="width: 48px; height: 48px; background: var(--Primary_Light); color: var(--Primary_Color); border-radius: 12px; display: flex; justify-content: center; align-items: center; font-size: 1.5rem;">
@ -188,64 +251,65 @@ $votersList = $stmt->fetchAll();
<!-- Actions -->
<div style="display: flex; justify-content: flex-end; gap: 12px; margin-bottom: 20px;">
<button class="Button_Primary" style="width: auto; padding: 10px 20px;">
<button class="Button_Primary Voter_Actions_Btn" onclick="openAddModal()">
<i class="fas fa-plus"></i> Add Voter
</button>
<button class="Button_Secondary" style="width: auto; padding: 10px 20px;">
<button class="Button_Secondary Voter_Actions_Btn">
<i class="fas fa-file-import"></i> Import CSV
</button>
</div>
<!-- Filters -->
<form method="GET" class="Filter_Bar">
<div class="Filter_Group">
<label>Search</label>
<div class="Search_Input_Wrapper">
<i class="fas fa-search"></i>
<input type="text" name="search" class="Input" placeholder="Search by email" value="<?php echo htmlspecialchars($search); ?>">
<!-- Table Card -->
<div class="Card" style="padding: 0; overflow: hidden;">
<!-- Filters (Integrated into Card with Flex Layout) -->
<form method="GET" class="Filter_Bar" style="display: flex; flex-wrap: nowrap; gap: 20px; align-items: flex-end; background: transparent; border: none; border-bottom: 1px solid var(--Border_Color); border-radius: 0; margin-bottom: 0; padding: 24px;">
<div class="Filter_Group" style="flex: 2;">
<label>Search</label>
<div class="Search_Input_Wrapper">
<i class="fas fa-search"></i>
<input type="text" name="search" class="Input" placeholder="Search by email" value="<?php echo htmlspecialchars($search); ?>">
</div>
</div>
</div>
<div class="Filter_Group">
<label>Track</label>
<select name="track" class="Select" onchange="this.form.submit()">
<option value="">All Tracks</option>
<?php
$allTracks = $db->query("SELECT DISTINCT Track_Cluster FROM Voters WHERE Track_Cluster IS NOT NULL")->fetchAll(PDO::FETCH_COLUMN);
foreach ($allTracks as $t): ?>
<option value="<?php echo htmlspecialchars($t); ?>" <?php echo $filterTrack == $t ? 'selected' : ''; ?>><?php echo htmlspecialchars($t); ?></option>
<?php endforeach; ?>
</select>
</div>
<div class="Filter_Group">
<label>Grade</label>
<select name="grade" class="Select" onchange="this.form.submit()">
<option value="">All Grades</option>
<?php
$allGrades = $db->query("SELECT DISTINCT Grade_Level FROM Voters WHERE Grade_Level IS NOT NULL ORDER BY Grade_Level")->fetchAll(PDO::FETCH_COLUMN);
foreach ($allGrades as $g): ?>
<option value="<?php echo htmlspecialchars($g); ?>" <?php echo $filterGrade == $g ? 'selected' : ''; ?>>Grade <?php echo htmlspecialchars($g); ?></option>
<?php endforeach; ?>
</select>
</div>
<div class="Filter_Group">
<label>Section</label>
<select name="section" class="Select" onchange="this.form.submit()">
<option value="">All Sections</option>
<?php
$allSections = $db->query("SELECT DISTINCT Section FROM Voters WHERE Section IS NOT NULL ORDER BY Section")->fetchAll(PDO::FETCH_COLUMN);
foreach ($allSections as $s): ?>
<option value="<?php echo htmlspecialchars($s); ?>" <?php echo $filterSection == $s ? 'selected' : ''; ?>><?php echo htmlspecialchars($s); ?></option>
<?php endforeach; ?>
</select>
</div>
</form>
<div class="Filter_Group" style="flex: 1;">
<label>Track</label>
<select name="track" class="Select" onchange="this.form.submit()" style="width: 100%;">
<option value="">All Tracks</option>
<?php
$allTracks = $db->query("SELECT DISTINCT Track_Cluster FROM Voters WHERE Track_Cluster IS NOT NULL")->fetchAll(PDO::FETCH_COLUMN);
foreach ($allTracks as $t): ?>
<option value="<?php echo htmlspecialchars($t); ?>" <?php echo $filterTrack == $t ? 'selected' : ''; ?>><?php echo htmlspecialchars($t); ?></option>
<?php endforeach; ?>
</select>
</div>
<div class="Filter_Group" style="flex: 1;">
<label>Grade</label>
<select name="grade" class="Select" onchange="this.form.submit()" style="width: 100%;">
<option value="">All Grades</option>
<?php
$allGrades = $db->query("SELECT DISTINCT Grade_Level FROM Voters WHERE Grade_Level IS NOT NULL ORDER BY Grade_Level")->fetchAll(PDO::FETCH_COLUMN);
foreach ($allGrades as $g): ?>
<option value="<?php echo htmlspecialchars($g); ?>" <?php echo $filterGrade == $g ? 'selected' : ''; ?>>Grade <?php echo htmlspecialchars($g); ?></option>
<?php endforeach; ?>
</select>
</div>
<div class="Filter_Group" style="flex: 1;">
<label>Section</label>
<select name="section" class="Select" onchange="this.form.submit()" style="width: 100%;">
<option value="">All Sections</option>
<?php
$allSections = $db->query("SELECT DISTINCT Section FROM Voters WHERE Section IS NOT NULL ORDER BY Section")->fetchAll(PDO::FETCH_COLUMN);
foreach ($allSections as $s): ?>
<option value="<?php echo htmlspecialchars($s); ?>" <?php echo $filterSection == $s ? 'selected' : ''; ?>><?php echo htmlspecialchars($s); ?></option>
<?php endforeach; ?>
</select>
</div>
</form>
<!-- Table -->
<div class="Card" style="padding: 0;">
<div class="Table_Wrapper">
<table>
<thead>
<tr>
<th>User ID</th>
<th>Email</th>
<th>Track</th>
<th>Grade</th>
@ -256,14 +320,15 @@ $votersList = $stmt->fetchAll();
<tbody>
<?php if (empty($votersList)): ?>
<tr>
<td colspan="5" style="text-align: center; padding: 40px; color: var(--Text_Secondary);">
<td colspan="6" style="text-align: center; padding: 40px; color: var(--Text_Secondary);">
No voters found matching your criteria.
</td>
</tr>
<?php else: ?>
<?php foreach ($votersList as $voter): ?>
<tr>
<td style="font-weight: 600;"><?php echo htmlspecialchars($voter['Email']); ?></td>
<td style="font-weight: 600;"><?php echo htmlspecialchars($voter['User_ID']); ?></td>
<td><?php echo htmlspecialchars($voter['Email']); ?></td>
<td><?php echo htmlspecialchars($voter['Track_Cluster'] ?: 'N/A'); ?></td>
<td>Grade <?php echo htmlspecialchars($voter['Grade_Level'] ?: 'N/A'); ?></td>
<td><?php echo htmlspecialchars($voter['Section'] ?: 'N/A'); ?></td>
@ -287,5 +352,62 @@ $votersList = $stmt->fetchAll();
</div>
</main>
</div>
<!-- Add Voter Modal -->
<div id="AddModal" class="Modal">
<div class="Modal_Content">
<div class="Modal_Header">
<h2 class="Modal_Title">Add New Voter</h2>
<span class="Modal_Close" onclick="closeModal()">&times;</span>
</div>
<form action="../Logic/Backend/Add_Voter_Handler.php" method="POST">
<div class="Form_Group">
<label class="Label">Email Address</label>
<input type="email" name="email" class="Input" placeholder="voter@school.edu" required>
</div>
<div class="Form_Group" style="margin-top: 15px;">
<label class="Label">User ID (Leave blank for random)</label>
<input type="text" name="user_id" class="Input" placeholder="00-0000">
</div>
<div class="Form_Group" style="margin-top: 15px;">
<label class="Label">Track / Strand</label>
<input type="text" name="track" class="Input" placeholder="e.g. STEM" required>
</div>
<div class="Form_Group" style="margin-top: 15px;">
<label class="Label">Grade Level</label>
<select name="grade" class="Select" style="width: 100%; height: 50px;" required>
<option value="11">Grade 11</option>
<option value="12">Grade 12</option>
</select>
</div>
<div class="Form_Group" style="margin-top: 15px;">
<label class="Label">Section</label>
<input type="text" name="section" class="Input" placeholder="e.g. A" required>
</div>
<div class="Form_Group" style="margin-top: 15px;">
<label class="Label">Password (Leave blank for default)</label>
<input type="password" name="password" class="Input" placeholder="Set password">
</div>
<button type="submit" class="Button_Primary" style="width: 100%; margin-top: 24px;">Save Voter</button>
</form>
</div>
</div>
<script>
function openAddModal() {
document.getElementById('AddModal').style.display = 'flex';
}
function closeModal() {
document.getElementById('AddModal').style.display = 'none';
}
// Close modal when clicking outside
window.onclick = function(event) {
if (event.target == document.getElementById('AddModal')) {
closeModal();
}
}
</script>
</body>
</html>
</html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

View File

@ -62,10 +62,10 @@ try {
// Insert default admin (Password: Admin123)
$stmt = $pdo->prepare("INSERT IGNORE INTO Officers (User_ID, Role, Access_Level, Email, Password, Name) VALUES (?, ?, ?, ?, ?, ?)");
$stmt->execute(['ADMIN-001', 'Admin', 3, 'admin@school.edu', password_hash('Admin123', PASSWORD_DEFAULT), 'System Administrator']);
$stmt->execute(['00-0000', 'Admin', 3, 'admin@school.edu', password_hash('Admin123', PASSWORD_DEFAULT), 'System Administrator']);
echo "Database initialized successfully.\n";
} catch (Exception $e) {
die("Error initializing database: " . $e->getMessage());
}
}