From 0519e1cd355e3537a6d02598b7c01849d7008bb8 Mon Sep 17 00:00:00 2001 From: Yoga Pangestu Date: Sat, 4 Apr 2026 00:37:29 +0700 Subject: [PATCH] feat: implement new actions for viewing assignments, attendance, and materials in class sessions, and update routing for course sessions --- .../Actions/ViewAssignmentsAction.php | 72 +++++++ .../Actions/ViewAttendanceAction.php | 57 ++++++ .../Actions/ViewMaterialsAction.php | 35 ++++ .../ClassSessions/ClassSessionResource.php | 2 +- .../Pages/ListCourseSessions.php | 187 ++++-------------- .../Pages/ManageClassSessions.php | 77 +++++++- .../ClassSessions/Schemas/SessionForm.php | 54 +++++ lang/id/notif.php | 50 +++++ .../class-sessions/assignment-modal.blade.php | 86 ++++++++ .../assignments-modal.blade.php | 77 -------- .../class-sessions/attendance-modal.blade.php | 50 +++-- .../learning/class-sessions/index.blade.php | 24 +-- ...dal.blade.php => material-modal.blade.php} | 7 +- .../class-sessions/materials-modal.blade.php | 29 --- .../learning/class-sessions/show.blade.php | 85 ++++---- tests/Feature/ClassSessionTest.php | 52 ++++- 16 files changed, 606 insertions(+), 338 deletions(-) create mode 100644 app/Filament/Resources/Learning/ClassSessions/Actions/ViewAssignmentsAction.php create mode 100644 app/Filament/Resources/Learning/ClassSessions/Actions/ViewAttendanceAction.php create mode 100644 app/Filament/Resources/Learning/ClassSessions/Actions/ViewMaterialsAction.php create mode 100644 app/Filament/Resources/Learning/ClassSessions/Schemas/SessionForm.php create mode 100644 resources/views/filament/resources/learning/class-sessions/assignment-modal.blade.php delete mode 100644 resources/views/filament/resources/learning/class-sessions/assignments-modal.blade.php rename resources/views/filament/resources/learning/class-sessions/{material-detail-modal.blade.php => material-modal.blade.php} (51%) delete mode 100644 resources/views/filament/resources/learning/class-sessions/materials-modal.blade.php diff --git a/app/Filament/Resources/Learning/ClassSessions/Actions/ViewAssignmentsAction.php b/app/Filament/Resources/Learning/ClassSessions/Actions/ViewAssignmentsAction.php new file mode 100644 index 0000000..302622c --- /dev/null +++ b/app/Filament/Resources/Learning/ClassSessions/Actions/ViewAssignmentsAction.php @@ -0,0 +1,72 @@ +label('Tugas') + ->color(Color::Sky) + ->icon('heroicon-o-clipboard-document-list') + ->outlined() + ->modalHeading('Tugas Sesi') + ->modalSubmitAction(false) + ->modalCancelActionLabel('Tutup') + ->modalWidth(Width::FourExtraLarge) + ->modalContent(fn (array $arguments) => view('filament.resources.learning.class-sessions.assignment-modal', (function () use ($arguments) { + $session = ClassSession::with([ + 'assignments.assignmentSubmissions', + ])->find($arguments['session'] ?? null); + + if (! $session) { + return ['assignment' => null]; + } + + $assignment = $session->assignments->first(); + + if (! $assignment) { + return ['assignment' => null]; + } + + $activeStudents = Student::query() + ->whereHas('user', fn ($q) => $q->active()) + ->orderBy('full_name') + ->get(); + + $submissionMap = $assignment->assignmentSubmissions->keyBy('student_id'); + + $data = (object) [ + 'id' => $assignment->id, + 'title' => $assignment->title, + 'type_label' => $assignment->type?->getLabel() ?? 'Tugas', + 'due_date_formatted' => $assignment->due_date?->translatedFormat('d F Y H:i') ?? '-', + 'submissions' => $activeStudents->map(fn ($student) => (object) [ + 'student_name' => $student->full_name, + 'student_number' => $student->student_number, + 'submitted_at_formatted' => $submissionMap->get($student->id)?->submitted_at?->translatedFormat('d F Y H:i'), + 'is_submitted' => $submissionMap->has($student->id), + ]), + 'submission_count' => $submissionMap->count(), + 'total_students' => $activeStudents->count(), + 'url' => AssignmentResource::getUrl('submit', ['record' => $assignment->id]), + ]; + + return ['assignment' => $data]; + })())); + } +} diff --git a/app/Filament/Resources/Learning/ClassSessions/Actions/ViewAttendanceAction.php b/app/Filament/Resources/Learning/ClassSessions/Actions/ViewAttendanceAction.php new file mode 100644 index 0000000..6a33013 --- /dev/null +++ b/app/Filament/Resources/Learning/ClassSessions/Actions/ViewAttendanceAction.php @@ -0,0 +1,57 @@ +label('Presensi') + ->color('success') + ->icon('heroicon-o-check-circle') + ->link() + ->modalHeading('Daftar Presensi') + ->modalSubmitAction(false) + ->modalCancelActionLabel('Tutup') + ->modalWidth(Width::TwoExtraLarge) + ->modalContent(fn (array $arguments) => view('filament.resources.learning.class-sessions.attendance-modal', (function () use ($arguments) { + $session = ClassSession::with([ + 'attendances.student', + ])->find($arguments['session'] ?? null); + + if (! $session) { + return ['students' => collect(), 'attendedCount' => 0]; + } + + $activeStudents = Student::query() + ->whereHas('user', fn ($q) => $q->active()) + ->orderBy('full_name') + ->get(); + + $attendanceMap = $session->attendances->keyBy('student_id'); + + $students = $activeStudents->map(fn ($student) => (object) [ + 'student' => $student, + 'attended_at' => $attendanceMap->get($student->id)?->attended_at?->translatedFormat('d F Y H:i'), + 'has_attended' => $attendanceMap->has($student->id), + ]); + + return [ + 'students' => $students, + 'attendedCount' => $attendanceMap->count(), + ]; + })())); + } +} diff --git a/app/Filament/Resources/Learning/ClassSessions/Actions/ViewMaterialsAction.php b/app/Filament/Resources/Learning/ClassSessions/Actions/ViewMaterialsAction.php new file mode 100644 index 0000000..3733556 --- /dev/null +++ b/app/Filament/Resources/Learning/ClassSessions/Actions/ViewMaterialsAction.php @@ -0,0 +1,35 @@ +label('Materi') + ->color(Color::Purple) + ->icon('heroicon-o-book-open') + ->outlined() + ->modalHeading('Materi Sesi') + ->modalSubmitAction(false) + ->modalCancelActionLabel('Tutup') + ->modalWidth(Width::FourExtraLarge) + ->modalContent(function (array $arguments) { + $material = ClassSession::find($arguments['session'] ?? null)?->materials()->first(); + + return view('filament.resources.learning.class-sessions.material-modal', ['record' => $material]); + }); + } +} diff --git a/app/Filament/Resources/Learning/ClassSessions/ClassSessionResource.php b/app/Filament/Resources/Learning/ClassSessions/ClassSessionResource.php index 75bf799..05f6880 100644 --- a/app/Filament/Resources/Learning/ClassSessions/ClassSessionResource.php +++ b/app/Filament/Resources/Learning/ClassSessions/ClassSessionResource.php @@ -29,7 +29,7 @@ public static function getPages(): array { return [ 'index' => ManageClassSessions::route('/'), - 'course' => ListCourseSessions::route('/course/{courseId}'), + 'course' => ListCourseSessions::route('/course/{course}'), ]; } diff --git a/app/Filament/Resources/Learning/ClassSessions/Pages/ListCourseSessions.php b/app/Filament/Resources/Learning/ClassSessions/Pages/ListCourseSessions.php index 2761bb7..3fcc534 100644 --- a/app/Filament/Resources/Learning/ClassSessions/Pages/ListCourseSessions.php +++ b/app/Filament/Resources/Learning/ClassSessions/Pages/ListCourseSessions.php @@ -7,23 +7,20 @@ use App\Filament\Resources\Learning\ClassSessions\Actions\EditSessionAction; use App\Filament\Resources\Learning\ClassSessions\Actions\GenerateSessionsAction; use App\Filament\Resources\Learning\ClassSessions\Actions\ShareAttendanceAction; +use App\Filament\Resources\Learning\ClassSessions\Actions\ViewAssignmentsAction; +use App\Filament\Resources\Learning\ClassSessions\Actions\ViewAttendanceAction; +use App\Filament\Resources\Learning\ClassSessions\Actions\ViewMaterialsAction; use App\Filament\Resources\Learning\ClassSessions\ClassSessionResource; -use App\Models\ClassSession; +use App\Filament\Resources\Learning\ClassSessions\Schemas\SessionForm; +use App\Filament\Support\SystemNotification; use App\Models\Course; -use App\Models\Material; use App\Models\Student; -use Filament\Actions\Action; use Filament\Actions\Concerns\InteractsWithActions; use Filament\Actions\Contracts\HasActions; -use Filament\Forms\Components\DatePicker; -use Filament\Forms\Components\TextInput; -use Filament\Forms\Components\TimePicker; use Filament\Forms\Concerns\InteractsWithForms; use Filament\Forms\Contracts\HasForms; use Filament\Resources\Pages\Page; -use Filament\Schemas\Components\Grid; use Filament\Schemas\Schema; -use Filament\Support\Enums\Width; use Illuminate\Support\Collection; use Livewire\Attributes\Computed; @@ -35,17 +32,11 @@ class ListCourseSessions extends Page implements HasActions, HasForms protected string $view = 'filament.resources.learning.class-sessions.show'; - public $courseId; + public Course $course; - public function mount($courseId): void + public function mount(Course $course): void { - $this->courseId = $courseId; - } - - #[Computed] - public function course(): Course - { - return Course::findOrFail($this->courseId); + $this->course = $course; } #[Computed] @@ -74,44 +65,7 @@ public function sessions(): Collection public function form(Schema $schema): Schema { - return $schema - ->schema([ - Grid::make(['default' => 2]) - ->schema([ - TextInput::make('session_number') - ->label('Pertemuan Ke-') - ->placeholder('1') - ->numeric() - ->required() - ->minValue(1) - ->maxValue(16) - ->autocomplete(false), - - DatePicker::make('date') - ->label('Tanggal') - ->placeholder('Pilih Tanggal') - ->required() - ->native(false) - ->displayFormat('l, d F Y') - ->default(now()->toDateString()), - - TimePicker::make('start_time') - ->label('Waktu Mulai') - ->placeholder('08:00') - ->native(false) - ->displayFormat('H:i') - ->seconds(false) - ->required(), - - TimePicker::make('end_time') - ->label('Waktu Selesai') - ->placeholder('10:00') - ->native(false) - ->displayFormat('H:i') - ->seconds(false) - ->required(), - ]), - ]); + return SessionForm::configure($schema); } #[Computed] @@ -135,114 +89,51 @@ protected function getHeaderActions(): array ]; } - protected function getActions(): array + public function viewAttendanceAction(): ViewAttendanceAction { - return [ - $this->shareAttendanceAction(), - $this->viewAttendanceAction(), - $this->viewMaterialsAction(), - $this->viewAssignmentsAction(), - $this->editSessionAction(), - $this->deleteSessionAction(), - $this->viewMaterialDetailAction(), - ]; + return ViewAttendanceAction::make(); } - public function shareAttendanceAction(): Action + public function viewMaterialsAction(): ViewMaterialsAction + { + return ViewMaterialsAction::make(); + } + + public function viewAssignmentsAction(): ViewAssignmentsAction + { + return ViewAssignmentsAction::make(); + } + + public function shareAttendanceAction(): ShareAttendanceAction { return ShareAttendanceAction::make(); } - public function viewAttendanceAction(): Action - { - return Action::make('viewAttendance') - ->label('Presensi') - ->color('success') - ->icon('heroicon-o-check-circle') - ->link() - ->modalHeading('Daftar Presensi') - ->modalSubmitAction(false) - ->modalCancelActionLabel('Tutup') - ->modalWidth(Width::TwoExtraLarge) - ->modalContent(fn (array $arguments) => view('filament.resources.learning.class-sessions.attendance-modal', [ - 'attendances' => ClassSession::find($arguments['session'] ?? null, ['*'])?->attendances()->with('student')->latest('attended_at')->get() ?? collect(), - ])); - } - - public function viewMaterialsAction(): Action - { - return Action::make('viewMaterials') - ->label('Materi') - ->color('purple') - ->icon('heroicon-o-book-open') - ->link() - ->modalHeading('Materi Sesi') - ->modalSubmitAction(false) - ->modalCancelActionLabel('Tutup') - ->modalWidth(Width::TwoExtraLarge) - ->modalContent(fn (array $arguments) => view('filament.resources.learning.class-sessions.materials-modal', [ - 'materials' => ClassSession::find($arguments['session'] ?? null, ['*'])?->materials() - ->latest() - ->get() - ->map(fn ($m) => (object) [ - 'id' => $m->id, - 'title' => $m->title, - 'created_at_formatted' => $m->created_at?->translatedFormat('d F Y') ?? '-', - ]) ?? collect(), - ])); - } - - public function viewAssignmentsAction(): Action - { - return Action::make('viewAssignments') - ->label('Tugas') - ->color('sky') - ->icon('heroicon-o-clipboard-document-list') - ->link() - ->modalHeading('Tugas Sesi') - ->modalSubmitAction(false) - ->modalCancelActionLabel('Tutup') - ->modalWidth(Width::TwoExtraLarge) - ->modalContent(fn (array $arguments) => view('filament.resources.learning.class-sessions.assignments-modal', [ - 'assignments' => ClassSession::find($arguments['session'] ?? null, ['*'])?->assignments() - ->with(['assignmentSubmissions.student']) - ->latest() - ->get() - ->map(fn ($a) => (object) [ - 'id' => $a->id, - 'title' => $a->title, - 'type_label' => $a->type?->getLabel() ?? 'Tugas', - 'due_date_formatted' => $a->due_date?->translatedFormat('d F Y H:i') ?? '-', - 'submissions' => $a->assignmentSubmissions->map(fn ($s) => (object) [ - 'student_name' => $s->student->full_name, - 'student_number' => $s->student->student_number, - 'submitted_at_formatted' => $s->submitted_at?->translatedFormat('d F Y H:i') ?? '-', - 'id' => $s->id, - ]), - ]) ?? collect(), - ])); - } - - public function editSessionAction(): Action + public function editSessionAction(): EditSessionAction { return EditSessionAction::make(); } - public function deleteSessionAction(): Action + public function deleteSessionAction(): DeleteSessionAction { return DeleteSessionAction::make(); } - public function viewMaterialDetailAction(): Action + #[Computed] + public function emptyStateHeading(): string { - return Action::make('viewMaterialDetail') - ->record(fn (array $arguments) => Material::find($arguments['record'] ?? null, ['*'])) - ->modalHeading('Detail Materi') - ->modalSubmitAction(false) - ->modalCancelActionLabel('Tutup') - ->modalWidth(Width::FourExtraLarge) - ->modalContent(fn (?Material $record) => $record ? view('filament.resources.learning.class-sessions.material-detail-modal', [ - 'record' => $record, - ]) : null); + return SystemNotification::getByKey('labels.empty_course_sessions.title'); + } + + #[Computed] + public function emptyStateDescription(): string + { + return SystemNotification::getByKey('labels.empty_course_sessions.description'); + } + + #[Computed] + public function emptyStateIcon(): string + { + return SystemNotification::getByKey('icons.empty_course_sessions'); } } diff --git a/app/Filament/Resources/Learning/ClassSessions/Pages/ManageClassSessions.php b/app/Filament/Resources/Learning/ClassSessions/Pages/ManageClassSessions.php index 1d6087a..bcef960 100644 --- a/app/Filament/Resources/Learning/ClassSessions/Pages/ManageClassSessions.php +++ b/app/Filament/Resources/Learning/ClassSessions/Pages/ManageClassSessions.php @@ -3,6 +3,7 @@ namespace App\Filament\Resources\Learning\ClassSessions\Pages; use App\Filament\Resources\Learning\ClassSessions\ClassSessionResource; +use App\Filament\Support\SystemNotification; use App\Models\ClassSession; use App\Models\Course; use App\Models\Student; @@ -84,7 +85,7 @@ public function todaySessions(): Collection 'attendance_percentage' => $percentage, 'materials_count' => $session->materials_count, 'assignments_count' => $session->assignments_count, - 'url' => ClassSessionResource::getUrl('course', ['courseId' => $session->course_id]), + 'url' => ClassSessionResource::getUrl('course', ['course' => $session->course]), // 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', @@ -125,8 +126,80 @@ public function courses(): Collection 'lecturer' => $course->lecturer ?? 'Dosen Belum Ditentukan', 'sessions_count' => $course->classSessions->count(), 'total_students' => $totalActiveStudents, - 'url' => ClassSessionResource::getUrl('course', ['courseId' => $course->id]), + 'url' => ClassSessionResource::getUrl('course', ['course' => $course]), ]; }); } + + #[Computed] + public function sessionsHeading(): string + { + return SystemNotification::getByKey('labels.today_sessions.title'); + } + + #[Computed] + public function sessionsDescription(): string + { + return SystemNotification::getByKey('labels.today_sessions.description', ['date' => $this->todayDate]); + } + + #[Computed] + public function coursesHeading(): string + { + return SystemNotification::getByKey('labels.semester_courses.title'); + } + + #[Computed] + public function coursesDescription(): string + { + return SystemNotification::getByKey('labels.semester_courses.description'); + } + + #[Computed] + public function sessionsIcon(): string + { + return SystemNotification::getByKey('icons.today_sessions'); + } + + #[Computed] + public function coursesIcon(): string + { + return SystemNotification::getByKey('icons.semester_courses'); + } + + #[Computed] + public function sessionsEmptyHeading(): string + { + return SystemNotification::getByKey('labels.empty_today_sessions.title'); + } + + #[Computed] + public function sessionsEmptyDescription(): string + { + return SystemNotification::getByKey('labels.empty_today_sessions.description'); + } + + #[Computed] + public function sessionsEmptyIcon(): string + { + return SystemNotification::getByKey('icons.empty_today_sessions'); + } + + #[Computed] + public function coursesEmptyHeading(): string + { + return SystemNotification::getByKey('labels.empty_semester_courses.title'); + } + + #[Computed] + public function coursesEmptyDescription(): string + { + return SystemNotification::getByKey('labels.empty_semester_courses.description'); + } + + #[Computed] + public function coursesEmptyIcon(): string + { + return SystemNotification::getByKey('icons.empty_semester_courses'); + } } diff --git a/app/Filament/Resources/Learning/ClassSessions/Schemas/SessionForm.php b/app/Filament/Resources/Learning/ClassSessions/Schemas/SessionForm.php new file mode 100644 index 0000000..1d97916 --- /dev/null +++ b/app/Filament/Resources/Learning/ClassSessions/Schemas/SessionForm.php @@ -0,0 +1,54 @@ +schema([ + Grid::make(['default' => 2]) + ->schema([ + TextInput::make('session_number') + ->label('Pertemuan Ke-') + ->placeholder('1') + ->numeric() + ->required() + ->minValue(1) + ->maxValue(16) + ->autocomplete(false), + + DatePicker::make('date') + ->label('Tanggal') + ->placeholder('Pilih Tanggal') + ->required() + ->native(false) + ->displayFormat('l, d F Y') + ->default(now()->toDateString()), + + TimePicker::make('start_time') + ->label('Waktu Mulai') + ->placeholder('08:00') + ->native(false) + ->displayFormat('H:i') + ->seconds(false) + ->required(), + + TimePicker::make('end_time') + ->label('Waktu Selesai') + ->placeholder('10:00') + ->native(false) + ->displayFormat('H:i') + ->seconds(false) + ->required(), + ]), + ]); + } +} diff --git a/lang/id/notif.php b/lang/id/notif.php index 6e874bb..d9a8de3 100644 --- a/lang/id/notif.php +++ b/lang/id/notif.php @@ -201,6 +201,26 @@ 'title' => 'Belum Ada Catatan Rilis 📭', 'description' => 'Belum ada update yang tercatat nih. Buat mulai catat riwayat pembaruan pertama! 🚀', ], + 'today_sessions' => [ + 'title' => 'Sesi Kelas Hari Ini ✨🚀', + 'description' => 'Daftar sesi seru yang udah dijadwalkan buat hari ini (:date). Semangat belajar! 💪', + ], + 'semester_courses' => [ + 'title' => 'Daftar Mata Kuliah Semester Ini 📚🎓', + 'description' => 'Pilih aja mata kuliahnya buat ngecek materi sama tugas-tugas seru lainnya! 🧐✨', + ], + 'empty_course_sessions' => [ + 'title' => 'Duh, Belum Ada Sesi Kelas! 🏢🛌', + 'description' => 'Belum ada sesi perkuliahan yang dibuat buat mata kuliah ini. Waktunya istirahat mungkin? 😴✨', + ], + 'empty_today_sessions' => [ + 'title' => 'Yah, Gak Ada Kelas Hari Ini! 😴🏖️', + 'description' => 'Hari ini kosong melompong. Kamu bisa rebahan sebentar atau kejar materi lain! ✨', + ], + 'empty_semester_courses' => [ + 'title' => 'Belum Ada Mata Kuliah Nih! 📚😴', + 'description' => 'Semester ini kayanya masih kosong. Coba hubungi admin kalau ada yang salah ya! ✨', + ], ], // Icons @@ -212,6 +232,11 @@ 'user_account' => 'heroicon-o-face-smile', 'account_security' => 'heroicon-o-shield-check', 'appearance_settings' => 'heroicon-o-swatch', + 'today_sessions' => 'heroicon-o-sparkles', + 'semester_courses' => 'heroicon-o-book-open', + 'empty_course_sessions' => 'heroicon-o-presentation-chart-bar', + 'empty_today_sessions' => 'heroicon-o-calendar-days', + 'empty_semester_courses' => 'heroicon-o-academic-cap', ], ], 'formal' => [ @@ -414,6 +439,26 @@ 'title' => 'Tidak Ada Riwayat Pembaruan', 'description' => 'Belum ada catatan rilis yang tersedia. Buat untuk mendokumentasikan pembaruan aplikasi.', ], + 'today_sessions' => [ + 'title' => 'Sesi Hari Ini', + 'description' => 'Sesi yang dijadwalkan pada :date', + ], + 'semester_courses' => [ + 'title' => 'Daftar Mata Kuliah Semester Ini', + 'description' => 'Pilih mata kuliah untuk melihat dan mengelola semua riwayat sesi.', + ], + 'empty_course_sessions' => [ + 'title' => 'Tidak ada data yang ditemukan', + 'description' => 'Belum ada sesi perkuliahan yang dibuat untuk mata kuliah ini.', + ], + 'empty_today_sessions' => [ + 'title' => 'Tidak ada data yang ditemukan', + 'description' => 'Tidak ada sesi perkuliahan yang dijadwalkan untuk hari ini.', + ], + 'empty_semester_courses' => [ + 'title' => 'Mata Kuliah Belum Terdaftar', + 'description' => 'Belum ada mata kuliah yang terdaftar untuk semester aktif ini.', + ], ], // Icons @@ -425,6 +470,11 @@ 'user_account' => 'heroicon-o-user', 'account_security' => 'heroicon-o-lock-closed', 'appearance_settings' => 'heroicon-o-paint-brush', + 'today_sessions' => 'heroicon-o-bolt', + 'semester_courses' => 'heroicon-o-academic-cap', + 'empty_course_sessions' => 'heroicon-o-presentation-chart-bar', + 'empty_today_sessions' => 'heroicon-o-calendar-days', + 'empty_semester_courses' => 'heroicon-o-academic-cap', ], ], ]; diff --git a/resources/views/filament/resources/learning/class-sessions/assignment-modal.blade.php b/resources/views/filament/resources/learning/class-sessions/assignment-modal.blade.php new file mode 100644 index 0000000..01c0d10 --- /dev/null +++ b/resources/views/filament/resources/learning/class-sessions/assignment-modal.blade.php @@ -0,0 +1,86 @@ +
+ +
+
+ +
+
+ + {{ $assignment->type_label }} + + {{ $assignment->title }} +
+ + + Deadline: {{ $assignment->due_date_formatted }} + +
+
+
+ +
+ +
+ + + + + + + + + + @foreach ($assignment->submissions as $submission) + + + + + + @endforeach + +
MahasiswaStatusWaktu Unggah
+
+ {{ $submission->student_name }} + + NIM: {{ $submission->student_number }} + +
+
+ @if ($submission->is_submitted) + + Sudah Unggah + + @else + + Belum Unggah + + @endif + + @if ($submission->is_submitted) + + {{ $submission->submitted_at_formatted }} + + @else + + - + + @endif +
+
+
+

+ Total Mahasiswa: {{ $assignment->total_students }} + | + Sudah Mengumpulkan: {{ $assignment->submission_count }} +

+
+
diff --git a/resources/views/filament/resources/learning/class-sessions/assignments-modal.blade.php b/resources/views/filament/resources/learning/class-sessions/assignments-modal.blade.php deleted file mode 100644 index f5d7a8e..0000000 --- a/resources/views/filament/resources/learning/class-sessions/assignments-modal.blade.php +++ /dev/null @@ -1,77 +0,0 @@ -
- @forelse($assignments as $assignment) -
-
-
-
- -
-
- - {{ $assignment->type_label }} - - {{ $assignment->title }} -
- - - Deadline: {{ $assignment->due_date_formatted }} - -
-
-
-
- - @if ($assignment->submissions->count() > 0) -
- - - - - - - - - @foreach ($assignment->submissions as $submission) - - - - - @endforeach - -
MahasiswaWaktu Unggah
-
- {{ $submission->student_name }} - - NIM: {{ $submission->student_number }} - -
-
- - {{ $submission->submitted_at_formatted }} - -
-
-
-

- Total Pengumpulan: {{ $assignment->submissions->count() }} - Mahasiswa -

-
- @else -
- -

Belum ada pengumpulan untuk tugas ini.

-
- @endif -
- @empty - - - @endforelse -
- diff --git a/resources/views/filament/resources/learning/class-sessions/attendance-modal.blade.php b/resources/views/filament/resources/learning/class-sessions/attendance-modal.blade.php index eba0b44..5c520a3 100644 --- a/resources/views/filament/resources/learning/class-sessions/attendance-modal.blade.php +++ b/resources/views/filament/resources/learning/class-sessions/attendance-modal.blade.php @@ -1,12 +1,13 @@
- @forelse ($attendances as $attendance) + @foreach ($students as $item) @if ($loop->first)
+ class="fi-ta-content overflow-x-auto overflow-y-auto max-h-150 rounded-xl border border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-900"> + @@ -15,16 +16,34 @@ class="fi-ta-content overflow-x-auto overflow-y-auto max-h-96 rounded-xl border - + @if ($loop->last) @@ -33,14 +52,13 @@ class="inline-flex items-center rounded-md bg-success-50 dark:bg-success-400/10

- Total Kehadiran: {{ $loop->count }} - Mahasiswa + Total Mahasiswa: {{ $loop->count }} + | + Sudah Hadir: {{ $attendedCount }}

@endif -@empty - - -@endforelse +@endforeach diff --git a/resources/views/filament/resources/learning/class-sessions/index.blade.php b/resources/views/filament/resources/learning/class-sessions/index.blade.php index 6642023..ca40441 100644 --- a/resources/views/filament/resources/learning/class-sessions/index.blade.php +++ b/resources/views/filament/resources/learning/class-sessions/index.blade.php @@ -6,11 +6,11 @@ - {{ \App\Filament\Support\SystemNotification::getMessage('Sesi Kelas Hari Ini ✨🚀', 'Sesi Hari Ini') }} - {{ \App\Filament\Support\SystemNotification::getMessage('Daftar sesi seru yang udah dijadwalkan buat hari ini (' . $this->today_date . '). Semangat belajar! 💪', 'Sesi yang dijadwalkan pada ' . $this->today_date) }} + {{ $this->sessionsHeading }} + {{ $this->sessionsDescription }}
@forelse ($this->todaySessions as $session) @@ -92,9 +92,9 @@
@empty - @endforelse @@ -102,11 +102,11 @@
- {{ \App\Filament\Support\SystemNotification::getMessage('Daftar Mata Kuliah Semester Ini 📚🎓', 'Daftar Mata Kuliah Semester Ini') }} - {{ \App\Filament\Support\SystemNotification::getMessage('Pilih aja mata kuliahnya buat ngecek materi sama tugas-tugas seru lainnya! 🧐✨', 'Pilih mata kuliah untuk melihat dan mengelola semua riwayat sesi.') }} + {{ $this->coursesHeading }} + {{ $this->coursesDescription }}
@forelse ($this->courses as $course) @@ -137,9 +137,9 @@ class="fi-card rounded-xl border border-gray-200 dark:border-gray-700 bg-white d @empty
-
diff --git a/resources/views/filament/resources/learning/class-sessions/material-detail-modal.blade.php b/resources/views/filament/resources/learning/class-sessions/material-modal.blade.php similarity index 51% rename from resources/views/filament/resources/learning/class-sessions/material-detail-modal.blade.php rename to resources/views/filament/resources/learning/class-sessions/material-modal.blade.php index 42d6c52..663f9fc 100644 --- a/resources/views/filament/resources/learning/class-sessions/material-detail-modal.blade.php +++ b/resources/views/filament/resources/learning/class-sessions/material-modal.blade.php @@ -1,9 +1,6 @@ -
- @foreach($record->getMedia('materials') as $item) +
+ @foreach ($record->getMedia('materials') as $item)
-
- Lampiran PDF -
diff --git a/resources/views/filament/resources/learning/class-sessions/materials-modal.blade.php b/resources/views/filament/resources/learning/class-sessions/materials-modal.blade.php deleted file mode 100644 index ae5860c..0000000 --- a/resources/views/filament/resources/learning/class-sessions/materials-modal.blade.php +++ /dev/null @@ -1,29 +0,0 @@ -
- @forelse($materials as $material) -
-
-
- -
-
- {{ $material->title }} - - Dibuat: {{ $material->created_at_formatted }} - -
-
-
- -
-
- @empty - - - @endforelse -
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 986b2cc..4e7379b 100644 --- a/resources/views/filament/resources/learning/class-sessions/show.blade.php +++ b/resources/views/filament/resources/learning/class-sessions/show.blade.php @@ -2,67 +2,72 @@
@forelse ($this->sessions as $session) -
+
-
+
Sesi - Ke-{{ $session->session_number }} + Ke-{{ $session->session_number }}
-
-
-
- - {{ $session->date_formatted }} +
+
+
+
+ + {{ $session->date_formatted }} +
+
+ + {{ $session->time_range }} +
-
- - {{ $session->time_range }} -
-
-
-
-
+
+
- {{ $session->attendances_count }}/{{ $session->total_students }} Presensi + {{ $session->attendances_count }}/{{ $session->total_students }}
-
-
-
-
- {{ $session->attendance_percentage }}% +
+
-
- -
- - {{ $session->materials_count }} Materi -
- -
- - {{ $session->assignments_count }} Tugas + {{ $session->attendance_percentage }}%
+ + @if ($session->materials_count || $session->assignments_count) +
+ @if ($session->materials_count) + {{ ($this->viewMaterialsAction)(['session' => $session->id]) }} + @endif + @if ($session->assignments_count) + {{ ($this->viewAssignmentsAction)(['session' => $session->id]) }} + @endif +
+ @endif
- -
+
+ {{ ($this->viewAttendanceAction)(['session' => $session->id]) }} {{ ($this->shareAttendanceAction)(['session' => $session->id]) }} {{ ($this->editSessionAction)(['session' => $session->id]) }} {{ ($this->deleteSessionAction)(['session' => $session->id]) }}
@empty - @endforelse diff --git a/tests/Feature/ClassSessionTest.php b/tests/Feature/ClassSessionTest.php index 1cbdba7..e33d9c7 100644 --- a/tests/Feature/ClassSessionTest.php +++ b/tests/Feature/ClassSessionTest.php @@ -3,6 +3,7 @@ use App\Enums\RoleEnum; use App\Filament\Resources\Learning\ClassSessions\Pages\ListCourseSessions; use App\Filament\Resources\Learning\ClassSessions\Pages\ManageClassSessions; +use App\Filament\Support\SystemNotification; use App\Models\Assignment; use App\Models\AssignmentSubmission; use App\Models\ClassSession; @@ -65,15 +66,15 @@ // Check actions on ListCourseSessions page $this->actingAs($developer); - Livewire::test(ListCourseSessions::class, ['courseId' => $course->id]) + Livewire::test(ListCourseSessions::class, ['course' => $course]) ->assertActionVisible('generateSessions'); $this->actingAs($kosma); - Livewire::test(ListCourseSessions::class, ['courseId' => $course->id]) + Livewire::test(ListCourseSessions::class, ['course' => $course]) ->assertActionVisible('generateSessions'); $this->actingAs($regularStudent); - Livewire::test(ListCourseSessions::class, ['courseId' => $course->id]) + Livewire::test(ListCourseSessions::class, ['course' => $course]) ->assertActionHidden('generateSessions'); }); }); @@ -116,7 +117,7 @@ 'end_time' => '10:00', ]); - Livewire::test(ListCourseSessions::class, ['courseId' => $course->id]) + Livewire::test(ListCourseSessions::class, ['course' => $course]) ->callAction('generateSessions', [ 'total_sessions' => 2, 'start_date' => now()->toDateString(), @@ -137,7 +138,7 @@ $course = Course::factory()->create(['semester' => $this->currentSemester]); $session = ClassSession::factory()->create(['course_id' => $course->id, 'session_number' => 1]); - Livewire::test(ListCourseSessions::class, ['courseId' => $course->id]) + Livewire::test(ListCourseSessions::class, ['course' => $course]) ->callAction('editSession', [ 'session_number' => 5, 'date' => now()->toDateString(), @@ -153,7 +154,7 @@ $course = Course::factory()->create(['semester' => $this->currentSemester]); $session = ClassSession::factory()->create(['course_id' => $course->id]); - Livewire::test(ListCourseSessions::class, ['courseId' => $course->id]) + Livewire::test(ListCourseSessions::class, ['course' => $course]) ->callAction('shareAttendance', [], ['session' => $session->id]) ->assertHasNoActionErrors(); }); @@ -162,7 +163,7 @@ $course = Course::factory()->create(['semester' => $this->currentSemester]); $session = ClassSession::factory()->create(['course_id' => $course->id]); - Livewire::test(ListCourseSessions::class, ['courseId' => $course->id]) + Livewire::test(ListCourseSessions::class, ['course' => $course]) ->assertActionVisible('viewAttendance') ->assertActionVisible('viewMaterials') ->assertActionVisible('viewAssignments'); @@ -184,11 +185,46 @@ 'submitted_at' => now(), ]); - Livewire::test(ListCourseSessions::class, ['courseId' => $course->id]) + Livewire::test(ListCourseSessions::class, ['course' => $course]) ->mountAction('viewAssignments', ['session' => $session->id]) ->assertActionMounted('viewAssignments') ->assertSuccessful(); }); + + it('can delete a session', function () { + $course = Course::factory()->create(['semester' => $this->currentSemester]); + $session = ClassSession::factory()->create(['course_id' => $course->id]); + + Livewire::test(ListCourseSessions::class, ['course' => $course]) + ->callAction('deleteSession', [], ['session' => $session->id]) + ->assertHasNoActionErrors(); + + expect($session->refresh()->trashed())->toBeTrue(); + }); + + it('shows attendance in the attendance modal', function () { + $course = Course::factory()->create(['semester' => $this->currentSemester]); + $session = ClassSession::factory()->create(['course_id' => $course->id]); + + Livewire::test(ListCourseSessions::class, ['course' => $course]) + ->mountAction('viewAttendance', ['session' => $session->id]) + ->assertActionMounted('viewAttendance') + ->assertSuccessful(); + }); + + it('displays dynamic empty states based on system notification style', function () { + $course = Course::factory()->create(['semester' => $this->currentSemester]); + + // Test ManageClassSessions empty states + Livewire::test(ManageClassSessions::class) + ->assertSee(SystemNotification::getByKey('labels.empty_today_sessions.title')) + ->assertSee(SystemNotification::getByKey('labels.empty_today_sessions.description')); + + // Test ListCourseSessions empty states + Livewire::test(ListCourseSessions::class, ['course' => $course]) + ->assertSee(SystemNotification::getByKey('labels.empty_course_sessions.title')) + ->assertSee(SystemNotification::getByKey('labels.empty_course_sessions.description')); + }); }); describe('Search and Dashboard Filter', function () {
MahasiswaStatus Waktu Presensi
- {{ $attendance->student->full_name }} + {{ $item->student->full_name }} NIM: - {{ $attendance->student->student_number }} + {{ $item->student->student_number }}
- - {{ $attendance->attended_at?->format('H:i') }} - + + @if ($item->has_attended) + + Hadir + + @else + + Belum Hadir + + @endif + + @if ($item->has_attended) + + {{ $item->attended_at }} + + @else + + - + + @endif