From 75c71a1ac926f16149faf949aa54110d8d0130c1 Mon Sep 17 00:00:00 2001 From: Yoga Pangestu Date: Tue, 7 Apr 2026 11:44:29 +0700 Subject: [PATCH] feat: implement missed attendance handling and split schedule cards into today and missed sections --- .../Attendances/Pages/ManageAttendances.php | 72 ++++++++-- lang/id/notif.php | 12 +- .../learning/attendances/index.blade.php | 136 ++++++++++++++---- 3 files changed, 178 insertions(+), 42 deletions(-) diff --git a/app/Filament/Resources/Learning/Attendances/Pages/ManageAttendances.php b/app/Filament/Resources/Learning/Attendances/Pages/ManageAttendances.php index f78b62c..690aead 100644 --- a/app/Filament/Resources/Learning/Attendances/Pages/ManageAttendances.php +++ b/app/Filament/Resources/Learning/Attendances/Pages/ManageAttendances.php @@ -60,6 +60,18 @@ public function historyDescription(): string return SystemNotification::getByKey('labels.attendance_history.description'); } + #[Computed] + public function missedHeading(): string + { + return SystemNotification::getByKey('labels.missed_attendance.title'); + } + + #[Computed] + public function missedDescription(): string + { + return SystemNotification::getByKey('labels.missed_attendance.description'); + } + #[Computed] public function emptyHeading(): string { @@ -110,7 +122,8 @@ public function getSchedules() ->with(['course', 'attendances' => function ($q) use ($student) { $q->where('student_id', $student->id); }]) - ->orderBy('start_time') + ->orderBy('date', 'desc') + ->orderBy('start_time', 'asc') ->get(); } @@ -123,9 +136,14 @@ public function scheduleCards() $isAttended = $attendance !== null; $isSent = $schedule->is_sent_to_lecturer; + $sessionStart = $schedule->date->copy()->setTimeFrom($schedule->start_time); $now = now(); - $startTime = now()->setTimeFrom($schedule->start_time); - $canAttend = $now->greaterThanOrEqualTo($startTime) && ! $isSent; + $isPassed = $now->greaterThanOrEqualTo($sessionStart); + $isMissed = $schedule->date->isPast() && ! $schedule->date->isToday(); + + // If it's a missed session, we don't care about the time match anymore, + // as long as it's not sent yet. + $canAttend = ! $isSent && ($isMissed || $isPassed); $statusLabel = 'Belum Presensi'; $statusColor = 'warning'; @@ -139,19 +157,24 @@ public function scheduleCards() $statusLabel = 'Terkirim'; $statusColor = 'danger'; $statusIcon = 'heroicon-o-paper-airplane'; - } elseif (! $canAttend) { + } elseif (! $isPassed && ! $isMissed) { $statusLabel = 'Belum Dimulai'; $statusColor = 'gray'; $statusIcon = 'heroicon-o-lock-closed'; } + $isMissed = $schedule->date->isPast() && ! $schedule->date->isToday(); + return (object) [ 'id' => $schedule->id, 'course_name' => $schedule->course?->name, 'lecturer_name' => $schedule->course?->lecturer ?? 'Belum Ditentukan', + 'session_number' => $schedule->session_number, + 'date' => $schedule->date?->translatedFormat('l, d F Y'), 'time_range' => $schedule->start_time?->format('H:i').' - '.$schedule->end_time?->format('H:i'), 'is_attended' => $isAttended, 'is_sent_to_lecturer' => $isSent, + 'is_missed' => $isMissed, 'can_attend' => $canAttend && ! $isAttended, 'status_label' => $statusLabel, 'status_color' => $statusColor, @@ -197,6 +220,18 @@ public function scheduleCards() }); } + #[Computed] + public function todayScheduleCards() + { + return $this->scheduleCards()->filter(fn ($card) => ! $card->is_missed); + } + + #[Computed] + public function missedScheduleCards() + { + return $this->scheduleCards()->filter(fn ($card) => $card->is_missed); + } + public function attend(int $sessionId): void { /** @var User|null $user */ @@ -223,23 +258,30 @@ public function attend(int $sessionId): void return; } - // Check session status - if ($session->is_sent_to_lecturer) { + $isSent = $session->is_sent_to_lecturer; + $isMissed = $session->date->isPast() && ! $session->date->isToday(); + $now = now(); + $startTime = $session->date->copy()->setTimeFrom($session->start_time); + + // Attendance is allowed if: + // 1. Not sent yet + // 2. Either it's a missed session (from previous days) OR it's today and already started + if (! $isSent) { + if ($isMissed || $now->greaterThanOrEqualTo($startTime)) { + // Proceed with attendance + } else { + SystemNotification::send('attendance_not_started', type: 'warning') + ->send(); + + return; + } + } else { SystemNotification::send('attendance_closed', type: 'danger') ->send(); return; } - // Check time - $startTime = $session->start_time; - if (now()->lessThan($startTime)) { - SystemNotification::send('attendance_not_started', type: 'warning') - ->send(); - - return; - } - // Find schedule for compatibility $schedule = CourseSchedule::where('course_id', $session->course_id) ->where('day_of_week', $session->date?->dayOfWeekIso) diff --git a/lang/id/notif.php b/lang/id/notif.php index 38a6193..c48eecb 100644 --- a/lang/id/notif.php +++ b/lang/id/notif.php @@ -219,14 +219,18 @@ ], 'attendance_section' => [ 'title' => 'Ayo Presensi Dulu! 🙋‍♂️✨', - 'description' => 'Jangan lupa absen ya kalau kelasnya udah mulai! Biar tercatat hadir. 💪', + 'description' => 'Jangan lupa presensi ya kalau kelasnya udah mulai! Biar tercatat hadir. 💪', + ], + 'missed_attendance' => [ + 'title' => 'Presensi Terlewat! ⌛🏃‍♂️', + 'description' => 'Ayo segera presensi buat kelas yang udah lewat ini sebelum sesi ditutup! Jangan sampai alpa ya. 💪✨', ], 'attendance_history' => [ 'title' => 'Jejak Presensimu 🕰️📜', 'description' => 'Di sini kamu bisa ngecek semua histori kehadiranmu sebelumnya. Rajin-rajin ya! 🎓', ], 'empty_attendance' => [ - 'title' => 'Yah, Absensi Belum Muncul! 💁‍♂️🌀', + 'title' => 'Yah, Presensi Belum Muncul! 💁‍♂️🌀', 'description' => 'Sabar ya, setelah jadwal perkuliahan kamu muncul baru bisa diabsen di sini. Pantau terus jam kuliahnya! 🕒💪', ], 'assignment_list' => [ @@ -537,6 +541,10 @@ 'title' => 'Sesi Kuliah', 'description' => 'Silakan melakukan presensi sesuai dengan waktu kelas.', ], + 'missed_attendance' => [ + 'title' => 'Presensi Terlewat', + 'description' => 'Segera lakukan presensi untuk sesi perkuliahan yang telah berlalu berikut sebelum akses ditutup.', + ], 'attendance_history' => [ 'title' => 'Riwayat Presensi', 'description' => 'Berikut adalah riwayat kehadiran Anda pada sesi perkuliahan.', diff --git a/resources/views/filament/resources/learning/attendances/index.blade.php b/resources/views/filament/resources/learning/attendances/index.blade.php index e61fb57..de725e9 100644 --- a/resources/views/filament/resources/learning/attendances/index.blade.php +++ b/resources/views/filament/resources/learning/attendances/index.blade.php @@ -1,27 +1,30 @@ - + {{ $this->heading }} {{ $this->description }} - @if (count($this->scheduleCards)) + @if (count($this->todayScheduleCards))
- @foreach ($this->scheduleCards as $card) + @foreach ($this->todayScheduleCards as $card)
-

- !$card->is_attended, - 'text-primary-500' => $card->is_attended, - ]) /> - {{ $card->course_name }} -

+
+

+ !$card->is_attended, + 'text-primary-500' => $card->is_attended, + ]) /> + {{ $card->course_name }} +

+ + Sesi {{ $card->session_number }} + +
!$card->is_attended]) /> @@ -75,8 +78,8 @@ class="text-primary-600 dark:text-primary-200">{{ $card->attended_at }}
@if ($card->can_attend) + size="xs" icon="heroicon-m-finger-print" + class="rounded-lg shadow-sm font-bold uppercase tracking-tight text-[10px] w-full"> Presensi Sekarang @elseif ($card->is_attended) @@ -109,19 +112,102 @@ class="text-[10px] font-bold text-gray-400 uppercase tracking-widest leading-non @endforeach
@else - + @endif - - + @if (count($this->missedScheduleCards)) + + {{ $this->missedHeading }} + {{ $this->missedDescription }} + +
+ @foreach ($this->missedScheduleCards as $card) +
+
+ +
+
+
+

+ !$card->is_attended, + 'text-primary-500' => $card->is_attended, + ]) /> + {{ $card->course_name }} +

+ + Sesi {{ $card->session_number }} + +
+
+ !$card->is_attended]) /> + + {{ $card->lecturer_name }} + +
+
+ +
+
+
+ !$card->is_attended, + 'text-primary-700 dark:text-primary-200' => $card->is_attended, + ]) /> +
+
+ + {{ $card->date }} + + + {{ $card->time_range }} + +
+
+ +
!$card->is_attended, + 'border-primary-200 dark:border-primary-800' => $card->is_attended, + ])> +
+ Status +
+ + {{ $card->status_label }} + +
+
+
+
+
+ +
+ @if ($card->can_attend) + + Presensi Sekarang + + @endif +
+
+
+ @endforeach +
+
+ @endif + + {{ $this->historyHeading }} {{ $this->historyDescription }}