Add Cross-Posting support for multiple social accounts

- Create post_social_account pivot table for many-to-many relationship
- Add migration to migrate existing social_account_id to pivot table
- Remove social_account_id column from posts table
- Update Post model with socialAccounts() BelongsToMany relationship
- Update Posts Livewire component to use array of selectedAccountIds
- Update Posts view with checkboxes for selecting multiple accounts
- Update PublishPostJob to iterate through all social accounts of matching type
- Update Publisher interface to accept optional SocialAccount parameter
- Update all Publishers (WordPress, Reddit, Twitter, LinkedIn) to support cross-posting
- Display multiple social accounts as badges in posts table

Now a single post can be published to multiple social accounts of the same type.
For example: a Twitter post can be published to multiple Twitter accounts.

Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
This commit is contained in:
m105
2026-07-27 21:24:49 +03:00
parent 44f979820f
commit 90f313c134
12 changed files with 183 additions and 33 deletions
+24 -7
View File
@@ -45,7 +45,17 @@
@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">
@if ($post->socialAccounts->count() > 0)
<span class="flex flex-wrap gap-1">
@foreach ($post->socialAccounts as $account)
<span class="text-xs bg-blue-100 text-blue-800 px-2 py-0.5 rounded">{{ $account->username }}</span>
@endforeach
</span>
@else
<span class="text-gray-400">—</span>
@endif
</td>
<td class="p-3 text-right space-x-2">
@if (in_array($post->type, ['blog', 'reddit', 'twitter', 'linkedin']) && in_array($post->status, ['draft', 'scheduled', 'failed']))
<button wire:click="publish({{ $post->id }})" class="text-green-700">Publish</button>
@@ -90,14 +100,21 @@
</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>
<label class="block text-sm font-medium">Social Accounts (optional - select multiple for cross-posting)</label>
<div class="border border-gray-200 rounded p-2 max-h-32 overflow-y-auto bg-gray-50">
@foreach ($accounts as $account)
<option value="{{ $account->id }}">{{ $account->username }} ({{ ucfirst($account->network) }})</option>
<label class="flex items-center gap-2 p-1 hover:bg-white rounded cursor-pointer">
<input
type="checkbox"
wire:model="selectedAccountIds"
value="{{ $account->id }}"
class="h-4 w-4 rounded"
>
<span class="text-sm">{{ $account->username }} ({{ ucfirst($account->network) }})</span>
</label>
@endforeach
</select>
@error('socialAccountId') <span class="text-red-600 text-sm">{{ $message }}</span> @enderror
</div>
@error('selectedAccountIds') <span class="text-red-600 text-sm">{{ $message }}</span> @enderror
</div>
<div>