{{ __('Analytics · Workspace') }} Live · last refresh 14:08 UTC+1
@php $totalMessages = $totalMessages ?? 0; $delivered = $delivered ?? 0; $failed = $failed ?? 0; $repliesIn = $repliesIn ?? 0; $uniqueRecipients = $uniqueRecipients ?? 0; $deliverabilityPct = $deliverabilityPct ?? 0; $replyRatePct = $replyRatePct ?? 0; @endphp

{{ number_format($deliverabilityPct, 1) }}% of messages reached their reader this week.

@if ($totalMessages) {{ number_format($totalMessages) }} messages · {{ number_format($uniqueRecipients) }} unique recipients · {{ number_format($replyRatePct, 1) }}% reply rate. @else No outgoing messages yet — once your first queue sends, this dashboard fills in. @endif

@foreach ([ '7d' => 'Last 7 days', '30d' => 'Last 30 days', '90d' => 'Last 90 days', 'custom' => 'Custom', ] as $key => $label) @endforeach
{{-- Bridge real workspace data to the analytics chart JS. The JS file reads window.ANALYTICS_DATA so we don't rely on data-attributes per element (cleaner than 6 separate dataset reads). --}}
{{ __('Total messages · all time') }} @if ($totalMessages) {{ number_format($deliverabilityPct, 1) }}% delivered @endif
{{ number_format($totalMessages) }}
{{ number_format($delivered) }} {{ __('delivered') }}
{{ number_format($failed) }} {{ __('failed') }}
{{ __('recipients') }}{{ number_format($uniqueRecipients) }}
{{ __('replies in') }}{{ number_format($repliesIn) }}
{{ __('reply rate') }}{{ number_format($replyRatePct, 1) }}%
@php $trendPill = function ($delta, $invertGood = false) { $isUp = $delta >= 0; $good = $invertGood ? !$isUp : $isUp; $color = $good ? 'bg-wa-green/10 text-wa-deep border border-wa-green/30' : 'bg-accent-coral/10 text-[#A1431F] border border-accent-coral/35'; $arrow = $isUp ? '↑' : '↓'; $val = abs((float) $delta); return [$color, $arrow, $val]; }; @endphp @php [$cD, $aD, $vD] = $trendPill($deltaDelivered); @endphp
Delivered
{{ $aD }} {{ $vD }}%
{{ number_format($delivered) }}
{{ $deliverabilityPct }}% rate
@php [$cR, $aR, $vR] = $trendPill($deltaRecipients); @endphp
Recipients
{{ $aR }} {{ $vR }}%
{{ number_format($uniqueRecipients) }}
{{ __('unique numbers') }}
@php $devsHealthy = $devicesTotalCount > 0 && $devicesOnlineCount === $devicesTotalCount; $devsPill = $devsHealthy ? 'bg-wa-mint text-wa-deep border border-wa-green/40' : ($devicesOnlineCount > 0 ? 'bg-accent-amber/15 text-[#7B5A14] border border-accent-amber/40' : 'bg-accent-coral/10 text-[#A1431F] border border-accent-coral/35'); $devsPillLabel = $devsHealthy ? 'Healthy' : ($devicesOnlineCount > 0 ? 'Degraded' : 'Offline'); @endphp
Devices
{{ $devsPillLabel }}
{{ $devicesOnlineCount }} / {{ $devicesTotalCount }}
{{ $devsHealthy ? 'all online' : ($devicesOnlineCount > 0 ? $devicesTotalCount - $devicesOnlineCount . ' offline' : 'no devices') }}
@php [$cQ, $aQ, $vQ] = $trendPill($deltaQueued, true); @endphp
Queued
{{ $aQ }} {{ $vQ }}%
{{ number_format($queued) }}
{{ __('in queue') }}
@php [$cF, $aF, $vF] = $trendPill($deltaFailed, true); @endphp
Failed
{{ $aF }} {{ $vF }}%
{{ number_format($failed) }}
{{ __('carrier errors') }}
@php $rrUp = $deltaReplyRate >= 0; $rrColor = $rrUp ? 'bg-wa-green/10 text-wa-deep border border-wa-green/30' : 'bg-accent-coral/10 text-[#A1431F] border border-accent-coral/35'; $rrArrow = $rrUp ? '↑' : '↓'; @endphp
Reply rate
{{ $rrArrow }} {{ abs($deltaReplyRate) }}pp
{{ $replyRatePct }}%
{{ __('conversation start') }}
{{ __('Volume trend') }}

{{ __('Messages over time') }}

Sent Delivered Failed
{{ __('Quality') }}

{{ __('Delivery rates') }}

{{ __('Daily totals') }}

{{ __('Sent · queued · failed') }}

{{ __('stacked') }}
{{ __('Conversion') }}

{{ __('Engagement funnel') }}

{{ $range }}
@php $stepStyles = ['bg-wa-deep text-paper-0', 'bg-wa-teal/15', 'bg-wa-green/20', 'bg-paper-50']; @endphp @foreach ($funnelSteps as $i => $step) @php $isFirst = $i === 0; $widthPct = max(8.0, min(100.0, (float) $step['pct'])); $rowStyle = $stepStyles[$i] ?? 'bg-paper-50'; $labelMono = $isFirst ? 'text-paper-0/60' : 'text-ink-500'; $valColor = $isFirst ? 'text-paper-0/60' : 'text-ink-500'; @endphp @if (!$isFirst)
↓ {{ $step['pct'] }}%
@endif
{{ $step['stage'] }}
{{ $step['label'] }}
{{ number_format($step['count']) }}
{{ $isFirst ? '100%' : '↘ ' . number_format($step['drop']) }}
@endforeach
{{ __('end-to-end') }} {{ $funnelEndPct }}% @php $fnUp = $funnelDeltaPp >= 0; $fnColor = $fnUp ? 'bg-wa-bubble text-wa-deep' : 'bg-accent-coral/10 text-[#A1431F]'; $fnArrow = $fnUp ? '▲' : '▼'; @endphp {{ $fnArrow }} {{ abs($funnelDeltaPp) }}pp vs prev
{{ __('Performance') }}

{{ __('By device') }}

{{ $devicesOnlineCount }}/{{ $devicesTotalCount }} {{ __('online') }}
{{ __('Mix') }}

{{ __('Message types') }}

{{ __('Top performers') }}

{{ __('Templates') }}

{{ __('top 8') }}
    @forelse ($topTemplates as $i => $t)
  • {{ $i + 1 }}
    {{ $t['name'] }}
    {{ ucfirst((string) ($t['category'] ?? 'template')) }} · {{ strtoupper((string) ($t['language'] ?? 'en')) }}
    {{ number_format($t['sends']) }}
  • @empty
  • {{ __('No template sends in this window.') }}
  • @endforelse
{{ __('Reach') }}

{{ __('By country') }}

38 markets
@php $geoMax = collect($geoBuckets)->max('count') ?: 1; @endphp
@forelse ($geoBuckets as $g)
{{ $g['code'] }} {{ $g['code'] === '—' ? 'Unspecified' : $g['code'] }}
{{ number_format($g['count']) }}
@empty
{{ __('No device regions yet.') }}
@endforelse
Live

{{ __('Event stream') }}

    @forelse ($eventStream as $i => $e)
  1. {{ $e['title'] }}
    {{ $e['at'] }} · {{ $e['meta'] }}
  2. @empty
  3. {{ __('No recent activity in this window.') }}
  4. @endforelse
{{ __('Most engaged') }}

{{ __('Top contacts') }}

{{ __('Export') }}
@php $contactGradients = [ 'from-wa-teal to-wa-deep', 'from-accent-coral to-[#A1431F]', 'from-accent-amber to-[#7B5A14]', 'from-[#7B61FF] to-[#5B3D8A]', 'from-[#13478A] to-[#0B1F1C]', ]; @endphp
@forelse ($topContacts as $i => $c) @php $initials = strtoupper( substr(preg_replace('/[^a-z]/i', '', (string) $c['title']) ?: 'CO', 0, 2), ); $isLast = $loop->last; @endphp
{{ $initials }}
{{ $c['title'] }}
{{ $c['phone'] }} · {{ $c['last_at'] ?: '—' }}
{{ number_format($c['msgs']) }}
{{ __('msgs') }}
@empty
{{ __('No conversations in this window.') }}
@endforelse
{{-- #6 — Team performance section: per-agent rollup of conversations handled + response times + CSAT. Only shows when there's at least one assigned conversation in the window. --}} @if (!empty($teamPerformance))
{{ __('Team performance') }}

{{ __('Agents in this window') }}

{{ __('Conversations handled, response latency, customer satisfaction. Drawn from real workspace data.') }}

{{ count($teamPerformance) }} {{ __('agents') }}
@foreach ($teamPerformance as $row) @php $initials = strtoupper( substr(preg_replace('/[^a-z]/i', '', (string) $row['name']) ?: 'AG', 0, 2), ); $fr = $row['avg_first_resp_m']; $rr = $row['avg_resolve_m']; $fmtMin = fn($m) => $m === null ? '—' : ($m < 60 ? round($m) . 'm' : floor($m / 60) . 'h ' . (int) round($m) % 60 . 'm'); @endphp @endforeach
{{ __('Agent') }} {{ __('Convos') }} {{ __('Resolved') }} {{ __('Avg 1st resp.') }} {{ __('Avg resolution') }} {{ __('CSAT') }}
{{ $initials }}
{{ $row['name'] }}
{{ $row['email'] }}
{{ number_format($row['convos']) }} {{ number_format($row['resolved']) }} {{ $fmtMin($fr) }} {{ $fmtMin($rr) }} @if ($row['csat'] !== null) {{ number_format($row['csat'], 2) }} ({{ $row['csat_count'] }}) @else @endif
@endif
{{ __('Best hours') }}

{{ __('When your audience is awake') }}

{{ __('Read rate by hour × day · darker = higher engagement') }}

{{ __('UTC+1') }}
Computed from 241,802 events · refreshed every 5 minutes · synced with WABA #BL-049