user()?->can('audit.view') ?? false; } public static function canCreate(): bool { return false; } public static function canEdit($record): bool { return false; } public static function canDelete($record): bool { return false; } public static function form(Form $form): Form { return $form->schema([]); } public static function table(Table $table): Table { return $table ->columns([ Tables\Columns\TextColumn::make('created_at') ->label('Date/Time') ->dateTime('M j, Y H:i:s') ->sortable(), Tables\Columns\TextColumn::make('user.name') ->label('User') ->default('System') ->searchable() ->sortable(), Tables\Columns\BadgeColumn::make('event') ->label('Action') ->colors([ 'success' => 'created', 'warning' => 'updated', 'danger' => 'deleted', 'info' => 'restored', ]) ->formatStateUsing(fn (string $state): string => ucfirst($state)), Tables\Columns\TextColumn::make('auditable_type') ->label('Model') ->formatStateUsing(fn (string $state): string => class_basename($state)) ->sortable(), Tables\Columns\TextColumn::make('auditable_id') ->label('Record ID') ->sortable(), Tables\Columns\TextColumn::make('ip_address') ->label('IP Address') ->toggleable(isToggledHiddenByDefault: true), Tables\Columns\TextColumn::make('url') ->label('URL') ->limit(30) ->toggleable(isToggledHiddenByDefault: true), ]) ->defaultSort('created_at', 'desc') ->filters([ Tables\Filters\SelectFilter::make('event') ->label('Action') ->options([ 'created' => 'Created', 'updated' => 'Updated', 'deleted' => 'Deleted', 'restored' => 'Restored', ]), Tables\Filters\SelectFilter::make('user_id') ->label('User') ->relationship('user', 'name') ->searchable() ->preload(), Tables\Filters\SelectFilter::make('auditable_type') ->label('Model') ->options(fn () => Audit::query() ->distinct() ->pluck('auditable_type') ->mapWithKeys(fn ($type) => [$type => class_basename($type)]) ->toArray() ), Tables\Filters\Filter::make('created_at') ->form([ Forms\Components\DatePicker::make('from') ->label('From Date'), Forms\Components\DatePicker::make('until') ->label('Until Date'), ]) ->query(function (Builder $query, array $data): Builder { return $query ->when( $data['from'], fn (Builder $query, $date): Builder => $query->whereDate('created_at', '>=', $date), ) ->when( $data['until'], fn (Builder $query, $date): Builder => $query->whereDate('created_at', '<=', $date), ); }) ->indicateUsing(function (array $data): array { $indicators = []; if ($data['from'] ?? null) { $indicators['from'] = 'From: ' . $data['from']; } if ($data['until'] ?? null) { $indicators['until'] = 'Until: ' . $data['until']; } return $indicators; }), ]) ->actions([ Tables\Actions\ViewAction::make(), ]) ->bulkActions([]) ->poll('60s'); } public static function infolist(Infolist $infolist): Infolist { return $infolist ->schema([ Infolists\Components\Section::make('Audit Details') ->schema([ Infolists\Components\Grid::make(3) ->schema([ Infolists\Components\TextEntry::make('created_at') ->label('Date/Time') ->dateTime('F j, Y H:i:s'), Infolists\Components\TextEntry::make('user.name') ->label('User') ->default('System'), Infolists\Components\TextEntry::make('event') ->label('Action') ->badge() ->color(fn (string $state): string => match ($state) { 'created' => 'success', 'updated' => 'warning', 'deleted' => 'danger', 'restored' => 'info', default => 'gray', }) ->formatStateUsing(fn (string $state): string => ucfirst($state)), ]), Infolists\Components\Grid::make(3) ->schema([ Infolists\Components\TextEntry::make('auditable_type') ->label('Model') ->formatStateUsing(fn (string $state): string => class_basename($state)), Infolists\Components\TextEntry::make('auditable_id') ->label('Record ID'), Infolists\Components\TextEntry::make('ip_address') ->label('IP Address') ->default('N/A'), ]), Infolists\Components\TextEntry::make('url') ->label('URL') ->columnSpanFull(), ]), Infolists\Components\Section::make('Changes') ->schema([ Infolists\Components\ViewEntry::make('changes') ->view('filament.infolists.entries.audit-changes') ->columnSpanFull(), ]), ]); } public static function getRelations(): array { return []; } public static function getPages(): array { return [ 'index' => Pages\ListAudits::route('/'), 'view' => Pages\ViewAudit::route('/{record}'), ]; } }