Candidate Management
+Managing SY Election
+Define Positions
+Define Parties
+Register New Candidate
+Candidates by Position
+No data available
+ +Candidates by Party
+No data available
+ +Candidates by Grade
+No data available
+ +diff --git a/Logic/Backend/Add_Candidate_Handler.php b/Logic/Backend/Add_Candidate_Handler.php new file mode 100644 index 0000000..3ff40df --- /dev/null +++ b/Logic/Backend/Add_Candidate_Handler.php @@ -0,0 +1,85 @@ +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; +} diff --git a/Logic/Backend/Add_Officer_Handler.php b/Logic/Backend/Add_Officer_Handler.php new file mode 100644 index 0000000..3d39c65 --- /dev/null +++ b/Logic/Backend/Add_Officer_Handler.php @@ -0,0 +1,77 @@ +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"); +} \ No newline at end of file diff --git a/Logic/Backend/Add_Voter_Handler.php b/Logic/Backend/Add_Voter_Handler.php new file mode 100644 index 0000000..fada3fc --- /dev/null +++ b/Logic/Backend/Add_Voter_Handler.php @@ -0,0 +1,69 @@ +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"); +} diff --git a/Logic/Backend/Delete_Officer_Handler.php b/Logic/Backend/Delete_Officer_Handler.php new file mode 100644 index 0000000..0d374a5 --- /dev/null +++ b/Logic/Backend/Delete_Officer_Handler.php @@ -0,0 +1,46 @@ +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())); +} diff --git a/Logic/Backend/Edit_Officer_Handler.php b/Logic/Backend/Edit_Officer_Handler.php new file mode 100644 index 0000000..ca7215a --- /dev/null +++ b/Logic/Backend/Edit_Officer_Handler.php @@ -0,0 +1,59 @@ +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"); +} diff --git a/Screens/Candidates.php b/Screens/Candidates.php new file mode 100644 index 0000000..11ac910 --- /dev/null +++ b/Screens/Candidates.php @@ -0,0 +1,589 @@ +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(); + +?> + + +
+ + +Managing SY Election
+No data available
+ +No data available
+ +No data available
+ +
+
- This portal is currently a PROTOTYPE MODEL 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. -
- -+ This portal is currently a PROTOTYPE MODEL 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. +
+ +Access the election management system
- - - - - -