Filament UI
The package ships three Filament surfaces — an infolist component, a relation manager, and a header action. All three mount the same Relaticle\ActivityLog\Filament\Livewire\ActivityLogLivewire component under the hood, but each exposes a different override mechanism and ships with different defaults. This page covers every knob, plus the URL-bound filter UI that the Livewire component renders for free.
Defaults at a glance
| Surface | groupByDate | perPage | infiniteScroll |
|---|---|---|---|
ActivityLog infolist | true | 3 | true |
ActivityLogRelationManager | true (static $groupByDate) | 20 (static $perPage) | true (static $infiniteScroll) |
ActivityLogAction | true (hardcoded) | builder default (20 from default_per_page) | n/a |
The default_per_page config key only governs $builder->paginate(null) calls made directly without UI — see /essentials/configuration. The infolist and relation manager pass an explicit perPage to the Livewire component and bypass the config value.
Infolist component
Relaticle\ActivityLog\Filament\Infolists\Components\ActivityLog is the standard way to embed the timeline inside an existing Filament infolist (resource view page, custom page, or modal).
use Relaticle\ActivityLog\Filament\Infolists\Components\ActivityLog;
ActivityLog::make('timeline')
->groupByDate()
->perPage(10)
->emptyState('Nothing here yet — give it time.')
->infiniteScroll()
->columnSpanFull(),
Fluent surface
| Method | Purpose |
|---|---|
make(string $name) | Standard Filament constructor. Name is the schema key. |
groupByDate(bool $enabled = true) | Buckets entries by this_week / last_week / week_of <date>. Defaults to true. |
perPage(int $perPage) | Page size for the timeline. Defaults to 3. |
emptyState(string $message) | Replaces the default "No activity yet." message. |
infiniteScroll(bool $enabled = true) | true (default) renders a wire:intersect sentinel that auto-loads on scroll; false renders an explicit "Load more" button. |
record(Model $record) | Bind a non-current record. Alias for model(). Useful when the record can't be inferred from the form context. |
The component extends Filament\Infolists\Components\Entry, so all standard methods (columnSpanFull(), visible(), hidden(), etc.) work as expected.
Relaticle\ActivityLog\Contracts\HasTimeline. The component throws a LogicException at render time if the record is missing or doesn't implement the contract.Relation manager
Relaticle\ActivityLog\Filament\RelationManagers\ActivityLogRelationManager adds a dedicated "Activity" tab to a resource. Register it in your resource's getRelations():
use Relaticle\ActivityLog\Filament\RelationManagers\ActivityLogRelationManager;
public static function getRelations(): array
{
return [
ActivityLogRelationManager::class,
];
}
canViewForRecord() always returns true, so the tab shows for every record. Override the method in a subclass if you need permission gating.
Overriding defaults
The relation manager exposes three public static properties. Set them once from a service provider's boot() to apply project-wide:
use Relaticle\ActivityLog\Filament\RelationManagers\ActivityLogRelationManager;
public function boot(): void
{
ActivityLogRelationManager::$infiniteScroll = false;
ActivityLogRelationManager::$groupByDate = false;
ActivityLogRelationManager::$perPage = 50;
}
getRelationship() returns a self-referential HasOne against the owner record itself, which satisfies Filament's machinery while letting the embedded ActivityLogLivewire component own the data fetching. getTableQuery() returns null for the same reason.Header action
Relaticle\ActivityLog\Filament\Actions\ActivityLogAction opens the timeline in a slide-over from any page header. Register it in getHeaderActions():
use Relaticle\ActivityLog\Filament\Actions\ActivityLogAction;
protected function getHeaderActions(): array
{
return [
ActivityLogAction::make(),
];
}
Defaults
ActivityLogAction::getDefaultName() returns 'activityLog'. Use that name when calling the action in tests (->callAction('activityLog')) or when overriding it in a panel via Action::configureUsing().
setUp() applies the following defaults:
label—__('activity-log::messages.title')icon—heroicon-o-bars-3-bottom-leftcolor—graymodalWidth—Filament\Support\Enums\Width::TwoExtraLargeslideOver()— yesmodalSubmitAction(false)andmodalCancelActionLabel(__('activity-log::messages.close'))— no submit, just a close button
Standard Filament action methods (->visible(), ->color(), ->modalHeading(), ->icon(), etc.) work normally and override the defaults above.
groupByDate is hardcoded to true inside the action's Livewire::make() mount, and perPage is not exposed at all — the Livewire component falls back to its own default of 20. If you need different defaults inside the slide-over, either use the relation manager (configurable via the three statics above) or build a custom action that mounts Livewire::make(ActivityLogLivewire::class, [...]) with your own arguments.Built-in URL filter UI
The ActivityLogLivewire component ships three filters bound to URL query parameters via Livewire's #[Url] attribute:
#[Url(as: 'type')] public ?string $typeFilter = null;
#[Url(as: 'from')] public ?string $fromDate = null;
#[Url(as: 'to')] public ?string $toDate = null;
The timeline view renders the filter chrome (entry-type select, date range, clear button) automatically. State syncs to the URL on every change, which means filtered views are shareable and bookmarkable:
/admin/people/42?type=activity_log&from=2026-01-01&to=2026-04-30
Each filter mutates the underlying Relaticle\ActivityLog\Timeline\TimelineBuilder before the entries are resolved:
typeFilter→$builder->ofType([$typeFilter])fromDateandtoDate→$builder->between(CarbonImmutable::parse($fromDate), CarbonImmutable::parse($toDate))(either or both may benull)
A resetFilters() Livewire action is wired to the clear button — it nulls all three properties and rewinds visibleCount to perPage.
typeFilter value is matched against $entry->type, so only the three real entry-type values resolve (activity_log, related_model, custom). Filtering by 'related_activity_log' matches nothing — see the type taxonomy in /concepts/how-it-works.For programmatic equivalents — pre-applying filters from PHP rather than the URL — see /essentials/refining-the-timeline.