diff --git a/app/Livewire/Posts/Index.php b/app/Livewire/Posts/Index.php index cc8d036..5892c78 100644 --- a/app/Livewire/Posts/Index.php +++ b/app/Livewire/Posts/Index.php @@ -6,6 +6,7 @@ use App\Jobs\PublishPostJob; use App\Models\Post; use App\Models\Site; use App\Models\SocialAccount; +use App\Models\Template; use App\Support\Publishing\PublisherManager; use Livewire\Attributes\Layout; use Livewire\Component; @@ -17,6 +18,8 @@ class Index extends Component public ?int $editingId = null; + public ?int $templateId = null; + public string $siteId = ''; public string $socialAccountId = ''; @@ -47,6 +50,7 @@ class Index extends Component 'accounts' => SocialAccount::orderBy('username')->get(), 'types' => Post::TYPES, 'statuses' => Post::STATUSES, + 'templates' => Template::orderBy('name')->get(), 'hasPublishing' => $posts->contains('status', 'publishing'), ]); } @@ -74,6 +78,37 @@ class Index extends Component $this->showForm = true; } + 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::findOrFail($id); @@ -128,6 +163,7 @@ class Index extends Component public function resetForm(): void { $this->editingId = null; + $this->templateId = null; $this->siteId = ''; $this->socialAccountId = ''; $this->type = ''; diff --git a/app/Livewire/Templates/Index.php b/app/Livewire/Templates/Index.php new file mode 100644 index 0000000..e1cf50d --- /dev/null +++ b/app/Livewire/Templates/Index.php @@ -0,0 +1,102 @@ + Template::orderBy('name')->get(), + 'types' => Template::TYPES, + ]); + } + + public function create(): void + { + $this->resetForm(); + $this->showForm = true; + } + + public function edit(int $id): void + { + $template = Template::findOrFail($id); + $this->editingId = $template->id; + $this->name = $template->name; + $this->body = $template->body; + $this->type = $template->type; + $this->title = $template->title; + $this->target = $template->target; + $this->placeholders = $template->placeholders ? implode(', ', $template->placeholders) : ''; + $this->showForm = true; + } + + public function save(): void + { + $validated = $this->validate([ + 'name' => ['required', 'string', 'max:255'], + 'body' => ['required', 'string'], + 'type' => ['nullable', 'string', 'in:' . implode(',', Template::TYPES)], + 'title' => ['nullable', 'string', 'max:255'], + 'target' => ['nullable', 'string', 'max:255'], + 'placeholders' => ['nullable', 'string'], + ]); + + // Parse placeholders from comma-separated string + $placeholderArray = null; + if ($validated['placeholders']) { + $placeholderArray = array_map('trim', explode(',', $validated['placeholders'])); + $placeholderArray = array_filter($placeholderArray); + } + + Template::updateOrCreate(['id' => $this->editingId], [ + 'name' => $validated['name'], + 'body' => $validated['body'], + 'type' => $validated['type'], + 'title' => $validated['title'], + 'target' => $validated['target'], + 'placeholders' => $placeholderArray, + ]); + + $this->resetForm(); + $this->showForm = false; + } + + public function delete(int $id): void + { + Template::findOrFail($id)->delete(); + } + + public function resetForm(): void + { + $this->editingId = null; + $this->name = ''; + $this->body = ''; + $this->type = null; + $this->title = null; + $this->target = null; + $this->placeholders = ''; + $this->resetValidation(); + } +} diff --git a/app/Models/Template.php b/app/Models/Template.php new file mode 100644 index 0000000..4f8b7d0 --- /dev/null +++ b/app/Models/Template.php @@ -0,0 +1,54 @@ + 'array', + ]; + + public function applyToPost(array $data): array + { + $result = [ + 'title' => $this->title, + 'body' => $this->body, + 'type' => $this->type, + 'target' => $this->target, + ]; + + // Replace placeholders in all text fields + if ($this->placeholders && is_array($data)) { + foreach ($data as $key => $value) { + if (in_array($key, $this->placeholders)) { + foreach (['title', 'body', 'target'] as $field) { + if ($result[$field]) { + $result[$field] = str_replace("{{$key}}", $value, $result[$field]); + } + } + } + } + } + + return $result; + } +} diff --git a/database/migrations/2026_07_27_000000_create_templates_table.php b/database/migrations/2026_07_27_000000_create_templates_table.php new file mode 100644 index 0000000..b99e443 --- /dev/null +++ b/database/migrations/2026_07_27_000000_create_templates_table.php @@ -0,0 +1,27 @@ +id(); + $table->string('name'); + $table->text('body')->nullable(); + $table->string('type')->nullable(); // blog, twitter, reddit, etc. + $table->string('title')->nullable(); + $table->string('target')->nullable(); // e.g., subreddit for reddit + $table->json('placeholders')->nullable(); // JSON array of placeholder keys + $table->timestamps(); + }); + } + + public function down(): void + { + Schema::dropIfExists('templates'); + } +}; diff --git a/resources/views/livewire/posts/index.blade.php b/resources/views/livewire/posts/index.blade.php index 6adfa75..8015530 100644 --- a/resources/views/livewire/posts/index.blade.php +++ b/resources/views/livewire/posts/index.blade.php @@ -63,6 +63,21 @@