mortezamasumi / fb-activity
Filament v5 activity log resource with exports, filters, and Shield integration
Requires
- php: ^8.5
- filament/filament: ^5.0
- mortezamasumi/fb-essentials: ^5.1.1
- spatie/laravel-activitylog: ^4.0
- spatie/laravel-package-tools: ^1.0
Requires (Dev)
- filament/upgrade: ^5.0
- larastan/larastan: ^3.10
- laravel/pint: ^1.30
- orchestra/testbench: ^11.0
- pestphp/pest: ^4.0
- pestphp/pest-plugin-arch: ^4.0
- pestphp/pest-plugin-browser: ^4.0
- pestphp/pest-plugin-laravel: ^4.0
- pestphp/pest-plugin-livewire: ^4.0
- phpstan/phpstan: ^2.2
Suggests
None
Provides
None
Conflicts
None
Replaces
None
- dev-main
- v5.4.0
- v5.3.0
- v5.2.5
- v5.2.4
- v5.2.3
- v5.2.2
- v5.2.1
- v5.2.0
- v5.1.5
- v5.1.4
- v5.1.3
- v5.1.2
- v5.1.1
- v5.1.0
- v5.0.4
- v5.0.3
- v5.0.2
- v5.0.1
- v5.0.0
- v4.2.5
- v4.2.4
- v4.2.3
- v4.2.2
- v4.2.1
- v4.2.0
- v4.1.9
- v4.1.8
- v4.1.7
- v4.1.6
- v4.1.5
- v4.1.4
- v4.1.3
- v4.1.2
- v4.1.1
- v4.1.0
- v4.0.28
- v4.0.27
- v4.0.26
- v4.0.25
- v4.0.24
- v4.0.23
- v4.0.22
- v4.0.21
- v4.0.20
- v4.0.19
- v4.0.18
- v4.0.17
- v4.0.16
- v4.0.15
- v4.0.14
- v4.0.13
- v4.0.12
- v4.0.11
- v4.0.10
- v4.0.9
- v4.0.8
- v4.0.7
- v4.0.6
- v4.0.5
- v4.0.4
- v4.0.3
- v4.0.2
- v4.0.1
- v4.0.0
- v3.0.0
- v2.0.0
- v1.0.0
This package is auto-updated.
Last update: 2026-09-06 07:54:17 UTC
README
A Filament v5 plugin that surfaces spatie/laravel-activitylog records as a read-only admin resource: searchable, filterable table with CSV export, an infolist view page, Persian date handling, and Filament Shield permission integration.
Features
- Activity resource — index and view pages backed by the
activity_logtable, with eager-loaded causer/subject relationships - CSV export — Filament export action with a configurable max row limit and completion notifications
- Table & filters — searchable/sortable columns, a
created_atdate-range filter, and causer search acrossname/first_name/last_name - View page — infolist with human-readable subject names and a rendered
propertiessection - Log scoping — include/exclude activity descriptions by wildcard pattern via config or env
- Permissions —
ViewAny:Activity,View:Activity,Delete:Activity,Export:Activity,ViewAllUsers:Activitywired through Filament Shield - Localized UI — Persian and English translations shipped out of the box
Installation
composer require mortezamasumi/fb-activity
Publish and run the migration:
php artisan vendor:publish --tag="fb-activity-migrations"
php artisan migrate
Publish the config file:
php artisan vendor:publish --tag="fb-activity-config"
Configuration
// config/fb-activity.php return [ 'navigation' => [ 'model_label' => 'fb-activity::fb-activity.navigation.label', 'plural_model_label' => 'fb-activity::fb-activity.navigation.plural_label', 'group' => 'fb-activity::fb-activity.navigation.group', 'parent_item' => null, 'icon' => 'heroicon-o-queue-list', 'active_icon' => 'heroicon-s-queue-list', 'badge' => false, 'badge_tooltip' => null, 'sort' => 20, ], 'export' => [ 'exporter' => '\Mortezamasumi\FbActivity\Resources\Exports\ActivityExporter', 'max_export_rows' => env('ACTIVITY_MAX_EXPORT_ROWS', 3000), ], 'exclude_logs' => env('ACTIVITY_EXCLUDE_LOGS', null), 'include_logs' => env('ACTIVITY_INCLUDE_LOGS', null), 'timezone' => [ 'storage' => env('FB_ACTIVITY_STORAGE_TIMEZONE'), 'display' => env('FB_ACTIVITY_DISPLAY_TIMEZONE'), ], 'subject' => [ 'titles' => [], 'labels' => [], 'urls' => [], 'attribute_cascade' => ['display_name', 'full_name', 'name', 'title'], 'use_filament_record_title' => true, 'show_model_label' => true, 'recover_deleted' => true, 'link' => ['enabled' => true], ], 'causer' => [ 'titles' => [], 'attribute_cascade' => ['display_name', 'full_name', 'name'], ], 'events' => [ 'colors' => [ 'created' => 'success', 'updated' => 'warning', 'deleted' => 'danger', 'restored' => 'info', ], 'icons' => [], ], 'logs' => [ 'colors' => [], ], ];
navigation.*— panel navigation label, group, icons, badge, and sort orderexport.max_export_rows— maximum rows per export (default3000)exclude_logs/include_logs— comma-separated description patterns;*matches any characters and?matches a single character. Include patterns apply asOR(any match is kept), exclude patterns apply asNOT LIKEon top of the include filtertimezone.storage— timezone the storedcreated_atwall-clock values were written in (e.g.UTCfor rows produced by an environment withoutAPP_TIMEZONE);timezone.display— timezone to render in; bothnull= default Laravel behaviorsubject.*/causer.*— smart title resolution (see below)events.colors/events.icons/logs.colors— per-event/log badge colors and icons; unknown events fall back toprimary(events also honor a legacydraft => graymapping)
Smart subject and causer titles
The list page, view page and export render subjects and causers as human titles, not
Model::class + id. Resolution order (first non-empty wins, every step is failure-safe):
- Per-model config override —
fb-activity.subject.titles/causer.titles, keyed by model FQCN. Values may be an attribute/dot-path (evaluated on the model), aClosure fn (Model $subject, Activity $activity): ?string, an invokable class-string, or explicitnullto skip to the cascade. HasActivityTitlecontract — implementMortezamasumi\FbActivity\Contracts\HasActivityTitle::activityTitle(Activity): ?stringon the model for full control (can branch on$activity->event).- Filament record title — when the model is managed by a Filament resource,
Resource::getRecordTitle()/$recordTitleAttributeare used (disable withsubject.use_filament_record_title = false). - Attribute cascade —
subject.attribute_cascade(defaultdisplay_name, full_name, name, title; causer defaults todisplay_name, full_name, name). Attributes are read withgetAttribute(), so accessors and SpatieHasTranslations(per-locale JSON with fallback locale) resolve naturally. - Deleted-subject recovery — when the subject record no longer exists, the same
cascade is applied to the activity's
properties.attributes/properties.old(disable withsubject.recover_deleted = false). - Fallback —
Label #id(e.g.Podcast #42).
Short labels ("Podcast", "Patient", …) come from subject.labels (string or
translation key) → the Filament resource label → the class basename.
Search on the list page matches
subject_type/subject_id(SQL), not the resolved titles. If you want "Podcast" casing instead of Filament's kebab-case label, setsubject.labels[Model::class] => 'Podcast'.
use App\Models\Patient; use Mortezamasumi\FbActivity\Contracts\HasActivityTitle; use Spatie\Activitylog\Models\Activity; // config/fb-activity.php 'subject' => [ 'titles' => [ Patient::class => 'full_name', // attribute or dot-path // Patient::class => fn ($record) => $record->file_number, // Closure ], 'labels' => [ Patient::class => 'patient::patient.navigation.label', ], 'urls' => [ Patient::class => 'patient.patients.view', // route name (record param) // Patient::class => '/patients/{id}/chart', // or a {id} pattern // Patient::class => fn ($record) => ..., // or a Closure ], ], // or on the model: class Patient extends Model implements HasActivityTitle { public function activityTitle(Activity $activity): ?string { return $activity->event === 'deleted' ? $this->getRawOriginal('full_name') : $this->full_name; } }
Subject links
When a URL resolves for the subject (config urls map first, then the Filament
resource's view page if the current panel has one), the subject title on the list
page renders as a link. Disable entirely with subject.link.enabled = false. Route
names with extra parameters should use the Closure form. The destination page enforces
its own access policy; the link is shown regardless.
Timezone reinterpretation
Stored activity timestamps are wall-clock strings with no offset. If some rows were
written by an environment whose app timezone differed from the one you display in
(e.g. a production container without APP_TIMEZONE writing UTC), set:
FB_ACTIVITY_STORAGE_TIMEZONE=UTC FB_ACTIVITY_DISPLAY_TIMEZONE=Asia/Tehran
All rendering (list, view, export) shifts those stored walls into the display timezone, and the created-at date filter converts your display-timezone bounds into storage-timezone days. There is no data migration — the original offset was never recorded, so existing rows can only be reinterpreted, not rewritten.
Usage
Register the plugin in a panel
use Mortezamasumi\FbActivity\FbActivityPlugin; public function panel(Panel $panel): Panel { return $panel ->plugin(FbActivityPlugin::make()); }
Human-readable subject names
use Mortezamasumi\FbActivity\Facades\FbActivity; FbActivity::getSubjectName($record, $subjectType); // 'Podcast' from 'App\Models\Podcast' FbActivity::getSubject($record, $subjectType); // 'Podcast ↣ Episode 42' when the subject resolves
getSubject() resolves the activity's subject model by ID and displays its name, title, or
text attribute (in that order); it falls back to the raw subject_id when the class is unknown.
Permissions
Create the permissions with Filament Shield as Activity permissions and add the resource via
filamentShieldAddResource (done automatically by the service provider):
| Permission | Effect |
|---|---|
ViewAny:Activity |
view the resource index |
View:Activity |
view a single activity |
Delete:Activity |
bulk-delete activities |
Export:Activity |
export activities as CSV |
ViewAllUsers:Activity |
see activities by all users (otherwise only your own) |
Support policy
| PHP | Laravel | Filament |
|---|---|---|
| 8.3 | 12 | 5.x |
Testing
composer test
Contributing
Please see CONTRIBUTING for details.
Security
If you discover a security vulnerability, please review our security policy on how to report it.
Changelog
Please see CHANGELOG for recent changes.
License
The MIT License (MIT). See LICENSE.md for details.