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 = '
' . '

New TimeOff Request

' . '

A staff time-off request was submitted from the administrator portal.

' . '' . '' . '' . '' . '' . '' . '' . '' . '' . '' . '
Name' . esc($fullName) . '
Email' . 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) . '
' . '
'; $linkService = new StaffTimeOffLinkService(); $token = $linkService->createToken([ 'uid' => $userId, 'email' => $userEmail, 'name' => $fullName, 'role' => $role, 'dates' => $dateList ?: '-', 'reason' => $reasonText, 'reason_type' => $reasonType, 'submitted_at' => $submittedAt, 'origin' => 'administrator portal', ]); $notifyUrl = base_url('timeoff/notify/' . rawurlencode($token)); $body .= '

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.'); }