schema([ TextInput::make('ip_address') ->label('IP Address') ->required() ->ip() ->maxLength(45), TextInput::make('reason') ->label('Reason') ->required() ->maxLength(255), Textarea::make('notes') ->label('Notes') ->rows(3) ->maxLength(1000), Toggle::make('is_permanent') ->label('Permanent Block') ->default(false) ->reactive(), DateTimePicker::make('blocked_until') ->label('Blocked Until') ->nullable() ->hidden(fn (Get $get) => $get('is_permanent')), Select::make('blocked_by') ->label('Blocked By') ->relationship('blockedBy', 'email') ->nullable() ->default(fn () => Auth::id()), ]); } public static function table(Table $table): Table { return $table ->columns([ TextColumn::make('ip_address') ->label('IP Address') ->searchable() ->sortable() ->copyable(), TextColumn::make('reason') ->label('Reason') ->searchable() ->limit(50), BadgeColumn::make('is_permanent') ->label('Type') ->getStateUsing(fn (BlockedIP $record) => $record->is_permanent ? 'Permanent' : 'Temporary') ->colors([ 'danger' => 'Permanent', 'warning' => 'Temporary', ]), TextColumn::make('blocked_until') ->label('Until') ->dateTime() ->sortable(), BooleanColumn::make('is_active') ->label('Active'), TextColumn::make('failed_login_attempts') ->label('Failed Attempts') ->sortable(), TextColumn::make('blockedBy.email') ->label('Blocked By') ->sortable() ->default('System'), TextColumn::make('blocked_at') ->label('Blocked At') ->dateTime() ->sortable(), ]) ->actions([ EditAction::make(), Action::make('unblock') ->label('Unblock IP') ->icon('heroicon-m-check') ->color('success') ->action(fn (BlockedIP $record) => $record->unblock(Auth::id())) ->requiresConfirmation() ->visible(fn (BlockedIP $record) => $record->is_active), ]) ->bulkActions([ BulkActionGroup::make([ DeleteBulkAction::make(), ]), ]) ->defaultSort('blocked_at', 'desc'); } public static function getPages(): array { return [ 'index' => Pages\ListBlockedIPs::route('/'), 'create' => Pages\CreateBlockedIP::route('/create'), 'edit' => Pages\EditBlockedIP::route('/{record}/edit'), ]; } }