diff --git a/_chunk1.txt b/_chunk1.txt new file mode 100644 index 0000000..e49f1ca --- /dev/null +++ b/_chunk1.txt @@ -0,0 +1,25 @@ +class AdministratorController extends BaseController +{ + protected $roleModel; + protected $userModel; + protected $userRoleModel; + protected $configModel; + protected $semester; + protected $schoolYear; + protected $staffAttendanceModel; + + public function __construct() + { + helper('auth'); + $this->roleModel = new RoleModel(); + $this->userModel = new UserModel(); + $this->configModel = new ConfigurationModel(); + $this->userRoleModel = new UserRoleModel(); + $this->semester = getSemester(); + $this->schoolYear = $this->configModel->getConfig('school_year'); + $this->staffAttendanceModel = new StaffAttendanceModel(); + } + + /** + * Compute all allowed absence dates (future Sundays within Sep..May for the configured school year) + */ diff --git a/_chunk10.txt b/_chunk10.txt new file mode 100644 index 0000000..3b2bf61 --- /dev/null +++ b/_chunk10.txt @@ -0,0 +1,97 @@ + } + + return view('administrator/print_notification_admins', service('adminNotificationSettings')->printRecipientsPage()); + } + + public function savePrintNotificationRecipients() + { + if (!$this->canManageAdminNotifications()) { + return redirect()->to('/login'); + } + + $result = service('adminNotificationSettings')->savePrintRecipients((array) $this->request->getPost('notify')); + + return redirect()->to('/administrator/print-notifications') + ->with((string) ($result['type'] ?? 'info'), (string) ($result['message'] ?? '')); + } + + public function studentProfiles() + { + $selectedYear = $this->currentSchoolYearName((string) ($this->schoolYear ?? '')); + + return view( + 'administrator/student_profiles', + service('administratorDirectory')->studentProfiles($selectedYear) + ); + } + + public function parentProfiles() + { + if ($redirect = $this->redirectWithoutLegacyTermFilters('administrator/parent_profiles')) { + return $redirect; + } + + return view('administrator/parent_profile', service('administratorDirectory')->parentProfiles()); + } + + public function showEnrollmentWithdrawalPage() + { + try { + $schoolYearContext = $this->resolveSchoolYearContext(); + $selectedYear = $schoolYearContext->yearName(); + $payload = service('enrollmentWithdrawal')->buildRoster($selectedYear, (string) $this->semester); + + return view('enroll_withdraw/enrollment_withdrawal', [ + 'students' => $payload['students'], + 'classes' => $payload['classes'], + 'selectedYear' => $selectedYear, + 'currentYear' => $selectedYear, + 'isCurrentYear' => ! $schoolYearContext->isReadonly(), + 'missingYear' => $selectedYear === '', + ]); + } catch (\Throwable $e) { + log_message('error', 'Enrollment/Withdrawal page error: {msg}', ['msg' => $e->getMessage()]); + return view('errors/html/error_500'); + } + } + + public function enrollmentWithdrawalData() + { + try { + $selectedYear = $this->currentSchoolYearName((string) ($this->schoolYear ?? '')); + $payload = service('enrollmentWithdrawal')->buildRoster($selectedYear, (string) $this->semester); + + return $this->response->setJSON([ + 'students' => $payload['students'], + 'classes' => $payload['classes'], + 'csrfHash' => csrf_hash(), + 'semester' => (string) $this->semester, + 'school_year' => (string) $selectedYear, + ]); + } catch (\Throwable $e) { + log_message('error', 'enrollmentWithdrawalData error: {msg}', ['msg' => $e->getMessage()]); + return $this->response->setStatusCode(500)->setJSON(['error' => 'Server error']); + } + } + + public function showNewStudents() + { + return view( + 'enroll_withdraw/new-students', + service('enrollmentWithdrawal')->newStudents((string) $this->schoolYear) + ); + } + + public function adminEnrollmentWithdrawalHandler() + { + $result = service('enrollmentWithdrawal')->updateStatuses( + $this->request->getPost('enrollment_status'), + (string) $this->schoolYear, + (string) $this->semester, + (int) (session()->get('user_id') ?? 0) ?: null + ); + + return redirect()->to(base_url('enroll_withdraw/enrollment_withdrawal')) + ->with(!empty($result['ok']) ? 'success' : 'error', (string) ($result['message'] ?? '')); + } +} diff --git a/_chunk2.txt b/_chunk2.txt new file mode 100644 index 0000000..73738ae --- /dev/null +++ b/_chunk2.txt @@ -0,0 +1,45 @@ + private function allowedAbsenceDates(): array + { + $todayStr = local_date(utc_now(), 'Y-m-d'); + try { $today = new \DateTimeImmutable($todayStr); } catch (\Throwable $e) { $today = new \DateTimeImmutable(); } + + $sy = (string)($this->schoolYear ?? ''); + $startYear = null; $endYear = null; + if (preg_match('/^(\d{4})\D+(\d{4})$/', $sy, $m)) { + $startYear = (int)$m[1]; + $endYear = (int)$m[2]; + } else { + $cy = (int)date('Y'); + $cm = (int)date('n'); + if ($cm >= 9) { // Sep-Dec + $startYear = $cy; + $endYear = $cy + 1; + } else { + $startYear = $cy - 1; + $endYear = $cy; + } + } + + try { + $start = new \DateTimeImmutable(sprintf('%04d-09-01', $startYear)); + $end = new \DateTimeImmutable(sprintf('%04d-05-31', $endYear)); + } catch (\Throwable $e) { + return []; + } + + if ($start < $today) { + $start = $today; + } + + $dates = []; + for ($cursor = $start; $cursor <= $end; $cursor = $cursor->modify('+1 day')) { + if ((int)$cursor->format('w') === 0) { // Sunday + $dates[] = $cursor->format('Y-m-d'); + } + } + return $dates; + } + + /** + * Admin self-service absence/vacation page (same features as teacher page) + */ diff --git a/_chunk3.txt b/_chunk3.txt new file mode 100644 index 0000000..d579c6a --- /dev/null +++ b/_chunk3.txt @@ -0,0 +1,32 @@ + public function absenceFormAdmin() + { + $userId = (int)(session()->get('user_id') ?? 0); + if ($userId <= 0) { + return redirect()->to('login')->with('error', 'Please log in first.'); + } + + $admin = $this->userModel->find($userId); + $displayName = $admin ? trim(($admin['firstname'] ?? '') . ' ' . ($admin['lastname'] ?? '')) : 'Administrator'; + + $semester = (string)($this->semester ?? ''); + $schoolYear = (string)($this->schoolYear ?? ''); + + $existing = $this->staffAttendanceModel + ->where('user_id', $userId) + ->where('semester', $semester) + ->where('school_year', $schoolYear) + ->orderBy('date', 'DESC') + ->findAll(); + + return view('administrator/absence_vacation', [ + 'admin_name' => $displayName, + 'semester' => $semester, + 'schoolYear' => $schoolYear, + 'existing' => $existing, + 'availableDates' => $this->allowedAbsenceDates(), + ]); + } + + /** + * Handle admin absence submission (upserts staff_attendance like the teacher flow) + */ diff --git a/_chunk4.txt b/_chunk4.txt new file mode 100644 index 0000000..00175c3 --- /dev/null +++ b/_chunk4.txt @@ -0,0 +1,124 @@ + public function submitAbsenceAdmin() + { + $userId = (int)(session()->get('user_id') ?? 0); + if ($userId <= 0) { + return redirect()->to('login')->with('error', 'Please log in first.'); + } + + $semester = (string)($this->semester ?? ''); + $schoolYear = (string)($this->schoolYear ?? ''); + if ($schoolYear === '') { + return redirect()->to('/administrator/absence')->with('status', 'error')->with('message', 'Semester or school year not configured.'); + } + $semesterResolver = new SemesterRangeService($this->configModel); + + $dates = (array)($this->request->getPost('dates') ?? []); + $reasonType = trim((string)($this->request->getPost('reason_type') ?? '')); + $reasonText = trim((string)($this->request->getPost('reason') ?? '')); + + if ($reasonText === '') { + return redirect()->to('/administrator/absence') + ->with('status', 'error') + ->with('message', 'Reason is required.'); + } + + $reasonBase = ($reasonType !== '') ? strtolower($reasonType) : ''; + $reason = $reasonBase !== '' ? ($reasonBase . ': ' . $reasonText) : $reasonText; + + $allowedDates = $this->allowedAbsenceDates(); + $allowedSet = array_fill_keys($allowedDates, true); + + $roleName = $this->userModel->getUserRole($userId) ?: null; + + $saved = 0; $invalid = []; + $savedDates = []; + $dates = array_values(array_unique(array_map('strval', $dates))); + foreach ($dates as $d) { + $d = trim((string)$d); + if ($d === '') continue; + $dt = date_create_from_format('Y-m-d', $d); + if (!$dt || $dt->format('Y-m-d') !== $d || empty($allowedSet[$d])) { + $invalid[] = $d; + continue; + } + + $semesterForDate = $semesterResolver->getSemesterForDate($d); + if ($semesterForDate === '') { + $semesterForDate = $semester; + } + + $ok = $this->staffAttendanceModel->upsertOne( + userId: $userId, + roleName: $roleName, + date: $d, + semester: $semesterForDate, + schoolYear: $schoolYear, + status: StaffAttendanceModel::STATUS_ABSENT, + reason: $reason, + editorId: $userId + ); + if ($ok) { $saved++; $savedDates[] = $d; } + } + + if (!empty($invalid)) { + return redirect()->to('/administrator/absence') + ->with('status', 'error') + ->with('message', 'Invalid dates: ' . implode(', ', $invalid)); + } + + // Send notification email to principal with request details + try { + $user = $this->userModel->find($userId) ?: []; + $fullName = trim(($user['firstname'] ?? '') . ' ' . ($user['lastname'] ?? '')) ?: 'Administrator'; + $userEmail = $user['email'] ?? ''; + $role = $roleName ?: 'admin'; + $dateList = !empty($savedDates) ? implode(', ', $savedDates) : implode(', ', $dates); + + $subject = sprintf('TimeOff Request: %s (%s) — %s', $fullName, ucfirst((string)$role), $dateList ?: 'No dates'); + $submittedAt = utc_now(); + + $body = '
A staff time-off request was submitted from the administrator portal.
' + . '| Name | ' . esc($fullName) . ' |
| ' . esc($userEmail) . ' | |
| Role | ' . esc((string)$role) . ' |
| Semester | ' . esc($semester) . ' |
| School Year | ' . esc($schoolYear) . ' |
| Reason Type | ' . esc($reasonType ?: '-') . ' |
| Reason | ' . esc($reasonText) . ' |
| Dates | ' . esc($dateList ?: '-') . ' |
| Submitted At | ' . esc($submittedAt) . ' |
Click Send confirmation email to ' + . esc($fullName) . ' so the staff member is notified automatically. ' + . 'This link expires in 14 days.
'; + + $mailer = \Config\Services::emailService(); + $principalEmail = env('PRINCIPAL_EMAIL') ?: 'principal@alrahmaisgl.org'; + $mailer->send($principalEmail, $subject, $body, 'notifications'); + } catch (\Throwable $e) { + log_message('error', 'Failed to send TimeOff email (admin): ' . $e->getMessage()); + } + + return redirect()->to('/administrator/absence') + ->with('status', 'success') + ->with('message', $saved . ' day(s) saved as absent.'); + } + diff --git a/_chunk5.txt b/_chunk5.txt new file mode 100644 index 0000000..80994dd --- /dev/null +++ b/_chunk5.txt @@ -0,0 +1,39 @@ + public function index() + { + $data = [ + 'users' => $this->userModel->findAll(), + 'roles' => $this->roleModel->findAll(), + ]; + + return view('administrator/dashboard', $data); + } + + public function isadministrator() + { + $session = session(); + $allowedRoles = [ + 'administrator', + 'admin', + 'principal', + //'vice_principal', + //'vice principal', + ]; + + $role = strtolower((string) $session->get('role')); + if ($role !== '' && in_array($role, $allowedRoles, true)) { + return true; + } + + $roles = $session->get('roles'); + if (is_array($roles)) { + foreach ($roles as $candidate) { + $candidate = strtolower((string) $candidate); + if ($candidate !== '' && in_array($candidate, $allowedRoles, true)) { + return true; + } + } + } + + return false; + } + diff --git a/_chunk6.txt b/_chunk6.txt new file mode 100644 index 0000000..b634d50 --- /dev/null +++ b/_chunk6.txt @@ -0,0 +1,35 @@ + public function teachers() + { + return view('administrator/teachers'); // This is the correct view path + } + + public function classes() + { + return view('administrator/classes'); + } + + public function subjects() + { + return view('administrator/subjects'); + } + + public function reports() + { + return view('administrator/reports'); + } + + public function teacherDashboard() + { + return view('teacher/dashboard'); + } + + public function parentDashboard() + { + return view('parent/dashboard'); + } + + public function CourseMaterials() + { + return view('administrator/course_materials'); + } + diff --git a/_chunk7.txt b/_chunk7.txt new file mode 100644 index 0000000..ca4f3c2 --- /dev/null +++ b/_chunk7.txt @@ -0,0 +1,88 @@ + public function administratorProfiles() + { + return view('administrator/administrator_profiles'); + } + + public function payrollManagement() + { + return view('administrator/payroll_management'); + } + + public function administratorAttendanceRecords() + { + return view('administrator/administrator_attendance_records'); + } + + public function performanceReviews() + { + return view('administrator/performance_reviews'); + } + + public function communicationLogs() + { + return view('administrator/communication_logs'); + } + + public function feePaymentRecords() + { + return view('administrator/fee_payment_records'); + } + + public function feedbackComplaints() + { + return view('administrator/feedback_complaints'); + } + + public function feeCollection() + { + return view('administrator/fee_collection'); + } + + public function expenseManagement() + { + return view('administrator/expense_management'); + } + + public function budgetReports() + { + return view('administrator/budget_reports'); + } + + public function scholarshipInformation() + { + return view('administrator/scholarship_information'); + } + + public function attendanceRecords() + { + return view('administrator/attendance_records'); + } + + public function academicPerformance() + { + return view('administrator/academic_performance'); + } + + public function behaviorReports() + { + return view('administrator/behavior_reports'); + } + + public function healthRecords() + { + return view('administrator/health_records'); + } + + public function contactInformation() + { + return view('administrator/contact_information'); + } + + private function redirectWithoutLegacyTermFilters(string $route): ?\CodeIgniter\HTTP\RedirectResponse + { + $legacyTermKeys = ['school_year', 'schoolYear', 'year', 'semester']; + $query = $this->request->getGet(); + $hasLegacyTermFilter = false; + + foreach ($legacyTermKeys as $key) { + if (array_key_exists($key, $query)) { diff --git a/_chunk8.txt b/_chunk8.txt new file mode 100644 index 0000000..a7e30ca --- /dev/null +++ b/_chunk8.txt @@ -0,0 +1,100 @@ + unset($query[$key]); + $hasLegacyTermFilter = true; + } + } + + if (!$hasLegacyTermFilter) { + return null; + } + + $target = site_url($route) . (!empty($query) ? '?' . http_build_query($query) : ''); + return redirect()->to($target); + } + + public function manageUsers() + {// Fetch all users + $users = $this->userModel->findAll(); + + // Fetch roles for each user + foreach ($users as &$user) { + $userRoles = ($this->userRoleModel)->where('user_id', $user['id'])->findAll(); + $user['roles'] = []; + foreach ($userRoles as $userRole) { + $role = $this->roleModel->find($userRole['role_id']); + if ($role) { + $user['roles'][] = $role['name']; + } + } + } + + $data['users'] = $users; + + return view('administrator/manage_users', $data); + } + + public function editUser($userId) + {$user = $this->userModel->find($userId); + + // Fetch roles assigned to the user + $userRoles = $this->userRoleModel->where('user_id', $userId)->findAll(); + $assignedRoles = []; + foreach ($userRoles as $userRole) { + $role = $this->roleModel->find($userRole['role_id']); + if ($role) { + $assignedRoles[] = $role['id']; + } + } + + $roles = $this->roleModel->findAll(); + + $data = [ + 'user' => $user, + 'roles' => $roles, + 'assignedRoles' => $assignedRoles, + ]; + + return view('administrator/edit_user', $data); + } + + public function updateUser() + {$userId = $this->request->getPost('id'); + $roleIds = $this->request->getPost('role_ids'); + + // Update user details + $data = [ + 'id' => $userId, + 'firstname' => $this->request->getPost('firstname'), + 'lastname' => $this->request->getPost('lastname'), + 'email' => $this->request->getPost('email'), + ]; + $this->userModel->save($data); + + // Update user roles + $this->userRoleModel->where('user_id', $userId)->delete(); + foreach ($roleIds as $roleId) { + $this->userRoleModel->save([ + 'user_id' => $userId, + 'role_id' => $roleId, + ]); + } + + service('staffDirectorySync')->syncUser((int) $userId); + + return redirect()->to('/administrator/manage-users'); + } + + public function deleteUser($userId) + {// Delete user roles first + $this->userRoleModel->where('user_id', $userId)->delete(); + + // Delete the user + $this->userModel->delete($userId); + + return redirect()->to('/administrator/manage-users'); + } + + private function canManageAdminNotifications(): bool + { + $session = session(); + if (! $session->get('is_logged_in')) { + return false; diff --git a/_chunk9.txt b/_chunk9.txt new file mode 100644 index 0000000..5de582d --- /dev/null +++ b/_chunk9.txt @@ -0,0 +1,100 @@ + } + + $role = trim((string) ($session->get('role') ?? '')); + if ($role === '') { + return false; + } + + $excluded = array_map( + fn ($value) => strtolower(trim((string) $value)), + service('adminNotificationSettings')->excludedRoles() + ); + + return !in_array(strtolower($role), $excluded, true); + } + + public function administratorDashboard() + { + helper('url'); + + $searchData = service('administratorDashboard')->search((string) $this->request->getGet('query')); + + return view('administrator/administratordashboard', array_merge($searchData, [ + 'dashboardEndpoint' => site_url('api/administrator/dashboard'), + 'schoolYear' => $this->schoolYear, + ])); + } + + public function dashboardMetrics() + { + return $this->response->setJSON( + service('administratorDashboard')->metrics((string) $this->schoolYear, (string) $this->semester) + ); + } + + public function userSearch() + { + $data = service('administratorDashboard')->search((string) $this->request->getGet('query')); + + return view('administrator/search_results', $data); + } + + public function teacherSubmissionsReport() + { + $semester = (string) ($this->semester !== '' ? $this->semester : (getSemester() ?? $this->semester ?? '')); + $schoolYear = trim((string) ($this->request->getGet('school_year') ?? '')); + if ($schoolYear === '') { + $schoolYear = $this->currentSchoolYearName((string) ($this->schoolYear ?? '')); + } + $lowProgressRaw = (string) $this->request->getGet('low_progress_sections'); + $lowProgressSectionIds = array_values(array_unique(array_filter(array_map( + 'intval', + preg_split('/\s*,\s*/', $lowProgressRaw, -1, PREG_SPLIT_NO_EMPTY) + )))); + + return view( + 'administrator/teacher_submissions', + service('teacherSubmissionReport')->buildReport($semester, $schoolYear, $lowProgressSectionIds) + ); + } + + public function sendTeacherSubmissionNotifications() + { + $result = service('teacherSubmissionReport')->sendNotifications( + (array) $this->request->getPost(), + (string) ($this->semester !== '' ? $this->semester : (getSemester() ?? '')), + (int) (session()->get('user_id') ?? 0) + ); + + if (($result['redirect'] ?? '') === 'login') { + return redirect()->to('/login'); + } + + return redirect()->back()->with((string) ($result['type'] ?? 'info'), (string) ($result['message'] ?? '')); + } + + public function notificationsAlerts() + { + if (!$this->canManageAdminNotifications()) { + return redirect()->to('/login'); + } + + return view('administrator/notifications_alerts', service('adminNotificationSettings')->alertsPage()); + } + + public function saveNotificationSubjects() + { + if (!$this->canManageAdminNotifications()) { + return redirect()->to('/login'); + } + + $result = service('adminNotificationSettings')->saveSubjects($this->request->getPost('subjects')); + + return redirect()->to('/administrator/notifications_alerts') + ->with((string) ($result['type'] ?? 'info'), (string) ($result['message'] ?? '')); + } + + public function printNotificationRecipients() + { + if (!$this->canManageAdminNotifications()) { + return redirect()->to('/login'); diff --git a/_func_list.txt b/_func_list.txt new file mode 100644 index 0000000..408f671 --- /dev/null +++ b/_func_list.txt @@ -0,0 +1,50 @@ +23: public function __construct() +38: private function allowedAbsenceDates(): array +83: public function absenceFormAdmin() +115: public function submitAbsenceAdmin() +239: public function index() +249: public function isadministrator() +278: public function teachers() +283: public function classes() +288: public function subjects() +293: public function reports() +298: public function teacherDashboard() +303: public function parentDashboard() +308: public function CourseMaterials() +313: public function administratorProfiles() +318: public function payrollManagement() +323: public function administratorAttendanceRecords() +328: public function performanceReviews() +333: public function communicationLogs() +338: public function feePaymentRecords() +343: public function feedbackComplaints() +348: public function feeCollection() +353: public function expenseManagement() +358: public function budgetReports() +363: public function scholarshipInformation() +368: public function attendanceRecords() +373: public function academicPerformance() +378: public function behaviorReports() +383: public function healthRecords() +388: public function contactInformation() +393: private function redirectWithoutLegacyTermFilters(string $route): ?\CodeIgn +414: public function manageUsers() +435: public function editUser($userId) +459: public function updateUser() +486: public function deleteUser($userId) +496: private function canManageAdminNotifications(): bool +516: public function administratorDashboard() +528: public function dashboardMetrics() +535: public function userSearch() +542: public function teacherSubmissionsReport() +561: public function sendTeacherSubmissionNotifications() +576: public function notificationsAlerts() +585: public function saveNotificationSubjects() +597: public function printNotificationRecipients() +606: public function savePrintNotificationRecipients() +618: public function studentProfiles() +628: public function parentProfiles() +637: public function showEnrollmentWithdrawalPage() +658: public function enrollmentWithdrawalData() +677: public function showNewStudents() +685: public function adminEnrollmentWithdrawalHandler() diff --git a/app/Views/about.php b/app/Views/about.php deleted file mode 100644 index b5927e1..0000000 --- a/app/Views/about.php +++ /dev/null @@ -1,239 +0,0 @@ - - - - - -Discover the impactful work we do and immerse yourself in our vibrant cultural activities. -
-Our programs are designed to enrich the community, fostering a deep appreciation for diverse traditions and values. - Join us to experience firsthand the creativity and passion that drive our initiatives. -
-
-
-
-
- Becoming a teacher or admin at our Sunday school is a rewarding opportunity to make a meaningful impact on young lives. As a teacher, you will inspire and guide children on their spiritual journey, fostering their growth and understanding of faith. As an admin, you will play a crucial role in supporting the school's operations and ensuring a smooth and effective learning environment. Join our dedicated team and contribute to a nurturing environment that shapes the future of our community.
- Get Started Now -
-
- Becoming a teacher or admin at our Sunday school is a rewarding opportunity to make a meaningful impact on young lives. As a teacher, you will inspire and guide children on their spiritual journey, fostering their growth and understanding of faith. As an admin, you will play a crucial role in supporting the school's operations and ensuring a smooth and effective learning environment. Join our dedicated team and contribute to a nurturing environment that shapes the future of our community.
- Get Started Now -Our school offers classes for students from grades 1 to 10, providing a comprehensive and engaging curriculum that fosters academic and personal growth. Additionally, our youth program offers enriching activities and mentorship for older students, helping them develop leadership skills and a sense of community. Together, these programs ensure a well-rounded education and support for every stage of your child's development.
-
-
-
- Get in touch with us today to learn more about our programs and how you can get involved.
-