'boolean', 'last_login_at' => 'datetime', 'password' => 'hashed', 'two_factor_confirmed_at' => 'datetime', ]; } protected function fullName(): Attribute { return Attribute::get(fn () => $this->profile?->full_name ?? $this->username); } public function profile(): HasOne { return $this->hasOne(UserProfile::class); } public function student(): HasOne { return $this->hasOne(Student::class); } public function lecturer(): HasOne { return $this->hasOne(Lecturer::class); } public function notifications(): HasMany { return $this->hasMany(Notification::class); } public function feedbacks(): HasMany { return $this->hasMany(Feedback::class); } public function isAdvisorOf(Student $student): bool { return $this->hasRole('dosen') && $this->lecturer && $student->academic_advisor_id === $this->lecturer->id; } public function isKaprodiOf(Student $student): bool { return $this->hasRole('kaprodi') && $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. */ public function canAccessCourseRegistrationOf(Student $student): bool { if ($this->hasRole('dosen') || $this->hasRole('kaprodi')) { return $this->isAdvisorOf($student) || $this->isKaprodiOf($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. */ public function canReviewCourseRegistrationOf(Student $student): bool { if ($this->hasRole('dosen')) { return $this->isAdvisorOf($student); } if ($this->hasRole('kaprodi')) { return false; } return true; } /** * @return array */ public function ledDepartmentIds(): array { return $this->lecturer ? $this->lecturer->leaderships()->whereNull('ended_at')->pluck('department_id')->all() : []; } }