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:
@@ -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 = '';
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class Template extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
public const TYPES = [
|
||||
'twitter', 'instagram', 'facebook', 'reddit',
|
||||
'tiktok', 'linkedin', 'youtube', 'blog', 'other',
|
||||
];
|
||||
|
||||
protected $fillable = [
|
||||
'name',
|
||||
'body',
|
||||
'type',
|
||||
'title',
|
||||
'target',
|
||||
'placeholders',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'placeholders' => '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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user