components([ Select::make('user_id') ->relationship('user', 'username') ->required() ->searchable(), Select::make('rank_id') ->label('Rank') ->relationship('rank', 'rank_name') ->searchable() ->nullable(), Select::make('team_id') ->label('Team') ->relationship('team', 'rank_name') ->searchable() ->nullable(), Textarea::make('content') ->required() ->columnSpanFull(), ]); } #[\Override] public static function table(Table $table): Table { return $table ->columns([ TextColumn::make('user.username') ->label('User') ->sortable() ->searchable(), TextColumn::make('applied_for') ->label('Applied For') ->state(fn (WebsiteStaffApplications $record) => $record->team_id ? ($record->team->rank_name ?? '-') : ($record->rank->rank_name ?? '-')) ->searchable(query: function ($query, string $search) { $query ->orWhereHas('rank', fn ($q) => $q->where('rank_name', 'like', "%{$search}%")) ->orWhereHas('team', fn ($q) => $q->where('rank_name', 'like', "%{$search}%")); }) ->sortable(), TextColumn::make('status') ->label('Status') ->badge() ->formatStateUsing(fn (?string $state) => ucfirst($state ?? 'pending')) ->color(fn (?string $state) => [ 'pending' => 'warning', 'approved' => 'success', 'rejected' => 'danger', ][$state ?? 'pending'] ?? 'gray') ->sortable(), TextColumn::make('content')->limit(50)->wrap()->sortable(), TextColumn::make('created_at')->dateTime()->sortable(), TextColumn::make('updated_at')->dateTime()->sortable(), ]) ->recordActions([ Action::make('approveTeam') ->label('Approve to Team') ->icon('heroicon-o-check-circle') ->color('success') ->visible(fn (WebsiteStaffApplications $r) => filled($r->team_id) && ($r->status === 'pending' || is_null($r->status))) ->requiresConfirmation() ->modalHeading('Approve to Team') ->modalDescription(function (WebsiteStaffApplications $r): string { $user = $r->user; $targetTeam = optional($r->team)->rank_name ?? '—'; $currentTeam = optional($user?->team)->rank_name; if ($currentTeam && $user?->team_id !== $r->team_id) { return "This user is currently in '{$currentTeam}'. Approving will move them to '{$targetTeam}'. Continue?"; } return "Approve this application and assign the user to '{$targetTeam}'?"; }) ->action(function (WebsiteStaffApplications $r) { $user = $r->user; $team = $r->team; if (! $user || ! $team) { Notification::make() ->danger()->title('Unable to approve') ->body('Missing user or team on this application.') ->send(); return; } if ((int) $user->team_id !== (int) $team->id) { $user->forceFill(['team_id' => $team->id])->save(); } $r->update([ 'status' => 'approved', 'approved_by' => auth()->id(), 'approved_at' => now(), 'rejected_by' => null, 'rejected_at' => null, ]); Notification::make() ->success()->title('Approved') ->body("{$user->username} has been added to '{$team->rank_name}'.") ->send(); }), Action::make('rejectTeam') ->label('Reject') ->icon('heroicon-o-x-circle') ->color('danger') ->visible(fn (WebsiteStaffApplications $r) => filled($r->team_id) && in_array($r->status, ['pending', 'approved', null], true)) ->requiresConfirmation() ->modalHeading('Reject Application') ->modalDescription(function (WebsiteStaffApplications $r): string { $user = $r->user; $teamName = optional($r->team)->rank_name ?? '—'; if ($r->status === 'approved') { return "This will mark the application as rejected and remove {$user->username} from '{$teamName}' (if still on it). Continue?"; } return 'This will mark the application as rejected. Continue?'; }) ->action(function (WebsiteStaffApplications $r) { $user = $r->user; $team = $r->team; if (! $user || ! $team) { Notification::make() ->danger()->title('Unable to reject') ->body('Missing user or team on this application.') ->send(); return; } if ($r->status === 'approved' && (int) $user->team_id === (int) $team->id) { $user->forceFill(['team_id' => null])->save(); } $r->update([ 'status' => 'rejected', 'rejected_by' => auth()->id(), 'rejected_at' => now(), ]); Notification::make() ->success()->title('Rejected') ->body($r->status === 'approved' ? "{$user->username} has been removed from '{$team->rank_name}' and the application marked as rejected." : 'Application has been marked as rejected.') ->send(); }), Action::make('reopen') ->label('Re-open') ->icon('heroicon-o-arrow-path') ->color('warning') ->visible(fn (WebsiteStaffApplications $r) => $r->status === 'rejected') ->requiresConfirmation() ->modalHeading('Re-open Application') ->modalDescription('This will set the application status back to pending.') ->action(function (WebsiteStaffApplications $r) { $r->update([ 'status' => 'pending', 'rejected_by' => null, 'rejected_at' => null, ]); Notification::make() ->success()->title('Re-opened') ->body('Application status set to pending.') ->send(); }), DeleteAction::make(), ]) ->toolbarActions([ DeleteBulkAction::make(), ]); } #[\Override] public static function getPages(): array { return [ 'index' => ListStaffApplications::route('/'), ]; } }