Files
2026-07-14 00:01:48 +03:00

376 lines
17 KiB
PHP
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
@extends('admin.layouts.app')
@section('title', 'Kullanıcı Analitiği')
@section('content')
<div class="d-flex align-items-center justify-content-between mb-4">
<div>
<h4 class="mb-1"><i class="bi bi-people me-2 text-info"></i>Kullanıcı Analitiği</h4>
<p class="text-muted mb-0 small">Gerçek kullanıcılar, bot trafiği, ülke dağılımı ve aktivite takibi</p>
</div>
<form class="d-flex gap-2 align-items-center" method="GET">
<input type="hidden" name="tab" value="{{ $tab }}">
<select name="period" class="form-select form-select-sm" onchange="this.form.submit()">
@foreach([7=>'Son 7 Gün',14=>'Son 14 Gün',30=>'Son 30 Gün',90=>'Son 90 Gün'] as $d=>$l)
<option value="{{ $d }}" {{ $period==$d?'selected':'' }}>{{ $l }}</option>
@endforeach
</select>
@if($country)
<a href="{{ request()->fullUrlWithoutQuery(['country']) }}" class="btn btn-sm btn-outline-secondary"><i class="bi bi-x"></i> {{ $country }}</a>
@endif
</form>
</div>
{{-- Overview Cards --}}
<div class="row g-3 mb-4">
<div class="col-6 col-md-2">
<div class="card text-center py-3 border-info">
<div class="h3 mb-0 text-info">{{ number_format($totalReal) }}</div>
<small class="text-muted">Toplam Kullanıcı</small>
</div>
</div>
<div class="col-6 col-md-2">
<div class="card text-center py-3 border-success">
<div class="h3 mb-0 text-success">+{{ number_format($newReal) }}</div>
<small class="text-muted">Yeni Üye</small>
</div>
</div>
<div class="col-6 col-md-2">
<div class="card text-center py-3 border-primary">
<div class="h3 mb-0 text-primary">{{ number_format($active30) }}</div>
<small class="text-muted">Aktif Kullanıcı</small>
</div>
</div>
<div class="col-6 col-md-2">
<div class="card text-center py-3 border-success">
<div class="h3 mb-0 text-success">{{ number_format($realViews) }}</div>
<small class="text-muted">Gerçek Görüntüleme</small>
</div>
</div>
<div class="col-6 col-md-2">
<div class="card text-center py-3 border-danger">
<div class="h3 mb-0 text-danger">{{ number_format($botViews) }}</div>
<small class="text-muted">Bot Görüntüleme</small>
</div>
</div>
<div class="col-6 col-md-2">
<div class="card text-center py-3">
@php $ratio = ($realViews+$botViews) > 0 ? round($botViews/($realViews+$botViews)*100,1) : 0; @endphp
<div class="h3 mb-0 {{ $ratio > 30 ? 'text-danger' : 'text-warning' }}">{{ $ratio }}%</div>
<small class="text-muted">Bot Oranı</small>
</div>
</div>
</div>
{{-- Tabs --}}
<ul class="nav nav-tabs mb-3">
@foreach(['overview'=>'Genel Bakış','activity'=>'Aktivite Günlüğü','bots'=>'Bot Analizi','country'=>'Ülke Dağılımı'] as $t=>$l)
<li class="nav-item">
<a class="nav-link {{ $tab==$t?'active':'' }}" href="{{ request()->fullUrlWithQuery(['tab'=>$t]) }}">{{ $l }}</a>
</li>
@endforeach
</ul>
{{-- ── Overview Tab ──────────────────────────────────────────────────────────── --}}
@if($tab === 'overview')
<div class="row g-4">
{{-- Daily new users chart --}}
<div class="col-md-8">
<div class="card">
<div class="card-header"><strong>Günlük Yeni Üye</strong></div>
<div class="card-body"><canvas id="dailyChart" height="80"></canvas></div>
</div>
</div>
{{-- Device breakdown --}}
<div class="col-md-4">
<div class="card">
<div class="card-header"><strong>Cihaz Dağılımı (gerçek kullanıcı)</strong></div>
<div class="card-body">
@foreach($deviceBreakdown as $dev)
@php $pct = $realViews > 0 ? round($dev->cnt/$realViews*100,1) : 0; @endphp
<div class="d-flex justify-content-between align-items-center mb-2">
<span class="text-capitalize">{{ $dev->device ?: 'bilinmiyor' }}</span>
<div class="d-flex align-items-center gap-2">
<div style="width:80px;height:6px;background:#333;border-radius:3px;overflow:hidden">
<div style="width:{{ $pct }}%;height:100%;background:var(--bs-info)"></div>
</div>
<small class="text-muted">{{ number_format($dev->cnt) }} ({{ $pct }}%)</small>
</div>
</div>
@endforeach
</div>
</div>
</div>
{{-- Top active users --}}
<div class="col-md-6">
<div class="card">
<div class="card-header"><strong>En Aktif Kullanıcılar</strong></div>
<div class="table-responsive">
<table class="table table-sm mb-0">
<thead class="table-dark"><tr><th>#</th><th>Kullanıcı</th><th>Aksiyon</th></tr></thead>
<tbody>
@foreach($topUsers as $i => $tu)
@php $u = $topUserMap[$tu->user_id] ?? null; @endphp
<tr>
<td class="text-muted">{{ $i+1 }}</td>
<td>
@if($u)
<a href="{{ route('admin.analytics.user-detail', $u) }}" class="text-decoration-none">{{ $u->name }}</a>
<br><small class="text-muted">{{ $u->email }}</small>
@else <span class="text-muted">#{{ $tu->user_id }}</span> @endif
</td>
<td><span class="badge bg-primary">{{ number_format($tu->actions) }}</span></td>
</tr>
@endforeach
</tbody>
</table>
</div>
</div>
</div>
{{-- Action breakdown --}}
<div class="col-md-6">
<div class="card">
<div class="card-header"><strong>Aksiyon Dağılımı</strong></div>
<div class="card-body">
@foreach($actionBreakdown as $ab)
@php $labels = \App\Models\UserActivityLog::$actionLabels; @endphp
<div class="d-flex justify-content-between align-items-center mb-2">
<span style="font-size:12px">{{ $labels[$ab->action] ?? $ab->action }}</span>
<span class="badge bg-secondary">{{ number_format($ab->cnt) }}</span>
</div>
@endforeach
@if($actionBreakdown->isEmpty()) <p class="text-muted text-center small py-3">Henüz aktivite yok</p> @endif
</div>
</div>
</div>
</div>
{{-- ── Activity Tab ──────────────────────────────────────────────────────────── --}}
@elseif($tab === 'activity')
<div class="card">
<div class="card-header d-flex justify-content-between align-items-center">
<strong>Aktivite Günlüğü</strong>
<form class="d-flex gap-2" method="GET">
<input type="hidden" name="tab" value="activity">
<input type="hidden" name="period" value="{{ $period }}">
<select name="action" class="form-select form-select-sm" onchange="this.form.submit()">
<option value="">Tüm Aksiyonlar</option>
@foreach(\App\Models\UserActivityLog::$actionLabels as $k=>$v)
<option value="{{ $k }}" {{ request('action')==$k?'selected':'' }}>{{ $v }}</option>
@endforeach
</select>
<input type="text" name="user_id" class="form-control form-control-sm" placeholder="User ID" value="{{ request('user_id') }}" style="width:100px">
<input type="text" name="country" class="form-control form-control-sm" placeholder="Ülke" value="{{ $country }}" style="width:80px">
<button type="submit" class="btn btn-sm btn-outline-info"><i class="bi bi-search"></i></button>
</form>
</div>
<div class="table-responsive">
<table class="table table-sm table-hover mb-0">
<thead class="table-dark">
<tr><th>Kullanıcı</th><th>Aksiyon</th><th>Konu</th><th>IP</th><th>Ülke</th><th>Cihaz</th><th>Zaman</th></tr>
</thead>
<tbody>
@forelse($actLogs as $log)
@php $labels = \App\Models\UserActivityLog::$actionLabels; @endphp
<tr>
<td>
@if($log->user)
<a href="{{ route('admin.analytics.user-detail', $log->user) }}" class="text-decoration-none small">{{ $log->user->name }}</a>
@else <span class="text-muted small">Misafir</span> @endif
</td>
<td><span class="badge bg-info text-dark" style="font-size:10px">{{ $labels[$log->action] ?? $log->action }}</span></td>
<td><small class="text-muted">{{ $log->subject_type ? class_basename($log->subject_type).'#'.$log->subject_id : '—' }}</small></td>
<td><code style="font-size:10px">{{ $log->ip }}</code></td>
<td>
@if($log->country)
<a href="{{ request()->fullUrlWithQuery(['tab'=>'activity','country'=>$log->country]) }}" class="badge bg-secondary text-decoration-none" style="font-size:10px">{{ $log->country }}</a>
@else @endif
</td>
<td><small class="text-muted text-capitalize">{{ $log->device }}</small></td>
<td><small class="text-muted">{{ \Carbon\Carbon::parse($log->created_at)->diffForHumans() }}</small></td>
</tr>
@empty
<tr><td colspan="7" class="text-center text-muted py-4">Kayıt bulunamadı</td></tr>
@endforelse
</tbody>
</table>
</div>
<div class="card-footer">{{ $actLogs->links() }}</div>
</div>
{{-- ── Bots Tab ──────────────────────────────────────────────────────────────── --}}
@elseif($tab === 'bots')
<div class="row g-4">
<div class="col-md-6">
<div class="card">
<div class="card-header"><strong><i class="bi bi-robot me-1 text-danger"></i>Bot Trafik Analizi</strong></div>
<div class="table-responsive">
<table class="table table-sm mb-0">
<thead class="table-dark"><tr><th>Bot / Araç</th><th>Aksiyon</th><th>İstek</th></tr></thead>
<tbody>
@forelse($botStats as $b)
<tr>
<td><code style="font-size:11px">{{ $b->bot_name }}</code></td>
<td>
<span class="badge {{ $b->action==='blocked'?'bg-danger':($b->action==='allowed'?'bg-success':'bg-warning text-dark') }}" style="font-size:10px">
{{ $b->action }}
</span>
</td>
<td><span class="badge bg-secondary">{{ number_format($b->cnt) }}</span></td>
</tr>
@empty
<tr><td colspan="3" class="text-center text-muted py-3">Bot logu bulunamadı</td></tr>
@endforelse
</tbody>
</table>
</div>
</div>
</div>
<div class="col-md-6">
<div class="card">
<div class="card-header"><strong><i class="bi bi-geo-alt me-1 text-danger"></i>En Fazla İstek Atan IP'ler (Botlar)</strong></div>
<div class="table-responsive">
<table class="table table-sm mb-0">
<thead class="table-dark"><tr><th>IP</th><th>İstek</th><th>Durum</th><th></th></tr></thead>
<tbody>
@forelse($topBotIps as $bip)
@php $blocked = $blockedIps->firstWhere('ip', $bip->ip); @endphp
<tr>
<td><code style="font-size:11px">{{ $bip->ip }}</code></td>
<td><span class="badge bg-danger">{{ number_format($bip->cnt) }}</span></td>
<td>
@if($blocked)
<span class="badge bg-warning text-dark" style="font-size:10px">Engelli</span>
@else
<span class="badge bg-secondary" style="font-size:10px">Serbest</span>
@endif
</td>
<td>
@if(!$blocked)
<form method="POST" action="{{ route('admin.analytics.block-ip') }}" class="d-inline">
@csrf <input type="hidden" name="ip" value="{{ $bip->ip }}">
<input type="hidden" name="reason" value="Bot trafiği — otomatik">
<button class="btn btn-xs btn-danger py-0 px-1" style="font-size:10px">Engelle</button>
</form>
@else
<form method="POST" action="{{ route('admin.analytics.unblock-ip') }}" class="d-inline">
@csrf <input type="hidden" name="ip" value="{{ $bip->ip }}">
<button class="btn btn-xs btn-success py-0 px-1" style="font-size:10px">Serbest Bırak</button>
</form>
@endif
</td>
</tr>
@empty
<tr><td colspan="4" class="text-center text-muted py-3">Bot IP kaydı yok</td></tr>
@endforelse
</tbody>
</table>
</div>
</div>
</div>
<div class="col-12">
<div class="card">
<div class="card-header"><strong><i class="bi bi-shield-x me-1 text-warning"></i>Engelli IP'ler ({{ $blockedIps->count() }})</strong></div>
<div class="table-responsive">
<table class="table table-sm mb-0">
<thead class="table-dark"><tr><th>IP</th><th>Sebep</th><th>Otomatik</th><th>Engellenme</th><th>Bitiş</th><th></th></tr></thead>
<tbody>
@forelse($blockedIps as $bi)
<tr>
<td><code style="font-size:11px">{{ $bi->ip }}</code></td>
<td><small class="text-muted">{{ $bi->reason }}</small></td>
<td>{{ $bi->auto_blocked ? '<span class="badge bg-warning text-dark" style="font-size:10px">Otomatik</span>' : '<span class="badge bg-secondary" style="font-size:10px">Manuel</span>' }}</td>
<td><small class="text-muted">{{ \Carbon\Carbon::parse($bi->blocked_at)->format('d.m.Y H:i') }}</small></td>
<td><small class="text-muted">{{ $bi->expires_at ? \Carbon\Carbon::parse($bi->expires_at)->diffForHumans() : 'Kalıcı' }}</small></td>
<td>
<form method="POST" action="{{ route('admin.analytics.unblock-ip') }}" class="d-inline">
@csrf <input type="hidden" name="ip" value="{{ $bi->ip }}">
<button class="btn btn-xs btn-outline-success py-0 px-1" style="font-size:10px">Serbest</button>
</form>
</td>
</tr>
@empty
<tr><td colspan="6" class="text-center text-muted py-3">Engelli IP yok</td></tr>
@endforelse
</tbody>
</table>
</div>
</div>
</div>
</div>
{{-- ── Country Tab ───────────────────────────────────────────────────────────── --}}
@elseif($tab === 'country')
<div class="card">
<div class="card-header"><strong><i class="bi bi-globe me-1"></i>Ülke Bazlı Trafik (Gerçek Kullanıcı)</strong></div>
<div class="table-responsive">
<table class="table table-hover mb-0">
<thead class="table-dark"><tr><th>#</th><th>Ülke</th><th>Görüntüleme</th><th>Benzersiz Kullanıcı</th><th>Oran</th><th></th></tr></thead>
<tbody>
@php $totalViews = $countries->sum('views'); @endphp
@forelse($countries as $i => $c)
@php $pct = $totalViews > 0 ? round($c->views/$totalViews*100,1) : 0; @endphp
<tr>
<td class="text-muted">{{ $i+1 }}</td>
<td class="fw-semibold">{{ $c->country ?: 'Bilinmiyor' }}</td>
<td>{{ number_format($c->views) }}</td>
<td>{{ number_format($c->users) }}</td>
<td>
<div class="d-flex align-items-center gap-2">
<div style="width:100px;height:6px;background:#333;border-radius:3px;overflow:hidden">
<div style="width:{{ $pct }}%;height:100%;background:var(--bs-info)"></div>
</div>
<small class="text-muted">{{ $pct }}%</small>
</div>
</td>
<td>
<a href="{{ request()->fullUrlWithQuery(['tab'=>'activity','country'=>$c->country]) }}" class="btn btn-xs btn-outline-info py-0 px-1" style="font-size:10px">Aktivite</a>
</td>
</tr>
@empty
<tr><td colspan="6" class="text-center text-muted py-4">Ülke verisi bulunamadı</td></tr>
@endforelse
</tbody>
</table>
</div>
</div>
@endif
@endsection
@push('scripts')
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.0/dist/chart.umd.min.js"></script>
<script>
@if($tab === 'overview')
const labels = @json($dailyNew->keys());
const data = @json($dailyNew->values());
new Chart(document.getElementById('dailyChart'), {
type: 'bar',
data: {
labels,
datasets: [{
label: 'Yeni Üye',
data,
backgroundColor: 'rgba(13,202,240,0.5)',
borderColor: 'rgba(13,202,240,1)',
borderWidth: 1,
borderRadius: 4,
}]
},
options: {
responsive: true,
plugins: { legend: { display: false } },
scales: {
x: { grid: { color: '#333' }, ticks: { color: '#aaa', maxRotation: 45 } },
y: { grid: { color: '#333' }, ticks: { color: '#aaa' }, beginAtZero: true },
}
}
});
@endif
</script>
@endpush