components([ TextInput::make('name') ->required() ->maxLength(255), TextInput::make('email') ->email() ->required() ->maxLength(255), TextInput::make('password') ->password() ->required() ->minLength(6) ->maxLength(255) ->visibleOn('create'), TextInput::make('email_verified_at') ->label('Email Verification Status') ->disabled() ->formatStateUsing(fn ($record): string => $record->email_verified_at ?? '' ? 'Verified at '.$record->email_verified_at->toDateTimeString() : 'Not Verified') ->helperText('Shows whether the user has verified their email address.'), TextInput::make('stripe_id') ->label('Stripe ID') ->disabled() ->helperText('Automatically managed by Stripe'), TextInput::make('pm_type') ->label('Payment Method Type') ->disabled(), TextInput::make('pm_last_four') ->label('Card Last 4 Digits') ->disabled(), DatePicker::make('trial_ends_at') ->label('Trial Ends At') ->disabled() ->displayFormat('Y-m-d H:i:s'), Select::make('level') ->label('User Level') ->options(UserLevel::class) ->enum(UserLevel::class) ->required() ->helperText('Select the appropriate user level. Super Admin has full system access.'), ]); } public static function table(Table $table): Table { return $table ->columns([ TextColumn::make('name')->sortable()->searchable(), TextColumn::make('email')->sortable()->searchable(), IconColumn::make('email_verified_at') ->label('Verified') ->boolean() ->trueIcon('heroicon-o-check-circle') ->falseIcon('heroicon-o-x-circle') ->trueColor('success') ->falseColor('danger') ->getStateUsing(fn ($record): bool => ! is_null($record->email_verified_at)) ->sortable(), TextColumn::make('level') ->label('User Level') ->badge() ->getStateUsing(fn ($record): string => $record->level->getLabel()) ->color(fn ($record): string => $record->level->getColor()) ->icon(fn ($record): string => $record->level->getIcon()) ->sortable(), TextColumn::make('stripe_id')->label('Stripe ID')->copyable(), TextColumn::make('pm_last_four')->label('Card Last 4'), TextColumn::make('trial_ends_at')->label('Trial Ends')->dateTime()->sortable(), ]) ->defaultSort('created_at', 'desc') ->filters([ SelectFilter::make('level') ->label('User Level') ->options(UserLevel::class), SelectFilter::make('subscription_status') ->label('Subscription Status') ->options([ 'subscribed' => 'Has Active Subscription', 'not_subscribed' => 'No Active Subscription', ]) ->query(function ($query, array $data): void { if ($data['value'] === 'subscribed') { $query->whereHas('subscriptions', function ($query): void { $query->where('stripe_status', 'active') ->orWhere('stripe_status', 'trialing'); }); } elseif ($data['value'] === 'not_subscribed') { $query->whereDoesntHave('subscriptions', function ($query): void { $query->where('stripe_status', 'active') ->orWhere('stripe_status', 'trialing'); }); } }), SelectFilter::make('email_verified') ->label('Email Verification') ->options([ 'verified' => 'Verified', 'not_verified' => 'Not Verified', ]) ->query(function ($query, array $data): void { if ($data['value'] === 'verified') { $query->whereNotNull('email_verified_at'); } elseif ($data['value'] === 'not_verified') { $query->whereNull('email_verified_at'); } }), ]) ->recordActions([ EditAction::make(), Action::make('impersonate') ->label('Login as User') ->icon('heroicon-o-arrow-right-on-rectangle') ->color('warning') ->requiresConfirmation() ->modalHeading('Start User Impersonation') ->modalDescription('This will open a new tab where you will be logged in as this user. All actions will be logged for security. Your admin panel will remain open in this tab.') ->modalSubmitActionLabel('Start Impersonation') ->modalCancelActionLabel('Cancel') ->visible(fn (User $record): bool => auth()->user()?->isSuperAdmin() && $record->isNormalUser() && ! app(ImpersonationService::class)->isImpersonating() ) ->url(function (User $record): string { $admin = auth()->user(); $impersonationService = app(ImpersonationService::class); if (! $impersonationService->canImpersonate($admin, $record)) { Notification::make() ->title('Impersonation Failed') ->body('Unable to start impersonation. Please check permissions and try again.') ->danger() ->send(); return '#'; } // Return impersonation URL return route('impersonation.start', $record); }) ->openUrlInNewTab(), ]) ->toolbarActions([ Action::make('stopImpersonation') ->label('Stop Impersonating') ->icon('heroicon-o-arrow-left-on-rectangle') ->color('danger') ->visible(fn (): bool => app(ImpersonationService::class)->isImpersonating()) ->action(function () { $impersonationService = app(ImpersonationService::class); if (! $impersonationService->stopImpersonation(request())) { Notification::make() ->title('Failed to Stop Impersonation') ->body('Unable to stop impersonation session.') ->danger() ->send(); return; } Notification::make() ->title('Impersonation Ended') ->body('You have been returned to your admin account.') ->success() ->send(); return redirect()->to(\Filament\Pages\Dashboard::getUrl()); }), BulkActionGroup::make([ DeleteBulkAction::make(), BulkAction::make('updateLevel') ->label('Update User Level') ->action(function (Collection $records, array $data): void { $newLevel = (int) $data['new_level']; // Prevent bulk updating to Super Admin level for security if ($newLevel === UserLevel::SUPERADMIN->value) { $message = 'Cannot bulk assign Super Admin level for security reasons.'; Log::warning('Attempted bulk Super Admin assignment', [ 'user_ids' => $records->pluck('id')->toArray(), 'attempted_level' => $newLevel, 'ip' => request()->ip(), ]); Notification::make() ->title('Security Restriction') ->body($message) ->danger() ->send(); return; } DB::table('users') ->whereIn('id', $records->pluck('id')) ->update(['level' => $newLevel]); Log::info('Bulk user level update completed', [ 'user_ids' => $records->pluck('id')->toArray(), 'new_level' => $newLevel, 'updated_by' => auth()->id(), ]); Notification::make() ->title('User Level Updated') ->body('The selected users\' levels have been updated successfully.') ->success() ->send(); }) ->icon('heroicon-o-pencil') ->color('primary') ->modalHeading('Select User Level') ->modalDescription('Please choose the user level to apply to the selected users.') ->modalSubmitActionLabel('Update Level') ->modalCancelActionLabel('Cancel') ->form([ Select::make('new_level') ->label('Select User Level') ->options([ UserLevel::NORMALUSER->value => UserLevel::NORMALUSER->getLabel(), UserLevel::BANNEDUSER->value => UserLevel::BANNEDUSER->getLabel(), ]) ->required() ->helperText('Super Admin level cannot be assigned via bulk action for security.'), ]), ]), ]); } public static function getRelations(): array { return [ LogsRelationManager::class, UsageLogsRelationManager::class, ]; } public static function getPages(): array { return [ 'index' => ListUsers::route('/'), 'create' => CreateUser::route('/create'), 'edit' => EditUser::route('/{record}/edit'), ]; } }