Files
muninn/resources/views/livewire/posts/index.blade.php
T
m105andClaude Sonnet 5 42abc658f2 Add an image field, required for WordPress posts
Posts get a nullable image_url. WordPressPublisher now downloads it
and uploads it to WordPress's media endpoint before creating the
post, setting it as featured_media — and rejects publishing (without
touching status) if a blog post has no image set, mirroring the
existing title requirement. Every other network treats it as optional.

Not enforced at save time, same as title — you can still draft an
incomplete WordPress post, it just can't be published until an image
is set. Exposed through the UI form (with a thumbnail/""missing""
indicator in the table), the JSON API, and the Python CLI wrapper.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-21 20:03:25 +03:00

151 lines
7.9 KiB
PHP

<div @if ($hasPublishing) wire:poll.3s @endif>
<div class="flex justify-between items-center mb-4">
<h1 class="text-xl font-semibold">Posts</h1>
<button wire:click="create" class="bg-blue-600 text-white px-4 py-2 rounded">Add Post</button>
</div>
@if ($publishNotice)
<div class="mb-4 p-3 bg-amber-50 border border-amber-200 text-amber-800 rounded text-sm">{{ $publishNotice }}</div>
@endif
<table class="w-full bg-white border border-gray-200 rounded">
<thead>
<tr class="text-left border-b border-gray-200">
<th class="p-3">Image</th>
<th class="p-3">Site</th>
<th class="p-3">Type</th>
<th class="p-3">Title</th>
<th class="p-3">Status</th>
<th class="p-3">Scheduled</th>
<th class="p-3">Account</th>
<th class="p-3"></th>
</tr>
</thead>
<tbody>
@foreach ($posts as $post)
<tr wire:key="post-{{ $post->id }}" class="border-b border-gray-100">
<td class="p-3">
@if ($post->image_url)
<img src="{{ $post->image_url }}" alt="" class="w-10 h-10 object-cover rounded">
@elseif ($post->type === 'blog')
<span class="text-xs text-amber-600">missing</span>
@else
<span class="text-gray-400">—</span>
@endif
</td>
<td class="p-3">{{ $post->site->name }}</td>
<td class="p-3">{{ ucfirst($post->type) }}</td>
<td class="p-3">{{ $post->title ?: \Illuminate\Support\Str::limit($post->body, 40) }}</td>
<td class="p-3">
@if ($post->status === 'published' && $post->published_url)
<a href="{{ $post->published_url }}" target="_blank" class="text-green-700 hover:underline">Published</a>
@else
{{ ucfirst($post->status) }}
@endif
@if ($post->status === 'failed' && $post->publish_error)
<div class="text-xs text-red-600 mt-0.5">{{ $post->publish_error }}</div>
@endif
</td>
<td class="p-3">{{ $post->scheduled_at?->format('Y-m-d H:i') ?? '—' }}</td>
<td class="p-3">{{ $post->socialAccount?->username ?? '—' }}</td>
<td class="p-3 text-right space-x-2">
@if (in_array($post->type, ['blog', 'reddit']) && in_array($post->status, ['draft', 'scheduled', 'failed']))
<button wire:click="publish({{ $post->id }})" class="text-green-700">Publish</button>
@endif
<button wire:click="edit({{ $post->id }})" class="text-blue-600">Edit</button>
<button wire:click="delete({{ $post->id }})" wire:confirm="Delete this post?" class="text-red-600">Delete</button>
</td>
</tr>
@endforeach
</tbody>
</table>
@if ($showForm)
<div class="fixed inset-0 bg-black/30 flex items-center justify-center">
<div class="bg-white p-6 rounded w-full max-w-md space-y-4 max-h-[90vh] overflow-y-auto">
<h2 class="text-lg font-semibold">{{ $editingId ? 'Edit Post' : 'Add Post' }}</h2>
<div>
<label class="block text-sm font-medium">Site</label>
<select wire:model="siteId" class="w-full border rounded p-2">
<option value="">Select site</option>
@foreach ($sites as $site)
<option value="{{ $site->id }}">{{ $site->name }}</option>
@endforeach
</select>
@error('siteId') <span class="text-red-600 text-sm">{{ $message }}</span> @enderror
</div>
<div>
<label class="block text-sm font-medium">Social Account (optional)</label>
<select wire:model="socialAccountId" class="w-full border rounded p-2">
<option value="">None</option>
@foreach ($accounts as $account)
<option value="{{ $account->id }}">{{ $account->username }} ({{ ucfirst($account->network) }})</option>
@endforeach
</select>
@error('socialAccountId') <span class="text-red-600 text-sm">{{ $message }}</span> @enderror
</div>
<div>
<label class="block text-sm font-medium">Type</label>
<select wire:model="type" class="w-full border rounded p-2">
<option value="">Select type</option>
@foreach ($types as $t)
<option value="{{ $t }}">{{ ucfirst($t) }}</option>
@endforeach
</select>
@error('type') <span class="text-red-600 text-sm">{{ $message }}</span> @enderror
</div>
<div>
<label class="block text-sm font-medium">Title (optional)</label>
<input type="text" wire:model="title" class="w-full border rounded p-2">
@error('title') <span class="text-red-600 text-sm">{{ $message }}</span> @enderror
</div>
<div>
<label class="block text-sm font-medium">Body</label>
<textarea wire:model="body" rows="4" class="w-full border rounded p-2"></textarea>
@error('body') <span class="text-red-600 text-sm">{{ $message }}</span> @enderror
</div>
<div>
<label class="block text-sm font-medium">Target (optional — e.g. subreddit for Reddit posts)</label>
<input type="text" wire:model="target" class="w-full border rounded p-2">
@error('target') <span class="text-red-600 text-sm">{{ $message }}</span> @enderror
</div>
<div>
<label class="block text-sm font-medium">
Image URL {{ $type === 'blog' ? '(required to publish to WordPress)' : '(optional)' }}
</label>
<input type="url" wire:model="imageUrl" placeholder="https://..." class="w-full border rounded p-2">
@error('imageUrl') <span class="text-red-600 text-sm">{{ $message }}</span> @enderror
</div>
<div>
<label class="block text-sm font-medium">Status</label>
<select wire:model="status" class="w-full border rounded p-2">
@foreach ($statuses as $s)
<option value="{{ $s }}">{{ ucfirst($s) }}</option>
@endforeach
</select>
@error('status') <span class="text-red-600 text-sm">{{ $message }}</span> @enderror
</div>
<div>
<label class="block text-sm font-medium">Scheduled At (optional)</label>
<input type="datetime-local" wire:model="scheduledAt" class="w-full border rounded p-2">
@error('scheduledAt') <span class="text-red-600 text-sm">{{ $message }}</span> @enderror
</div>
<div class="flex justify-end gap-2">
<button wire:click="$set('showForm', false)" class="px-4 py-2">Cancel</button>
<button wire:click="save" class="bg-blue-600 text-white px-4 py-2 rounded">Save</button>
</div>
</div>
</div>
@endif
</div>