diff --git a/app/Filament/Resources/Learning/Assignments/Actions/MarkAsSentAction.php b/app/Filament/Resources/Learning/Assignments/Actions/MarkAsSentAction.php index 7001077..0ed0b1a 100644 --- a/app/Filament/Resources/Learning/Assignments/Actions/MarkAsSentAction.php +++ b/app/Filament/Resources/Learning/Assignments/Actions/MarkAsSentAction.php @@ -25,7 +25,7 @@ protected function setUp(): void ->icon(function (array $arguments, ?Assignment $record) { $assignment = $record ?? Assignment::find($arguments['record'] ?? null); - return $assignment?->is_sent_to_lecturer ? 'heroicon-o-lock-open' : 'heroicon-o-check-circle'; + return $assignment?->is_sent_to_lecturer ? 'heroicon-o-lock-open' : 'heroicon-o-paper-airplane'; }) ->color(function (array $arguments, ?Assignment $record) { $assignment = $record ?? Assignment::find($arguments['record'] ?? null); diff --git a/app/Filament/Resources/Learning/Assignments/Pages/SubmissionDetailPage.php b/app/Filament/Resources/Learning/Assignments/Pages/SubmissionDetailPage.php index d1c5c3a..b53994d 100644 --- a/app/Filament/Resources/Learning/Assignments/Pages/SubmissionDetailPage.php +++ b/app/Filament/Resources/Learning/Assignments/Pages/SubmissionDetailPage.php @@ -3,6 +3,7 @@ namespace App\Filament\Resources\Learning\Assignments\Pages; use App\Enums\AssignmentType; +use App\Enums\NotifStyle; use App\Filament\Actions\BackAction; use App\Filament\Resources\Learning\Assignments\Actions\MarkAsSentAction; use App\Filament\Resources\Learning\Assignments\AssignmentResource; @@ -181,7 +182,7 @@ public function assignmentSummary(): array #[Computed] public function sectionIcon(): string { - return SystemNotification::getNotifStyle() === \App\Enums\NotifStyle::Cheerful + return SystemNotification::getNotifStyle() === NotifStyle::Cheerful ? 'heroicon-o-document-magnifying-glass' : 'heroicon-o-document-text'; } diff --git a/app/Filament/Resources/Learning/Attendances/Pages/ManageAttendances.php b/app/Filament/Resources/Learning/Attendances/Pages/ManageAttendances.php index 4640ccc..f78b62c 100644 --- a/app/Filament/Resources/Learning/Attendances/Pages/ManageAttendances.php +++ b/app/Filament/Resources/Learning/Attendances/Pages/ManageAttendances.php @@ -99,7 +99,14 @@ public function getSchedules() } return ClassSession::query() - ->whereDate('date', now()) + ->where(function ($query) use ($student) { + $query->whereDate('date', now()) + ->orWhere(function ($q) use ($student) { + $q->whereDate('date', '<', now()) + ->where('is_sent_to_lecturer', false) + ->whereDoesntHave('attendances', fn ($aq) => $aq->where('student_id', $student->id)); + }); + }) ->with(['course', 'attendances' => function ($q) use ($student) { $q->where('student_id', $student->id); }]) @@ -114,10 +121,11 @@ public function scheduleCards() ->map(function ($schedule) { $attendance = $schedule->attendances?->first(); $isAttended = $attendance !== null; + $isSent = $schedule->is_sent_to_lecturer; $now = now(); $startTime = now()->setTimeFrom($schedule->start_time); - $canAttend = $now->greaterThanOrEqualTo($startTime); + $canAttend = $now->greaterThanOrEqualTo($startTime) && ! $isSent; $statusLabel = 'Belum Presensi'; $statusColor = 'warning'; @@ -127,6 +135,10 @@ public function scheduleCards() $statusLabel = 'Hadir'; $statusColor = 'success'; $statusIcon = 'heroicon-o-check-circle'; + } elseif ($isSent) { + $statusLabel = 'Terkirim'; + $statusColor = 'danger'; + $statusIcon = 'heroicon-o-paper-airplane'; } elseif (! $canAttend) { $statusLabel = 'Belum Dimulai'; $statusColor = 'gray'; @@ -139,6 +151,7 @@ public function scheduleCards() 'lecturer_name' => $schedule->course?->lecturer ?? 'Belum Ditentukan', 'time_range' => $schedule->start_time?->format('H:i').' - '.$schedule->end_time?->format('H:i'), 'is_attended' => $isAttended, + 'is_sent_to_lecturer' => $isSent, 'can_attend' => $canAttend && ! $isAttended, 'status_label' => $statusLabel, 'status_color' => $statusColor, @@ -210,6 +223,14 @@ public function attend(int $sessionId): void return; } + // Check session status + if ($session->is_sent_to_lecturer) { + SystemNotification::send('attendance_closed', type: 'danger') + ->send(); + + return; + } + // Check time $startTime = $session->start_time; if (now()->lessThan($startTime)) { diff --git a/app/Filament/Resources/Learning/ClassSessions/Actions/MarkAsSentAction.php b/app/Filament/Resources/Learning/ClassSessions/Actions/MarkAsSentAction.php new file mode 100644 index 0000000..93b63ee --- /dev/null +++ b/app/Filament/Resources/Learning/ClassSessions/Actions/MarkAsSentAction.php @@ -0,0 +1,56 @@ +label(function (array $arguments, ?ClassSession $record) { + $session = $record ?? ClassSession::find($arguments['record'] ?? null); + + return $session?->is_sent_to_lecturer ? 'Buka Kunci' : 'Tandai Terkirim'; + }) + ->icon(function (array $arguments, ?ClassSession $record) { + $session = $record ?? ClassSession::find($arguments['record'] ?? null); + + return $session?->is_sent_to_lecturer ? 'heroicon-o-lock-open' : 'heroicon-o-paper-airplane'; + }) + ->color(function (array $arguments, ?ClassSession $record) { + $session = $record ?? ClassSession::find($arguments['record'] ?? null); + + return $session?->is_sent_to_lecturer ? 'warning' : 'success'; + }) + ->link() + ->visible(fn () => auth()->user()->hasRole(['Kosma', 'Developer'])) + ->action(function (array $arguments, $livewire, ?ClassSession $record) { + $session = $record ?? ClassSession::find($arguments['record'] ?? null); + + if (! $session) { + return; + } + + $session->update([ + 'is_sent_to_lecturer' => ! $session->is_sent_to_lecturer, + ]); + + SystemNotification::send($session->is_sent_to_lecturer ? 'session_sent' : 'session_unlocked') + ->send(); + + if (isset($livewire->sessions)) { + unset($livewire->sessions); + } + }); + } +} diff --git a/app/Filament/Resources/Learning/ClassSessions/Pages/ListCourseSessions.php b/app/Filament/Resources/Learning/ClassSessions/Pages/ListCourseSessions.php index 4ad186d..e91d960 100644 --- a/app/Filament/Resources/Learning/ClassSessions/Pages/ListCourseSessions.php +++ b/app/Filament/Resources/Learning/ClassSessions/Pages/ListCourseSessions.php @@ -6,6 +6,7 @@ use App\Filament\Resources\Learning\ClassSessions\Actions\DeleteSessionAction; use App\Filament\Resources\Learning\ClassSessions\Actions\EditSessionAction; use App\Filament\Resources\Learning\ClassSessions\Actions\GenerateSessionsAction; +use App\Filament\Resources\Learning\ClassSessions\Actions\MarkAsSentAction; use App\Filament\Resources\Learning\ClassSessions\Actions\ShareAttendanceAction; use App\Filament\Resources\Learning\ClassSessions\Actions\ViewAssignmentsAction; use App\Filament\Resources\Learning\ClassSessions\Actions\ViewAttendanceAction; @@ -60,6 +61,7 @@ public function sessions(): Collection 'attendance_percentage' => $totalActiveStudents > 0 ? round(($session->attendances_count / $totalActiveStudents) * 100) : 0, 'materials_count' => $session->materials_count, 'assignments_count' => $session->assignments_count, + 'is_sent_to_lecturer' => $session->is_sent_to_lecturer, ]); } @@ -114,6 +116,11 @@ public function editSessionAction(): EditSessionAction return EditSessionAction::make(); } + public function markAsSentAction(): MarkAsSentAction + { + return MarkAsSentAction::make(); + } + public function deleteSessionAction(): DeleteSessionAction { return DeleteSessionAction::make(); diff --git a/app/Filament/Resources/Learning/ClassSessions/Pages/ManageClassSessions.php b/app/Filament/Resources/Learning/ClassSessions/Pages/ManageClassSessions.php index d4b2b45..443f89e 100644 --- a/app/Filament/Resources/Learning/ClassSessions/Pages/ManageClassSessions.php +++ b/app/Filament/Resources/Learning/ClassSessions/Pages/ManageClassSessions.php @@ -15,6 +15,7 @@ use Filament\Forms\Contracts\HasForms; use Filament\Resources\Pages\Page; use Filament\Schemas\Schema; +use Illuminate\Support\Arr; use Illuminate\Support\Collection; use Livewire\Attributes\Computed; @@ -85,13 +86,21 @@ public function todaySessions(): Collection 'attendance_percentage' => $percentage, 'materials_count' => $session->materials_count, 'assignments_count' => $session->assignments_count, + 'is_sent_to_lecturer' => $session->is_sent_to_lecturer, 'url' => ClassSessionResource::getUrl('course', ['course' => $session->course]), + // Pre-calculated classes + 'status_label' => $session->is_sent_to_lecturer ? 'Terkirim' : $session->time_range, + 'status_icon' => $session->is_sent_to_lecturer ? 'heroicon-s-paper-airplane' : 'heroicon-s-clock', // Pre-calculated classes 'card_classes' => 'group flex w-full rounded-xl border border-primary-300 dark:border-primary-700 bg-primary-50/30 dark:bg-primary-900/10 shadow-sm overflow-hidden relative transition-all hover:shadow-md', 'session_badge_classes' => 'flex h-12 w-12 shrink-0 flex-col items-center justify-center rounded-lg font-bold border bg-primary-100 dark:bg-primary-800 text-primary-700 dark:text-primary-300 border-primary-200 dark:border-primary-700', 'title_classes' => 'font-bold transition-colors text-gray-900 dark:text-white group-hover:text-primary-600', - 'status_badge_classes' => 'flex items-center gap-1.5 rounded-full px-3 py-1 text-[11px] font-bold shadow-sm bg-primary-500 text-white animate-pulse', + 'status_badge_classes' => Arr::toCssClasses([ + 'flex items-center gap-1.5 rounded-full px-3 py-1 text-[11px] font-bold shadow-sm text-white', + 'bg-primary-500 animate-pulse' => ! $session->is_sent_to_lecturer, + 'bg-danger-500' => $session->is_sent_to_lecturer, + ]), 'attendance_section_classes' => 'flex flex-col items-center justify-center border-l border-primary-200 dark:border-primary-800 p-2 bg-white/30 dark:bg-black/10 transition-colors group-hover:bg-primary-500/5', ]; }); diff --git a/app/Models/ClassSession.php b/app/Models/ClassSession.php index 6750f15..7361066 100644 --- a/app/Models/ClassSession.php +++ b/app/Models/ClassSession.php @@ -28,6 +28,7 @@ protected function casts(): array 'end_time' => 'datetime', 'session_number' => 'integer', 'start_time' => 'datetime', + 'is_sent_to_lecturer' => 'boolean', ]; } diff --git a/database/migrations/2026_04_07_094148_add_is_sent_to_lecturer_to_assignments_table.php b/database/migrations/2026_04_07_094148_add_is_sent_to_lecturer_to_assignments_table.php index d4606d6..4eb40db 100644 --- a/database/migrations/2026_04_07_094148_add_is_sent_to_lecturer_to_assignments_table.php +++ b/database/migrations/2026_04_07_094148_add_is_sent_to_lecturer_to_assignments_table.php @@ -11,7 +11,7 @@ */ public function up(): void { - if (!Schema::hasColumn('assignments', 'is_sent_to_lecturer')) { + if (! Schema::hasColumn('assignments', 'is_sent_to_lecturer')) { Schema::table('assignments', function (Blueprint $table) { $table->boolean('is_sent_to_lecturer')->default(false)->after('due_date'); }); diff --git a/database/migrations/2026_04_07_102319_add_is_sent_to_lecturer_to_class_sessions_table.php b/database/migrations/2026_04_07_102319_add_is_sent_to_lecturer_to_class_sessions_table.php new file mode 100644 index 0000000..aeb7b16 --- /dev/null +++ b/database/migrations/2026_04_07_102319_add_is_sent_to_lecturer_to_class_sessions_table.php @@ -0,0 +1,30 @@ +boolean('is_sent_to_lecturer')->default(false)->after('end_time'); + }); + } + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('class_sessions', function (Blueprint $table) { + $table->dropColumn('is_sent_to_lecturer'); + }); + } +}; diff --git a/lang/id/notif.php b/lang/id/notif.php index 826b890..38a6193 100644 --- a/lang/id/notif.php +++ b/lang/id/notif.php @@ -157,6 +157,18 @@ 'title' => 'Tugas Terkunci! 🔒', 'body' => 'Mohon maaf, tugas ini sudah ditandai terkirim ke dosen oleh Kosma dan tidak dapat diubah lagi. 🚫', ], + 'session_sent' => [ + 'title' => 'Sesi Terkirim! 🚀✨', + 'body' => 'Laporan presensi untuk sesi ini telah resmi ditandai terkirim ke dosen. Data aman tersimpan! ✅', + ], + 'session_unlocked' => [ + 'title' => 'Kunci Presensi Dibuka! 🔓✨', + 'body' => 'Mahasiswa sekarang bisa melakukan presensi kembali untuk sesi ini. 📝', + ], + 'attendance_closed' => [ + 'title' => 'Yah, Presensi Sudah Tutup! 😭⌛', + 'body' => 'Maaf ya, sesi presensi ini sudah ditutup oleh Kosma. Lain kali jangan sampai terlambat ya! 💪', + ], // UI Labels & Descriptions 'labels' => [ @@ -461,6 +473,18 @@ 'title' => 'Akses Pengumpulan Ditutup', 'body' => 'Tugas ini telah dalam status final (terkirim ke dosen). Perubahan data tidak lagi dimungkinkan.', ], + 'session_sent' => [ + 'title' => 'Konfirmasi Pengiriman Laporan', + 'body' => 'Laporan presensi sesi perkuliahan telah berhasil ditandai sebagai terkirim ke dosen pengampu.', + ], + 'session_unlocked' => [ + 'title' => 'Kunci Akses Presensi Dibuka', + 'body' => 'Otoritas presensi telah diaktifkan kembali. Seluruh mahasiswa terkait dapat melakukan pengisian data kehadiran.', + ], + 'attendance_closed' => [ + 'title' => 'Akses Presensi Ditutup', + 'body' => 'Sesi presensi untuk mata kuliah ini telah ditutup secara resmi. Anda tidak dapat melakukan pengisian data kehadiran lagi.', + ], // UI Labels & Descriptions 'labels' => [ diff --git a/resources/views/filament/resources/learning/attendances/index.blade.php b/resources/views/filament/resources/learning/attendances/index.blade.php index 96479be..e61fb57 100644 --- a/resources/views/filament/resources/learning/attendances/index.blade.php +++ b/resources/views/filament/resources/learning/attendances/index.blade.php @@ -89,6 +89,13 @@ class="text-[10px] font-bold text-primary-700 dark:text-primary-200 uppercase tr Selesai + @elseif ($card->is_sent_to_lecturer) +
+ + Sesi + Terkirim +
@else
- - {{ $session->time_range }} + + {{ $session->status_label }}
diff --git a/resources/views/filament/resources/learning/class-sessions/show.blade.php b/resources/views/filament/resources/learning/class-sessions/show.blade.php index 4e7379b..cf41c96 100644 --- a/resources/views/filament/resources/learning/class-sessions/show.blade.php +++ b/resources/views/filament/resources/learning/class-sessions/show.blade.php @@ -27,7 +27,7 @@ class="flex flex-wrap items-center gap-x-4 gap-y-1 text-sm text-gray-500 dark:te -
+
@@ -40,6 +40,12 @@ class="text-[10px] opacity-60">/{{ $session->total_students }}
{{ $session->attendance_percentage }}% + + @if ($session->is_sent_to_lecturer) + + Terkirim + + @endif
@@ -60,6 +66,7 @@ class="text-[10px] font-bold text-gray-500">{{ $session->attendance_percentage } class="flex flex-wrap items-center gap-3 px-4 py-3 border-t border-gray-100 dark:border-gray-700"> {{ ($this->viewAttendanceAction)(['session' => $session->id]) }} {{ ($this->shareAttendanceAction)(['session' => $session->id]) }} + {{ ($this->markAsSentAction)(['record' => $session->id]) }} {{ ($this->editSessionAction)(['session' => $session->id]) }} {{ ($this->deleteSessionAction)(['session' => $session->id]) }} diff --git a/tests/Feature/AssignmentTest.php b/tests/Feature/AssignmentTest.php index 4def8a9..b06f7e3 100644 --- a/tests/Feature/AssignmentTest.php +++ b/tests/Feature/AssignmentTest.php @@ -433,4 +433,3 @@ ->assertDontSeeHtml('type="file"'); }); }); -