{{-- /admin/packages/analytics/overview — mirrors prototype D:\wadesk_2806\whatsnap\tutot (3)\admin-package-analytics.html exactly. Every prototype "static" number is replaced with the matching value computed in AdminPagesController::packageAnalytics(). --}} @php $cur = fn ($v) => \App\Support\FormatSettings::currency((float) ($v ?? 0)); @endphp
{{ __('Admin') }} {{ __('Packages') }} {{ __('Analytics') }}
{{ __('Admin · Billing & plans · Package analytics') }}

{{ __('Package') }} {{ __('analytics') }}

{{ __('How each plan is performing — subscriber growth, MRR contribution, churn cohorts, upgrade paths, and feature adoption.') }}

{{ __('Total MRR') }}
{!! $cur($totalMrr) !!}
{{ $newPaid30d }} paid · last {{ $days }}d
{{ __('Subscribers') }}
{{ number_format($subsTotal) }}
+{{ $newPaid30d }} {{ __('net new') }}
{{ __('ARPA') }}
{!! $cur($arpa) !!}
{{ __('avg revenue / account') }}
{{ __('LTV avg') }}
{!! $cur($ltvAvg) !!}
28-mo lifetime
Net churn {{ $days }}d
{{ $churnPct }}%
{{ $cancelled30d }} cancels · {{ $newPaid30d }} {{ __('new') }}
{{ __('Trial → paid') }}
{{ $trialToPaidPct }}%
of {{ $trialActive }} {{ __('trials') }}
{{ $days }}-day trend

{{ __('MRR by package') }}

@php $colors = ['bg-wa-deep','bg-[#13478A]','bg-accent-amber']; @endphp @foreach ($mrrSeries['series'] ?? [] as $i => $s) {{ $s['name'] }} @endforeach
{{ __('Distribution') }}

{{ __('Plan share') }}

@php $sharePalette = ['bg-wa-deep','bg-[#13478A]','bg-accent-amber','bg-paper-300','bg-accent-coral']; @endphp @foreach ($share as $i => $row)
{{ $row['label'] }}{{ $row['count'] }} · {!! $cur($row['mrr']) !!}
@endforeach
{{ __('Per-package performance') }}

{{ __('Leaderboard') }}

Last {{ $days }} {{ __('days') }}
@php // Per-row badge styles cycle through the prototype palette so // ranks 1..N look like Pro / Enterprise / Starter / Free in // the original mock. $rowPalette = [ ['bg' => 'bg-wa-bubble', 'text' => 'text-wa-deep'], ['bg' => 'bg-[#D9E5F2]', 'text' => 'text-[#13478A]'], ['bg' => 'bg-[#FFF4E0]', 'text' => 'text-[#7B5A14]'], ['bg' => 'bg-paper-100', 'text' => 'text-ink-700'], ['bg' => 'bg-accent-coral/10', 'text' => 'text-accent-coral'], ]; @endphp @foreach ($leaderboard as $i => $row) @php $pkg = $row['package']; $initials = strtoupper(substr(preg_replace('/\s+/', '', $pkg->pname), 0, 2)); $palette = $rowPalette[$i] ?? $rowPalette[0]; $trendIcon = ['up' => '↑', 'flat' => '→', 'down' => '↓'][$row['trend']] ?? '·'; $trendCls = ['up' => 'text-wa-deep', 'flat' => 'text-accent-amber', 'down' => 'text-accent-coral'][ $row['trend'] ] ?? ''; $priceLabel = $pkg->free ? 'Free' : ($pkg->lifetime ? 'Lifetime' : \App\Support\FormatSettings::currency( (float) ($pkg->offer_price ?: $pkg->plan_amount), ) . '/' . ($pkg->plan_duration ?: 'mo')); @endphp @endforeach
{{ __('Package') }} {{ __('Subscribers') }} {{ __('Net new') }} {{ __('MRR') }} {{ __('ARPA') }} {{ __('Churn') }} {{ __('Avg LTV') }} {{ __('Trend') }}
{{ $initials }}
{{ $pkg->pname }}
{!! $priceLabel !!}
{{ $row['count'] }} {{ $row['net_new'] > 0 ? '+' . $row['net_new'] : ($row['net_new'] === 0 ? 0 : $row['net_new']) }} {!! $row['mrr'] > 0 ? $cur($row['mrr']) : '—' !!} {!! $row['arpa'] > 0 ? $cur($row['arpa']) : '—' !!} {{ $row['churn_pct'] }}% {!! $row['ltv'] > 0 ? $cur($row['ltv']) : '—' !!} {{ $trendIcon }}
Upgrade paths · {{ $days }}d

{{ __('Where do upgrades come from?') }}

@forelse ($upgrades as $u) @php $fromColor = match (strtolower($u['from'])) { 'trial', 'first paid' => 'bg-paper-100 text-ink-700', default => match (strtolower($u['from'])) { 'starter' => 'bg-[#FFF4E0] text-[#7B5A14]', 'pro' => 'bg-wa-bubble text-wa-deep', 'enterprise' => 'bg-[#D9E5F2] text-[#13478A]', 'any plan' => 'bg-paper-100 text-ink-700', default => 'bg-paper-100 text-ink-700', }, }; $toColor = match (strtolower($u['to'])) { 'starter' => 'bg-[#FFF4E0] text-[#7B5A14]', 'pro', 'first paid' => 'bg-wa-bubble text-wa-deep', 'enterprise' => 'bg-[#D9E5F2] text-[#13478A]', 'cancel' => 'bg-accent-coral/10 text-accent-coral', default => 'bg-paper-100 text-ink-700', }; $barColor = $u['kind'] === 'cancel' ? 'bg-accent-coral' : ($u['kind'] === 'downgrade' ? 'bg-accent-amber' : 'bg-wa-deep'); $countLabel = $u['kind'] === 'cancel' ? $u['count'] . ' cancels' : ($u['kind'] === 'downgrade' ? $u['count'] . ' downgrades' : $u['count'] . ' conversions'); $countCls = $u['kind'] === 'cancel' ? 'text-accent-coral' : ($u['kind'] === 'downgrade' ? 'text-accent-amber' : 'text-wa-deep'); @endphp
{{ $u['from'] }} {{ $u['to'] }} {{ $countLabel }}
@empty
No plan transitions in the last {{ $days }} days.
@endforelse
{{ __('Cancellation reasons') }}

{{ __('Why people leave') }}

    @forelse ($reasons as $r) @php $pct = (int) round($r->n / max(1, $maxReason) * 100); @endphp
  • {{ $r->label }}
    {{ $r->n }}
  • @empty
  • {{ __('No cancellations on record yet.') }}
  • @endforelse
@if ($cancelled30d > 0) {{ $cancelled30d }} customers cancelled · {{ $reasons->count() > 0 ? round((($reasons->first()->n ?? 0) / max(1, $cancelled30d)) * 100) : 0 }}% cite "{{ $reasons->first()->label ?? '—' }}" @else Customer cancellations + the top reason show here once they happen. @endif
{{ __('Feature adoption') }}

{{ __('What workspaces actually use') }}

% of subscribers using each feature
@foreach ($featureAdoption as $f) @php $low = $f['pct'] < 15; $cardCls = $low ? 'rounded-xl border border-accent-amber/40 p-3' : 'rounded-xl border border-paper-200 p-3'; $pctCls = $low ? 'font-serif text-[24px] text-accent-amber' : 'font-serif text-[24px]'; $barCls = $low ? 'h-full bg-accent-amber' : 'h-full bg-wa-deep'; @endphp
{{ $f['pct'] }}%
{{ $f['label'] }}
@endforeach