Commit eea6ad2e authored by Mahmoud Aglan's avatar Mahmoud Aglan

Split attendance page: trainers on top, players below with role-based access

Trainers section (purple header) shows first with max 2-3 records.
Players section (blue header) shows below with full table.

Access control:
- Trainer account: can only mark their own attendance (others disabled)
- Supervisor/head_trainer (role level >= 60): can mark all trainers
Co-Authored-By: 's avatarClaude Opus 4.6 <noreply@anthropic.com>
parent 04dcc7e4
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
use App\Domain\Participant\Models\Participant; use App\Domain\Participant\Models\Participant;
use App\Domain\Training\Models\Enrollment; use App\Domain\Training\Models\Enrollment;
use App\Domain\Training\Models\TrainingSession; use App\Domain\Training\Models\TrainingSession;
use App\Models\User;
use Livewire\Attributes\Layout; use Livewire\Attributes\Layout;
use Livewire\Attributes\Title; use Livewire\Attributes\Title;
use Livewire\Component; use Livewire\Component;
...@@ -55,6 +56,11 @@ public function saveRecordNote(int $recordId): void ...@@ -55,6 +56,11 @@ public function saveRecordNote(int $recordId): void
public function markAs(int $recordId, string $status, AttendanceMarkingService $service): void public function markAs(int $recordId, string $status, AttendanceMarkingService $service): void
{ {
$record = AttendanceRecord::findOrFail($recordId); $record = AttendanceRecord::findOrFail($recordId);
if (!$this->canMarkRecord($record)) {
return;
}
$statusEnum = AttendanceStatus::from($status); $statusEnum = AttendanceStatus::from($status);
$service->markStatus($record, $statusEnum, auth()->user()); $service->markStatus($record, $statusEnum, auth()->user());
} }
...@@ -62,6 +68,11 @@ public function markAs(int $recordId, string $status, AttendanceMarkingService $ ...@@ -62,6 +68,11 @@ public function markAs(int $recordId, string $status, AttendanceMarkingService $
public function markPresent(int $recordId, AttendanceMarkingService $service): void public function markPresent(int $recordId, AttendanceMarkingService $service): void
{ {
$record = AttendanceRecord::findOrFail($recordId); $record = AttendanceRecord::findOrFail($recordId);
if (!$this->canMarkRecord($record)) {
return;
}
$service->markPresent($record, auth()->user()); $service->markPresent($record, auth()->user());
} }
...@@ -72,10 +83,43 @@ public function markAllPresent(AttendanceMarkingService $service): void ...@@ -72,10 +83,43 @@ public function markAllPresent(AttendanceMarkingService $service): void
->get(); ->get();
foreach ($records as $record) { foreach ($records as $record) {
$service->markPresent($record, auth()->user()); if ($this->canMarkRecord($record)) {
$service->markPresent($record, auth()->user());
}
} }
} }
private function canMarkRecord(AttendanceRecord $record): bool
{
$user = auth()->user();
if ($record->subject_type === Participant::class) {
return true;
}
if ($record->subject_type === User::class) {
if ($this->canManageTrainers()) {
return true;
}
return $record->subject_id === $user->id;
}
return true;
}
private function canManageTrainers(): bool
{
$user = auth()->user();
if ($user->is_super_admin) {
return true;
}
$roleLevel = $user->roles->max('level') ?? 0;
return $roleLevel >= 60;
}
public function render() public function render()
{ {
$records = AttendanceRecord::where('training_session_id', $this->session->id) $records = AttendanceRecord::where('training_session_id', $this->session->id)
...@@ -84,6 +128,10 @@ public function render() ...@@ -84,6 +128,10 @@ public function render()
->orderBy('status') ->orderBy('status')
->get(); ->get();
// Split into trainers and participants
$trainerRecords = $records->where('subject_type', User::class)->values();
$participantRecords = $records->where('subject_type', Participant::class)->values();
// Populate per-record notes from DB (only on first load or if not yet set) // Populate per-record notes from DB (only on first load or if not yet set)
foreach ($records as $record) { foreach ($records as $record) {
if (!array_key_exists($record->id, $this->recordNotes)) { if (!array_key_exists($record->id, $this->recordNotes)) {
...@@ -92,8 +140,7 @@ public function render() ...@@ -92,8 +140,7 @@ public function render()
} }
// Detect unpaid/unrenewed participants for red highlight // Detect unpaid/unrenewed participants for red highlight
$participantIds = $records $participantIds = $participantRecords
->where('subject_type', Participant::class)
->pluck('subject_id') ->pluck('subject_id')
->unique() ->unique()
->toArray(); ->toArray();
...@@ -130,11 +177,17 @@ public function render() ...@@ -130,11 +177,17 @@ public function render()
'excused' => $records->where('status', AttendanceStatus::Excused)->count(), 'excused' => $records->where('status', AttendanceStatus::Excused)->count(),
]; ];
$canManageTrainers = $this->canManageTrainers();
return view('livewire.attendance.take-attendance', [ return view('livewire.attendance.take-attendance', [
'trainerRecords' => $trainerRecords,
'participantRecords' => $participantRecords,
'records' => $records, 'records' => $records,
'summary' => $summary, 'summary' => $summary,
'statuses' => AttendanceStatus::cases(), 'statuses' => AttendanceStatus::cases(),
'notPaidParticipantIds' => $notPaidParticipantIds, 'notPaidParticipantIds' => $notPaidParticipantIds,
'canManageTrainers' => $canManageTrainers,
'currentUserId' => auth()->id(),
]); ]);
} }
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment