feat: refactor CourseRegistration management; update controller methods, add new request classes, and adjust routes for department leader role
This commit is contained in:
parent
80435b6cfa
commit
88319dac15
@ -2,10 +2,12 @@
|
||||
|
||||
namespace App\Http\Controllers\Admin\Manage;
|
||||
|
||||
use App\Enums\UserRole;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\Admin\Manage\RejectCourseRegistrationRequest;
|
||||
use App\Http\Requests\Admin\Manage\SaveCourseRegistrationRequest;
|
||||
use App\Http\Requests\Admin\Manage\SignCourseRegistrationRequest;
|
||||
use App\Http\Requests\Admin\Manage\CourseRegistration\RejectCourseRegistrationRequest;
|
||||
use App\Http\Requests\Admin\Manage\CourseRegistration\SignAsAdvisorCourseRegistrationRequest;
|
||||
use App\Http\Requests\Admin\Manage\CourseRegistration\SignAsDepartmentLeaderCourseRegistrationRequest;
|
||||
use App\Http\Requests\Admin\Manage\CourseRegistration\UpdateCourseRegistrationRequest;
|
||||
use App\Http\Requests\PaginatedRequest;
|
||||
use App\Models\Course;
|
||||
use App\Models\CourseRegistrationSubmission;
|
||||
@ -25,21 +27,18 @@ public function __construct(
|
||||
|
||||
public function index(PaginatedRequest $request): Response|RedirectResponse
|
||||
{
|
||||
$user = auth()->user();
|
||||
$user = $request->user();
|
||||
|
||||
if ($user->hasRole('mahasiswa')) {
|
||||
if ($user->hasRole(UserRole::Mahasiswa->value)) {
|
||||
return to_route('admin.manage.course-registrations.show', $user->student->id);
|
||||
}
|
||||
|
||||
$isKaprodi = $user->hasRole('kaprodi');
|
||||
|
||||
return Inertia::render('admin/manage/course-registrations/index', [
|
||||
'departments' => $this->service->departmentSummary(),
|
||||
'registrations' => $this->service->paginated(
|
||||
$user,
|
||||
...$request->validatedWithDefaults(),
|
||||
departmentId: $request->validated('department_id'),
|
||||
advisorLecturerId: ! $isKaprodi && $user->hasRole('dosen') ? $user->lecturer?->id : null,
|
||||
ledDepartmentIds: $isKaprodi ? $user->ledDepartmentIds() : null,
|
||||
),
|
||||
'filters' => $request->only(['department_id']),
|
||||
]);
|
||||
@ -47,8 +46,8 @@ public function index(PaginatedRequest $request): Response|RedirectResponse
|
||||
|
||||
public function show(Student $student): Response
|
||||
{
|
||||
$user = auth()->user();
|
||||
$isStudent = $user->hasRole('mahasiswa');
|
||||
$user = request()->user();
|
||||
$isStudent = $user->hasRole(UserRole::Mahasiswa->value);
|
||||
|
||||
abort_if($isStudent && $user->student->id !== $student->id, 403);
|
||||
abort_if(! $isStudent && ! $user->canAccessCourseRegistrationOf($student), 403);
|
||||
@ -61,7 +60,7 @@ public function show(Student $student): Response
|
||||
&& $user->can('approve-course-registrations')
|
||||
&& $user->canReviewCourseRegistrationOf($student);
|
||||
|
||||
$canSignAsKaprodi = $user->isKaprodiOf($student);
|
||||
$canSignAsDepartmentLeader = $user->isDepartmentLeaderOf($student);
|
||||
$canSignAsAdvisor = $user->isAdvisorOf($student);
|
||||
|
||||
$activeTerm = $this->academicTermService->getActive();
|
||||
@ -97,12 +96,12 @@ public function show(Student $student): Response
|
||||
'hasActiveTerm' => $activeTerm !== null,
|
||||
'backHref' => $backHref,
|
||||
'canReview' => $canReview,
|
||||
'canSignAsKaprodi' => $canSignAsKaprodi,
|
||||
'canSignAsDepartmentLeader' => $canSignAsDepartmentLeader,
|
||||
'canSignAsAdvisor' => $canSignAsAdvisor,
|
||||
]);
|
||||
}
|
||||
|
||||
public function save(Student $student, string $semester, SaveCourseRegistrationRequest $request): RedirectResponse
|
||||
public function update(Student $student, string $semester, UpdateCourseRegistrationRequest $request): RedirectResponse
|
||||
{
|
||||
$semesterNumber = CourseRegistrationSubmission::parseSemesterKey($semester);
|
||||
|
||||
@ -138,23 +137,19 @@ public function save(Student $student, string $semester, SaveCourseRegistrationR
|
||||
return to_route('admin.manage.course-registrations.show', $student->id);
|
||||
}
|
||||
|
||||
public function signAsKaprodi(Student $student, string $semester, SignCourseRegistrationRequest $request): RedirectResponse
|
||||
public function signAsDepartmentLeader(Student $student, string $semester, SignAsDepartmentLeaderCourseRegistrationRequest $request): RedirectResponse
|
||||
{
|
||||
abort_unless(auth()->user()->isKaprodiOf($student), 403);
|
||||
|
||||
$submission = $this->service->findOrCreateForSemester($student, $semester);
|
||||
|
||||
$this->service->signAsKaprodi($submission, $request->file('signature'));
|
||||
$this->service->signAsDepartmentLeader($submission, $request->file('signature'));
|
||||
|
||||
Inertia::flash('toast', ['type' => 'success', 'message' => 'Tanda tangan berhasil disimpan.']);
|
||||
|
||||
return to_route('admin.manage.course-registrations.show', $student->id);
|
||||
}
|
||||
|
||||
public function signAsAdvisor(Student $student, string $semester, SignCourseRegistrationRequest $request): RedirectResponse
|
||||
public function signAsAdvisor(Student $student, string $semester, SignAsAdvisorCourseRegistrationRequest $request): RedirectResponse
|
||||
{
|
||||
abort_unless(auth()->user()->isAdvisorOf($student), 403);
|
||||
|
||||
$submission = $this->service->findOrCreateForSemester($student, $semester);
|
||||
|
||||
$this->service->signAsAdvisor($submission, $request->file('signature'));
|
||||
@ -166,7 +161,11 @@ public function signAsAdvisor(Student $student, string $semester, SignCourseRegi
|
||||
|
||||
public function approve(CourseRegistrationSubmission $submission): RedirectResponse
|
||||
{
|
||||
abort_unless(auth()->user()->canReviewCourseRegistrationOf($submission->student), 403);
|
||||
abort_unless(
|
||||
request()->user()->can('approve-course-registrations')
|
||||
&& request()->user()->canReviewCourseRegistrationOf($submission->student),
|
||||
403
|
||||
);
|
||||
|
||||
$this->service->approve($submission);
|
||||
|
||||
@ -175,8 +174,6 @@ public function approve(CourseRegistrationSubmission $submission): RedirectRespo
|
||||
|
||||
public function reject(RejectCourseRegistrationRequest $request, CourseRegistrationSubmission $submission): RedirectResponse
|
||||
{
|
||||
abort_unless(auth()->user()->canReviewCourseRegistrationOf($submission->student), 403);
|
||||
|
||||
$this->service->reject($submission, $request->validated('reason'));
|
||||
|
||||
return Inertia::flash('toast', ['type' => 'success', 'message' => 'KRS berhasil ditolak.'])->back();
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Requests\Admin\Manage;
|
||||
namespace App\Http\Requests\Admin\Manage\CourseRegistration;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
@ -8,7 +8,8 @@ class RejectCourseRegistrationRequest extends FormRequest
|
||||
{
|
||||
public function authorize(): bool
|
||||
{
|
||||
return $this->user()->can('reject-course-registrations');
|
||||
return $this->user()->can('reject-course-registrations')
|
||||
&& $this->user()->canReviewCourseRegistrationOf($this->route('submission')->student);
|
||||
}
|
||||
|
||||
public function rules(): array
|
||||
@ -17,11 +18,4 @@ public function rules(): array
|
||||
'reason' => ['required', 'string', 'max:1000'],
|
||||
];
|
||||
}
|
||||
|
||||
public function messages(): array
|
||||
{
|
||||
return [
|
||||
'reason.required' => 'Alasan penolakan wajib diisi.',
|
||||
];
|
||||
}
|
||||
}
|
||||
@ -1,14 +1,14 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Requests\Admin\Manage;
|
||||
namespace App\Http\Requests\Admin\Manage\CourseRegistration;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class SignCourseRegistrationRequest extends FormRequest
|
||||
class SignAsAdvisorCourseRegistrationRequest extends FormRequest
|
||||
{
|
||||
public function authorize(): bool
|
||||
{
|
||||
return true;
|
||||
return $this->user()->isAdvisorOf($this->route('student'));
|
||||
}
|
||||
|
||||
public function rules(): array
|
||||
@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Requests\Admin\Manage\CourseRegistration;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class SignAsDepartmentLeaderCourseRegistrationRequest extends FormRequest
|
||||
{
|
||||
public function authorize(): bool
|
||||
{
|
||||
return $this->user()->isDepartmentLeaderOf($this->route('student'));
|
||||
}
|
||||
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'signature' => ['required', 'file', 'image', 'max:2048'],
|
||||
];
|
||||
}
|
||||
}
|
||||
@ -1,16 +1,17 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Requests\Admin\Manage;
|
||||
namespace App\Http\Requests\Admin\Manage\CourseRegistration;
|
||||
|
||||
use App\Enums\UserRole;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class SaveCourseRegistrationRequest extends FormRequest
|
||||
class UpdateCourseRegistrationRequest extends FormRequest
|
||||
{
|
||||
public function authorize(): bool
|
||||
{
|
||||
$student = $this->route('student');
|
||||
|
||||
return $this->user()->hasRole('mahasiswa')
|
||||
return $this->user()->hasRole(UserRole::Mahasiswa->value)
|
||||
&& $this->user()->student?->id === $student?->id;
|
||||
}
|
||||
|
||||
@ -13,7 +13,7 @@
|
||||
use Spatie\MediaLibrary\InteractsWithMedia;
|
||||
|
||||
#[Guarded(['id'])]
|
||||
#[Appends(['signature_url', 'kaprodi_signature_url', 'advisor_signature_url'])]
|
||||
#[Appends(['signature_url', 'department_leader_signature_url', 'advisor_signature_url'])]
|
||||
class CourseRegistrationSubmission extends Model implements HasMedia
|
||||
{
|
||||
use InteractsWithMedia;
|
||||
@ -31,7 +31,7 @@ protected function casts(): array
|
||||
public function registerMediaCollections(): void
|
||||
{
|
||||
$this->addMediaCollection('signature')->singleFile();
|
||||
$this->addMediaCollection('kaprodi_signature')->singleFile();
|
||||
$this->addMediaCollection('department_leader_signature')->singleFile();
|
||||
$this->addMediaCollection('advisor_signature')->singleFile();
|
||||
}
|
||||
|
||||
@ -67,10 +67,10 @@ protected function signatureUrl(): Attribute
|
||||
);
|
||||
}
|
||||
|
||||
protected function kaprodiSignatureUrl(): Attribute
|
||||
protected function departmentLeaderSignatureUrl(): Attribute
|
||||
{
|
||||
return Attribute::make(
|
||||
get: fn () => $this->getFirstMediaUrl('kaprodi_signature') ?: null,
|
||||
get: fn () => $this->getFirstMediaUrl('department_leader_signature') ?: null,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@ -2,6 +2,7 @@
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Enums\UserRole;
|
||||
use Illuminate\Database\Eloquent\Attributes\Appends;
|
||||
use Illuminate\Database\Eloquent\Attributes\Guarded;
|
||||
use Illuminate\Database\Eloquent\Attributes\Hidden;
|
||||
@ -110,38 +111,38 @@ public function uploads(): HasMany
|
||||
|
||||
public function isAdvisorOf(Student $student): bool
|
||||
{
|
||||
return $this->hasRole('dosen') && $this->lecturer && $student->academic_advisor_id === $this->lecturer->id;
|
||||
return $this->hasRole(UserRole::Dosen->value) && $this->lecturer && $student->academic_advisor_id === $this->lecturer->id;
|
||||
}
|
||||
|
||||
public function isKaprodiOf(Student $student): bool
|
||||
public function isDepartmentLeaderOf(Student $student): bool
|
||||
{
|
||||
return $this->hasRole('kaprodi') && $this->lecturer && $student->department?->currentLeader?->lecturer_id === $this->lecturer->id;
|
||||
return $this->hasRole(UserRole::Kaprodi->value) && $this->lecturer && $student->department?->currentLeader?->lecturer_id === $this->lecturer->id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gates page access: the student's academic advisor, the kaprodi of
|
||||
* their department, or staff roles may view a student's KRS.
|
||||
* Gates page access: the student's academic advisor, the department
|
||||
* leader of their department, or staff roles may view a student's KRS.
|
||||
*/
|
||||
public function canAccessCourseRegistrationOf(Student $student): bool
|
||||
{
|
||||
if ($this->hasRole('dosen') || $this->hasRole('kaprodi')) {
|
||||
return $this->isAdvisorOf($student) || $this->isKaprodiOf($student);
|
||||
if ($this->hasRole(UserRole::Dosen->value) || $this->hasRole(UserRole::Kaprodi->value)) {
|
||||
return $this->isAdvisorOf($student) || $this->isDepartmentLeaderOf($student);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Only the student's academic advisor may approve/reject; the kaprodi
|
||||
* signs but does not decide. Other staff roles retain override access.
|
||||
* Only the student's academic advisor may approve/reject; the department
|
||||
* leader signs but does not decide. Other staff roles retain override access.
|
||||
*/
|
||||
public function canReviewCourseRegistrationOf(Student $student): bool
|
||||
{
|
||||
if ($this->hasRole('dosen')) {
|
||||
if ($this->hasRole(UserRole::Dosen->value)) {
|
||||
return $this->isAdvisorOf($student);
|
||||
}
|
||||
|
||||
if ($this->hasRole('kaprodi')) {
|
||||
if ($this->hasRole(UserRole::Kaprodi->value)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@ -4,6 +4,7 @@
|
||||
|
||||
use App\Enums\RegistrationStatus;
|
||||
use App\Enums\StudentStatus;
|
||||
use App\Enums\UserRole;
|
||||
use App\Models\AcademicTerm;
|
||||
use App\Models\ClassEnrollment;
|
||||
use App\Models\Course;
|
||||
@ -12,6 +13,7 @@
|
||||
use App\Models\CourseRegistrationSubmission;
|
||||
use App\Models\Department;
|
||||
use App\Models\Student;
|
||||
use App\Models\User;
|
||||
use App\Services\Admin\Master\AcademicTermService;
|
||||
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
|
||||
use Illuminate\Http\UploadedFile;
|
||||
@ -24,22 +26,12 @@ public function __construct(
|
||||
private readonly AcademicTermService $academicTermService,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* @return Collection<int, Department>
|
||||
*/
|
||||
public function departmentSummary(): Collection
|
||||
public function paginated(User $user, int $perPage = 25, string $search = '', ?int $departmentId = null): LengthAwarePaginator
|
||||
{
|
||||
return Department::query()
|
||||
->select('id', 'name')
|
||||
->orderBy('name')
|
||||
->get();
|
||||
}
|
||||
$isDepartmentLeader = $user->hasRole(UserRole::Kaprodi->value);
|
||||
$advisorLecturerId = ! $isDepartmentLeader && $user->hasRole(UserRole::Dosen->value) ? $user->lecturer?->id : null;
|
||||
$ledDepartmentIds = $isDepartmentLeader ? $user->ledDepartmentIds() : null;
|
||||
|
||||
/**
|
||||
* @param array<int, int>|null $ledDepartmentIds Restricts results to a kaprodi's own department(s).
|
||||
*/
|
||||
public function paginated(int $perPage = 25, string $search = '', ?int $departmentId = null, ?int $advisorLecturerId = null, ?array $ledDepartmentIds = null): LengthAwarePaginator
|
||||
{
|
||||
$paginator = Student::query()
|
||||
->select(['students.id', 'students.user_id', 'students.student_number', 'students.department_id'])
|
||||
->join('departments', 'departments.id', '=', 'students.department_id')
|
||||
@ -219,11 +211,11 @@ public function saveRegistrations(
|
||||
});
|
||||
}
|
||||
|
||||
public function signAsKaprodi(CourseRegistrationSubmission $submission, UploadedFile $signature): CourseRegistrationSubmission
|
||||
public function signAsDepartmentLeader(CourseRegistrationSubmission $submission, UploadedFile $signature): CourseRegistrationSubmission
|
||||
{
|
||||
$this->assertSignable($submission);
|
||||
|
||||
$submission->addMedia($signature)->toMediaCollection('kaprodi_signature');
|
||||
$submission->addMedia($signature)->toMediaCollection('department_leader_signature');
|
||||
|
||||
return $submission;
|
||||
}
|
||||
@ -246,7 +238,7 @@ private function assertSignable(CourseRegistrationSubmission $submission): void
|
||||
public function approve(CourseRegistrationSubmission $submission): CourseRegistrationSubmission
|
||||
{
|
||||
abort_unless($submission->status === RegistrationStatus::Submitted, 403);
|
||||
abort_if($submission->kaprodi_signature_url === null, 422, 'Ketua Program Studi belum menandatangani KRS ini.');
|
||||
abort_if($submission->department_leader_signature_url === null, 422, 'Ketua Program Studi belum menandatangani KRS ini.');
|
||||
abort_if($submission->advisor_signature_url === null, 422, 'Silakan tanda tangani KRS ini terlebih dahulu.');
|
||||
|
||||
return DB::transaction(function () use ($submission) {
|
||||
@ -299,4 +291,14 @@ public function reject(CourseRegistrationSubmission $submission, string $reason)
|
||||
return $submission;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Collection<int, Department>
|
||||
*/
|
||||
public function departmentSummary(): Collection
|
||||
{
|
||||
return Department::query()
|
||||
->orderBy('name')
|
||||
->get(['id', 'name']);
|
||||
}
|
||||
}
|
||||
|
||||
@ -25,9 +25,9 @@ import { Textarea } from '@/components/ui/textarea';
|
||||
import {
|
||||
advisor_signature,
|
||||
approve,
|
||||
kaprodi_signature,
|
||||
department_leader_signature,
|
||||
reject,
|
||||
save,
|
||||
update,
|
||||
} from '@/routes/admin/manage/course-registrations';
|
||||
import type {
|
||||
CourseRegistrationSubmission,
|
||||
@ -238,7 +238,7 @@ function SemesterCard({
|
||||
isSemesterOpen,
|
||||
backHref,
|
||||
canReview,
|
||||
canSignAsKaprodi,
|
||||
canSignAsDepartmentLeader,
|
||||
canSignAsAdvisor,
|
||||
}: {
|
||||
semester: number | null;
|
||||
@ -247,14 +247,14 @@ function SemesterCard({
|
||||
isSemesterOpen: boolean;
|
||||
backHref: string | null;
|
||||
canReview: boolean;
|
||||
canSignAsKaprodi: boolean;
|
||||
canSignAsDepartmentLeader: boolean;
|
||||
canSignAsAdvisor: boolean;
|
||||
}) {
|
||||
const [rejecting, setRejecting] = useState(false);
|
||||
|
||||
const student = submission.student;
|
||||
const department = student?.department ?? null;
|
||||
const kaprodi = department?.current_leader?.lecturer ?? null;
|
||||
const departmentLeader = department?.current_leader?.lecturer ?? null;
|
||||
const advisor = student?.academic_advisor ?? null;
|
||||
|
||||
const isViewOnly = backHref !== null;
|
||||
@ -267,11 +267,13 @@ function SemesterCard({
|
||||
canReview && submission.status === 'submitted' && !notYetSubmitted;
|
||||
const canReviewersSign =
|
||||
submission.status === 'submitted' && !notYetSubmitted;
|
||||
const missingKaprodiSignature = !submission.kaprodi_signature_url;
|
||||
const missingDepartmentLeaderSignature =
|
||||
!submission.department_leader_signature_url;
|
||||
const missingOwnSignature =
|
||||
(canSignAsKaprodi && !submission.kaprodi_signature_url) ||
|
||||
(canSignAsDepartmentLeader &&
|
||||
!submission.department_leader_signature_url) ||
|
||||
(canSignAsAdvisor && !submission.advisor_signature_url);
|
||||
const cannotApprove = missingKaprodiSignature || missingOwnSignature;
|
||||
const cannotApprove = missingDepartmentLeaderSignature || missingOwnSignature;
|
||||
|
||||
const totalCredits = submission.course_registrations.reduce(
|
||||
(sum, registration) =>
|
||||
@ -291,7 +293,7 @@ function SemesterCard({
|
||||
|
||||
return (
|
||||
<Form
|
||||
action={save([studentId, semesterKey(semester)])}
|
||||
action={update([studentId, semesterKey(semester)])}
|
||||
options={{ preserveScroll: true }}
|
||||
className="flex flex-col gap-6"
|
||||
>
|
||||
@ -471,25 +473,28 @@ function SemesterCard({
|
||||
<p>Ketua Program Studi,</p>
|
||||
<ReviewerSignatureField
|
||||
signatureUrl={
|
||||
submission.kaprodi_signature_url
|
||||
submission.department_leader_signature_url
|
||||
}
|
||||
alt="Tanda tangan Ketua Program Studi"
|
||||
canSign={
|
||||
canSignAsKaprodi &&
|
||||
canSignAsDepartmentLeader &&
|
||||
canReviewersSign
|
||||
}
|
||||
submitUrl={kaprodi_signature.url([
|
||||
studentId,
|
||||
semesterKey(semester),
|
||||
])}
|
||||
submitUrl={department_leader_signature.url(
|
||||
[
|
||||
studentId,
|
||||
semesterKey(semester),
|
||||
],
|
||||
)}
|
||||
/>
|
||||
<p className="font-semibold">
|
||||
{kaprodi?.user?.profile
|
||||
{departmentLeader?.user?.profile
|
||||
?.full_name ?? '-'}
|
||||
</p>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
NIDN.{' '}
|
||||
{kaprodi?.lecturer_number ?? '-'}
|
||||
{departmentLeader?.lecturer_number ??
|
||||
'-'}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@ -579,7 +584,7 @@ function SemesterCard({
|
||||
</div>
|
||||
{cannotApprove && (
|
||||
<p className="text-xs text-muted-foreground">
|
||||
{missingKaprodiSignature
|
||||
{missingDepartmentLeaderSignature
|
||||
? 'Menunggu tanda tangan Ketua Program Studi sebelum dapat disetujui.'
|
||||
: 'Silakan tanda tangani KRS ini terlebih dahulu sebelum menyetujui.'}
|
||||
</p>
|
||||
@ -628,7 +633,7 @@ type Props = {
|
||||
hasActiveTerm: boolean;
|
||||
backHref: string | null;
|
||||
canReview: boolean;
|
||||
canSignAsKaprodi: boolean;
|
||||
canSignAsDepartmentLeader: boolean;
|
||||
canSignAsAdvisor: boolean;
|
||||
};
|
||||
|
||||
@ -640,7 +645,7 @@ export default function CourseRegistrationDetail({
|
||||
hasActiveTerm,
|
||||
backHref,
|
||||
canReview,
|
||||
canSignAsKaprodi,
|
||||
canSignAsDepartmentLeader,
|
||||
canSignAsAdvisor,
|
||||
}: Props) {
|
||||
const [activeSemester, setActiveSemester] = useState(() =>
|
||||
@ -712,7 +717,7 @@ export default function CourseRegistrationDetail({
|
||||
)}
|
||||
backHref={backHref}
|
||||
canReview={canReview}
|
||||
canSignAsKaprodi={canSignAsKaprodi}
|
||||
canSignAsDepartmentLeader={canSignAsDepartmentLeader}
|
||||
canSignAsAdvisor={canSignAsAdvisor}
|
||||
/>
|
||||
) : (
|
||||
|
||||
@ -78,7 +78,7 @@ export type CourseRegistrationSubmission = {
|
||||
rejection_reason: string | null;
|
||||
signed_at: string;
|
||||
signature_url: string | null;
|
||||
kaprodi_signature_url: string | null;
|
||||
department_leader_signature_url: string | null;
|
||||
advisor_signature_url: string | null;
|
||||
has_registrations: boolean;
|
||||
student: CourseRegistrationSubmissionStudent | null;
|
||||
|
||||
@ -107,8 +107,8 @@
|
||||
Route::patch('submissions/{submission}/approve', [CourseRegistrationController::class, 'approve'])->name('approve')->middleware('permission:approve-course-registrations');
|
||||
Route::patch('submissions/{submission}/reject', [CourseRegistrationController::class, 'reject'])->name('reject')->middleware('permission:reject-course-registrations');
|
||||
Route::get('{student}', [CourseRegistrationController::class, 'show'])->name('show')->middleware('permission:view-course-registrations');
|
||||
Route::put('{student}/{semester}', [CourseRegistrationController::class, 'save'])->name('save')->middleware('permission:view-course-registrations');
|
||||
Route::patch('{student}/{semester}/kaprodi-signature', [CourseRegistrationController::class, 'signAsKaprodi'])->name('kaprodi_signature')->middleware('permission:view-course-registrations');
|
||||
Route::put('{student}/{semester}', [CourseRegistrationController::class, 'update'])->name('update')->middleware('permission:view-course-registrations');
|
||||
Route::patch('{student}/{semester}/department-leader-signature', [CourseRegistrationController::class, 'signAsDepartmentLeader'])->name('department_leader_signature')->middleware('permission:view-course-registrations');
|
||||
Route::patch('{student}/{semester}/advisor-signature', [CourseRegistrationController::class, 'signAsAdvisor'])->name('advisor_signature')->middleware('permission:approve-course-registrations');
|
||||
});
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user