diff --git a/app/Filament/Pages/Information/CourseSchedule/Actions/CreateScheduleAction.php b/app/Filament/Pages/Information/CourseSchedule/Actions/CreateScheduleAction.php new file mode 100644 index 0000000..e899510 --- /dev/null +++ b/app/Filament/Pages/Information/CourseSchedule/Actions/CreateScheduleAction.php @@ -0,0 +1,32 @@ +model(CourseSchedule::class) + ->label('Tambah') + ->modalHeading('Tambah Jadwal Kuliah') + ->modalSubmitActionLabel('Simpan') + ->modalCancelActionLabel('Batal') + ->extraModalFooterActions(fn (CreateAction $action): array => [ + $action->makeModalSubmitAction('createAnother', arguments: ['another' => true]) + ->label('Simpan dan Tambah Lagi'), + ]) + ->modalWidth(Width::Large) + ->schema(fn ($livewire) => $livewire->scheduleFormSchema()); + } +} diff --git a/app/Filament/Pages/Information/CourseSchedule/Actions/DeleteScheduleAction.php b/app/Filament/Pages/Information/CourseSchedule/Actions/DeleteScheduleAction.php new file mode 100644 index 0000000..0487fcc --- /dev/null +++ b/app/Filament/Pages/Information/CourseSchedule/Actions/DeleteScheduleAction.php @@ -0,0 +1,27 @@ +record(function (array $arguments): CourseSchedule { + return CourseSchedule::findOrFail($arguments['schedule']); + }) + ->modalHeading('Hapus Jadwal Kuliah') + ->modalDescription('Apakah Anda yakin ingin menghapus jadwal ini? Tindakan ini tidak dapat dibatalkan.') + ->modalSubmitActionLabel('Hapus') + ->modalCancelActionLabel('Batal'); + } +} diff --git a/app/Filament/Pages/Information/CourseSchedule/Actions/EditScheduleAction.php b/app/Filament/Pages/Information/CourseSchedule/Actions/EditScheduleAction.php new file mode 100644 index 0000000..71a3920 --- /dev/null +++ b/app/Filament/Pages/Information/CourseSchedule/Actions/EditScheduleAction.php @@ -0,0 +1,29 @@ +record(function (array $arguments): CourseSchedule { + return CourseSchedule::findOrFail($arguments['schedule']); + }) + ->modalHeading('Ubah Jadwal Kuliah') + ->modalSubmitActionLabel('Simpan') + ->modalCancelActionLabel('Batal') + ->modalWidth(Width::Large) + ->schema(fn ($livewire) => $livewire->scheduleFormSchema()); + } +} diff --git a/app/Filament/Pages/Information/CourseSchedule/Index.php b/app/Filament/Pages/Information/CourseSchedule/Index.php new file mode 100644 index 0000000..2922ee0 --- /dev/null +++ b/app/Filament/Pages/Information/CourseSchedule/Index.php @@ -0,0 +1,217 @@ +course_semester = null; + + $this->form->fill([ + 'course_semester' => $this->course_semester, + ]); + } + + public function form(Schema $schema): Schema + { + return $schema + ->schema([ + Select::make('course_semester') + ->label('Semester') + ->options(array_combine(range(1, 8), array_map(fn ($i) => "Semester $i", range(1, 8)))) + ->native(false) + ->live() + ->afterStateUpdated(fn ($state) => $this->course_semester = $state), + + TextInput::make('search') + ->label('Cari') + ->placeholder('Cari Mata Kuliah atau Dosen...') + ->autocomplete(false) + ->live(debounce: 500) + ->afterStateUpdated(fn ($state) => $this->search = $state), + ]) + ->columns([ + 'sm' => 1, + 'md' => 3, + ]); + } + + protected function getHeaderActions(): array + { + return [ + CreateScheduleAction::make(), + ]; + } + + protected function editScheduleAction(): Action + { + return EditScheduleAction::make(); + } + + protected function deleteScheduleAction(): Action + { + return DeleteScheduleAction::make(); + } + + public function getSchedules(): Collection + { + $user = auth()->user(); + $lecturer = $user->lecturer; + + $query = CourseSchedule::with(['course.lecturer']); + + if ($lecturer) { + $query->whereHas('course', fn ($q) => $q->where('lecturer_id', $lecturer->id)); + } + + if ($this->course_semester) { + $query->whereHas('course', fn ($q) => $q->where('semester', $this->course_semester)); + } + + if ($this->search) { + $query->where(function ($q) { + $q->whereHas('course', fn ($sq) => $sq->where('name', 'like', "%{$this->search}%")) + ->orWhereHas('course.lecturer', fn ($sq) => $sq->where('full_name', 'like', "%{$this->search}%")); + }); + } + + return $query->get() + ->sortBy([ + ['day_of_week', 'asc'], + ['start_time', 'asc'], + ]) + ->groupBy('day_of_week'); + } + + protected function getViewData(): array + { + return [ + 'schedules' => $this->getSchedules(), + 'days' => [ + 1 => 'Senin', + 2 => 'Selasa', + 3 => 'Rabu', + 4 => 'Kamis', + 5 => 'Jumat', + 6 => 'Sabtu', + 7 => 'Minggu', + ], + ]; + } + + public function scheduleFormSchema(): array + { + return [ + Grid::make(2) + ->schema([ + Select::make('course_id') + ->label('Mata Kuliah') + ->options(function () { + $user = auth()->user(); + $lecturer = $user->lecturer; + + if ($lecturer) { + $courses = Course::getOptionsForLecturer($lecturer); + } else { + $courses = Course::all(); + } + + return $courses + ->groupBy('semester') + ->mapWithKeys(function ($courses, $semester) { + return [ + "Semester $semester" => $courses->pluck('name', 'id')->toArray(), + ]; + }) + ->toArray(); + }) + ->searchable() + ->live() + ->required() + ->columnSpanFull(), + + Grid::make(3) + ->schema([ + Select::make('day_of_week') + ->label('Hari') + ->placeholder('Pilih Hari') + ->options([ + 1 => 'Senin', + 2 => 'Selasa', + 3 => 'Rabu', + 4 => 'Kamis', + 5 => 'Jumat', + 6 => 'Sabtu', + 7 => 'Minggu', + ]) + ->native(false) + ->required(), + + TimePicker::make('start_time') + ->label('Jam Mulai') + ->placeholder('08:00') + ->required() + ->native(false) + ->seconds(false), + + TimePicker::make('end_time') + ->label('Jam Selesai') + ->placeholder('10:00') + ->required() + ->native(false) + ->seconds(false), + ]) + ->columnSpanFull(), + ]), + ]; + } +} diff --git a/app/Models/Course.php b/app/Models/Course.php index 5837e2a..a1dec5d 100644 --- a/app/Models/Course.php +++ b/app/Models/Course.php @@ -2,6 +2,7 @@ namespace App\Models; +use Illuminate\Database\Eloquent\Collection; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; @@ -17,4 +18,11 @@ public function lecturer(): BelongsTo { return $this->belongsTo(Lecturer::class); } + + public static function getOptionsForLecturer(Lecturer $lecturer): Collection + { + return static::query() + ->where('lecturer_id', $lecturer->id) + ->get(); + } } diff --git a/app/Models/CourseSchedule.php b/app/Models/CourseSchedule.php new file mode 100644 index 0000000..c934e28 --- /dev/null +++ b/app/Models/CourseSchedule.php @@ -0,0 +1,28 @@ + 'datetime:H:i', + 'start_time' => 'datetime:H:i', + ]; + } + + public function course(): BelongsTo + { + return $this->belongsTo(Course::class); + } +} diff --git a/database/migrations/2026_02_25_024504_create_course_schedules_table.php b/database/migrations/2026_02_25_024504_create_course_schedules_table.php new file mode 100644 index 0000000..6879c5c --- /dev/null +++ b/database/migrations/2026_02_25_024504_create_course_schedules_table.php @@ -0,0 +1,35 @@ +id(); + $table->foreignId('course_id')->constrained()->cascadeOnDelete(); + $table->integer('day_of_week'); + $table->time('start_time'); + $table->time('end_time'); + $table->timestamp('created_at')->useCurrent(); + $table->timestamp('updated_at')->useCurrent()->useCurrentOnUpdate(); + $table->softDeletes(); + + $table->index('course_id'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('course_schedules'); + } +}; diff --git a/database/seeders/CourseScheduleSeeder.php b/database/seeders/CourseScheduleSeeder.php new file mode 100644 index 0000000..7553193 --- /dev/null +++ b/database/seeders/CourseScheduleSeeder.php @@ -0,0 +1,34 @@ + $course->id, + 'day_of_week' => rand(1, 6), + 'start_time' => $slot[0], + 'end_time' => $slot[1], + ]); + } + } +} diff --git a/database/seeders/CourseSeeder.php b/database/seeders/CourseSeeder.php index 714d9d4..5cd2855 100644 --- a/database/seeders/CourseSeeder.php +++ b/database/seeders/CourseSeeder.php @@ -3,6 +3,7 @@ namespace Database\Seeders; use App\Models\Course; +use App\Models\Lecturer; use Illuminate\Database\Seeder; class CourseSeeder extends Seeder @@ -83,6 +84,33 @@ public function run(): void ['code' => 'FSI839', 'name' => 'Startup Digital', 'credit' => 2, 'semester' => 8], ]; + $lecturers = Lecturer::pluck('id')->toArray(); + + shuffle($lecturers); + + $lecturerSemesterMap = []; + + foreach ($courses as $i => &$course) { + + $semester = $course['semester']; + + if ($i < count($lecturers)) { + $lecturerId = $lecturers[$i]; + } else { + + do { + $lecturerId = $lecturers[array_rand($lecturers)]; + } while ( + isset($lecturerSemesterMap[$lecturerId]) && + in_array($semester, $lecturerSemesterMap[$lecturerId]) + ); + } + + $course['lecturer_id'] = $lecturerId; + + $lecturerSemesterMap[$lecturerId][] = $semester; + } + Course::insert($courses); } } diff --git a/database/seeders/DatabaseSeeder.php b/database/seeders/DatabaseSeeder.php index 2071881..e85a609 100644 --- a/database/seeders/DatabaseSeeder.php +++ b/database/seeders/DatabaseSeeder.php @@ -18,6 +18,7 @@ public function run(): void UserSeeder::class, LecturerSeeder::class, CourseSeeder::class, + CourseScheduleSeeder::class, ]); } } diff --git a/resources/views/filament/pages/information/list-course-schedules.blade.php b/resources/views/filament/pages/information/list-course-schedules.blade.php new file mode 100644 index 0000000..d7e396a --- /dev/null +++ b/resources/views/filament/pages/information/list-course-schedules.blade.php @@ -0,0 +1,140 @@ + + +
+
+ + +
+ Geser ke samping untuk melihat hari lainnya. +
+
+
+ +
+
+ {{ $this->form }} +
+
+ + @if ($schedules->isNotEmpty()) +
+
+ + @foreach ($days as $dayNumber => $dayName) + @php + $daySchedules = $schedules->get($dayNumber); + @endphp + + @if ($daySchedules && $daySchedules->count() > 0) +
+ +
+ +

+ {{ $dayName }} +

+
+ +
+ @foreach ($daySchedules as $schedule) +
+ +
+
+
+ + {{ $schedule->course->code ?? 'MATKUL' }} + + + @if ($schedule->mode) + @php + $color = $schedule->mode->getColor(); + @endphp + + {{ $schedule->mode->getLabel() }} + + @endif +
+ + @if ($schedule->room) + + + {{ $schedule->room }} + + @endif +
+ +

+ {{ $schedule->course->name ?? 'Mata Kuliah Tidak Diketahui' }} +

+
+ + + {{ $schedule->course->lecturer->full_name ?? 'Dosen Belum Ditentukan' }} + +
+ +
+ @if ($schedule->course?->semester) + + + Semester {{ $schedule->course->semester }} + + @endif +
+ +
+
+ + {{ $schedule->start_time->format('H:i') }} + + {{ $schedule->end_time->format('H:i') }} +
+ + + {{ $schedule->course->credit ?? '-' }} SKS + +
+ +
+ + + + + + + +
+
+
+ @endforeach +
+
+ @endif + @endforeach + +
+
+ @endif + + @if ($schedules->isEmpty()) + + + @endif +
+