getId() !== 'admin') { return false; } return parent::shouldRegisterNavigation(); } public static function canViewAny(): bool { $user = auth()->user(); if (! $user instanceof User) { return false; } return $user->can('viewAny', AlertDelivery::class); } public static function canView(Model $record): bool { $user = auth()->user(); if (! $user instanceof User || ! $record instanceof AlertDelivery) { return false; } return $user->can('view', $record); } public static function actionSurfaceDeclaration(): ActionSurfaceDeclaration { return ActionSurfaceDeclaration::forResource(ActionSurfaceProfile::ListOnlyReadOnly) ->exempt(ActionSurfaceSlot::ListHeader, 'Read-only history list intentionally has no list-header actions.') ->satisfy(ActionSurfaceSlot::InspectAffordance, ActionSurfaceInspectAffordance::ClickableRow->value) ->exempt(ActionSurfaceSlot::ListRowMoreMenu, 'No secondary row actions are exposed for read-only deliveries.') ->exempt(ActionSurfaceSlot::ListBulkMoreGroup, 'No bulk actions are exposed for read-only deliveries.') ->exempt(ActionSurfaceSlot::ListEmptyState, 'Deliveries are generated by jobs and intentionally have no empty-state CTA.') ->exempt(ActionSurfaceSlot::DetailHeader, 'View page is informational with no mutating header actions.'); } public static function getEloquentQuery(): Builder { $workspaceId = app(WorkspaceContext::class)->currentWorkspaceId(request()); $user = auth()->user(); return parent::getEloquentQuery() ->with(['tenant', 'rule', 'destination']) ->when( ! $user instanceof User, fn (Builder $query): Builder => $query->whereRaw('1 = 0'), ) ->when( ! is_int($workspaceId), fn (Builder $query): Builder => $query->whereRaw('1 = 0'), ) ->when( is_int($workspaceId), fn (Builder $query): Builder => $query->where('workspace_id', $workspaceId), ) ->when( $user instanceof User, fn (Builder $query): Builder => $query->where(function (Builder $q) use ($user): void { $q->whereIn('tenant_id', $user->tenantMemberships()->select('tenant_id')) ->orWhereNull('tenant_id'); }), ) ->when( Filament::getTenant() instanceof Tenant, fn (Builder $query): Builder => $query->where('tenant_id', (int) Filament::getTenant()->getKey()), ) ->latest('id'); } public static function form(Schema $schema): Schema { return $schema; } public static function infolist(Schema $schema): Schema { return $schema ->schema([ Section::make('Delivery') ->schema([ TextEntry::make('status') ->badge() ->formatStateUsing(BadgeRenderer::label(BadgeDomain::AlertDeliveryStatus)) ->color(BadgeRenderer::color(BadgeDomain::AlertDeliveryStatus)) ->icon(BadgeRenderer::icon(BadgeDomain::AlertDeliveryStatus)), TextEntry::make('event_type') ->label('Event') ->badge() ->formatStateUsing(fn (?string $state): string => AlertRuleResource::eventTypeLabel((string) $state)), TextEntry::make('severity') ->badge() ->formatStateUsing(fn (?string $state): string => ucfirst((string) $state)) ->placeholder('—'), TextEntry::make('tenant.name') ->label('Tenant'), TextEntry::make('rule.name') ->label('Rule') ->placeholder('—'), TextEntry::make('destination.name') ->label('Destination') ->placeholder('—'), TextEntry::make('attempt_count') ->label('Attempts'), TextEntry::make('fingerprint_hash') ->label('Fingerprint') ->copyable(), TextEntry::make('send_after') ->dateTime() ->placeholder('—'), TextEntry::make('sent_at') ->dateTime() ->placeholder('—'), TextEntry::make('last_error_code') ->label('Last error code') ->placeholder('—'), TextEntry::make('last_error_message') ->label('Last error message') ->placeholder('—') ->columnSpanFull(), TextEntry::make('created_at') ->dateTime(), TextEntry::make('updated_at') ->dateTime(), ]) ->columns(2) ->columnSpanFull(), Section::make('Payload') ->schema([ ViewEntry::make('payload') ->label('') ->view('filament.infolists.entries.snapshot-json') ->state(fn (AlertDelivery $record): array => is_array($record->payload) ? $record->payload : []) ->columnSpanFull(), ]) ->columnSpanFull(), ]); } public static function table(Table $table): Table { return $table ->defaultSort('id', 'desc') ->recordUrl(fn (AlertDelivery $record): ?string => static::canView($record) ? static::getUrl('view', ['record' => $record]) : null) ->columns([ TextColumn::make('created_at') ->label('Created') ->since(), TextColumn::make('tenant.name') ->label('Tenant') ->searchable(), TextColumn::make('event_type') ->label('Event') ->badge(), TextColumn::make('severity') ->badge() ->formatStateUsing(fn (?string $state): string => ucfirst((string) $state)) ->placeholder('—'), TextColumn::make('status') ->badge() ->formatStateUsing(BadgeRenderer::label(BadgeDomain::AlertDeliveryStatus)) ->color(BadgeRenderer::color(BadgeDomain::AlertDeliveryStatus)) ->icon(BadgeRenderer::icon(BadgeDomain::AlertDeliveryStatus)), TextColumn::make('rule.name') ->label('Rule') ->placeholder('—'), TextColumn::make('destination.name') ->label('Destination') ->placeholder('—'), TextColumn::make('attempt_count') ->label('Attempts'), ]) ->filters([ SelectFilter::make('status') ->options([ AlertDelivery::STATUS_QUEUED => 'Queued', AlertDelivery::STATUS_DEFERRED => 'Deferred', AlertDelivery::STATUS_SENT => 'Sent', AlertDelivery::STATUS_FAILED => 'Failed', AlertDelivery::STATUS_SUPPRESSED => 'Suppressed', AlertDelivery::STATUS_CANCELED => 'Canceled', ]), SelectFilter::make('event_type') ->label('Event type') ->options(function (): array { $options = AlertRuleResource::eventTypeOptions(); $options[AlertDelivery::EVENT_TYPE_TEST] = 'Test'; return $options; }), SelectFilter::make('alert_destination_id') ->label('Destination') ->options(function (): array { $workspaceId = app(WorkspaceContext::class)->currentWorkspaceId(request()); if (! is_int($workspaceId)) { return []; } return AlertDestination::query() ->where('workspace_id', $workspaceId) ->orderBy('name') ->pluck('name', 'id') ->all(); }), ]) ->actions([ ViewAction::make()->label('View'), ]) ->bulkActions([]); } public static function getPages(): array { return [ 'index' => Pages\ListAlertDeliveries::route('/'), 'view' => Pages\ViewAlertDelivery::route('/{record}'), ]; } }