@php $tonePill = function ($result) { return match ($result) { 'failure' => [ 'cls' => 'bg-accent-coral/10 text-accent-coral border border-accent-coral/30', 'dot' => 'bg-accent-coral', ], 'warning' => ['cls' => 'bg-accent-amber/15 text-accent-amber', 'dot' => 'bg-accent-amber'], default => ['cls' => 'bg-wa-mint text-wa-deep', 'dot' => 'bg-wa-green'], }; }; $exportQuery = http_build_query( array_filter( [ 'q' => $q, 'event' => $event, 'result' => $result, 'layer' => $layer, 'from' => $from, 'to' => $to, ], fn($v) => $v !== '' && $v !== null, ), ); @endphp
{{ __('Admin') }} {{ __('Audit log') }}
{{ __('Admin - Security & compliance') }}

{{ __('Audit') }} {{ __('log') }}.

{{ __('Review administrator, workspace, billing, campaign, device, and security events across the platform.') }}

{{ __('Events captured') }}
{{ number_format($stats['total']) }}
{{ __('all time') }}
{{ __('Today') }}
{{ number_format($stats['today']) }}
{{ __('since midnight') }}
{{ __('Failures') }}
{{ number_format($stats['failures']) }}
{{ __('all time') }}
{{ __('Platform-level') }}
{{ number_format($stats['platform']) }}
{{ __('admin actions') }}
@php $layerOptions = [ '' => ['label' => 'All', 'count' => $stats['total']], 'platform' => ['label' => 'Platform', 'count' => $stats['platform']], 'workspace' => ['label' => 'Workspace', 'count' => $stats['total'] - $stats['platform']], ]; @endphp @foreach ($layerOptions as $value => $opt) {{ $opt['label'] }} ({{ number_format($opt['count']) }}) @endforeach
{{ __('Event stream') }}

{{ __('Recent platform activity') }}

live {{ $rows->total() }} {{ __('events') }}
{{-- Wrapping in overflow-x-auto means the table can scroll on tight viewports instead of cells crashing into each other. Widths tuned for the ~880px column budget the grid-cols-[minmax(0,1fr)_360px] parent gives us. --}}
@forelse ($rows as $r) @php $actor = $r->actor_user_id ? $actors[$r->actor_user_id] ?? null : null; $ws = $r->workspace_id ? $workspaces[$r->workspace_id] ?? null : null; $tone = $tonePill($r->result); $rowBg = $r->result === 'failure' ? 'bg-accent-coral/5' : ($r->result === 'warning' ? 'bg-accent-amber/5' : ''); $subj = $r->subject_type && $r->subject_id ? $r->subject_type . '#' . $r->subject_id : ''; $label = $r->payload['_label'] ?? $subj; @endphp @empty @endforelse
{{ __('Time') }} {{ __('Actor') }} {{ __('Event') }} {{ __('Resource') }} {{ __('Workspace') }} IP {{ __('Result') }}
{{ $r->created_at?->format('Y-m-d') }}
{{ $r->created_at?->format('H:i:s') }}
{{ $actor?->name ?? ($r->actor_user_id ? 'User #' . $r->actor_user_id : 'System') }}
{{ $actor?->email ?? $r->layer }}
{{ $r->action }}
{{ $label ?: '—' }}
{{ $subj ?: '' }}
{{ $ws?->name ?? '—' }} {{ $r->ip ?? 'internal' }} {{ $r->result }}
{{ __('No audit events match the current filters.') }}
{{ $rows->links() }}
{{ __('Top actors') }}

{{ __('Last 7 days') }}

@php $maxActor = max(array_column($topActors, 'count') ?: [1]); @endphp @forelse ($topActors as $a) @php $pct = max(8, round(($a['count'] / $maxActor) * 100)); @endphp
{{ $a['name'] }}{{ number_format($a['count']) }}
@empty
{{ __('No actor activity in the last 7 days.') }}
@endforelse
{{ __('Event mix') }}

{{ __('By module (7d)') }}

@forelse ($eventMix as $m)
{{ $m['module'] }}{{ $m['pct'] }}%
@empty
{{ __('No events in window.') }}
@endforelse
{{ __('Retention') }}

{{ __('Policy') }}

Audit events are kept {{ __('indefinitely') }}. Use the export button at the top of this page to download a filtered CSV snapshot for compliance review.
{{ __('Download CSV') }}