@extends('layouts.admin')
@section('title', 'Campaign — ' . $campaign->name)
@push('styles')
{{-- Flatpickr (date/time picker) — CDN'd to avoid touching the Vite build. --}}
@endpush
@section('admin')
@php
$statusColors = [
'draft' => ['text'=>'text-gray-300', 'bg'=>'bg-gray-800', 'border'=>'border-gray-700'],
'scheduled' => ['text'=>'text-blue-300', 'bg'=>'bg-blue-900/30', 'border'=>'border-blue-700/50'],
'queued' => ['text'=>'text-purple-300','bg'=>'bg-purple-900/30','border'=>'border-purple-700/50'],
'sending' => ['text'=>'text-emerald-300','bg'=>'bg-emerald-900/30','border'=>'border-emerald-700/50'],
'paused' => ['text'=>'text-amber-300', 'bg'=>'bg-amber-900/20', 'border'=>'border-amber-700/40'],
'sent' => ['text'=>'text-emerald-300','bg'=>'bg-emerald-900/20','border'=>'border-emerald-700/40'],
'canceled' => ['text'=>'text-gray-500', 'bg'=>'bg-gray-900', 'border'=>'border-gray-700'],
'failed' => ['text'=>'text-red-300', 'bg'=>'bg-red-900/20', 'border'=>'border-red-700/40'],
];
$sc = $statusColors[$campaign->status] ?? $statusColors['draft'];
$errorIssues = array_values(array_filter($issues, fn($i) => $i['level'] === 'error'));
$warnings = array_values(array_filter($issues, fn($i) => $i['level'] === 'warning'));
$infos = array_values(array_filter($issues, fn($i) => $i['level'] === 'info'));
$errCount = count($errorIssues);
$warnCount = count($warnings);
$totalChecks = max(1, count($issues));
$passedChecks = count($infos);
$score = empty($issues) ? 100 : max(0, 100 - $errCount * 25 - $warnCount * 10);
$scoreColor = $score >= 80 ? '#4ade80' : ($score >= 50 ? '#fcd34d' : '#f87171');
$sentPct = $campaign->total_recipients > 0
? min(100, ($campaign->sent_count / max(1, $campaign->total_recipients)) * 100) : 0;
@endphp
@include('admin._nav')
{{-- Breadcrumb --}}
{{-- Header --}}
@if($campaign->status === 'sending')@endif
{{ ucfirst($campaign->status) }}
@if($campaign->approval_status && $campaign->approval_status !== 'none')
@php
$apv = [
'pending' => ['bg'=>'bg-amber-900/30','text'=>'text-amber-300','border'=>'border-amber-700/50','icon'=>'fa-hourglass-half','label'=>'Pending Approval'],
'approved' => ['bg'=>'bg-emerald-900/30','text'=>'text-emerald-300','border'=>'border-emerald-700/50','icon'=>'fa-circle-check','label'=>'Approved'],
'rejected' => ['bg'=>'bg-red-900/30','text'=>'text-red-300','border'=>'border-red-700/50','icon'=>'fa-circle-xmark','label'=>'Rejected'],
][$campaign->approval_status] ?? null;
@endphp
@if($apv)
{{ $apv['label'] }}
@endif
@endif
@if($campaign->subject_b && $campaign->ab_split_percent)
A/B {{ $campaign->ab_split_percent }}%
@endif
@if($campaign->is_quick_send)Quick Send@endif
{{ $campaign->name }}
{{ $campaign->subject }}
@if($campaign->preheader)
{{ $campaign->preheader }}
@endif
@if(session('success'))
{{ session('success') }}
@endif
@if(session('error'))
{{ session('error') }}
@endif
{{-- Key metrics --}}
Audience
{{ number_format($audience) }}
active subscribers
Sent
{{ number_format($campaign->sent_count) }}
of {{ number_format($campaign->total_recipients) }} queued
Failed
{{ number_format($campaign->failed_count) }}
delivery errors
Progress
{{ number_format($sentPct, 0) }}%
{{-- Preflight checklist --}}
Pre-send Checklist
@if(empty($issues))
All clear
@elseif($errCount > 0)
{{ $errCount }} error{{ $errCount > 1 ? 's' : '' }}
@else
{{ $warnCount }} warning{{ $warnCount > 1 ? 's' : '' }}
@endif
@if(empty($issues))
All checks passed
This campaign is ready to send.
@else
@foreach($errorIssues as $i)
{{ $i['message'] }}
@if(!empty($i['detail']))
{{ $i['detail'] }}
@endif
@endforeach
@foreach($warnings as $i)
{{ $i['message'] }}
@if(!empty($i['detail']))
{{ $i['detail'] }}
@endif
@endforeach
@foreach($infos as $i)
@endforeach
@endif
{{-- Readiness score --}}
Readiness Score
@if($score === 100)
Launch ready
All systems go.
@elseif($score >= 60)
Nearly ready
Review warnings above.
@else
Needs attention
Fix errors before sending.
@endif
{{-- Approval workflow + A/B winner --}}
@php
$reviewer = $campaign->approved_by ? \App\Models\User::find($campaign->approved_by) : null;
@endphp
Approval workflow
Lock down the bytes a reviewer signs off on.
@if($campaign->approval_status === 'pending')
Awaiting approval. The campaign body and schedule are locked until this review completes.
@if($campaign->approval_notes)
Author notes: "{{ $campaign->approval_notes }}"
@endif
@elseif($campaign->approval_status === 'approved')
Approved {{ optional($campaign->approved_at)->diffForHumans() }} @if($reviewer) by {{ $reviewer->name }}@endif. Ready to launch.
@if($campaign->approval_notes)
"{{ $campaign->approval_notes }}"
@endif
@elseif($campaign->approval_status === 'rejected')
Rejected {{ optional($campaign->approved_at)->diffForHumans() }} @if($reviewer) by {{ $reviewer->name }}@endif.
@if($campaign->approval_notes)
{{ $campaign->approval_notes }}
@endif
Edit the campaign and resubmit when ready.
@else
No review requested yet.
@if(in_array($campaign->status, ['draft']))
@else
Only drafts can be submitted for approval.
@endif
@endif
@if($campaign->subject_b && $campaign->ab_split_percent && !empty($abStats))
A/B winner
{{ $campaign->ab_split_percent }}% of recipients receive variant B; pick winner by opens or clicks.
@foreach(['a' => 'Subject A', 'b' => 'Subject B'] as $key => $label)
@php $s = $abStats[$key]; $isWinner = $campaign->winner_variant === $key; @endphp
{{ $label }} @if($isWinner)★ winner@endif
{{ $s['sent'] }} sent
{{ $key === 'b' ? $campaign->subject_b : $campaign->subject }}
{{ $s['opens'] }} ({{ $s['open_rate'] }}%)
{{ $s['clicks'] }} ({{ $s['click_rate'] }}%)
@endforeach
@if($campaign->winner_variant)
Variant {{ strtoupper($campaign->winner_variant) }} picked on {{ $campaign->winner_metric }} {{ optional($campaign->winner_picked_at)->diffForHumans() }}.
@else
@endif
@endif
{{-- Schedule & launch --}}
@php
// Curated common zones; the JS layer below will inject the browser's
// detected zone if it isn't already in this list, so any user gets a
// sensible default without having to scroll through 400+ IANA names.
$tzOptions = [
'America/New_York' => 'Eastern (New York)',
'America/Chicago' => 'Central (Chicago)',
'America/Denver' => 'Mountain (Denver)',
'America/Phoenix' => 'Mountain (Phoenix — no DST)',
'America/Los_Angeles' => 'Pacific (Los Angeles)',
'America/Anchorage' => 'Alaska (Anchorage)',
'Pacific/Honolulu' => 'Hawaii (Honolulu)',
'UTC' => 'UTC',
'Europe/London' => 'London',
'Europe/Berlin' => 'Berlin / Paris',
'Asia/Tokyo' => 'Tokyo',
'Australia/Sydney' => 'Sydney',
];
$defaultTz = $campaign->schedule_timezone ?: config('app.timezone', 'America/Denver');
// datetime-local default: 15 minutes from now in the chosen tz.
try {
$defaultWhen = \Illuminate\Support\Carbon::now($defaultTz)->addMinutes(15)->format('Y-m-d\TH:i');
} catch (\Throwable) {
$defaultWhen = \Illuminate\Support\Carbon::now()->addMinutes(15)->format('Y-m-d\TH:i');
}
@endphp
Schedule & Launch
Mode: {{ ucfirst($campaign->schedule_mode) }}
@if($campaign->scheduled_at)
·
@if($campaign->schedule_timezone)
{{ $campaign->scheduled_at->copy()->setTimezone($campaign->schedule_timezone)->format('M j, Y · g:i A') }}
({{ $campaign->schedule_timezone }})
@else
{{ $campaign->scheduled_at->format('M j, Y · g:i A') }} UTC
@endif
@endif
@if($campaign->throttle_per_minute)
·
throttle {{ $campaign->throttle_per_minute }}/min
@endif
@if($campaign->cron_expression)·{{ $campaign->cron_expression }}@endif
@if($campaign->next_run_at)·next {{ $campaign->next_run_at->diffForHumans() }}@endif
@if(in_array($campaign->status, ['draft','paused','scheduled']))
@php $approvalBlocks = in_array($campaign->approval_status, ['pending','rejected'], true); @endphp
@endif
@if($campaign->isPausable())
@endif
@if($campaign->status==='paused')
@endif
@if($campaign->isCancelable())
@endif
@if(!in_array($campaign->status, ['sent','sending']))
@endif
{{-- Unified Send / Schedule modal --}}
{{-- Send test --}}
Send a Test
Send yourself a preview before launching.
@endsection
@push('scripts')
@endpush