@php // Chart scaling is computed once here rather than per-bar in the loop. $peak = max(1, $dailySeries->max('views')); @endphp

Analytics

{{ number_format($onlineNow) }} online now (last 5 min)

First-party traffic data. Visitor IPs are only ever stored as one-way hashes, so “visitors” is a close estimate rather than an exact count.

@foreach($ranges as $value => $label) {{ $label }} @endforeach
{{-- --- Summary tiles ------------------------------------------------- --}}
@php $tiles = [ ['label' => 'Page views', 'value' => number_format($summary['views']), 'note' => $summary['views_change'] === null ? 'No earlier data to compare' : ($summary['views_change'] >= 0 ? '+' : '').$summary['views_change'].'% vs previous period'], ['label' => 'Visitors', 'value' => number_format($summary['visitors']), 'note' => 'Distinct hashed visitors'], ['label' => 'Pages per visitor', 'value' => $summary['views_per_visitor'], 'note' => 'Average over the period'], ['label' => 'Enquiries', 'value' => number_format($messageStats['range']), 'note' => $messageStats['unread'].' unread'], ]; @endphp @foreach($tiles as $tile)

{{ $tile['label'] }}

{{ $tile['value'] }}

{{ $tile['note'] }}

@endforeach
{{-- --- Traffic over time --------------------------------------------- --}}

Traffic over time

@if($dailySeries->sum('views') === 0)

No visits recorded yet in this period.

@else {{-- A CSS bar chart rather than a charting library: it is a single series of counts, and adding a JS chart dependency to the admin bundle for this would cost more than the feature is worth (PRD 2.12 — no heavy dependencies where CSS suffices). --}}
{{ $dailySeries->first()['label'] }} Peak: {{ number_format($peak) }} views/day {{ $dailySeries->last()['label'] }}
@endif
{{-- --- Top pages -------------------------------------------------- --}}

Top pages

@if($topPages->isEmpty())

No data yet.

@else @foreach($topPages as $page) @endforeach
PathViewsVisitors
/{{ ltrim($page->path, '/') }} {{ number_format($page->views) }} {{ number_format($page->visitors) }}
@endif
{{-- --- Traffic sources -------------------------------------------- --}}

Traffic sources

@if($topSources->isEmpty())

No data yet.

@else @foreach($topSources as $source) @endforeach
SourceViews
{{ $source['label'] }} {{ number_format($source['views']) }}
@endif
{{-- --- Where visitors come from -------------------------------------- --}}

Top countries

@if($countries->isEmpty())

@if(! $locationTracked) No location data yet. Put the site behind Cloudflare (free) so the visitor's country header is sent, then new visits will appear here. @else No data yet. @endif

@else @foreach($countries as $country) @endforeach
CountryViewsVisitors
{{ $country['flag'] }} {{ $country['name'] }} {{ number_format($country['views']) }} {{ number_format($country['visitors']) }}
@endif

Top cities

@if($cities->isEmpty())

No city data yet. In Cloudflare enable Rules → Managed Transforms → “Add visitor location headers”.

@else @foreach($cities as $city) @endforeach
CityViewsVisitors
{{ $city['city'] }}@if($city['country']) , {{ $city['country'] }}@endif {{ number_format($city['views']) }} {{ number_format($city['visitors']) }}
@endif
{{-- --- Devices + browsers ------------------------------------------ --}} @foreach([['Devices', $devices], ['Browsers', $browsers]] as [$heading, $rows])

{{ $heading }}

@if($rows->isEmpty())

No data yet.

@else @foreach($rows as $row)
{{ $row['label'] }} {{ $row['percentage'] }}%
@endforeach @endif
@endforeach {{-- --- Most-read content ------------------------------------------ --}}

Most-read articles

@if($popularArticles->isEmpty())

No articles published yet.

@else
    @foreach($popularArticles as $article)
  1. {{ Str::limit($article->title, 52) }} — {{ number_format($article->views_count) }} views
  2. @endforeach
@endif

Most-viewed projects

@if($popularProjects->isEmpty())

No projects published yet.

@else
    @foreach($popularProjects as $project)
  1. {{ Str::limit($project->title, 52) }} — {{ number_format($project->views_count) }} views
  2. @endforeach
@endif
{{-- --- Enquiries -------------------------------------------------- --}}

Enquiries

@foreach([ 'Today' => $messageStats['today'], 'This period' => $messageStats['range'], 'Unread' => $messageStats['unread'], 'Replied' => $messageStats['replied'], 'Archived' => $messageStats['archived'], ] as $label => $value)

{{ $label }}

{{ number_format($value) }}

@endforeach
Open inbox