@extends('layouts.staff') @section('title', 'Site Analytics & Traffic Telemetry › MTU Library Staff') @section('breadcrumb-trail') › Home › Site Analytics @endsection @section('content')

📈 OPAC Catalog Traffic & Visitor Telemetry

Real-time page views, unique visitor counts (privacy-salted), inbound referrers, and search bot activity.

@foreach([1 => 'Today', 7 => '7 Days', 14 => '14 Days', 30 => '30 Days', 90 => '90 Days'] as $val => $label) {{ $label }} @endforeach
@if(session('success'))
{{ session('success') }}
@endif @if(session('error'))
{{ session('error') }}
@endif

🛡️ Adaptive Bot Shield & 404 Flood Penalty Box

{{ count($activeBans) }} Active Lockout(s)
@if(!empty($activeBans)) @foreach($activeBans as $ban) @endforeach
Restricted IP Address Lockout Reason Strikes Lockout Remaining Action
{{ $ban['ip'] }} @if(!empty($ban['offense_level']) && $ban['offense_level'] > 1) Tier {{ $ban['offense_level'] }} @endif {{ $ban['reason'] }} {{ $ban['strikes'] }} @php $rem = $ban['remaining_seconds'] ?? 1800; $mins = max(1, (int) ceil($rem / 60)); $timeStr = $mins >= 60 ? (round($mins / 60, 1) . ' hrs') : ($mins . ' min'); @endphp ~{{ $timeStr }} remaining
@csrf
@else
Origin server protected with higher timers: /record/ and /search endpoints are shielded against scrapers and query bursts. Any IP generating ≥ 5 not-found or abusive requests within 10 minutes is automatically restricted (30 mins up to 24 hrs progressive lockout, HTTP 429). ✓ Shield Active & Normal
@endif
Total Catalog Views
{{ number_format($stats['total_views']) }}
Human page impressions
Views Today
{{ number_format($stats['today_views']) }}
Since 00:00 midnight
Unique Visitors Today
{{ number_format($stats['unique_visitors_today']) }}
Salted distinct client hashes
Unique Patrons (All-Time)
{{ number_format($stats['total_unique_visitors']) }}
{{ $stats['human_percent'] }}% human traffic ratio

📊 14-Day Catalog Traffic Trend

Page Views Unique Visitors
@php $maxVal = 1; foreach ($trend as $t) { if ($t['views'] > $maxVal) $maxVal = $t['views']; } @endphp
@foreach($trend as $dayItem) @php $viewHeight = max(4, round(($dayItem['views'] / $maxVal) * 120)); $uniqHeight = max(2, round(($dayItem['uniques'] / $maxVal) * 120)); @endphp
{{ $dayItem['label'] }}
@endforeach

📄 Top Visited Pages & Catalog Records

@if(!empty($topPages)) @foreach($topPages as $page) @endforeach
Page Path Views Uniques
{{ Str::limit($page['path'], 38) }} {{ number_format($page['views']) }} {{ number_format($page['uniques']) }}
@else

No page visits recorded in this timeframe yet.

@endif

🔗 Top Inbound Referrers (External Traffic)

@if(!empty($topReferrers)) @foreach($topReferrers as $ref) @endforeach
Referring Domain Inbound Hits
{{ $ref['referrer_host'] }} {{ number_format($ref['hits']) }}
@else
Direct traffic or bookmark visits dominate current traffic.
@endif

💻 Device & Client Distribution

@php $totalClients = max(1, array_sum($deviceBreakdown)); $deviceColors = [ 'Desktop' => '#4f7fb5', 'Mobile' => '#28a745', 'Tablet' => '#ff9800', 'Bot' => '#6c757d', ]; @endphp @foreach(['Desktop', 'Mobile', 'Tablet', 'Bot'] as $device) @php $count = $deviceBreakdown[$device] ?? 0; $pct = round(($count / $totalClients) * 100, 1); $color = $deviceColors[$device] ?? '#4f7fb5'; @endphp
{{ $device }} {{ $pct }}%
{{ number_format($count) }} visits
@endforeach

⚡ Real-Time Visitor Activity Stream (Last 25 Hits) ● Live Recording Active

@if(!empty($recentHits)) @foreach($recentHits as $hit) @endforeach
Timestamp Path Device Traffic Type Status
{{ \Carbon\Carbon::parse($hit['created_at'])->diffForHumans() }} {{ $hit['path'] }} {{ $hit['device_type'] }} @if($hit['is_bot']) Bot @else Human @endif {{ $hit['status_code'] }}
@else

No recent hits recorded yet.

@endif
@endsection