From 9892f60e5eb62d9ec152083d6a18052b610d9bff Mon Sep 17 00:00:00 2001 From: Yoga Pangestu Date: Wed, 2 Sep 2026 13:59:02 +0700 Subject: [PATCH] feat: refactor course registration logic to use department-specific courses and improve submission payload structure --- .../Manage/CourseRegistrationController.php | 28 +++-- .../Manage/CourseRegistrationService.php | 38 +++++-- .../manage/course-registrations/show.tsx | 102 ++++++++++++------ resources/js/types/course-registration.ts | 14 +-- 4 files changed, 126 insertions(+), 56 deletions(-) diff --git a/app/Http/Controllers/Admin/Manage/CourseRegistrationController.php b/app/Http/Controllers/Admin/Manage/CourseRegistrationController.php index 801d86b..a912333 100644 --- a/app/Http/Controllers/Admin/Manage/CourseRegistrationController.php +++ b/app/Http/Controllers/Admin/Manage/CourseRegistrationController.php @@ -7,7 +7,7 @@ use App\Http\Requests\Admin\Manage\SaveCourseRegistrationRequest; use App\Http\Requests\Admin\Manage\SignCourseRegistrationRequest; use App\Http\Requests\PaginatedRequest; -use App\Models\CourseClass; +use App\Models\Course; use App\Models\CourseRegistrationSubmission; use App\Models\Student; use App\Services\Admin\Manage\CourseRegistrationService; @@ -69,25 +69,31 @@ public function show(Student $student): Response ? $this->service->availableCourseClasses($student->department_id, $activeTerm->id) : collect(); - $semesters = $allCourseClasses - ->map(fn (CourseClass $courseClass) => $courseClass->course?->semester_number) + $departmentCourses = $this->service->departmentCourses($student->department_id); + + $semesters = $departmentCourses + ->map(fn (Course $course) => $course->semester_number) ->unique() ->sort() ->values(); - $submissions = $semesters->mapWithKeys(fn (?int $semesterNumber) => [ - CourseRegistrationSubmission::semesterKey($semesterNumber) => $this->service->buildSubmissionPayload( - $student, - $activeTerm, - $semesterNumber, - $allCourseClasses, - ), - ]); + $submissions = $activeTerm + ? $semesters->mapWithKeys(fn (?int $semesterNumber) => [ + CourseRegistrationSubmission::semesterKey($semesterNumber) => $this->service->buildSubmissionPayload( + $student, + $activeTerm, + $semesterNumber, + $departmentCourses, + $allCourseClasses, + ), + ]) + : collect(); return Inertia::render('admin/manage/course-registrations/show', [ 'submissions' => $submissions, 'semesters' => $semesters->values(), 'openSemesters' => $activeTerm?->open_semesters ?? [], + 'hasActiveTerm' => $activeTerm !== null, 'backHref' => $backHref, 'canReview' => $canReview, 'canSignAsKaprodi' => $canSignAsKaprodi, diff --git a/app/Services/Admin/Manage/CourseRegistrationService.php b/app/Services/Admin/Manage/CourseRegistrationService.php index fbb8081..c1a242a 100644 --- a/app/Services/Admin/Manage/CourseRegistrationService.php +++ b/app/Services/Admin/Manage/CourseRegistrationService.php @@ -6,6 +6,7 @@ use App\Enums\StudentStatus; use App\Models\AcademicTerm; use App\Models\ClassEnrollment; +use App\Models\Course; use App\Models\CourseClass; use App\Models\CourseRegistration; use App\Models\CourseRegistrationSubmission; @@ -85,7 +86,7 @@ public function findOrCreateForSemester(Student $student, string $semester): Cou /** * @return array */ - public function buildSubmissionPayload(Student $student, ?AcademicTerm $activeTerm, ?int $semesterNumber, Collection $allCourseClasses): array + public function buildSubmissionPayload(Student $student, AcademicTerm $activeTerm, ?int $semesterNumber, Collection $departmentCourses, Collection $allCourseClasses): array { $submission = $this->findOrCreateForStudent($student, $activeTerm, $semesterNumber); @@ -93,20 +94,41 @@ public function buildSubmissionPayload(Student $student, ?AcademicTerm $activeTe $payload = $data->toArray(); $registeredCourseClassIds = $data->courseRegistrations->pluck('course_class_id')->all(); + $courseClassesByCourseId = $allCourseClasses->keyBy('course_id'); $payload['has_registrations'] = ! empty($registeredCourseClassIds); - $payload['course_registrations'] = $allCourseClasses - ->filter(fn (CourseClass $courseClass) => $courseClass->course?->semester_number === $semesterNumber) - ->map(fn (CourseClass $courseClass) => [ - 'id' => $courseClass->id, - 'course_class' => ['id' => $courseClass->id, 'course' => $courseClass->course], - 'is_registered' => in_array($courseClass->id, $registeredCourseClassIds), - ]) + $payload['course_registrations'] = $departmentCourses + ->filter(fn (Course $course) => $course->semester_number === $semesterNumber) + ->map(function (Course $course) use ($courseClassesByCourseId, $registeredCourseClassIds) { + $courseClass = $courseClassesByCourseId->get($course->id); + + return [ + 'id' => $courseClass?->id ?? -$course->id, + 'course' => $course, + 'course_class' => $courseClass ? ['id' => $courseClass->id] : null, + 'is_registered' => $courseClass !== null && in_array($courseClass->id, $registeredCourseClassIds, true), + ]; + }) ->values(); return $payload; } + /** + * The department's full course curriculum, independent of which classes + * have been opened for the current academic term. + * + * @return Collection + */ + public function departmentCourses(int $departmentId): Collection + { + return Course::query() + ->where('department_id', $departmentId) + ->orderBy('semester_number') + ->orderBy('name') + ->get(['id', 'code', 'name', 'credits', 'semester_number']); + } + public function withDetails(CourseRegistrationSubmission $submission): CourseRegistrationSubmission { return $submission->load([ diff --git a/resources/js/pages/admin/manage/course-registrations/show.tsx b/resources/js/pages/admin/manage/course-registrations/show.tsx index 0f94274..8e037db 100644 --- a/resources/js/pages/admin/manage/course-registrations/show.tsx +++ b/resources/js/pages/admin/manage/course-registrations/show.tsx @@ -1,6 +1,6 @@ import { Form, Head, Link, router } from '@inertiajs/react'; import { format } from 'date-fns'; -import { ArrowLeft, Check, Send, X } from 'lucide-react'; +import { ArrowLeft, Check, Inbox, Send, X } from 'lucide-react'; import { useState } from 'react'; import { FormDialog } from '@/components/form-dialog'; import InputError from '@/components/input-error'; @@ -265,10 +265,10 @@ function SemesterCard({ (sum, registration) => submission.has_registrations ? registration.is_registered - ? sum + (registration.course_class?.course?.credits ?? 0) + ? sum + registration.course.credits : sum - : isSemesterOpen - ? sum + (registration.course_class?.course?.credits ?? 0) + : isSemesterOpen && registration.course_class + ? sum + registration.course.credits : sum, 0, ); @@ -373,9 +373,7 @@ function SemesterCard({ index, ) => { const course = - registration - .course_class - ?.course; + registration.course; const checked = submission.has_registrations ? registration.is_registered @@ -391,34 +389,42 @@ function SemesterCard({ {index + 1} - {course?.name ?? - 'N/A'} + {course.name} - {course?.code ?? - '-'} + {course.code} - {course?.credits ?? - '-'} + { + course.credits + } - + {registration.course_class ? ( + + ) : ( + + — + + )} ); @@ -602,6 +608,7 @@ type Props = { submissions: Record; semesters: (number | null)[]; openSemesters: number[]; + hasActiveTerm: boolean; backHref: string | null; canReview: boolean; canSignAsKaprodi: boolean; @@ -612,6 +619,7 @@ export default function CourseRegistrationDetail({ submissions, semesters, openSemesters, + hasActiveTerm, backHref, canReview, canSignAsKaprodi, @@ -670,7 +678,7 @@ export default function CourseRegistrationDetail({ )} - {currentSubmission && ( + {currentSubmission ? ( + ) : ( + semesters.length > 0 && + !hasActiveTerm && ( + + + +

+ Belum ada periode akademik aktif +

+

+ Registrasi KRS hanya dapat dilakukan saat + ada periode akademik yang aktif. Silakan + hubungi admin untuk informasi lebih + lanjut. +

+
+
+ ) + )} + + {semesters.length === 0 && ( + + + +

+ Belum ada mata kuliah terdaftar +

+

+ Belum ada mata kuliah yang terdaftar untuk + jurusan ini. Silakan hubungi admin atau + program studi. +

+
+
)} diff --git a/resources/js/types/course-registration.ts b/resources/js/types/course-registration.ts index da7ba2e..306ae8d 100644 --- a/resources/js/types/course-registration.ts +++ b/resources/js/types/course-registration.ts @@ -52,15 +52,15 @@ export type CourseRegistrationSubmissionStudent = { export type CourseRegistrationSubmissionEntry = { id: number; is_registered: boolean; + course: { + id: number; + code: string; + name: string; + credits: number; + semester_number: number | null; + }; course_class: { id: number; - course: { - id: number; - code: string; - name: string; - credits: number; - semester_number: number | null; - } | null; } | null; };