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:
@@ -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) {
|
||||||
|
|||||||
79
app/Filament/Jabali/Widgets/ActivityLogTable.php
Normal file
79
app/Filament/Jabali/Widgets/ActivityLogTable.php
Normal 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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>
|
|
||||||
Reference in New Issue
Block a user