Add Post Templates feature

- Create templates table with name, body, type, title, target, placeholders
- Add Template model with applyToPost method for placeholder replacement
- Add Livewire Templates/Index component for CRUD operations
- Add templates index view with form and table
- Add /templates route
- Update Posts/Index to support template selection when creating posts
- Templates can be applied to pre-fill post form fields

Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
This commit is contained in:
m105
2026-07-27 21:00:41 +03:00
parent b3b250aaca
commit 2a41328130
7 changed files with 342 additions and 0 deletions
+36
View File
@@ -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 = '';
+102
View File
@@ -0,0 +1,102 @@
<?php
namespace App\Livewire\Templates;
use App\Models\Template;
use Livewire\Attributes\Layout;
use Livewire\Component;
#[Layout('components.layouts.app')]
class Index extends Component
{
public bool $showForm = false;
public ?int $editingId = null;
public string $name = '';
public string $body = '';
public ?string $type = null;
public ?string $title = null;
public ?string $target = null;
public string $placeholders = '';
public function render()
{
return view('livewire.templates.index', [
'templates' => 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();
}
}