Autosave: 20260204-211347
This commit is contained in:
parent
b813668e12
commit
b3fd5e275d
85
Logic/Backend/Add_Candidate_Handler.php
Normal file
85
Logic/Backend/Add_Candidate_Handler.php
Normal 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;
|
||||
}
|
||||
77
Logic/Backend/Add_Officer_Handler.php
Normal file
77
Logic/Backend/Add_Officer_Handler.php
Normal 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");
|
||||
}
|
||||
69
Logic/Backend/Add_Voter_Handler.php
Normal file
69
Logic/Backend/Add_Voter_Handler.php
Normal 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");
|
||||
}
|
||||
46
Logic/Backend/Delete_Officer_Handler.php
Normal file
46
Logic/Backend/Delete_Officer_Handler.php
Normal 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()));
|
||||
}
|
||||
59
Logic/Backend/Edit_Officer_Handler.php
Normal file
59
Logic/Backend/Edit_Officer_Handler.php
Normal 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
589
Screens/Candidates.php
Normal 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>
|
||||
@ -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; ?>
|
||||
|
||||
@ -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">
|
||||
|
||||
@ -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">
|
||||
© 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)">×</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
487
Screens/Officers.php
Normal 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()">×</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>
|
||||
@ -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()">×</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>
|
||||
|
||||
BIN
assets/pasted-20260204-210904-9c726cb9.png
Normal file
BIN
assets/pasted-20260204-210904-9c726cb9.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.7 MiB |
BIN
assets/pasted-20260204-211215-fded7a35.png
Normal file
BIN
assets/pasted-20260204-211215-fded7a35.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 29 KiB |
@ -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());
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user