'', 'student_id' => '', 'institutional_email' => '', 'college_department' => '', 'degree_program' => '', 'year_level' => '', 'contact_number' => '', 'reason' => '', ]); render('register', ['form' => $form]); break; case 'login': if ($user !== null) { redirect($user['role'] === 'admin' ? 'admin' : 'student'); } render('login', [ 'challenge' => login_challenge(), 'adminChallenge' => login_challenge('admin'), ]); break; case 'admin_login': if ($user !== null) { redirect($user['role'] === 'admin' ? 'admin' : 'student'); } redirect('login'); case 'student': $student = require_student(); audit_event('student.application_viewed', 'Viewed own Wi-Fi application status.', (int) $student['id']); render('student', ['student' => $student]); break; case 'account_security': $account = require_authenticated_user(); $profileStatement = db()->prepare( 'SELECT age, gender, degree_program, year_level FROM users WHERE id = ?' ); $profileStatement->execute([(int) $account['id']]); $profile = $profileStatement->fetch(); $courseOptions = db()->query( "SELECT DISTINCT degree_program FROM users WHERE degree_program <> '' ORDER BY degree_program" )->fetchAll(PDO::FETCH_COLUMN); if ($profile && $profile['degree_program'] !== '' && !in_array($profile['degree_program'], $courseOptions, true)) { $courseOptions[] = $profile['degree_program']; sort($courseOptions, SORT_NATURAL | SORT_FLAG_CASE); } $profileErrors = $_SESSION['account_profile_errors'] ?? []; $profileOld = $_SESSION['account_profile_old'] ?? []; $passwordErrors = $_SESSION['account_password_errors'] ?? []; unset( $_SESSION['account_profile_errors'], $_SESSION['account_profile_old'], $_SESSION['account_password_errors'] ); $profile = $profile ?: ['age' => '', 'gender' => '', 'degree_program' => '', 'year_level' => '']; $profile['course'] = $profile['degree_program']; if ($profileOld) { $profile = array_merge($profile, is_array($profileOld) ? $profileOld : []); } render('account_security', [ 'account' => $account, 'profile' => $profile, 'courseOptions' => $courseOptions, 'profileErrors' => is_array($profileErrors) ? $profileErrors : [], 'passwordErrors' => is_array($passwordErrors) ? $passwordErrors : [], ]); break; case 'backup_restore': $admin = require_admin(); render('backup_restore', [ 'admin' => $admin, 'demoMode' => app_config('APP_ENV') === 'development', ]); break; case 'admin_accounts': require_admin(); $adminAccounts = db()->query( "SELECT id, full_name, institutional_email, created_at, is_active FROM users WHERE role = 'admin' ORDER BY full_name ASC" )->fetchAll(); audit_event('admin.accounts_viewed', 'Viewed administrator accounts.'); render('admin_accounts', ['adminAccounts' => $adminAccounts]); break; case 'settings': require_admin(); render('settings', [ 'environment' => (string) app_config('APP_ENV'), 'recaptchaEnabled' => !empty(app_config('RECAPTCHA_SITE_KEY')) && !empty(app_config('RECAPTCHA_SECRET_KEY')), ]); break; case 'applications': case 'admin': $admin = require_admin(); $pending = db()->query( "SELECT id, full_name, student_id, institutional_email, college_department, degree_program, year_level, contact_number, reason, created_at FROM users WHERE role = 'student' AND application_status = 'pending' ORDER BY created_at ASC" )->fetchAll(); $stats = db()->query( "SELECT COUNT(*) AS total_count, COALESCE(SUM(application_status = 'pending'), 0) AS pending_count, COALESCE(SUM(application_status = 'approved'), 0) AS approved_count, COALESCE(SUM(application_status = 'rejected'), 0) AS rejected_count, COALESCE(SUM(application_status = 'approved' AND is_active = 1 AND (access_expires_at IS NULL OR access_expires_at >= NOW())), 0) AS active_count, COALESCE(SUM(is_active = 0), 0) AS suspended_count, COALESCE(SUM(application_status = 'approved' AND access_expires_at < NOW()), 0) AS expired_count FROM users WHERE role = 'student'" )->fetch(); $statusCounts = ['pending' => 0, 'approved' => 0, 'rejected' => 0]; foreach (db()->query( "SELECT application_status, COUNT(*) AS total FROM users WHERE role = 'student' GROUP BY application_status" )->fetchAll() as $row) { $statusCounts[$row['application_status']] = (int) $row['total']; } $monthStart = new DateTimeImmutable('first day of this month'); $monthStart = $monthStart->modify('-5 months'); $monthRows = db()->prepare( 'SELECT DATE_FORMAT(created_at, "%Y-%m") AS month_key, COUNT(*) AS total FROM users WHERE role = "student" AND created_at >= ? GROUP BY month_key ORDER BY month_key' ); $monthRows->execute([$monthStart->format('Y-m-d')]); $monthlyCounts = []; foreach ($monthRows->fetchAll() as $row) { $monthlyCounts[$row['month_key']] = (int) $row['total']; } $monthly = []; for ($offset = 5; $offset >= 0; $offset--) { $month = $monthStart->modify('+' . $offset . ' months'); $key = $month->format('Y-m'); $monthly[] = ['label' => $month->format('M'), 'total' => $monthlyCounts[$key] ?? 0]; } $departmentRows = db()->query( "SELECT college_department, COUNT(*) AS total FROM users WHERE role = 'student' GROUP BY college_department ORDER BY total DESC, college_department ASC LIMIT 6" )->fetchAll(); $departments = []; $topDepartmentTotal = 0; foreach ($departmentRows as $row) { $departments[] = ['label' => $row['college_department'], 'total' => (int) $row['total']]; $topDepartmentTotal += (int) $row['total']; } $otherDepartments = (int) $stats['total_count'] - $topDepartmentTotal; if ($otherDepartments > 0) { $departments[] = ['label' => 'Other departments', 'total' => $otherDepartments]; } audit_event('dashboard.viewed', 'Viewed the administrator dashboard.'); $activities = db()->query( 'SELECT actor_name, activity, details, occurred_at FROM audit_logs ORDER BY occurred_at DESC, id DESC LIMIT 6' )->fetchAll(); render('admin', [ 'admin' => $admin, 'pending' => $pending, 'stats' => $stats, 'statusCounts' => $statusCounts, 'monthly' => $monthly, 'departments' => $departments, 'activities' => $activities, ]); break; case 'accounts': require_admin(); [$filters, $filterErrors] = report_filters($_GET); if ($filterErrors) { http_response_code(400); render('error', ['title' => 'Invalid filters', 'message' => implode(' ', $filterErrors)]); break; } $students = filtered_students($filters); $departments = db()->query( "SELECT DISTINCT college_department FROM users WHERE role = 'student' ORDER BY college_department" )->fetchAll(PDO::FETCH_COLUMN); audit_event('student.list_viewed', 'Viewed filtered student records; ' . count($students) . ' results.'); render('accounts', ['students' => $students, 'filters' => $filters, 'departments' => $departments]); break; case 'approved': require_admin(); $students = db()->query( "SELECT full_name, student_id, institutional_email, college_department, degree_program, year_level, is_active, access_expires_at FROM users WHERE role = 'student' AND application_status = 'approved' ORDER BY full_name ASC" )->fetchAll(); audit_event('student.approved_list_viewed', 'Viewed approved student records; ' . count($students) . ' results.'); render('approved', ['students' => $students]); break; case 'student_new': require_admin(); $form = take_form_values([ 'full_name' => '', 'student_id' => '', 'institutional_email' => '', 'college_department' => '', 'degree_program' => '', 'year_level' => '', 'contact_number' => '', 'reason' => '', 'application_status' => 'pending', 'is_active' => 1, 'access_expires_date' => '', ]); render('student_form', ['form' => $form, 'formMode' => 'create']); break; case 'student_edit': require_admin(); $userId = filter_var(input_string($_GET, 'id'), FILTER_VALIDATE_INT); $statement = db()->prepare( "SELECT id, full_name, student_id, institutional_email, college_department, degree_program, year_level, contact_number, reason, application_status, is_active, access_expires_at FROM users WHERE id = ? AND role = 'student'" ); $statement->execute([$userId ?: 0]); $student = $statement->fetch(); if (!$student) { http_response_code(404); render('error', ['title' => 'Student not found', 'message' => 'The requested student record does not exist.']); break; } $student['access_expires_date'] = $student['access_expires_at'] ? substr($student['access_expires_at'], 0, 10) : ''; $form = take_form_values($student); render('student_form', ['form' => $form, 'formMode' => 'edit', 'studentId' => (int) $student['id']]); break; case 'student_record': require_admin(); $userId = filter_var(input_string($_GET, 'id'), FILTER_VALIDATE_INT); $statement = db()->prepare( "SELECT id, full_name, student_id, institutional_email, college_department, degree_program, year_level, contact_number, reason, application_status, is_active, access_expires_at, policy_accepted_at, created_at, updated_at FROM users WHERE id = ? AND role = 'student'" ); $statement->execute([$userId ?: 0]); $student = $statement->fetch(); if (!$student) { http_response_code(404); render('error', ['title' => 'Student not found', 'message' => 'The requested student record does not exist.']); break; } audit_event( 'student.record_viewed', 'Viewed student record ' . $student['student_id'] . '.', (int) $student['id'] ); render('student_record', ['student' => $student]); break; case 'reports': require_admin(); [$filters, $filterErrors] = report_filters($_GET); if ($filterErrors) { http_response_code(400); render('error', ['title' => 'Invalid report filters', 'message' => implode(' ', $filterErrors)]); break; } $students = filtered_students($filters); $reportCounts = ['pending' => 0, 'approved' => 0, 'rejected' => 0, 'enabled' => 0, 'disabled' => 0, 'expired' => 0]; foreach ($students as $student) { $reportCounts[$student['application_status']]++; $reportCounts[(int) $student['is_active'] === 1 ? 'enabled' : 'disabled']++; if ($student['application_status'] === 'approved' && !empty($student['access_expires_at']) && strtotime($student['access_expires_at']) < time()) { $reportCounts['expired']++; } } $departments = db()->query( "SELECT DISTINCT college_department FROM users WHERE role = 'student' ORDER BY college_department" )->fetchAll(PDO::FETCH_COLUMN); audit_event('report.generated', 'Generated filtered student report; ' . count($students) . ' records.'); render('reports', [ 'filters' => $filters, 'students' => $students, 'reportCounts' => $reportCounts, 'departments' => $departments, ]); break; case 'audit': require_admin(); [$auditFilters, $auditEntries, $auditTotal, $auditLastPage, $auditErrors] = audit_log_page($_GET); if ($auditErrors) { http_response_code(400); render('error', ['title' => 'Invalid audit filters', 'message' => implode(' ', $auditErrors)]); break; } audit_event('audit.viewed', 'Viewed the filtered activity log.'); $activities = db()->query( 'SELECT DISTINCT activity FROM audit_logs ORDER BY activity' )->fetchAll(PDO::FETCH_COLUMN); render('audit', [ 'auditFilters' => $auditFilters, 'auditEntries' => $auditEntries, 'auditTotal' => $auditTotal, 'auditPage' => max(1, (int) (filter_var(input_string($_GET, 'log_page'), FILTER_VALIDATE_INT) ?: 1)), 'auditLastPage' => $auditLastPage, 'activities' => $activities, ]); break; default: http_response_code(404); render('error', ['title' => 'Page not found', 'message' => 'The requested page does not exist.']); }