import { Head, router } from '@inertiajs/react'; import { Plus } from 'lucide-react'; import { useState } from 'react'; import type { PaginationState } from '@/components/data-table'; import { DataTable } from '@/components/data-table'; import { DeleteConfirmDialog } from '@/components/delete-confirm-dialog'; import type { FilterField } from '@/components/filter-dialog'; import { FilterDialog } from '@/components/filter-dialog'; import { FormDialog } from '@/components/form-dialog'; import InputError from '@/components/input-error'; import { PageHeader } from '@/components/page-header'; import { Button } from '@/components/ui/button'; import { Combobox, ComboboxChip, ComboboxChips, ComboboxChipsInput, ComboboxCollection, ComboboxContent, ComboboxEmpty, ComboboxGroup, ComboboxInput, ComboboxItem, ComboboxLabel, ComboboxList, useComboboxAnchor, } from '@/components/ui/combobox'; import { Label } from '@/components/ui/label'; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from '@/components/ui/select'; import { usePermissions } from '@/hooks/use-permissions'; import { useServerTable } from '@/hooks/use-server-table'; import { index as courseClassIndex, destroy, store, update, } from '@/routes/admin/manage/course-classes'; import { formatAcademicTermLabel } from '@/types/academic-term'; import { ClassMethodLabels, ClassMethods } from '@/types/course-class'; import type { CourseClass } from '@/types/course-class'; import { createCourseClassColumns } from './columns'; type Course = { id: number; code: string; name: string; department_id: number; department: { id: number; name: string } | null; semester_number: number | null; }; type CourseGroup = { value: string; items: Course[] }; type Lecturer = { id: number; lecturer_number: string; departments: { id: number; name: string }[]; user: { profile: { full_name: string } | null } | null; }; type AcademicTerm = { id: number; academic_year: string; semester: string; }; type CourseClassAssignment = { id: number; course_id: number; academic_term_id: number; }; function groupCoursesByDepartmentAndSemester(courses: Course[]): CourseGroup[] { const groups: CourseGroup[] = []; let currentKey: string | null = null; for (const course of courses) { const key = `${course.department?.name ?? 'Tanpa Jurusan'} — Semester ${course.semester_number ?? 'Tidak ditentukan'}`; if (key !== currentKey) { currentKey = key; groups.push({ value: key, items: [] }); } groups[groups.length - 1].items.push(course); } return groups; } type Props = { courseClasses: { data: CourseClass[]; current_page: number; last_page: number; per_page: number; total: number; }; courses: Course[]; lecturers: Lecturer[]; academicTerms: AcademicTerm[]; courseClassAssignments: CourseClassAssignment[]; highlight?: number; filters: { academic_term_id?: string; method?: string; }; }; export default function CourseClassIndex({ courseClasses, courses, lecturers, academicTerms, courseClassAssignments, highlight, filters, }: Props) { const [createOpen, setCreateOpen] = useState(false); const [editing, setEditing] = useState(null); const [deleting, setDeleting] = useState(null); const { hasPermission } = usePermissions(); const canCreate = hasPermission('create-course-classes'); const canUpdate = hasPermission('update-course-classes'); const canDelete = hasPermission('delete-course-classes'); const canViewEnrollments = hasPermission('view-course-class-enrollments'); const filterFields: FilterField[] = [ { key: 'academic_term_id', label: 'Periode Akademik', options: academicTerms.map((term) => ({ value: String(term.id), label: formatAcademicTermLabel(term), })), }, { key: 'method', label: 'Metode', options: ClassMethods.map((method) => ({ value: method, label: ClassMethodLabels[method], })), }, ]; const pagination: PaginationState = { current_page: courseClasses.current_page, last_page: courseClasses.last_page, per_page: courseClasses.per_page, total: courseClasses.total, }; const { search, handlePageChange, handlePerPageChange, handleSearchChange, applyFilters, } = useServerTable({ route: () => courseClassIndex.url(), pagination, filters, }); function handleDelete() { if (!deleting) { return; } router.delete(destroy(deleting.id), { onSuccess: () => setDeleting(null), }); } const columns = createCourseClassColumns({ handleEdit: (courseClass) => setEditing(courseClass), handleDeleteClick: (courseClass) => setDeleting(courseClass), canUpdate, canDelete, canViewEnrollments, }); return ( <>
Menampilkan kelas mata kuliah dari notifikasi.

) } actions={ canCreate && ( ) } /> { if (!open) { setEditing(null); } }} editing={editing} courses={courses} lecturers={lecturers} academicTerms={academicTerms} courseClassAssignments={courseClassAssignments} /> } /> { if (!open) { setDeleting(null); } }} title="Hapus Kelas Mata Kuliah" description={(courseClass) => `Apakah Anda yakin ingin menghapus kelas "${courseClass.course?.name}"? Tindakan ini tidak dapat dibatalkan.` } onConfirm={handleDelete} />
); } function CreateForm({ open, onOpenChange, courses, lecturers, academicTerms, courseClassAssignments, }: { open: boolean; onOpenChange: (open: boolean) => void; courses: Course[]; lecturers: Lecturer[]; academicTerms: AcademicTerm[]; courseClassAssignments: CourseClassAssignment[]; }) { const [academicTermId, setAcademicTermId] = useState(''); 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 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 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(''); setSelectedCourses([]); setLecturer(null); } return ( { onOpenChange(false); reset(); }} > {({ errors }) => (
{selectedCourses.map((c) => ( ))} { 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. {(group: CourseGroup) => ( {group.value} {(c: Course) => ( {c.code} - {c.name} )} )}
`${lect.user?.profile?.full_name ?? 'N/A'} - ${lect.lecturer_number}` } isItemEqualToValue={(a, b) => a.id === b.id} disabled={selectedCourses.length === 0} > 0 ? 'Pilih dosen pengampu' : 'Pilih mata kuliah terlebih dahulu' } className="w-full" /> Dosen tidak ditemukan. {(lect: Lecturer) => ( {lect.user?.profile?.full_name ?? 'N/A'}{' '} - {lect.lecturer_number} )}
)}
); } function EditForm({ open, onOpenChange, editing, courses, lecturers, academicTerms, courseClassAssignments, }: { open: boolean; onOpenChange: (open: boolean) => void; editing: CourseClass | null; courses: Course[]; lecturers: Lecturer[]; academicTerms: AcademicTerm[]; courseClassAssignments: CourseClassAssignment[]; }) { const [academicTermId, setAcademicTermId] = useState( editing ? String(editing.academic_term_id) : '', ); const [course, setCourse] = useState( editing ? (courses.find((c) => c.id === editing.course_id) ?? null) : null, ); const [lecturer, setLecturer] = useState( editing ? (lecturers.find((l) => l.id === editing.lecturer_id) ?? null) : null, ); const takenCourseIds = new Set( courseClassAssignments .filter( (a) => String(a.academic_term_id) === academicTermId && a.id !== editing?.id, ) .map((a) => a.course_id), ); const availableCourses = courses.filter((c) => !takenCourseIds.has(c.id)); const courseGroups = groupCoursesByDepartmentAndSemester(availableCourses); const availableLecturers = course ? lecturers.filter((l) => l.departments.some( (department) => department.id === course.department_id, ), ) : lecturers; return ( onOpenChange(false)} > {({ errors }) => editing && (
{ setCourse(value); setLecturer(null); }} itemToStringLabel={(c) => `${c.code} - ${c.name}` } isItemEqualToValue={(a, b) => a.id === b.id} > Mata kuliah tidak ditemukan. {(group: CourseGroup) => ( {group.value} {(c: Course) => ( {c.code} -{' '} {c.name} )} )}
`${lect.user?.profile?.full_name ?? 'N/A'} - ${lect.lecturer_number}` } isItemEqualToValue={(a, b) => a.id === b.id} disabled={!course} > Dosen tidak ditemukan. {(lect: Lecturer) => ( {lect.user?.profile ?.full_name ?? 'N/A'}{' '} - {lect.lecturer_number} )}
) }
); }