Initial commit: Animexe Laravel platform

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-14 00:01:48 +03:00
co-authored by Claude Opus 4.8
commit a63515cfc6
366 changed files with 74773 additions and 0 deletions
@@ -0,0 +1,375 @@
@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