diff --git a/app/Http/Controllers/Admin/Manage/CourseClassController.php b/app/Http/Controllers/Admin/Manage/CourseClassController.php index d84ea7c..f1f32d9 100644 --- a/app/Http/Controllers/Admin/Manage/CourseClassController.php +++ b/app/Http/Controllers/Admin/Manage/CourseClassController.php @@ -41,9 +41,14 @@ public function index(PaginatedRequest $request): Response public function store(CourseClassRequest $request): RedirectResponse { - $this->service->create($request->validated()); + $created = $this->service->createMany($request->validated()); - Inertia::flash('toast', ['type' => 'success', 'message' => 'Kelas mata kuliah berhasil ditambahkan.']); + Inertia::flash('toast', [ + 'type' => 'success', + 'message' => $created->count() > 1 + ? "{$created->count()} kelas mata kuliah berhasil ditambahkan." + : 'Kelas mata kuliah berhasil ditambahkan.', + ]); return to_route('admin.manage.course-classes.index'); } diff --git a/app/Http/Requests/Admin/Manage/CourseClassRequest.php b/app/Http/Requests/Admin/Manage/CourseClassRequest.php index 3c1c0e1..a67692d 100644 --- a/app/Http/Requests/Admin/Manage/CourseClassRequest.php +++ b/app/Http/Requests/Admin/Manage/CourseClassRequest.php @@ -4,6 +4,7 @@ use App\Enums\ClassMethod; use App\Models\Course; +use App\Models\Lecturer; use Illuminate\Foundation\Http\FormRequest; use Illuminate\Validation\Rule; @@ -16,10 +17,51 @@ public function authorize(): bool public function rules(): array { + $academicTermId = $this->input('academic_term_id'); + + if ($this->isMethod('post')) { + return [ + 'course_ids' => ['required', 'array', 'min:1'], + 'course_ids.*' => [ + 'integer', + Rule::exists('courses', 'id'), + Rule::unique('course_classes', 'course_id') + ->where('academic_term_id', $academicTermId) + ->whereNull('deleted_at'), + ], + 'lecturer_id' => [ + 'required', + 'integer', + function ($attribute, $value, $fail) { + $courseIds = (array) $this->input('course_ids', []); + $departmentIds = Course::whereIn('id', $courseIds)->pluck('department_id')->unique(); + $lecturer = Lecturer::find($value); + $coveredDepartmentIds = $lecturer + ? $lecturer->departments()->pluck('departments.id') + : collect(); + + if ($departmentIds->diff($coveredDepartmentIds)->isNotEmpty()) { + $fail('Dosen tidak terdaftar di jurusan seluruh mata kuliah yang dipilih.'); + } + }, + ], + 'academic_term_id' => ['required', 'integer', Rule::exists('academic_terms', 'id')], + 'method' => ['nullable', 'string', Rule::in(ClassMethod::values())], + ]; + } + $departmentId = Course::find($this->input('course_id'))?->department_id; return [ - 'course_id' => ['required', 'integer', Rule::exists('courses', 'id')], + 'course_id' => [ + 'required', + 'integer', + Rule::exists('courses', 'id'), + Rule::unique('course_classes', 'course_id') + ->where('academic_term_id', $academicTermId) + ->whereNull('deleted_at') + ->ignore($this->route('course_class')?->id), + ], 'lecturer_id' => [ 'required', 'integer', diff --git a/app/Services/Admin/Manage/CourseClassService.php b/app/Services/Admin/Manage/CourseClassService.php index aa7f00a..b6417e9 100644 --- a/app/Services/Admin/Manage/CourseClassService.php +++ b/app/Services/Admin/Manage/CourseClassService.php @@ -28,9 +28,18 @@ public function paginated(int $perPage = 25, string $search = '', ?int $academic ->paginate($perPage); } - public function create(array $data): CourseClass + /** + * @param array{course_ids: array, lecturer_id: int, academic_term_id: int, method?: string|null} $data + * @return Collection + */ + public function createMany(array $data): Collection { - return CourseClass::create($data); + return new Collection(array_map(fn (int $courseId) => CourseClass::create([ + 'course_id' => $courseId, + 'lecturer_id' => $data['lecturer_id'], + 'academic_term_id' => $data['academic_term_id'], + 'method' => $data['method'] ?? null, + ]), $data['course_ids'])); } public function update(CourseClass $courseClass, array $data): CourseClass diff --git a/resources/js/pages/admin/manage/course-classes/index.tsx b/resources/js/pages/admin/manage/course-classes/index.tsx index 01e98fe..b972bd0 100644 --- a/resources/js/pages/admin/manage/course-classes/index.tsx +++ b/resources/js/pages/admin/manage/course-classes/index.tsx @@ -12,6 +12,9 @@ import { PageHeader } from '@/components/page-header'; import { Button } from '@/components/ui/button'; import { Combobox, + ComboboxChip, + ComboboxChips, + ComboboxChipsInput, ComboboxCollection, ComboboxContent, ComboboxEmpty, @@ -20,6 +23,7 @@ import { ComboboxItem, ComboboxLabel, ComboboxList, + useComboboxAnchor, } from '@/components/ui/combobox'; import { Label } from '@/components/ui/label'; import { @@ -299,27 +303,37 @@ function CreateForm({ courseClassAssignments: CourseClassAssignment[]; }) { const [academicTermId, setAcademicTermId] = useState(''); - const [course, setCourse] = useState(null); + const [selectedCourses, setSelectedCourses] = useState([]); const [lecturer, setLecturer] = useState(null); + const courseAnchor = useComboboxAnchor(); const takenCourseIds = new Set( courseClassAssignments .filter((a) => String(a.academic_term_id) === academicTermId) .map((a) => a.course_id), ); - const availableCourses = courses.filter((c) => !takenCourseIds.has(c.id)); + const selectedCourseIds = new Set(selectedCourses.map((c) => c.id)); + const availableCourses = courses.filter( + (c) => !takenCourseIds.has(c.id) && !selectedCourseIds.has(c.id), + ); const courseGroups = groupCoursesByDepartmentAndSemester(availableCourses); - const availableLecturers = course - ? lecturers.filter((l) => - l.departments.some( - (department) => department.id === course.department_id, - ), - ) - : lecturers; + const selectedDepartmentIds = new Set( + selectedCourses.map((c) => c.department_id), + ); + const availableLecturers = + selectedCourses.length > 0 + ? lecturers.filter((l) => + [...selectedDepartmentIds].every((departmentId) => + l.departments.some( + (department) => department.id === departmentId, + ), + ), + ) + : lecturers; function reset() { setAcademicTermId(''); - setCourse(null); + setSelectedCourses([]); setLecturer(null); } @@ -351,7 +365,7 @@ function CreateForm({ value={academicTermId} onValueChange={(value) => { setAcademicTermId(value); - setCourse(null); + setSelectedCourses([]); setLecturer(null); }} > @@ -376,26 +390,43 @@ function CreateForm({ Mata Kuliah{' '} * - + {selectedCourses.map((c) => ( + + ))} { - setCourse(value); + setSelectedCourses(value); setLecturer(null); }} itemToStringLabel={(c) => `${c.code} - ${c.name}`} isItemEqualToValue={(a, b) => a.id === b.id} > - - + + {selectedCourses.map((c) => ( + + {c.code} + + ))} + + + Mata kuliah tidak ditemukan. @@ -423,7 +454,7 @@ function CreateForm({ - +