@php /** * Shared card markup for /wa-campaigns — visual design mirrors * the admin /admin/meta-ads cards (round status icon, title + * type tag, status pill + action group, KPI strip, metadata * footer with icons). * * @var \Illuminate\Support\Collection $campaigns */ $statusPillClasses = [ 'running' => [ 'bg' => 'bg-wa-green/10', 'text' => 'text-wa-deep', 'border' => 'border-wa-green/30', 'dot' => 'bg-wa-green', ], 'paused' => [ 'bg' => 'bg-[#EFE5F5]', 'text' => 'text-[#5B3D8A]', 'border' => 'border-[#D9C7E8]', 'dot' => 'bg-[#5B3D8A]', ], 'scheduled' => [ 'bg' => 'bg-[#13478A]/10', 'text' => 'text-[#13478A]', 'border' => 'border-[#13478A]/30', 'dot' => 'bg-[#13478A]', ], 'completed' => [ 'bg' => 'bg-ink-900', 'text' => 'text-paper-0', 'border' => 'border-ink-900', 'dot' => 'bg-paper-0', ], 'failed' => [ 'bg' => 'bg-accent-coral/10', 'text' => 'text-[#A1431F]', 'border' => 'border-accent-coral/30', 'dot' => 'bg-accent-coral', ], 'cancelled' => [ 'bg' => 'bg-paper-100', 'text' => 'text-ink-600', 'border' => 'border-paper-200', 'dot' => 'bg-paper-300', ], 'draft' => [ 'bg' => 'bg-paper-50', 'text' => 'text-ink-700', 'border' => 'border-paper-200', 'dot' => 'bg-paper-300', ], ]; $typeLabels = [ 'template' => 'Template', 'flow' => 'Flow', 'text' => 'Text', 'media' => 'Media', 'button' => 'Buttons', 'custom' => 'Custom', ]; @endphp @forelse ($campaigns as $c) @php $statusKey = strtolower((string) $c->status) ?: 'draft'; $pill = $statusPillClasses[$statusKey] ?? $statusPillClasses['draft']; $typeKey = strtolower((string) $c->campaign_type); $typeLabel = $typeLabels[$typeKey] ?? ucfirst($typeKey ?: 'Broadcast'); $cardBorder = match ($statusKey) { 'failed' => 'border-accent-coral/30 hover:border-accent-coral', 'completed' => 'border-paper-200 hover:border-wa-deep/25', default => 'border-paper-200 hover:border-wa-deep/25', }; $iconBg = match ($statusKey) { 'running' => 'bg-wa-bubble text-wa-deep', 'paused' => 'bg-[#EFE5F5] text-[#5B3D8A]', 'scheduled' => 'bg-[#13478A]/10 text-[#13478A]', 'completed' => 'bg-wa-bubble text-wa-deep', 'failed' => 'bg-accent-coral/10 text-accent-coral', 'cancelled' => 'bg-paper-100 text-ink-500', default => 'bg-paper-100 text-ink-600', }; $sent = (int) $c->sent_count; $delivered = (int) $c->delivered_count; $read = (int) $c->read_count; $responded = (int) $c->responded_count; $clicked = (int) $c->clicked_count; $failed = (int) $c->failed_count; $total = (int) $c->total_recipients; $deliveryRate = $sent > 0 ? round(($delivered / $sent) * 100, 1) : 0; $readRate = $delivered > 0 ? round(($read / $delivered) * 100, 1) : 0; $device = $c->device_id ? \App\Models\Device::find($c->device_id) : null; $deviceLabel = $device ? (trim($device->device_name) ?: 'Device #' . $device->id) : null; @endphp
@switch($statusKey) @case('running') @break @case('paused') @break @case('scheduled') @break @case('completed') @break @case('failed') @break @case('cancelled') @break @default @endswitch
{{ $c->campaign_name }} {{ $typeLabel }} @if ($c->ab_testing) A/B @endif
{{ $typeLabel }}@if ($deviceLabel) · Device: {{ $deviceLabel }} @endif · {{ number_format($total) }} recipient{{ $total === 1 ? '' : 's' }}
{{ ucfirst($statusKey) }} @if ($statusKey === 'running' || $statusKey === 'scheduled')
@csrf
@elseif ($statusKey === 'paused' || $statusKey === 'failed' || $statusKey === 'cancelled')
@csrf
@endif {{-- Edit ⇄ Resend are mutually exclusive and status-gated: Edit shows for mutable campaigns (draft / scheduled / paused); Resend shows for finished ones (completed / failed / cancelled). A running campaign is in flight, so neither appears. --}} @if (in_array($statusKey, ['draft', 'scheduled', 'paused'], true)) @elseif (in_array($statusKey, ['completed', 'failed', 'cancelled'], true))
@csrf
@endif
@csrf @method('DELETE')
@if ($statusKey === 'failed' && $failed > 0)
Failed: {{ number_format($failed) }} message{{ $failed === 1 ? '' : 's' }} didn't send
{{ __('Click') }} Resume to retry the failures, or open analytics to see which recipients bounced.
@endif
{{ __('Sent') }}
{{ number_format($sent) }}
{{ __('Delivered') }}
{{ number_format($delivered) }}
{{ __('Read') }}
{{ number_format($read) }}
{{ __('Replied') }}
{{ number_format($responded) }}
{{ __('Clicked') }}
{{ number_format($clicked) }}
{{ __('Failed') }}
{{ number_format($failed) }}
{{ __('Delivery') }}
{{ number_format($deliveryRate, 1) }}%
Created: {{ $c->created_at?->format('Y-m-d') }} {{ number_format($total) }} recipient{{ $total === 1 ? '' : 's' }} @if ($c->schedule_type === 'scheduled' && $c->send_date) Scheduled: {{ \Illuminate\Support\Carbon::parse($c->send_date)->format('Y-m-d') }}@if ($c->send_time) · {{ \Illuminate\Support\Str::limit($c->send_time, 5, '') }} @endif @else Send: instant @endif @if ($c->template_id) Template #{{ $c->template_id }} @elseif ($c->flow_id) Flow #{{ $c->flow_id }} @endif @if ($statusKey === 'running') Delivering now @elseif ($statusKey === 'scheduled') Waiting for send window @elseif ($statusKey === 'completed') Read rate: {{ number_format($readRate, 1) }}% @elseif ($statusKey === 'failed') Needs attention @elseif ($statusKey === 'paused') Paused @elseif ($statusKey === 'cancelled') Cancelled @else Draft @endif
@empty @include('user.partials.empty-state', [ 'message' => 'No WhatsApp campaigns match the current filters. Try clearing filters or launch your first broadcast.', 'resetHref' => url('/wa-campaigns'), 'actionHref' => route('user.wa-campaigns.create'), 'actionLabel' => 'Create campaign', ]) @endforelse