feat: enhance student and lecturer data retrieval with improved joins and sorting
This commit is contained in:
parent
8528a3258d
commit
53afdb1fec
@ -41,15 +41,19 @@ public function departmentSummary(): Collection
|
||||
public function paginated(int $perPage = 25, string $search = '', ?int $departmentId = null, ?int $advisorLecturerId = null, ?array $ledDepartmentIds = null): LengthAwarePaginator
|
||||
{
|
||||
$paginator = Student::query()
|
||||
->select(['id', 'user_id', 'student_number', 'department_id'])
|
||||
->where('status', StudentStatus::Active)
|
||||
->when($ledDepartmentIds !== null, fn ($q) => $q->whereIn('department_id', $ledDepartmentIds))
|
||||
->when($departmentId, fn ($q) => $q->where('department_id', $departmentId))
|
||||
->when($advisorLecturerId, fn ($q) => $q->where('academic_advisor_id', $advisorLecturerId))
|
||||
->when($search, fn ($q) => $q->where('student_number', 'like', "%{$search}%")
|
||||
->orWhereHas('user.profile', fn ($q) => $q->where('full_name', 'like', "%{$search}%")))
|
||||
->select(['students.id', 'students.user_id', 'students.student_number', 'students.department_id'])
|
||||
->join('departments', 'departments.id', '=', 'students.department_id')
|
||||
->join('users', 'users.id', '=', 'students.user_id')
|
||||
->join('user_profiles', 'user_profiles.user_id', '=', 'users.id')
|
||||
->where('students.status', StudentStatus::Active)
|
||||
->when($ledDepartmentIds !== null, fn ($q) => $q->whereIn('students.department_id', $ledDepartmentIds))
|
||||
->when($departmentId, fn ($q) => $q->where('students.department_id', $departmentId))
|
||||
->when($advisorLecturerId, fn ($q) => $q->where('students.academic_advisor_id', $advisorLecturerId))
|
||||
->when($search, fn ($q) => $q->where('students.student_number', 'like', "%{$search}%")
|
||||
->orWhere('user_profiles.full_name', 'like', "%{$search}%"))
|
||||
->with(['user.profile', 'department:id,name'])
|
||||
->orderBy('student_number')
|
||||
->orderBy('departments.name')
|
||||
->orderBy('user_profiles.full_name')
|
||||
->paginate($perPage);
|
||||
|
||||
return $paginator->through(fn (Student $student) => [
|
||||
|
||||
@ -14,12 +14,15 @@ class LecturerService
|
||||
{
|
||||
public function getAllForSelect(): Collection
|
||||
{
|
||||
return Lecturer::select(['id', 'user_id', 'lecturer_number'])
|
||||
return Lecturer::select(['lecturers.id', 'lecturers.user_id', 'lecturers.lecturer_number'])
|
||||
->join('users', 'users.id', '=', 'lecturers.user_id')
|
||||
->join('user_profiles', 'user_profiles.user_id', '=', 'users.id')
|
||||
->with([
|
||||
'user:id,username',
|
||||
'user.profile:id,user_id,full_name',
|
||||
'departments:id,name',
|
||||
])
|
||||
->orderBy('user_profiles.full_name')
|
||||
->get();
|
||||
}
|
||||
|
||||
|
||||
@ -14,13 +14,19 @@ class StudentService
|
||||
{
|
||||
public function getAllForSelect(?string $status = null): Collection
|
||||
{
|
||||
return Student::select(['id', 'user_id', 'student_number', 'department_id', 'current_semester'])
|
||||
return Student::select(['students.id', 'students.user_id', 'students.student_number', 'students.department_id', 'students.current_semester'])
|
||||
->join('departments', 'departments.id', '=', 'students.department_id')
|
||||
->join('users', 'users.id', '=', 'students.user_id')
|
||||
->join('user_profiles', 'user_profiles.user_id', '=', 'users.id')
|
||||
->with([
|
||||
'user:id,username',
|
||||
'user.profile:id,user_id,full_name',
|
||||
'department:id,name',
|
||||
])
|
||||
->when($status, fn ($q) => $q->where('status', $status))
|
||||
->when($status, fn ($q) => $q->where('students.status', $status))
|
||||
->orderBy('departments.name')
|
||||
->orderBy('students.current_semester')
|
||||
->orderBy('user_profiles.full_name')
|
||||
->get();
|
||||
}
|
||||
|
||||
|
||||
@ -32,10 +32,13 @@ import {
|
||||
ComboboxChip,
|
||||
ComboboxChips,
|
||||
ComboboxChipsInput,
|
||||
ComboboxCollection,
|
||||
ComboboxContent,
|
||||
ComboboxEmpty,
|
||||
ComboboxGroup,
|
||||
ComboboxInput,
|
||||
ComboboxItem,
|
||||
ComboboxLabel,
|
||||
ComboboxList,
|
||||
useComboboxAnchor,
|
||||
} from '@/components/ui/combobox';
|
||||
@ -108,6 +111,28 @@ function studentLabel(student: TuitionInvoiceStudent): string {
|
||||
return `${student.user?.profile?.full_name ?? 'N/A'} - ${student.student_number}`;
|
||||
}
|
||||
|
||||
type StudentGroup = { value: string; items: TuitionInvoiceStudent[] };
|
||||
|
||||
function groupStudentsByDepartmentAndSemester(
|
||||
students: TuitionInvoiceStudent[],
|
||||
): StudentGroup[] {
|
||||
const groups: StudentGroup[] = [];
|
||||
let currentKey: string | null = null;
|
||||
|
||||
for (const student of students) {
|
||||
const key = `${student.department?.name ?? 'Tanpa Jurusan'} — Semester ${student.current_semester ?? 'Tidak ditentukan'}`;
|
||||
|
||||
if (key !== currentKey) {
|
||||
currentKey = key;
|
||||
groups.push({ value: key, items: [] });
|
||||
}
|
||||
|
||||
groups[groups.length - 1].items.push(student);
|
||||
}
|
||||
|
||||
return groups;
|
||||
}
|
||||
|
||||
export default function TuitionInvoiceIndex({
|
||||
invoices,
|
||||
summary,
|
||||
@ -379,6 +404,7 @@ function CreateForm({
|
||||
!student.invoiced_term_ids?.includes(Number(academicTermId)),
|
||||
)
|
||||
: [];
|
||||
const studentGroups = groupStudentsByDepartmentAndSemester(availableStudents);
|
||||
|
||||
function reset() {
|
||||
setAcademicTermId('');
|
||||
@ -468,7 +494,7 @@ function CreateForm({
|
||||
/>
|
||||
))}
|
||||
<Combobox
|
||||
items={availableStudents}
|
||||
items={studentGroups}
|
||||
multiple
|
||||
disabled={!academicTermId}
|
||||
value={selectedStudents}
|
||||
@ -501,13 +527,27 @@ function CreateForm({
|
||||
Mahasiswa tidak ditemukan.
|
||||
</ComboboxEmpty>
|
||||
<ComboboxList>
|
||||
{(student: TuitionInvoiceStudent) => (
|
||||
<ComboboxItem
|
||||
key={student.id}
|
||||
value={student}
|
||||
{(group: StudentGroup) => (
|
||||
<ComboboxGroup
|
||||
key={group.value}
|
||||
items={group.items}
|
||||
>
|
||||
{studentLabel(student)}
|
||||
</ComboboxItem>
|
||||
<ComboboxLabel>
|
||||
{group.value}
|
||||
</ComboboxLabel>
|
||||
<ComboboxCollection>
|
||||
{(
|
||||
student: TuitionInvoiceStudent,
|
||||
) => (
|
||||
<ComboboxItem
|
||||
key={student.id}
|
||||
value={student}
|
||||
>
|
||||
{studentLabel(student)}
|
||||
</ComboboxItem>
|
||||
)}
|
||||
</ComboboxCollection>
|
||||
</ComboboxGroup>
|
||||
)}
|
||||
</ComboboxList>
|
||||
</ComboboxContent>
|
||||
@ -569,6 +609,7 @@ function EditForm({
|
||||
const [student, setStudent] = useState<TuitionInvoiceStudent | null>(
|
||||
students.find((s) => s.id === editing?.student_id) ?? null,
|
||||
);
|
||||
const studentGroups = groupStudentsByDepartmentAndSemester(students);
|
||||
|
||||
return (
|
||||
<FormDialog
|
||||
@ -618,7 +659,7 @@ function EditForm({
|
||||
value={student?.id ?? ''}
|
||||
/>
|
||||
<Combobox
|
||||
items={students}
|
||||
items={studentGroups}
|
||||
value={student}
|
||||
onValueChange={setStudent}
|
||||
itemToStringLabel={studentLabel}
|
||||
@ -633,13 +674,29 @@ function EditForm({
|
||||
Mahasiswa tidak ditemukan.
|
||||
</ComboboxEmpty>
|
||||
<ComboboxList>
|
||||
{(option: TuitionInvoiceStudent) => (
|
||||
<ComboboxItem
|
||||
key={option.id}
|
||||
value={option}
|
||||
{(group: StudentGroup) => (
|
||||
<ComboboxGroup
|
||||
key={group.value}
|
||||
items={group.items}
|
||||
>
|
||||
{studentLabel(option)}
|
||||
</ComboboxItem>
|
||||
<ComboboxLabel>
|
||||
{group.value}
|
||||
</ComboboxLabel>
|
||||
<ComboboxCollection>
|
||||
{(
|
||||
option: TuitionInvoiceStudent,
|
||||
) => (
|
||||
<ComboboxItem
|
||||
key={option.id}
|
||||
value={option}
|
||||
>
|
||||
{studentLabel(
|
||||
option,
|
||||
)}
|
||||
</ComboboxItem>
|
||||
)}
|
||||
</ComboboxCollection>
|
||||
</ComboboxGroup>
|
||||
)}
|
||||
</ComboboxList>
|
||||
</ComboboxContent>
|
||||
|
||||
@ -26,11 +26,6 @@ export function createCourseRegistrationColumns(): ColumnDef<CourseRegistrationR
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'department',
|
||||
header: () => <span>Jurusan</span>,
|
||||
cell: ({ row }) => row.original.student?.department?.name ?? '-',
|
||||
},
|
||||
{
|
||||
id: 'actions',
|
||||
header: () => <span className="block text-center">Aksi</span>,
|
||||
|
||||
@ -79,6 +79,9 @@ export default function CourseRegistrationIndex({
|
||||
onSearchChange={handleSearchChange}
|
||||
searchValue={search}
|
||||
searchKey="student"
|
||||
groupBy={(row) =>
|
||||
row.student?.department?.name ?? 'Tanpa Jurusan'
|
||||
}
|
||||
toolbar={
|
||||
<FilterDialog
|
||||
fields={filterFields}
|
||||
|
||||
@ -2,6 +2,7 @@ export type TuitionInvoiceStudent = {
|
||||
id: number;
|
||||
student_number: string;
|
||||
department: { id: number; name: string } | null;
|
||||
current_semester: number;
|
||||
user: { profile: { full_name: string } | null } | null;
|
||||
invoiced_term_ids?: number[];
|
||||
};
|
||||
|
||||
Loading…
Reference in New Issue
Block a user