Replace custom HTML activity log table with Filament EmbeddedTable

The activity tab on the user Logs page used a raw HTML table with
Tailwind classes. This replaces it with a proper Filament embedded
table widget (ActivityLogTable) for consistent styling, pagination,
badges, and dark mode support.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-07 16:50:49 +00:00
parent 8acc55a799
commit c6f5b6cab8
3 changed files with 82 additions and 46 deletions

View File

@@ -4,7 +4,7 @@ declare(strict_types=1);
namespace App\Filament\Jabali\Pages; namespace App\Filament\Jabali\Pages;
use App\Models\AuditLog; use App\Filament\Jabali\Widgets\ActivityLogTable;
use App\Services\Agent\AgentClient; use App\Services\Agent\AgentClient;
use BackedEnum; use BackedEnum;
use Filament\Actions\Action; use Filament\Actions\Action;
@@ -16,6 +16,7 @@ use Filament\Notifications\Notification;
use Filament\Pages\Page; use Filament\Pages\Page;
use Filament\Schemas\Components\Tabs; use Filament\Schemas\Components\Tabs;
use Filament\Schemas\Components\Tabs\Tab; use Filament\Schemas\Components\Tabs\Tab;
use Filament\Schemas\Components\EmbeddedTable;
use Filament\Schemas\Components\View; use Filament\Schemas\Components\View;
use Filament\Schemas\Schema; use Filament\Schemas\Schema;
use Illuminate\Contracts\Support\Htmlable; use Illuminate\Contracts\Support\Htmlable;
@@ -119,7 +120,7 @@ class Logs extends Page implements HasActions, HasForms
'activity' => Tab::make(__('Activity Log')) 'activity' => Tab::make(__('Activity Log'))
->icon('heroicon-o-clipboard-document-list') ->icon('heroicon-o-clipboard-document-list')
->schema([ ->schema([
View::make('filament.jabali.pages.logs-tab-activity'), EmbeddedTable::make(ActivityLogTable::class),
]), ]),
]), ]),
]); ]);
@@ -227,15 +228,6 @@ class Logs extends Page implements HasActions, HasForms
->send(); ->send();
} }
public function getActivityLogs()
{
return AuditLog::query()
->where('user_id', Auth::id())
->latest()
->limit(50)
->get();
}
public function generateStats(): void public function generateStats(): void
{ {
if (! $this->selectedDomain) { if (! $this->selectedDomain) {

View File

@@ -0,0 +1,79 @@
<?php
declare(strict_types=1);
namespace App\Filament\Jabali\Widgets;
use App\Models\AuditLog;
use Filament\Actions\Concerns\InteractsWithActions;
use Filament\Actions\Contracts\HasActions;
use Filament\Schemas\Concerns\InteractsWithSchemas;
use Filament\Schemas\Contracts\HasSchemas;
use Filament\Tables\Columns\TextColumn;
use Filament\Tables\Concerns\InteractsWithTable;
use Filament\Tables\Contracts\HasTable;
use Filament\Tables\Table;
use Illuminate\Support\Facades\Auth;
use Livewire\Component;
class ActivityLogTable extends Component implements HasTable, HasSchemas, HasActions
{
use InteractsWithTable;
use InteractsWithSchemas;
use InteractsWithActions;
public function table(Table $table): Table
{
return $table
->query(
AuditLog::query()
->where('user_id', Auth::id())
->latest()
)
->columns([
TextColumn::make('created_at')
->label(__('Time'))
->dateTime('M d, H:i')
->color('gray'),
TextColumn::make('category')
->label(__('Category'))
->badge()
->color(fn (string $state): string => match ($state) {
'domain' => 'info',
'email' => 'primary',
'database' => 'warning',
'auth' => 'gray',
'firewall' => 'danger',
'service' => 'success',
default => 'gray',
}),
TextColumn::make('action')
->label(__('Action'))
->badge()
->color(fn (string $state): string => match ($state) {
'create', 'created' => 'success',
'update', 'updated' => 'warning',
'delete', 'deleted' => 'danger',
'login' => 'info',
default => 'gray',
}),
TextColumn::make('description')
->label(__('Description'))
->limit(60)
->wrap(),
TextColumn::make('ip_address')
->label(__('IP'))
->color('gray'),
])
->defaultPaginationPageOption(25)
->striped()
->emptyStateHeading(__('No activity recorded yet'))
->emptyStateDescription(__('Recent actions performed in your account will appear here.'))
->emptyStateIcon('heroicon-o-clipboard-document-list');
}
public function render()
{
return $this->getTable()->render();
}
}

View File

@@ -1,35 +0,0 @@
<x-filament::section class="mt-4" icon="heroicon-o-clipboard-document-list">
<x-slot name="heading">{{ __('Activity Log') }}</x-slot>
<x-slot name="description">{{ __('Recent actions performed in your account.') }}</x-slot>
<div class="overflow-x-auto">
<table class="min-w-full divide-y divide-gray-200 dark:divide-gray-700">
<thead class="bg-gray-50 dark:bg-gray-800">
<tr>
<th class="px-4 py-3 text-left fi-section-header-heading">{{ __('Time') }}</th>
<th class="px-4 py-3 text-left fi-section-header-heading">{{ __('Category') }}</th>
<th class="px-4 py-3 text-left fi-section-header-heading">{{ __('Action') }}</th>
<th class="px-4 py-3 text-left fi-section-header-heading">{{ __('Description') }}</th>
<th class="px-4 py-3 text-left fi-section-header-heading">{{ __('IP') }}</th>
</tr>
</thead>
<tbody class="divide-y divide-gray-200 dark:divide-gray-700">
@forelse($this->getActivityLogs() as $log)
<tr>
<td class="px-4 py-3 fi-section-header-description">{{ $log->created_at?->format('Y-m-d H:i') }}</td>
<td class="px-4 py-3 fi-section-header-description">{{ $log->category }}</td>
<td class="px-4 py-3 fi-section-header-description">{{ $log->action }}</td>
<td class="px-4 py-3 fi-section-header-description">{{ $log->description }}</td>
<td class="px-4 py-3 fi-section-header-description">{{ $log->ip_address }}</td>
</tr>
@empty
<tr>
<td colspan="5" class="px-4 py-6 text-center fi-section-header-description">
{{ __('No activity recorded yet.') }}
</td>
</tr>
@endforelse
</tbody>
</table>
</div>
</x-filament::section>