Files
alrahma_sunday_school_api/app/Services/Administrator/AdministratorAbsenceService.php
T
2026-06-08 23:30:22 -04:00

232 lines
8.0 KiB
PHP

<?php
namespace App\Services\Administrator;
use App\Models\StaffAttendance;
use App\Models\User;
use App\Services\ApplicationUrlService;
use App\Services\SemesterRangeService;
use App\Services\Staff\StaffTimeOffLinkService;
use Carbon\Carbon;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Mail;
class AdministratorAbsenceService
{
public function __construct(
protected AdministratorSharedService $shared,
protected User $userModel,
protected StaffAttendance $staffAttendanceModel,
protected SemesterRangeService $semesterRangeService,
protected StaffTimeOffLinkService $staffTimeOffLinkService,
protected ApplicationUrlService $urls,
) {}
public function getAbsenceFormData(int $userId): array
{
$admin = $this->userModel->find($userId);
$displayName = $admin
? trim(($admin->firstname ?? '').' '.($admin->lastname ?? ''))
: 'Administrator';
$semester = $this->shared->getSemester();
$schoolYear = $this->shared->getSchoolYear();
$existing = $this->staffAttendanceModel
->where('user_id', $userId)
->where('semester', $semester)
->where('school_year', $schoolYear)
->orderByDesc('date')
->get()
->toArray();
return [
'admin_name' => $displayName,
'semester' => $semester,
'schoolYear' => $schoolYear,
'existing' => $existing,
'availableDates' => $this->shared->allowedAbsenceDates(),
];
}
public function submit(Request $request, int $userId): array
{
$semester = $this->shared->getSemester();
$schoolYear = $this->shared->getSchoolYear();
if ($schoolYear === '') {
return [
'success' => false,
'message' => 'Semester or school year not configured.',
'status' => 422,
];
}
$dates = (array) $request->input('dates', []);
$reasonType = trim((string) $request->input('reason_type', ''));
$reasonText = trim((string) $request->input('reason', ''));
if ($reasonText === '') {
return [
'success' => false,
'message' => 'Reason is required.',
'status' => 422,
];
}
$reasonBase = $reasonType !== '' ? strtolower($reasonType) : '';
$reason = $reasonBase !== '' ? ($reasonBase.': '.$reasonText) : $reasonText;
$allowedSet = array_fill_keys($this->shared->allowedAbsenceDates(), true);
$roleName = method_exists($this->userModel, 'getUserRole')
? ($this->userModel->getUserRole($userId) ?: null)
: null;
$saved = 0;
$invalid = [];
$savedDates = [];
$dates = array_values(array_unique(array_map('strval', $dates)));
foreach ($dates as $d) {
$d = trim($d);
if ($d === '') {
continue;
}
try {
$parsed = Carbon::createFromFormat('Y-m-d', $d);
if ($parsed->format('Y-m-d') !== $d || empty($allowedSet[$d])) {
$invalid[] = $d;
continue;
}
} catch (\Throwable) {
$invalid[] = $d;
continue;
}
$semesterForDate = $this->semesterRangeService->getSemesterForDate($d);
if ($semesterForDate === '') {
$semesterForDate = $semester;
}
$ok = $this->staffAttendanceModel->upsertOne(
userId: $userId,
roleName: $roleName,
date: $d,
semester: $semesterForDate,
schoolYear: $schoolYear,
status: StaffAttendance::STATUS_ABSENT,
reason: $reason,
editorId: $userId
);
if ($ok) {
$saved++;
$savedDates[] = $d;
}
}
if (! empty($invalid)) {
return [
'success' => false,
'message' => 'Invalid dates: '.implode(', ', $invalid),
'status' => 422,
];
}
$this->sendPrincipalNotification(
userId: $userId,
roleName: $roleName,
semester: $semester,
schoolYear: $schoolYear,
reasonType: $reasonType,
reasonText: $reasonText,
dates: $dates,
savedDates: $savedDates
);
return [
'success' => true,
'message' => $saved.' day(s) saved as absent.',
'saved' => $saved,
'dates' => $savedDates,
'status' => 200,
];
}
protected function sendPrincipalNotification(
int $userId,
?string $roleName,
string $semester,
string $schoolYear,
string $reasonType,
string $reasonText,
array $dates,
array $savedDates
): void {
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 = now()->toDateTimeString();
$body = '<div style="font-family:Arial,Helvetica,sans-serif;font-size:14px;line-height:1.5;">'
.'<h3 style="margin:0 0 8px;">New TimeOff Request</h3>'
.'<p style="margin:0 0 12px;">A staff time-off request was submitted from the administrator portal.</p>'
.'<table cellpadding="6" cellspacing="0" style="border-collapse:collapse;">'
.'<tr><td><strong>Name</strong></td><td>'.e($fullName).'</td></tr>'
.'<tr><td><strong>Email</strong></td><td>'.e($userEmail).'</td></tr>'
.'<tr><td><strong>Role</strong></td><td>'.e((string) $role).'</td></tr>'
.'<tr><td><strong>Semester</strong></td><td>'.e($semester).'</td></tr>'
.'<tr><td><strong>School Year</strong></td><td>'.e($schoolYear).'</td></tr>'
.'<tr><td><strong>Reason Type</strong></td><td>'.e($reasonType ?: '-').'</td></tr>'
.'<tr><td><strong>Reason</strong></td><td>'.e($reasonText).'</td></tr>'
.'<tr><td><strong>Dates</strong></td><td>'.e($dateList ?: '-').'</td></tr>'
.'<tr><td><strong>Submitted At</strong></td><td>'.e($submittedAt).'</td></tr>'
.'</table>'
.'</div>';
$token = $this->staffTimeOffLinkService->createToken([
'uid' => $userId,
'email' => $userEmail,
'name' => $fullName,
'role' => $role,
'dates' => $dateList ?: '-',
'reason' => $reasonText,
'reason_type' => $reasonType,
'submitted_at' => $submittedAt,
'origin' => 'administrator portal',
]);
$notifyUrl = $this->urls->timeoffNotifyUrl($token);
$body .= '<p style="margin-top:16px;">Click <a href="'.e($notifyUrl).'">Send confirmation email to '
.e($fullName)
.'</a> so the staff member is notified automatically. This link expires in 14 days.</p>';
$principalEmail = env('PRINCIPAL_EMAIL', 'principal@alrahmaisgl.org');
Mail::html($body, function ($message) use ($principalEmail, $subject) {
$message->to($principalEmail)->subject($subject);
});
} catch (\Throwable $e) {
Log::error('Failed to send TimeOff email (admin): '.$e->getMessage());
}
}
}