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