query(WebhookEndpoint::query()) ->columns([ TextColumn::make('name') ->label(__('Name')) ->searchable(), TextColumn::make('url') ->label(__('URL')) ->limit(40) ->tooltip(fn (WebhookEndpoint $record) => $record->url), TextColumn::make('events') ->label(__('Events')) ->formatStateUsing(function ($state): string { if (is_array($state)) { return implode(', ', $state); } return (string) $state; }) ->wrap(), IconColumn::make('is_active') ->label(__('Active')) ->boolean(), TextColumn::make('last_triggered_at') ->label(__('Last Triggered')) ->since(), TextColumn::make('last_response_code') ->label(__('Last Response')), ]) ->recordActions([ Action::make('test') ->label(__('Test')) ->icon('heroicon-o-signal') ->color('info') ->action(function (WebhookEndpoint $record): void { $payload = [ 'event' => 'test', 'timestamp' => now()->toIso8601String(), 'request_id' => Str::uuid()->toString(), ]; $headers = []; if (! empty($record->secret_token)) { $signature = hash_hmac('sha256', json_encode($payload), $record->secret_token); $headers['X-Jabali-Signature'] = $signature; } try { $response = Http::withHeaders($headers)->post($record->url, $payload); $record->update([ 'last_response_code' => $response->status(), 'last_triggered_at' => now(), ]); Notification::make() ->title(__('Webhook delivered')) ->body(__('Status: :status', ['status' => $response->status()])) ->success() ->send(); } catch (\Exception $e) { $record->update([ 'last_response_code' => null, 'last_triggered_at' => now(), ]); Notification::make() ->title(__('Webhook failed')) ->body($e->getMessage()) ->danger() ->send(); } }), EditAction::make(), DeleteAction::make(), ]) ->emptyStateHeading(__('No webhooks configured')) ->emptyStateDescription(__('Add a webhook to receive system notifications.')); } }