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 :team. Approving will move them to :target. Continue?', ['team' => $currentTeam, 'target' => $targetTeam]); } return __('Approve this application and assign the user to :team?', ['team' => $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(__(':username has been added to :team.', ['username' => $user->username, 'team' => $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 :username from :team. Continue?', ['username' => $user->username, 'team' => $teamName]); } 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' ? __(':username has been removed from :team and the application marked as rejected.', ['username' => $user->username, 'team' => $team->rank_name]) : __('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('/'), ]; } }