feat: update CreateForm and EditForm to filter courses and lecturers based on selected lecturer, enhancing user experience

This commit is contained in:
Yoga Pangestu 2026-09-02 18:04:14 +07:00
parent 1b68cf6125
commit cdd81caf22

View File

@ -349,26 +349,19 @@ function CreateForm({
.map((a) => a.course_id), .map((a) => a.course_id),
); );
const selectedCourseIds = new Set(selectedCourses.map((c) => c.id)); const selectedCourseIds = new Set(selectedCourses.map((c) => c.id));
const availableCourses = courses.filter( const lecturerDepartmentIds = new Set(
(c) => lecturer ? lecturer.departments.map((d) => d.id) : [],
!takenCourseIds.has(c.id) &&
!selectedCourseIds.has(c.id) &&
courseMatchesTermParity(c, selectedTerm),
); );
const availableCourses = lecturer
? courses.filter(
(c) =>
lecturerDepartmentIds.has(c.department_id) &&
!takenCourseIds.has(c.id) &&
!selectedCourseIds.has(c.id) &&
courseMatchesTermParity(c, selectedTerm),
)
: [];
const courseGroups = groupCoursesByDepartmentAndSemester(availableCourses); 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() { function reset() {
setAcademicTermId(''); setAcademicTermId('');
@ -405,7 +398,6 @@ function CreateForm({
onValueChange={(value) => { onValueChange={(value) => {
setAcademicTermId(value); setAcademicTermId(value);
setSelectedCourses([]); setSelectedCourses([]);
setLecturer(null);
}} }}
> >
<SelectTrigger className="w-full"> <SelectTrigger className="w-full">
@ -424,6 +416,52 @@ function CreateForm({
</Select> </Select>
<InputError message={errors.academic_term_id} /> <InputError message={errors.academic_term_id} />
</div> </div>
<div className="grid gap-2">
<Label>
Dosen Pengampu{' '}
<span className="text-destructive">*</span>
</Label>
<input
type="hidden"
name="lecturer_id"
value={lecturer?.id ?? ''}
/>
<Combobox
items={lecturers}
value={lecturer}
onValueChange={(value) => {
setLecturer(value);
setSelectedCourses([]);
}}
itemToStringLabel={(lect) =>
`${lect.user?.profile?.full_name ?? 'N/A'} - ${lect.lecturer_number}`
}
isItemEqualToValue={(a, b) => a.id === b.id}
>
<ComboboxInput
placeholder="Pilih dosen pengampu"
className="w-full"
/>
<ComboboxContent>
<ComboboxEmpty>
Dosen tidak ditemukan.
</ComboboxEmpty>
<ComboboxList>
{(lect: Lecturer) => (
<ComboboxItem
key={lect.id}
value={lect}
>
{lect.user?.profile?.full_name ??
'N/A'}{' '}
- {lect.lecturer_number}
</ComboboxItem>
)}
</ComboboxList>
</ComboboxContent>
</Combobox>
<InputError message={errors.lecturer_id} />
</div>
<div className="grid gap-2"> <div className="grid gap-2">
<Label> <Label>
Mata Kuliah{' '} Mata Kuliah{' '}
@ -441,12 +479,10 @@ function CreateForm({
items={courseGroups} items={courseGroups}
multiple multiple
value={selectedCourses} value={selectedCourses}
onValueChange={(value) => { onValueChange={setSelectedCourses}
setSelectedCourses(value);
setLecturer(null);
}}
itemToStringLabel={(c) => `${c.code} - ${c.name}`} itemToStringLabel={(c) => `${c.code} - ${c.name}`}
isItemEqualToValue={(a, b) => a.id === b.id} isItemEqualToValue={(a, b) => a.id === b.id}
disabled={!lecturer}
> >
<ComboboxChips ref={courseAnchor}> <ComboboxChips ref={courseAnchor}>
{selectedCourses.map((c) => ( {selectedCourses.map((c) => (
@ -458,10 +494,13 @@ function CreateForm({
</ComboboxChip> </ComboboxChip>
))} ))}
<ComboboxChipsInput <ComboboxChipsInput
disabled={!lecturer}
placeholder={ placeholder={
selectedCourses.length === 0 !lecturer
? 'Pilih mata kuliah (bisa lebih dari satu)' ? 'Pilih dosen pengampu terlebih dahulu'
: '' : selectedCourses.length === 0
? 'Pilih mata kuliah (bisa lebih dari satu)'
: ''
} }
/> />
</ComboboxChips> </ComboboxChips>
@ -495,55 +534,6 @@ function CreateForm({
</Combobox> </Combobox>
<InputError message={errors.course_ids} /> <InputError message={errors.course_ids} />
</div> </div>
<div className="grid gap-2">
<Label>
Dosen Pengampu{' '}
<span className="text-destructive">*</span>
</Label>
<input
type="hidden"
name="lecturer_id"
value={lecturer?.id ?? ''}
/>
<Combobox
items={availableLecturers}
value={lecturer}
onValueChange={setLecturer}
itemToStringLabel={(lect) =>
`${lect.user?.profile?.full_name ?? 'N/A'} - ${lect.lecturer_number}`
}
isItemEqualToValue={(a, b) => a.id === b.id}
disabled={selectedCourses.length === 0}
>
<ComboboxInput
disabled={selectedCourses.length === 0}
placeholder={
selectedCourses.length > 0
? 'Pilih dosen pengampu'
: 'Pilih mata kuliah terlebih dahulu'
}
className="w-full"
/>
<ComboboxContent>
<ComboboxEmpty>
Dosen tidak ditemukan.
</ComboboxEmpty>
<ComboboxList>
{(lect: Lecturer) => (
<ComboboxItem
key={lect.id}
value={lect}
>
{lect.user?.profile?.full_name ??
'N/A'}{' '}
- {lect.lecturer_number}
</ComboboxItem>
)}
</ComboboxList>
</ComboboxContent>
</Combobox>
<InputError message={errors.lecturer_id} />
</div>
<div className="grid gap-2"> <div className="grid gap-2">
<Label>Metode</Label> <Label>Metode</Label>
<input type="hidden" name="method" /> <input type="hidden" name="method" />
@ -610,18 +600,18 @@ function EditForm({
) )
.map((a) => a.course_id), .map((a) => a.course_id),
); );
const availableCourses = courses.filter( const lecturerDepartmentIds = new Set(
(c) => lecturer ? lecturer.departments.map((d) => d.id) : [],
!takenCourseIds.has(c.id) && courseMatchesTermParity(c, selectedTerm),
); );
const courseGroups = groupCoursesByDepartmentAndSemester(availableCourses); const availableCourses = lecturer
const availableLecturers = course ? courses.filter(
? lecturers.filter((l) => (c) =>
l.departments.some( lecturerDepartmentIds.has(c.department_id) &&
(department) => department.id === course.department_id, !takenCourseIds.has(c.id) &&
), courseMatchesTermParity(c, selectedTerm),
) )
: lecturers; : [];
const courseGroups = groupCoursesByDepartmentAndSemester(availableCourses);
return ( return (
<FormDialog <FormDialog
@ -635,6 +625,85 @@ function EditForm({
{({ errors }) => {({ errors }) =>
editing && ( editing && (
<div className="grid gap-4"> <div className="grid gap-4">
<div className="grid gap-2">
<Label>
Periode Akademik{' '}
<span className="text-destructive">*</span>
</Label>
<input
type="hidden"
name="academic_term_id"
value={academicTermId}
/>
<Select
value={academicTermId}
onValueChange={(value) => {
setAcademicTermId(value);
setCourse(null);
}}
>
<SelectTrigger className="w-full">
<SelectValue placeholder="Pilih periode akademik" />
</SelectTrigger>
<SelectContent>
{academicTerms.map((term) => (
<SelectItem
key={term.id}
value={String(term.id)}
>
{formatAcademicTermLabel(term)}
</SelectItem>
))}
</SelectContent>
</Select>
<InputError message={errors.academic_term_id} />
</div>
<div className="grid gap-2">
<Label>
Dosen Pengampu{' '}
<span className="text-destructive">*</span>
</Label>
<input
type="hidden"
name="lecturer_id"
value={lecturer?.id ?? ''}
/>
<Combobox
items={lecturers}
value={lecturer}
onValueChange={(value) => {
setLecturer(value);
setCourse(null);
}}
itemToStringLabel={(lect) =>
`${lect.user?.profile?.full_name ?? 'N/A'} - ${lect.lecturer_number}`
}
isItemEqualToValue={(a, b) => a.id === b.id}
>
<ComboboxInput
placeholder="Pilih dosen pengampu"
className="w-full"
/>
<ComboboxContent>
<ComboboxEmpty>
Dosen tidak ditemukan.
</ComboboxEmpty>
<ComboboxList>
{(lect: Lecturer) => (
<ComboboxItem
key={lect.id}
value={lect}
>
{lect.user?.profile
?.full_name ?? 'N/A'}{' '}
- {lect.lecturer_number}
</ComboboxItem>
)}
</ComboboxList>
</ComboboxContent>
</Combobox>
<InputError message={errors.lecturer_id} />
</div>
<div className="grid gap-2"> <div className="grid gap-2">
<Label> <Label>
Mata Kuliah{' '} Mata Kuliah{' '}
@ -648,17 +717,20 @@ function EditForm({
<Combobox <Combobox
items={courseGroups} items={courseGroups}
value={course} value={course}
onValueChange={(value) => { onValueChange={setCourse}
setCourse(value);
setLecturer(null);
}}
itemToStringLabel={(c) => itemToStringLabel={(c) =>
`${c.code} - ${c.name}` `${c.code} - ${c.name}`
} }
isItemEqualToValue={(a, b) => a.id === b.id} isItemEqualToValue={(a, b) => a.id === b.id}
disabled={!lecturer}
> >
<ComboboxInput <ComboboxInput
placeholder="Pilih mata kuliah" disabled={!lecturer}
placeholder={
lecturer
? 'Pilih mata kuliah'
: 'Pilih dosen pengampu terlebih dahulu'
}
className="w-full" className="w-full"
/> />
<ComboboxContent> <ComboboxContent>
@ -692,89 +764,6 @@ function EditForm({
</Combobox> </Combobox>
<InputError message={errors.course_id} /> <InputError message={errors.course_id} />
</div> </div>
<div className="grid gap-2">
<Label>
Dosen Pengampu{' '}
<span className="text-destructive">*</span>
</Label>
<input
type="hidden"
name="lecturer_id"
value={lecturer?.id ?? ''}
/>
<Combobox
items={availableLecturers}
value={lecturer}
onValueChange={setLecturer}
itemToStringLabel={(lect) =>
`${lect.user?.profile?.full_name ?? 'N/A'} - ${lect.lecturer_number}`
}
isItemEqualToValue={(a, b) => a.id === b.id}
disabled={!course}
>
<ComboboxInput
disabled={!course}
placeholder={
course
? 'Pilih dosen pengampu'
: 'Pilih mata kuliah terlebih dahulu'
}
className="w-full"
/>
<ComboboxContent>
<ComboboxEmpty>
Dosen tidak ditemukan.
</ComboboxEmpty>
<ComboboxList>
{(lect: Lecturer) => (
<ComboboxItem
key={lect.id}
value={lect}
>
{lect.user?.profile
?.full_name ?? 'N/A'}{' '}
- {lect.lecturer_number}
</ComboboxItem>
)}
</ComboboxList>
</ComboboxContent>
</Combobox>
<InputError message={errors.lecturer_id} />
</div>
<div className="grid gap-2">
<Label>
Periode Akademik{' '}
<span className="text-destructive">*</span>
</Label>
<input
type="hidden"
name="academic_term_id"
value={academicTermId}
/>
<Select
value={academicTermId}
onValueChange={(value) => {
setAcademicTermId(value);
setCourse(null);
setLecturer(null);
}}
>
<SelectTrigger className="w-full">
<SelectValue placeholder="Pilih periode akademik" />
</SelectTrigger>
<SelectContent>
{academicTerms.map((term) => (
<SelectItem
key={term.id}
value={String(term.id)}
>
{formatAcademicTermLabel(term)}
</SelectItem>
))}
</SelectContent>
</Select>
<InputError message={errors.academic_term_id} />
</div>
<div className="grid gap-2"> <div className="grid gap-2">
<Label>Metode</Label> <Label>Metode</Label>
<Select <Select