label(__('admin.messages.ai_optimize')) ->visible(function (): bool { try { return (bool) app(AiSettings::class)->content_optimization_enabled; } catch (\Throwable) { return false; } }) ->action(function (): void { OptimizeArticleContentJob::dispatch($this->record->getKey()); Notification::make() ->title(__('admin.messages.ai_optimize_queued')) ->body(__('admin.messages.ai_optimize_queue_hint')) ->success() ->send(); }), Action::make('applyAiPolish') ->label(__('admin.messages.ai_apply_polish')) ->color('gray') ->requiresConfirmation() ->modalHeading(__('admin.messages.ai_apply_polish')) ->modalDescription(__('admin.messages.ai_apply_polish_confirm')) ->visible(fn (): bool => filled($this->record->ai_polished_content)) ->action(function (): void { $polished = (string) $this->record->ai_polished_content; if ($polished === '') { Notification::make() ->title(__('admin.messages.ai_polish_missing')) ->danger() ->send(); return; } $this->record->forceFill(['content' => $polished])->save(); $this->refreshFormData(['content', 'ai_polished_content', 'ai_summary', 'description']); Notification::make() ->title(__('admin.messages.ai_apply_polish_done')) ->success() ->send(); }), Action::make('autoCover') ->label(__('admin.messages.auto_cover')) ->color('gray') ->action(function (): void { GenerateArticleCoverJob::dispatch($this->record->getKey(), 'auto'); Notification::make() ->title(__('admin.messages.auto_cover_queued')) ->body(__('admin.messages.ai_optimize_queue_hint')) ->success() ->send(); }), Action::make('generateCover') ->label(__('admin.messages.generate_cover')) ->color('gray') ->action(function (): void { GenerateArticleCoverJob::dispatch($this->record->getKey(), 'generate'); Notification::make() ->title(__('admin.messages.generate_cover_queued')) ->body(__('admin.messages.ai_optimize_queue_hint')) ->success() ->send(); }), DeleteAction::make(), ...Hook::collect('filament.article.actions'), ]; } /** * @param array $data * @return array */ protected function mutateFormDataBeforeFill(array $data): array { $items = is_array($this->record->ai_suggestions) ? $this->record->ai_suggestions : []; $data['ai_suggestions_text'] = collect($items) ->filter(fn ($item) => filled($item)) ->values() ->map(fn ($item, $index) => ($index + 1).'. '.$item) ->implode("\n"); $filtered = Hook::filter('filament.article.mutate_before_fill', $data, $this->record); return is_array($filtered) ? $filtered : $data; } /** * @param array $data * @return array */ protected function mutateFormDataBeforeSave(array $data): array { if (filled($data['cover_path'] ?? null)) { $path = (string) $data['cover_path']; $data['cover_status'] = 'ready'; $data['cover_source'] = str_starts_with($path, 'http://') || str_starts_with($path, 'https://') ? 'content_image' : 'manual'; $data['cover_disk'] = ($data['cover_source'] === 'content_image') ? 'url' : ($this->record->cover_disk ?: config('larablog.attachments_disk', 'attachments')); } $filtered = Hook::filter('filament.article.mutate_before_save', $data, $this->record); $data = is_array($filtered) ? $filtered : $data; $validated = Hook::filter('filament.article.validate_access_restrictions', $data, $this->record); $data = is_array($validated) ? $validated : $data; unset($data['paid_content'], $data['membership']); return $data; } protected function afterSave(): void { Hook::dispatch('filament.article.after_save', $this->record, $this->form->getState()); } }