search) { $query->whereFullText(['title', 'body'], $this->search); } // Filter by type if ($this->typeFilter) { $query->where('type', $this->typeFilter); } // Filter by status if ($this->statusFilter) { $query->where('status', $this->statusFilter); } // Filter by site if ($this->siteFilter) { $query->where('site_id', $this->siteFilter); } $posts = $query->orderByDesc('created_at')->get(); return view('livewire.posts.index', [ 'posts' => $posts, 'sites' => Site::orderBy('name')->get(), 'accounts' => SocialAccount::orderBy('username')->get(), 'types' => Post::TYPES, 'statuses' => Post::STATUSES, 'templates' => Template::orderBy('name')->get(), 'hasPublishing' => $posts->contains('status', 'publishing'), ]); } public function publish(int $id): void { $this->publishNotice = null; $post = Post::with('site')->findOrFail($id); if (! app(PublisherManager::class)->resolve($post)) { $this->publishNotice = "No publisher available yet for post type '{$post->type}'."; return; } $post->update(['status' => 'publishing', 'publish_error' => null]); PublishPostJob::dispatch($post->id); } public function create(): void { $this->resetForm(); $this->showForm = true; } public function clearFilters(): void { $this->search = ''; $this->typeFilter = ''; $this->statusFilter = ''; $this->siteFilter = ''; } public function createFromTemplate(int $templateId): void { $template = Template::findOrFail($templateId); $this->resetForm(); $this->templateId = $template->id; // Pre-fill form from template if ($template->type) { $this->type = $template->type; } if ($template->title) { $this->title = $template->title; } if ($template->body) { $this->body = $template->body; } if ($template->target) { $this->target = $template->target; } $this->showForm = true; } public function applyTemplate(): void { if ($this->templateId) { $this->createFromTemplate($this->templateId); } } public function edit(int $id): void { $post = Post::with('socialAccounts')->findOrFail($id); $this->editingId = $post->id; $this->siteId = (string) $post->site_id; $this->selectedAccountIds = $post->socialAccounts->pluck('id')->map(fn ($id) => (string) $id)->toArray(); $this->type = $post->type; $this->title = (string) $post->title; $this->body = $post->body; $this->target = (string) $post->target; $this->imageUrl = (string) $post->image_url; $this->status = $post->status; $this->scheduledAt = $post->scheduled_at?->format('Y-m-d\TH:i') ?? ''; $this->showForm = true; } public function save(): void { $validated = $this->validate([ 'siteId' => ['required', 'integer', 'exists:sites,id'], 'selectedAccountIds' => ['array'], 'selectedAccountIds.*' => ['integer', 'exists:social_accounts,id'], 'type' => ['required', 'string', 'in:' . implode(',', Post::TYPES)], 'title' => ['nullable', 'string', 'max:255'], 'body' => ['required', 'string'], 'target' => ['nullable', 'string', 'max:255'], 'imageUrl' => ['nullable', 'url', 'max:2048'], 'status' => ['required', 'string', 'in:' . implode(',', Post::STATUSES)], 'scheduledAt' => ['nullable', 'date'], ]); $post = Post::updateOrCreate(['id' => $this->editingId], [ 'site_id' => $validated['siteId'], 'type' => $validated['type'], 'title' => $validated['title'] ?: null, 'body' => $validated['body'], 'target' => $validated['target'] ?: null, 'image_url' => $validated['imageUrl'] ?: null, 'status' => $validated['status'], 'scheduled_at' => $validated['scheduledAt'] ?: null, ]); // Sync social accounts using pivot table if ($this->editingId) { $post->socialAccounts()->sync($validated['selectedAccountIds'] ?: []); } else { $post->socialAccounts()->attach($validated['selectedAccountIds'] ?: []); } $this->resetForm(); $this->showForm = false; } public function delete(int $id): void { Post::findOrFail($id)->delete(); } public function resetForm(): void { $this->editingId = null; $this->templateId = null; $this->siteId = ''; $this->selectedAccountIds = []; $this->type = ''; $this->title = ''; $this->body = ''; $this->target = ''; $this->imageUrl = ''; $this->status = 'draft'; $this->scheduledAt = ''; $this->resetValidation(); } }