feat: implement CourseClass and ClassEnrollment management; add controllers, services, and requests for course enrollment functionality

This commit is contained in:
Yoga Pangestu 2026-09-03 19:59:58 +07:00
parent f6da193473
commit 80435b6cfa
8 changed files with 50 additions and 47 deletions

View File

@ -1,11 +1,11 @@
<?php <?php
namespace App\Http\Controllers\Admin\Manage; namespace App\Http\Controllers\Admin\Manage\CourseClass;
use App\Http\Controllers\Controller; use App\Http\Controllers\Controller;
use App\Models\ClassEnrollment; use App\Models\ClassEnrollment;
use App\Models\CourseClass; use App\Models\CourseClass;
use App\Services\Admin\Manage\ClassEnrollmentService; use App\Services\Admin\Manage\CourseClass\ClassEnrollmentService;
use Illuminate\Http\RedirectResponse; use Illuminate\Http\RedirectResponse;
use Inertia\Inertia; use Inertia\Inertia;
use Inertia\Response; use Inertia\Response;

View File

@ -1,13 +1,13 @@
<?php <?php
namespace App\Http\Controllers\Admin\Manage; namespace App\Http\Controllers\Admin\Manage\CourseClass;
use App\Http\Controllers\Controller; use App\Http\Controllers\Controller;
use App\Http\Requests\Admin\Manage\CourseClassRequest; use App\Http\Requests\Admin\Manage\CourseClass\CourseClassRequest;
use App\Http\Requests\Admin\Manage\DuplicateCourseClassesRequest; use App\Http\Requests\Admin\Manage\CourseClass\DuplicateCourseClassesRequest;
use App\Http\Requests\PaginatedRequest; use App\Http\Requests\PaginatedRequest;
use App\Models\CourseClass; use App\Models\CourseClass;
use App\Services\Admin\Manage\CourseClassService; use App\Services\Admin\Manage\CourseClass\CourseClassService;
use App\Services\Admin\Master\AcademicTermService; use App\Services\Admin\Master\AcademicTermService;
use App\Services\Admin\Master\CourseService; use App\Services\Admin\Master\CourseService;
use App\Services\Admin\Users\LecturerService; use App\Services\Admin\Users\LecturerService;

View File

@ -1,6 +1,6 @@
<?php <?php
namespace App\Http\Requests\Admin\Manage; namespace App\Http\Requests\Admin\Manage\CourseClass;
use App\Enums\ClassMethod; use App\Enums\ClassMethod;
use App\Enums\Semester; use App\Enums\Semester;

View File

@ -1,6 +1,6 @@
<?php <?php
namespace App\Http\Requests\Admin\Manage; namespace App\Http\Requests\Admin\Manage\CourseClass;
use App\Models\AcademicTerm; use App\Models\AcademicTerm;
use Illuminate\Foundation\Http\FormRequest; use Illuminate\Foundation\Http\FormRequest;

View File

@ -1,6 +1,6 @@
<?php <?php
namespace App\Services\Admin\Manage; namespace App\Services\Admin\Manage\CourseClass;
use App\Models\ClassEnrollment; use App\Models\ClassEnrollment;
use App\Models\CourseClass; use App\Models\CourseClass;

View File

@ -1,6 +1,6 @@
<?php <?php
namespace App\Services\Admin\Manage; namespace App\Services\Admin\Manage\CourseClass;
use App\Models\CourseClass; use App\Models\CourseClass;
use App\Models\User; use App\Models\User;
@ -10,28 +10,6 @@
class CourseClassService class CourseClassService
{ {
/**
* When $user is a dosen, only their own assigned classes are returned.
* When $user is a mahasiswa, only classes from their own department are returned.
*/
public function getAllForSelect(?User $user = null): Collection
{
return CourseClass::query()
->select(['course_classes.id', 'course_classes.course_id', 'course_classes.lecturer_id', 'course_classes.academic_term_id'])
->join('courses', 'courses.id', '=', 'course_classes.course_id')
->join('departments', 'departments.id', '=', 'courses.department_id')
->with([
'course:id,code,name,semester_number,department_id',
'course.department:id,name',
])
->when($user?->hasRole('dosen'), fn ($q) => $q->where('course_classes.lecturer_id', $user->lecturer?->id))
->when($user?->hasRole('mahasiswa'), fn ($q) => $q->where('courses.department_id', $user->student?->department_id))
->orderBy('departments.name')
->orderBy('courses.semester_number')
->orderBy('courses.name')
->get();
}
public function paginated(int $perPage = 25, string $search = '', ?int $academicTermId = null, ?string $method = null): LengthAwarePaginator public function paginated(int $perPage = 25, string $search = '', ?int $academicTermId = null, ?string $method = null): LengthAwarePaginator
{ {
return CourseClass::query() return CourseClass::query()
@ -65,11 +43,12 @@ public function createMany(array $data): Collection
public function update(CourseClass $courseClass, array $data): CourseClass public function update(CourseClass $courseClass, array $data): CourseClass
{ {
$courseClass->course_id = $data['course_id']; $courseClass->update([
$courseClass->lecturer_id = $data['lecturer_id']; 'course_id' => $data['course_id'],
$courseClass->academic_term_id = $data['academic_term_id']; 'lecturer_id' => $data['lecturer_id'],
$courseClass->method = $data['method'] ?? null; 'academic_term_id' => $data['academic_term_id'],
$courseClass->update(); 'method' => $data['method'] ?? null,
]);
return $courseClass; return $courseClass;
} }
@ -121,4 +100,26 @@ public function duplicateFromTerm(int $sourceAcademicTermId, int $targetAcademic
return ['created' => $created, 'skipped' => $skipped]; return ['created' => $created, 'skipped' => $skipped];
}); });
} }
/**
* When $user is a dosen, only their own assigned classes are returned.
* When $user is a mahasiswa, only classes from their own department are returned.
*/
public function getAllForSelect(?User $user = null): Collection
{
return CourseClass::query()
->select(['course_classes.id', 'course_classes.course_id', 'course_classes.lecturer_id', 'course_classes.academic_term_id'])
->join('courses', 'courses.id', '=', 'course_classes.course_id')
->join('departments', 'departments.id', '=', 'courses.department_id')
->with([
'course:id,code,name,semester_number,department_id',
'course.department:id,name',
])
->when($user?->hasRole('dosen'), fn ($q) => $q->where('course_classes.lecturer_id', $user->lecturer?->id))
->when($user?->hasRole('mahasiswa'), fn ($q) => $q->where('courses.department_id', $user->student?->department_id))
->orderBy('departments.name')
->orderBy('courses.semester_number')
->orderBy('courses.name')
->get();
}
} }

View File

@ -42,15 +42,17 @@ export default function ClassEnrollmentIndex({
const columns: ColumnDef<ClassEnrollment>[] = [ const columns: ColumnDef<ClassEnrollment>[] = [
{ {
accessorKey: 'student.student_number', id: 'identity',
header: () => <span>NIM</span>, header: () => <span>NIM</span>,
cell: ({ row }) => row.original.student?.student_number ?? '-', cell: ({ row }) => (
}, <div className="flex flex-col">
{ <span>{row.original.student?.student_number ?? '-'}</span>
accessorKey: 'student.user.profile.full_name', <span className="text-sm text-muted-foreground">
header: () => <span>Nama Mahasiswa</span>, {row.original.student?.user?.profile?.full_name ??
cell: ({ row }) => '-'}
row.original.student?.user?.profile?.full_name ?? '-', </span>
</div>
),
}, },
{ {
accessorKey: 'student.department.name', accessorKey: 'student.department.name',

View File

@ -11,8 +11,8 @@
use App\Http\Controllers\Admin\Finances\TuitionInvoiceController; use App\Http\Controllers\Admin\Finances\TuitionInvoiceController;
use App\Http\Controllers\Admin\Finances\TuitionPaymentController; use App\Http\Controllers\Admin\Finances\TuitionPaymentController;
use App\Http\Controllers\Admin\Manage\AnnouncementController; use App\Http\Controllers\Admin\Manage\AnnouncementController;
use App\Http\Controllers\Admin\Manage\ClassEnrollmentController; use App\Http\Controllers\Admin\Manage\CourseClass\ClassEnrollmentController;
use App\Http\Controllers\Admin\Manage\CourseClassController; use App\Http\Controllers\Admin\Manage\CourseClass\CourseClassController;
use App\Http\Controllers\Admin\Manage\CourseRegistrationController; use App\Http\Controllers\Admin\Manage\CourseRegistrationController;
use App\Http\Controllers\Admin\Master\AcademicTermController; use App\Http\Controllers\Admin\Master\AcademicTermController;
use App\Http\Controllers\Admin\Master\CourseController; use App\Http\Controllers\Admin\Master\CourseController;