where('is_active', false) ->where('consecutive_failures', '>=', Webhook::MAX_FAILURES) ->count(); return $off > 0 ? (string) $off : null; } public static function getNavigationBadgeColor(): ?string { return 'danger'; } public static function form(Schema $schema): Schema { return $schema->components([ Section::make() ->columns(2) ->schema([ TextInput::make('name') ->label(__('panel.webhooks.fields.name')) ->required() ->maxLength(120), TextInput::make('url') ->label(__('panel.webhooks.fields.url')) ->helperText(__('panel.webhooks.fields.url_hint')) ->required() ->url() ->maxLength(2048) ->rule(new PublicHttpsUrl), Select::make('events') ->label(__('panel.webhooks.fields.events')) ->helperText(__('panel.webhooks.events_hint')) ->multiple() ->searchable() ->options(fn () => self::eventOptions()) ->required() ->columnSpanFull(), Toggle::make('include_personal_data') ->label(__('panel.webhooks.fields.include_personal_data')) ->helperText(__('panel.webhooks.fields.include_personal_data_hint')), Toggle::make('is_active') ->label(__('panel.webhooks.fields.is_active')) ->default(true), ]), ]); } public static function table(Table $table): Table { return $table ->columns([ TextColumn::make('name') ->label(__('panel.webhooks.fields.name')) ->weight('bold') ->description(fn (Webhook $record) => $record->url) ->searchable(), TextColumn::make('events') ->label(__('panel.webhooks.fields.events')) ->badge() ->color('gray'), IconColumn::make('is_active') ->label(__('panel.webhooks.fields.is_active')) ->boolean(), TextColumn::make('consecutive_failures') ->label(__('panel.webhooks.fields.failures')) ->color(fn (int $state) => $state > 0 ? 'danger' : 'gray'), TextColumn::make('last_delivered_at') ->label(__('panel.webhooks.fields.last_delivered_at')) ->since() ->dateTimeTooltip() ->placeholder('—'), ]) ->recordActions([ self::pingAction(), EditAction::make(), DeleteAction::make(), ]); } public static function eventLabel(string $event): string { $key = str_replace('.', '_', $event); return __("panel.webhooks.events.{$key}"); } /** * Grouped for the select: everything, the events with a meaning of their * own, any record by action, then each kind of record. * * @return array> */ public static function eventOptions(): array { $groups = [ __('panel.webhooks.groups.everything') => [ '*' => __('panel.webhooks.events.*'), ...collect(Webhook::ACTIONS)->mapWithKeys(fn (string $action) => [ "*.{$action}" => __('panel.webhooks.any_record', ['action' => self::actionLabel($action)]), ])->all(), ], __('panel.webhooks.groups.notices') => collect(Webhook::EVENTS) ->mapWithKeys(fn (string $event) => [$event => self::eventLabel($event)]) ->all(), ]; foreach (Webhook::RESOURCES as $resource) { $groups[__("panel.webhooks.resources.{$resource}")] = [ "{$resource}.*" => __('panel.webhooks.all_of', ['resource' => __("panel.webhooks.resources.{$resource}")]), ...collect(Webhook::ACTIONS)->mapWithKeys(fn (string $action) => [ "{$resource}.{$action}" => __("panel.webhooks.resources.{$resource}").' · '.self::actionLabel($action), ])->all(), ]; } return $groups; } protected static function actionLabel(string $action): string { return __("panel.webhooks.actions_on.{$action}"); } public static function pingAction(): Action { return Action::make('ping') ->label(__('panel.webhooks.actions.ping')) ->icon('heroicon-o-paper-airplane') ->color('gray') ->authorize('update') ->action(function (Webhook $record): void { Webhooks::ping($record); Notification::make() ->title(__('panel.webhooks.actions.ping_sent')) ->body(__('panel.webhooks.actions.ping_hint')) ->success() ->send(); }); } /** * A new secret, shown once. The old one stops being valid at once, so the * receiving end has to be updated straight after. */ public static function rotateAction(): Action { return Action::make('rotate_secret') ->label(__('panel.webhooks.actions.rotate')) ->icon('heroicon-o-key') ->color('warning') ->requiresConfirmation() ->modalDescription(__('panel.webhooks.actions.rotate_hint')) ->authorize('update') ->action(function (Webhook $record): void { $secret = Str::random(40); $record->forceFill(['secret' => $secret])->save(); self::showSecret($secret); }); } public static function showSecret(string $secret): void { Notification::make() ->title(__('panel.webhooks.secret.title')) ->body(__('panel.webhooks.secret.body', ['secret' => $secret])) ->warning() ->persistent() ->send(); } public static function getRelations(): array { return [ DeliveriesRelationManager::class, ]; } public static function getPages(): array { return [ 'index' => ListWebhooks::route('/'), 'create' => CreateWebhook::route('/create'), 'edit' => EditWebhook::route('/{record}/edit'), ]; } }